about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
authorThomas E. Dickey <dickey@invisible-island.net>2010-05-03 00:45:10 -0400
committerThomas E. Dickey <dickey@invisible-island.net>2010-05-03 00:45:10 -0400
commit903885454167e86ce4cb967f901cbaf741f21501 (patch)
tree90a46f9f1e6c6194c8f43bbb4aa81e1e50e7e2fe /src
parentdc748b1c47baadafae2c90f0e188927b11b7e029 (diff)
downloadlynx-snapshots-903885454167e86ce4cb967f901cbaf741f21501.tar.gz
snapshot of project "lynx", label v2-8-8dev_3c
Diffstat (limited to 'src')
-rw-r--r--src/AttrList.h62
-rw-r--r--src/DefaultStyle.c504
-rw-r--r--src/GridText.c14668
-rw-r--r--src/GridText.h292
-rw-r--r--src/HTAlert.c1174
-rw-r--r--src/HTAlert.h165
-rw-r--r--src/HTFWriter.c1410
-rw-r--r--src/HTFont.h50
-rw-r--r--src/HTForms.h166
-rw-r--r--src/HTInit.c1469
-rw-r--r--src/HTML.c8304
-rw-r--r--src/HTML.h276
-rw-r--r--src/HTNestedList.h44
-rw-r--r--src/HTSaveToFile.h29
-rw-r--r--src/LYBookmark.c1123
-rw-r--r--src/LYBookmark.h25
-rw-r--r--src/LYCgi.c751
-rw-r--r--src/LYCgi.h16
-rw-r--r--src/LYCharSets.c1157
-rw-r--r--src/LYCharSets.h154
-rw-r--r--src/LYCharUtils.c3415
-rw-r--r--src/LYCharUtils.h103
-rw-r--r--src/LYCharVals.h34
-rw-r--r--src/LYClean.c224
-rw-r--r--src/LYClean.h24
-rw-r--r--src/LYCookie.c2743
-rw-r--r--src/LYCookie.h57
-rw-r--r--src/LYCurses.c2970
-rw-r--r--src/LYCurses.h831
-rw-r--r--src/LYDownload.c586
-rw-r--r--src/LYDownload.h21
-rw-r--r--src/LYEdit.c298
-rw-r--r--src/LYEdit.h18
-rw-r--r--src/LYEditmap.c1299
-rw-r--r--src/LYExtern.c418
-rw-r--r--src/LYExtern.h16
-rw-r--r--src/LYForms.c1039
-rw-r--r--src/LYGCurses.h246
-rw-r--r--src/LYGetFile.c1576
-rw-r--r--src/LYGetFile.h38
-rw-r--r--src/LYGlobalDefs.h689
-rw-r--r--src/LYHash.c59
-rw-r--r--src/LYHash.h83
-rw-r--r--src/LYHistory.c1173
-rw-r--r--src/LYHistory.h39
-rw-r--r--src/LYIcon.rc1
-rw-r--r--src/LYJump.c501
-rw-r--r--src/LYJump.h36
-rw-r--r--src/LYJustify.h83
-rw-r--r--src/LYKeymap.c1821
-rw-r--r--src/LYKeymap.h290
-rw-r--r--src/LYLeaks.c1017
-rw-r--r--src/LYList.c347
-rw-r--r--src/LYList.h15
-rw-r--r--src/LYLocal.c2629
-rw-r--r--src/LYLocal.h36
-rw-r--r--src/LYMail.c1735
-rw-r--r--src/LYMail.h56
-rw-r--r--src/LYMain.c4439
-rw-r--r--src/LYMainLoop.c7862
-rw-r--r--src/LYMainLoop.h34
-rw-r--r--src/LYMap.c658
-rw-r--r--src/LYMap.h28
-rw-r--r--src/LYNews.c509
-rw-r--r--src/LYNews.h18
-rw-r--r--src/LYOptions.c4028
-rw-r--r--src/LYOptions.h37
-rw-r--r--src/LYPrettySrc.c431
-rw-r--r--src/LYPrettySrc.h92
-rw-r--r--src/LYPrint.c1458
-rw-r--r--src/LYPrint.h20
-rw-r--r--src/LYReadCFG.c2501
-rw-r--r--src/LYReadCFG.h72
-rw-r--r--src/LYSearch.c376
-rw-r--r--src/LYSearch.h26
-rw-r--r--src/LYSession.c265
-rw-r--r--src/LYSession.h16
-rw-r--r--src/LYShowInfo.c481
-rw-r--r--src/LYShowInfo.h21
-rw-r--r--src/LYSignal.h31
-rw-r--r--src/LYStrings.c6121
-rw-r--r--src/LYStrings.h367
-rw-r--r--src/LYStructs.h191
-rw-r--r--src/LYStyle.c797
-rw-r--r--src/LYStyle.h82
-rw-r--r--src/LYTraversal.c182
-rw-r--r--src/LYTraversal.h23
-rw-r--r--src/LYUpload.c225
-rw-r--r--src/LYUpload.h17
-rw-r--r--src/LYUtils.c7841
-rw-r--r--src/LYUtils.h538
-rw-r--r--src/LYVMSdef.h18
-rw-r--r--src/LYebcdic.c48
-rw-r--r--src/LYexit.c185
-rw-r--r--src/LYmktime.c337
-rw-r--r--src/LYrcFile.c1042
-rw-r--r--src/LYrcFile.h282
-rw-r--r--src/TRSTable.c2033
-rw-r--r--src/TRSTable.h49
-rw-r--r--src/UCAuto.c820
-rw-r--r--src/UCAuto.h14
-rw-r--r--src/UCAux.c627
-rw-r--r--src/UCdomap.c2484
-rw-r--r--src/UCdomap.h178
-rw-r--r--src/Xsystem.c584
-rw-r--r--src/chrtrans/README.format138
-rw-r--r--src/chrtrans/README.tables76
-rw-r--r--src/chrtrans/UCkd.h54
-rw-r--r--src/chrtrans/build-chrtrans.com141
-rw-r--r--src/chrtrans/build-header.com37
-rw-r--r--src/chrtrans/caselower.h738
-rw-r--r--src/chrtrans/cp1250_uni.tbl172
-rw-r--r--src/chrtrans/cp1251_uni.tbl161
-rw-r--r--src/chrtrans/cp1252_uni.tbl177
-rw-r--r--src/chrtrans/cp1253_uni.tbl161
-rw-r--r--src/chrtrans/cp1255_uni.tbl161
-rw-r--r--src/chrtrans/cp1256_uni.tbl161
-rw-r--r--src/chrtrans/cp1257_uni.tbl162
-rw-r--r--src/chrtrans/cp437_uni.tbl181
-rw-r--r--src/chrtrans/cp737_uni.tbl172
-rw-r--r--src/chrtrans/cp775_uni.tbl159
-rw-r--r--src/chrtrans/cp850_uni.tbl177
-rw-r--r--src/chrtrans/cp852_uni.tbl170
-rw-r--r--src/chrtrans/cp857_uni.tbl159
-rw-r--r--src/chrtrans/cp862_uni.tbl160
-rw-r--r--src/chrtrans/cp864_uni.tbl160
-rw-r--r--src/chrtrans/cp866_uni.tbl159
-rw-r--r--src/chrtrans/cp866u_uni.tbl157
-rw-r--r--src/chrtrans/cp869_uni.tbl160
-rw-r--r--src/chrtrans/def7_uni.tbl2943
-rw-r--r--src/chrtrans/dmcs_uni.tbl233
-rw-r--r--src/chrtrans/entities.h1414
-rw-r--r--src/chrtrans/hp_uni.tbl212
-rw-r--r--src/chrtrans/iso01_uni.tbl334
-rw-r--r--src/chrtrans/iso02_uni.tbl265
-rw-r--r--src/chrtrans/iso03_uni.tbl255
-rw-r--r--src/chrtrans/iso04_uni.tbl252
-rw-r--r--src/chrtrans/iso05_uni.tbl259
-rw-r--r--src/chrtrans/iso06_uni.tbl208
-rw-r--r--src/chrtrans/iso07_uni.tbl275
-rw-r--r--src/chrtrans/iso08_uni.tbl229
-rw-r--r--src/chrtrans/iso09_uni.tbl266
-rw-r--r--src/chrtrans/iso10_uni.tbl153
-rw-r--r--src/chrtrans/iso13_uni.tbl114
-rw-r--r--src/chrtrans/iso14_uni.tbl114
-rw-r--r--src/chrtrans/iso15_uni.tbl216
-rw-r--r--src/chrtrans/jcuken_kb.h22
-rw-r--r--src/chrtrans/koi8r_uni.tbl147
-rw-r--r--src/chrtrans/koi8u_uni.tbl154
-rw-r--r--src/chrtrans/mac_uni.tbl284
-rw-r--r--src/chrtrans/make-msc.bat6
-rw-r--r--src/chrtrans/makefile.bcb123
-rw-r--r--src/chrtrans/makefile.dos135
-rw-r--r--src/chrtrans/makefile.in200
-rw-r--r--src/chrtrans/makefile.msc136
-rw-r--r--src/chrtrans/makehdrs.bat50
-rw-r--r--src/chrtrans/makeuctb.c893
-rw-r--r--src/chrtrans/makew32.bat13
-rw-r--r--src/chrtrans/mnem2_suni.tbl1865
-rw-r--r--src/chrtrans/mnem_suni.tbl1861
-rw-r--r--src/chrtrans/next_uni.tbl185
-rw-r--r--src/chrtrans/pt154_uni.tbl174
-rw-r--r--src/chrtrans/rfc_suni.tbl1958
-rw-r--r--src/chrtrans/rot13_kb.h22
-rw-r--r--src/chrtrans/utf8_uni.tbl35
-rw-r--r--src/chrtrans/viscii_uni.tbl300
-rw-r--r--src/chrtrans/yawerty_kb.h22
-rw-r--r--src/cmu_tcp.opt1
-rw-r--r--src/decc.opt2
-rw-r--r--src/descrip.mms172
-rw-r--r--src/gnuc.opt3
-rw-r--r--src/makefile.dos115
-rw-r--r--src/makefile.dsl105
-rw-r--r--src/makefile.in228
-rw-r--r--src/makefile.wsl68
-rw-r--r--src/mktime.c72
-rw-r--r--src/multinet.opt1
-rw-r--r--src/parsdate.c1519
-rw-r--r--src/parsdate.h21
-rw-r--r--src/parsdate.y963
-rw-r--r--src/socketshr_tcp.opt1
-rw-r--r--src/strstr.c60
-rw-r--r--src/structdump.h157
-rw-r--r--src/tcpipolb.opt1
-rw-r--r--src/tcpipshr.opt1
-rw-r--r--src/tcpwareolb.opt1
-rw-r--r--src/tcpwareshr.opt1
-rw-r--r--src/tidy_tls.c601
-rw-r--r--src/ucxolb.opt1
-rw-r--r--src/ucxshr.opt1
-rw-r--r--src/vaxc.opt2
-rw-r--r--src/win_tcp.opt1
192 files changed, 131471 insertions, 0 deletions
diff --git a/src/AttrList.h b/src/AttrList.h
new file mode 100644
index 00000000..d64a7308
--- /dev/null
+++ b/src/AttrList.h
@@ -0,0 +1,62 @@
+/*
+ * $LynxId: AttrList.h,v 1.16 2009/04/16 23:42:58 tom Exp $
+ */
+#if !defined(__ATTRLIST_H)
+#define __ATTRLIST_H
+
+#include <HText.h>
+#include <HTMLDTD.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    enum {
+	ABS_OFF = 0,
+	STACK_OFF = 0,
+	STACK_ON,
+	ABS_ON
+    };
+
+#define STARTAT 8
+
+    enum {
+	DSTYLE_LINK = HTML_A + STARTAT,
+	DSTYLE_STATUS = HTML_ELEMENTS + STARTAT,
+	DSTYLE_ALINK,		/* active link */
+	DSTYLE_NORMAL,		/* default attributes */
+	DSTYLE_OPTION,		/* option on the option screen */
+	DSTYLE_VALUE,		/* value on the option screen */
+	DSTYLE_CANDY,		/* possibly going to vanish */
+	DSTYLE_WHEREIS,		/* whereis search target */
+	DSTYLE_ELEMENTS
+    };
+
+    typedef struct {
+	int color;		/* color highlighting to be done */
+	int mono;		/* mono highlighting to be done */
+	int cattr;		/* attributes to go with the color */
+    } HTCharStyle;
+
+#if 0
+#define HText_characterStyle CTRACE((tfp,"HTC called from %s/%d\n",__FILE__,__LINE__));_internal_HTC
+#else
+#define HText_characterStyle _internal_HTC
+#endif
+
+#if defined(USE_COLOR_STYLE)
+    extern void _internal_HTC(HText *text, int style, int dir);
+
+#define TEMPSTRINGSIZE 256
+    extern char class_string[TEMPSTRINGSIZE];
+
+/* stack of attributes during page rendering */
+#define MAX_LAST_STYLES 128
+    extern int last_styles[MAX_LAST_STYLES + 1];
+    extern int last_colorattr_ptr;
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/DefaultStyle.c b/src/DefaultStyle.c
new file mode 100644
index 00000000..3dc17f75
--- /dev/null
+++ b/src/DefaultStyle.c
@@ -0,0 +1,504 @@
+/*
+ * $LynxId: DefaultStyle.c,v 1.20 2009/11/27 13:04:27 tom Exp $
+ *
+ *	A real style sheet for the Character Grid browser
+ *
+ *	The dimensions are all in characters!
+ */
+
+#include <HTUtils.h>
+#include <HTFont.h>
+#include <HTStyle.h>
+
+#include <LYGlobalDefs.h>
+#include <LYLeaks.h>
+
+/*	Tab arrays:
+*/
+static const HTTabStop tabs_8[] =
+{
+    {0, 8},
+    {0, 16},
+    {0, 24},
+    {0, 32},
+    {0, 40},
+    {0, 48},
+    {0, 56},
+    {0, 64},
+    {0, 72},
+    {0, 80},
+    {0, 88},
+    {0, 96},
+    {0, 104},
+    {0, 112},
+    {0, 120},
+    {0, 128},
+    {0, 136},
+    {0, 144},
+    {0, 152},
+    {0, 160},
+    {0, 168},
+    {0, 176},
+    {0, 0}			/* Terminate */
+};
+
+/* Template:
+ *	link to next, name, name id (enum), tag,
+ *	font, size, colour, superscript, anchor id,
+ *	indents: 1st, left, right, alignment	lineheight, descent,	tabs,
+ *	word wrap, free format, space: before, after, flags.
+ */
+
+static HTStyle HTStyleNormal =
+HTStyleInit(
+	       0, Normal, "P",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       3, 3, 6, HT_LEFT, 1, 0, tabs_8,
+	       YES, YES, 1, 0, 0);
+
+static HTStyle HTStyleDivCenter =
+HTStyleInit(
+	       &HTStyleNormal, DivCenter, "DCENTER",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       3, 3, 6, HT_CENTER, 1, 0, tabs_8,
+	       YES, YES, 1, 0, 0);
+
+static HTStyle HTStyleDivLeft =
+HTStyleInit(
+	       &HTStyleDivCenter, DivLeft, "DLEFT",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       3, 3, 6, HT_LEFT, 1, 0, tabs_8,
+	       YES, YES, 1, 0, 0);
+
+static HTStyle HTStyleDivRight =
+HTStyleInit(
+	       &HTStyleDivLeft, DivRight, "DRIGHT",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       3, 3, 6, HT_RIGHT, 1, 0, tabs_8,
+	       YES, YES, 1, 0, 0);
+
+static HTStyle HTStyleBanner =
+HTStyleInit(
+	       &HTStyleDivRight, Banner, "BANNER",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       3, 3, 6, HT_LEFT, 1, 0, tabs_8,
+	       YES, YES, 1, 0, 0);
+
+static HTStyle HTStyleBlockquote =
+HTStyleInit(
+	       &HTStyleBanner, Blockquote, "BLOCKQUOTE",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       5, 5, 7, HT_LEFT, 1, 0, tabs_8,
+	       YES, YES, 1, 0, 0);
+
+static HTStyle HTStyleBq =
+HTStyleInit(			/* HTML 3.0 BLOCKQUOTE - FM */
+	       &HTStyleBlockquote, Bq, "BQ",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       5, 5, 7, HT_LEFT, 1, 0, tabs_8,
+	       YES, YES, 1, 0, 0);
+
+static HTStyle HTStyleFootnote =
+HTStyleInit(			/* HTML 3.0 FN - FM */
+	       &HTStyleBq, Footnote, "FN",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       5, 5, 7, HT_LEFT, 1, 0, tabs_8,
+	       YES, YES, 1, 0, 0);
+
+static HTStyle HTStyleList =
+HTStyleInit(
+	       &HTStyleFootnote, List, "UL",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       3, 7, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 0, 0, 0);
+
+static HTStyle HTStyleList1 =
+HTStyleInit(
+	       &HTStyleList, List1, "UL",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       8, 12, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 0, 0, 0);
+
+static HTStyle HTStyleList2 =
+HTStyleInit(
+	       &HTStyleList1, List2, "UL",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       13, 17, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 0, 0, 0);
+
+static HTStyle HTStyleList3 =
+HTStyleInit(
+	       &HTStyleList2, List3, "UL",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       18, 22, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 0, 0, 0);
+
+static HTStyle HTStyleList4 =
+HTStyleInit(
+	       &HTStyleList3, List4, "UL",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       23, 27, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 0, 0, 0);
+
+static HTStyle HTStyleList5 =
+HTStyleInit(
+	       &HTStyleList4, List5, "UL",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       28, 32, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 0, 0, 0);
+
+static HTStyle HTStyleList6 =
+HTStyleInit(
+	       &HTStyleList5, List6, "UL",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       33, 37, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 0, 0, 0);
+
+static HTStyle HTStyleMenu =
+HTStyleInit(
+	       &HTStyleList6, Menu, "MENU",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       3, 7, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 0, 0, 0
+);
+
+static HTStyle HTStyleMenu1 =
+HTStyleInit(
+	       &HTStyleMenu, Menu1, "MENU",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       8, 12, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 0, 0, 0
+);
+
+static HTStyle HTStyleMenu2 =
+HTStyleInit(
+	       &HTStyleMenu1, Menu2, "MENU",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       13, 17, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 0, 0, 0
+);
+
+static HTStyle HTStyleMenu3 =
+HTStyleInit(
+	       &HTStyleMenu2, Menu3, "MENU",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       18, 22, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 0, 0, 0
+);
+
+static HTStyle HTStyleMenu4 =
+HTStyleInit(
+	       &HTStyleMenu3, Menu4, "MENU",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       23, 27, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 0, 0, 0
+);
+
+static HTStyle HTStyleMenu5 =
+HTStyleInit(
+	       &HTStyleMenu4, Menu5, "MENU",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       28, 33, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 0, 0, 0
+);
+
+static HTStyle HTStyleMenu6 =
+HTStyleInit(
+	       &HTStyleMenu5, Menu6, "MENU",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       33, 38, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 0, 0, 0
+);
+
+static HTStyle HTStyleGlossary =
+HTStyleInit(
+	       &HTStyleMenu6, Glossary, "DL",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       3, 10, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 1, 1, 0
+);
+
+static HTStyle HTStyleGlossary1 =
+HTStyleInit(
+	       &HTStyleGlossary, Glossary1, "DL",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       8, 16, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 1, 1, 0
+);
+
+static HTStyle HTStyleGlossary2 =
+HTStyleInit(
+	       &HTStyleGlossary1, Glossary2, "DL",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       14, 22, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 1, 1, 0
+);
+
+static HTStyle HTStyleGlossary3 =
+HTStyleInit(
+	       &HTStyleGlossary2, Glossary3, "DL",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       20, 28, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 1, 1, 0
+);
+
+static HTStyle HTStyleGlossary4 =
+HTStyleInit(
+	       &HTStyleGlossary3, Glossary4, "DL",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       26, 34, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 1, 1, 0
+);
+
+static HTStyle HTStyleGlossary5 =
+HTStyleInit(
+	       &HTStyleGlossary4, Glossary5, "DL",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       32, 40, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 1, 1, 0
+);
+
+static HTStyle HTStyleGlossary6 =
+HTStyleInit(
+	       &HTStyleGlossary5, Glossary6, "DL",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       38, 46, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 1, 1, 0
+);
+
+static HTStyle HTStyleGlossaryCompact =
+HTStyleInit(
+	       &HTStyleGlossary6, GlossaryCompact, "DLC",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       3, 10, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 0, 0, 0
+);
+
+static HTStyle HTStyleGlossaryCompact1 =
+HTStyleInit(
+	       &HTStyleGlossaryCompact,
+	       GlossaryCompact1, "DLC",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       8, 15, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 0, 0, 0
+);
+
+static HTStyle HTStyleGlossaryCompact2 =
+HTStyleInit(
+	       &HTStyleGlossaryCompact1,
+	       GlossaryCompact2, "DLC",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       13, 20, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 0, 0, 0
+);
+
+static HTStyle HTStyleGlossaryCompact3 =
+HTStyleInit(
+	       &HTStyleGlossaryCompact2,
+	       GlossaryCompact3, "DLC",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       18, 25, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 0, 0, 0
+);
+
+static HTStyle HTStyleGlossaryCompact4 =
+HTStyleInit(
+	       &HTStyleGlossaryCompact3,
+	       GlossaryCompact4, "DLC",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       23, 30, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 0, 0, 0
+);
+
+static HTStyle HTStyleGlossaryCompact5 =
+HTStyleInit(
+	       &HTStyleGlossaryCompact4,
+	       GlossaryCompact5, "DLC",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       28, 35, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 0, 0, 0
+);
+
+static HTStyle HTStyleGlossaryCompact6 =
+HTStyleInit(
+	       &HTStyleGlossaryCompact5,
+	       GlossaryCompact6, "DLC",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       33, 40, 6, HT_LEFT, 1, 0, 0,
+	       YES, YES, 0, 0, 0
+);
+
+static HTStyle HTStyleExample =
+HTStyleInit(
+	       &HTStyleGlossaryCompact6,
+	       Example, "XMP",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       0, 0, 0, HT_LEFT, 1, 0, tabs_8,
+	       NO, NO, 0, 0, 0
+);
+
+static HTStyle HTStylePreformatted =
+HTStyleInit(
+	       &HTStyleExample,
+	       Preformatted, "PRE",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       0, 0, 0, HT_LEFT, 1, 0, tabs_8,
+	       NO, NO, 0, 0, 0
+);
+
+static HTStyle HTStyleListing =
+HTStyleInit(
+	       &HTStylePreformatted, Listing, "LISTING",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       0, 0, 0, HT_LEFT, 1, 0, tabs_8,
+	       NO, NO, 0, 0, 0);
+
+static HTStyle HTStyleAddress =
+HTStyleInit(
+	       &HTStyleListing, Address, "ADDRESS",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       4, 4, 7, HT_LEFT, 1, 0, tabs_8,
+	       YES, YES, 2, 0, 0);
+
+static HTStyle HTStyleNote =
+HTStyleInit(			/* HTML 3.0 NOTE - FM */
+	       &HTStyleAddress, Note, "NOTE",
+	       HT_FONT, 1, HT_BLACK, 0, 0,
+	       5, 5, 7, HT_LEFT, 1, 0, tabs_8,
+	       YES, YES, 1, 0, 0);
+
+static HTStyle HTStyleHeading1 =
+HTStyleInit(
+	       &HTStyleNote, Heading1, "H1",
+	       HT_FONT + HT_BOLD, 1, HT_BLACK, 0, 0,
+	       0, 0, 0, HT_CENTER, 1, 0, 0,
+	       YES, YES, 1, 1, 0);
+
+static HTStyle HTStyleHeading2 =
+HTStyleInit(
+	       &HTStyleHeading1, Heading2, "H2",
+	       HT_FONT + HT_BOLD, 1, HT_BLACK, 0, 0,
+	       0, 0, 0, HT_LEFT, 1, 0, 0,
+	       YES, YES, 1, 1, 0);
+
+static HTStyle HTStyleHeading3 =
+HTStyleInit(
+	       &HTStyleHeading2, Heading3, "H3",
+	       HT_FONT + HT_BOLD, 1, HT_BLACK, 0, 0,
+	       2, 2, 0, HT_LEFT, 1, 0, 0,
+	       YES, YES, 1, 0, 0);
+
+static HTStyle HTStyleHeading4 =
+HTStyleInit(
+	       &HTStyleHeading3, Heading4, "H4",
+	       HT_FONT + HT_BOLD, 1, HT_BLACK, 0, 0,
+	       4, 4, 0, HT_LEFT, 1, 0, 0,
+	       YES, YES, 1, 0, 0);
+
+static HTStyle HTStyleHeading5 =
+HTStyleInit(
+	       &HTStyleHeading4, Heading5, "H5",
+	       HT_FONT + HT_BOLD, 1, HT_BLACK, 0, 0,
+	       6, 6, 0, HT_LEFT, 1, 0, 0,
+	       YES, YES, 1, 0, 0);
+
+static HTStyle HTStyleHeading6 =
+HTStyleInit(
+	       &HTStyleHeading5, Heading6, "H6",
+	       HT_FONT + HT_BOLD, 1, HT_BLACK, 0, 0,
+	       8, 8, 0, HT_LEFT, 1, 0, 0,
+	       YES, YES, 1, 0, 0);
+
+static HTStyle HTStyleHeadingCenter =
+HTStyleInit(
+	       &HTStyleHeading6, HeadingCenter, "HCENTER",
+	       HT_FONT + HT_BOLD, 1, HT_BLACK, 0, 0,
+	       0, 0, 3, HT_CENTER, 1, 0, tabs_8,
+	       YES, YES, 1, 0, 0);
+
+static HTStyle HTStyleHeadingLeft =
+HTStyleInit(
+	       &HTStyleHeadingCenter, HeadingLeft, "HLEFT",
+	       HT_FONT + HT_BOLD, 1, HT_BLACK, 0, 0,
+	       0, 0, 3, HT_LEFT, 1, 0, tabs_8,
+	       YES, YES, 1, 0, 0);
+
+static HTStyle HTStyleHeadingRight =
+HTStyleInit(
+	       &HTStyleHeadingLeft, HeadingRight, "HRIGHT",
+	       HT_FONT + HT_BOLD, 1, HT_BLACK, 0, 0,
+	       0, 0, 3, HT_RIGHT, 1, 0, tabs_8,
+	       YES, YES, 1, 0, 0);
+
+/* Style sheet points to the last in the list:
+*/
+static HTStyleSheet sheet =
+{"default.style",
+ &HTStyleHeadingRight};		/* sheet */
+
+static HTStyle *st_array[ST_HeadingRight + 1] =
+{NULL};
+
+static HTStyleSheet *result = NULL;
+
+#ifdef LY_FIND_LEAKS
+static void FreeDefaultStyle(void)
+{
+    HTStyle *style;
+
+    while ((style = result->styles) != 0) {
+	result->styles = style->next;
+	FREE(style);
+    }
+    FREE(result);
+}
+#endif /* LY_FIND_LEAKS */
+
+HTStyleSheet *DefaultStyle(HTStyle ***result_array)
+{
+    HTStyle *p, *q;
+
+    /*
+     * The first time we're called, allocate a copy of the 'sheet' linked
+     * list.  Thereafter, simply copy the data from 'sheet' into our copy
+     * (preserving the copy's linked-list pointers).  We do this to reset the
+     * parameters of a style that might be altered while processing a page.
+     */
+    if (result == 0) {		/* allocate & copy */
+	result = HTStyleSheetNew();
+	*result = sheet;
+	result->styles = 0;
+#ifdef LY_FIND_LEAKS
+	atexit(FreeDefaultStyle);
+#endif
+	for (p = sheet.styles; p != 0; p = p->next) {
+	    q = HTStyleNew();
+	    *q = *p;
+	    if (no_margins) {
+		q->indent1st = 0;
+		q->leftIndent = 0;
+		q->rightIndent = 0;
+	    }
+	    st_array[q->id] = q;
+	    q->next = result->styles;
+	    result->styles = q;
+	}
+    } else {			/* recopy the data */
+	for (q = result->styles, p = sheet.styles;
+	     p != 0 && q != 0;
+	     p = p->next, q = q->next) {
+	    HTStyle *r = q->next;
+
+	    *q = *p;
+	    if (no_margins) {
+		q->indent1st = 0;
+		q->leftIndent = 0;
+		q->rightIndent = 0;
+	    }
+	    st_array[q->id] = q;
+	    q->next = r;
+	}
+    }
+    *result_array = st_array;
+    return result;
+}
diff --git a/src/GridText.c b/src/GridText.c
new file mode 100644
index 00000000..cfbd4f81
--- /dev/null
+++ b/src/GridText.c
@@ -0,0 +1,14668 @@
+/*
+ * $LynxId: GridText.c,v 1.185 2010/05/02 22:28:14 tom Exp $
+ *
+ *		Character grid hypertext object
+ *		===============================
+ */
+
+#include <HTUtils.h>
+#include <HTString.h>
+#include <HTAccess.h>
+#include <HTAnchor.h>
+#include <HTParse.h>
+#include <HTTP.h>
+#include <HTAlert.h>
+#include <HTCJK.h>
+#include <HTFile.h>
+#include <UCDefs.h>
+#include <UCAux.h>
+#include <HText.h>
+
+#include <assert.h>
+
+#include <GridText.h>
+#include <LYCurses.h>
+#include <LYUtils.h>
+#include <LYStrings.h>
+#include <LYStructs.h>
+#include <LYGlobalDefs.h>
+#include <LYGetFile.h>
+#include <LYClean.h>
+#include <LYMail.h>
+#include <LYList.h>
+#include <LYCharSets.h>
+#include <LYCharUtils.h>	/* LYUCTranslateBack... */
+#include <UCMap.h>
+#include <LYEdit.h>
+#include <LYPrint.h>
+#include <LYPrettySrc.h>
+#include <TRSTable.h>
+#include <LYHistory.h>
+#ifdef EXP_CHARTRANS_AUTOSWITCH
+#include <UCAuto.h>
+#endif /* EXP_CHARTRANS_AUTOSWITCH */
+
+#include <LYexit.h>
+#include <LYLeaks.h>
+
+/*#define DEBUG_APPCH 1*/
+/*#define DEBUG_STYLE 1*/
+
+#ifdef DEBUG_STYLE
+#define CTRACE_STYLE(p) CTRACE2(TRACE_STYLE, p)
+#else
+#define CTRACE_STYLE(p)		/* nothing */
+#endif
+
+#ifdef USE_COLOR_STYLE
+#include <AttrList.h>
+#include <LYHash.h>
+#include <LYStyle.h>
+
+#endif
+
+#include <LYJustify.h>
+
+#ifdef CONV_JISX0201KANA_JISX0208KANA
+#define is_CJK2(b) (IS_CJK_TTY && is8bits(UCH(b)))
+#else
+#define is_CJK2(b) (IS_CJK_TTY && is8bits(UCH(b)) && kanji_code != SJIS)
+#endif
+
+#ifdef USE_CURSES_PADS
+#  define DISPLAY_COLS    (LYwideLines ? MAX_COLS : LYcols)
+#  define WRAP_COLS(text) ((text)->stbl ?				\
+			   (LYtableCols <= 0				\
+			    ? DISPLAY_COLS				\
+			    : (LYtableCols * LYcols)/12) - LYbarWidth	\
+			   : LYcolLimit)
+#else
+#  define DISPLAY_COLS    LYcols
+#  define WRAP_COLS(text) LYcolLimit
+#endif
+
+#define FirstHTLine(text) ((text)->last_line->next)
+#define LastHTLine(text)  ((text)->last_line)
+
+static void HText_trimHightext(HText *text, BOOLEAN final,
+			       int stop_before);
+
+struct _HTStream {		/* only know it as object */
+    const HTStreamClass *isa;
+    /* ... */
+};
+
+#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)))
+
+#ifdef KANJI_CODE_OVERRIDE
+HTkcode last_kcode = NOKANJI;	/* 1997/11/14 (Fri) 09:09:26 */
+#endif
+
+#ifdef CJK_EX
+#define CHAR_WIDTH 6
+#else
+#define CHAR_WIDTH 1
+#endif
+
+/*	Exports
+*/
+HText *HTMainText = NULL;	/* Equivalent of main window */
+HTParentAnchor *HTMainAnchor = NULL;	/* Anchor for HTMainText */
+
+const char *HTAppName = LYNX_NAME;	/* Application name */
+const char *HTAppVersion = LYNX_VERSION;	/* Application version */
+
+static int HTFormNumber = 0;
+static int HTFormFields = 0;
+static char *HTCurSelectGroup = NULL;	/* Form select group name */
+static int HTCurSelectGroupCharset = -1;	/* ... and name's charset */
+int HTCurSelectGroupType = F_RADIO_TYPE;	/* Group type */
+char *HTCurSelectGroupSize = NULL;	/* Length of select */
+static char *HTCurSelectedOptionValue = NULL;	/* Select choice */
+
+const char *checked_box = "[X]";
+const char *unchecked_box = "[ ]";
+const char *checked_radio = "(*)";
+const char *unchecked_radio = "( )";
+
+static BOOLEAN underline_on = OFF;
+static BOOLEAN bold_on = OFF;
+
+#ifdef USE_SOURCE_CACHE
+int LYCacheSource = SOURCE_CACHE_NONE;
+int LYCacheSourceForAborted = SOURCE_CACHE_FOR_ABORTED_DROP;
+#endif
+
+#ifdef USE_SCROLLBAR
+BOOLEAN LYShowScrollbar = FALSE;
+BOOLEAN LYsb_arrow = TRUE;
+int LYsb_begin = -1;
+int LYsb_end = -1;
+#endif
+
+#ifndef VMS			/* VMS has a better way - right? - kw */
+#define CHECK_FREE_MEM
+#endif
+
+#ifdef CHECK_FREE_MEM
+static void *LY_check_calloc(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.
+ */
+#ifndef ALIGN_SIZE
+#define ALIGN_SIZE      sizeof(double)
+#endif
+
+typedef struct {
+    unsigned int sc_direction:2;	/* on or off */
+    unsigned int sc_horizpos:14;	/* horizontal position of this change */
+    unsigned int sc_style:16;	/* which style to change to */
+} HTStyleChange;
+
+#if defined(USE_COLOR_STYLE)
+#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
+
+typedef HTStyleChange pool_data;
+
+enum {
+    POOL_SIZE = ((8192
+		  - 4 * sizeof(void *)
+		  - sizeof(struct _HTPool *)
+		  - sizeof(int))
+		 / sizeof(pool_data))
+};
+
+typedef struct _HTPool {
+    pool_data 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 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* 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 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
+objects that are freed in batch, that are not deallocated not in the batch, and
+not reallocated.
+ Pools greatly reduce memory fragmentation and memory allocation/deallocation
+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 stored using a cast.]
+
+ Pool is referenced by the pointer to the last chunk that contains free slots.
+Functions that allocate memory in the 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))
+
+/**************************************************************************/
+/*
+ * 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.
+ */
+static pool_data *ALLOC_IN_POOL(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;
+		*ppoolptr = newpool;
+	    }
+	}
+    }
+    return ptr;
+}
+
+/*
+ * Returns a pointer to initialized pool of type 'HTPool', or NULL if fails.
+ */
+static HTPool *POOL_NEW(void)
+{
+    HTPool *poolptr = (HTPool *) LY_CALLOC(1, sizeof(HTPool));
+
+    if (poolptr) {
+	poolptr->prev = NULL;
+	poolptr->used = 0;
+    }
+    return poolptr;
+}
+
+/*
+ * Frees a pool of type 'HTPool' pointed by poolptr.
+ */
+static void POOL_FREE(HTPool * poolptr)
+{
+    HTPool *cur = poolptr;
+    HTPool *prev;
+
+    while (cur) {
+	prev = cur->prev;
+	free(cur);
+	cur = prev;
+    }
+}
+
+/**************************************************************************/
+/**************************************************************************/
+
+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(size) (sizeof(HTLine)+(size))		/* Allow for terminator */
+
+#ifndef HTLINE_NOT_IN_POOL
+#define HTLINE_NOT_IN_POOL 0	/* debug with this set to 1 */
+#endif
+
+#if HTLINE_NOT_IN_POOL
+#define allocHTLine(ptr, size)  { ptr = (HTLine *)calloc(1, LINE_SIZE(size)); }
+#define freeHTLine(self, ptr)   { \
+	if (ptr && ptr != TEMP_LINE(self, 0) && ptr != TEMP_LINE(self, 1)) \
+	    FREE(ptr); \
+    }
+#else
+#define allocHTLine(ptr, size)  POOLallocHTLine(ptr, size)
+#define freeHTLine(self, ptr)   {}
+#endif
+
+/*
+ * 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;
+    struct _TextAnchor *prev;	/* www_user_search only! */
+    int sgml_offset;		/* used for updating position after reparsing */
+    int number;			/* For user interface */
+    int line_num;		/* Place in document */
+    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;
+
+typedef struct {
+    char *name;			/* ID value of TAB */
+    int column;			/* Zero-based column value */
+} HTTabID;
+
+typedef enum {
+    S_text,
+    S_esc,
+    S_dollar,
+    S_paren,
+    S_nonascii_text,
+    S_dollar_paren,
+    S_jisx0201_text
+} eGridState;			/* Escape sequence? */
+
+#ifdef USE_TH_JP_AUTO_DETECT
+typedef enum {			/* Detected Kanji code */
+    DET_SJIS,
+    DET_EUC,
+    DET_NOTYET,
+    DET_MIXED
+} eDetectedKCode;
+
+typedef enum {
+    SJIS_state_neutral,
+    SJIS_state_in_kanji,
+    SJIS_state_has_bad_code
+} eSJIS_status;
+
+typedef enum {
+    EUC_state_neutral,
+    EUC_state_in_kanji,
+    EUC_state_in_kana,
+    EUC_state_has_bad_code
+} eEUC_status;
+#endif
+
+/*	Notes on struct _HText:
+ *	next_line is valid if stale 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;
+    HTLineTemp temp_line[2];
+    int Lines;			/* Number of them */
+    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? */
+    BOOL toolbar;		/* Toolbar set? */
+    HTList *tabs;		/* TAB IDs */
+    HTList *hidden_links;	/* Content-less links ... */
+    int hiddenlinkflag;		/*  ... and how to treat them */
+    BOOL no_cache;		/* Always refresh? */
+    char LastChar;		/* For absorbing white space */
+
+/* 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 */
+    unsigned permissible_split;	/* in last line */
+    BOOL in_line_1;		/* of paragraph */
+    BOOL stale;			/* Must refresh */
+    BOOL page_has_target;	/* has target on screen */
+    BOOL has_utf8;		/* has utf-8 on screen or line */
+    BOOL had_utf8;		/* had utf-8 when last displayed */
+#ifdef DISP_PARTIAL
+    int first_lineno_last_disp_partial;
+    int last_lineno_last_disp_partial;
+#endif
+    STable_info *stbl;
+    HTList *enclosed_stbl;
+
+    HTkcode kcode;		/* Kanji code? */
+    HTkcode specified_kcode;	/* Specified Kanji code */
+#ifdef USE_TH_JP_AUTO_DETECT
+    eDetectedKCode detected_kcode;
+    eSJIS_status SJIS_status;
+    eEUC_status EUC_status;
+#endif
+    eGridState 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 */
+
+    HTPool *pool;		/* this HText memory pool */
+
+#ifdef USE_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
+};
+
+/* exported */
+void *HText_pool_calloc(HText *text, unsigned size)
+{
+    return (void *) ALLOC_IN_POOL(&text->pool, size);
+}
+
+static void HText_AddHiddenLink(HText *text, TextAnchor *textanchor);
+
+#ifdef USE_JUSTIFY_ELTS
+BOOL can_justify_here;
+BOOL can_justify_here_saved;
+
+BOOL can_justify_this_line;	/* =FALSE if line contains form objects */
+int wait_for_this_stacked_elt;	/* -1 if can justify contents of the
+
+				   element on the op of stack. If positive - specifies minimal stack depth
+				   plus 1 at which we can justify element (can be MAX_LINE+2 if
+				   ok_justify ==FALSE or in psrcview. */
+BOOL form_in_htext;		/*to indicate that we are in form (since HTML_FORM is
+
+				   not stacked in the HTML.c */
+BOOL in_DT = FALSE;
+
+#ifdef DEBUG_JUSTIFY
+BOOL can_justify_stack_depth;	/* can be 0 or 1 if all code is correct */
+#endif
+
+typedef struct {
+    int byte_len;		/*length in bytes */
+    int cell_len;		/*length in cells */
+} ht_run_info;
+
+static int justify_start_position;	/* this is an index of char from which
+
+					   justification can start (eg after "* " preceeding <li> text) */
+
+static int ht_num_runs;		/*the number of runs filled */
+static ht_run_info ht_runs[MAX_LINE];
+static BOOL this_line_was_split;
+static TextAnchor *last_anchor_of_previous_line;
+static BOOL have_raw_nbsps = FALSE;
+
+void ht_justify_cleanup(void)
+{
+    wait_for_this_stacked_elt = !ok_justify
+#  ifdef USE_PRETTYSRC
+	|| psrc_view
+#  endif
+	? 30000 /*MAX_NESTING */  + 2	/*some unreachable value */
+	: -1;
+    can_justify_here = TRUE;
+    can_justify_this_line = TRUE;
+    form_in_htext = FALSE;
+
+    last_anchor_of_previous_line = NULL;
+    this_line_was_split = FALSE;
+    in_DT = FALSE;
+    have_raw_nbsps = FALSE;
+}
+
+void mark_justify_start_position(void *text)
+{
+    if (text && ((HText *) text)->last_line)
+	justify_start_position = ((HText *) text)->last_line->size;
+}
+
+#define REALLY_CAN_JUSTIFY(text) ( (wait_for_this_stacked_elt<0) && \
+	( text->style->alignment == HT_LEFT     || \
+	  text->style->alignment == HT_JUSTIFY) && \
+	!IS_CJK_TTY && !in_DT && \
+	can_justify_here && can_justify_this_line && !form_in_htext )
+
+#endif /* USE_JUSTIFY_ELTS */
+
+/*
+ * Boring static variable used for moving cursor across
+ */
+#define UNDERSCORES(n) \
+ ((n) >= MAX_LINE ? underscore_string : &underscore_string[(MAX_LINE-1)] - (n))
+
+/*
+ *	Memory leak fixed.
+ *	05-29-94 Lynx 2-3-1 Garrett Arch Blythe
+ *	Changed to arrays.
+ */
+static char underscore_string[MAX_LINE + 1];
+char star_string[MAX_LINE + 1];
+
+static int ctrl_chars_on_this_line = 0;		/* num of ctrl chars in current line */
+static int utfxtra_on_this_line = 0;	/* num of UTF-8 extra bytes in line,
+
+					   they *also* count as ctrl chars. */
+#ifdef WIDEC_CURSES
+#define UTFXTRA_ON_THIS_LINE 0
+#else
+#define UTFXTRA_ON_THIS_LINE utfxtra_on_this_line
+#endif
+
+static HTStyle default_style =
+{0, NULL, "(Unstyled)", 0, NULL, "",
+ (HTFont) 0, 1, HT_BLACK, 0, 0,
+ 0, 0, 0, HT_LEFT, 1, 0, 0,
+ NO, NO, 0, 0, 0};
+
+static HTList *loaded_texts = NULL;	/* A list of all those in memory */
+HTList *search_queries = NULL;	/* isindex and whereis queries   */
+
+#ifdef LY_FIND_LEAKS
+static void free_all_texts(void);
+#endif
+
+static BOOL HText_TrueEmptyLine(HTLine *line, HText *text, BOOL IgnoreSpaces);
+
+static int HText_TrueLineSize(HTLine *line, HText *text, BOOL IgnoreSpaces);
+
+#ifdef CHECK_FREE_MEM
+
+/*
+ * text->halted = 1: have set fake 'Z' and output a message
+ *		  2: next time when HText_appendCharacter is called
+ *		     it will append *** MEMORY EXHAUSTED ***, then set
+ *		     to 3.
+ *		  3: normal text output will be suppressed (but not anchors,
+ *		     form fields etc.)
+ */
+static void HText_halt(void)
+{
+    if (HTFormNumber > 0)
+	HText_DisableCurrentForm();
+    if (!HTMainText)
+	return;
+    if (HTMainText->halted < 2)
+	HTMainText->halted = 2;
+}
+
+#define MIN_NEEDED_MEM 5000
+
+/*
+ * Check whether factor*min(bytes,MIN_NEEDED_MEM) is available,
+ * or bytes if factor is 0.
+ * MIN_NEEDED_MEM and factor together represent a security margin,
+ * to take account of all the memory allocations where we don't check
+ * and of buffers which may be emptied before HTCheckForInterupt()
+ * is (maybe) called and other things happening, with some chance of
+ * success.
+ * This just tries to malloc() the to-be-checked-for amount of memory,
+ * which might make the situation worse depending how allocation works.
+ * There should be a better way...  - kw
+ */
+static BOOL mem_is_avail(size_t factor, size_t bytes)
+{
+    void *p;
+
+    if (bytes < MIN_NEEDED_MEM && factor > 0)
+	bytes = MIN_NEEDED_MEM;
+    if (factor == 0)
+	factor = 1;
+    p = malloc(factor * bytes);
+    if (p) {
+	FREE(p);
+	return YES;
+    } else {
+	return NO;
+    }
+}
+
+/*
+ * Replacement for calloc which checks for "enough" free memory
+ * (with some security margins) and tries various recovery actions
+ * if deemed necessary.  - kw
+ */
+static void *LY_check_calloc(size_t nmemb, size_t size)
+{
+    int i, n;
+
+    if (mem_is_avail(4, nmemb * size)) {
+	return (calloc(nmemb, size));
+    }
+    n = HTList_count(loaded_texts);
+    for (i = n - 1; i > 0; i--) {
+	HText *t = (HText *) HTList_objectAt(loaded_texts, i);
+
+	CTRACE((tfp,
+		"\nBUG *** Emergency freeing document %d/%d for '%s'%s!\n",
+		i + 1, n,
+		((t && t->node_anchor &&
+		  t->node_anchor->address) ?
+		 t->node_anchor->address : "unknown anchor"),
+		((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)) {
+	    return (calloc(nmemb, size));
+	}
+    }
+    LYFakeZap(YES);
+    if (!HTMainText || HTMainText->halted <= 1) {
+	if (!mem_is_avail(2, nmemb * size)) {
+	    HText_halt();
+	    if (mem_is_avail(0, 700)) {
+		HTAlert(gettext("Memory exhausted, display interrupted!"));
+	    }
+	} else {
+	    if ((!HTMainText || HTMainText->halted == 0) &&
+		mem_is_avail(0, 700)) {
+		HTAlert(gettext("Memory exhausted, will interrupt transfer!"));
+		if (HTMainText)
+		    HTMainText->halted = 1;
+	    }
+	}
+    }
+    return (calloc(nmemb, size));
+}
+
+#endif /* CHECK_FREE_MEM */
+
+#ifdef USE_COLOR_STYLE
+/*
+ * Color style information is stored with the multibyte-character offset into
+ * the string at which the style would apply.  Compute the corresponding column
+ * so we can compare it with the updated column value after writing strings
+ * with curses.
+ *
+ * The offsets count multibyte characters.  Other parts of the code assume each
+ * character uses one cell, but some CJK (or UTF-8) codes use two cells.  We
+ * need to know the number of cells.
+ */
+static int StyleToCols(HText *text, HTLine *line, int nstyle)
+{
+    int result = line->offset;	/* this much is spaces one byte/cell */
+    int nchars = line->styles[nstyle].sc_horizpos;
+    char *data = line->data;
+    char *last = line->size + data;
+    int utf_extra;
+
+    while (nchars > 0 && data < last) {
+	if (IsSpecialAttrChar(*data) && *data != LY_SOFT_NEWLINE) {
+	    ++data;
+	} else {
+	    utf_extra = utf8_length(text->T.output_utf8, data);
+	    if (utf_extra++) {
+		result += LYstrExtent(data, utf_extra, 2);
+		data += utf_extra;
+	    } else if (is_CJK2(*data)) {
+		data += 2;
+		result += 2;
+	    } else {
+		++data;
+		++result;
+	    }
+	    --nchars;
+	}
+    }
+
+    return result;
+}
+#endif
+
+/*
+ * Clear highlight information for a given anchor
+ * (text was allocated in the pool).
+ */
+static void LYClearHiText(TextAnchor *a)
+{
+    FREE(a->lites.hl_info);
+
+    a->lites.hl_base.hl_text = NULL;
+    a->lites.hl_len = 0;
+}
+
+#define LYFreeHiText(a)     FREE((a)->lites.hl_info)
+
+/*
+ * Set the initial highlight information for a given anchor.
+ */
+static void LYSetHiText(TextAnchor *a,
+			const char *text,
+			int len)
+{
+    if (text != NULL) {
+	POOLallocstring(a->lites.hl_base.hl_text, len + 1);
+	memcpy(a->lites.hl_base.hl_text, text, len);
+	*(a->lites.hl_base.hl_text + len) = '\0';
+
+	a->lites.hl_len = 1;
+    }
+}
+
+/*
+ * Add highlight information for the next line of a anchor.
+ */
+static void LYAddHiText(TextAnchor *a,
+			const 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) + 1);
+    strcpy(have[need].hl_text, text);
+    have[need].hl_x = x;
+}
+
+/*
+ * Return an offset to skip leading blanks in the highlighted link.  That is
+ * needed to avoid having the color-style paint the leading blanks.
+ */
+#ifdef USE_COLOR_STYLE
+static int LYAdjHiTextPos(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 != 0) ? (LYSkipBlanks(result) - result) : 0;
+}
+#else
+#define LYAdjHiTextPos(a,count) 0
+#endif
+
+/*
+ * Get the highlight text, counting from zero.
+ */
+static char *LYGetHiTextStr(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;
+    result += LYAdjHiTextPos(a, count);
+    return result;
+}
+
+/*
+ * Get the X-ordinate at which to draw the corresponding highlight-text
+ */
+static int LYGetHiTextPos(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;
+    result += LYAdjHiTextPos(a, count);
+    return result;
+}
+
+/*
+ * Copy highlighting information from anchor 'b' to 'a'.
+ */
+static void LYCopyHiText(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));
+	}
+    }
+}
+
+static void HText_getChartransInfo(HText *me)
+{
+    me->UCLYhndl = HTAnchor_getUCLYhndl(me->node_anchor, UCT_STAGE_HTEXT);
+    if (me->UCLYhndl < 0) {
+	int chndl = current_char_set;
+
+	HTAnchor_setUCInfoStage(me->node_anchor, chndl,
+				UCT_STAGE_HTEXT, UCT_SETBY_STRUCTURED);
+	me->UCLYhndl = HTAnchor_getUCLYhndl(me->node_anchor,
+					    UCT_STAGE_HTEXT);
+    }
+    me->UCI = HTAnchor_getUCInfoStage(me->node_anchor, UCT_STAGE_HTEXT);
+}
+
+static void PerFormInfo_free(PerFormInfo * form)
+{
+    if (form) {
+	FREE(form->accept_cs);
+	FREE(form->thisacceptcs);
+	FREE(form);
+    }
+}
+
+static void free_form_fields(FormInfo * input_field)
+{
+    /*
+     * Free form fields.
+     */
+    if (input_field->type == F_OPTION_LIST_TYPE &&
+	input_field->select_list != NULL) {
+	/*
+	 * Free off option lists if present.
+	 * It should always be present for F_OPTION_LIST_TYPE
+	 * unless we had invalid markup which prevented
+	 * HText_setLastOptionValue from finishing its job
+	 * and left the input field in an insane state.  - kw
+	 */
+	OptionType *optptr = input_field->select_list;
+	OptionType *tmp;
+
+	while (optptr) {
+	    tmp = optptr;
+	    optptr = tmp->next;
+	    FREE(tmp->name);
+	    FREE(tmp->cp_submit_value);
+	    FREE(tmp);
+	}
+	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.
+	 */
+	input_field->value = NULL;
+	input_field->orig_value = NULL;
+	input_field->cp_submit_value = NULL;
+	input_field->orig_submit_value = NULL;
+    } else {
+	FREE(input_field->value);
+	FREE(input_field->orig_value);
+	FREE(input_field->cp_submit_value);
+	FREE(input_field->orig_submit_value);
+    }
+    FREE(input_field->name);
+    FREE(input_field->submit_action);
+    FREE(input_field->submit_enctype);
+    FREE(input_field->submit_title);
+
+    FREE(input_field->accept_cs);
+}
+
+static void FormList_delete(HTList *forms)
+{
+    HTList *cur = forms;
+    PerFormInfo *form;
+
+    while ((form = (PerFormInfo *) HTList_nextObject(cur)) != NULL)
+	PerFormInfo_free(form);
+    HTList_delete(forms);
+}
+
+#ifdef DISP_PARTIAL
+static void ResetPartialLinenos(HText *text)
+{
+    if (text != 0) {
+	text->first_lineno_last_disp_partial = -1;
+	text->last_lineno_last_disp_partial = -1;
+    }
+}
+#endif
+
+/*			Creation Method
+ *			---------------
+ */
+HText *HText_new(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 = typecalloc(HText);
+
+    if (!self)
+	return self;
+
+    CTRACE((tfp, "GridText: start HText_new\n"));
+
+#if defined(VMS) && defined (VAXC) && !defined(__DECC)
+    status = lib$stat_vm(&VMType, &VMTotal);
+    CTRACE((tfp, "GridText: VMTotal = %d\n", VMTotal));
+#endif /* VMS && VAXC && !__DECC */
+
+    /*
+     * If the previously shown text had UTF-8 characters on screen,
+     * remember this in the newly created object.  Do this now, before
+     * the previous object may become invalid.  - kw
+     */
+    if (HTMainText) {
+	if (HText_hasUTF8OutputSet(HTMainText) &&
+	    HTLoadedDocumentEightbit() &&
+	    IS_UTF8_TTY) {
+	    self->had_utf8 = HTMainText->has_utf8;
+	} else {
+	    self->had_utf8 = HTMainText->has_utf8;
+	}
+	HTMainText->has_utf8 = NO;
+    }
+
+    if (!loaded_texts) {
+	loaded_texts = HTList_new();
+#ifdef LY_FIND_LEAKS
+	atexit(free_all_texts);
+#endif
+    }
+
+    /*
+     * 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(), 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);
+	CTRACE((tfp, "GridText: Auto-uncaching\n"));
+
+	HTAnchor_delete_links(anchor);
+	((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 */
+    {
+	CTRACE((tfp, "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);
+	CTRACE((tfp, "GridText: VMTotal reduced to %d\n", VMTotal));
+#endif /* VMS && VAXC && !__DECC */
+    }
+
+    self->pool = POOL_NEW();
+    if (!self->pool)
+	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];
+#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;
+    self->last_anchor_number = 0;	/* Numbering of them for references */
+    self->stale = YES;
+    self->toolbar = NO;
+    self->tabs = NULL;
+#ifdef USE_SOURCE_CACHE
+    /*
+     * Remember the parse settings.
+     */
+    self->clickable_images = clickable_images;
+    self->pseudo_inline_alts = pseudo_inline_alts;
+    self->verbose_img = verbose_img;
+    self->raw_mode = LYUseDefaultRawMode;
+    self->historical_comments = historical_comments;
+    self->minimal_comments = minimal_comments;
+    self->soft_dquotes = soft_dquotes;
+    self->old_dtd = Old_DTD;
+    self->keypad_mode = keypad_mode;
+    self->disp_lines = LYlines;
+    self->disp_cols = DISPLAY_COLS;
+#endif
+    /*
+     * If we are going to render the List Page, always merge in hidden
+     * links to get the numbering consistent if form fields are numbered
+     * and show up as hidden links in the list of links.
+     * If we are going to render a bookmark file, also always merge in
+     * hidden links, to get the link numbers consistent with the counting
+     * in remove_bookmark_link().  Normally a bookmark file shouldn't
+     * contain any entries with empty titles, but it might happen.  - kw
+     */
+    if (anchor->bookmark ||
+	LYIsUIPage3(anchor->address, UIP_LIST_PAGE, 0) ||
+	LYIsUIPage3(anchor->address, UIP_ADDRLIST_PAGE, 0))
+	self->hiddenlinkflag = HIDDENLINKS_MERGE;
+    else
+	self->hiddenlinkflag = LYHiddenLinks;
+    self->hidden_links = NULL;
+    self->no_cache = (BOOLEAN) ((anchor->no_cache ||
+				 anchor->post_data)
+				? YES
+				: NO);
+    self->LastChar = '\0';
+
+#ifndef USE_PRETTYSRC
+    if (HTOutputFormat == WWW_SOURCE)
+	self->source = YES;
+    else
+	self->source = NO;
+#else
+    /* mark_htext_as_source == TRUE if we are parsing html file (and psrc_view
+     * is set temporary to false at creation time)
+     *
+     * psrc_view == TRUE if source of the text produced by some lynx module
+     * (like ftp browsers) is requested).  - VH
+     */
+    self->source = (BOOL) (LYpsrc
+			   ? mark_htext_as_source || psrc_view
+			   : HTOutputFormat == WWW_SOURCE);
+    mark_htext_as_source = FALSE;
+#endif
+    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->specified_kcode = NOKANJI;
+#ifdef USE_TH_JP_AUTO_DETECT
+    self->detected_kcode = DET_NOTYET;
+    self->SJIS_status = SJIS_state_neutral;
+    self->EUC_status = EUC_state_neutral;
+#endif
+    self->state = S_text;
+    self->kanji_buf = '\0';
+    self->in_sjis = 0;
+    self->have_8bit_chars = NO;
+    HText_getChartransInfo(self);
+    UCSetTransParams(&self->T,
+		     self->UCLYhndl, self->UCI,
+		     current_char_set,
+		     &LYCharSet_UC[current_char_set]);
+
+    /*
+     * Check the kcode setting if the anchor has a charset element.  -FM
+     */
+    HText_setKcode(self, anchor->charset,
+		   HTAnchor_getUCInfoStage(anchor, UCT_STAGE_HTEXT));
+
+    /*
+     * 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] != '.') {
+	/*
+	 * Create an array of dots for the UNDERSCORES macro.  -FM
+	 */
+	memset(underscore_string, '.', (MAX_LINE - 1));
+	underscore_string[(MAX_LINE - 1)] = '\0';
+	underscore_string[MAX_LINE] = '\0';
+	/*
+	 * Create an array of underscores for the STARS macro.  -FM
+	 */
+	memset(star_string, '_', (MAX_LINE - 1));
+	star_string[(MAX_LINE - 1)] = '\0';
+	star_string[MAX_LINE] = '\0';
+    }
+
+    underline_on = FALSE;	/* reset */
+    bold_on = FALSE;
+
+#ifdef DISP_PARTIAL
+    /*
+     * By this function we create HText object
+     * so we may start displaying the document while downloading. - LP
+     */
+    if (display_partial_flag) {
+	display_partial = TRUE;	/* enable HTDisplayPartial() */
+	NumOfLines_partial = 0;	/* initialize */
+    }
+
+    /*
+     * These two fields should only be set to valid line numbers
+     * by calls of display_page during partial displaying.  This
+     * is just so that the FIRST display_page AFTER that can avoid
+     * repainting the same lines on the screen.  - kw
+     */
+    ResetPartialLinenos(self);
+#endif
+
+#ifdef USE_JUSTIFY_ELTS
+    ht_justify_cleanup();
+#endif
+    return self;
+}
+
+/*			Creation Method 2
+ *			---------------
+ *
+ *      Stream is assumed open and left open.
+ */
+HText *HText_new2(HTParentAnchor *anchor,
+		  HTStream *stream)
+{
+    HText *result = HText_new(anchor);
+
+    if (stream) {
+	result->target = stream;
+	result->targetClass = *stream->isa;	/* copy action procedures */
+    }
+    return result;
+}
+
+/*	Free Entire Text
+ *	----------------
+ */
+void HText_free(HText *self)
+{
+    if (!self)
+	return;
+
+#if HTLINE_NOT_IN_POOL
+    {
+	HTLine *f = FirstHTLine(self);
+	HTLine *l = self->last_line;
+
+	while (l != f) {	/* Free off line array */
+	    self->last_line = l->prev;
+	    freeHTLine(self, l);
+	    l = self->last_line;
+	}
+	freeHTLine(self, f);
+    }
+#endif
+
+    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(l->input_field);
+	}
+
+	LYFreeHiText(l);
+    }
+    FormList_delete(self->forms);
+
+    /*
+     * 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);
+	    FREE(Tab);
+	}
+	HTList_delete(self->tabs);
+	self->tabs = NULL;
+    }
+
+    /*
+     * Free the hidden links list.  -FM
+     */
+    if (self->hidden_links) {
+	LYFreeStringList(self->hidden_links);
+	self->hidden_links = NULL;
+    }
+
+    /*
+     * Invoke HTAnchor_delete() to free the node_anchor
+     * if it is not a destination of other links.  -FM
+     */
+    if (self->node_anchor) {
+	HTAnchor_resetUCInfoStage(self->node_anchor, -1, UCT_STAGE_STRUCTURED,
+				  UCT_SETBY_NONE);
+	HTAnchor_resetUCInfoStage(self->node_anchor, -1, UCT_STAGE_HTEXT,
+				  UCT_SETBY_NONE);
+#ifdef USE_SOURCE_CACHE
+	/* Remove source cache files and chunks always, even if the
+	 * HTAnchor_delete call does not actually remove the anchor.
+	 * Keeping them would just be a waste of space - they won't
+	 * be used any more after the anchor has been disassociated
+	 * from a HText structure. - kw
+	 */
+	HTAnchor_clearSourceCache(self->node_anchor);
+#endif
+
+	HTAnchor_delete_links(self->node_anchor);
+
+	HTAnchor_setDocument(self->node_anchor, (HyperDoc *) 0);
+
+	if (HTAnchor_delete(self->node_anchor->parent))
+	    /*
+	     * Make sure HTMainAnchor won't point
+	     * to an invalid structure.  - KW
+	     */
+	    HTMainAnchor = NULL;
+    }
+
+    POOL_FREE(self->pool);
+    FREE(self);
+}
+
+/*		Display Methods
+ *		---------------
+ */
+
+/*	Output a line
+ *	-------------
+ */
+static int display_line(HTLine *line,
+			HText *text,
+			int scrline GCC_UNUSED,
+			const char *target GCC_UNUSED)
+{
+    register int i, j;
+    char buffer[7];
+    char *data;
+    size_t utf_extra = 0;
+    char LastDisplayChar = ' ';
+
+#ifdef USE_COLOR_STYLE
+    int current_style = 0;
+
+#define inunderline NO
+#define inbold NO
+#else
+    BOOL inbold = NO, inunderline = NO;
+#endif
+#if defined(SHOW_WHEREIS_TARGETS) && !defined(USE_COLOR_STYLE)
+    const char *cp_tgt;
+    int i_start_tgt = 0, i_after_tgt;
+    int HitOffset, LenNeeded;
+    BOOL intarget = NO;
+
+#else
+#define intarget NO
+#endif /* SHOW_WHEREIS_TARGETS && !USE_COLOR_STYLE */
+
+#if !(defined(NCURSES_VERSION) || defined(WIDEC_CURSES))
+    text->has_utf8 = NO;	/* use as per-line flag, except with ncurses */
+#endif
+
+#if defined(WIDEC_CURSES)
+    /*
+     * FIXME: this should not be necessary, but in some wide-character pages
+     * the output line wraps, foiling our attempt to just use newlines to
+     * advance to the next page.
+     */
+    LYmove(scrline + TITLE_LINES - 1, 0);
+#endif
+
+    /*
+     * Set up the multibyte character buffer,
+     * and clear the line to which we will be
+     * writing.
+     */
+    buffer[0] = buffer[1] = buffer[2] = '\0';
+    LYclrtoeol();
+
+    /*
+     * Add offset, making sure that we do not
+     * go over the COLS limit on the display.
+     */
+    j = (int) line->offset;
+    if (j >= DISPLAY_COLS)
+	j = DISPLAY_COLS - 1;
+#ifdef USE_SLANG
+    SLsmg_forward(j);
+    i = j;
+#else
+#ifdef USE_COLOR_STYLE
+    if (line->size == 0)
+	i = j;
+    else
+#endif
+	for (i = 0; i < j; i++)
+	    LYaddch(' ');
+#endif /* USE_SLANG */
+
+    /*
+     * Add the data, making sure that we do not
+     * go over the COLS limit on the display.
+     */
+    data = line->data;
+    i++;
+
+#ifndef USE_COLOR_STYLE
+#if defined(SHOW_WHEREIS_TARGETS)
+    /*
+     * If the target is on this line, it will be emphasized.
+     */
+    i_after_tgt = i;
+    if (target) {
+	cp_tgt = LYno_attr_mb_strstr(data,
+				     target,
+				     text->T.output_utf8, YES,
+				     &HitOffset,
+				     &LenNeeded);
+	if (cp_tgt) {
+	    if (((int) line->offset + LenNeeded) >= DISPLAY_COLS) {
+		cp_tgt = NULL;
+	    } else {
+		text->page_has_target = YES;
+		i_start_tgt = i + HitOffset;
+		i_after_tgt = i + LenNeeded;
+	    }
+	}
+    } else {
+	cp_tgt = NULL;
+    }
+#endif /* SHOW_WHEREIS_TARGETS */
+#endif /* USE_COLOR_STYLE */
+
+    while ((i <= DISPLAY_COLS) && ((buffer[0] = *data) != '\0')) {
+
+#ifndef USE_COLOR_STYLE
+#if defined(SHOW_WHEREIS_TARGETS)
+	if (cp_tgt && i >= i_after_tgt) {
+	    if (intarget) {
+		cp_tgt = LYno_attr_mb_strstr(data,
+					     target,
+					     text->T.output_utf8, YES,
+					     &HitOffset,
+					     &LenNeeded);
+		if (cp_tgt) {
+		    i_start_tgt = i + HitOffset;
+		    i_after_tgt = i + LenNeeded;
+		}
+		if (!cp_tgt || i_start_tgt != i) {
+		    LYstopTargetEmphasis();
+		    intarget = NO;
+		    if (inbold)
+			lynx_start_bold();
+		    if (inunderline)
+			lynx_start_underline();
+		}
+	    }
+	}
+#endif /* SHOW_WHEREIS_TARGETS */
+#endif /* USE_COLOR_STYLE */
+
+	data++;
+
+#if defined(USE_COLOR_STYLE)
+#define CStyle line->styles[current_style]
+
+	while (current_style < line->numstyles &&
+	       i >= (int) (CStyle.sc_horizpos + line->offset + 1)) {
+	    LynxChangeStyle(CStyle.sc_style, CStyle.sc_direction);
+	    current_style++;
+	}
+#endif
+	switch (buffer[0]) {
+
+#ifndef USE_COLOR_STYLE
+	case LY_UNDERLINE_START_CHAR:
+	    if (dump_output_immediately && use_underscore) {
+		LYaddch('_');
+		i++;
+	    } else {
+		inunderline = YES;
+		if (!intarget) {
+#if defined(PDCURSES)
+		    if (LYShowColor == SHOW_COLOR_NEVER)
+			lynx_start_bold();
+		    else
+			lynx_start_underline();
+#else
+		    lynx_start_underline();
+#endif /* PDCURSES */
+		}
+	    }
+	    break;
+
+	case LY_UNDERLINE_END_CHAR:
+	    if (dump_output_immediately && use_underscore) {
+		LYaddch('_');
+		i++;
+	    } else {
+		inunderline = NO;
+		if (!intarget) {
+#if defined(PDCURSES)
+		    if (LYShowColor == SHOW_COLOR_NEVER)
+			lynx_stop_bold();
+		    else
+			lynx_stop_underline();
+#else
+		    lynx_stop_underline();
+#endif /* PDCURSES */
+		}
+	    }
+	    break;
+
+	case LY_BOLD_START_CHAR:
+	    inbold = YES;
+	    if (!intarget)
+		lynx_start_bold();
+	    break;
+
+	case LY_BOLD_END_CHAR:
+	    inbold = NO;
+	    if (!intarget)
+		lynx_stop_bold();
+	    break;
+
+#endif /* !USE_COLOR_STYLE */
+	case LY_SOFT_NEWLINE:
+	    if (!dump_output_immediately) {
+		LYaddch('+');
+		i++;
+#if defined(SHOW_WHEREIS_TARGETS) && !defined(USE_COLOR_STYLE)
+		i_after_tgt++;
+#endif
+	    }
+	    break;
+
+	case LY_SOFT_HYPHEN:
+	    if (*data != '\0' ||
+		isspace(UCH(LastDisplayChar)) ||
+		LastDisplayChar == '-') {
+		/*
+		 * Ignore the soft hyphen if it is not the last character in
+		 * the line.  Also ignore it if is first character following
+		 * the margin, or if it is preceded by a white character (we
+		 * loaded 'M' into LastDisplayChar if it was a multibyte
+		 * character) or hyphen, though it should have been excluded by
+		 * HText_appendCharacter() or by split_line() in those cases. 
+		 * -FM
+		 */
+		break;
+	    } else {
+		/*
+		 * Make it a hard hyphen and fall through.  -FM
+		 */
+		buffer[0] = '-';
+	    }
+	    /* FALLTHRU */
+
+	default:
+#ifndef USE_COLOR_STYLE
+#if defined(SHOW_WHEREIS_TARGETS)
+	    if (!intarget && cp_tgt && i >= i_start_tgt) {
+		/*
+		 * Start the emphasis.
+		 */
+		if (data > cp_tgt) {
+		    LYstartTargetEmphasis();
+		    intarget = YES;
+		}
+	    }
+#endif /* SHOW_WHEREIS_TARGETS */
+#endif /* USE_COLOR_STYLE */
+	    if (text->T.output_utf8 && is8bits(buffer[0])) {
+		text->has_utf8 = YES;
+		utf_extra = utf8_length(text->T.output_utf8, data - 1);
+		LastDisplayChar = 'M';
+	    }
+	    if (utf_extra) {
+		strncpy(&buffer[1], data, utf_extra);
+		buffer[utf_extra + 1] = '\0';
+		LYaddstr(buffer);
+		buffer[1] = '\0';
+		data += utf_extra;
+		utf_extra = 0;
+	    } else if (is_CJK2(buffer[0])) {
+		/*
+		 * For CJK strings, by Masanobu Kimura.
+		 */
+		if (i <= DISPLAY_COLS) {
+		    buffer[1] = *data;
+		    buffer[2] = '\0';
+		    data++;
+		    i++;
+		    LYaddstr(buffer);
+		    buffer[1] = '\0';
+		    /*
+		     * For now, load 'M' into LastDisplayChar, but we should
+		     * check whether it's white and if so, use ' '.  I don't
+		     * know if there actually are white CJK characters, and
+		     * we're loading ' ' for multibyte spacing characters in
+		     * this code set, but this will become an issue when the
+		     * development code set's multibyte character handling is
+		     * used.  -FM
+		     */
+		    LastDisplayChar = 'M';
+#ifndef USE_SLANG
+		    {
+			int y, x;
+
+			getyx(LYwin, y, x);
+			if (x >= DISPLAY_COLS || x == 0)
+			    break;
+		    }
+#endif
+		}
+	    } else {
+		LYaddstr(buffer);
+		LastDisplayChar = buffer[0];
+	    }
+	    i++;
+	}			/* end of switch */
+    }				/* end of while */
+
+#if !(defined(NCURSES_VERSION) || defined(WIDEC_CURSES))
+    if (text->has_utf8) {
+	LYtouchline(scrline);
+	text->has_utf8 = NO;	/* we had some, but have dealt with it. */
+    }
+#endif
+    /*
+     * Add the return.
+     */
+    LYaddch('\n');
+
+#if defined(SHOW_WHEREIS_TARGETS) && !defined(USE_COLOR_STYLE)
+    if (intarget)
+	LYstopTargetEmphasis();
+#else
+#undef intarget
+#endif /* SHOW_WHEREIS_TARGETS && !USE_COLOR_STYLE */
+#ifndef USE_COLOR_STYLE
+    lynx_stop_underline();
+    lynx_stop_bold();
+#else
+    while (current_style < line->numstyles) {
+	LynxChangeStyle(CStyle.sc_style, CStyle.sc_direction);
+	current_style++;
+    }
+#undef CStyle
+#endif
+    return (0);
+}
+
+/*	Output the title line
+ *	---------------------
+ */
+static void display_title(HText *text)
+{
+    char *title = NULL;
+    char percent[20];
+    unsigned char *tmp = NULL;
+    int i = 0, j = 0;
+    int limit;
+
+#ifdef USE_COLOR_STYLE
+    int toolbar = 0;
+#endif
+
+    /*
+     * Make sure we have a text structure.  -FM
+     */
+    if (!text)
+	return;
+
+    lynx_start_title_color();
+#ifdef USE_COLOR_STYLE
+/* turn the TITLE style on */
+    if (last_colorattr_ptr > 0) {
+	LynxChangeStyle(s_title, STACK_ON);
+    } else {
+	LynxChangeStyle(s_title, ABS_ON);
+    }
+#endif /* USE_COLOR_STYLE */
+
+    /*
+     * Load the title field.  -FM
+     */
+    StrAllocCopy(title,
+		 (HTAnchor_title(text->node_anchor) ?
+		  HTAnchor_title(text->node_anchor) : " "));	/* "" -> " " */
+    LYReduceBlanks(title);
+
+    /*
+     * Generate the page indicator (percent) string.
+     */
+    limit = LYscreenWidth();
+    if (limit < 10) {
+	percent[0] = '\0';
+    } else if ((display_lines) <= 0 && LYlines > 0 &&
+	       text->top_of_screen <= 99999 && text->Lines <= 999999) {
+	sprintf(percent, " (l%d of %d)",
+		text->top_of_screen, text->Lines);
+    } else if ((text->Lines >= display_lines) && (display_lines > 0)) {
+	int total_pages = ((text->Lines + display_lines)
+			   / display_lines);
+	int start_of_last_page = ((text->Lines <= display_lines)
+				  ? 0
+				  : (text->Lines - 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 {
+	percent[0] = '\0';
+    }
+
+    /*
+     * Generate and display the title string, with page indicator
+     * if appropriate, preceded by the toolbar token if appropriate,
+     * and truncated if necessary.  -FM & KW
+     */
+    if (IS_CJK_TTY) {
+	if (*title &&
+	    (tmp = typecallocn(unsigned char, (strlen(title) * 2 + 256)))) {
+	    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] != CH_ESC) {	/* S/390 -- gil -- 1487 */
+			tmp[j++] = UCH(title[i]);
+		    }
+		}
+		tmp[j] = '\0';
+	    }
+	    StrAllocCopy(title, (const char *) tmp);
+	    FREE(tmp);
+	}
+    }
+    LYmove(0, 0);
+    LYclrtoeol();
+#if defined(SH_EX) && defined(KANJI_CODE_OVERRIDE)
+    LYaddstr(str_kcode(last_kcode));
+#endif
+    if (HText_hasToolbar(text)) {
+	LYaddch('#');
+#ifdef USE_COLOR_STYLE
+	toolbar = 1;
+#endif
+    }
+#ifdef USE_COLOR_STYLE
+    if (s_forw_backw != NOSTYLE && (nhist || nhist_extra > 1)) {
+	chtype c = nhist ? ACS_LARROW : ' ';
+
+	/* turn the FORWBACKW.ARROW style on */
+	LynxChangeStyle(s_forw_backw, STACK_ON);
+	if (nhist) {
+	    LYaddch(c);
+	    LYaddch(c);
+	    LYaddch(c);
+	} else
+	    LYmove(0, 3 + toolbar);
+	if (nhist_extra > 1) {
+	    LYaddch(ACS_RARROW);
+	    LYaddch(ACS_RARROW);
+	    LYaddch(ACS_RARROW);
+	}
+	LynxChangeStyle(s_forw_backw, STACK_OFF);
+    }
+#endif /* USE_COLOR_STYLE */
+#ifdef WIDEC_CURSES
+    i = limit - LYbarWidth - strlen(percent) - LYstrCells(title);
+    if (i <= 0) {		/* title is truncated */
+	i = limit - LYbarWidth - strlen(percent) - 3;
+	if (i <= 0) {		/* no room at all */
+	    title[0] = '\0';
+	} else {
+	    strcpy(title + LYstrFittable(title, i), "...");
+	}
+	i = 0;
+    }
+    LYmove(0, i);
+#else
+    i = (limit - 1) - strlen(percent) - strlen(title);
+    if (i >= CHAR_WIDTH) {
+	LYmove(0, i);
+    } else {
+	/*
+	 * Truncation takes into account the possibility that
+	 * multibyte characters might be present.  -HS (H.  Senshu)
+	 */
+	int last;
+
+	last = (int) strlen(percent) + CHAR_WIDTH;
+	if (limit - 3 >= last) {
+	    title[(limit - 3) - last] = '.';
+	    title[(limit - 2) - last] = '.';
+	    title[(limit - 1) - last] = '\0';
+	} else {
+	    title[(limit - 1) - last] = '\0';
+	}
+	LYmove(0, CHAR_WIDTH);
+    }
+#endif
+    LYaddstr(title);
+    if (percent[0] != '\0')
+	LYaddstr(percent);
+    LYaddch('\n');
+    FREE(title);
+
+#if defined(USE_COLOR_STYLE) && defined(CAN_CUT_AND_PASTE)
+    if (s_hot_paste != NOSTYLE) {	/* Only if the user set the style */
+	LYmove(0, LYcolLimit);
+	LynxChangeStyle(s_hot_paste, STACK_ON);
+	LYaddch(ACS_RARROW);
+	LynxChangeStyle(s_hot_paste, STACK_OFF);
+	LYmove(1, 0);		/* As after \n */
+    }
+#endif /* USE_COLOR_STYLE */
+
+#ifdef USE_COLOR_STYLE
+/* turn the TITLE style off */
+    LynxChangeStyle(s_title, STACK_OFF);
+#endif /* USE_COLOR_STYLE */
+    lynx_stop_title_color();
+
+    return;
+}
+
+/*	Output the scrollbar
+ *	---------------------
+ */
+#ifdef USE_SCROLLBAR
+static void display_scrollbar(HText *text)
+{
+    int i;
+    int h = display_lines - 2 * (LYsb_arrow != 0);	/* Height of the scrollbar */
+    int off = (LYsb_arrow != 0);	/* Start of the scrollbar */
+    int top_skip, bot_skip, sh, shown;
+
+    LYsb_begin = LYsb_end = -1;
+    if (!LYShowScrollbar || !text || h <= 2
+	|| text->Lines <= display_lines)
+	return;
+
+    if (text->top_of_screen >= text->Lines - display_lines) {
+	/* Only part of the screen shows actual text */
+	shown = text->Lines - text->top_of_screen;
+
+	if (shown <= 0)
+	    shown = 1;
+    } else
+	shown = display_lines;
+    /* Each cell of scrollbar represents text->Lines/h lines of text. */
+    /* Always smaller than h */
+    sh = (shown * h + text->Lines / 2) / text->Lines;
+    if (sh <= 0)
+	sh = 1;
+    if (sh >= h - 1)
+	sh = h - 2;		/* Position at ends indicates BEG and END */
+
+    if (text->top_of_screen == 0)
+	top_skip = 0;
+    else if (text->Lines - (text->top_of_screen + display_lines - 1) <= 0)
+	top_skip = h - sh;
+    else {
+	/* text->top_of_screen between 1 and text->Lines - display_lines
+	   corresponds to top_skip between 1 and h - sh - 1 */
+	/* Use rounding to get as many positions into top_skip==h - sh - 1
+	   as into top_skip == 1:
+	   1--->1, text->Lines - display_lines + 1--->h - sh. */
+	top_skip = (int) (1 +
+			  1. * (h - sh - 1) * text->top_of_screen
+			  / (text->Lines - display_lines + 1));
+    }
+    bot_skip = h - sh - top_skip;
+
+    LYsb_begin = top_skip;
+    LYsb_end = h - bot_skip;
+
+    if (LYsb_arrow) {
+#ifdef USE_COLOR_STYLE
+	int s = top_skip ? s_sb_aa : s_sb_naa;
+
+	if (last_colorattr_ptr > 0) {
+	    LynxChangeStyle(s, STACK_ON);
+	} else {
+	    LynxChangeStyle(s, ABS_ON);
+	}
+#endif /* USE_COLOR_STYLE */
+	LYmove(1, LYcolLimit + LYshiftWin);
+	addch_raw(ACS_UARROW);
+#ifdef USE_COLOR_STYLE
+	LynxChangeStyle(s, STACK_OFF);
+#endif /* USE_COLOR_STYLE */
+    }
+#ifdef USE_COLOR_STYLE
+    if (last_colorattr_ptr > 0) {
+	LynxChangeStyle(s_sb_bg, STACK_ON);
+    } else {
+	LynxChangeStyle(s_sb_bg, ABS_ON);
+    }
+#endif /* USE_COLOR_STYLE */
+
+    for (i = 1; i <= h; i++) {
+#ifdef USE_COLOR_STYLE
+	if (i - 1 <= top_skip && i > top_skip)
+	    LynxChangeStyle(s_sb_bar, STACK_ON);
+	if (i - 1 <= h - bot_skip && i > h - bot_skip)
+	    LynxChangeStyle(s_sb_bar, STACK_OFF);
+#endif /* USE_COLOR_STYLE */
+	LYmove(i + off, LYcolLimit + LYshiftWin);
+	if (i > top_skip && i <= h - bot_skip) {
+	    LYaddch(ACS_BLOCK);
+	} else {
+	    LYaddch(ACS_CKBOARD);
+	}
+    }
+#ifdef USE_COLOR_STYLE
+    LynxChangeStyle(s_sb_bg, STACK_OFF);
+#endif /* USE_COLOR_STYLE */
+
+    if (LYsb_arrow) {
+#ifdef USE_COLOR_STYLE
+	int s = bot_skip ? s_sb_aa : s_sb_naa;
+
+	if (last_colorattr_ptr > 0) {
+	    LynxChangeStyle(s, STACK_ON);
+	} else {
+	    LynxChangeStyle(s, ABS_ON);
+	}
+#endif /* USE_COLOR_STYLE */
+	LYmove(h + 2, LYcolLimit + LYshiftWin);
+	addch_raw(ACS_DARROW);
+#ifdef USE_COLOR_STYLE
+	LynxChangeStyle(s, STACK_OFF);
+#endif /* USE_COLOR_STYLE */
+    }
+    return;
+}
+#else
+#define display_scrollbar(text)	/*nothing */
+#endif /* USE_SCROLLBAR */
+
+/*	Output a page
+ *	-------------
+ */
+static void display_page(HText *text,
+			 int line_number,
+			 const char *target)
+{
+    HTLine *line = NULL;
+    int i;
+    int title_lines = TITLE_LINES;
+
+#if defined(USE_COLOR_STYLE) && defined(SHOW_WHEREIS_TARGETS)
+    const char *cp;
+#endif
+    char tmp[7];
+    TextAnchor *Anchor_ptr = NULL;
+    int stop_before_for_anchors;
+    FormInfo *FormInfo_ptr;
+    BOOL display_flag = FALSE;
+    HTAnchor *link_dest;
+    HTAnchor *link_dest_intl = NULL;
+    static int last_nlinks = 0;
+    static int charset_last_displayed = -1;
+
+#ifdef DISP_PARTIAL
+    int last_disp_partial = -1;
+#endif
+
+    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) {
+	    LYaddch('*');
+	    LYrefresh();
+	    LYclear();
+	}
+	LYaddstr("\n\nError accessing document!\nNo data available!\n");
+	LYrefresh();
+	nlinks = 0;		/* set number of links to 0 */
+	return;
+    }
+#ifdef DISP_PARTIAL
+    if (display_partial || recent_sizechange || text->stale) {
+	/*  Reset them, will be set near end if all is okay. - kw */
+	ResetPartialLinenos(text);
+    }
+#endif /* DISP_PARTIAL */
+
+    tmp[0] = tmp[1] = tmp[2] = '\0';
+    if (target && *target == '\0')
+	target = NULL;
+    text->page_has_target = NO;
+    if (display_lines <= 0) {
+	/* No screen space to display anything!
+	 * returning here makes it more likely we will survive if
+	 * an xterm is temporarily made very small.  - kw */
+	return;
+    }
+
+    line_number = HText_getPreferredTopLine(text, line_number);
+
+    for (i = 0, line = FirstHTLine(text);	/* Find line */
+	 i < line_number && (line != text->last_line);
+	 i++, line = line->next) {	/* Loop */
+#ifndef VMS
+	if (!LYNoCore) {
+	    assert(line->next != NULL);
+	} else if (line->next == NULL) {
+	    if (enable_scrollback) {
+		LYaddch('*');
+		LYrefresh();
+		LYclear();
+	    }
+	    LYaddstr("\n\nError drawing page!\nBad HText structure!\n");
+	    LYrefresh();
+	    nlinks = 0;		/* set number of links to 0 */
+	    return;
+	}
+#else
+	assert(line->next != NULL);
+#endif /* !VMS */
+    }				/* Loop */
+
+    if (LYlowest_eightbit[current_char_set] <= 255 &&
+	(current_char_set != charset_last_displayed) &&
+    /*
+     * current_char_set has changed since last invocation,
+     * and it's not just 7-bit.
+     * Also we don't want to do this for -dump and -source etc.
+     */
+	LYCursesON) {
+#ifdef EXP_CHARTRANS_AUTOSWITCH
+	UCChangeTerminalCodepage(current_char_set,
+				 &LYCharSet_UC[current_char_set]);
+#endif /* EXP_CHARTRANS_AUTOSWITCH */
+	charset_last_displayed = current_char_set;
+    }
+
+    /*
+     * 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) {
+	LYaddch('*');
+	LYrefresh();
+	LYclear();
+    }
+#ifdef USE_COLOR_STYLE
+    /*
+     * Reset stack of color attribute changes to avoid color leaking,
+     * except if what we last displayed from this text was the previous
+     * screenful, in which case carrying over the state might be beneficial
+     * (although it shouldn't generally be needed any more).  - kw
+     */
+    if (text->stale ||
+	line_number != text->top_of_screen + (display_lines)) {
+	last_colorattr_ptr = 0;
+    }
+#endif
+
+    text->top_of_screen = line_number;
+    text->top_of_screen_line = line;
+    if (no_title) {
+	LYmove(0, 0);
+	title_lines = 0;
+    } else {
+	display_title(text);	/* will move cursor to top of screen */
+    }
+    display_flag = TRUE;
+
+#ifdef USE_COLOR_STYLE
+#ifdef DISP_PARTIAL
+    if (display_partial ||
+	line_number != text->first_lineno_last_disp_partial ||
+	line_number > text->last_lineno_last_disp_partial)
+#endif /* DISP_PARTIAL */
+	ResetCachedStyles();
+#endif /* USE_COLOR_STYLE */
+
+#ifdef DISP_PARTIAL
+    if (display_partial && text->stbl) {
+	stop_before_for_anchors = Stbl_getStartLineDeep(text->stbl);
+	if (stop_before_for_anchors > line_number + (display_lines))
+	    stop_before_for_anchors = line_number + (display_lines);
+    } else
+#endif
+	stop_before_for_anchors = line_number + (display_lines);
+
+    /*
+     * Output the page.
+     */
+    if (line) {
+#if defined(USE_COLOR_STYLE) && defined(SHOW_WHEREIS_TARGETS)
+	char *data;
+	int offset, LenNeeded;
+#endif
+#ifdef DISP_PARTIAL
+	if (display_partial ||
+	    line_number != text->first_lineno_last_disp_partial)
+	    text->has_utf8 = NO;
+#else
+	text->has_utf8 = NO;
+#endif
+	for (i = 0; i < (display_lines); i++) {
+	    /*
+	     * Verify and display each line.
+	     */
+#ifndef VMS
+	    if (!LYNoCore) {
+		assert(line != NULL);
+	    } else if (line == NULL) {
+		if (enable_scrollback) {
+		    LYaddch('*');
+		    LYrefresh();
+		    LYclear();
+		}
+		LYaddstr("\n\nError drawing page!\nBad HText structure!\n");
+		LYrefresh();
+		nlinks = 0;	/* set number of links to 0 */
+		return;
+	    }
+#else
+	    assert(line != NULL);
+#endif /* !VMS */
+
+#ifdef DISP_PARTIAL
+	    if (!display_partial &&
+		line_number == text->first_lineno_last_disp_partial &&
+		i + line_number <= text->last_lineno_last_disp_partial)
+		LYmove((i + title_lines + 1), 0);
+	    else
+#endif
+		display_line(line, text, i + 1, target);
+
+#if defined(SHOW_WHEREIS_TARGETS)
+#ifdef USE_COLOR_STYLE		/* otherwise done in display_line - kw */
+	    /*
+	     * If the target is on this line, recursively
+	     * seek and emphasize it.  -FM
+	     */
+	    data = (char *) line->data;
+	    offset = (int) line->offset;
+	    while (non_empty(target) &&
+		   (cp = LYno_attr_mb_strstr(data,
+					     target,
+					     text->T.output_utf8, YES,
+					     NULL,
+					     &LenNeeded)) != NULL &&
+		   ((int) line->offset + LenNeeded) <= DISPLAY_COLS) {
+		int itmp = 0;
+		int written = 0;
+		int x_pos = offset + (int) (cp - data);
+		int len = strlen(target);
+		size_t utf_extra = 0;
+		int y;
+
+		text->page_has_target = YES;
+
+		/*
+		 * Start the emphasis.
+		 */
+		LYstartTargetEmphasis();
+
+		/*
+		 * Output the target characters.
+		 */
+		for (;
+		     written < len && (tmp[0] = data[itmp]) != '\0';
+		     itmp++) {
+		    if (IsSpecialAttrChar(tmp[0]) && tmp[0] != LY_SOFT_NEWLINE) {
+			/*
+			 * Ignore special characters.
+			 */
+			x_pos--;
+
+		    } else if (&data[itmp] >= cp) {
+			if (cp == &data[itmp]) {
+			    /*
+			     * First printable character of target.
+			     */
+			    LYmove((i + title_lines), x_pos);
+			}
+			/*
+			 * Output all the printable target chars.
+			 */
+			utf_extra = utf8_length(text->T.output_utf8, data + itmp);
+			if (utf_extra) {
+			    strncpy(&tmp[1], &line->data[itmp + 1], utf_extra);
+			    tmp[utf_extra + 1] = '\0';
+			    itmp += utf_extra;
+			    LYaddstr(tmp);
+			    tmp[1] = '\0';
+			    written += (utf_extra + 1);
+			} else if (IS_CJK_TTY && is8bits(tmp[0])) {
+			    /*
+			     * For CJK strings, by Masanobu Kimura.
+			     */
+			    tmp[1] = data[++itmp];
+			    LYaddstr(tmp);
+			    tmp[1] = '\0';
+			    written += 2;
+			} else {
+			    LYaddstr(tmp);
+			    written++;
+			}
+		    }
+		}
+
+		/*
+		 * Stop the emphasis, and reset the offset and
+		 * data pointer for our current position in the
+		 * line.  -FM
+		 */
+		LYstopTargetEmphasis();
+		LYGetYX(y, offset);
+		data = (char *) &data[itmp];
+
+		/*
+		 * Adjust the cursor position, should we be at
+		 * the end of the line, or not have another hit
+		 * in it.  -FM
+		 */
+		LYmove((i + title_lines + 1), 0);
+	    }			/* end while */
+#endif /* USE_COLOR_STYLE */
+#endif /* SHOW_WHEREIS_TARGETS */
+
+	    /*
+	     * Stop if this is the last line.  Otherwise, make sure
+	     * display_flag is set and process the next line.  -FM
+	     */
+	    if (line == text->last_line) {
+		/*
+		 * Clear remaining lines of display.
+		 */
+		for (i++; i < (display_lines); i++) {
+		    LYmove((i + title_lines), 0);
+		    LYclrtoeol();
+		}
+		break;
+	    }
+#ifdef DISP_PARTIAL
+	    if (display_partial) {
+		/*
+		 * Remember as fully shown during last partial display,
+		 * if it was not the last text line.  - kw
+		 */
+		last_disp_partial = i + line_number;
+	    }
+#endif /* DISP_PARTIAL */
+	    display_flag = TRUE;
+	    line = line->next;
+	}			/* end of "Verify and display each line." loop */
+    }
+    /* end "Output the page." */
+    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 <= stop_before_for_anchors;
+	 Anchor_ptr = Anchor_ptr->next) {
+
+	if (Anchor_ptr->line_num >= line_number
+	    && Anchor_ptr->line_num < stop_before_for_anchors) {
+	    char *hi_string = LYGetHiTextStr(Anchor_ptr, 0);
+
+	    /*
+	     * Load normal hypertext anchors.
+	     */
+	    if (Anchor_ptr->show_anchor
+		&& non_empty(hi_string)
+		&& (Anchor_ptr->link_type & HYPERTEXT_ANCHOR)) {
+		int count;
+		char *s;
+
+		for (count = 0;; ++count) {
+		    s = LYGetHiTextStr(Anchor_ptr, count);
+		    if (count == 0)
+			LYSetHilite(nlinks, s);
+		    if (s == NULL)
+			break;
+		    if (count != 0) {
+			LYAddHilite(nlinks, s, LYGetHiTextPos(Anchor_ptr, count));
+		    }
+		}
+
+		links[nlinks].inUnderline = Anchor_ptr->inUnderline;
+
+		links[nlinks].sgml_offset = Anchor_ptr->sgml_offset;
+		links[nlinks].anchor_number = Anchor_ptr->number;
+		links[nlinks].anchor_line_num = Anchor_ptr->line_num;
+
+		link_dest = HTAnchor_followLink(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 {
+#ifndef DONT_TRACK_INTERNAL_LINKS
+			if (Anchor_ptr->link_type == INTERNAL_LINK_ANCHOR) {
+			    link_dest_intl = HTAnchor_followTypedLink(
+									 Anchor_ptr->anchor, HTInternalLink);
+			    if (link_dest_intl && link_dest_intl != link_dest) {
+
+				CTRACE((tfp,
+					"GridText: display_page: unexpected typed link to %s!\n",
+					link_dest_intl->parent->address));
+				link_dest_intl = NULL;
+			    }
+			} else
+			    link_dest_intl = NULL;
+			if (link_dest_intl) {
+			    char *cp2 = HTAnchor_address(link_dest_intl);
+
+			    cp_AnchorAddress = cp2;
+			} else
+#endif
+			    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);
+		if (link_dest_intl)
+		    links[nlinks].type = WWW_INTERN_LINK_TYPE;
+		else
+		    links[nlinks].type = WWW_LINK_TYPE;
+		links[nlinks].target = empty_string;
+		links[nlinks].l_form = NULL;
+
+		nlinks++;
+		display_flag = TRUE;
+
+	    } else if (Anchor_ptr->link_type == INPUT_ANCHOR
+		       && Anchor_ptr->input_field->type != F_HIDDEN_TYPE) {
+		/*
+		 * Handle form fields.
+		 */
+		lynx_mode = FORMS_LYNX_MODE;
+
+		FormInfo_ptr = Anchor_ptr->input_field;
+
+		links[nlinks].sgml_offset = Anchor_ptr->sgml_offset;
+		links[nlinks].anchor_number = Anchor_ptr->number;
+		links[nlinks].anchor_line_num = Anchor_ptr->line_num;
+
+		links[nlinks].l_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].inUnderline = Anchor_ptr->inUnderline;
+		links[nlinks].target = empty_string;
+		StrAllocCopy(links[nlinks].lname, empty_string);
+
+		if (FormInfo_ptr->type == F_RADIO_TYPE) {
+		    LYSetHilite(nlinks,
+				FormInfo_ptr->num_value
+				? checked_radio
+				: unchecked_radio);
+		} else if (FormInfo_ptr->type == F_CHECKBOX_TYPE) {
+		    LYSetHilite(nlinks,
+				FormInfo_ptr->num_value
+				? checked_box
+				: unchecked_box);
+		} else if (FormInfo_ptr->type == F_PASSWORD_TYPE) {
+		    LYSetHilite(nlinks,
+				STARS(LYstrCells(FormInfo_ptr->value)));
+		} else {	/* TEXT type */
+		    LYSetHilite(nlinks,
+				FormInfo_ptr->value);
+		}
+
+		nlinks++;
+		/*
+		 * Bold the link after incrementing nlinks.
+		 */
+		LYhighlight(OFF, (nlinks - 1), target);
+
+		display_flag = TRUE;
+
+	    } else {
+		/*
+		 * Not showing anchor.
+		 */
+		if (non_empty(hi_string))
+		    CTRACE((tfp,
+			    "\nGridText: Not showing link, hightext=%s\n",
+			    hi_string));
+	    }
+	}
+
+	if (nlinks == MAXLINKS) {
+	    /*
+	     * Links array is full.  If interactive, tell user
+	     * to use half-page or two-line scrolling.  -FM
+	     */
+	    if (LYCursesON) {
+		HTAlert(MAXLINKS_REACHED);
+	    }
+	    CTRACE((tfp, "\ndisplay_page: MAXLINKS reached.\n"));
+	    break;
+	}
+    }				/* end of loop "Add the anchors to Lynx structures." */
+
+    /*
+     * Free any un-reallocated links[] entries
+     * from the previous page draw.  -FM
+     */
+    LYFreeHilites(nlinks, last_nlinks);
+    last_nlinks = nlinks;
+
+    /*
+     * 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.
+	 */
+	LYaddstr("\n     Document is empty");
+    }
+    display_scrollbar(text);
+
+#ifdef DISP_PARTIAL
+    if (display_partial && display_flag &&
+	last_disp_partial >= text->top_of_screen &&
+	!enable_scrollback &&
+	!recent_sizechange) {	/*  really remember them if ok - kw  */
+	text->first_lineno_last_disp_partial = text->top_of_screen;
+	text->last_lineno_last_disp_partial = last_disp_partial;
+    } else {
+	ResetPartialLinenos(text);
+    }
+#endif /* DISP_PARTIAL */
+
+#if !defined(WIDEC_CURSES)
+    if (text->has_utf8 || text->had_utf8) {
+	/*
+	 * For other than ncurses, repainting is taken care of
+	 * by touching lines in display_line and highlight.  - kw 1999-10-07
+	 */
+	text->had_utf8 = text->has_utf8;
+	clearok(curscr, TRUE);
+    } else if (IS_CJK_TTY) {
+	/*
+	 * For non-multibyte curses.
+	 *
+	 * Full repainting is necessary, otherwise only part of a multibyte
+	 * character sequence might be written because of curses output
+	 * optimizations.
+	 */
+	clearok(curscr, TRUE);
+    }
+#endif /* WIDEC_CURSES */
+
+    LYrefresh();
+    return;
+}
+
+/*			Object Building methods
+ *			-----------------------
+ *
+ *	These are used by a parser to build the text in an object
+ */
+void HText_beginAppend(HText *text)
+{
+    text->permissible_split = 0;
+    text->in_line_1 = YES;
+
+}
+
+/* LYcols_cu is the notion that the display library has of the screen
+   width.  Normally it is the same as LYcols, but there may be a
+   difference via SLANG_MBCS_HACK.  Checks of the line length (as the
+   non-UTF-8-aware display library would see it) against LYcols_cu are
+   is used to try to prevent that lines with UTF-8 chars get wrapped
+   by the library when they shouldn't.
+   If there is no display library involved, i.e. dump_output_immediately,
+   no such limit should be imposed.  MAX_COLS should be just as good
+   as any other large value.  (But don't use INT_MAX or something close
+   to it to, avoid over/underflow.) - kw */
+#ifdef USE_SLANG
+#define LYcols_cu(text) (dump_output_immediately ? MAX_COLS : SLtt_Screen_Cols)
+#else
+#ifdef WIDEC_CURSES
+#define LYcols_cu(text) WRAP_COLS(text)
+#else
+#define LYcols_cu(text) (dump_output_immediately ? MAX_COLS : DISPLAY_COLS)
+#endif
+#endif
+
+/*	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 DEBUG_SPLITLINE */
+
+#ifdef DEBUG_SPLITLINE
+#define CTRACE_SPLITLINE(p)	CTRACE(p)
+#else
+#define CTRACE_SPLITLINE(p)	/*nothing */
+#endif
+
+static int set_style_by_embedded_chars(char *s,
+				       char *e,
+				       unsigned char start_c,
+				       unsigned char end_c)
+{
+    int ret = NO;
+
+    while (--e >= s) {
+	if (*e == end_c)
+	    break;
+	if (*e == start_c) {
+	    ret = YES;
+	    break;
+	}
+    }
+    return ret;
+}
+
+static void move_anchors_in_region(HTLine *line, int line_number,
+				   TextAnchor **prev_anchor,	/*updates++ */
+				   int *prev_head_processed,
+				   int sbyte,
+				   int ebyte,
+				   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.
+     */
+    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
+     * 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) {
+	/* extent==0 needs to be special-cased; happens if no text for
+	   the anchor was processed yet.  */
+	/* Subtract one so that the space is not inserted at the end
+	   of the anchor... */
+	int last = a->line_pos + (a->extent ? a->extent - 1 : 0);
+
+	/* Include the anchors started on the previous line */
+	if (a->line_num < line_number - 1)
+	    continue;
+	if (a->line_num == line_number - 1)
+	    last -= line->prev->size + 1;	/* Fake "\n" "between" lines counted too */
+	if (last < sbyte)	/* Completely before the start */
+	    continue;
+
+	if (!head_processed	/* a->line_pos is not edited yet */
+	    && a->line_num == line_number
+	    && a->line_pos >= ebyte)	/* Completely after the end */
+	    break;
+	/* Now we know that the anchor context intersects the chunk */
+
+	/* Fix the start */
+	if (!head_processed && a->line_num == line_number
+	    && a->line_pos >= sbyte) {
+	    a->line_pos += shift;
+	    a->extent -= shift;
+	    head_processed = 1;
+	}
+	/* Fix the end */
+	if (last < ebyte) {
+	    a->extent += shift;
+	} else {
+	    break;		/* Keep this `a' for the next step */
+	}
+    }
+    *prev_anchor = a;
+    *prev_head_processed = head_processed;
+}
+
+/*
+ * Given a line and two int arrays of old/now position, this function
+ * creates a new line where spaces have been inserted/removed
+ * 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.  Updates 'prev_anchor' internally.
+ * Returns a newly allocated HTLine* if changes were made
+ * (caller has to free the old one).
+ * Returns NULL if no changes needed.  (Remove-spaces code may be buggy...)
+ * - kw
+ */
+static HTLine *insert_blanks_in_line(HTLine *line, int line_number,
+				     HText *text,
+				     TextAnchor **prev_anchor,	/*updates++ */
+				     int ninserts,
+				     int *oldpos,	/* Measured in cells */
+				     int *newpos)	/* Likewise */
+{
+    int ioldc = 0;		/* count visible characters */
+    int ip;			/* count insertion pairs */
+
+#if defined(USE_COLOR_STYLE)
+    int istyle = 0;
+#endif
+    int added_chars = 0;
+    int shift = 0;
+    int head_processed;
+    HTLine *mod_line;
+    char *newdata;
+    char *s = line->data;
+    char *pre = s;
+    char *copied = line->data, *t;
+
+    if (!(line && line->size && ninserts))
+	return NULL;
+    for (ip = 0; ip < ninserts; ip++)
+	if (newpos[ip] > oldpos[ip] &&
+	    (newpos[ip] - oldpos[ip]) > added_chars)
+	    added_chars = newpos[ip] - oldpos[ip];
+    if (line->size + added_chars > MAX_LINE - 2)
+	return NULL;
+    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 {
+	allocHTLine(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(0));
+    t = newdata = mod_line->data;
+    ip = 0;
+    while (ip <= ninserts) {
+	/* line->size is in bytes, so it may be larger than needed... */
+	int curlim = (ip < ninserts
+		      ? oldpos[ip]
+		      : ((int) line->size <= MAX_LINE
+			 ? MAX_LINE + 1
+			 : (int) line->size + 1));
+
+	pre = s;
+
+	/* Fast forward to char==curlim or EOL.  Stop *before* the
+	   style-change chars. */
+	while (*s) {
+	    if (text && text->T.output_utf8
+		&& UCH(*s) >= 0x80 && UCH(*s) < 0xC0) {
+		pre = s + 1;
+	    } else if (!IsSpecialAttrChar(*s)) {	/* At a "displayed" char */
+		if (ioldc >= curlim)
+		    break;
+		ioldc++;
+		pre = s + 1;
+	    }
+	    s++;
+	}
+
+	/* 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 /*updates++ */ ,
+				   &head_processed,
+				   copied - line->data, pre - line->data,
+				   shift);
+#if defined(USE_COLOR_STYLE)	/* Move styles too */
+#define NStyle mod_line->styles[istyle]
+	for (;
+	     istyle < line->numstyles && (int) NStyle.sc_horizpos < curlim;
+	     istyle++)
+	    /* Should not we include OFF-styles at curlim? */
+	    NStyle.sc_horizpos += shift;
+#endif
+	while (copied < pre)	/* Copy verbatim to byte == pre */
+	    *t++ = *copied++;
+	if (ip < ninserts) {	/* Insert spaces */
+	    int delta = newpos[ip] - oldpos[ip] - shift;
+
+	    if (delta < 0) {	/* Not used yet? */
+		while (delta++ < 0 && t > newdata && t[-1] == ' ')
+		    t--, shift--;
+	    } else
+		shift = newpos[ip] - oldpos[ip];
+	    while (delta-- > 0)
+		*t++ = ' ';
+	}
+	ip++;
+    }
+    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;
+    *t = '\0';
+    mod_line->size = t - newdata;
+    return mod_line;
+}
+
+#if defined(USE_COLOR_STYLE)
+/*
+ * Found an OFF change not part of an adjacent matched pair.
+ *
+ * Walk backward looking for the corresponding ON change.
+ * Move everything after split_pos to be at split_pos.
+ *
+ * This can only work correctly if all changes are correctly nested!  If this
+ * fails, assume it is safer to leave whatever comes before the OFF on the
+ * previous line alone.
+ */
+static HTStyleChange *skip_matched_and_correct_offsets(HTStyleChange *end,
+						       HTStyleChange *start,
+						       unsigned split_pos)
+{
+    HTStyleChange *result = 0;
+    int level = 0;
+    HTStyleChange *tmp = end;
+
+    CTRACE_STYLE((tfp, "SKIP Style %d %d (%d)\n",
+		  tmp->sc_horizpos,
+		  tmp->sc_style,
+		  tmp->sc_direction));
+    for (; tmp >= start; tmp--) {
+	CTRACE_STYLE((tfp, "... %d %d (%d)\n",
+		      tmp->sc_horizpos,
+		      tmp->sc_style,
+		      tmp->sc_direction));
+	if (tmp->sc_style == end->sc_style) {
+	    if (tmp->sc_direction == STACK_OFF) {
+		level--;
+	    } else if (tmp->sc_direction == STACK_ON) {
+		if (++level == 0) {
+		    result = tmp;
+		    break;
+		}
+	    } else {
+		break;
+	    }
+	}
+	if (tmp->sc_horizpos > split_pos) {
+	    tmp->sc_horizpos = split_pos;
+	}
+    }
+    return result;
+}
+#endif /* USE_COLOR_STYLE */
+
+static void split_line(HText *text, unsigned split)
+{
+    HTStyle *style = text->style;
+    int spare;
+    int indent = (text->in_line_1
+		  ? text->style->indent1st
+		  : text->style->leftIndent);
+    int new_offset;
+    short alignment;
+    TextAnchor *a;
+    int CurLine = text->Lines;
+    int HeadTrim = 0;
+    int SpecialAttrChars = 0;
+    int TailTrim = 0;
+    int s, s_post, s_pre, t_underline = underline_on, t_bold = bold_on;
+    char *p;
+    char *cp;
+    int ctrl_chars_on_previous_line = 0;
+
+#ifndef WIDEC_CURSES
+    int utfxtra_on_previous_line = UTFXTRA_ON_THIS_LINE;
+#endif
+
+    HTLine *previous = text->last_line;
+    HTLine *line;
+
+    /*
+     * Set new line.
+     */
+    if (previous == TEMP_LINE(text, 0))
+	line = TEMP_LINE(text, 1);
+    else
+	line = TEMP_LINE(text, 0);
+    if (line == NULL)
+	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 = ' ';
+
+#ifdef DEBUG_APPCH
+    CTRACE((tfp, "GridText: split_line(%p,%d) called\n", text, split));
+    CTRACE((tfp, "   bold_on=%d, underline_on=%d\n", bold_on, underline_on));
+#endif
+
+    cp = previous->data;
+
+    /* Float LY_SOFT_NEWLINE to the start */
+    if (cp[0] == LY_BOLD_START_CHAR
+	|| cp[0] == LY_UNDERLINE_START_CHAR) {
+	switch (cp[1]) {
+	case LY_SOFT_NEWLINE:
+	    cp[1] = cp[0];
+	    cp[0] = LY_SOFT_NEWLINE;
+	    break;
+	case LY_BOLD_START_CHAR:
+	case LY_UNDERLINE_START_CHAR:
+	    if (cp[2] == LY_SOFT_NEWLINE) {
+		cp[2] = cp[1];
+		cp[1] = cp[0];
+		cp[0] = LY_SOFT_NEWLINE;
+	    }
+	    break;
+	}
+    }
+    if (split > previous->size) {
+	CTRACE((tfp,
+		"*** split_line: split==%u greater than last_line->size==%d !\n",
+		split, previous->size));
+	if (split > MAX_LINE) {
+	    split = previous->size;
+	    if ((cp = strrchr(previous->data, ' ')) &&
+		cp - previous->data > 1)
+		split = cp - previous->data;
+	    CTRACE((tfp, "                split adjusted to %u.\n", split));
+	}
+    }
+
+    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';
+
+    alignment = style->alignment;
+
+    if (split > 0) {		/* Restore flags to the value at the splitting point */
+	if (!(dump_output_immediately && use_underscore))
+	    t_underline = set_style_by_embedded_chars(previous->data,
+						      previous->data + split,
+						      LY_UNDERLINE_START_CHAR, LY_UNDERLINE_END_CHAR);
+
+	t_bold = set_style_by_embedded_chars(previous->data,
+					     previous->data + split,
+					     LY_BOLD_START_CHAR, LY_BOLD_END_CHAR);
+
+    }
+
+    if (!(dump_output_immediately && use_underscore) && t_underline) {
+	line->data[line->size++] = LY_UNDERLINE_START_CHAR;
+	line->data[line->size] = '\0';
+	ctrl_chars_on_this_line++;
+	SpecialAttrChars++;
+    }
+    if (t_bold) {
+	line->data[line->size++] = LY_BOLD_START_CHAR;
+	line->data[line->size] = '\0';
+	ctrl_chars_on_this_line++;
+	SpecialAttrChars++;
+    }
+
+    /*
+     * Split at required point
+     */
+    if (split > 0) {		/* Delete space at "split" splitting line */
+	char *prevdata = previous->data, *linedata = line->data;
+	unsigned 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 == ' '
+#ifdef USE_JUSTIFY_ELTS
+	/* if justification is allowed for prev line, then raw
+	 * HT_NON_BREAK_SPACE are still present in data[] (they'll be
+	 * substituted at the end of this function with ' ') - VH
+	 */
+		 || *p == HT_NON_BREAK_SPACE
+#endif
+		)
+		&& (HeadTrim || text->first_anchor ||
+		    underline_on || bold_on ||
+		    alignment != HT_LEFT ||
+		    style->wordWrap || style->freeFormat ||
+		    style->spaceBefore || style->spaceAfter)) ||
+	       *p == LY_SOFT_HYPHEN) {
+	    p++;
+	    HeadTrim++;
+	}
+
+	plen = strlen(p);
+	if (plen) {		/* Count funny characters */
+	    for (i = (plen - 1); i >= 0; i--) {
+		if (p[i] == LY_UNDERLINE_START_CHAR ||
+		    p[i] == LY_UNDERLINE_END_CHAR ||
+		    p[i] == LY_BOLD_START_CHAR ||
+		    p[i] == LY_BOLD_END_CHAR ||
+		    p[i] == LY_SOFT_HYPHEN) {
+		    ctrl_chars_on_this_line++;
+		} else if (IS_UTF_EXTRA(p[i])) {
+		    utfxtra_on_this_line++;
+		}
+		if (p[i] == LY_SOFT_HYPHEN &&
+		    (int) text->permissible_split < i)
+		    text->permissible_split = i + 1;
+	    }
+	    ctrl_chars_on_this_line += utfxtra_on_this_line;
+
+	    /* Add the data to the new line. -FM */
+	    strcat(linedata, p);
+	    line->size += plen;
+	}
+    }
+
+    /*
+     * Economize on space.
+     */
+    p = previous->data + previous->size - 1;
+    while (p >= previous->data
+	   && (*p == ' '
+#ifdef USE_JUSTIFY_ELTS
+    /* if justification is allowed for prev line, then raw
+     * HT_NON_BREAK_SPACE are still present in data[] (they'll be
+     * substituted at the end of this function with ' ') - VH
+     */
+	       || *p == HT_NON_BREAK_SPACE
+#endif
+	   )
+#ifdef USE_PRETTYSRC
+	   && !psrc_view	/*don't strip trailing whites - since next line can
+				   start with LY_SOFT_NEWLINE - so we don't lose spaces when
+				   'p'rinting this text to file -VH */
+#endif
+	   && (ctrl_chars_on_this_line || HeadTrim || text->first_anchor ||
+	       underline_on || bold_on ||
+	       alignment != HT_LEFT ||
+	       style->wordWrap || style->freeFormat ||
+	       style->spaceBefore || style->spaceAfter)) {
+	p--;			/*  Strip trailers. */
+    }
+    TailTrim = previous->data + previous->size - 1 - p;		/*  Strip trailers. */
+    previous->size -= TailTrim;
+    p[1] = '\0';
+
+    /*
+     * s is the effective split position, given by either a non-zero
+     * value of split or by the size of the previous line before
+     * trimming.  - kw
+     */
+    if (split == 0) {
+	s = previous->size + TailTrim;	/* the original size */
+    } else {
+	s = split;
+    }
+    s_post = s + HeadTrim;
+    s_pre = s - TailTrim;
+
+#ifdef DEBUG_SPLITLINE
+#ifdef DEBUG_APPCH
+    if (s != (int) split)
+#endif
+	CTRACE((tfp, "GridText: split_line(%u [now:%d]) called\n", split, s));
+#endif
+
+#if defined(USE_COLOR_STYLE)
+    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;
+	HTStyleChange *to = line->styles + MAX_STYLES_ON_LINE - 1;
+	HTStyleChange *scan, *at_end;
+
+	/* Color style changes after the split position
+	 * are transferred to the new line.  Ditto for changes
+	 * in the trimming region, but we stop when we reach an OFF change.
+	 * The second loop below may then handle remaining changes.  - kw */
+	while (from >= previous->styles && to >= line->styles) {
+	    *to = *from;
+	    if ((int) to->sc_horizpos > s_post) {
+		to->sc_horizpos += -s_post + SpecialAttrChars;
+	    } else if ((int) to->sc_horizpos > s_pre &&
+		       (to->sc_direction == STACK_ON ||
+			to->sc_direction == ABS_ON)) {
+		to->sc_horizpos = ((int) to->sc_horizpos < s) ? 0 : SpecialAttrChars;
+	    } else {
+		break;
+	    }
+	    to--;
+	    from--;
+	}
+	/* FROM may be invalid, otherwise it is either an ON change at or
+	   before s_pre, or is an OFF change at or before s_post.  */
+
+	scan = from;
+	at_end = from;
+	/* Now on the previous line we have a correctly nested but
+	   possibly non-terminated sequence of style changes.
+	   Terminate it, and duplicate unterminated changes at the
+	   beginning of the new line. */
+	while (scan >= previous->styles && at_end >= previous->styles) {
+	    /* The algorithm: scan back though the styles on the previous line.
+	       a) If OFF, skip the matched group.
+	       Report a bug on failure.
+	       b) If ON, (try to) cancel the corresponding ON at at_end,
+	       and the corresponding OFF at to;
+	       If not, put the corresponding OFF at at_end, and copy to to;
+	     */
+	    if (scan->sc_direction == STACK_OFF) {
+		scan = skip_matched_and_correct_offsets(scan, previous->styles,
+							s_pre);
+		if (!scan) {
+		    CTRACE((tfp, "BUG: styles improperly nested.\n"));
+		    break;
+		}
+	    } else if (scan->sc_direction == STACK_ON) {
+		if (at_end->sc_direction == STACK_ON
+		    && at_end->sc_style == scan->sc_style
+		    && (int) at_end->sc_horizpos >= s_pre)
+		    at_end--;
+		else if (at_end >= previous->styles + MAX_STYLES_ON_LINE - 1) {
+		    CTRACE((tfp, "BUG: style overflow before split_line.\n"));
+		    break;
+		} else {
+		    at_end++;
+		    at_end->sc_direction = STACK_OFF;
+		    at_end->sc_style = scan->sc_style;
+		    at_end->sc_horizpos = s_pre;
+		    CTRACE_STYLE((tfp,
+				  "split_line, %d:style[%d] %d (dir=%d)\n",
+				  s_pre,
+				  at_end - from,
+				  scan->sc_style,
+				  at_end->sc_direction));
+		}
+		if (to < line->styles + MAX_STYLES_ON_LINE - 1
+		    && to[1].sc_direction == STACK_OFF
+		    && to[1].sc_horizpos <= (unsigned) SpecialAttrChars
+		    && to[1].sc_style == scan->sc_style)
+		    to++;
+		else if (to >= line->styles) {
+		    *to = *scan;
+		    to->sc_horizpos = SpecialAttrChars;
+		    to--;
+		} else {
+		    CTRACE((tfp, "BUG: style overflow after split_line.\n"));
+		    break;
+		}
+	    }
+	    if ((int) scan->sc_horizpos > s_pre) {
+		scan->sc_horizpos = s_pre;
+	    }
+	    scan--;
+	}
+	line->numstyles = line->styles + MAX_STYLES_ON_LINE - 1 - to;
+	if (line->numstyles > 0 && line->numstyles < MAX_STYLES_ON_LINE) {
+	    int n;
+
+	    for (n = 0; n < line->numstyles; n++)
+		line->styles[n] = to[n + 1];
+	} else if (line->numstyles == 0) {
+	    line->styles[0].sc_horizpos = ~0;	/* ?!!! */
+	}
+	previous->numstyles = at_end - previous->styles + 1;
+	if (previous->numstyles == 0) {
+	    previous->styles[0].sc_horizpos = ~0;	/* ?!!! */
+	}
+    }
+#endif /*USE_COLOR_STYLE */
+
+    {
+	HTLine *temp;
+
+	allocHTLine(temp, previous->size);
+	if (!temp)
+	    outofmem(__FILE__, "split_line_2");
+
+	assert(temp != NULL);
+
+	memcpy(temp, previous, LINE_SIZE(previous->size));
+#if defined(USE_COLOR_STYLE)
+	POOLallocstyles(temp->styles, previous->numstyles);
+	if (!temp->styles)
+	    outofmem(__FILE__, "split_line_2");
+	memcpy(temp->styles, previous->styles, sizeof(HTStyleChange) * previous->numstyles);
+#endif
+	previous = temp;
+    }
+
+    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.
+     */
+    spare = 0;
+    if (
+#ifdef USE_JUSTIFY_ELTS
+	   this_line_was_split ||
+#endif
+	   (alignment == HT_CENTER ||
+	    alignment == HT_RIGHT) || text->stbl) {
+	/* Calculate spare character positions if needed */
+	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 ||
+#ifndef WIDEC_CURSES
+		IS_UTF_EXTRA(*cp) ||
+#endif
+		*cp == LY_SOFT_HYPHEN) {
+		ctrl_chars_on_previous_line++;
+	    }
+	}
+	if ((previous->size > 0) &&
+	    (int) (previous->data[previous->size - 1] == LY_SOFT_HYPHEN))
+	    ctrl_chars_on_previous_line--;
+
+	/* @@ first line indent */
+#ifdef WIDEC_CURSES
+	spare = WRAP_COLS(text)
+	    - (int) style->rightIndent
+	    - indent
+	    + ctrl_chars_on_previous_line
+	    - LYstrExtent2(previous->data, previous->size);
+	if (spare < 0 && LYwideLines)	/* Can be wider than screen */
+	    spare = 0;
+#else
+	spare = WRAP_COLS(text)
+	    - (int) style->rightIndent
+	    - indent
+	    + ctrl_chars_on_previous_line
+	    - previous->size;
+	if (spare < 0 && LYwideLines)	/* Can be wider than screen */
+	    spare = 0;
+
+	if (spare > 0 && !dump_output_immediately &&
+	    text->T.output_utf8 && ctrl_chars_on_previous_line) {
+	    utfxtra_on_previous_line -= UTFXTRA_ON_THIS_LINE;
+	    if (utfxtra_on_previous_line) {
+		int spare_cu = (LYcols_cu(text) -
+				utfxtra_on_previous_line - indent +
+				ctrl_chars_on_previous_line - previous->size);
+
+		/*
+		 * Shift non-leftaligned UTF-8 lines that would be
+		 * mishandled by the display library towards the left
+		 * if this would make them fit.  The resulting display
+		 * will not be as intended, but this is better than
+		 * having them split by curses.  (Curses cursor movement
+		 * optimization may still cause wrong positioning within
+		 * the line, in particular after a sequence of spaces).
+		 * - kw
+		 */
+		if (spare_cu < spare) {
+		    if (spare_cu >= 0) {
+			if (alignment == HT_CENTER &&
+			    (int) (previous->offset + indent + spare / 2 +
+				   previous->size)
+			    - ctrl_chars_on_previous_line
+			    + utfxtra_on_previous_line <= LYcols_cu(text))
+			    /* do nothing - it still fits - kw */ ;
+			else {
+			    spare = spare_cu;
+			}
+		    } else if (indent + (int) previous->offset + spare_cu >= 0) {	/* subtract overdraft from effective indentation */
+			indent += (int) previous->offset + spare_cu;
+			previous->offset = 0;
+			spare = 0;
+		    }
+		}
+	    }
+	}
+#endif
+    }
+
+    new_offset = previous->offset;
+    switch (style->alignment) {
+    case HT_CENTER:
+	new_offset += indent + spare / 2;
+	break;
+    case HT_RIGHT:
+	new_offset += indent + spare;
+	break;
+    case HT_LEFT:
+    case HT_JUSTIFY:		/* Not implemented */
+    default:
+	new_offset += indent;
+	break;
+    }				/* switch */
+    previous->offset = ((new_offset < 0) ? 0 : new_offset);
+
+    if (text->stbl)
+	/*
+	 * Notify simple table stuff of line split, so that it can
+	 * set the last cell's length.  The last cell should and
+	 * its row should really end here, or on one of the following
+	 * lines with no more characters added after the break.
+	 * We don't know whether a cell has been started, so ignore
+	 * errors here.
+	 * This call is down here because we need the
+	 * ctrl_chars_on_previous_line, which have just been re-
+	 * counted above.  - kw
+	 */
+	Stbl_lineBreak(text->stbl,
+		       text->Lines - 1,
+		       previous->offset,
+		       previous->size - ctrl_chars_on_previous_line);
+
+    text->in_line_1 = NO;	/* unless caller sets it otherwise */
+
+    /*
+     * If we split the line, adjust the anchor
+     * structure values for the new line.  -FM
+     */
+
+    if (s > 0) {		/* if not completely empty */
+	int moved = 0;
+
+	/* In the algorithm below we move or not move anchors between
+	   lines using some heuristic criteria.  However, it is
+	   desirable not to have two consequent anchors on different
+	   lines *in a wrong order*!  (How can this happen?)
+	   So when the "reasonable choice" is not unique, we use the
+	   MOVED flag to choose one.
+	 */
+	/* Our operations can make a non-empty all-whitespace link
+	   empty.  So what? */
+	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;
+		   this is the only case when we use s, not s_pre/s_post);
+		   b) Those which start before s_pre;
+		 */
+		if (start < s_pre) {
+		    if (end <= s_pre)
+			continue;	/* No problem */
+
+		    CTRACE_SPLITLINE((tfp, "anchor %d: no relocation", n));
+		    if (end > s_post) {
+			CTRACE_SPLITLINE((tfp, " of the start.\n"));
+			a->extent += -(TailTrim + HeadTrim) + SpecialAttrChars;
+		    } else {
+			CTRACE_SPLITLINE((tfp, ", cut the end.\n"));
+			a->extent = s_pre - start;
+		    }
+		    continue;
+		} else if (start < s && !len
+			   && (!n || (a->show_anchor && !moved))) {
+		    CTRACE_SPLITLINE((tfp,
+				      "anchor %d: no relocation, empty-finished",
+				      n));
+		    a->line_pos = s_pre;	/* Leave at the end of line */
+		    continue;
+		}
+
+		/* The rest we relocate */
+		moved = 1;
+		a->line_num++;
+		CTRACE_SPLITLINE((tfp,
+				  "anchor %d: (T,H,S)=(%d,%d,%d); (line,pos,ext):(%d,%d,%d), ",
+				  n, TailTrim, HeadTrim, SpecialAttrChars,
+				  a->line_num, a->line_pos, a->extent));
+		if (end < s_post) {	/* Move the end to s_post */
+		    CTRACE_SPLITLINE((tfp, "Move end +%d, ", s_post - end));
+		    len += s_post - end;
+		}
+		if (start < s_post) {	/* Move the start to s_post */
+		    CTRACE_SPLITLINE((tfp, "Move start +%d, ", s_post - start));
+		    len -= s_post - start;
+		    start = s_post;
+		}
+		a->line_pos = start - s_post + SpecialAttrChars;
+		a->extent = len;
+
+		CTRACE_SPLITLINE((tfp, "->(%d,%d,%d)\n",
+				  a->line_num, a->line_pos, a->extent));
+	    } else if (a->line_num > CurLine)
+		break;
+	}
+    }
+#ifdef USE_JUSTIFY_ELTS
+    /* now perform justification - by VH */
+
+    if (this_line_was_split
+	&& spare > 0
+	&& !text->stbl		/* We don't inform TRST on the cell width change yet */
+	&& justify_max_void_percent > 0
+	&& justify_max_void_percent <= 100
+	&& justify_max_void_percent >= ((100 * spare)
+					/ (WRAP_COLS(text)
+					   - (int) style->rightIndent
+					   - indent
+					   + ctrl_chars_on_previous_line))) {
+	/* this is the only case when we need justification */
+	char *jp = previous->data + justify_start_position;
+	ht_run_info *r = ht_runs;
+	char c;
+	int d_, r_;
+	HTLine *jline;
+
+	ht_num_runs = 0;
+	r->byte_len = r->cell_len = 0;
+
+	for (; (c = *jp) != 0; ++jp) {
+	    if (c == ' ') {
+		++r;
+		++ht_num_runs;
+		r->byte_len = r->cell_len = 0;
+		continue;
+	    }
+	    ++r->byte_len;
+	    if (IsSpecialAttrChar(c))
+		continue;
+
+	    ++r->cell_len;
+	    if (c == HT_NON_BREAK_SPACE) {
+		*jp = ' ';	/* substitute it */
+		continue;
+	    }
+	    if (text->T.output_utf8 && is8bits(c)) {
+		int utf_extra = utf8_length(text->T.output_utf8, jp);
+
+		r->byte_len += utf_extra;
+		jp += utf_extra;
+	    }
+	}
+	++ht_num_runs;
+
+	if (ht_num_runs != 1) {
+	    int *oldpos = (int *) malloc(sizeof(int) * 2 * (ht_num_runs - 1));
+	    int *newpos = oldpos + ht_num_runs - 1;
+	    int i = 1;
+
+	    if (oldpos == NULL)
+		outofmem(__FILE__, "split_line_3");
+
+	    d_ = spare / (ht_num_runs - 1);
+	    r_ = spare % (ht_num_runs - 1);
+
+	    /* The first run is not moved, proceed to the second one */
+	    oldpos[0] = justify_start_position + ht_runs[0].cell_len + 1;
+	    newpos[0] = oldpos[0] + (d_ + (r_-- > 0));
+	    while (i < ht_num_runs - 1) {
+		int delta = ht_runs[i].cell_len + 1;
+
+		oldpos[i] = oldpos[i - 1] + delta;
+		newpos[i] = newpos[i - 1] + delta + (d_ + (r_-- > 0));
+		i++;
+	    }
+	    jline = insert_blanks_in_line(previous, CurLine, text,
+					  &last_anchor_of_previous_line /*updates++ */ ,
+					  ht_num_runs - 1, oldpos, newpos);
+	    free(oldpos);
+	    if (jline == NULL)
+		outofmem(__FILE__, "split_line_4");
+	    previous->next->prev = jline;
+	    previous->prev->next = jline;
+
+	    freeHTLine(text, previous);
+
+	    previous = jline;
+	}
+	if (justify_start_position) {
+	    char *p2 = previous->data;
+
+	    for (; p2 < previous->data + justify_start_position; ++p2)
+		*p2 = (char) (*p2 == HT_NON_BREAK_SPACE ? ' ' : *p2);
+	}
+    } else {
+	if (REALLY_CAN_JUSTIFY(text)) {
+	    char *p2;
+
+	    /* it was permitted to justify line, but this function was called
+	     * to end paragraph - we must substitute HT_NON_BREAK_SPACEs with
+	     * spaces in previous line
+	     */
+	    if (line->size && !text->stbl) {
+		CTRACE((tfp,
+			"BUG: justification: shouldn't happen - new line is not empty!\n\t'%.*s'\n",
+			line->size, line->data));
+	    }
+
+	    for (p2 = previous->data; *p2; ++p2)
+		if (*p2 == HT_NON_BREAK_SPACE)
+		    *p2 = ' ';
+	} else if (have_raw_nbsps) {
+	    /* this is very rare case, that can happen in forms placed in
+	       table cells */
+	    unsigned i;
+
+	    for (i = 0; i < previous->size; ++i)
+		if (previous->data[i] == HT_NON_BREAK_SPACE)
+		    previous->data[i] = ' ';
+
+	    /*next line won't be justified, so substitute nbsps in it too */
+	    for (i = 0; i < line->size; ++i)
+		if (line->data[i] == HT_NON_BREAK_SPACE)
+		    line->data[i] = ' ';
+	}
+
+	/* else HT_NON_BREAK_SPACEs were substituted with spaces in
+	   HText_appendCharacter */
+    }
+    /* cleanup */
+    can_justify_this_line = TRUE;
+    justify_start_position = 0;
+    this_line_was_split = FALSE;
+    have_raw_nbsps = FALSE;
+#endif /* USE_JUSTIFY_ELTS */
+    return;
+}				/* split_line */
+
+#ifdef DEBUG_SPLITLINE
+static void do_new_line(HText *text, const char *fn, int ln)
+{
+    CTRACE_SPLITLINE((tfp, "new_line %s@%d\n", fn, ln));
+    split_line(text, 0);
+}
+
+#define new_line(text) do_new_line(text, __FILE__, __LINE__)
+#else
+#define new_line(text) split_line(text, 0)
+#endif
+
+/*	Allow vertical blank space
+ *	--------------------------
+ */
+static void blank_lines(HText *text, int newlines)
+{
+    if (HText_LastLineEmpty(text, FALSE)) {	/* No text on current line */
+	HTLine *line = text->last_line->prev;
+	BOOL first = (BOOL) (line == text->last_line);
+
+	if (no_title && first)
+	    return;
+
+#ifdef USE_COLOR_STYLE
+	/* Style-change petty requests at the start of the document: */
+	if (first && newlines == 1)
+	    return;		/* Do not add a blank line at start */
+#endif
+
+	while (line != NULL &&
+	       line != text->last_line &&
+	       HText_TrueEmptyLine(line, text, FALSE)) {
+	    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.
+ */
+void HText_appendParagraph(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.
+ */
+void HText_setStyle(HText *text, HTStyle *style)
+{
+    int after, before;
+
+    if (!style)
+	return;			/* Safety */
+    after = text->style->spaceAfter;
+    before = style->spaceBefore;
+
+    CTRACE((tfp, "GridText: Change to style %s\n", GetHTStyleName(style)));
+
+    blank_lines(text, ((after > before) ? after : before));
+
+    text->style = style;
+}
+
+/*	Append a character to the text object
+ *	-------------------------------------
+ */
+void HText_appendCharacter(HText *text, int ch)
+{
+    HTLine *line;
+    HTStyle *style;
+    int indent;
+    int actual;
+
+#ifdef DEBUG_APPCH
+#ifdef CJK_EX
+    static unsigned char save_ch = 0;
+#endif
+
+    if (TRACE) {
+	char *special = NULL;	/* make trace a little more readable */
+
+	switch (ch) {
+	case HT_NON_BREAK_SPACE:
+	    special = "HT_NON_BREAK_SPACE";
+	    break;
+	case HT_EN_SPACE:
+	    special = "HT_EN_SPACE";
+	    break;
+	case LY_UNDERLINE_START_CHAR:
+	    special = "LY_UNDERLINE_START_CHAR";
+	    break;
+	case LY_UNDERLINE_END_CHAR:
+	    special = "LY_UNDERLINE_END_CHAR";
+	    break;
+	case LY_BOLD_START_CHAR:
+	    special = "LY_BOLD_START_CHAR";
+	    break;
+	case LY_BOLD_END_CHAR:
+	    special = "LY_BOLD_END_CHAR";
+	    break;
+	case LY_SOFT_HYPHEN:
+	    special = "LY_SOFT_HYPHEN";
+	    break;
+	case LY_SOFT_NEWLINE:
+	    special = "LY_SOFT_NEWLINE";
+	    break;
+	default:
+	    special = NULL;
+	    break;
+	}
+
+	if (special != NULL) {
+	    CTRACE((tfp, "add(%s %d special char) %d/%d\n", special, ch,
+		    HTisDocumentSource(), HTOutputFormat != WWW_SOURCE));
+	} else {
+#ifdef CJK_EX			/* 1998/08/30 (Sun) 13:26:23 */
+	    if (save_ch == 0) {
+		if (IS_SJIS_HI1(ch) || IS_SJIS_HI2(ch)) {
+		    save_ch = ch;
+		} else {
+		    CTRACE((tfp, "add(%c) %d/%d\n", ch,
+			    HTisDocumentSource(), HTOutputFormat != WWW_SOURCE));
+		}
+	    } else {
+		CTRACE((tfp, "add(%c%c) %d/%d\n", save_ch, ch,
+			HTisDocumentSource(), HTOutputFormat != WWW_SOURCE));
+		save_ch = 0;
+	    }
+#else
+	    if (UCH(ch) < 0x80) {
+		CTRACE((tfp, "add(%c) %d/%d\n", UCH(ch),
+			HTisDocumentSource(), HTOutputFormat != WWW_SOURCE));
+	    } else {
+		CTRACE((tfp, "add(%02x) %d/%d\n", UCH(ch),
+			HTisDocumentSource(), HTOutputFormat != WWW_SOURCE));
+	    }
+#endif /* CJK_EX */
+	}
+    }				/* trace only */
+#endif /* DEBUG_APPCH */
+
+    /*
+     * Make sure we don't crash on NULLs.
+     */
+    if (!text)
+	return;
+
+    if (text->halted > 1) {
+	/*
+	 * We should stop outputting more text, because low memory was
+	 * detected.  - kw
+	 */
+	if (text->halted == 2) {
+	    /*
+	     * But if we haven't done so yet, first append a warning.
+	     * We should still have a few bytes left for that :).
+	     * We temporarily reset test->halted to 0 for this, since
+	     * this function will get called recursively.  - kw
+	     */
+	    text->halted = 0;
+	    text->kanji_buf = '\0';
+	    HText_appendText(text, gettext(" *** MEMORY EXHAUSTED ***"));
+	}
+	text->halted = 3;
+	return;
+    }
+#ifdef USE_TH_JP_AUTO_DETECT
+    if ((HTCJK == JAPANESE) && (text->detected_kcode != DET_MIXED) &&
+	(text->specified_kcode != SJIS) && (text->specified_kcode != EUC)) {
+	unsigned char c;
+	eDetectedKCode save_d_kcode;
+
+	c = UCH(ch);
+	save_d_kcode = text->detected_kcode;
+	switch (text->SJIS_status) {
+	case SJIS_state_has_bad_code:
+	    break;
+	case SJIS_state_neutral:
+	    if (IS_SJIS_HI1(c) || IS_SJIS_HI2(c)) {
+		text->SJIS_status = SJIS_state_in_kanji;
+	    } else if ((c & 0x80) && !IS_SJIS_X0201KANA(c)) {
+		text->SJIS_status = SJIS_state_has_bad_code;
+		if (text->EUC_status == EUC_state_has_bad_code)
+		    text->detected_kcode = DET_MIXED;
+		else
+		    text->detected_kcode = DET_EUC;
+	    }
+	    break;
+	case SJIS_state_in_kanji:
+	    if (IS_SJIS_LO(c)) {
+		text->SJIS_status = SJIS_state_neutral;
+	    } else {
+		text->SJIS_status = SJIS_state_has_bad_code;
+		if (text->EUC_status == EUC_state_has_bad_code)
+		    text->detected_kcode = DET_MIXED;
+		else
+		    text->detected_kcode = DET_EUC;
+	    }
+	    break;
+	}
+	switch (text->EUC_status) {
+	case EUC_state_has_bad_code:
+	    break;
+	case EUC_state_neutral:
+	    if (IS_EUC_HI(c)) {
+		text->EUC_status = EUC_state_in_kanji;
+	    } else if (c == 0x8e) {
+		text->EUC_status = EUC_state_in_kana;
+	    } else if (c & 0x80) {
+		text->EUC_status = EUC_state_has_bad_code;
+		if (text->SJIS_status == SJIS_state_has_bad_code)
+		    text->detected_kcode = DET_MIXED;
+		else
+		    text->detected_kcode = DET_SJIS;
+	    }
+	    break;
+	case EUC_state_in_kanji:
+	    if (IS_EUC_LOX(c)) {
+		text->EUC_status = EUC_state_neutral;
+	    } else {
+		text->EUC_status = EUC_state_has_bad_code;
+		if (text->SJIS_status == SJIS_state_has_bad_code)
+		    text->detected_kcode = DET_MIXED;
+		else
+		    text->detected_kcode = DET_SJIS;
+	    }
+	    break;
+	case EUC_state_in_kana:
+	    if ((0xA1 <= c) && (c <= 0xDF)) {
+		text->EUC_status = EUC_state_neutral;
+	    } else {
+		text->EUC_status = EUC_state_has_bad_code;
+		if (text->SJIS_status == SJIS_state_has_bad_code)
+		    text->detected_kcode = DET_MIXED;
+		else
+		    text->detected_kcode = DET_SJIS;
+	    }
+	    break;
+	}
+	if (save_d_kcode != text->detected_kcode) {
+	    switch (text->detected_kcode) {
+	    case DET_SJIS:
+		CTRACE((tfp,
+			"TH_JP_AUTO_DETECT: This document's kcode seems SJIS.\n"));
+		break;
+	    case DET_EUC:
+		CTRACE((tfp,
+			"TH_JP_AUTO_DETECT: This document's kcode seems EUC.\n"));
+		break;
+	    case DET_MIXED:
+		CTRACE((tfp,
+			"TH_JP_AUTO_DETECT: This document's kcode seems mixed!\n"));
+		break;
+	    default:
+		CTRACE((tfp,
+			"TH_JP_AUTO_DETECT: This document's kcode is unexpected!\n"));
+		break;
+	    }
+	}
+    }
+#endif /* USE_TH_JP_AUTO_DETECT */
+    /*
+     * Make sure we don't hang on escape sequences.
+     */
+    if (ch == CH_ESC && !IS_CJK_TTY) {	/* decimal 27  S/390 -- gil -- 1504 */
+	return;
+    }
+#ifndef USE_SLANG
+    /*
+     * Block 8-bit chars not allowed by the current display character
+     * set if they are below what LYlowest_eightbit indicates.
+     * Slang used its own replacements, so for USE_SLANG blocking here
+     * is not necessary to protect terminals from those characters.
+     * They should have been filtered out or translated by an earlier
+     * processing stage anyway.  - kw
+     */
+#ifndef   EBCDIC		/* S/390 -- gil -- 1514 */
+    if (is8bits(ch) && !IS_CJK_TTY &&
+	!text->T.transp && !text->T.output_utf8 &&
+	UCH(ch) < LYlowest_eightbit[current_char_set]) {
+	return;
+    }
+#endif /* EBCDIC */
+#endif /* !USE_SLANG */
+    if (UCH(ch) == 155 && !IS_CJK_TTY) {	/* octal 233 */
+	if (!HTPassHighCtrlRaw &&
+	    !text->T.transp && !text->T.output_utf8 &&
+	    (155 < LYlowest_eightbit[current_char_set])) {
+	    return;
+	}
+    }
+
+    line = text->last_line;
+    style = text->style;
+
+    indent = text->in_line_1 ? (int) style->indent1st : (int) style->leftIndent;
+
+    if (IS_CJK_TTY) {
+	switch (text->state) {
+	case S_text:
+	    if (ch == CH_ESC) {	/* S/390 -- gil -- 1536 */
+		/*
+		 * Setting up for CJK escape sequence handling (based on
+		 * Takuya ASADA's (asada@three-a.co.jp) CJK Lynx).  -FM
+		 */
+		text->state = S_esc;
+		text->kanji_buf = '\0';
+		return;
+	    }
+	    break;
+
+	case S_esc:
+	    /*
+	     * Expecting '$'or '(' following CJK ESC.
+	     */
+	    if (ch == '$') {
+		text->state = S_dollar;
+		return;
+	    } else if (ch == '(') {
+		text->state = S_paren;
+		return;
+	    } else {
+		text->state = S_text;
+	    }
+	    /* FALLTHRU */
+
+	case S_dollar:
+	    /*
+	     * Expecting '@', 'B', 'A' or '(' after CJK "ESC$".
+	     */
+	    if (ch == '@' || ch == 'B' || ch == 'A') {
+		text->state = S_nonascii_text;
+		if (ch == '@' || ch == 'B')
+		    text->kcode = JIS;
+		return;
+	    } else if (ch == '(') {
+		text->state = S_dollar_paren;
+		return;
+	    } else {
+		text->state = S_text;
+	    }
+	    break;
+
+	case S_dollar_paren:
+	    /*
+	     * Expecting 'C' after CJK "ESC$(".
+	     */
+	    if (ch == 'C') {
+		text->state = S_nonascii_text;
+		return;
+	    } else {
+		text->state = S_text;
+	    }
+	    break;
+
+	case S_paren:
+	    /*
+	     * Expecting 'B', 'J', 'T' or 'I' after CJK "ESC(".
+	     */
+	    if (ch == 'B' || ch == 'J' || ch == 'T') {
+		/*
+		 * Can split here.  -FM
+		 */
+		text->permissible_split = text->last_line->size;
+		text->state = S_text;
+		return;
+	    } else if (ch == 'I') {
+		text->state = S_jisx0201_text;
+		/*
+		 * Can split here.  -FM
+		 */
+		text->permissible_split = text->last_line->size;
+		text->kcode = JIS;
+		return;
+	    } else {
+		text->state = S_text;
+	    }
+	    break;
+
+	case S_nonascii_text:
+	    /*
+	     * Expecting CJK ESC after non-ASCII text.
+	     */
+	    if (ch == CH_ESC) {	/* S/390 -- gil -- 1553 */
+		text->state = S_esc;
+		text->kanji_buf = '\0';
+		if (HTCJK == JAPANESE) {
+		    text->kcode = NOKANJI;
+		}
+		return;
+	    } else if (UCH(ch) < 32) {
+		text->state = S_text;
+		text->kanji_buf = '\0';
+		if (HTCJK == JAPANESE) {
+		    text->kcode = NOKANJI;
+		}
+	    } else {
+		ch |= 0200;
+	    }
+	    break;
+
+	    /*
+	     * JIS X0201 Kana in JIS support.  - by ASATAKU
+	     */
+	case S_jisx0201_text:
+	    if (ch == CH_ESC) {	/* S/390 -- gil -- 1570 */
+		text->state = S_esc;
+		text->kanji_buf = '\0';
+		text->kcode = NOKANJI;
+		return;
+	    } else {
+		text->kanji_buf = '\216';
+		ch |= 0200;
+	    }
+	    break;
+	}			/* end switch */
+
+	if (!text->kanji_buf) {
+	    if ((ch & 0200) != 0) {
+		/*
+		 * JIS X0201 Kana in SJIS support.  - by ASATAKU
+		 */
+		if ((text->kcode != JIS)
+		    && (
+#ifdef KANJI_CODE_OVERRIDE
+			   (last_kcode == SJIS) ||
+			   ((last_kcode == NOKANJI) &&
+#endif
+			    ((text->kcode == SJIS) ||
+#ifdef USE_TH_JP_AUTO_DETECT
+			     ((text->detected_kcode == DET_SJIS) &&
+			      (text->specified_kcode == NOKANJI)) ||
+#endif
+			     ((text->kcode == NOKANJI) &&
+			      (text->specified_kcode == SJIS)))
+#ifdef KANJI_CODE_OVERRIDE
+			   )
+#endif
+		    ) &&
+		    (UCH(ch) >= 0xA1) &&
+		    (UCH(ch) <= 0xDF)) {
+#ifdef CONV_JISX0201KANA_JISX0208KANA
+		    unsigned char c = UCH(ch);
+		    unsigned char kb = UCH(text->kanji_buf);
+
+		    JISx0201TO0208_SJIS(c,
+					(unsigned char *) &kb,
+					(unsigned char *) &c);
+		    ch = (char) c;
+		    text->kanji_buf = kb;
+#endif
+		    /* 1998/01/19 (Mon) 09:06:15 */
+		    text->permissible_split = (int) text->last_line->size;
+		} else {
+		    text->kanji_buf = ch;
+		    /*
+		     * Can split here.  -FM
+		     */
+		    text->permissible_split = text->last_line->size;
+		    return;
+		}
+	    }
+	} else {
+	    goto check_WrapSource;
+	}
+    } else if (ch == CH_ESC) {	/* S/390 -- gil -- 1587 */
+	return;
+    }
+#ifdef CJK_EX			/* MOJI-BAKE Fix! 1997/10/12 -- 10/31 (Fri) 00:22:57 - JH7AYN */
+    if (IS_CJK_TTY &&		/* added condition - kw */
+	(ch == LY_BOLD_START_CHAR || ch == LY_BOLD_END_CHAR)) {
+	text->permissible_split = (int) line->size;	/* Can split here */
+	if (HTCJK == JAPANESE)
+	    text->kcode = NOKANJI;
+    }
+#endif
+
+    if (IsSpecialAttrChar(ch) && ch != LY_SOFT_NEWLINE) {
+#if !defined(USE_COLOR_STYLE) || !defined(NO_DUMP_WITH_BACKSPACES)
+	if (line->size >= (MAX_LINE - 1)) {
+	    return;
+	}
+#if defined(USE_COLOR_STYLE) && !defined(NO_DUMP_WITH_BACKSPACES)
+	if (with_backspaces && !IS_CJK_TTY && !text->T.output_utf8) {
+#endif
+	    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;
+	    } else if (ch == LY_SOFT_HYPHEN) {
+		int i;
+
+		/*
+		 * Ignore the soft hyphen if it is the first character
+		 * on the line, or if it is preceded by a space or
+		 * hyphen.  -FM
+		 */
+		if (line->size < 1 || text->permissible_split >= line->size) {
+		    return;
+		}
+
+		for (i = (text->permissible_split + 1); line->data[i]; i++) {
+		    if (!IsSpecialAttrChar(UCH(line->data[i])) &&
+			!isspace(UCH(line->data[i])) &&
+			UCH(line->data[i]) != '-' &&
+			UCH(line->data[i]) != HT_NON_BREAK_SPACE &&
+			UCH(line->data[i]) != HT_EN_SPACE) {
+			break;
+		    }
+		}
+		if (line->data[i] == '\0') {
+		    return;
+		}
+	    }
+#if defined(USE_COLOR_STYLE) && !defined(NO_DUMP_WITH_BACKSPACES)
+	} else {
+	    /* if (with_backspaces && HTCJK==HTNOCJK && !text->T.output_utf8) */
+	    return;
+	}
+#endif
+
+#else
+	return;
+#endif
+    } else if (ch == LY_SOFT_NEWLINE) {
+	line->data[line->size++] = LY_SOFT_NEWLINE;
+	line->data[line->size] = '\0';
+	return;
+    }
+
+    if (text->T.output_utf8) {
+	/*
+	 * Some extra checks for UTF-8 output here to make sure
+	 * memory is not overrun.  For a non-first char, append
+	 * to the line here and return.  - kw
+	 */
+	if (IS_UTF_EXTRA(ch)) {
+	    if ((line->size > (MAX_LINE - 1))
+		|| (indent + (int) (line->offset + line->size)
+		    + UTFXTRA_ON_THIS_LINE
+		    - ctrl_chars_on_this_line
+		    + ((line->size > 0) &&
+		       (int) (line->data[line->size - 1] ==
+			      LY_SOFT_HYPHEN ?
+			      1 : 0)) >= LYcols_cu(text))
+		) {
+		if (!text->permissible_split || text->source) {
+		    text->permissible_split = line->size;
+		    while (text->permissible_split > 0 &&
+			   IS_UTF_EXTRA(line->data[text->permissible_split - 1]))
+			text->permissible_split--;
+		    if (text->permissible_split &&
+			(line->data[text->permissible_split - 1] & 0x80))
+			text->permissible_split--;
+		    if (text->permissible_split == line->size)
+			text->permissible_split = 0;
+		}
+		split_line(text, text->permissible_split);
+		line = text->last_line;
+		if (text->source && line->size - ctrl_chars_on_this_line
+		    + UTFXTRA_ON_THIS_LINE == 0)
+		    HText_appendCharacter(text, LY_SOFT_NEWLINE);
+	    }
+	    line->data[line->size++] = (char) ch;
+	    line->data[line->size] = '\0';
+	    utfxtra_on_this_line++;
+	    ctrl_chars_on_this_line++;
+	    return;
+	} else if (ch & 0x80) {	/* a first char of UTF-8 sequence - kw */
+	    if ((line->size > (MAX_LINE - 7))) {
+		if (!text->permissible_split || text->source) {
+		    text->permissible_split = line->size;
+		    while (text->permissible_split > 0 &&
+			   (line->data[text->permissible_split - 1] & 0x80)
+			   == 0xC0) {
+			text->permissible_split--;
+		    }
+		    if (text->permissible_split == line->size)
+			text->permissible_split = 0;
+		}
+		split_line(text, text->permissible_split);
+		line = text->last_line;
+		if (text->source && line->size - ctrl_chars_on_this_line
+		    + UTFXTRA_ON_THIS_LINE == 0)
+		    HText_appendCharacter(text, LY_SOFT_NEWLINE);
+	    }
+	}
+    }
+
+    /*
+     * New Line.
+     */
+    if (ch == '\n') {
+	new_line(text);
+	text->in_line_1 = YES;	/* First line of new paragraph */
+	/*
+	 * There are some pages written in
+	 * different kanji codes.  - TA & kw
+	 */
+	if (HTCJK == JAPANESE)
+	    text->kcode = NOKANJI;
+	return;
+    }
+
+    /*
+     * Convert EN_SPACE to a space here so that it doesn't get collapsed.
+     */
+    if (ch == HT_EN_SPACE)
+	ch = ' ';
+
+#ifdef SH_EX			/* 1997/11/01 (Sat) 12:08:54 */
+    if (ch == 0x0b) {		/* ^K ??? */
+	ch = '\r';
+    }
+    if (ch == 0x1a) {		/* ^Z ??? */
+	ch = '\r';
+    }
+#endif
+
+    /*
+     * 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;
+	/*
+	 * There are some pages written in
+	 * different kanji codes.  - TA & kw
+	 */
+	if (HTCJK == JAPANESE)
+	    text->kcode = NOKANJI;
+	return;
+    }
+
+    /*
+     * Tabs.
+     */
+    if (ch == '\t') {
+	const HTTabStop *Tab;
+	int target, target_cu;	/* Where to tab to */
+	int here, here_cu;	/* in _cu we try to guess what curses thinks */
+
+	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 + line->offset) + indent)
+	    - ctrl_chars_on_this_line;	/* Consider special chars GAB */
+	here_cu = here + UTFXTRA_ON_THIS_LINE;
+	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 >= here)
+	    target_cu = target;
+	else
+	    target_cu = target + (here_cu - here);
+
+	if (target > WRAP_COLS(text) - (int) style->rightIndent &&
+	    HTOutputFormat != WWW_SOURCE) {
+	    new_line(text);
+	} else {
+	    /*
+	     * Can split here.  -FM
+	     */
+	    text->permissible_split = line->size;
+	    if (target_cu > WRAP_COLS(text))
+		target -= target_cu - WRAP_COLS(text);
+	    if (line->size == 0) {
+		line->offset += (target - here);
+	    } else {
+		for (; here < target; here++) {
+		    /* Put character into line */
+		    line->data[line->size++] = ' ';
+		    line->data[line->size] = '\0';
+		}
+	    }
+	}
+	return;
+    }
+    /* if tab */
+  check_WrapSource:
+    if ((text->source || dont_wrap_pre) && text == HTMainText) {
+	/*
+	 * If we're displaying document source, wrap long lines to keep all of
+	 * the source visible.
+	 */
+	int target = (int) (line->offset + line->size) - ctrl_chars_on_this_line;
+	int target_cu = target + UTFXTRA_ON_THIS_LINE;
+
+	if (target >= WRAP_COLS(text) - style->rightIndent -
+	    ((IS_CJK_TTY && text->kanji_buf) ? 1 : 0) ||
+	    (text->T.output_utf8 &&
+	     target_cu + UTF_XLEN(ch) >= LYcols_cu(text))) {
+	    int saved_kanji_buf;
+	    eGridState saved_state;
+
+	    new_line(text);
+	    line = text->last_line;
+
+	    saved_kanji_buf = text->kanji_buf;
+	    saved_state = text->state;
+	    text->kanji_buf = '\0';
+	    text->state = S_text;
+	    HText_appendCharacter(text, LY_SOFT_NEWLINE);
+	    text->kanji_buf = saved_kanji_buf;
+	    text->state = saved_state;
+	}
+    }
+
+    if (ch == ' ') {
+	/*
+	 * Can split here.  -FM
+	 */
+	text->permissible_split = text->last_line->size;
+	/*
+	 * There are some pages written in
+	 * different kanji codes.  - TA
+	 */
+	if (HTCJK == JAPANESE)
+	    text->kcode = NOKANJI;
+    }
+
+    /*
+     * 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)));
+
+    if ((actual
+	 + (int) style->rightIndent
+	 - ctrl_chars_on_this_line
+	 + ((IS_CJK_TTY && text->kanji_buf) ? 1 : 0)
+	) >= WRAP_COLS(text)
+	|| (text->T.output_utf8
+	    && ((actual
+		 + UTFXTRA_ON_THIS_LINE
+		 - ctrl_chars_on_this_line
+		 + UTF_XLEN(ch)
+		) > (LYcols_cu(text) - 1)))) {
+
+	if (style->wordWrap && HTOutputFormat != WWW_SOURCE) {
+#ifdef USE_JUSTIFY_ELTS
+	    if (REALLY_CAN_JUSTIFY(text))
+		this_line_was_split = TRUE;
+#endif
+	    split_line(text, text->permissible_split);
+	    if (ch == ' ') {
+		return;		/* Ignore space causing split */
+	    }
+
+	} else if (HTOutputFormat == WWW_SOURCE) {
+	    /*
+	     * For source output we don't want to wrap this stuff
+	     * unless absolutely necessary.  - 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.
+	     */
+	    if ((dump_output_immediately || (crawl && traversal))
+		&& dont_wrap_pre) {
+		if ((int) line->size >= (int) (MAX_LINE - 1)) {
+		    new_line(text);
+		}
+	    } else {
+		new_line(text);
+	    }
+	}
+    } else if ((int) line->size >= (int) (MAX_LINE - 1)) {
+	/*
+	 * Never overrun memory if DISPLAY_COLS is set to a large value - KW
+	 */
+	new_line(text);
+    }
+
+    /*
+     * Insert normal characters.
+     */
+    if (ch == HT_NON_BREAK_SPACE
+#ifdef USE_JUSTIFY_ELTS
+	&& !REALLY_CAN_JUSTIFY(text)
+#endif
+	)
+	ch = ' ';
+#ifdef USE_JUSTIFY_ELTS
+    else
+	have_raw_nbsps = TRUE;
+#endif
+
+    /* we leave raw HT_NON_BREAK_SPACE otherwise (we'll substitute it later) */
+
+    if (ch & 0x80)
+	text->have_8bit_chars = YES;
+
+    /*
+     * Kanji charactor handling.
+     */
+    {
+	HTFont font = style->font;
+	unsigned char hi, lo, tmp[2];
+
+	line = text->last_line;	/* May have changed */
+
+	if (IS_CJK_TTY && text->kanji_buf) {
+	    hi = UCH(text->kanji_buf);
+	    lo = UCH(ch);
+
+	    if (HTCJK == JAPANESE) {
+		if (text->kcode != JIS) {
+		    if (IS_SJIS_2BYTE(hi, lo)) {
+			if (IS_EUC(hi, lo)) {
+#ifdef KANJI_CODE_OVERRIDE
+			    if (last_kcode != NOKANJI)
+				text->kcode = last_kcode;
+			    else
+#endif
+			    if (text->specified_kcode != NOKANJI)
+				text->kcode = text->specified_kcode;
+#ifdef USE_TH_JP_AUTO_DETECT
+			    else if (text->detected_kcode == DET_EUC)
+				text->kcode = EUC;
+			    else if (text->detected_kcode == DET_SJIS)
+				text->kcode = SJIS;
+#endif
+			    else if (IS_EUC_X0201KANA(hi, lo) &&
+				     (text->kcode != EUC))
+				text->kcode = SJIS;
+			} else
+			    text->kcode = SJIS;
+		    } else if (IS_EUC(hi, lo))
+			text->kcode = EUC;
+		    else
+			text->kcode = NOKANJI;
+		}
+
+		switch (kanji_code) {
+		case EUC:
+		    if (text->kcode == SJIS) {
+			SJIS_TO_EUC1(hi, lo, tmp);
+			line->data[line->size++] = (char) tmp[0];
+			line->data[line->size++] = (char) tmp[1];
+		    } else if (IS_EUC(hi, lo)) {
+#ifdef CONV_JISX0201KANA_JISX0208KANA
+			JISx0201TO0208_EUC(hi, lo, &hi, &lo);
+#endif
+			line->data[line->size++] = (char) hi;
+			line->data[line->size++] = (char) lo;
+		    } else {
+			CTRACE((tfp,
+				"This character (%X:%X) doesn't seem Japanese\n",
+				hi, lo));
+			line->data[line->size++] = '=';
+			line->data[line->size++] = '=';
+		    }
+		    break;
+
+		case SJIS:
+		    if ((text->kcode == EUC) || (text->kcode == JIS)) {
+#ifndef CONV_JISX0201KANA_JISX0208KANA
+			if (IS_EUC_X0201KANA(hi, lo))
+			    line->data[line->size++] = lo;
+			else
+#endif
+			{
+			    EUC_TO_SJIS1(hi, lo, tmp);
+			    line->data[line->size++] = (char) tmp[0];
+			    line->data[line->size++] = (char) tmp[1];
+			}
+		    } else if (IS_SJIS_2BYTE(hi, lo)) {
+			line->data[line->size++] = (char) hi;
+			line->data[line->size++] = (char) lo;
+		    } else {
+			line->data[line->size++] = '=';
+			line->data[line->size++] = '=';
+			CTRACE((tfp,
+				"This character (%X:%X) doesn't seem Japanese\n",
+				hi, lo));
+		    }
+		    break;
+
+		default:
+		    break;
+		}
+	    } else {
+		line->data[line->size++] = (char) hi;
+		line->data[line->size++] = (char) lo;
+	    }
+	    text->kanji_buf = 0;
+	}
+#ifndef CONV_JISX0201KANA_JISX0208KANA
+	else if ((HTCJK == JAPANESE) && IS_SJIS_X0201KANA(UCH((ch))) &&
+		 (kanji_code == EUC)) {
+	    line->data[line->size++] = UCH(0x8e);
+	    line->data[line->size++] = ch;
+	}
+#endif
+	else if (IS_CJK_TTY) {
+	    line->data[line->size++] = (char) ((kanji_code != NOKANJI) ?
+					       ch :
+					       (font & HT_CAPITALS) ?
+					       TOUPPER(ch) : ch);
+	} else {
+	    line->data[line->size++] =	/* Put character into line */
+		(char) (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++;
+	    /*
+	     * Can split here.  -FM
+	     */
+	    text->permissible_split = text->last_line->size;
+	}
+	if (ch == LY_SOFT_NEWLINE) {
+	    ctrl_chars_on_this_line++;
+	}
+    }
+    return;
+}
+
+#ifdef USE_COLOR_STYLE
+/*  Insert a style change into the current line
+ *  -------------------------------------------
+ */
+void _internal_HTC(HText *text, int style, int dir)
+{
+    HTLine *line;
+
+    /* can't change style if we have no text to change style with */
+    if (text != 0) {
+
+	line = text->last_line;
+
+	if (line->numstyles > 0 && dir == 0 &&
+	    line->styles[line->numstyles - 1].sc_direction &&
+	    line->styles[line->numstyles - 1].sc_style == (unsigned) style &&
+	    (int) line->styles[line->numstyles - 1].sc_horizpos
+	    == (int) line->size - ctrl_chars_on_this_line) {
+	    /*
+	     * If this is an OFF change directly preceded by an
+	     * ON for the same style, just remove the previous one.  - kw
+	     */
+	    line->numstyles--;
+	} else if (line->numstyles < MAX_STYLES_ON_LINE) {
+	    line->styles[line->numstyles].sc_horizpos = line->size;
+	    /*
+	     * Special chars for bold and underlining usually don't
+	     * occur with color style, but soft hyphen can.
+	     * And in UTF-8 display mode all non-initial bytes are
+	     * counted as ctrl_chars.  - kw
+	     */
+	    if ((int) line->styles[line->numstyles].sc_horizpos >= ctrl_chars_on_this_line) {
+		line->styles[line->numstyles].sc_horizpos -= ctrl_chars_on_this_line;
+	    }
+	    line->styles[line->numstyles].sc_style = style;
+	    line->styles[line->numstyles].sc_direction = dir;
+	    CTRACE_STYLE((tfp, "internal_HTC %d:style[%d] %d (dir=%d)\n",
+			  line->size,
+			  line->numstyles,
+			  style,
+			  dir));
+	    line->numstyles++;
+	}
+    }
+}
+#endif
+
+/*	Set LastChar element in the text object.
+ *	----------------------------------------
+ */
+void HText_setLastChar(HText *text, char ch)
+{
+    if (!text)
+	return;
+
+    text->LastChar = ch;
+}
+
+/*	Get LastChar element in the text object.
+ *	----------------------------------------
+ */
+char HText_getLastChar(HText *text)
+{
+    if (!text)
+	return ('\0');
+
+    return ((char) text->LastChar);
+}
+
+/*		Simple table handling - private
+ *		-------------------------------
+ */
+
+/*
+ * HText_insertBlanksInStblLines fixes up table lines when simple table
+ * processing is closed, by calling insert_blanks_in_line for lines
+ * that need fixup.  Also recalculates alignment for those lines,
+ * does additional updating of anchor positions, and makes sure the
+ * display of the lines on screen will be updated after partial display
+ * upon return to mainloop.  - kw
+ */
+static int HText_insertBlanksInStblLines(HText *me, int ncols)
+{
+    HTLine *line;
+    HTLine *mod_line, *first_line = NULL;
+    int *oldpos;
+    int *newpos;
+    int ninserts, lineno;
+    int last_lineno, first_lineno_pass2;
+
+#ifdef EXP_NESTED_TABLES
+    int last_nonempty = -1;
+#endif
+    int added_chars_before = 0;
+    int lines_changed = 0;
+    int max_width = 0, indent, spare, table_offset;
+    HTStyle *style;
+    short alignment;
+    int i = 0;
+
+    lineno = Stbl_getStartLine(me->stbl);
+    if (lineno < 0 || lineno > me->Lines)
+	return -1;
+    /*
+     * oldpos, newpos:  allocate space for two int arrays.
+     */
+    oldpos = typecallocn(int, 2 * ncols);
+    if (!oldpos)
+	return -1;
+    else
+	newpos = oldpos + ncols;
+    for (line = FirstHTLine(me); i < lineno; line = line->next, i++) {
+	if (!line) {
+	    free(oldpos);
+	    return -1;
+	}
+    }
+    first_lineno_pass2 = last_lineno = me->Lines;
+    for (; line && lineno <= last_lineno; line = line->next, lineno++) {
+	ninserts = Stbl_getFixupPositions(me->stbl, lineno, oldpos, newpos);
+	if (ninserts < 0)
+	    continue;
+	if (!first_line) {
+	    first_line = line;
+	    first_lineno_pass2 = lineno;
+	    if (TRACE) {
+		int ip;
+
+		CTRACE((tfp, "line %d first to adjust  --  newpos:", lineno));
+		for (ip = 0; ip < ncols; ip++)
+		    CTRACE((tfp, " %d", newpos[ip]));
+		CTRACE((tfp, "\n"));
+	    }
+	}
+	if (line == me->last_line) {
+	    if (line->size == 0 || HText_TrueEmptyLine(line, me, FALSE))
+		continue;
+	    /*
+	     * Last ditch effort to end the table with a line break,
+	     * if HTML_end_element didn't do it.  - kw
+	     */
+	    if (first_line == line)	/* obscure: all table on last line... */
+		first_line = NULL;
+	    new_line(me);
+	    line = me->last_line->prev;
+	    if (first_line == NULL)
+		first_line = line;
+	}
+	if (ninserts == 0) {
+	    /*  Do it also for no positions (but not error) */
+	    int width = HText_TrueLineSize(line, me, FALSE);
+
+	    if (width > max_width)
+		max_width = width;
+#ifdef EXP_NESTED_TABLES
+	    if (nested_tables) {
+		if (width && last_nonempty < lineno)
+		    last_nonempty = lineno;
+	    }
+#endif
+	    CTRACE((tfp, "line %d true/max width:%d/%d oldpos: NONE\n",
+		    lineno, width, max_width));
+	    continue;
+	}
+	mod_line = insert_blanks_in_line(line, lineno, me,
+					 &me->last_anchor_before_stbl /*updates++ */ ,
+					 ninserts, oldpos, newpos);
+	if (mod_line) {
+	    if (line == me->last_line) {
+		me->last_line = mod_line;
+	    } else {
+		added_chars_before += (mod_line->size - line->size);
+	    }
+	    line->prev->next = mod_line;
+	    line->next->prev = mod_line;
+	    lines_changed++;
+	    if (line == first_line)
+		first_line = mod_line;
+	    freeHTLine(me, line);
+	    line = mod_line;
+#ifdef DISP_PARTIAL
+	    /*
+	     * Make sure modified lines get fully re-displayed after
+	     * loading with partial display is done.
+	     */
+	    if (me->first_lineno_last_disp_partial >= 0) {
+		if (me->first_lineno_last_disp_partial >= lineno) {
+		    ResetPartialLinenos(me);
+		} else if (me->last_lineno_last_disp_partial >= lineno) {
+		    me->last_lineno_last_disp_partial = lineno - 1;
+		}
+	    }
+#endif
+	} {
+	    int width = HText_TrueLineSize(line, me, FALSE);
+
+	    if (width > max_width)
+		max_width = width;
+#ifdef EXP_NESTED_TABLES
+	    if (nested_tables) {
+		if (width && last_nonempty < lineno)
+		    last_nonempty = lineno;
+	    }
+#endif
+	    if (TRACE) {
+		int ip;
+
+		CTRACE((tfp, "line %d true/max width:%d/%d oldpos:",
+			lineno, width, max_width));
+		for (ip = 0; ip < ninserts; ip++)
+		    CTRACE((tfp, " %d", oldpos[ip]));
+		CTRACE((tfp, "\n"));
+	    }
+	}
+    }
+    /*
+     * Line offsets have been set based on the paragraph style, and
+     * have already been updated for centering or right-alignment
+     * for each line in split_line.  Here we want to undo all that, and
+     * align the table as a whole (i.e.  all lines for which
+     * Stbl_getFixupPositions returned >= 0).  All those lines have to
+     * get the same offset, for the simple table formatting mechanism
+     * to make sense, and that may not actually be the case at this point.
+     *
+     * What indentation and alignment do we want for the table as
+     * a whole?  Let's take most style properties from me->style.
+     * With some luck, it is the appropriate style for the element
+     * enclosing the TABLE.  But let's take alignment from the attribute
+     * of the TABLE itself instead, if it was specified.
+     *
+     * Note that this logic assumes that all lines have been finished
+     * by split_line.  The order of calls made by HTML_end_element for
+     * HTML_TABLE should take care of this.
+     */
+    style = me->style;
+    alignment = Stbl_getAlignment(me->stbl);
+    if (alignment == HT_ALIGN_NONE)
+	alignment = style->alignment;
+    indent = style->leftIndent;
+    /* Calculate spare character positions */
+    spare = WRAP_COLS(me) -
+	(int) style->rightIndent - indent - max_width;
+    if (spare < 0 && (int) style->rightIndent + spare >= 0) {
+	/*
+	 * Not enough room!  But we can fit if we ignore right indentation,
+	 * so let's do that.
+	 */
+	spare = 0;
+    } else if (spare < 0) {
+	spare += style->rightIndent;	/* ignore right indent, but need more */
+    }
+    if (spare < 0 && indent + spare >= 0) {
+	/*
+	 * Still not enough room.  But we can move to the left.
+	 */
+	indent += spare;
+	spare = 0;
+    } else if (spare < 0) {
+	/*
+	 * Still not enough.  Something went wrong.  Try the best we
+	 * can do.
+	 */
+	CTRACE((tfp,
+		"BUG: insertBlanks: resulting table too wide by %d positions!\n",
+		-spare));
+	indent = spare = 0;
+    }
+    /*
+     * Align left, right or center.
+     */
+    switch (alignment) {
+    case HT_CENTER:
+	table_offset = indent + spare / 2;
+	break;
+    case HT_RIGHT:
+	table_offset = indent + spare;
+	break;
+    case HT_LEFT:
+    case HT_JUSTIFY:
+    default:
+	table_offset = indent;
+	break;
+    }				/* switch */
+
+    CTRACE((tfp, "changing offsets"));
+    for (line = first_line, lineno = first_lineno_pass2;
+	 line && lineno <= last_lineno && line != me->last_line;
+	 line = line->next, lineno++) {
+	ninserts = Stbl_getFixupPositions(me->stbl, lineno, oldpos, newpos);
+	if (ninserts >= 0 && (int) line->offset != table_offset) {
+#ifdef DISP_PARTIAL
+	    /*  As above make sure modified lines get fully re-displayed */
+	    if (me->first_lineno_last_disp_partial >= 0) {
+		if (me->first_lineno_last_disp_partial >= lineno) {
+		    ResetPartialLinenos(me);
+		} else if (me->last_lineno_last_disp_partial >= lineno) {
+		    me->last_lineno_last_disp_partial = lineno - 1;
+		}
+	    }
+#endif
+	    CTRACE((tfp, " %d:%d", lineno, table_offset - line->offset));
+	    line->offset = (table_offset > 0
+			    ? table_offset
+			    : 0);
+	}
+    }
+#ifdef EXP_NESTED_TABLES
+    if (nested_tables) {
+	if (max_width)
+	    Stbl_update_enclosing(me->stbl, max_width, last_nonempty);
+    }
+#endif
+    CTRACE((tfp, " %d:done\n", lineno));
+    free(oldpos);
+    return lines_changed;
+}
+
+/*		Simple table handling - public functions
+ *		----------------------------------------
+ */
+
+/*	Cancel simple table handling
+*/
+void HText_cancelStbl(HText *me)
+{
+    if (!me || !me->stbl) {
+	CTRACE((tfp, "cancelStbl: ignored.\n"));
+	return;
+    }
+    CTRACE((tfp, "cancelStbl: ok, will do.\n"));
+#ifdef EXP_NESTED_TABLES
+    if (nested_tables) {
+	STable_info *stbl = me->stbl;
+
+	while (stbl) {
+	    STable_info *enclosing = Stbl_get_enclosing(stbl);
+
+	    Stbl_free(stbl);
+	    stbl = enclosing;
+	}
+    } else
+#endif
+	Stbl_free(me->stbl);
+    me->stbl = NULL;
+}
+
+/*	Start simple table handling
+*/
+void HText_startStblTABLE(HText *me, short alignment)
+{
+#ifdef EXP_NESTED_TABLES
+    STable_info *current = me->stbl;
+#endif
+
+    if (!me)
+	return;
+
+#ifdef EXP_NESTED_TABLES
+    if (nested_tables) {
+	if (current)
+	    new_line(me);
+    } else
+#endif
+    {
+	if (me->stbl)
+	    HText_cancelStbl(me);	/* auto cancel previously open table */
+    }
+
+    me->stbl = Stbl_startTABLE(alignment);
+    if (me->stbl) {
+	CTRACE((tfp, "startStblTABLE: started.\n"));
+#ifdef EXP_NESTED_TABLES
+	if (nested_tables) {
+	    Stbl_set_enclosing(me->stbl, current, me->last_anchor_before_stbl);
+	}
+#endif
+	me->last_anchor_before_stbl = me->last_anchor;
+    } else {
+	CTRACE((tfp, "startStblTABLE: failed.\n"));
+    }
+}
+
+#ifdef EXP_NESTED_TABLES
+static void free_enclosed_stbl(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", (void *) 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.
+ */
+BOOLEAN HText_endStblTABLE(HText *me)
+{
+    int ncols, lines_changed = 0;
+    STable_info *enclosing = NULL;
+
+    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));
+#ifdef DISP_PARTIAL
+	/* allow HTDisplayPartial() to redisplay the changed lines.
+	 * There is no harm if we got several stbl in the document, hope so.
+	 */
+	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);
+	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", (void *) me->stbl));
+	}
+	me->stbl = enclosing;
+    } 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" : "", (void *) enclosing));
+
+    return (BOOLEAN) (enclosing != 0);
+}
+
+/*	Start simple table row
+*/
+void HText_startStblTR(HText *me, short alignment)
+{
+    if (!me || !me->stbl)
+	return;
+    if (Stbl_addRowToTable(me->stbl, alignment, me->Lines) < 0)
+	HText_cancelStbl(me);	/* give up */
+}
+
+/*	Finish simple table row
+*/
+void HText_endStblTR(HText *me)
+{
+    if (!me || !me->stbl)
+	return;
+    /* should this do something?? */
+}
+
+/*	Start simple table cell
+*/
+void HText_startStblTD(HText *me, int colspan,
+		       int rowspan,
+		       short alignment,
+		       BOOL isheader)
+{
+    if (!me || !me->stbl)
+	return;
+    if (colspan < 0)
+	colspan = 1;
+    if (colspan > TRST_MAXCOLSPAN) {
+	CTRACE((tfp, "*** COLSPAN=%d is too large, ignored!\n", colspan));
+	colspan = 1;
+    }
+    if (rowspan > TRST_MAXROWSPAN) {
+	CTRACE((tfp, "*** ROWSPAN=%d is too large, ignored!\n", rowspan));
+	rowspan = 1;
+    }
+    if (Stbl_addCellToTable(me->stbl, colspan, rowspan, alignment, isheader,
+			    me->Lines,
+			    HText_LastLineOffset(me),
+			    HText_LastLineSize(me, FALSE)) < 0)
+	HText_cancelStbl(me);	/* give up */
+}
+
+/*	Finish simple table cell
+*/
+void HText_endStblTD(HText *me)
+{
+    if (!me || !me->stbl)
+	return;
+    if (Stbl_finishCellInTable(me->stbl, TRST_ENDCELL_ENDTD,
+			       me->Lines,
+			       HText_LastLineOffset(me),
+			       HText_LastLineSize(me, FALSE)) < 0)
+	HText_cancelStbl(me);	/* give up */
+}
+
+/*	Remember COL info / Start a COLGROUP and remember info
+*/
+void HText_startStblCOL(HText *me, int span,
+			short alignment,
+			BOOL isgroup)
+{
+    if (!me || !me->stbl)
+	return;
+    if (span <= 0)
+	span = 1;
+    if (span > TRST_MAXCOLSPAN) {
+	CTRACE((tfp, "*** SPAN=%d is too large, ignored!\n", span));
+	span = 1;
+    }
+    if (Stbl_addColInfo(me->stbl, span, alignment, isgroup) < 0)
+	HText_cancelStbl(me);	/* give up */
+}
+
+/*	Finish a COLGROUP
+*/
+void HText_endStblCOLGROUP(HText *me)
+{
+    if (!me || !me->stbl)
+	return;
+    if (Stbl_finishColGroup(me->stbl) < 0)
+	HText_cancelStbl(me);	/* give up */
+}
+
+/*	Start a THEAD / TFOOT / TBODY - remember its alignment info
+*/
+void HText_startStblRowGroup(HText *me, short alignment)
+{
+    if (!me || !me->stbl)
+	return;
+    if (Stbl_addRowGroup(me->stbl, alignment) < 0)
+	HText_cancelStbl(me);	/* give up */
+}
+
+/*		Anchor handling
+ *		---------------
+ */
+static void add_link_number(HText *text, TextAnchor *a, BOOL save_position)
+{
+    char marker[32];
+
+    /*
+     * If we are doing link_numbering add the link number.
+     */
+    if ((a->number > 0)
+#ifdef USE_PRETTYSRC
+	&& (text->source ? !psrcview_no_anchor_numbering : 1)
+#endif
+	&& links_are_numbered()) {
+	char saved_lastchar = text->LastChar;
+	int saved_linenum = text->Lines;
+
+	sprintf(marker, "[%d]", a->number);
+	HText_appendText(text, marker);
+	if (saved_linenum && text->Lines && saved_lastchar != ' ')
+	    text->LastChar = ']';	/* if marker not after space caused split */
+	if (save_position) {
+	    a->line_num = text->Lines;
+	    a->line_pos = text->last_line->size;
+	}
+    }
+}
+
+/*	Start an anchor field
+*/
+int HText_beginAnchor(HText *text, BOOL underline,
+		      HTChildAnchor *anc)
+{
+    TextAnchor *a;
+
+    POOLtypecalloc(TextAnchor, a);
+
+    if (a == NULL)
+	outofmem(__FILE__, "HText_beginAnchor");
+
+    assert(a != NULL);
+
+    a->inUnderline = underline;
+
+    a->sgml_offset = SGML_offset();
+    a->line_num = text->Lines;
+    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;
+
+#ifndef DONT_TRACK_INTERNAL_LINKS
+    if (HTAnchor_followTypedLink(anc, HTInternalLink)) {
+	a->number = ++(text->last_anchor_number);
+	a->link_type = INTERNAL_LINK_ANCHOR;
+    } else
+#endif
+    if (HTAnchor_followLink(anc)) {
+	a->number = ++(text->last_anchor_number);
+    } else {
+	a->number = 0;
+    }
+
+    if (number_links_on_left)
+	add_link_number(text, a, TRUE);
+    return (a->number);
+}
+
+/* If !really, report whether the anchor is empty. */
+static BOOL HText_endAnchor0(HText *text, int number,
+			     int really)
+{
+    TextAnchor *a;
+
+    /*
+     * The number argument is set to 0 in HTML.c and
+     * LYCharUtils.c when we want to end the anchor
+     * for the immediately preceding HText_beginAnchor()
+     * call.  If it's greater than 0, we want to handle
+     * a particular anchor.  This allows us to set links
+     * for positions indicated by NAME or ID attributes,
+     * without needing to close any anchor with an HREF
+     * within which that link might be embedded.  -FM
+     */
+    if (number <= 0 || number == text->last_anchor->number) {
+	a = text->last_anchor;
+    } else {
+	for (a = text->first_anchor; a; a = a->next) {
+	    if (a->number == number) {
+		break;
+	    }
+	}
+	if (a == NULL) {
+	    /*
+	     * There's no anchor with that number,
+	     * so we'll default to the last anchor,
+	     * and cross our fingers.  -FM
+	     */
+	    a = text->last_anchor;
+	}
+    }
+
+    CTRACE((tfp, "GridText:HText_endAnchor0: number:%d link_type:%d\n",
+	    a->number, a->link_type));
+    if (a->link_type == INPUT_ANCHOR) {
+	/*
+	 * Shouldn't happen, but put test here anyway to be safe.  - LE
+	 */
+
+	CTRACE((tfp,
+		"BUG: HText_endAnchor0: internal error: last anchor was input field!\n"));
+	return FALSE;
+    }
+
+    if (a->number) {
+	/*
+	 * If it goes somewhere...
+	 */
+	int i, j, k, l;
+	BOOL remove_numbers_on_empty = (BOOL) ((links_are_numbered() &&
+						((text->hiddenlinkflag != HIDDENLINKS_MERGE)
+						 || (LYNoISMAPifUSEMAP &&
+						     !(text->node_anchor && text->node_anchor->bookmark)
+						     && HTAnchor_isISMAPScript
+						     (HTAnchor_followLink(a->anchor))))));
+	HTLine *last = text->last_line;
+	HTLine *prev = text->last_line->prev;
+	HTLine *start = last;
+	int CurBlankExtent = 0;
+	int BlankExtent = 0;
+	int extent_adjust = 0;
+
+	/* Find the length taken by the anchor */
+	l = text->Lines;	/* lineno of last */
+
+	/* the last line of an anchor may contain a trailing blank,
+	 * which will be trimmed later.  Discount it from the extent.
+	 */
+	if (l > a->line_num) {
+	    for (i = start->size; i > 0; --i) {
+		if (isspace(UCH(start->data[i - 1]))) {
+		    --extent_adjust;
+		} else {
+		    break;
+		}
+	    }
+	}
+
+	while (l > a->line_num) {
+	    extent_adjust += start->size;
+	    start = start->prev;
+	    l--;
+	}
+	/* Now start is the start line of the anchor */
+	extent_adjust += start->size - a->line_pos;
+	start = last;		/* Used later */
+
+	/*
+	 * Check if the anchor content has only
+	 * white and special characters, starting
+	 * with the content on the last line.  -FM
+	 */
+	a->extent += extent_adjust;
+	if (a->extent > (int) last->size) {
+	    /*
+	     * The anchor extends over more than one line,
+	     * so set up to check the entire last line.  -FM
+	     */
+	    i = last->size;
+	} else {
+	    /*
+	     * The anchor is restricted to the last line,
+	     * so check from the start of the anchor.  -FM
+	     */
+	    i = a->extent;
+	}
+	k = j = (last->size - i);
+	while (j < (int) last->size) {
+	    if (!IsSpecialAttrChar(last->data[j]) &&
+		!isspace(UCH(last->data[j])) &&
+		last->data[j] != HT_NON_BREAK_SPACE &&
+		last->data[j] != HT_EN_SPACE)
+		break;
+	    i--;
+	    j++;
+	}
+	if (i == 0) {
+	    if (a->extent > (int) last->size) {
+		/*
+		 * The anchor starts on a preceding line, and
+		 * the last line has only white and special
+		 * characters, so declare the entire extent
+		 * of the last line as blank.  -FM
+		 */
+		CurBlankExtent = BlankExtent = last->size;
+	    } else {
+		/*
+		 * The anchor starts on the last line, and
+		 * has only white or special characters, so
+		 * declare the anchor's extent as blank.  -FM
+		 */
+		CurBlankExtent = BlankExtent = a->extent;
+	    }
+	}
+	/*
+	 * While the anchor starts on a line preceding
+	 * the one we just checked, and the one we just
+	 * checked has only white and special characters,
+	 * check whether the anchor's content on the
+	 * immediately preceding line also has only
+	 * white and special characters.  -FM
+	 */
+	while (i == 0 &&
+	       (a->extent > CurBlankExtent ||
+		(a->extent == CurBlankExtent &&
+		 k == 0 &&
+		 prev != text->last_line &&
+		 (prev->size == 0 ||
+		  prev->data[prev->size - 1] == ']')))) {
+	    start = prev;
+	    k = j = prev->size - a->extent + CurBlankExtent;
+	    if (j < 0) {
+		/*
+		 * The anchor starts on a preceding line,
+		 * so check all of this line.  -FM
+		 */
+		j = 0;
+		i = prev->size;
+	    } else {
+		/*
+		 * The anchor starts on this line.  -FM
+		 */
+		i = a->extent - CurBlankExtent;
+	    }
+	    while (j < (int) prev->size) {
+		if (!IsSpecialAttrChar(prev->data[j]) &&
+		    !isspace(UCH(prev->data[j])) &&
+		    prev->data[j] != HT_NON_BREAK_SPACE &&
+		    prev->data[j] != HT_EN_SPACE)
+		    break;
+		i--;
+		j++;
+	    }
+	    if (i == 0) {
+		if (a->extent > (CurBlankExtent + (int) prev->size) ||
+		    (a->extent == CurBlankExtent + (int) prev->size &&
+		     k == 0 &&
+		     prev->prev != text->last_line &&
+		     (prev->prev->size == 0 ||
+		      prev->prev->data[prev->prev->size - 1] == ']'))) {
+		    /*
+		     * This line has only white and special
+		     * characters, so treat its entire extent
+		     * as blank, and decrement the pointer for
+		     * the line to be analyzed.  -FM
+		     */
+		    CurBlankExtent += prev->size;
+		    BlankExtent = CurBlankExtent;
+		    prev = prev->prev;
+		} else {
+		    /*
+		     * The anchor starts on this line, and it
+		     * has only white or special characters, so
+		     * declare the anchor's extent as blank.  -FM
+		     */
+		    BlankExtent = a->extent;
+		    break;
+		}
+	    }
+	}
+	if (!really) {		/* Just report whether it is empty */
+	    a->extent -= extent_adjust;
+	    return (BOOL) (i == 0);
+	}
+	if (i == 0) {
+	    /*
+	     * It's an invisible anchor probably from an ALT=""
+	     * or an ignored ISMAP attribute due to a companion
+	     * USEMAP.  -FM
+	     */
+	    a->show_anchor = NO;
+
+	    CTRACE((tfp,
+		    "HText_endAnchor0: hidden (line,pos,ext,BlankExtent):(%d,%d,%d,%d)",
+		    a->line_num, a->line_pos, a->extent,
+		    BlankExtent));
+
+	    /*
+	     * If links are numbered, then try to get rid of the
+	     * numbered bracket and adjust the anchor count.  -FM
+	     *
+	     * Well, let's do this only if -hiddenlinks=merged is not in
+	     * effect, or if we can be reasonably sure that
+	     * this is the result of an intentional non-generation of
+	     * anchor text via NO_ISMAP_IF_USEMAP.  In other cases it can
+	     * actually be a feature that numbered links alert the viewer
+	     * to the presence of a link which is otherwise not selectable -
+	     * possibly caused by HTML errors. - kw
+	     */
+	    if (remove_numbers_on_empty) {
+		int NumSize = 0;
+		TextAnchor *anc;
+
+		/*
+		 * Set start->data[j] to the close-square-bracket,
+		 * or to the beginning of the line on which the
+		 * anchor start.  -FM
+		 */
+		if (start == last) {
+		    /*
+		     * The anchor starts on the last line.  -FM
+		     */
+		    j = (last->size - a->extent - 1);
+		} else {
+		    /*
+		     * The anchor starts on a previous line.  -FM
+		     */
+		    prev = start->prev;
+		    j = (start->size - a->extent + CurBlankExtent - 1);
+		}
+		if (j < 0)
+		    j = 0;
+		i = j;
+
+		/*
+		 * If start->data[j] is a close-square-bracket, verify
+		 * that it's the end of the numbered bracket, and if so,
+		 * strip the numbered bracket.  If start->data[j] is not
+		 * a close-square-bracket, check whether we had a wrap
+		 * and the close-square-bracket is at the end of the
+		 * previous line.  If so, strip the numbered bracket
+		 * from that line.  -FM
+		 */
+		if (start->data[j] == ']') {
+		    j--;
+		    NumSize++;
+		    while (j >= 0 && isdigit(UCH(start->data[j]))) {
+			j--;
+			NumSize++;
+		    }
+		    while (j < 0) {
+			j++;
+			NumSize--;
+		    }
+		    if (start->data[j] == '[') {
+			/*
+			 * The numbered bracket is entirely
+			 * on this line.  -FM
+			 */
+			NumSize++;
+			if (start == last && (int) text->permissible_split > j) {
+			    if ((int) text->permissible_split - NumSize < j)
+				text->permissible_split = j;
+			    else
+				text->permissible_split -= NumSize;
+			}
+			k = j + NumSize;
+			while (k < (int) start->size)
+			    start->data[j++] = start->data[k++];
+			for (anc = a; anc; anc = anc->next) {
+			    if (anc->line_num == a->line_num &&
+				anc->line_pos >= NumSize) {
+				anc->line_pos -= NumSize;
+			    }
+			}
+			start->size = j;
+			start->data[j++] = '\0';
+			while (j < k)
+			    start->data[j++] = '\0';
+		    } else if (prev && prev->size > 1) {
+			k = (i + 1);
+			j = (prev->size - 1);
+			while ((j >= 0) && IsSpecialAttrChar(prev->data[j]))
+			    j--;
+			i = (j + 1);
+			while (j >= 0 &&
+			       isdigit(UCH(prev->data[j]))) {
+			    j--;
+			    NumSize++;
+			}
+			while (j < 0) {
+			    j++;
+			    NumSize--;
+			}
+			if (prev->data[j] == '[') {
+			    /*
+			     * The numbered bracket started on the
+			     * previous line, and part of it was
+			     * wrapped to this line.  -FM
+			     */
+			    while (i < (int) prev->size)
+				prev->data[j++] = prev->data[i++];
+			    prev->size = j;
+			    prev->data[j] = '\0';
+			    while (j < i)
+				prev->data[j++] = '\0';
+			    if (start == last && text->permissible_split > 0) {
+				if ((int) text->permissible_split < k)
+				    text->permissible_split = 0;
+				else
+				    text->permissible_split -= k;
+			    }
+			    j = 0;
+			    i = k;
+			    while (k < (int) start->size)
+				start->data[j++] = start->data[k++];
+			    for (anc = a; anc; anc = anc->next) {
+				if (anc->line_num == a->line_num &&
+				    anc->line_pos >= i) {
+				    anc->line_pos -= i;
+				}
+			    }
+			    start->size = j;
+			    start->data[j++] = '\0';
+			    while (j < k)
+				start->data[j++] = '\0';
+			} else {
+			    /*
+			     * Shucks!  We didn't find the
+			     * numbered bracket.  -FM
+			     */
+			    a->show_anchor = YES;
+			}
+		    } else {
+			/*
+			 * Shucks!  We didn't find the
+			 * numbered bracket.  -FM
+			 */
+			a->show_anchor = YES;
+		    }
+		} else if (prev && prev->size > 2) {
+		    j = (prev->size - 1);
+		    while ((j >= 0) && IsSpecialAttrChar(prev->data[j]))
+			j--;
+		    if (j < 0)
+			j = 0;
+		    if ((j >= 2) &&
+			(prev->data[j] == ']' &&
+			 isdigit(UCH(prev->data[j - 1])))) {
+			j--;
+			NumSize++;
+			while (j >= 0 &&
+			       isdigit(UCH(prev->data[j]))) {
+			    j--;
+			    NumSize++;
+			}
+			while (j < 0) {
+			    j++;
+			    NumSize--;
+			}
+			if (prev->data[j] == '[') {
+			    /*
+			     * The numbered bracket is all on the
+			     * previous line, and the anchor content
+			     * was wrapped to the last line.  -FM
+			     */
+			    NumSize++;
+			    k = j + NumSize;
+			    while (k < (int) prev->size)
+				prev->data[j++] = prev->data[k++];
+			    prev->size = j;
+			    prev->data[j++] = '\0';
+			    while (j < k)
+				prev->data[j++] = '\0';
+			} else {
+			    /*
+			     * Shucks!  We didn't find the
+			     * numbered bracket.  -FM
+			     */
+			    a->show_anchor = YES;
+			}
+		    } else {
+			/*
+			 * Shucks!  We didn't find the
+			 * numbered bracket.  -FM
+			 */
+			a->show_anchor = YES;
+		    }
+		} else {
+		    /*
+		     * Shucks!  We didn't find the
+		     * numbered bracket.  -FM
+		     */
+		    a->show_anchor = YES;
+		}
+	    }
+	} else {
+	    if (!number_links_on_left)
+		add_link_number(text, a, FALSE);
+	    /*
+	     * The anchor's content is not restricted to only
+	     * white and special characters, so we'll show it
+	     * as a link.  -FM
+	     */
+	    a->show_anchor = YES;
+	    if (BlankExtent) {
+		CTRACE((tfp,
+			"HText_endAnchor0: blanks (line,pos,ext,BlankExtent):(%d,%d,%d,%d)",
+			a->line_num, a->line_pos, a->extent,
+			BlankExtent));
+	    }
+	}
+	if (a->show_anchor == NO) {
+	    /*
+	     * The anchor's content is restricted to white
+	     * and special characters, so set its number
+	     * and extent to zero, decrement the visible
+	     * anchor number counter, and add this anchor
+	     * to the hidden links list.  -FM
+	     */
+	    a->extent = 0;
+	    if (text->hiddenlinkflag != HIDDENLINKS_MERGE) {
+		a->number = 0;
+		text->last_anchor_number--;
+		HText_AddHiddenLink(text, a);
+	    }
+	} else {
+	    /*
+	     * The anchor's content is not restricted to white
+	     * and special characters, so we'll display the
+	     * content, but shorten its extent by any trailing
+	     * blank lines we've detected.  -FM
+	     */
+	    a->extent -= ((BlankExtent < a->extent) ?
+			  BlankExtent : 0);
+	}
+	if (BlankExtent || a->extent <= 0 || a->number <= 0) {
+	    CTRACE((tfp,
+		    "->[%d](%d,%d,%d,%d)\n",
+		    a->number,
+		    a->line_num, a->line_pos, a->extent,
+		    BlankExtent));
+	}
+    } else {
+	if (!really)		/* Just report whether it is empty */
+	    return FALSE;
+	/*
+	 * It's a named anchor without an HREF, so it
+	 * should be registered but not shown as a
+	 * link.  -FM
+	 */
+	a->show_anchor = NO;
+	a->extent = 0;
+    }
+    return FALSE;
+}
+
+void HText_endAnchor(HText *text, int number)
+{
+    HText_endAnchor0(text, number, 1);
+}
+
+/*
+    This returns whether the given anchor has blank content. Shamelessly copied
+    from HText_endAnchor. The values returned are meaningful only for "normal"
+    links - like ones produced by <a href=".">foo</a>, no inputs, etc. - VH
+*/
+#ifdef MARK_HIDDEN_LINKS
+BOOL HText_isAnchorBlank(HText *text, int number)
+{
+    return HText_endAnchor0(text, number, 0);
+}
+#endif /* MARK_HIDDEN_LINKS */
+
+void HText_appendText(HText *text, const char *str)
+{
+    const char *p;
+
+    if (str == NULL)
+	return;
+
+    if (text->halted == 3)
+	return;
+
+    for (p = str; *p; p++) {
+	HText_appendCharacter(text, *p);
+    }
+}
+
+static int remove_special_attr_chars(char *buf)
+{
+    register char *cp;
+    register int soft_newline_count = 0;
+
+    for (cp = buf; *cp != '\0'; cp++) {
+	/*
+	 * Don't print underline chars.
+	 */
+	soft_newline_count += (*cp == LY_SOFT_NEWLINE);
+	if (!IsSpecialAttrChar(*cp)) {
+	    *buf++ = *cp;
+	}
+    }
+    *buf = '\0';
+    return soft_newline_count;
+}
+
+/*
+ *  This function trims blank lines from the end of the document, and
+ *  then gets 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.
+ */
+void HText_endAppend(HText *text)
+{
+    HTLine *line_ptr;
+
+    if (!text)
+	return;
+
+    CTRACE((tfp, "GridText: Entering HText_endAppend\n"));
+
+    /*
+     * Create a blank line at the bottom.
+     */
+    new_line(text);
+
+    if (text->halted) {
+	if (text->stbl)
+	    HText_cancelStbl(text);
+	/*
+	 * If output was stopped because memory was low, and we made
+	 * it to the end of the document, reset those flags and hope
+	 * things are better now.  - kw
+	 */
+	LYFakeZap(NO);
+	text->halted = 0;
+    } else if (text->stbl) {
+	/*
+	 * Could happen if TABLE end tag was missing.
+	 * Alternatively we could cancel in this case.  - kw
+	 */
+	HText_endStblTABLE(text);
+    }
+
+    /*
+     * Get the first line.
+     */
+    line_ptr = FirstHTLine(text);
+
+    /*
+     * 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 = text->last_line->prev;
+
+	CTRACE((tfp, "GridText: Removing bottom blank line: `%s'\n",
+		text->last_line->data));
+	/*
+	 * line_ptr points to the first line.
+	 */
+	next_to_the_last_line->next = line_ptr;
+	line_ptr->prev = next_to_the_last_line;
+	freeHTLine(text, text->last_line);
+	text->last_line = next_to_the_last_line;
+	text->Lines--;
+	CTRACE((tfp, "GridText: New bottom line: `%s'\n",
+		text->last_line->data));
+    }
+
+    /*
+     * Fix up the anchor structure values and
+     * create the hightext strings.  -FM
+     */
+    HText_trimHightext(text, TRUE, -1);
+}
+
+/*
+ *  This function gets 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.
+ *
+ *  `Forms input' fields cannot be displayed properly without this function
+ *  to be invoked (detected in display_partial mode).
+ *
+ *  If final is set, this is the final fixup; if not set, we don't have
+ *  to do everything because there should be another call later.
+ *
+ *  BEFORE this function has treated a TextAnchor, its line_pos and
+ *  extent fields are counting bytes in the HTLine data, including
+ *  invisible special attribute chars and counting UTF-8 multibyte
+ *  characters as multiple bytes.
+ *
+ *  AFTER the adjustment, the anchor line_pos (and hightext offset if
+ *  applicable) fields indicate x positions in terms of displayed character
+ *  cells, and the extent field apparently is unimportant; the anchor text has
+ *  been copied to the hightext fields (which should have been NULL up to that
+ *  point), with special attribute chars removed.
+ *
+ *  This needs to be done so that display_page finds the anchors in the
+ *  form it expects when it sets the links[] elements.
+ */
+static void HText_trimHightext(HText *text,
+			       BOOLEAN final,
+			       int stop_before)
+{
+    int cur_line, cur_shift;
+    TextAnchor *anchor_ptr;
+    TextAnchor *prev_a = NULL;
+    HTLine *line_ptr;
+    HTLine *line_ptr2;
+    unsigned char ch;
+    char *hilite_str;
+    int hilite_len;
+    int actual_len;
+    int count_line;
+
+    if (!text)
+	return;
+
+    if (final) {
+	CTRACE((tfp, "GridText: Entering HText_trimHightext (final)\n"));
+    } else {
+	if (stop_before < 0 || stop_before > text->Lines)
+	    stop_before = text->Lines;
+	CTRACE((tfp,
+		"GridText: Entering HText_trimHightext (partial: 0..%d/%d)\n",
+		stop_before, text->Lines));
+    }
+
+    /*
+     * Get the first line.
+     */
+    line_ptr = FirstHTLine(text);
+    cur_line = 0;
+
+    /*
+     * Fix up the anchor structure values and
+     * create the hightext strings.  -FM
+     */
+    for (anchor_ptr = text->first_anchor;
+	 anchor_ptr != NULL;
+	 prev_a = anchor_ptr, anchor_ptr = anchor_ptr->next) {
+	int anchor_col;
+
+      re_parse:
+	/*
+	 * Find the right line.
+	 */
+	for (; anchor_ptr->line_num > cur_line;
+	     line_ptr = line_ptr->next, cur_line++) {
+	    ;			/* null body */
+	}
+
+	if (!final) {
+	    /*
+	     * If this is not the final call, stop when we have reached
+	     * the last line, or the very end of preceding line.
+	     * The last line is probably still not finished.  - kw
+	     */
+	    if (cur_line >= stop_before)
+		break;
+	    if (anchor_ptr->line_num >= text->Lines - 1
+		&& anchor_ptr->line_pos >= (int) text->last_line->prev->size)
+		break;
+	    /*
+	     * Also skip this anchor if it looks like HText_endAnchor
+	     * is not yet done with it.  - kw
+	     */
+	    if (!anchor_ptr->extent && anchor_ptr->number &&
+		(anchor_ptr->link_type & HYPERTEXT_ANCHOR) &&
+		!anchor_ptr->show_anchor &&
+		anchor_ptr->number == text->last_anchor_number)
+		continue;
+	}
+
+	/*
+	 * If hightext has already been set, then we must have already
+	 * done the trimming & adjusting for this anchor, so avoid
+	 * doing it a second time.  - kw
+	 */
+	if (LYGetHiTextStr(anchor_ptr, 0) != NULL)
+	    continue;
+
+	if (anchor_ptr->line_pos > (int) line_ptr->size) {
+	    anchor_ptr->line_pos = line_ptr->size;
+	}
+	if (anchor_ptr->line_pos < 0) {
+	    anchor_ptr->line_pos = 0;
+	    anchor_ptr->line_num = cur_line;
+	}
+	CTRACE((tfp,
+		"GridText: Anchor found on line:%d col:%d [%05d:%d] ext:%d\n",
+		cur_line,
+		anchor_ptr->line_pos,
+		anchor_ptr->sgml_offset,
+		anchor_ptr->number,
+		anchor_ptr->extent));
+
+	cur_shift = 0;
+	/*
+	 * 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 = UCH(line_ptr->data[anchor_ptr->line_pos]);
+	    while (isspace(ch) ||
+		   IsSpecialAttrChar(ch)) {
+		anchor_ptr->line_pos++;
+		anchor_ptr->extent--;
+		cur_shift++;
+		ch = UCH(line_ptr->data[anchor_ptr->line_pos]);
+	    }
+	}
+	if (anchor_ptr->extent < 0) {
+	    anchor_ptr->extent = 0;
+	}
+
+	CTRACE((tfp, "anchor text: '%s'\n", line_ptr->data));
+	/*
+	 * If the link begins with an end of line and we have more lines, then
+	 * start the highlighting on the next line.  -FM.
+	 *
+	 * But if an empty anchor is at the end of line and empty, keep it
+	 * where it is, unless the previous anchor in the list (if any) already
+	 * starts later.  - kw
+	 */
+	if ((unsigned) anchor_ptr->line_pos >= strlen(line_ptr->data)) {
+	    if (cur_line < text->Lines &&
+		(anchor_ptr->extent ||
+		 anchor_ptr->line_pos != (int) line_ptr->size ||
+		 (prev_a &&	/* How could this happen? */
+		  (prev_a->line_num > anchor_ptr->line_num)))) {
+		anchor_ptr->line_num++;
+		anchor_ptr->line_pos = 0;
+		CTRACE((tfp, "found anchor at end of line\n"));
+		goto re_parse;
+	    } else {
+		CTRACE((tfp, "found anchor at end of line, leaving it there\n"));
+	    }
+	}
+
+	/*
+	 * Copy the link name into the data structure.
+	 */
+	if (line_ptr->data
+	    && anchor_ptr->extent > 0
+	    && anchor_ptr->line_pos >= 0) {
+	    int size = (int) line_ptr->size - anchor_ptr->line_pos;
+
+	    if (size > anchor_ptr->extent)
+		size = anchor_ptr->extent;
+	    LYClearHiText(anchor_ptr);
+	    LYSetHiText(anchor_ptr,
+			&line_ptr->data[anchor_ptr->line_pos],
+			size);
+	} else {
+	    LYClearHiText(anchor_ptr);
+	    LYSetHiText(anchor_ptr, "", 0);
+	}
+
+	/*
+	 * If the anchor extends over more than one line, copy that into the
+	 * data structure.
+	 */
+	hilite_str = LYGetHiTextStr(anchor_ptr, 0);
+	hilite_len = strlen(hilite_str);
+	actual_len = anchor_ptr->extent;
+
+	line_ptr2 = line_ptr;
+	assert(line_ptr2 != 0);
+
+	count_line = cur_line;
+	while (actual_len > hilite_len) {
+	    HTLine *old_line_ptr2 = line_ptr2;
+
+	    count_line++;
+	    if ((line_ptr2 = line_ptr2->next) == NULL)
+		break;
+
+	    if (!final
+		&& count_line >= stop_before) {
+		LYClearHiText(anchor_ptr);
+		break;
+	    } else if (old_line_ptr2 == text->last_line) {
+		break;
+	    }
+
+	    /*
+	     * Double check that we have a line pointer, and if so, copy into
+	     * highlight text.
+	     */
+	    if (line_ptr2) {
+		char *hi_string = NULL;
+		int hi_offset = line_ptr2->offset;
+
+		StrnAllocCopy(hi_string,
+			      line_ptr2->data,
+			      (actual_len - hilite_len));
+		actual_len -= strlen(hi_string);
+		/*handle LY_SOFT_NEWLINEs -VH */
+		hi_offset += remove_special_attr_chars(hi_string);
+
+		if (anchor_ptr->link_type & HYPERTEXT_ANCHOR) {
+		    LYTrimTrailing(hi_string);
+		}
+		if (non_empty(hi_string)) {
+		    LYAddHiText(anchor_ptr, hi_string, hi_offset);
+		} else if (actual_len > hilite_len) {
+		    LYAddHiText(anchor_ptr, "", hi_offset);
+		}
+		FREE(hi_string);
+	    }
+	}
+
+	if (!final
+	    && count_line >= stop_before) {
+	    break;
+	}
+
+	hilite_str = LYGetHiTextStr(anchor_ptr, 0);
+	remove_special_attr_chars(hilite_str);
+	if (anchor_ptr->link_type & HYPERTEXT_ANCHOR) {
+	    LYTrimTrailing(hilite_str);
+	}
+
+	/*
+	 * Save the offset (bytes) of the anchor in the line's data.
+	 */
+	anchor_col = anchor_ptr->line_pos;
+
+	/*
+	 * Subtract any formatting characters from the x position of the link.
+	 */
+#ifdef WIDEC_CURSES
+	if (anchor_ptr->line_pos > 0) {
+	    /*
+	     * LYstrExtent filters out the formatting characters, so we do not
+	     * have to count them here, except for soft newlines.
+	     */
+	    anchor_ptr->line_pos = LYstrExtent2(line_ptr->data, anchor_col);
+	    if (line_ptr->data[0] == LY_SOFT_NEWLINE)
+		anchor_ptr->line_pos += 1;
+	}
+#else /* 8-bit curses, etc.  */
+	if (anchor_ptr->line_pos > 0) {
+	    register int offset = 0, i = 0;
+	    int have_soft_newline_in_1st_line = 0;
+
+	    for (; i < anchor_col; i++) {
+		if (IS_UTF_EXTRA(line_ptr->data[i]) ||
+		    IsSpecialAttrChar(line_ptr->data[i])) {
+		    offset++;
+		    have_soft_newline_in_1st_line += (line_ptr->data[i] == LY_SOFT_NEWLINE);
+		}
+	    }
+	    anchor_ptr->line_pos -= offset;
+	    /*handle LY_SOFT_NEWLINEs -VH */
+	    anchor_ptr->line_pos += have_soft_newline_in_1st_line;
+	}
+#endif /* WIDEC_CURSES */
+
+	/*
+	 * Set the line number.
+	 */
+	anchor_ptr->line_pos += line_ptr->offset;
+	anchor_ptr->line_num = cur_line;
+
+	CTRACE((tfp, "GridText:     add link on line %d col %d [%d] %s\n",
+		cur_line, anchor_ptr->line_pos,
+		anchor_ptr->number, "in HText_trimHightext"));
+    }
+}
+
+/*	Return the anchor associated with this node
+*/
+HTParentAnchor *HText_nodeAnchor(HText *text)
+{
+    return text->node_anchor;
+}
+
+/*				GridText specials
+ *				=================
+ */
+
+/*
+ * HText_childNextNumber() returns the anchor with index [number],
+ * using a pointer from the previous number (=optimization) or NULL.
+ */
+HTChildAnchor *HText_childNextNumber(int number, void **prev)
+{
+    /* Sorry, TextAnchor is not declared outside this file, use a cast. */
+    TextAnchor *a = (TextAnchor *) *prev;
+
+    if (!HTMainText || number <= 0)
+	return (HTChildAnchor *) 0;	/* Fail */
+    if (number == 1 || !a)
+	a = HTMainText->first_anchor;
+
+    /* 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;
+}
+
+static const char *inputFieldDesc(FormInfo * input)
+{
+    const char *result = 0;
+
+    switch (input->type) {
+    case F_TEXT_TYPE:
+	result = gettext("text entry field");
+	break;
+    case F_PASSWORD_TYPE:
+	result = gettext("password entry field");
+	break;
+    case F_CHECKBOX_TYPE:
+	result = gettext("checkbox");
+	break;
+    case F_RADIO_TYPE:
+	result = gettext("radio button");
+	break;
+    case F_SUBMIT_TYPE:
+	result = gettext("submit button");
+	break;
+    case F_RESET_TYPE:
+	result = gettext("reset button");
+	break;
+    case F_BUTTON_TYPE:
+	result = gettext("script button");
+	break;
+    case F_OPTION_LIST_TYPE:
+	result = gettext("popup menu");
+	break;
+    case F_HIDDEN_TYPE:
+	result = gettext("hidden form field");
+	break;
+    case F_TEXTAREA_TYPE:
+	result = gettext("text entry area");
+	break;
+    case F_RANGE_TYPE:
+	result = gettext("range entry field");
+	break;
+    case F_FILE_TYPE:
+	result = gettext("file entry field");
+	break;
+    case F_TEXT_SUBMIT_TYPE:
+	result = gettext("text-submit field");
+	break;
+    case F_IMAGE_SUBMIT_TYPE:
+	result = gettext("image-submit button");
+	break;
+    case F_KEYGEN_TYPE:
+	result = gettext("keygen field");
+	break;
+    default:
+	result = gettext("unknown form field");
+	break;
+    }
+    return result;
+}
+
+/*
+ * HText_FormDescNumber() returns a description of the form field
+ * with index N.  The index corresponds to the [number] we print
+ * for the field.  -FM & LE
+ */
+void HText_FormDescNumber(int number,
+			  const char **desc)
+{
+    TextAnchor *a;
+
+    if (!desc)
+	return;
+
+    if (!(HTMainText && HTMainText->first_anchor) || number <= 0) {
+	*desc = gettext("unknown field or link");
+	return;
+    }
+
+    for (a = HTMainText->first_anchor; a; a = a->next) {
+	if (a->number == number) {
+	    if (!(a->input_field && a->input_field->type)) {
+		*desc = gettext("unknown field or link");
+		return;
+	    }
+	    break;
+	}
+    }
+
+    if (a != NULL)
+	*desc = inputFieldDesc(a->input_field);
+}
+
+/* HTGetRelLinkNum returns the anchor number to which follow_link_number()
+ * is to jump (input was 123+ or 123- or 123+g or 123-g or 123 or 123g)
+ * num is the number specified
+ * rel is 0 or '+' or '-'
+ * cur is the current link
+ */
+int HTGetRelLinkNum(int num,
+		    int rel,
+		    int cur)
+{
+    TextAnchor *a, *l = 0;
+    int scrtop = HText_getTopOfScreen();	/*XXX +1? */
+    int curline = links[cur].anchor_line_num;
+    int curpos = links[cur].lx;
+    int on_screen = (curline >= scrtop && curline < (scrtop + display_lines));
+
+    /* curanchor may or may not be the "current link", depending whether it's
+     * on the current screen
+     */
+    int curanchor = links[cur].anchor_number;
+
+    CTRACE((tfp, "HTGetRelLinkNum(%d,%d,%d) -- HTMainText=%p\n",
+	    num, rel, cur, (void *) HTMainText));
+    CTRACE((tfp,
+	    "  scrtop=%d, curline=%d, curanchor=%d, display_lines=%d, %s\n",
+	    scrtop, curline, curanchor, display_lines,
+	    on_screen ? "on_screen" : "0"));
+    if (!HTMainText)
+	return 0;
+    if (rel == 0)
+	return num;
+
+    /* if cur numbered link is on current page, use it */
+    if (on_screen && curanchor) {
+	CTRACE((tfp, "curanchor=%d at line %d on screen\n", curanchor, curline));
+	if (rel == '+')
+	    return curanchor + num;
+	else if (rel == '-')
+	    return curanchor - num;
+	else
+	    return num;		/* shouldn't happen */
+    }
+
+    /* no current link on screen, or current link is not numbered
+     * -- find previous closest numbered link
+     */
+    for (a = HTMainText->first_anchor; a; a = a->next) {
+	CTRACE((tfp, "  a->line_num=%d, a->number=%d\n", a->line_num, a->number));
+	if (a->line_num >= scrtop)
+	    break;
+	if (a->number == 0)
+	    continue;
+	l = a;
+	curanchor = l->number;
+    }
+    CTRACE((tfp, "  a=%p, l=%p, curanchor=%d\n", (void *) a, (void *) l, curanchor));
+    if (on_screen) {		/* on screen but not a numbered link */
+	for (; a; a = a->next) {
+	    if (a->number) {
+		l = a;
+		curanchor = l->number;
+	    }
+	    if (curline == a->line_num && curpos == a->line_pos)
+		break;
+	}
+    }
+    if (rel == '+') {
+	return curanchor + num;
+    } else if (rel == '-') {
+	if (l)
+	    return curanchor + 1 - num;
+	else {
+	    for (; a && a->number == 0; a = a->next) ;
+	    return a ? a->number - num : 0;
+	}
+    } else
+	return num;		/* shouldn't happen */
+}
+
+/*
+ * HTGetLinkInfo returns some link info based on the number.
+ *
+ * If want_go is not 0, caller requests to know a line number for
+ * the link indicated by number.  It will be returned in *go_line, and
+ * *linknum will be set to an index into the links[] array, to use after
+ * the line in *go_line has been made the new top screen line.
+ * *hightext and *lname are unchanged.  - KW
+ *
+ * If want_go is 0 and the number doesn't represent an input field, info
+ * on the link indicated by number is deposited in *hightext and *lname.
+ */
+int HTGetLinkInfo(int number,
+		  int want_go,
+		  int *go_line,
+		  int *linknum,
+		  char **hightext,
+		  char **lname)
+{
+    TextAnchor *a;
+    HTAnchor *link_dest;
+
+#ifndef DONT_TRACK_INTERNAL_LINKS
+    HTAnchor *link_dest_intl = NULL;
+#endif
+    int anchors_this_line = 0, anchors_this_screen = 0;
+    int prev_anchor_line = -1, prev_prev_anchor_line = -1;
+
+    if (!HTMainText)
+	return (NO);
+
+    for (a = HTMainText->first_anchor; a; a = a->next) {
+	/*
+	 * Count anchors, first on current line if there is more
+	 * than one.  We have to count all links, including form
+	 * field anchors and others with a->number == 0, because
+	 * they are or will be included in the links[] array.
+	 * The exceptions are hidden form fields and anchors with
+	 * show_anchor not set, because they won't appear in links[]
+	 * and don't count towards nlinks.  - KW
+	 */
+	if ((a->show_anchor) &&
+	    !(a->link_type == INPUT_ANCHOR
+	      && a->input_field->type == F_HIDDEN_TYPE)) {
+	    if (a->line_num == prev_anchor_line) {
+		anchors_this_line++;
+	    } else {
+		/*
+		 * This anchor is on a different line than the previous one.
+		 * Remember which was the line number of the previous anchor,
+		 * for use in screen positioning later.  - KW
+		 */
+		anchors_this_line = 1;
+		prev_prev_anchor_line = prev_anchor_line;
+		prev_anchor_line = a->line_num;
+	    }
+	    if (a->line_num >= HTMainText->top_of_screen) {
+		/*
+		 * Count all anchors starting with the top line of the
+		 * currently displayed screen.  Just keep on counting
+		 * beyond this screen's bottom line - we'll know whether
+		 * a found anchor is below the current screen by a check
+		 * against nlinks later.  - KW
+		 */
+		anchors_this_screen++;
+	    }
+	}
+
+	if (a->number == number) {
+	    /*
+	     * We found it.  Now process it, depending
+	     * on what kind of info is requested.  - KW
+	     */
+	    if (want_go || a->link_type == INPUT_ANCHOR) {
+		if (a->show_anchor == NO) {
+		    /*
+		     * The number requested has been assigned to an anchor
+		     * without any selectable text, so we cannot position
+		     * on it.  The code for suppressing such anchors in
+		     * HText_endAnchor() may not have applied, or it may
+		     * have failed.  Return a failure indication so that
+		     * the user will notice that something is wrong,
+		     * instead of positioning on some other anchor which
+		     * might result in inadvertent activation.  - KW
+		     */
+		    return (NO);
+		}
+		if (anchors_this_screen > 0 &&
+		    anchors_this_screen <= nlinks &&
+		    a->line_num >= HTMainText->top_of_screen &&
+		    a->line_num < HTMainText->top_of_screen + (display_lines)) {
+		    /*
+		     * If the requested anchor is within the current screen,
+		     * just set *go_line so that the screen window won't move
+		     * (keep it as it is), and set *linknum to the index of
+		     * this link in the current links[] array.  - KW
+		     */
+		    *go_line = HTMainText->top_of_screen;
+		    if (linknum)
+			*linknum = anchors_this_screen - 1;
+		} else {
+		    /*
+		     * if the requested anchor is not within the currently
+		     * displayed screen, set *go_line such that the top line
+		     * will be either
+		     *  (1) the line immediately below the previous
+		     *      anchor, or
+		     *  (2) about one third of a screenful above the line
+		     *      with the target, or
+		     *  (3) the first line of the document -
+		     * whichever comes last.  In all cases the line with our
+		     * target will end up being the first line with any links
+		     * on the new screen, so that we can use the
+		     * anchors_this_line counter to point to the anchor in
+		     * the new links[] array.  - kw
+		     */
+		    int max_offset = SEARCH_GOAL_LINE - 1;
+
+		    if (max_offset < 0)
+			max_offset = 0;
+		    else if (max_offset >= display_lines)
+			max_offset = display_lines - 1;
+		    *go_line = prev_anchor_line - max_offset;
+		    if (*go_line <= prev_prev_anchor_line)
+			*go_line = prev_prev_anchor_line + 1;
+		    if (*go_line < 0)
+			*go_line = 0;
+		    if (linknum)
+			*linknum = anchors_this_line - 1;
+		}
+		return (LINK_LINE_FOUND);
+	    } else {
+		*hightext = LYGetHiTextStr(a, 0);
+		link_dest = HTAnchor_followLink(a->anchor);
+		{
+		    char *cp_freeme = NULL;
+
+		    if (traversal) {
+			cp_freeme = stub_HTAnchor_address(link_dest);
+		    } else {
+#ifndef DONT_TRACK_INTERNAL_LINKS
+			if (a->link_type == INTERNAL_LINK_ANCHOR) {
+			    link_dest_intl =
+				HTAnchor_followTypedLink(a->anchor, HTInternalLink);
+			    if (link_dest_intl && link_dest_intl != link_dest) {
+
+				CTRACE((tfp,
+					"HTGetLinkInfo: unexpected typed link to %s!\n",
+					link_dest_intl->parent->address));
+				link_dest_intl = NULL;
+			    }
+			}
+			if (link_dest_intl) {
+			    char *cp2 = HTAnchor_address(link_dest_intl);
+
+			    FREE(*lname);
+			    *lname = cp2;
+			    return (WWW_INTERN_LINK_TYPE);
+			} else
+#endif
+			    cp_freeme = HTAnchor_address(link_dest);
+		    }
+		    StrAllocCopy(*lname, cp_freeme);
+		    FREE(cp_freeme);
+		}
+		return (WWW_LINK_TYPE);
+	    }
+	}
+    }
+    return (NO);
+}
+
+static BOOLEAN same_anchor_or_field(int numberA,
+				    FormInfo * formA,
+				    int numberB,
+				    FormInfo * formB,
+				    BOOLEAN ta_same)
+{
+    if (numberA > 0 || numberB > 0) {
+	if (numberA == numberB)
+	    return (YES);
+	else if (!ta_same)
+	    return (NO);
+    }
+    if (formA || formB) {
+	if (formA == formB) {
+	    return (YES);
+	} else if (!ta_same) {
+	    return (NO);
+	} else if (!(formA && formB)) {
+	    return (NO);
+	}
+    } else {
+	return (NO);
+    }
+    if (formA->type != formB->type ||
+	formA->type != F_TEXTAREA_TYPE ||
+	formB->type != F_TEXTAREA_TYPE) {
+	return (NO);
+    }
+    if (formA->number != formB->number)
+	return (NO);
+    if (!formA->name || !formB->name)
+	return (YES);
+    return (BOOL) (strcmp(formA->name, formB->name) == 0);
+}
+
+#define same_anchor_as_link(i,a,ta_same) (BOOL) (i >= 0 && a && \
+		same_anchor_or_field(links[i].anchor_number,\
+		(links[i].type == WWW_FORM_LINK_TYPE) ? links[i].l_form : NULL,\
+		a->number,\
+		(a->link_type == INPUT_ANCHOR) ? a->input_field : NULL,\
+		ta_same))
+#define same_anchors(a1,a2,ta_same) (BOOL) (a1 && a2 && \
+		same_anchor_or_field(a1->number,\
+		(a1->link_type == INPUT_ANCHOR) ? a1->input_field : NULL,\
+		a2->number,\
+		(a2->link_type == INPUT_ANCHOR) ? a2->input_field : NULL,\
+		ta_same))
+
+/*
+ * Are there more textarea lines belonging to the same textarea before
+ * (direction < 0) or after (direction > 0) the current one?
+ * On entry, curlink must be the index in links[] of a textarea field.  - kw
+ */
+BOOL HText_TAHasMoreLines(int curlink,
+			  int direction)
+{
+    TextAnchor *a;
+    TextAnchor *prev_a = NULL;
+
+    if (!HTMainText)
+	return (NO);
+    if (direction < 0) {
+	for (a = HTMainText->first_anchor; a; prev_a = a, a = a->next) {
+	    if (a->link_type == INPUT_ANCHOR &&
+		links[curlink].l_form == a->input_field) {
+		return same_anchors(a, prev_a, TRUE);
+	    }
+	    if (links[curlink].anchor_number &&
+		a->number >= links[curlink].anchor_number)
+		break;
+	}
+	return NO;
+    } else {
+	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);
+	    }
+	    if (links[curlink].anchor_number &&
+		a->number >= links[curlink].anchor_number)
+		break;
+	}
+	return NO;
+    }
+}
+
+/*
+ * HTGetLinkOrFieldStart - moving to previous or next link or form field.
+ *
+ * On input,
+ *	curlink: current link, as index in links[] array (-1 if none)
+ *	direction: whether to move up or down (or stay where we are)
+ *	ta_skip: if FALSE, input fields belonging to the same textarea are
+ *		 are treated as different fields, as usual;
+ *		 if TRUE, fields of the same textarea are treated as a
+ *		 group for skipping.
+ * The caller wants information for positioning on the new link to be
+ * deposited in *go_line and (if linknum is not NULL) *linknum.
+ *
+ * On failure (no more links in the requested direction) returns NO
+ * and doesn't change *go_line or *linknum.  Otherwise, LINK_DO_ARROWUP
+ * may be returned, and *go_line and *linknum not changed, to indicate that
+ * the caller should use a normal PREV_LINK or PREV_PAGE mechanism.
+ * Otherwise:
+ * The number (0-based counting) for the new top screen line will be returned
+ * in *go_line, and *linknum will be set to an index into the links[] array,
+ * to use after the line in *go_line has been made the new top screen
+ * line.  - kw
+ */
+int HTGetLinkOrFieldStart(int curlink,
+			  int *go_line,
+			  int *linknum,
+			  int direction,
+			  BOOLEAN ta_skip)
+{
+    TextAnchor *a;
+    int anchors_this_line = 0;
+    int prev_anchor_line = -1, prev_prev_anchor_line = -1;
+
+    struct agroup {
+	TextAnchor *anc;
+	int prev_anchor_line;
+	int anchors_this_line;
+	int anchors_this_group;
+    } previous, current;
+    struct agroup *group_to_go = NULL;
+
+    if (!HTMainText)
+	return (NO);
+
+    previous.anc = current.anc = NULL;
+    previous.prev_anchor_line = current.prev_anchor_line = -1;
+    previous.anchors_this_line = current.anchors_this_line = 0;
+    previous.anchors_this_group = current.anchors_this_group = 0;
+
+    for (a = HTMainText->first_anchor; a; a = a->next) {
+	/*
+	 * Count anchors, first on current line if there is more
+	 * than one.  We have to count all links, including form
+	 * field anchors and others with a->number == 0, because
+	 * they are or will be included in the links[] array.
+	 * The exceptions are hidden form fields and anchors with
+	 * show_anchor not set, because they won't appear in links[]
+	 * and don't count towards nlinks.  - KW
+	 */
+	if ((a->show_anchor) &&
+	    !(a->link_type == INPUT_ANCHOR
+	      && a->input_field->type == F_HIDDEN_TYPE)) {
+	    if (a->line_num == prev_anchor_line) {
+		anchors_this_line++;
+	    } else {
+		/*
+		 * This anchor is on a different line than the previous one.
+		 * Remember which was the line number of the previous anchor,
+		 * for use in screen positioning later.  - KW
+		 */
+		anchors_this_line = 1;
+		prev_prev_anchor_line = prev_anchor_line;
+		prev_anchor_line = a->line_num;
+	    }
+
+	    if (!same_anchors(current.anc, a, ta_skip)) {
+		previous.anc = current.anc;
+		previous.prev_anchor_line = current.prev_anchor_line;
+		previous.anchors_this_line = current.anchors_this_line;
+		previous.anchors_this_group = current.anchors_this_group;
+		current.anc = a;
+		current.prev_anchor_line = prev_prev_anchor_line;
+		current.anchors_this_line = anchors_this_line;
+		current.anchors_this_group = 1;
+	    } else {
+		current.anchors_this_group++;
+	    }
+	    if (curlink >= 0) {
+		if (same_anchor_as_link(curlink, a, ta_skip)) {
+		    if (direction == -1) {
+			group_to_go = &previous;
+			break;
+		    } else if (direction == 0) {
+			group_to_go = &current;
+			break;
+		    }
+		} else if (direction > 0 &&
+			   same_anchor_as_link(curlink, previous.anc, ta_skip)) {
+		    group_to_go = &current;
+		    break;
+		}
+	    } else {
+		if (a->line_num >= HTMainText->top_of_screen) {
+		    if (direction < 0) {
+			group_to_go = &previous;
+			break;
+		    } else if (direction == 0) {
+			if (previous.anc) {
+			    group_to_go = &previous;
+			    break;
+			} else {
+			    group_to_go = &current;
+			    break;
+			}
+		    } else {
+			group_to_go = &current;
+			break;
+		    }
+		}
+	    }
+	}
+    }
+    if (!group_to_go && curlink < 0 && direction <= 0) {
+	group_to_go = &current;
+    }
+    if (group_to_go) {
+	a = group_to_go->anc;
+	if (a) {
+	    int max_offset;
+
+	    /*
+	     * We know where to go; most of the stuff below is just
+	     * tweaks to try to position the new screen in a specific
+	     * way.
+	     *
+	     * In some cases going to a previous link can be done
+	     * via the normal LYK_PREV_LINK action, which may give
+	     * better positioning of the new screen.  - kw
+	     */
+	    if (a->line_num < HTMainText->top_of_screen &&
+		a->line_num >= HTMainText->top_of_screen - (display_lines)) {
+		if ((curlink < 0 &&
+		     group_to_go->anchors_this_group == 1) ||
+		    (direction < 0 &&
+		     group_to_go != &current &&
+		     current.anc &&
+		     current.anc->line_num >= HTMainText->top_of_screen &&
+		     group_to_go->anchors_this_group == 1) ||
+		    (a->next &&
+		     a->next->line_num >= HTMainText->top_of_screen)) {
+		    return (LINK_DO_ARROWUP);
+		}
+	    }
+	    /*
+	     * The fundamental limitation of the current anchors_this_line
+	     * counter method is that we only can set *linknum to the right
+	     * index into the future links[] array if the line with our link
+	     * ends up being the first line with any links (that count) on
+	     * the new screen.  Subject to that restriction we still have
+	     * some vertical liberty (sometimes), and try to make the best
+	     * of it.  It may be a question of taste though.  - kw
+	     */
+	    if (a->line_num <= (display_lines)) {
+		max_offset = 0;
+	    } else if (a->line_num < HTMainText->top_of_screen) {
+		int screensback =
+		(HTMainText->top_of_screen - a->line_num + (display_lines) - 1)
+		/ (display_lines);
+
+		max_offset = a->line_num - (HTMainText->top_of_screen -
+					    screensback * (display_lines));
+	    } else if (HTMainText->Lines - a->line_num <= (display_lines)) {
+		max_offset = a->line_num - (HTMainText->Lines + 1
+					    - (display_lines));
+	    } else if (a->line_num >=
+		       HTMainText->top_of_screen + (display_lines)) {
+		int screensahead =
+		(a->line_num - HTMainText->top_of_screen) / (display_lines);
+
+		max_offset = a->line_num - HTMainText->top_of_screen -
+		    screensahead * (display_lines);
+	    } else {
+		max_offset = SEARCH_GOAL_LINE - 1;
+	    }
+
+	    /* Stuff below should remain unchanged if line positioning
+	       is tweaked. - kw */
+	    if (max_offset < 0)
+		max_offset = 0;
+	    else if (max_offset >= display_lines)
+		max_offset = display_lines - 1;
+	    *go_line = a->line_num - max_offset;
+	    if (*go_line <= group_to_go->prev_anchor_line)
+		*go_line = group_to_go->prev_anchor_line + 1;
+
+	    if (*go_line < 0)
+		*go_line = 0;
+	    if (linknum)
+		*linknum = group_to_go->anchors_this_line - 1;
+	    return (LINK_LINE_FOUND);
+	}
+    }
+    return (NO);
+}
+
+/*
+ * This function finds the line indicated by line_num in the
+ * HText structure indicated by text, and searches that line
+ * for the first hit with the string indicated by target.  If
+ * there is no hit, FALSE is returned.  If there is a hit, then
+ * a copy of the line starting at that first hit is loaded into
+ * *data with all IsSpecial characters stripped, its offset and
+ * the printable target length (without IsSpecial, or extra CJK
+ * or utf8 characters) are loaded into *offset and *tLen, and
+ * TRUE is returned.  -FM
+ */
+BOOL HText_getFirstTargetInLine(HText *text, int line_num,
+				BOOL utf_flag,
+				int *offset,
+				int *tLen,
+				char **data,
+				const char *target)
+{
+    HTLine *line;
+    char *LineData;
+    int LineOffset, HitOffset, LenNeeded, i;
+    const char *cp;
+
+    /*
+     * 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))) {
+	return (FALSE);
+    }
+
+    /*
+     * Find the line and set up its data and offset -FM
+     */
+    for (i = 0, line = FirstHTLine(text);
+	 i < line_num && (line != text->last_line);
+	 i++, line = line->next) {
+	if (line->next == NULL) {
+	    return (FALSE);
+	}
+    }
+    if (!(line && line->data[0]))
+	return (FALSE);
+    LineData = (char *) line->data;
+    LineOffset = (int) line->offset;
+
+    /*
+     * If the target is on the line, load the offset of
+     * its first character and the subsequent line data,
+     * strip any special characters from the loaded line
+     * data, and return TRUE.  -FM
+     */
+    if (((cp = LYno_attr_mb_strstr(LineData,
+				   target,
+				   utf_flag, YES,
+				   &HitOffset,
+				   &LenNeeded)) != NULL) &&
+	(LineOffset + LenNeeded) <= DISPLAY_COLS) {
+	/*
+	 * We had a hit so load the results,
+	 * remove IsSpecial characters from
+	 * the allocated data string, and
+	 * return TRUE.  -FM
+	 */
+	*offset = (LineOffset + HitOffset);
+	*tLen = (LenNeeded - HitOffset);
+	StrAllocCopy(*data, cp);
+	remove_special_attr_chars(*data);
+	return (TRUE);
+    }
+
+    /*
+     * The line does not contain the target.  -FM
+     */
+    return (FALSE);
+}
+
+/*
+ * HText_getNumOfLines returns the number of lines in the
+ * current document.
+ */
+int HText_getNumOfLines(void)
+{
+    return (HTMainText ? HTMainText->Lines : 0);
+}
+
+/*
+ * HText_getNumOfBytes returns the size of the document, as rendered.  This
+ * may be different from the original filesize.
+ */
+int HText_getNumOfBytes(void)
+{
+    int result = -1;
+    HTLine *line = NULL;
+
+    if (HTMainText != 0) {
+	for (line = FirstHTLine(HTMainText);
+	     line != HTMainText->last_line;
+	     line = line->next) {
+	    result += 1 + strlen(line->data);
+	}
+    }
+    return result;
+}
+
+/*
+ * HText_getTitle returns the title of the
+ * current document.
+ */
+const char *HText_getTitle(void)
+{
+    return (HTMainText ?
+	    HTAnchor_title(HTMainText->node_anchor) : 0);
+}
+
+#ifdef USE_COLOR_STYLE
+const char *HText_getStyle(void)
+{
+    return (HTMainText ?
+	    HTAnchor_style(HTMainText->node_anchor) : 0);
+}
+#endif
+
+/*
+ * HText_getSugFname returns the suggested filename of the current
+ * document (normally derived from a Content-Disposition header with
+ * attachment; filename=name.suffix).  -FM
+ */
+const char *HText_getSugFname(void)
+{
+    return (HTMainText ?
+	    HTAnchor_SugFname(HTMainText->node_anchor) : 0);
+}
+
+/*
+ * HTCheckFnameForCompression receives the address of an allocated
+ * string containing a filename, and an anchor pointer, and expands
+ * or truncates the string's suffix if appropriate, based on whether
+ * the anchor indicates that the file is compressed.  We assume
+ * that the file was not uncompressed (as when downloading), and
+ * believe the headers about whether it's compressed or not.  -FM
+ *
+ * Added third arg - if strip_ok is FALSE, we don't trust the anchor
+ * info enough to remove a compression suffix if the anchor object
+ * does not indicate compression.  - kw
+ */
+void HTCheckFnameForCompression(char **fname,
+				HTParentAnchor *anchor,
+				BOOLEAN strip_ok)
+{
+    char *fn = *fname;
+    char *dot = NULL;
+    char *cp = NULL;
+    const char *suffix = "";
+    CompressFileType method;
+    CompressFileType second;
+
+    /*
+     * Make sure we have a string and anchor.  -FM
+     */
+    if (!(fn && anchor))
+	return;
+
+    /*
+     * Make sure we have a file, not directory, name.  -FM
+     */
+    if (*(fn = LYPathLeaf(fn)) == '\0')
+	return;
+
+    method = HTContentToCompressType(anchor);
+
+    /*
+     * If no Content-Encoding has been detected via the anchor
+     * pointer, but strip_ok is not set, there is nothing left
+     * to do.  - kw
+     */
+    if ((method == cftNone) && !strip_ok)
+	return;
+
+    /*
+     * Treat .tgz specially
+     */
+    if ((dot = strrchr(fn, '.')) != NULL
+	&& !strcasecomp(dot, ".tgz")) {
+	if (method == cftNone) {
+	    strcpy(dot, ".tar");
+	}
+	return;
+    }
+
+    /*
+     * Seek the last dot, and check whether
+     * we have a gzip or compress suffix.  -FM
+     */
+    if ((dot = strrchr(fn, '.')) != NULL) {
+	int rootlen = 0;
+
+	if (HTCompressFileType(fn, ".", &rootlen) != cftNone) {
+	    if (method == cftNone) {
+		/*
+		 * It has a suffix which signifies a gzipped
+		 * or compressed file for us, but the anchor
+		 * claims otherwise, so tweak the suffix.  -FM
+		 */
+		*dot = '\0';
+	    }
+	    return;
+	}
+	if ((second = HTCompressFileType(fn, "-_", &rootlen)) != cftNone) {
+	    cp = fn + rootlen;
+	    if (method == cftNone) {
+		/*
+		 * It has a tail which signifies a gzipped
+		 * file for us, but the anchor claims otherwise,
+		 * so tweak the suffix.  -FM
+		 */
+		if (cp == dot + 1)
+		    cp--;
+		*cp = '\0';
+	    } else {
+		/*
+		 * The anchor claims it's gzipped, and we
+		 * believe it, so force this tail to the
+		 * conventional suffix.  -FM
+		 */
+#ifdef VMS
+		*cp = '-';
+#else
+		*cp = '.';
+#endif /* VMS */
+		if (second == cftCompress)
+		    LYUpperCase(cp);
+		else
+		    LYLowerCase(cp);
+	    }
+	    return;
+	}
+    }
+
+    suffix = HTCompressTypeToSuffix(method);
+
+    /*
+     * Add the appropriate suffix.  -FM
+     */
+    if (*suffix) {
+	if (!dot) {
+	    StrAllocCat(*fname, suffix);
+	} else if (*++dot == '\0') {
+	    StrAllocCat(*fname, suffix + 1);
+	} else {
+	    StrAllocCat(*fname, suffix);
+#ifdef VMS
+	    (*fname)[strlen(*fname) - strlen(suffix)] = '-';
+#endif /* !VMS */
+	}
+    }
+}
+
+/*
+ * HText_getLastModified returns the Last-Modified header
+ * if available, for the current document.  -FM
+ */
+const char *HText_getLastModified(void)
+{
+    return (HTMainText ?
+	    HTAnchor_last_modified(HTMainText->node_anchor) : 0);
+}
+
+/*
+ * HText_getDate returns the Date header
+ * if available, for the current document.  -FM
+ */
+const char *HText_getDate(void)
+{
+    return (HTMainText ?
+	    HTAnchor_date(HTMainText->node_anchor) : 0);
+}
+
+/*
+ * HText_getServer returns the Server header
+ * if available, for the current document.  -FM
+ */
+const char *HText_getServer(void)
+{
+    return (HTMainText ?
+	    HTAnchor_server(HTMainText->node_anchor) : 0);
+}
+
+#ifdef EXP_HTTP_HEADERS
+/*
+ * Returns the full text of HTTP headers, if available, for the current
+ * document.
+ */
+const char *HText_getHttpHeaders(void)
+{
+    return (HTMainText ?
+	    HTAnchor_http_headers(HTMainText->node_anchor) : 0);
+}
+#endif
+
+/*
+ * HText_pageDisplay displays a screen of text
+ * starting from the line 'line_num'-1.
+ * This is the primary call for lynx.
+ */
+void HText_pageDisplay(int line_num,
+		       char *target)
+{
+#ifdef DISP_PARTIAL
+    if (debug_display_partial || (LYTraceLogFP != NULL)) {
+	CTRACE((tfp, "GridText: HText_pageDisplay at line %d started\n", line_num));
+    }
+
+    if (display_partial) {
+	int stop_before = -1;
+
+	/*
+	 * Garbage is reported from forms input fields in incremental mode.
+	 * So we start HText_trimHightext() to forget this side effect.
+	 * This function was split-out from HText_endAppend().
+	 * It may not be the best solution but it works.  - LP
+	 *
+	 * (FALSE = indicate that we are in partial mode)
+	 * Multiple calls of HText_trimHightext works without problem now.
+	 */
+	if (HTMainText && HTMainText->stbl)
+	    stop_before = Stbl_getStartLineDeep(HTMainText->stbl);
+	HText_trimHightext(HTMainText, FALSE, stop_before);
+    }
+#endif
+
+    display_page(HTMainText, line_num - 1, target);
+
+#ifdef DISP_PARTIAL
+    if (display_partial && debug_display_partial)
+	LYSleepMsg();
+#endif
+
+    is_www_index = HTAnchor_isIndex(HTMainAnchor);
+
+#ifdef DISP_PARTIAL
+    if (debug_display_partial || (LYTraceLogFP != NULL)) {
+	CTRACE((tfp, "GridText: HText_pageDisplay finished\n"));
+    }
+#endif
+}
+
+/*
+ * Return YES if we have a whereis search target on the displayed
+ * page.  - kw
+ */
+BOOL HText_pageHasPrevTarget(void)
+{
+    if (!HTMainText)
+	return NO;
+    else
+	return HTMainText->page_has_target;
+}
+
+/*
+ * Find the number of the closest anchor to the given document offset.  Used
+ * in reparsing, this will usually find an exact match, as a link shifts around
+ * on the display.  It will not find a match when (for example) the source view
+ * shows images that are not links in the html.
+ */
+int HText_closestAnchor(HText *text, int offset)
+{
+    int result = -1;
+    int absdiff = 0;
+    int newdiff;
+    TextAnchor *Anchor_ptr = NULL;
+    TextAnchor *closest = NULL;
+
+    for (Anchor_ptr = text->first_anchor;
+	 Anchor_ptr != NULL;
+	 Anchor_ptr = Anchor_ptr->next) {
+	if (Anchor_ptr->sgml_offset == offset) {
+	    result = Anchor_ptr->number;
+	    break;
+	} else {
+	    newdiff = abs(Anchor_ptr->sgml_offset - offset);
+	    if (absdiff == 0 || absdiff > newdiff) {
+		absdiff = newdiff;
+		closest = Anchor_ptr;
+	    }
+	}
+    }
+    if (result < 0 && closest != 0) {
+	result = closest->number;
+    }
+
+    return result;
+}
+
+/*
+ * Find the offset for the given anchor, e.g., the inverse of
+ * HText_closestAnchor().
+ */
+int HText_locateAnchor(HText *text, int anchor_number)
+{
+    int result = -1;
+    TextAnchor *Anchor_ptr = NULL;
+
+    for (Anchor_ptr = text->first_anchor;
+	 Anchor_ptr != NULL;
+	 Anchor_ptr = Anchor_ptr->next) {
+	if (Anchor_ptr->number == anchor_number) {
+	    result = Anchor_ptr->sgml_offset;
+	    break;
+	}
+    }
+
+    return result;
+}
+
+/*
+ * This is supposed to give the same result as the inline checks in
+ * display_page(), so we can decide which anchors will be visible.
+ */
+static BOOL anchor_is_numbered(TextAnchor *Anchor_ptr)
+{
+    BOOL result = FALSE;
+
+    if (Anchor_ptr->show_anchor
+    /* FIXME: && non_empty(hi_string) */
+	&& (Anchor_ptr->link_type & HYPERTEXT_ANCHOR)) {
+	result = TRUE;
+    } else if (Anchor_ptr->link_type == INPUT_ANCHOR
+	       && Anchor_ptr->input_field->type != F_HIDDEN_TYPE) {
+	result = TRUE;
+    }
+    return result;
+}
+
+/*
+ * Return the absolute line number (counting from the beginning of the
+ * document) for the given absolute anchor number.  Normally line numbers are
+ * computed within the screen, and for that we use the links[] array.  A few
+ * uses require the absolute anchor number.  For example, reparsing a document,
+ * e.g., switching between normal and source views will alter the line numbers
+ * of each link, and may require adjusting the top line number used for the
+ * display, before we recompute the links[] array.
+ */
+int HText_getAbsLineNumber(HText *text,
+			   int anchor_number)
+{
+    int result = -1;
+
+    if (anchor_number >= 0 && text != 0) {
+	TextAnchor *Anchor_ptr = NULL;
+
+	for (Anchor_ptr = text->first_anchor;
+	     Anchor_ptr != NULL;
+	     Anchor_ptr = Anchor_ptr->next) {
+	    if (anchor_is_numbered(Anchor_ptr)
+		&& Anchor_ptr->number == anchor_number) {
+		result = Anchor_ptr->line_num;
+		break;
+	    }
+	}
+    }
+    return result;
+}
+
+/*
+ * Compute the link-number in a page, given the top line number of the page and
+ * the absolute anchor number.
+ */
+int HText_anchorRelativeTo(HText *text, int top_lineno, int anchor_number)
+{
+    int result = 0;
+    int from_top = 0;
+    TextAnchor *Anchor_ptr = NULL;
+
+    for (Anchor_ptr = text->first_anchor;
+	 Anchor_ptr != NULL;
+	 Anchor_ptr = Anchor_ptr->next) {
+	if (Anchor_ptr->number == anchor_number) {
+	    result = from_top;
+	    break;
+	}
+	if (!anchor_is_numbered(Anchor_ptr))
+	    continue;
+	if (Anchor_ptr->line_num >= top_lineno) {
+	    ++from_top;
+	}
+    }
+    return result;
+}
+
+/*
+ * HText_LinksInLines returns the number of links in the
+ * 'Lines' number of lines beginning with 'line_num'-1.  -FM
+ */
+int HText_LinksInLines(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 &&
+	    !(Anchor_ptr->link_type == INPUT_ANCHOR
+	      && Anchor_ptr->input_field->type == F_HIDDEN_TYPE))
+	    ++total;
+    }
+
+    return total;
+}
+
+void HText_setStale(HText *text)
+{
+    text->stale = YES;
+}
+
+void HText_refresh(HText *text)
+{
+    if (text->stale)
+	display_page(text, text->top_of_screen, "");
+}
+
+int HText_sourceAnchors(HText *text)
+{
+    return (text ? text->last_anchor_number : -1);
+}
+
+BOOL HText_canScrollUp(HText *text)
+{
+    return (BOOL) (text->top_of_screen != 0);
+}
+
+/*
+ * Check if there is more info below this page.
+ */
+BOOL HText_canScrollDown(void)
+{
+    HText *text = HTMainText;
+
+    return (BOOL) ((text != 0)
+		   && ((text->top_of_screen + display_lines) <= text->Lines));
+}
+
+/*		Scroll actions
+*/
+void HText_scrollTop(HText *text)
+{
+    display_page(text, 0, "");
+}
+
+void HText_scrollDown(HText *text)
+{
+    display_page(text, text->top_of_screen + display_lines, "");
+}
+
+void HText_scrollUp(HText *text)
+{
+    display_page(text, text->top_of_screen - display_lines, "");
+}
+
+void HText_scrollBottom(HText *text)
+{
+    display_page(text, text->Lines - display_lines, "");
+}
+
+/*		Browsing functions
+ *		==================
+ */
+
+/* Bring to front and highlight it
+*/
+BOOL HText_select(HText *text)
+{
+    if (text != HTMainText) {
+	/*
+	 * Reset flag for whereis search string - cannot be true here
+	 * since text is not our HTMainText.  - kw
+	 */
+	if (text)
+	    text->page_has_target = NO;
+
+#ifdef DISP_PARTIAL
+	/* Reset these for the previous and current text. - kw */
+	ResetPartialLinenos(text);
+	ResetPartialLinenos(HTMainText);
+#endif /* DISP_PARTIAL */
+
+#ifdef CAN_SWITCH_DISPLAY_CHARSET
+	/* text->UCLYhndl is not reset by META, so use a more circumvent way */
+	if (text->node_anchor->UCStages->s[UCT_STAGE_HTEXT].LYhndl
+	    != current_char_set)
+	    Switch_Display_Charset(text->node_anchor->UCStages->s[UCT_STAGE_HTEXT].LYhndl, SWITCH_DISPLAY_CHARSET_MAYBE);
+#endif
+	assert(text != NULL);
+	if (HTMainText) {
+	    if (HText_hasUTF8OutputSet(HTMainText) &&
+		HTLoadedDocumentEightbit() &&
+		IS_UTF8_TTY) {
+		text->had_utf8 = HTMainText->has_utf8;
+	    } else {
+		text->had_utf8 = NO;
+	    }
+	    HTMainText->has_utf8 = NO;
+	    text->has_utf8 = NO;
+	}
+
+	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);
+    }
+    return YES;
+}
+
+/*
+ * This function returns TRUE if doc's post_data, address
+ * and isHEAD elements are identical to those of a loaded
+ * (memory cached) text.  -FM
+ */
+BOOL HText_POSTReplyLoaded(DocInfo *doc)
+{
+    HText *text = NULL;
+    HTList *cur = loaded_texts;
+    bstring *post_data;
+    char *address;
+    BOOL is_head;
+
+    /*
+     * Make sure we have the structures.  -FM
+     */
+    if (!cur || !doc)
+	return (FALSE);
+
+    /*
+     * Make sure doc is for a POST reply.  -FM
+     */
+    if ((post_data = doc->post_data) == NULL ||
+	(address = doc->address) == NULL)
+	return (FALSE);
+    is_head = doc->isHEAD;
+
+    /*
+     * Loop through the loaded texts looking for a
+     * POST reply match.  -FM
+     */
+    while (NULL != (text = (HText *) HTList_nextObject(cur))) {
+	if (text->node_anchor &&
+	    text->node_anchor->post_data &&
+	    BINEQ(post_data, text->node_anchor->post_data) &&
+	    text->node_anchor->address &&
+	    !strcmp(address, text->node_anchor->address) &&
+	    is_head == text->node_anchor->isHEAD) {
+	    return (TRUE);
+	}
+    }
+
+    return (FALSE);
+}
+
+BOOL HTFindPoundSelector(const char *selector)
+{
+    TextAnchor *a;
+
+    CTRACE((tfp, "FindPound: searching for \"%s\"\n", selector));
+    for (a = HTMainText->first_anchor; a != 0; a = a->next) {
+
+	if (a->anchor && a->anchor->tag) {
+	    if (!strcmp(a->anchor->tag, selector)) {
+
+		www_search_result = a->line_num + 1;
+
+		CTRACE((tfp, "FindPound: Selecting anchor [%d] at line %d\n",
+			a->number, www_search_result));
+		if (!strcmp(selector, LYToolbarName)) {
+		    --www_search_result;
+		}
+		return (YES);
+	    }
+	}
+    }
+    return (NO);
+}
+
+BOOL HText_selectAnchor(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) {
+	CTRACE((tfp, "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 = a->line_num;
+
+	CTRACE((tfp, "HText: Selecting anchor [%d] at line %d\n",
+		a->number, 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 original text object.
+ */
+
+/*	Style handling:
+*/
+/*	Apply this style to the selection
+*/
+void HText_applyStyle(HText *me GCC_UNUSED, HTStyle *style GCC_UNUSED)
+{
+
+}
+
+/*	Update all text with changed style.
+*/
+void HText_updateStyle(HText *me GCC_UNUSED, HTStyle *style GCC_UNUSED)
+{
+
+}
+
+/*	Return style of  selection
+*/
+HTStyle *HText_selectionStyle(HText *me GCC_UNUSED, HTStyleSheet *sheet GCC_UNUSED)
+{
+    return 0;
+}
+
+/*	Paste in styled text
+*/
+void HText_replaceSel(HText *me GCC_UNUSED, const char *aString GCC_UNUSED,
+		      HTStyle *aStyle GCC_UNUSED)
+{
+}
+
+/*	Apply this style to the selection and all similarly formatted text
+ *	(style recovery only)
+ */
+void HTextApplyToSimilar(HText *me GCC_UNUSED, HTStyle *style GCC_UNUSED)
+{
+
+}
+
+/*	Select the first unstyled run.
+ *	(style recovery only)
+ */
+void HTextSelectUnstyled(HText *me GCC_UNUSED, HTStyleSheet *sheet GCC_UNUSED)
+{
+
+}
+
+/*	Anchor handling:
+*/
+void HText_unlinkSelection(HText *me GCC_UNUSED)
+{
+
+}
+
+HTAnchor *HText_referenceSelected(HText *me GCC_UNUSED)
+{
+    return 0;
+}
+
+int HText_getTopOfScreen(void)
+{
+    HText *text = HTMainText;
+
+    return text != 0 ? text->top_of_screen : 0;
+}
+
+int HText_getLines(HText *text)
+{
+    return text->Lines;
+}
+
+/*
+ * Constrain the line number to be within the document.  The line number is
+ * zero-based.
+ */
+int HText_getPreferredTopLine(HText *text, int line_number)
+{
+    int last_screen = text->Lines - (display_lines - 2);
+
+    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;
+    }
+    return line_number;
+}
+
+HTAnchor *HText_linkSelTo(HText *me GCC_UNUSED,
+			  HTAnchor * anchor GCC_UNUSED)
+{
+    return 0;
+}
+
+/*
+ * Utility for freeing the list of previous isindex and whereis queries.  -FM
+ */
+void HTSearchQueries_free(void)
+{
+    LYFreeStringList(search_queries);
+    search_queries = NULL;
+}
+
+/*
+ * Utility for listing isindex and whereis queries, making
+ * any repeated queries the most current in the list.  -FM
+ */
+void HTAddSearchQuery(char *query)
+{
+    char *new_query = NULL;
+    char *old;
+    HTList *cur;
+
+    if (!non_empty(query))
+	return;
+
+    StrAllocCopy(new_query, query);
+
+    if (!search_queries) {
+	search_queries = HTList_new();
+#ifdef LY_FIND_LEAKS
+	atexit(HTSearchQueries_free);
+#endif
+	HTList_addObject(search_queries, new_query);
+	return;
+    }
+
+    cur = search_queries;
+    while (NULL != (old = (char *) HTList_nextObject(cur))) {
+	if (!strcmp(old, new_query)) {
+	    HTList_removeObject(search_queries, old);
+	    FREE(old);
+	    break;
+	}
+    }
+    HTList_addObject(search_queries, new_query);
+
+    return;
+}
+
+int do_www_search(DocInfo *doc)
+{
+    char searchstring[256], temp[256], *cp, *tmpaddress = NULL;
+    int ch;
+    RecallType recall;
+    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;
+	LYstrncpy(searchstring, ++cp, sizeof(searchstring) - 1);
+	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_URL : 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) {
+		LYstrncpy(searchstring, cp, sizeof(searchstring) - 1);
+		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) {
+		LYstrncpy(searchstring, cp, sizeof(searchstring) - 1);
+		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.
+	 */
+	HTInfoMsg(CANCELLED);
+	return (NULLFILE);
+    }
+
+    /*
+     * Strip leaders and trailers.  -FM
+     */
+    LYTrimLeading(searchstring);
+    if (!(*searchstring)) {
+	HTInfoMsg(CANCELLED);
+	return (NULLFILE);
+    }
+    LYTrimTrailing(searchstring);
+
+    /*
+     * Don't resubmit the same query unintentionally.
+     */
+    if (!LYforce_no_cache && 0 == strcmp(temp, searchstring)) {
+	HTUserMsg(USE_C_R_TO_RESUB_CUR_QUERY);
+	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);
+#ifdef SYSLOG_REQUESTED_URLS
+    LYSyslog(tmpaddress);
+#endif
+    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);
+
+	CTRACE((tfp, "\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);
+}
+
+static void write_offset(FILE *fp, HTLine *line)
+{
+    int i;
+
+    if (line->data[0]) {
+	for (i = 0; i < (int) line->offset; i++) {
+	    fputc(' ', fp);
+	}
+    }
+}
+
+static void write_hyphen(FILE *fp)
+{
+    if (dump_output_immediately &&
+	LYRawMode &&
+	LYlowest_eightbit[current_char_set] <= 173 &&
+	(LYCharSet_UC[current_char_set].enc == UCT_ENC_8859 ||
+	 (LYCharSet_UC[current_char_set].like8859 & UCT_R_8859SPECL)) != 0) {
+	fputc(0xad, fp);	/* the iso8859 byte for SHY */
+    } else {
+	fputc('-', fp);
+    }
+}
+
+/*
+ * Returns the length after trimming trailing blanks.  Modify the string as
+ * needed so that any special character which follows a trailing blank is moved
+ * before the (trimmed) blank, so the result which will be dumped has no
+ * trailing blanks.
+ */
+static int TrimmedLength(char *string)
+{
+    int result = strlen(string);
+
+    if (!HTisDocumentSource()) {
+	int adjust = result;
+	unsigned ch;
+
+	while (adjust > 0) {
+	    ch = UCH(string[adjust - 1]);
+	    if (isspace(ch) || IsSpecialAttrChar(ch)) {
+		--adjust;
+	    } else {
+		break;
+	    }
+	}
+	if (result != adjust) {
+	    char *dst = string + adjust;
+	    char *src = dst;
+
+	    for (;;) {
+		src = LYSkipBlanks(src);
+		if ((*dst++ = *src++) == '\0')
+		    break;
+	    }
+	    result = (dst - string - 1);
+	}
+    }
+    return result;
+}
+
+typedef struct _AnchorIndex {
+    struct _AnchorIndex *next;
+    int type;			/* field type */
+    int size;			/* character-width of field */
+    int length;			/* byte-count for field's data */
+    int offset;			/* byte-offset in line's data */
+    int filler;			/* character to use for filler */
+    const char *value;		/* field's value */
+} AnchorIndex;
+
+static unsigned countHTLines(void)
+{
+    unsigned result = 0;
+    HTLine *line = FirstHTLine(HTMainText);
+
+    while (line != 0) {
+	++result;
+	if (line == HTMainText->last_line)
+	    break;
+	line = line->next;
+    }
+    CTRACE((tfp, "countHTLines %u\n", result));
+    return result;
+}
+
+/*
+ * The TextAnchor list is not organized to allow efficient dumping of a page.
+ * Make an array with one item per line of the page, and store (by byte-offset)
+ * pointers to the TextAnchor's we want to use.
+ */
+static AnchorIndex **allocAnchorIndex(unsigned *size)
+{
+    AnchorIndex **result = NULL;
+    AnchorIndex *p, *q;
+    TextAnchor *anchor = NULL;
+    FormInfo *input = NULL;
+
+    *size = countHTLines();
+    if (*size != 0) {
+	result = typecallocn(AnchorIndex *, *size + 1);
+
+	for (anchor = HTMainText->first_anchor;
+	     anchor != NULL;
+	     anchor = anchor->next) {
+
+	    if (anchor->link_type == INPUT_ANCHOR
+		&& anchor->show_anchor
+		&& anchor->line_num < (int) *size
+		&& (input = anchor->input_field) != NULL) {
+		CTRACE2(TRACE_GRIDTEXT,
+			(tfp, "line %d.%d %d %s->%s(%s)\n",
+			 anchor->line_num,
+			 anchor->line_pos,
+			 input->size,
+			 inputFieldDesc(input),
+			 input->value,
+			 input->orig_value));
+		switch (input->type) {
+		case F_SUBMIT_TYPE:
+		case F_RESET_TYPE:
+		case F_TEXT_SUBMIT_TYPE:
+		case F_IMAGE_SUBMIT_TYPE:
+		    CTRACE2(TRACE_GRIDTEXT, (tfp, "skipping\n"));
+		    continue;
+		case F_TEXT_TYPE:
+		case F_PASSWORD_TYPE:
+		case F_CHECKBOX_TYPE:
+		case F_RADIO_TYPE:
+		case F_OPTION_LIST_TYPE:
+		case F_TEXTAREA_TYPE:
+		case F_RANGE_TYPE:
+		case F_FILE_TYPE:
+		    p = typecalloc(AnchorIndex);
+		    if (p == NULL)
+			outofmem(__FILE__, "allocAnchorIndex");
+
+		    assert(p != NULL);
+		    p->type = input->type;
+		    p->size = input->size;
+		    p->offset = anchor->line_pos;
+		    p->value = input->value;
+
+		    switch (input->type) {
+		    case F_TEXTAREA_TYPE:
+		    case F_TEXT_TYPE:
+		    case F_PASSWORD_TYPE:
+			p->filler = '_';
+			break;
+		    case F_OPTION_LIST_TYPE:
+			p->filler = '_';
+			break;
+		    case F_CHECKBOX_TYPE:
+			p->value = (input->num_value
+				    ? checked_box
+				    : unchecked_box);
+			break;
+		    case F_RADIO_TYPE:
+			p->value = (input->num_value
+				    ? checked_radio
+				    : unchecked_radio);
+			break;
+		    default:
+			p->filler = ' ';
+			break;
+		    }
+		    p->length = strlen(p->value);
+
+		    if ((q = result[anchor->line_num]) != NULL) {
+			/* insert, ordering by offset */
+			if (q->offset < p->offset) {
+			    while (q->next != NULL
+				   && q->next->offset < p->offset) {
+				q = q->next;
+			    }
+			    p->next = q->next;
+			    q->next = p;
+			} else {
+			    p->next = q;
+			    result[anchor->line_num] = p;
+			}
+		    } else {
+			result[anchor->line_num] = p;
+		    }
+		    break;
+		}
+	    }
+	}
+    }
+    return result;
+}
+
+/*
+ * Free the data allocated in allocAnchorIndex().
+ */
+static void freeAnchorIndex(AnchorIndex ** inx, unsigned inx_size)
+{
+    AnchorIndex *cur;
+    unsigned num;
+
+    if (inx != 0) {
+	if (inx_size != 0) {
+	    for (num = 0; num < inx_size; ++num) {
+		while ((cur = inx[num]) != NULL) {
+		    inx[num] = cur->next;
+		    free(cur);
+		}
+	    }
+	}
+	free(inx);
+    }
+}
+
+/*
+ * Print the contents of the file in HTMainText to
+ * the file descriptor fp.
+ * If is_email is TRUE add ">" before each "From " line.
+ * If is_reply is TRUE add ">" to the beginning of each
+ * line to specify the file is a reply to message.
+ */
+#define FieldFirst(p) (this_wrap ? 0 : (p)->offset)
+#define FieldLast(p)  (FieldFirst(p) + (p)->size - this_wrap)
+void print_wwwfile_to_fd(FILE *fp,
+			 BOOLEAN is_email,
+			 BOOLEAN is_reply)
+{
+    int line_num, byte_num, byte_count;
+    int first = TRUE;
+    HTLine *line;
+    AnchorIndex **inx;		/* sorted index of input-fields */
+    AnchorIndex *cur = 0;	/* current input-field */
+    unsigned inx_size;		/* number of entries in inx[] */
+    int in_field = -1;		/* if positive, is index in cur->value[] */
+    int this_wrap = 0;		/* current wrapping point of cur->value[] */
+    int next_wrap = 0;		/* next wrapping point of cur->value[] */
+
+#ifndef NO_DUMP_WITH_BACKSPACES
+    HText *text = HTMainText;
+    BOOL in_b = FALSE;
+    BOOL in_u = FALSE;
+    BOOL bs = (BOOL) (!is_email && !is_reply
+		      && text != 0
+		      && with_backspaces
+		      && !IS_CJK_TTY
+		      && !text->T.output_utf8);
+#endif
+
+    if (!HTMainText)
+	return;
+
+    /*
+     * Build an index of anchors for each line, so we can override the
+     * static text which is stored in the list of HTLine's.
+     */
+    inx = allocAnchorIndex(&inx_size);
+
+    line = FirstHTLine(HTMainText);
+    for (line_num = 0;; ++line_num, line = line->next) {
+	if (in_field >= 0) {
+	    this_wrap = next_wrap;
+	    next_wrap = 0;	/* FIXME - allow for multiple continuations */
+	    CTRACE2(TRACE_GRIDTEXT,
+		    (tfp, "wrap %d:%d, offset %d\n",
+		     in_field, cur ? cur->length : -1, this_wrap));
+	} else {
+	    cur = inx[line_num];
+	}
+
+	CTRACE2(TRACE_GRIDTEXT, (tfp, "dump %d:%s\n", line_num, line->data));
+
+	if (first) {
+	    first = FALSE;
+	    if (is_reply) {
+		fputc('>', fp);
+	    } else if (is_email && !strncmp(line->data, "From ", 5)) {
+		fputc('>', fp);
+	    }
+	} else if (line->data[0] != LY_SOFT_NEWLINE) {
+	    fputc('\n', fp);
+	    /*
+	     * Add news-style quotation if requested.  -FM
+	     */
+	    if (is_reply) {
+		fputc('>', fp);
+	    } else if (is_email && !strncmp(line->data, "From ", 5)) {
+		fputc('>', fp);
+	    }
+	}
+
+	write_offset(fp, line);
+
+	/*
+	 * Add data.
+	 */
+	byte_count = TrimmedLength(line->data);
+	for (byte_num = 0; byte_num < byte_count; byte_num++) {
+	    int byte_offset = byte_num + line->offset;
+	    int ch = UCH(line->data[byte_num]);
+	    int c2;
+
+	    while (cur != 0 && FieldLast(cur) < byte_offset) {
+		CTRACE2(TRACE_GRIDTEXT,
+			(tfp, "skip field since last %d < %d\n",
+			 FieldLast(cur), byte_offset));
+		cur = cur->next;
+		in_field = -1;
+	    }
+	    if (cur != 0 && in_field >= 0) {
+		CTRACE2(TRACE_GRIDTEXT,
+			(tfp, "compare %d to [%d..%d]\n",
+			 byte_offset,
+			 FieldFirst(cur),
+			 FieldLast(cur) - 1));
+	    }
+	    if (cur != 0
+		&& FieldFirst(cur) <= byte_offset
+		&& FieldLast(cur) > byte_offset) {
+		int off2 = ((in_field > 0)
+			    ? in_field
+			    : (byte_offset - FieldFirst(cur)));
+
+		/*
+		 * On the first time (for each line that the field appears on),
+		 * check if this field wraps.  If it does, save the offset into
+		 * the field which will be used to adjust the beginning of the
+		 * continuation line.
+		 */
+		if (byte_offset == FieldFirst(cur)) {
+		    next_wrap = 0;
+		    if (cur->size - this_wrap + byte_num > byte_count) {
+			CTRACE((tfp, "size %d, offset %d, length %d\n",
+				cur->size,
+				cur->offset,
+				cur->length));
+			CTRACE((tfp, "byte_count %d, byte_num %d\n",
+				byte_count, byte_num));
+			next_wrap = byte_count - byte_num;
+			CTRACE2(TRACE_GRIDTEXT,
+				(tfp, "field will wrap: %d\n", next_wrap));
+		    }
+		}
+
+		c2 = ((off2 < cur->length)
+		      ? cur->value[off2]
+		      : cur->filler);
+
+		if (ch != c2) {
+		    CTRACE2(TRACE_GRIDTEXT,
+			    (tfp, "line %d %d/%d [%d..%d] map %d %c->%c\n",
+			     line_num,
+			     off2, cur->length,
+			     FieldFirst(cur), FieldLast(cur) - 1,
+			     byte_offset, ch, c2));
+		    ch = c2;
+		}
+		++off2;
+		if ((off2 >= cur->size) &&
+		    (off2 >= cur->length || F_TEXTLIKE(cur->type))) {
+		    in_field = -1;
+		    this_wrap = 0;
+		    next_wrap = 0;
+		} else {
+		    in_field = off2;
+		}
+	    }
+
+	    if (!IsSpecialAttrChar(ch)) {
+#ifndef NO_DUMP_WITH_BACKSPACES
+		if (in_b) {
+		    fputc(ch, fp);
+		    fputc('\b', fp);
+		    fputc(ch, fp);
+		} else if (in_u) {
+		    fputc('_', fp);
+		    fputc('\b', fp);
+		    fputc(ch, fp);
+		} else
+#endif
+		    fputc(ch, fp);
+	    } else if (ch == LY_SOFT_HYPHEN &&
+		       (byte_num + 1) >= byte_count) {
+		write_hyphen(fp);
+	    } else if (dump_output_immediately && use_underscore) {
+		switch (ch) {
+		case LY_UNDERLINE_START_CHAR:
+		case LY_UNDERLINE_END_CHAR:
+		    fputc('_', fp);
+		    break;
+		case LY_BOLD_START_CHAR:
+		case LY_BOLD_END_CHAR:
+		    break;
+		}
+	    }
+#ifndef NO_DUMP_WITH_BACKSPACES
+	    else if (bs) {
+		switch (ch) {
+		case LY_UNDERLINE_START_CHAR:
+		    if (!in_b)
+			in_u = TRUE;	/*favor bold over underline */
+		    break;
+		case LY_UNDERLINE_END_CHAR:
+		    in_u = FALSE;
+		    break;
+		case LY_BOLD_START_CHAR:
+		    if (in_u)
+			in_u = FALSE;	/* turn it off */
+		    in_b = TRUE;
+		    break;
+		case LY_BOLD_END_CHAR:
+		    in_b = FALSE;
+		    break;
+		}
+	    }
+#endif
+	}
+
+	if (line == HTMainText->last_line)
+	    break;
+
+#ifdef VMS
+	if (HadVMSInterrupt)
+	    break;
+#endif /* VMS */
+    }
+    fputc('\n', fp);
+
+    freeAnchorIndex(inx, inx_size);
+}
+
+/*
+ * Print the contents of the file in HTMainText to
+ * the file descriptor fp.
+ * First output line is "thelink", ie, the URL for this file.
+ */
+void print_crawl_to_fd(FILE *fp, char *thelink,
+		       char *thetitle)
+{
+    register int i;
+    int first = TRUE;
+    int limit;
+    HTLine *line;
+
+    if (!HTMainText)
+	return;
+
+    line = FirstHTLine(HTMainText);
+    fprintf(fp, "THE_URL:%s\n", thelink);
+    if (thetitle != NULL) {
+	fprintf(fp, "THE_TITLE:%s\n", thetitle);
+    }
+
+    for (;; line = line->next) {
+	if (!first && line->data[0] != LY_SOFT_NEWLINE)
+	    fputc('\n', fp);
+	first = FALSE;
+	write_offset(fp, line);
+
+	/*
+	 * Add data.
+	 */
+	limit = TrimmedLength(line->data);
+	for (i = 0; i < limit; i++) {
+	    int ch = UCH(line->data[i]);
+
+	    if (!IsSpecialAttrChar(ch)) {
+		fputc(ch, fp);
+	    } else if (ch == LY_SOFT_HYPHEN &&
+		       (i + 1) >= limit) {	/* last char on line */
+		write_hyphen(fp);
+	    }
+	}
+
+	if (line == HTMainText->last_line) {
+	    break;
+	}
+    }
+    fputc('\n', fp);
+
+    /*
+     * Add the References list if appropriate
+     */
+    if ((no_list == FALSE) &&
+	links_are_numbered()) {
+	printlist(fp, FALSE);
+    }
+#ifdef VMS
+    HadVMSInterrupt = FALSE;
+#endif /* VMS */
+}
+
+static void adjust_search_result(DocInfo *doc, int tentative_result,
+				 int start_line)
+{
+    if (tentative_result > 0) {
+	int anch_line = -1;
+	TextAnchor *a;
+	int nl_closest = -1;
+	int goal = SEARCH_GOAL_LINE;
+	int max_offset;
+	BOOL on_screen = (BOOL) (tentative_result > HTMainText->top_of_screen &&
+				 tentative_result <= HTMainText->top_of_screen +
+				 display_lines);
+
+	if (goal < 1)
+	    goal = 1;
+	else if (goal > display_lines)
+	    goal = display_lines;
+	max_offset = goal - 1;
+
+	if (on_screen && nlinks > 0) {
+	    int i;
+
+	    for (i = 0; i < nlinks; i++) {
+		if (doc->line + links[i].ly - 1 <= tentative_result)
+		    nl_closest = i;
+		if (doc->line + links[i].ly - 1 >= tentative_result)
+		    break;
+	    }
+	    if (nl_closest >= 0 &&
+		doc->line + links[nl_closest].ly - 1 == tentative_result) {
+		www_search_result = doc->line;
+		doc->link = nl_closest;
+		return;
+	    }
+	}
+
+	/* find last anchor before or on target line */
+	for (a = HTMainText->first_anchor;
+	     a && a->line_num <= tentative_result - 1; a = a->next) {
+	    anch_line = a->line_num + 1;
+	}
+	/* position such that the anchor found is on first screen line,
+	   if it is not too far above the target line; but also try to
+	   make sure we move forward. */
+	if (anch_line >= 0 &&
+	    anch_line >= tentative_result - max_offset &&
+	    (anch_line > start_line ||
+	     tentative_result <= HTMainText->top_of_screen)) {
+	    www_search_result = anch_line;
+	} else if (tentative_result - start_line > 0 &&
+		   tentative_result - (start_line + 1) <= max_offset) {
+	    www_search_result = start_line + 1;
+	} else if (tentative_result > HTMainText->top_of_screen &&
+		   tentative_result <= start_line &&	/* have wrapped */
+		   tentative_result <= HTMainText->top_of_screen + goal) {
+	    www_search_result = HTMainText->top_of_screen + 1;
+	} else if (tentative_result <= goal)
+	    www_search_result = 1;
+	else
+	    www_search_result = tentative_result - max_offset;
+	if (www_search_result == doc->line) {
+	    if (nl_closest >= 0) {
+		doc->link = nl_closest;
+		return;
+	    }
+	}
+    }
+}
+
+static BOOL anchor_has_target(TextAnchor *a, char *target)
+{
+    OptionType *option;
+    char *stars = NULL, *sp;
+    const char *cp;
+    int count;
+
+    /*
+     * Search the hightext strings, taking the case_sensitive setting into
+     * account.  -FM
+     */
+    for (count = 0;; ++count) {
+	if ((cp = LYGetHiTextStr(a, count)) == NULL)
+	    break;
+	if (LYno_attr_strstr(cp, target))
+	    return TRUE;
+    }
+
+    /*
+     * Search the relevant form fields, taking the
+     * case_sensitive setting into account.  -FM
+     */
+    if ((a->input_field != NULL && a->input_field->value != NULL) &&
+	a->input_field->type != F_HIDDEN_TYPE) {
+	if (a->input_field->type == F_PASSWORD_TYPE) {
+	    /*
+	     * Check the actual, hidden password, and then
+	     * the displayed string.  -FM
+	     */
+	    if (LYno_attr_strstr(a->input_field->value, target)) {
+		return TRUE;
+	    }
+	    StrAllocCopy(stars, a->input_field->value);
+	    for (sp = stars; *sp != '\0'; sp++)
+		*sp = '*';
+	    if (LYno_attr_strstr(stars, target)) {
+		FREE(stars);
+		return TRUE;
+	    }
+	    FREE(stars);
+	} else if (a->input_field->type == F_OPTION_LIST_TYPE) {
+	    /*
+	     * Search the option strings that are displayed
+	     * when the popup is invoked.  -FM
+	     */
+	    option = a->input_field->select_list;
+	    while (option != NULL) {
+		if (LYno_attr_strstr(option->name, target)) {
+		    return TRUE;
+		}
+		option = option->next;
+	    }
+	} else if (a->input_field->type == F_RADIO_TYPE) {
+	    /*
+	     * Search for checked or unchecked parens.  -FM
+	     */
+	    if (a->input_field->num_value) {
+		cp = checked_radio;
+	    } else {
+		cp = unchecked_radio;
+	    }
+	    if (LYno_attr_strstr(cp, target)) {
+		return TRUE;
+	    }
+	} else if (a->input_field->type == F_CHECKBOX_TYPE) {
+	    /*
+	     * Search for checked or unchecked square brackets.  -FM
+	     */
+	    if (a->input_field->num_value) {
+		cp = checked_box;
+	    } else {
+		cp = unchecked_box;
+	    }
+	    if (LYno_attr_strstr(cp, target)) {
+		return TRUE;
+	    }
+	} else {
+	    /*
+	     * Check the values intended for display.  May have been found
+	     * already via the hightext search, but make sure here that the
+	     * entire value is searched.  -FM
+	     */
+	    if (LYno_attr_strstr(a->input_field->value, target)) {
+		return TRUE;
+	    }
+	}
+    }
+    return FALSE;
+}
+
+static TextAnchor *line_num_to_anchor(int line_num)
+{
+    TextAnchor *a;
+
+    if (HTMainText != 0) {
+	a = HTMainText->first_anchor;
+	while (a != 0 && a->line_num < line_num) {
+	    a = a->next;
+	}
+    } else {
+	a = 0;
+    }
+    return a;
+}
+
+static int line_num_in_text(HText *text, HTLine *line)
+{
+    int result = 1;
+    HTLine *temp = FirstHTLine(text);
+
+    while (temp != line) {
+	temp = temp->next;
+	++result;
+    }
+    return result;
+}
+
+/* Computes the 'prev' pointers on demand, and returns the one for the given
+ * anchor.
+ */
+static TextAnchor *get_prev_anchor(TextAnchor *a)
+{
+    TextAnchor *p, *q;
+
+    if (a->prev == 0) {
+	if ((p = HTMainText->first_anchor) != 0) {
+	    while ((q = p->next) != 0) {
+		q->prev = p;
+		p = q;
+	    }
+	}
+    }
+    return a->prev;
+}
+
+static int www_search_forward(int start_line,
+			      DocInfo *doc,
+			      char *target,
+			      HTLine *line,
+			      int count)
+{
+    int wrapped = 0;
+    TextAnchor *a = line_num_to_anchor(count - 1);
+    int tentative_result = -1;
+
+    for (;;) {
+	while ((a != NULL) && a->line_num == (count - 1)) {
+	    if (a->show_anchor &&
+		!(a->link_type == INPUT_ANCHOR
+		  && a->input_field->type == F_HIDDEN_TYPE)) {
+		if (anchor_has_target(a, target)) {
+		    adjust_search_result(doc, count, start_line);
+		    return 1;
+		}
+	    }
+	    a = a->next;
+	}
+
+	if (LYno_attr_strstr(line->data, target)) {
+	    tentative_result = count;
+	    break;
+	} else if ((count == start_line && wrapped) || wrapped > 1) {
+	    HTUserMsg2(STRING_NOT_FOUND, target);
+	    return -1;
+	} else if (line == HTMainText->last_line) {
+	    count = 0;
+	    wrapped++;
+	}
+	line = line->next;
+	count++;
+    }
+    if (tentative_result > 0) {
+	adjust_search_result(doc, tentative_result, start_line);
+    }
+    return 0;
+}
+
+static int www_search_backward(int start_line,
+			       DocInfo *doc,
+			       char *target,
+			       HTLine *line,
+			       int count)
+{
+    int wrapped = 0;
+    TextAnchor *a = line_num_to_anchor(count - 1);
+    int tentative_result = -1;
+
+    for (;;) {
+	while ((a != NULL) && a->line_num == (count - 1)) {
+	    if (a->show_anchor &&
+		!(a->link_type == INPUT_ANCHOR
+		  && a->input_field->type == F_HIDDEN_TYPE)) {
+		if (anchor_has_target(a, target)) {
+		    adjust_search_result(doc, count, start_line);
+		    return 1;
+		}
+	    }
+	    a = get_prev_anchor(a);
+	}
+
+	if (LYno_attr_strstr(line->data, target)) {
+	    tentative_result = count;
+	    break;
+	} else if ((count == start_line && wrapped) || wrapped > 1) {
+	    HTUserMsg2(STRING_NOT_FOUND, target);
+	    return -1;
+	} else if (line == FirstHTLine(HTMainText)) {
+	    count = line_num_in_text(HTMainText, LastHTLine(HTMainText)) + 1;
+	    wrapped++;
+	}
+	line = line->prev;
+	count--;
+    }
+    if (tentative_result > 0) {
+	adjust_search_result(doc, tentative_result, start_line);
+    }
+    return 0;
+}
+
+void www_user_search(int start_line,
+		     DocInfo *doc,
+		     char *target,
+		     int direction)
+{
+    HTLine *line;
+    int count;
+
+    if (!HTMainText) {
+	return;
+    }
+
+    /*
+     * Advance to the start line.
+     */
+    line = FirstHTLine(HTMainText);
+    if (start_line + direction > 0) {
+	for (count = 1;
+	     count < start_line + direction;
+	     line = line->next, count++) {
+	    if (line == HTMainText->last_line) {
+		line = FirstHTLine(HTMainText);
+		count = 1;
+		break;
+	    }
+	}
+    } else {
+	line = HTMainText->last_line;
+	count = line_num_in_text(HTMainText, line);
+    }
+
+    if (direction >= 0)
+	www_search_forward(start_line, doc, target, line, count);
+    else
+	www_search_backward(start_line, doc, target, line, count);
+}
+
+void user_message(const char *message,
+		  const char *argument)
+{
+    if (message == NULL) {
+	mustshow = FALSE;
+    } else {
+	char *temp = NULL;
+
+	HTSprintf0(&temp, message, NonNull(argument));
+	statusline(temp);
+	FREE(temp);
+    }
+}
+
+/*
+ * HText_getOwner returns the owner of the
+ * current document.
+ */
+const char *HText_getOwner(void)
+{
+    return (HTMainText ?
+	    HTAnchor_owner(HTMainText->node_anchor) : 0);
+}
+
+/*
+ * HText_setMainTextOwner sets the owner for the
+ * current document.
+ */
+void HText_setMainTextOwner(const char *owner)
+{
+    if (!HTMainText)
+	return;
+
+    HTAnchor_setOwner(HTMainText->node_anchor, owner);
+}
+
+/*
+ * HText_getRevTitle returns the RevTitle element of the
+ * current document, used as the subject for mailto comments
+ * to the owner.
+ */
+const char *HText_getRevTitle(void)
+{
+    return (HTMainText ?
+	    HTAnchor_RevTitle(HTMainText->node_anchor) : 0);
+}
+
+/*
+ * HText_getContentBase returns the Content-Base header
+ * of the current document.
+ */
+const char *HText_getContentBase(void)
+{
+    return (HTMainText ?
+	    HTAnchor_content_base(HTMainText->node_anchor) : 0);
+}
+
+/*
+ * HText_getContentLocation returns the Content-Location header
+ * of the current document.
+ */
+const char *HText_getContentLocation(void)
+{
+    return (HTMainText ?
+	    HTAnchor_content_location(HTMainText->node_anchor) : 0);
+}
+
+/*
+ * HText_getMessageID returns the Message-ID of the
+ * current document.
+ */
+const char *HText_getMessageID(void)
+{
+    return (HTMainText ?
+	    HTAnchor_messageID(HTMainText->node_anchor) : NULL);
+}
+
+void HTuncache_current_document(void)
+{
+    /*
+     * Should remove current document from memory.
+     */
+    if (HTMainText) {
+	HTParentAnchor *htmain_anchor = HTMainText->node_anchor;
+
+	if (htmain_anchor) {
+	    if (!(HTOutputFormat && HTOutputFormat == WWW_SOURCE)) {
+		FREE(htmain_anchor->UCStages);
+	    }
+	}
+	CTRACE((tfp, "\nHTuncache.. freeing document for '%s'%s\n",
+		((htmain_anchor &&
+		  htmain_anchor->address) ?
+		 htmain_anchor->address : "unknown anchor"),
+		((htmain_anchor &&
+		  htmain_anchor->post_data)
+		 ? " with POST data"
+		 : "")));
+	HTList_removeObject(loaded_texts, HTMainText);
+	HText_free(HTMainText);
+	HTMainText = NULL;
+    } else {
+	CTRACE((tfp, "HTuncache.. HTMainText already is NULL!\n"));
+    }
+}
+
+#ifdef USE_SOURCE_CACHE
+
+/* dummy - kw */
+static HTProtocol scm =
+{
+    "source-cache-mem", 0, 0
+};
+
+static BOOLEAN useSourceCache(void)
+{
+    BOOLEAN result = FALSE;
+
+    if (LYCacheSource == SOURCE_CACHE_FILE) {
+	result = (BOOLEAN) (HTMainAnchor->source_cache_file != 0);
+	CTRACE((tfp, "SourceCache: file-cache%s found\n",
+		result ? "" : " not"));
+    }
+    return result;
+}
+
+static BOOLEAN useMemoryCache(void)
+{
+    BOOLEAN result = FALSE;
+
+    if (LYCacheSource == SOURCE_CACHE_MEMORY) {
+	result = (BOOLEAN) (HTMainAnchor->source_cache_chunk != 0);
+	CTRACE((tfp, "SourceCache: memory-cache%s found\n",
+		result ? "" : " not"));
+    }
+    return result;
+}
+
+BOOLEAN HTreparse_document(void)
+{
+    BOOLEAN ok = FALSE;
+
+    if (!HTMainAnchor || LYCacheSource == SOURCE_CACHE_NONE) {
+	CTRACE((tfp, "HTreparse_document returns FALSE\n"));
+    } else if (useSourceCache()) {
+	FILE *fp;
+	HTFormat format;
+	int ret;
+
+	CTRACE((tfp, "SourceCache: Reparsing file %s\n",
+		HTMainAnchor->source_cache_file));
+
+	/*
+	 * This magic FREE(anchor->UCStages) call
+	 * stolen from HTuncache_current_document() above.
+	 */
+	if (!(HTOutputFormat && HTOutputFormat == WWW_SOURCE)) {
+	    FREE(HTMainAnchor->UCStages);
+	}
+
+	/*
+	 * This is more or less copied out of HTLoadFile(), except we don't
+	 * get a content encoding.  This may be overkill.  -dsb
+	 */
+	if (HTMainAnchor->content_type) {
+	    format = HTAtom_for(HTMainAnchor->content_type);
+	} else {
+	    format = HTFileFormat(HTMainAnchor->source_cache_file, NULL, NULL);
+	    format = HTCharsetFormat(format, HTMainAnchor,
+				     UCLYhndl_for_unspec);
+	    /* not UCLYhndl_HTFile_for_unspec - we are talking about remote
+	     * documents...
+	     */
+	}
+	CTRACE((tfp, "  Content type is \"%s\"\n", format->name));
+
+	fp = fopen(HTMainAnchor->source_cache_file, "r");
+	if (!fp) {
+	    CTRACE((tfp, "  Cannot read file %s\n", HTMainAnchor->source_cache_file));
+	    LYRemoveTemp(HTMainAnchor->source_cache_file);
+	    FREE(HTMainAnchor->source_cache_file);
+	} else {
+
+	    if (HText_HaveUserChangedForms(HTMainText)) {
+		/*
+		 * Issue a warning.  Will not restore changed forms, currently.
+		 */
+		HTAlert(RELOADING_FORM);
+	    }
+	    /* Set HTMainAnchor->protocol or HTMainAnchor->physical to convince
+	     * the SourceCacheWriter to not regenerate the cache file (which
+	     * would be an unnecessary "loop"). - kw
+	     */
+	    HTAnchor_setProtocol(HTMainAnchor, &HTFile);
+	    ret = HTParseFile(format, HTOutputFormat, HTMainAnchor, fp, NULL);
+	    LYCloseInput(fp);
+	    if (ret == HT_PARTIAL_CONTENT) {
+		HTInfoMsg(gettext("Loading incomplete."));
+		CTRACE((tfp,
+			"SourceCache: `%s' has been accessed, partial content.\n",
+			HTLoadedDocumentURL()));
+	    }
+	    ok = (BOOL) (ret == HT_LOADED || ret == HT_PARTIAL_CONTENT);
+
+	    CTRACE((tfp, "Reparse file %s\n", (ok ? "succeeded" : "failed")));
+	}
+    } else if (useMemoryCache()) {
+	HTFormat format = WWW_HTML;
+	int ret;
+
+	CTRACE((tfp, "SourceCache: Reparsing from memory chunk %p\n",
+		(void *) HTMainAnchor->source_cache_chunk));
+
+	/*
+	 * This magic FREE(anchor->UCStages) call
+	 * stolen from HTuncache_current_document() above.
+	 */
+	if (!(HTOutputFormat && HTOutputFormat == WWW_SOURCE)) {
+	    FREE(HTMainAnchor->UCStages);
+	}
+
+	if (HTMainAnchor->content_type) {
+	    format = HTAtom_for(HTMainAnchor->content_type);
+	} else {
+	    /*
+	     * This is only done to make things aligned with SOURCE_CACHE_NONE
+	     * and SOURCE_CACHE_FILE when switching to source mode since the
+	     * original document's charset will be LYPushAssumed() and then
+	     * LYPopAssumed().  See LYK_SOURCE in mainloop if you change
+	     * something here.  No user-visible benefits, seems just '=' Info
+	     * Page will show source's effective charset as "(assumed)".
+	     */
+	    format = HTCharsetFormat(format, HTMainAnchor,
+				     UCLYhndl_for_unspec);
+	}
+	/* not UCLYhndl_HTFile_for_unspec - we are talking about remote documents... */
+
+	if (HText_HaveUserChangedForms(HTMainText)) {
+	    /*
+	     * Issue a warning.  Will not restore changed forms, currently.
+	     */
+	    HTAlert(RELOADING_FORM);
+	}
+	/* Set HTMainAnchor->protocol or HTMainAnchor->physical to convince
+	 * the SourceCacheWriter to not regenerate the cache chunk (which
+	 * would be an unnecessary "loop"). - kw
+	 */
+	HTAnchor_setProtocol(HTMainAnchor, &scm);	/* cheating -
+							   anything != &HTTP or &HTTPS would do - kw */
+	ret = HTParseMem(format, HTOutputFormat, HTMainAnchor,
+			 HTMainAnchor->source_cache_chunk, NULL);
+	ok = (BOOL) (ret == HT_LOADED);
+
+	CTRACE((tfp, "Reparse memory %s\n", (ok ? "succeeded" : "failed")));
+    }
+
+    return ok;
+}
+
+BOOLEAN HTcan_reparse_document(void)
+{
+    BOOLEAN result = FALSE;
+
+    if (!HTMainAnchor || LYCacheSource == SOURCE_CACHE_NONE) {
+	result = FALSE;
+    } else if (useSourceCache()) {
+	result = LYCanReadFile(HTMainAnchor->source_cache_file);
+    } else if (useMemoryCache()) {
+	result = TRUE;
+    }
+
+    CTRACE((tfp, "HTcan_reparse_document -> %d\n", result));
+    return result;
+}
+
+static void trace_setting_change(const char *name,
+				 int prev_setting,
+				 int new_setting)
+{
+    if (prev_setting != new_setting)
+	CTRACE((tfp,
+		"HTdocument_settings_changed: %s setting has changed (was %d, now %d)\n",
+		name, prev_setting, new_setting));
+}
+
+BOOLEAN HTdocument_settings_changed(void)
+{
+    /*
+     * Annoying Hack(TM):  If we don't have a source cache, we can't
+     * reparse anyway, so pretend the settings haven't changed.
+     */
+    if (!HTMainText || !HTcan_reparse_document())
+	return FALSE;
+
+    if (TRACE) {
+	/*
+	 * If we're tracing, note everying that has changed.
+	 */
+	trace_setting_change("CLICKABLE_IMAGES",
+			     HTMainText->clickable_images, clickable_images);
+	trace_setting_change("PSEUDO_INLINE_ALTS",
+			     HTMainText->pseudo_inline_alts,
+			     pseudo_inline_alts);
+	trace_setting_change("VERBOSE_IMG",
+			     HTMainText->verbose_img,
+			     verbose_img);
+	trace_setting_change("RAW_MODE", HTMainText->raw_mode,
+			     LYUseDefaultRawMode);
+	trace_setting_change("HISTORICAL_COMMENTS",
+			     HTMainText->historical_comments,
+			     historical_comments);
+	trace_setting_change("MINIMAL_COMMENTS",
+			     HTMainText->minimal_comments, minimal_comments);
+	trace_setting_change("SOFT_DQUOTES",
+			     HTMainText->soft_dquotes, soft_dquotes);
+	trace_setting_change("OLD_DTD", HTMainText->old_dtd, Old_DTD);
+	trace_setting_change("KEYPAD_MODE",
+			     HTMainText->keypad_mode, keypad_mode);
+	if (HTMainText->disp_lines != LYlines || HTMainText->disp_cols != DISPLAY_COLS)
+	    CTRACE((tfp,
+		    "HTdocument_settings_changed: Screen size has changed (was %dx%d, now %dx%d)\n",
+		    HTMainText->disp_cols,
+		    HTMainText->disp_lines,
+		    DISPLAY_COLS,
+		    LYlines));
+    }
+
+    return (BOOLEAN) (HTMainText->clickable_images != clickable_images ||
+		      HTMainText->pseudo_inline_alts != pseudo_inline_alts ||
+		      HTMainText->verbose_img != verbose_img ||
+		      HTMainText->raw_mode != LYUseDefaultRawMode ||
+		      HTMainText->historical_comments != historical_comments ||
+		      (HTMainText->minimal_comments != minimal_comments &&
+		       !historical_comments) ||
+		      HTMainText->soft_dquotes != soft_dquotes ||
+		      HTMainText->old_dtd != Old_DTD ||
+		      HTMainText->keypad_mode != keypad_mode ||
+		      HTMainText->disp_cols != DISPLAY_COLS);
+}
+#endif
+
+int HTisDocumentSource(void)
+{
+    return (HTMainText != 0) ? HTMainText->source : FALSE;
+}
+
+const char *HTLoadedDocumentURL(void)
+{
+    if (!HTMainText)
+	return ("");
+
+    if (HTMainText->node_anchor && HTMainText->node_anchor->address)
+	return (HTMainText->node_anchor->address);
+    else
+	return ("");
+}
+
+bstring *HTLoadedDocumentPost_data(void)
+{
+    if (HTMainText
+	&& HTMainText->node_anchor
+	&& HTMainText->node_anchor->post_data)
+	return (HTMainText->node_anchor->post_data);
+    else
+	return (0);
+}
+
+const char *HTLoadedDocumentTitle(void)
+{
+    if (!HTMainText)
+	return ("");
+
+    if (HTMainText->node_anchor && HTMainText->node_anchor->title)
+	return (HTMainText->node_anchor->title);
+    else
+	return ("");
+}
+
+BOOLEAN HTLoadedDocumentIsHEAD(void)
+{
+    if (!HTMainText)
+	return (FALSE);
+
+    if (HTMainText->node_anchor && HTMainText->node_anchor->isHEAD)
+	return (HTMainText->node_anchor->isHEAD);
+    else
+	return (FALSE);
+}
+
+BOOLEAN HTLoadedDocumentIsSafe(void)
+{
+    if (!HTMainText)
+	return (FALSE);
+
+    if (HTMainText->node_anchor && HTMainText->node_anchor->safe)
+	return (HTMainText->node_anchor->safe);
+    else
+	return (FALSE);
+}
+
+const char *HTLoadedDocumentCharset(void)
+{
+    if (!HTMainText)
+	return (NULL);
+
+    if (HTMainText->node_anchor && HTMainText->node_anchor->charset)
+	return (HTMainText->node_anchor->charset);
+    else
+	return (NULL);
+}
+
+BOOL HTLoadedDocumentEightbit(void)
+{
+    if (!HTMainText)
+	return (NO);
+    else
+	return (HTMainText->have_8bit_chars);
+}
+
+void HText_setNodeAnchorBookmark(const char *bookmark)
+{
+    if (!HTMainText)
+	return;
+
+    if (HTMainText->node_anchor)
+	HTAnchor_setBookmark(HTMainText->node_anchor, bookmark);
+}
+
+const char *HTLoadedDocumentBookmark(void)
+{
+    if (!HTMainText)
+	return (NULL);
+
+    if (HTMainText->node_anchor && HTMainText->node_anchor->bookmark)
+	return (HTMainText->node_anchor->bookmark);
+    else
+	return (NULL);
+}
+
+int HText_LastLineSize(HText *text, BOOL IgnoreSpaces)
+{
+    if (!text || !text->last_line || !text->last_line->size)
+	return 0;
+    return HText_TrueLineSize(text->last_line, text, IgnoreSpaces);
+}
+
+BOOL HText_LastLineEmpty(HText *text, BOOL IgnoreSpaces)
+{
+    if (!text || !text->last_line || !text->last_line->size)
+	return TRUE;
+    return HText_TrueEmptyLine(text->last_line, text, IgnoreSpaces);
+}
+
+int HText_LastLineOffset(HText *text)
+{
+    if (!text || !text->last_line)
+	return 0;
+    return text->last_line->offset;
+}
+
+int HText_PreviousLineSize(HText *text, BOOL IgnoreSpaces)
+{
+    HTLine *line;
+
+    if (!text || !text->last_line)
+	return 0;
+    if (!(line = text->last_line->prev))
+	return 0;
+    return HText_TrueLineSize(line, text, IgnoreSpaces);
+}
+
+BOOL HText_PreviousLineEmpty(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.
+ */
+static int HText_TrueLineSize(HTLine *line, HText *text, BOOL IgnoreSpaces)
+{
+    size_t i;
+    int true_size = 0;
+
+    if (!(line && line->size))
+	return 0;
+
+    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) {
+		true_size++;
+	    }
+	}
+    } else {
+	for (i = 0; i < line->size; i++) {
+	    if (!IsSpecialAttrChar(line->data[i]) &&
+		IS_UTF8_EXTRA(line->data[i])) {
+		true_size++;
+	    }
+	}
+    }
+    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.
+ */
+static BOOL HText_TrueEmptyLine(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;
+}
+
+void HText_NegateLineOne(HText *text)
+{
+    if (text) {
+	text->in_line_1 = NO;
+    }
+    return;
+}
+
+BOOL HText_inLineOne(HText *text)
+{
+    if (text) {
+	return text->in_line_1;
+    }
+    return YES;
+}
+
+/*
+ * This function is for removing the first of two
+ * successive blank lines.  It should be called after
+ * checking the situation with HText_LastLineSize()
+ * and HText_PreviousLineSize().  Any characters in
+ * the removed line (i.e., control characters, or it
+ * wouldn't have tested blank) should have been
+ * reiterated by split_line() in the retained blank
+ * line.  -FM
+ */
+void HText_RemovePreviousLine(HText *text)
+{
+    HTLine *line, *previous;
+
+    if (!(text && text->Lines > 1))
+	return;
+
+    line = text->last_line->prev;
+    previous = line->prev;
+    previous->next = text->last_line;
+    text->last_line->prev = previous;
+    text->Lines--;
+    freeHTLine(text, line);
+}
+
+/*
+ * 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
+ */
+int HText_getCurrentColumn(HText *text)
+{
+    int column = 0;
+    BOOL IgnoreSpaces = FALSE;
+
+    if (text) {
+	column = (text->in_line_1 ?
+		  (int) text->style->indent1st : (int) text->style->leftIndent)
+	    + HText_LastLineSize(text, IgnoreSpaces)
+	    + (int) text->last_line->offset;
+    }
+    return column;
+}
+
+int HText_getMaximumColumn(HText *text)
+{
+    int column = DISPLAY_COLS;
+
+    if (text) {
+	column -= (int) text->style->rightIndent;
+    }
+    return column;
+}
+
+/*
+ * NOTE:  This function uses HText_getCurrentColumn() which
+ *	  presently is correct only if the alignment is
+ *	  HT_LEFT. -FM
+ */
+void HText_setTabID(HText *text, const char *name)
+{
+    HTTabID *Tab = NULL;
+    HTList *cur = text->tabs;
+    HTList *last = NULL;
+
+    if (!text || isEmpty(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;
+	}
+	if (last)
+	    cur = last;
+    }
+    if (!Tab) {			/* New name.  Create a new node */
+	Tab = typecalloc(HTTabID);
+	if (Tab == NULL)
+	    outofmem(__FILE__, "HText_setTabID");
+	HTList_addObject(cur, Tab);
+	StrAllocCopy(Tab->name, name);
+    }
+
+    assert(Tab != NULL);
+    Tab->column = HText_getCurrentColumn(text);
+    return;
+}
+
+int HText_getTabIDColumn(HText *text, const char *name)
+{
+    int column = 0;
+    HTTabID *Tab;
+    HTList *cur = text->tabs;
+
+    if (text && non_empty(name) && cur) {
+	while (NULL != (Tab = (HTTabID *) HTList_nextObject(cur))) {
+	    if (Tab->name && !strcmp(Tab->name, name))
+		break;
+	}
+	if (Tab)
+	    column = Tab->column;
+    }
+    return column;
+}
+
+/*
+ * This function is for saving the address of a link
+ * which had an attribute in the markup that resolved
+ * to a URL (i.e., not just a NAME or ID attribute),
+ * but was found in HText_endAnchor() to have no visible
+ * content for use as a link name.  It loads the address
+ * into text->hidden_links, whose count can be determined
+ * via HText_HiddenLinks(), below.  The addresses can be
+ * retrieved via HText_HiddenLinkAt(), below, based on
+ * count.  -FM
+ */
+static void HText_AddHiddenLink(HText *text, TextAnchor *textanchor)
+{
+    HTAnchor *dest;
+
+    /*
+     * Make sure we have an HText structure and anchor.  -FM
+     */
+    if (!(text && textanchor && textanchor->anchor))
+	return;
+
+    /*
+     * Create the hidden links list
+     * if it hasn't been already.  -FM
+     */
+    if (text->hidden_links == NULL)
+	text->hidden_links = HTList_new();
+
+    /*
+     * Store the address, in reverse list order
+     * so that first in will be first out on
+     * retrievals.  -FM
+     */
+    if ((dest = HTAnchor_followLink(textanchor->anchor)) &&
+	(text->hiddenlinkflag != HIDDENLINKS_IGNORE ||
+	 HTList_isEmpty(text->hidden_links))) {
+	HTList_appendObject(text->hidden_links, HTAnchor_address(dest));
+    }
+
+    return;
+}
+
+/*
+ * This function returns the number of addresses
+ * that are loaded in text->hidden_links.  -FM
+ */
+int HText_HiddenLinkCount(HText *text)
+{
+    int count = 0;
+
+    if (text && text->hidden_links)
+	count = HTList_count((HTList *) text->hidden_links);
+
+    return (count);
+}
+
+/*
+ * This function returns the address, corresponding to
+ * a hidden link, at the position (zero-based) in the
+ * text->hidden_links list of the number argument.  -FM
+ */
+const char *HText_HiddenLinkAt(HText *text, int number)
+{
+    char *href = NULL;
+
+    if (text && text->hidden_links && number >= 0)
+	href = (char *) HTList_objectAt((HTList *) text->hidden_links, number);
+
+    return (href);
+}
+
+/*
+ * Form methods
+ * These routines are used to build forms consisting
+ * of input fields
+ */
+static int HTFormMethod;
+static char *HTFormAction = NULL;
+static char *HTFormEnctype = NULL;
+static char *HTFormTitle = NULL;
+static char *HTFormAcceptCharset = NULL;
+static BOOLEAN HTFormDisabled = FALSE;
+static PerFormInfo *HTCurrentForm;
+
+void HText_beginForm(char *action,
+		     char *method,
+		     char *enctype,
+		     char *title,
+		     const char *accept_cs)
+{
+    PerFormInfo *newform;
+
+    HTFormMethod = URL_GET_METHOD;
+    HTFormNumber++;
+    HTFormFields = 0;
+    HTFormDisabled = FALSE;
+
+    /*
+     * Check the ACTION.  -FM
+     */
+    if (action != NULL) {
+	if (isMAILTO_URL(action)) {
+	    HTFormMethod = URL_MAIL_METHOD;
+	}
+	StrAllocCopy(HTFormAction, action);
+    } else
+	StrAllocCopy(HTFormAction, HTLoadedDocumentURL());
+
+    /*
+     * Check the METHOD.  -FM
+     */
+    if (method != NULL && HTFormMethod != URL_MAIL_METHOD)
+	if (!strcasecomp(method, "post") || !strcasecomp(method, "pget"))
+	    HTFormMethod = URL_POST_METHOD;
+
+    /*
+     * Check the ENCTYPE.  -FM
+     */
+    if (non_empty(enctype)) {
+	StrAllocCopy(HTFormEnctype, enctype);
+	if (HTFormMethod != URL_MAIL_METHOD &&
+	    !strncasecomp(enctype, "multipart/form-data", 19))
+	    HTFormMethod = URL_POST_METHOD;
+    } else {
+	FREE(HTFormEnctype);
+    }
+
+    /*
+     * Check the TITLE.  -FM
+     */
+    if (non_empty(title))
+	StrAllocCopy(HTFormTitle, title);
+    else
+	FREE(HTFormTitle);
+
+    /*
+     * Check for an ACCEPT_CHARSET.  If present, store it and
+     * convert to lowercase and collapse spaces.  - kw
+     */
+    if (accept_cs != NULL) {
+	StrAllocCopy(HTFormAcceptCharset, accept_cs);
+	LYRemoveBlanks(HTFormAcceptCharset);
+	LYLowerCase(HTFormAcceptCharset);
+    }
+
+    /*
+     * Create a new "PerFormInfo" structure to hold info on the current
+     * form.  The HTForm* variables could all migrate there, currently
+     * this isn't done (yet?) but it might be less confusing.
+     * Currently the only data saved in this structure that will actually
+     * be used is the accept_cs string.
+     * This will be appended to the forms list kept by the HText object
+     * if and when we reach a HText_endForm.  - kw
+     */
+    newform = typecalloc(PerFormInfo);
+    if (newform == NULL)
+	outofmem(__FILE__, "HText_beginForm");
+
+    assert(newform != NULL);
+    newform->number = HTFormNumber;
+
+    PerFormInfo_free(HTCurrentForm);	/* shouldn't happen here - kw */
+    HTCurrentForm = newform;
+
+    CTRACE((tfp, "BeginForm: action:%s Method:%d%s%s%s%s%s%s\n",
+	    HTFormAction, HTFormMethod,
+	    (HTFormTitle ? " Title:" : ""),
+	    NonNull(HTFormTitle),
+	    (HTFormEnctype ? " Enctype:" : ""),
+	    NonNull(HTFormEnctype),
+	    (HTFormAcceptCharset ? " Accept-charset:" : ""),
+	    NonNull(HTFormAcceptCharset)));
+}
+
+void HText_endForm(HText *text)
+{
+    if (text != NULL) {
+	if (HTFormFields == 1 && text->first_anchor) {
+	    /*
+	     * Support submission of a single text input field in
+	     * the form via <return> instead of a submit button.  -FM
+	     */
+	    TextAnchor *a;
+
+	    /*
+	     * Go through list of anchors and get our input field.  -FM
+	     */
+	    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) {
+		    /*
+		     * 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;
+		    if (HTFormDisabled)
+			a->input_field->disabled = TRUE;
+		    break;
+		}
+	    }
+	}
+
+	/*
+	 * Append info on the current form to the HText object's list of
+	 * forms.
+	 * HText_beginInput call will have set some of the data in the
+	 * PerFormInfo structure (if there were any form fields at all),
+	 * we also fill in the ACCEPT-CHARSET data now (this could have
+	 * been done earlier).  - kw
+	 */
+	if (HTCurrentForm) {
+	    if (HTFormDisabled)
+		HTCurrentForm->disabled = TRUE;
+	    HTCurrentForm->accept_cs = HTFormAcceptCharset;
+	    HTFormAcceptCharset = NULL;
+	    if (!text->forms)
+		text->forms = HTList_new();
+	    HTList_appendObject(text->forms, HTCurrentForm);
+	    HTCurrentForm = NULL;
+	} else {
+	    CTRACE((tfp, "endForm:    HTCurrentForm is missing!\n"));
+	}
+    } else {
+	CTRACE((tfp, "endForm:    HText is missing!\n"));
+    }
+
+    FREE(HTCurSelectGroup);
+    FREE(HTCurSelectGroupSize);
+    FREE(HTCurSelectedOptionValue);
+    FREE(HTFormAction);
+    FREE(HTFormEnctype);
+    FREE(HTFormTitle);
+    FREE(HTFormAcceptCharset);
+    HTFormFields = 0;
+    HTFormDisabled = FALSE;
+}
+
+void HText_beginSelect(char *name,
+		       int name_cs,
+		       BOOLEAN multiple,
+		       char *size)
+{
+    /*
+     * Save the group name.
+     */
+    StrAllocCopy(HTCurSelectGroup, name);
+    HTCurSelectGroupCharset = name_cs;
+
+    /*
+     * 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);
+
+    CTRACE((tfp, "HText_beginSelect: name=%s type=%d size=%s\n",
+	    ((HTCurSelectGroup == NULL) ?
+	     "<NULL>" : HTCurSelectGroup),
+	    HTCurSelectGroupType,
+	    ((HTCurSelectGroupSize == NULL) ?
+	     "<NULL>" : HTCurSelectGroupSize)));
+    CTRACE((tfp, "HText_beginSelect: name_cs=%d \"%s\"\n",
+	    HTCurSelectGroupCharset,
+	    (HTCurSelectGroupCharset >= 0 ?
+	     LYCharSet_UC[HTCurSelectGroupCharset].MIMEname : "<UNKNOWN>")));
+}
+
+/*
+ *  This function returns the number of the option whose
+ *  value currently is being accumulated for a select
+ *  block. - LE && FM
+ */
+int HText_getOptionNum(HText *text)
+{
+    TextAnchor *a;
+    OptionType *op;
+    int n = 1;			/* start count at 1 */
+
+    if (!(text && text->last_anchor))
+	return (0);
+
+    a = text->last_anchor;
+    if (!(a->link_type == INPUT_ANCHOR && a->input_field &&
+	  a->input_field->type == F_OPTION_LIST_TYPE))
+	return (0);
+
+    for (op = a->input_field->select_list; op; op = op->next)
+	n++;
+    CTRACE((tfp, "HText_getOptionNum: Got number '%d'.\n", n));
+    return (n);
+}
+
+/*
+ *  This function checks for a numbered option pattern
+ *  as the prefix for an option value.  If present, and
+ *  we are in the correct keypad mode, it returns a
+ *  pointer to the actual value, following that prefix.
+ *  Otherwise, it returns the original pointer.
+ */
+static char *HText_skipOptionNumPrefix(char *opname)
+{
+    /*
+     * Check if we are in the correct keypad mode.
+     */
+    if (fields_are_numbered()) {
+	/*
+	 * Skip the option number embedded in the option name so the
+	 * extra chars won't mess up cgi scripts processing the value.
+	 * The format is (nnn)__ where nnn is a number and there is a
+	 * minimum of 5 chars (no underscores if (nnn) exceeds 5 chars).
+	 * See HTML.c.  If the chars don't exactly match this format,
+	 * just use all of opname.  - LE
+	 */
+	char *cp = opname;
+
+	if ((non_empty(cp) && *cp++ == '(') &&
+	    *cp && isdigit(UCH(*cp++))) {
+	    while (*cp && isdigit(UCH(*cp)))
+		++cp;
+	    if (*cp && *cp++ == ')') {
+		int i = (cp - opname);
+
+		while (i < 5) {
+		    if (*cp != '_')
+			break;
+		    i++;
+		    cp++;
+		}
+		if (i < 5) {
+		    cp = opname;
+		}
+	    } else {
+		cp = opname;
+	    }
+	} else {
+	    cp = opname;
+	}
+	return (cp);
+    }
+
+    return (opname);
+}
+
+/*
+ *  We couldn'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.
+ */
+char *HText_setLastOptionValue(HText *text, char *value,
+			       char *submit_value,
+			       int order,
+			       BOOLEAN checked,
+			       int val_cs,
+			       int submit_val_cs)
+{
+    char *cp, *cp1;
+    char *ret_Value = NULL;
+    unsigned char *tmp = NULL;
+    int number = 0, i, j;
+
+    if (!(value
+	  && text
+	  && text->last_anchor
+	  && text->last_anchor->input_field
+	  && text->last_anchor->link_type == INPUT_ANCHOR)) {
+	CTRACE((tfp, "HText_setLastOptionValue: invalid call!  value:%s!\n",
+		(value ? value : "<NULL>")));
+	return NULL;
+    }
+
+    CTRACE((tfp,
+	    "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(UCH(*cp)) ||
+				 IsSpecialAttrChar(UCH(*cp))))
+	    cp--;
+	*(cp + 1) = '\0';
+    }
+
+    /*
+     * Find first non space
+     */
+    cp = value;
+    while (isspace(UCH(*cp)) ||
+	   IsSpecialAttrChar(UCH(*cp)))
+	cp++;
+    if (HTCurSelectGroupType == F_RADIO_TYPE &&
+	LYSelectPopups &&
+	fields_are_numbered()) {
+	/*
+	 * Collapse any space between the popup option
+	 * prefix and actual value.  -FM
+	 */
+	if ((cp1 = HText_skipOptionNumPrefix(cp)) > cp) {
+	    i = 0, j = (cp1 - cp);
+	    while (isspace(UCH(cp1[i])) ||
+		   IsSpecialAttrChar(UCH(cp1[i]))) {
+		i++;
+	    }
+	    if (i > 0) {
+		while (cp1[i] != '\0')
+		    cp[j++] = cp1[i++];
+		cp[j] = '\0';
+	    }
+	}
+    }
+
+    if (HTCurSelectGroupType == F_CHECKBOX_TYPE) {
+	StrAllocCopy(text->last_anchor->input_field->value, cp);
+	text->last_anchor->input_field->value_cs = val_cs;
+	/*
+	 * Put the text on the screen as well.
+	 */
+	HText_appendText(text, cp);
+
+    } else if (LYSelectPopups == FALSE) {
+	StrAllocCopy(text->last_anchor->input_field->value,
+		     (submit_value ? submit_value : cp));
+	text->last_anchor->input_field->value_cs = (submit_value ?
+						    submit_val_cs : val_cs);
+	/*
+	 * 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.
+	 */
+	LYReduceBlanks(value);
+
+	if (!op_ptr) {
+	    /*
+	     * No option items yet.
+	     */
+	    if (text->last_anchor->input_field->type != F_OPTION_LIST_TYPE) {
+		CTRACE((tfp,
+			"HText_setLastOptionValue: last input_field not F_OPTION_LIST_TYPE (%d)\n",
+			F_OPTION_LIST_TYPE));
+		CTRACE((tfp, "                          but %d, ignoring!\n",
+			text->last_anchor->input_field->type));
+		return NULL;
+	    }
+
+	    new_ptr = text->last_anchor->input_field->select_list =
+		typecalloc(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 = typecalloc(OptionType);
+	    if (new_ptr == NULL)
+		outofmem(__FILE__, "HText_setLastOptionValue");
+	}
+
+	assert(new_ptr != NULL);
+	new_ptr->name = NULL;
+	new_ptr->cp_submit_value = NULL;
+	new_ptr->next = NULL;
+	/*
+	 * Find first non-space again, convert_to_spaces above may have
+	 * changed the string.  - kw
+	 */
+	cp = value;
+	while (isspace(UCH(*cp)) ||
+	       IsSpecialAttrChar(UCH(*cp)))
+	    cp++;
+	for (i = 0, j = 0; cp[i]; i++) {
+	    if (cp[i] == HT_NON_BREAK_SPACE ||
+		cp[i] == HT_EN_SPACE) {
+		cp[j++] = ' ';
+	    } else if (cp[i] != LY_SOFT_HYPHEN &&
+		       !IsSpecialAttrChar(UCH(cp[i]))) {
+		cp[j++] = cp[i];
+	    }
+	}
+	cp[j] = '\0';
+	if (IS_CJK_TTY) {
+	    if (cp &&
+		(tmp = typecallocn(unsigned char, strlen(cp) * 2 + 1)) != 0) {
+		if (tmp == NULL)
+		    outofmem(__FILE__, "HText_setLastOptionValue");
+		if (kanji_code == EUC) {
+		    TO_EUC((unsigned char *) cp, tmp);
+		    val_cs = current_char_set;
+		} else if (kanji_code == SJIS) {
+		    TO_SJIS((unsigned char *) cp, tmp);
+		    val_cs = current_char_set;
+		} else {
+		    for (i = 0, j = 0; cp[i]; i++) {
+			if (cp[i] != CH_ESC) {	/* S/390 -- gil -- 1604 */
+			    tmp[j++] = UCH(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 :
+		      HText_skipOptionNumPrefix(new_ptr->name)));
+	new_ptr->value_cs = (submit_value ? submit_val_cs : val_cs);
+
+	if (first_option) {
+	    FormInfo *last_input = text->last_anchor->input_field;
+
+	    StrAllocCopy(HTCurSelectedOptionValue, new_ptr->name);
+	    last_input->num_value = 0;
+	    /*
+	     * If this is the first option in a popup select list,
+	     * HText_beginInput may have allocated the value and
+	     * cp_submit_value fields, so free them now to avoid
+	     * a memory leak.  - kw
+	     */
+	    FREE(last_input->value);
+	    FREE(last_input->cp_submit_value);
+
+	    last_input->value = last_input->select_list->name;
+	    last_input->orig_value = last_input->select_list->name;
+	    last_input->cp_submit_value = last_input->select_list->cp_submit_value;
+	    last_input->orig_submit_value = last_input->select_list->cp_submit_value;
+	    last_input->value_cs = new_ptr->value_cs;
+	} else {
+	    int newlen = (int) strlen(new_ptr->name);
+	    int curlen = (int) (HTCurSelectedOptionValue
+				? strlen(HTCurSelectedOptionValue)
+				: 0);
+
+	    /*
+	     * 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 = (HTCurSelectedOptionValue
+			  ? strlen(HTCurSelectedOptionValue)
+			  : 0);
+	    FormInfo *last_input = text->last_anchor->input_field;
+
+	    /*
+	     * Set the default option as this one.
+	     */
+	    last_input->num_value = number;
+	    last_input->value = new_ptr->name;
+	    last_input->orig_value = new_ptr->name;
+	    last_input->cp_submit_value = new_ptr->cp_submit_value;
+	    last_input->orig_submit_value = new_ptr->cp_submit_value;
+	    last_input->value_cs = new_ptr->value_cs;
+	    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.
+	     */
+	    if (HTCurSelectedOptionValue == 0)
+		StrAllocCopy(HTCurSelectedOptionValue, "");
+	    text->last_anchor->input_field->size =
+		strlen(HTCurSelectedOptionValue);
+	    ret_Value = HTCurSelectedOptionValue;
+	}
+    }
+
+    if (TRACE) {
+	CTRACE((tfp, "HText_setLastOptionValue:%s value=\"%s\"\n",
+		(order == LAST_ORDER) ? " LAST_ORDER" : "",
+		value));
+	CTRACE((tfp, "            val_cs=%d \"%s\"",
+		val_cs,
+		(val_cs >= 0 ?
+		 LYCharSet_UC[val_cs].MIMEname : "<UNKNOWN>")));
+	if (submit_value) {
+	    CTRACE((tfp, " (submit_val_cs %d \"%s\") submit_value%s=\"%s\"\n",
+		    submit_val_cs,
+		    (submit_val_cs >= 0 ?
+		     LYCharSet_UC[submit_val_cs].MIMEname : "<UNKNOWN>"),
+		    (HTCurSelectGroupType == F_CHECKBOX_TYPE) ?
+		    "(ignored)" : "",
+		    submit_value));
+	} else {
+	    CTRACE((tfp, "\n"));
+	}
+    }
+    return (ret_Value);
+}
+
+/*
+ * Assign a form input anchor.
+ * Returns the number of characters to leave
+ * blank so that the input field can fit.
+ */
+int HText_beginInput(HText *text, BOOL underline,
+		     InputFieldData * I)
+{
+    TextAnchor *a;
+    FormInfo *f;
+    const char *cp_option = NULL;
+    char *IValue = NULL;
+    unsigned char *tmp = NULL;
+    int i, j;
+    int adjust_marker = 0;
+    int MaximumSize;
+    char marker[16];
+
+    CTRACE((tfp, "GridText: Entering HText_beginInput type=%s\n", NonNull(I->type)));
+
+    POOLtypecalloc(TextAnchor, a);
+
+    POOLtypecalloc(FormInfo, f);
+    if (a == NULL || f == NULL)
+	outofmem(__FILE__, "HText_beginInput");
+
+    assert(a != NULL);
+    assert(f != NULL);
+
+    a->sgml_offset = SGML_offset();
+    a->inUnderline = underline;
+    a->line_num = text->Lines;
+    a->line_pos = text->last_line->size;
+
+    /*
+     * If this is a radio button, or an OPTION we're converting
+     * to 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->type != NULL && !strcmp(I->type, "OPTION") &&
+	HTCurSelectGroupType == F_RADIO_TYPE && LYSelectPopups == FALSE) {
+	I->type = "RADIO";
+	I->name = HTCurSelectGroup;
+	I->name_cs = HTCurSelectGroupCharset;
+    }
+    if (I->name && I->type && !strcasecomp(I->type, "radio")) {
+	if (!text->last_anchor) {
+	    I->checked = TRUE;
+	} else {
+	    TextAnchor *b;
+	    int i2 = 0;
+
+	    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) {
+		    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;
+			}
+			i2++;
+		    }
+		}
+	    }
+	    if (i2 == 0)
+		I->checked = TRUE;
+	}
+    }
+
+    a->next = 0;
+    a->anchor = NULL;
+    a->link_type = INPUT_ANCHOR;
+    a->show_anchor = YES;
+
+    LYClearHiText(a);
+    a->extent = 2;
+
+    a->input_field = f;
+
+    f->select_list = 0;
+    f->number = HTFormNumber;
+    f->disabled = HTFormDisabled || 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;
+
+    /*
+     * Set up VALUE.
+     */
+    if (I->value)
+	StrAllocCopy(IValue, I->value);
+    if (IValue &&
+	IS_CJK_TTY &&
+	((I->type == NULL) || strcasecomp(I->type, "hidden"))) {
+	if ((tmp = typecallocn(unsigned char, strlen(IValue) * 2 + 1)) != 0) {
+	    if (kanji_code == EUC) {
+		TO_EUC((unsigned char *) IValue, tmp);
+		I->value_cs = current_char_set;
+	    } else if (kanji_code == SJIS) {
+		TO_SJIS((unsigned char *) IValue, tmp);
+		I->value_cs = current_char_set;
+	    } else {
+		for (i = 0, j = 0; IValue[i]; i++) {
+		    if (IValue[i] != CH_ESC) {	/* S/390 -- gil -- 1621 */
+			tmp[j++] = UCH(IValue[i]);
+		    }
+		}
+	    }
+	    StrAllocCopy(IValue, (const char *) tmp);
+	    FREE(tmp);
+	}
+    }
+
+    /*
+     * Special case of OPTION.
+     * Is handled above if radio type and LYSelectPopups is FALSE.
+     */
+    /* 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;
+	I->name_cs = HTCurSelectGroupCharset;
+
+	/*
+	 * 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);
+	}
+    }
+
+    /*
+     * Set SIZE.
+     */
+    if (I->size != 0) {
+	f->size = 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 relevant 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")) {
+	    f->type = F_IMAGE_SUBMIT_TYPE;
+	} 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;
+	    CTRACE((tfp, "ok, got a file uploader\n"));
+	} else if (!strcasecomp(I->type, "keygen")) {
+	    f->type = F_KEYGEN_TYPE;
+	} else if (!strcasecomp(I->type, "button")) {
+	    f->type = F_BUTTON_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);
+	f->name_cs = I->name_cs;
+    } else {
+	if (f->type == F_RESET_TYPE ||
+	    f->type == F_SUBMIT_TYPE ||
+	    f->type == F_IMAGE_SUBMIT_TYPE) {
+	    /*
+	     * Set name to empty string.
+	     */
+	    StrAllocCopy(f->name, "");
+	} else {
+	    /*
+	     * Error!  NAME must be present.
+	     */
+	    CTRACE((tfp,
+		    "GridText: No name present in input field; not displaying\n"));
+	    FREE(IValue);
+	    return (0);
+	}
+    }
+
+    /*
+     * Add this anchor to the anchor list
+     */
+    if (text->last_anchor) {
+	text->last_anchor->next = a;
+    } else {
+	text->first_anchor = a;
+    }
+
+    /*
+     * Set VALUE, if it exists.  Otherwise, if it's not
+     * an option list make it a zero-length string.  -FM
+     */
+    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) {
+	    /*
+	     * Fill both with the value.  The f->value may be
+	     * overwritten in HText_setLastOptionValue....
+	     */
+	    StrAllocCopy(f->value, IValue);
+	    StrAllocCopy(f->cp_submit_value, IValue);
+	} else {
+	    StrAllocCopy(f->value, IValue);
+	}
+	f->value_cs = I->value_cs;
+    } else if (f->type != F_OPTION_LIST_TYPE) {
+	StrAllocCopy(f->value, "");
+	/*
+	 * May be an empty INPUT field.  The text entered will then
+	 * probably be in the current display character set.  - kw
+	 */
+	f->value_cs = current_char_set;
+    }
+
+    /*
+     * Run checks and fill in necessary values.
+     */
+    if (f->type == F_RESET_TYPE) {
+	if (non_empty(f->value)) {
+	    f->size = strlen(f->value);
+	} else {
+	    StrAllocCopy(f->value, "Reset");
+	    f->size = 5;
+	}
+    } else if (f->type == F_BUTTON_TYPE) {
+	if (non_empty(f->value)) {
+	    f->size = strlen(f->value);
+	} else {
+	    StrAllocCopy(f->value, "BUTTON");
+	    f->size = 5;
+	}
+    } else if (f->type == F_IMAGE_SUBMIT_TYPE ||
+	       f->type == F_SUBMIT_TYPE) {
+	if (non_empty(f->value)) {
+	    f->size = strlen(f->value);
+	} else if (f->type == F_IMAGE_SUBMIT_TYPE) {
+	    StrAllocCopy(f->value, "[IMAGE]-Submit");
+	    f->size = 14;
+	} 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, (f->type == F_CHECKBOX_TYPE ? "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);
+    }
+
+    /*
+     * Store accept-charset if present, converting to lowercase
+     * and collapsing spaces.  - kw
+     */
+    if (I->accept_cs) {
+	StrAllocCopy(f->accept_cs, I->accept_cs);
+	LYRemoveBlanks(f->accept_cs);
+	LYLowerCase(f->accept_cs);
+    }
+
+    /*
+     * Add numbers to form fields if needed.  - LE & FM
+     */
+    switch (f->type) {
+	/*
+	 * Do not supply number for hidden fields, nor
+	 * for types that are not yet implemented.
+	 */
+    case F_HIDDEN_TYPE:
+#ifndef USE_FILE_UPLOAD
+    case F_FILE_TYPE:
+#endif
+    case F_RANGE_TYPE:
+    case F_KEYGEN_TYPE:
+    case F_BUTTON_TYPE:
+	a->number = 0;
+	break;
+
+    default:
+	if (fields_are_numbered())
+	    a->number = ++(text->last_anchor_number);
+	else
+	    a->number = 0;
+	break;
+    }
+    if (fields_are_numbered() && (a->number > 0)) {
+	sprintf(marker, "[%d]", a->number);
+	adjust_marker = strlen(marker);
+	if (number_fields_on_left) {
+	    BOOL had_bracket = (BOOL) (f->type == F_OPTION_LIST_TYPE);
+
+	    HText_appendText(text, had_bracket ? (marker + 1) : marker);
+	    if (had_bracket)
+		HText_appendCharacter(text, '[');
+	}
+	a->line_num = text->Lines;
+	a->line_pos = text->last_line->size;
+    } else {
+	*marker = '\0';
+    }
+
+    /*
+     * Restrict SIZE to maximum allowable size.
+     */
+    MaximumSize = WRAP_COLS(text) + 1 - adjust_marker;
+    switch (f->type) {
+
+    case F_SUBMIT_TYPE:
+    case F_IMAGE_SUBMIT_TYPE:
+    case F_RESET_TYPE:
+    case F_TEXT_TYPE:
+    case F_TEXTAREA_TYPE:
+	/*
+	 * For submit and reset buttons, and for text entry
+	 * fields and areas, we limit the size element to that
+	 * of one line for the current style because that's
+	 * the most we could highlight on overwrites, and/or
+	 * handle in the line editor.  The actual values for
+	 * text entry lines can be long, and will be scrolled
+	 * horizontally within the editing window.  -FM
+	 */
+	MaximumSize -= (1 +
+			(int) text->style->leftIndent +
+			(int) text->style->rightIndent);
+
+	/*  If we are numbering form links, place is taken by [nn]  */
+	if (fields_are_numbered()) {
+	    if (!number_fields_on_left
+		&& f->type == F_TEXT_TYPE
+		&& MaximumSize > a->line_pos + 10)
+		MaximumSize -= a->line_pos;
+	    else
+		MaximumSize -= strlen(marker);
+	}
+
+	/*
+	 * Save value for submit/reset buttons so they
+	 * will be visible when printing the page.  - LE
+	 */
+	I->value = f->value;
+	break;
+
+    default:
+	/*
+	 * For all other fields we limit the size element to
+	 * 10 less than the screen width, because either they
+	 * are types with small placeholders, and/or are a
+	 * type which is handled via a popup window.  -FM
+	 */
+	MaximumSize -= 10;
+	break;
+    }
+
+    if (MaximumSize < 1)
+	MaximumSize = 1;
+
+    if (f->size > MaximumSize)
+	f->size = MaximumSize;
+
+    /*
+     * Add this anchor to the anchor list
+     */
+    text->last_anchor = a;
+
+    if (HTCurrentForm) {	/* should always apply! - kw */
+	if (!HTCurrentForm->first_field) {
+	    HTCurrentForm->first_field = f;
+	}
+	HTCurrentForm->last_field = f;
+	HTCurrentForm->nfields++;	/* will count hidden fields - kw */
+	/*
+	 * Propagate form field's accept-charset attribute to enclosing
+	 * form if the form itself didn't have an accept-charset - kw
+	 */
+	if (f->accept_cs && !HTFormAcceptCharset) {
+	    StrAllocCopy(HTFormAcceptCharset, f->accept_cs);
+	}
+	if (!text->forms) {
+	    text->forms = HTList_new();
+	}
+    } else {
+	CTRACE((tfp, "beginInput: HTCurrentForm is missing!\n"));
+    }
+
+    CTRACE((tfp, "Input link: name=%s\nvalue=%s\nsize=%d\n",
+	    f->name,
+	    NonNull(f->value),
+	    f->size));
+    CTRACE((tfp, "Input link: name_cs=%d \"%s\" (from %d \"%s\")\n",
+	    f->name_cs,
+	    (f->name_cs >= 0 ?
+	     LYCharSet_UC[f->name_cs].MIMEname : "<UNKNOWN>"),
+	    I->name_cs,
+	    (I->name_cs >= 0 ?
+	     LYCharSet_UC[I->name_cs].MIMEname : "<UNKNOWN>")));
+    CTRACE((tfp, "            value_cs=%d \"%s\" (from %d \"%s\")\n",
+	    f->value_cs,
+	    (f->value_cs >= 0 ?
+	     LYCharSet_UC[f->value_cs].MIMEname : "<UNKNOWN>"),
+	    I->value_cs,
+	    (I->value_cs >= 0 ?
+	     LYCharSet_UC[I->value_cs].MIMEname : "<UNKNOWN>")));
+
+    /*
+     * Return the SIZE of the input field.
+     */
+    if (I->size && f->size > adjust_marker) {
+	f->size -= adjust_marker;
+    }
+    return (f->size);
+}
+
+/*
+ * If we're numbering fields on the right, do it.  Note that some fields may
+ * be too long for the line - we'll lose the marker in that case rather than
+ * truncate the field.
+ */
+void HText_endInput(HText *text)
+{
+    if (fields_are_numbered()
+	&& !number_fields_on_left
+	&& text != NULL
+	&& text->last_anchor != NULL
+	&& text->last_anchor->number > 0) {
+	char marker[20];
+
+	sprintf(marker, "[%d]", text->last_anchor->number);
+	HText_appendText(text, marker);
+    }
+}
+
+/*
+ * Get a translation (properly:  transcoding) quality, factoring in
+ * our ability to translate (an UCTQ_t) and a possible q parameter
+ * on the given charset string, for cs_from -> givenmime.
+ * The parsed input string will be mutilated on exit(!).
+ * Note that results are not normalised to 1.0, but results from
+ * different calls of this function can be compared.  - kw
+ *
+ * Obsolete, it was planned to use here a quality parametr UCTQ_t,
+ * which is boolean now.
+ */
+static double get_trans_q(int cs_from,
+			  char *givenmime)
+{
+    double df = 1.0;
+    BOOL tq;
+    char *p;
+
+    if (!givenmime || !(*givenmime))
+	return 0.0;
+    if ((p = strchr(givenmime, ';')) != NULL) {
+	*p++ = '\0';
+    }
+    if (!strcmp(givenmime, "*"))
+	tq = UCCanTranslateFromTo(cs_from,
+				  UCGetLYhndl_byMIME("utf-8"));
+    else
+	tq = UCCanTranslateFromTo(cs_from,
+				  UCGetLYhndl_byMIME(givenmime));
+    if (!tq)
+	return 0.0;
+    if (non_empty(p)) {
+	char *pair, *field = p, *pval, *ptok;
+
+	/* Get all the parameters to the Charset */
+	while ((pair = HTNextTok(&field, ";", "\"", NULL)) != NULL) {
+	    if ((ptok = HTNextTok(&pair, "= ", NULL, NULL)) != NULL &&
+		(pval = HTNextField(&pair)) != NULL) {
+		if (0 == strcasecomp(ptok, "q")) {
+		    df = strtod(pval, NULL);
+		    break;
+		}
+	    }
+	}
+	return (df * tq);
+    } else {
+	return tq;
+    }
+}
+
+/*
+ * Find the best charset for submission, if we have an ACCEPT_CHARSET
+ * list.  It factors in how well we can translate (just as guess, and
+ * not a very good one..) and possible ";q=" factors.  Yes this is
+ * more general than it needs to be here.
+ *
+ * Input is cs_in and acceptstring.
+ *
+ * Will return charset handle as int.
+ * best_csname will point to a newly allocated MIME string for the
+ * charset corresponding to the return value if return value >= 0.
+ * - kw
+ */
+static int find_best_target_cs(char **best_csname,
+			       int cs_from,
+			       const char *acceptstring)
+{
+    char *paccept = NULL;
+    double bestq = -1.0;
+    char *bestmime = NULL;
+    char *field, *nextfield;
+
+    StrAllocCopy(paccept, acceptstring);
+    nextfield = paccept;
+    while ((field = HTNextTok(&nextfield, ",", "\"", NULL)) != NULL) {
+	double q;
+
+	if (*field != '\0') {
+	    /* Get the Charset */
+	    q = get_trans_q(cs_from, field);
+	    if (q > bestq) {
+		bestq = q;
+		bestmime = field;
+	    }
+	}
+    }
+    if (bestmime) {
+	if (!strcmp(bestmime, "*"))	/* non-standard for HTML attribute.. */
+	    StrAllocCopy(*best_csname, "utf-8");
+	else
+	    StrAllocCopy(*best_csname, bestmime);
+	FREE(paccept);
+	if (bestq > 0)
+	    return (UCGetLYhndl_byMIME(*best_csname));
+	else
+	    return (-1);
+    }
+    FREE(paccept);
+    return (-1);
+}
+
+#ifdef USE_FILE_UPLOAD
+static void load_a_file(const char *val_used,
+			bstring **result)
+{
+    FILE *fd;
+    size_t bytes;
+    char buffer[257];
+
+    CTRACE((tfp, "Ok, about to convert %s to mime/thingy\n", val_used));
+
+    if (*val_used) {		/* ignore empty form field */
+	if ((fd = fopen(val_used, BIN_R)) == 0) {
+	    HTAlert(gettext("Can't open file for uploading"));
+	} else {
+	    while ((bytes = fread(buffer, sizeof(char), 256, fd)) != 0) {
+		HTSABCat(result, buffer, bytes);
+	    }
+	    LYCloseInput(fd);
+	}
+    }
+}
+
+static const char *guess_content_type(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";
+}
+#endif /* USE_FILE_UPLOAD */
+
+static void cannot_transcode(BOOL *had_warning,
+			     const char *target_csname)
+{
+    if (*had_warning == NO) {
+	*had_warning = YES;
+	_user_message(CANNOT_TRANSCODE_FORM,
+		      target_csname ? target_csname : "UNKNOWN");
+	LYSleepAlert();
+    }
+}
+
+#define SPECIAL_8BIT 1
+#define SPECIAL_FORM 2
+
+static unsigned check_form_specialchars(const char *value)
+{
+    unsigned result = 0;
+    const char *p;
+
+    for (p = value;
+	 non_empty(p) && (result != (SPECIAL_8BIT | SPECIAL_FORM));
+	 p++) {
+	if ((*p == HT_NON_BREAK_SPACE) ||
+	    (*p == HT_EN_SPACE) ||
+	    (*p == LY_SOFT_HYPHEN)) {
+	    result |= SPECIAL_FORM;
+	} else if ((*p & 0x80) != 0) {
+	    result |= SPECIAL_8BIT;
+	}
+    }
+    return result;
+}
+
+/*
+ * Scan the given data, adding characters to the MIME-boundary to keep it from
+ * matching any part of the data.
+ */
+static void UpdateBoundary(char **Boundary,
+			   bstring *data)
+{
+    int j;
+    int have = strlen(*Boundary);
+    int last = BStrLen(data);
+    char *text = BStrData(data);
+    char *want = *Boundary;
+
+    assert(want != NULL);
+    assert(text != NULL);
+
+    for (j = 0; j <= (last - have); ++j) {
+	if (want[0] == text[j]
+	    && !memcmp(want, text + j, have)) {
+	    char temp[2];
+
+	    temp[0] = (char) (isdigit(UCH(text[have + j])) ? 'a' : '0');
+	    temp[1] = '\0';
+	    StrAllocCat(want, temp);
+	    ++have;
+	}
+    }
+    *Boundary = want;
+}
+
+/*
+ * Convert a string to base64
+ */
+static char *convert_to_base64(const char *src,
+			       int len)
+{
+#define B64_LINE       76
+
+    static const char basis_64[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+    char *dest;
+    int rlen;			/* length of result string */
+    unsigned char c1, c2, c3;
+    const char *eol;
+    char *r;
+    const char *str;
+    int eollen;
+    int chunk;
+
+    str = src;
+    eol = "\n";
+    eollen = 1;
+
+    /* calculate the length of the result */
+    rlen = (len + 2) / 3 * 4;	/* encoded bytes */
+    if (rlen) {
+	/* add space for EOL */
+	rlen += ((rlen - 1) / B64_LINE + 1) * eollen;
+    }
+
+    /* allocate a result buffer */
+    if ((dest = (char *) malloc(rlen + 1)) == NULL) {
+	outofmem(__FILE__, "convert_to_base64");
+    }
+    assert(dest != NULL);
+    r = dest;
+
+    /* encode */
+    for (chunk = 0; len > 0; len -= 3, chunk++) {
+	if (chunk == (B64_LINE / 4)) {
+	    const char *c = eol;
+	    const char *e = eol + eollen;
+
+	    while (c < e)
+		*r++ = *c++;
+	    chunk = 0;
+	}
+	c1 = UCH(*str++);
+	c2 = UCH(*str++);
+	*r++ = basis_64[c1 >> 2];
+	*r++ = basis_64[((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)];
+	if (len > 2) {
+	    c3 = UCH(*str++);
+	    *r++ = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6)];
+	    *r++ = basis_64[c3 & 0x3F];
+	} else if (len == 2) {
+	    *r++ = basis_64[(c2 & 0xF) << 2];
+	    *r++ = '=';
+	} else {		/* len == 1 */
+	    *r++ = '=';
+	    *r++ = '=';
+	}
+    }
+    if (rlen) {
+	/* append eol to the result string */
+	const char *c = eol;
+	const char *e = eol + eollen;
+
+	while (c < e)
+	    *r++ = *c++;
+    }
+    *r = '\0';
+
+    return dest;
+}
+
+typedef enum {
+    NO_QUOTE			/* no quoting needed */
+    ,QUOTE_MULTI		/* multipart */
+    ,QUOTE_BASE64		/* encode as base64 */
+    ,QUOTE_SPECIAL		/* escape special characters only */
+} QuoteData;
+
+typedef struct {
+    int type;			/* the type of this field */
+    BOOL first;			/* true if this begins a submission part */
+    char *name;			/* the name of this field */
+    char *value;		/* the nominal value of this field */
+    bstring *data;		/* its data, which is usually the same as the value */
+    QuoteData quote;		/* how to quote/translate the data */
+} PostData;
+
+static char *escape_or_quote_name(const char *name,
+				  QuoteData quoting,
+				  const char *MultipartContentType)
+{
+    char *escaped1 = NULL;
+
+    switch (quoting) {
+    case NO_QUOTE:
+	StrAllocCopy(escaped1, name);
+	break;
+    case QUOTE_MULTI:
+    case QUOTE_BASE64:
+	StrAllocCopy(escaped1, "Content-Disposition: form-data");
+	HTSprintf(&escaped1, "; name=\"%s\"", name);
+	if (MultipartContentType)
+	    HTSprintf(&escaped1, MultipartContentType, "text/plain");
+	if (quoting == QUOTE_BASE64)
+	    StrAllocCat(escaped1, "\r\nContent-Transfer-Encoding: base64");
+	StrAllocCat(escaped1, "\r\n\r\n");
+	break;
+    case QUOTE_SPECIAL:
+	escaped1 = HTEscapeSP(name, URL_XALPHAS);
+	break;
+    }
+    return escaped1;
+}
+
+static char *escape_or_quote_value(const char *value,
+				   QuoteData quoting)
+{
+    char *escaped2 = NULL;
+
+    switch (quoting) {
+    case NO_QUOTE:
+    case QUOTE_MULTI:
+	StrAllocCopy(escaped2, NonNull(value));
+	break;
+    case QUOTE_BASE64:
+	/* FIXME: this is redundant */
+	escaped2 = convert_to_base64(value, strlen(value));
+	break;
+    case QUOTE_SPECIAL:
+	escaped2 = HTEscapeSP(value, URL_XALPHAS);
+	break;
+    }
+    return escaped2;
+}
+
+/*
+ * Check if we should encode the data in base64.  We can, only if we're using
+ * a multipart content type.  We should, if we're sending mail and the data
+ * contains long lines or nonprinting characters.
+ */
+static int check_if_base64_needed(int submit_method,
+				  bstring *data)
+{
+    int width = 0;
+    BOOL printable = TRUE;
+    char *text = BStrData(data);
+
+    if (text != 0) {
+	int col = 0;
+	int n;
+	int length = BStrLen(data);
+
+	for (n = 0; n < length; ++n) {
+	    int ch = UCH(text[n]);
+
+	    if (is8bits(ch) || ((ch < 32 && ch != '\n'))) {
+		CTRACE((tfp, "nonprintable %d:%#x\n", n, ch));
+		printable = FALSE;
+	    }
+	    if (ch == '\n' || ch == '\r') {
+		if (width < col)
+		    width = col;
+		col = 0;
+	    } else {
+		++col;
+	    }
+	}
+	if (width < col)
+	    width = col;
+    }
+    return !printable && ((submit_method == URL_MAIL_METHOD) && (width > 72));
+}
+
+/*
+ * HText_SubmitForm - generate submit data from form fields.
+ * For mailto forms, send the data.
+ * For other methods, set fields in structure pointed to by doc
+ * appropriately for next request.
+ * Returns 1 if *doc set appropriately for next request,
+ * 0 otherwise.  - kw
+ */
+int HText_SubmitForm(FormInfo * submit_item, DocInfo *doc, char *link_name,
+		     char *link_value)
+{
+    BOOL had_chartrans_warning = NO;
+    BOOL have_accept_cs = NO;
+    BOOL success;
+    BOOLEAN PlainText = FALSE;
+    BOOLEAN SemiColon = FALSE;
+    BOOL skip_field = FALSE;
+    const char *out_csname;
+    const char *target_csname = NULL;
+    PerFormInfo *thisform;
+    PostData *my_data = NULL;
+    TextAnchor *anchor_ptr;
+    bstring *my_query = NULL;
+    char *Boundary = NULL;
+    char *MultipartContentType = NULL;
+    char *content_type_out = NULL;
+    char *copied_name_used = NULL;
+    char *copied_val_used = NULL;
+    char *escaped1 = NULL;
+    char *escaped2 = NULL;
+    char *last_textarea_name = NULL;
+    const char *name_used = "";
+    char *previous_blanks = NULL;
+    const char *val_used = "";
+    int anchor_count = 0;
+    int anchor_limit = 0;
+    int form_number = submit_item->number;
+    int result = 0;
+    int target_cs = -1;
+    int textarea_lineno = 0;
+    unsigned form_is_special = 0;
+
+    CTRACE((tfp, "SubmitForm\n  link_name=%s\n  link_value=%s\n", link_name, link_value));
+    if (!HTMainText)
+	return 0;
+
+    thisform = (PerFormInfo *) HTList_objectAt(HTMainText->forms, form_number
+					       - 1);
+    /*  Sanity check */
+    if (!thisform) {
+	CTRACE((tfp, "SubmitForm: form %d not in HTMainText's list!\n",
+		form_number));
+    } else if (thisform->number != form_number) {
+	CTRACE((tfp, "SubmitForm: failed sanity check, %d!=%d !\n",
+		thisform->number, form_number));
+	thisform = NULL;
+    }
+
+    if (isEmpty(submit_item->submit_action)) {
+	CTRACE((tfp, "SubmitForm: no action given\n"));
+	return 0;
+    }
+
+    /*
+     * If we're mailing, make sure it's a mailto ACTION.  -FM
+     */
+    if ((submit_item->submit_method == URL_MAIL_METHOD) &&
+	!isMAILTO_URL(submit_item->submit_action)) {
+	HTAlert(BAD_FORM_MAILTO);
+	return 0;
+    }
+
+    /*
+     * Check the ENCTYPE and set up the appropriate variables.  -FM
+     */
+    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;
+    } else if (submit_item->submit_enctype &&
+	       !strncasecomp(submit_item->submit_enctype,
+			     "multipart/form-data", 19)) {
+	/*
+	 * Use the multipart MIME format.  Later we will ensure it does not
+	 * occur within the content.
+	 */
+	Boundary = "xnyLAaB03X";
+    }
+
+    /*
+     * Determine in what character encoding (aka.  charset) we should
+     * submit.  We call this target_cs and the MIME name for it
+     * target_csname.
+     * TODO:   - actually use ACCEPT-CHARSET stuff from FORM
+     * TODO:   - deal with list in ACCEPT-CHARSET, find a "best"
+     *           charset to submit
+     */
+
+    /* Look at ACCEPT-CHARSET on the submitting field if we have one. */
+    if (thisform && submit_item->accept_cs &&
+	strcasecomp(submit_item->accept_cs, "UNKNOWN")) {
+	have_accept_cs = YES;
+	target_cs = find_best_target_cs(&thisform->thisacceptcs,
+					current_char_set,
+					submit_item->accept_cs);
+    }
+    /* Look at ACCEPT-CHARSET on form as a whole if submitting field
+     * didn't have one. */
+    if (thisform && !have_accept_cs && thisform->accept_cs &&
+	strcasecomp(thisform->accept_cs, "UNKNOWN")) {
+	have_accept_cs = YES;
+	target_cs = find_best_target_cs(&thisform->thisacceptcs,
+					current_char_set,
+					thisform->accept_cs);
+    }
+    if (have_accept_cs && (target_cs >= 0) && thisform->thisacceptcs) {
+	target_csname = thisform->thisacceptcs;
+    }
+
+    if (target_cs < 0 &&
+	HTMainText->node_anchor->charset &&
+	*HTMainText->node_anchor->charset) {
+	target_cs = UCGetLYhndl_byMIME(HTMainText->node_anchor->charset);
+	if (target_cs >= 0) {
+	    target_csname = HTMainText->node_anchor->charset;
+	} else {
+	    target_cs = UCLYhndl_for_unspec;	/* always >= 0 */
+	    target_csname = LYCharSet_UC[target_cs].MIMEname;
+	}
+    }
+    if (target_cs < 0) {
+	target_cs = UCLYhndl_for_unspec;	/* always >= 0 */
+    }
+
+    /*
+     * Go through list of anchors and get a "max." charset parameter - kw
+     */
+    for (anchor_ptr = HTMainText->first_anchor;
+	 anchor_ptr != NULL;
+	 anchor_ptr = anchor_ptr->next) {
+
+	if (anchor_ptr->link_type != INPUT_ANCHOR)
+	    continue;
+
+	if (anchor_ptr->input_field->number == form_number &&
+	    !anchor_ptr->input_field->disabled) {
+
+	    FormInfo *form_ptr = anchor_ptr->input_field;
+	    char *val = (form_ptr->cp_submit_value != NULL
+			 ? form_ptr->cp_submit_value
+			 : form_ptr->value);
+
+	    unsigned field_is_special = check_form_specialchars(val);
+	    unsigned name_is_special = check_form_specialchars(form_ptr->name);
+
+	    form_is_special = (field_is_special | name_is_special);
+
+	    if (field_is_special == 0) {
+		/* already ok */
+	    } else if (target_cs < 0) {
+		/* already confused */
+	    } else if ((field_is_special & SPECIAL_8BIT) == 0
+		       && (LYCharSet_UC[target_cs].enc == UCT_ENC_8859
+			   || (LYCharSet_UC[target_cs].like8859 & UCT_R_8859SPECL))) {
+		/* those specials will be trivial */
+	    } else if (UCNeedNotTranslate(form_ptr->value_cs, target_cs)) {
+		/* already ok */
+	    } else if (UCCanTranslateFromTo(form_ptr->value_cs, target_cs)) {
+		/* also ok */
+	    } else if (UCCanTranslateFromTo(target_cs, form_ptr->value_cs)) {
+		target_cs = form_ptr->value_cs;		/* try this */
+		target_csname = NULL;	/* will be set after loop */
+	    } else {
+		target_cs = -1;	/* don't know what to do */
+	    }
+
+	    /*  Same for name */
+	    if (name_is_special == 0) {
+		/* already ok */
+	    } else if (target_cs < 0) {
+		/* already confused */
+	    } else if ((name_is_special & SPECIAL_8BIT) == 0
+		       && (LYCharSet_UC[target_cs].enc == UCT_ENC_8859
+			   || (LYCharSet_UC[target_cs].like8859 & UCT_R_8859SPECL))) {
+		/* those specials will be trivial */
+	    } else if (UCNeedNotTranslate(form_ptr->name_cs, target_cs)) {
+		/* already ok */
+	    } else if (UCCanTranslateFromTo(form_ptr->name_cs, target_cs)) {
+		/* also ok */
+	    } else if (UCCanTranslateFromTo(target_cs, form_ptr->name_cs)) {
+		target_cs = form_ptr->value_cs;		/* try this */
+		target_csname = NULL;	/* will be set after loop */
+	    } else {
+		target_cs = -1;	/* don't know what to do */
+	    }
+
+	    ++anchor_limit;
+	} else if (anchor_ptr->input_field->number > form_number) {
+	    break;
+	}
+    }
+
+    /*
+     * If we have input fields (we expect this), make an array of them so we
+     * can organize the data.
+     */
+    if (anchor_limit != 0) {
+	my_data = typecallocn(PostData, anchor_limit);
+	if (my_data == 0)
+	    outofmem(__FILE__, "HText_SubmitForm");
+	assert(my_data != NULL);
+    }
+
+    if (target_csname == NULL && target_cs >= 0) {
+	if ((form_is_special & SPECIAL_8BIT) != 0) {
+	    target_csname = LYCharSet_UC[target_cs].MIMEname;
+	} else if ((form_is_special & SPECIAL_FORM) != 0) {
+	    target_csname = LYCharSet_UC[target_cs].MIMEname;
+	} else {
+	    target_csname = "us-ascii";
+	}
+    }
+
+    if (submit_item->submit_method == URL_GET_METHOD && Boundary == NULL) {
+	char *temp = NULL;
+
+	StrAllocCopy(temp, submit_item->submit_action);
+	/*
+	 * Method is GET.  Clip out any anchor in the current URL.
+	 */
+	strtok(temp, "#");
+	/*
+	 * Clip out any old query in the current URL.
+	 */
+	strtok(temp, "?");
+	/*
+	 * Add the lead question mark for the new URL.
+	 */
+	StrAllocCat(temp, "?");
+	BStrCat0(my_query, temp);
+    } else {
+	/*
+	 * We are submitting POST content to a server,
+	 * so load content_type_out.  This will be put in
+	 * the post_content_type element if all goes well.  -FM, kw
+	 */
+	if (SemiColon == TRUE) {
+	    StrAllocCopy(content_type_out,
+			 "application/sgml-form-urlencoded");
+	} else if (PlainText == TRUE) {
+	    StrAllocCopy(content_type_out,
+			 "text/plain");
+	} else if (Boundary != NULL) {
+	    StrAllocCopy(content_type_out,
+			 "multipart/form-data");
+	} else {
+	    StrAllocCopy(content_type_out,
+			 "application/x-www-form-urlencoded");
+	}
+
+	/*
+	 * If the ENCTYPE is not multipart/form-data, append the
+	 * charset we'll be sending to the post_content_type, IF
+	 *  (1) there was an explicit accept-charset attribute, OR
+	 *  (2) we have 8-bit or special chars, AND the document had
+	 *      an explicit (recognized and accepted) charset parameter,
+	 *      AND it or target_csname is different from iso-8859-1,
+	 *      OR
+	 *  (3) we have 8-bit or special chars, AND the document had
+	 *      no explicit (recognized and accepted) charset parameter,
+	 *      AND target_cs is different from the currently effective
+	 *      assumed charset (which should have been set by the user
+	 *      so that it reflects what the server is sending, if the
+	 *      document is rendered correctly).
+	 * For multipart/form-data the equivalent will be done later,
+	 * separately for each form field.  - kw
+	 */
+	if (have_accept_cs
+	    || ((form_is_special & SPECIAL_8BIT) != 0
+		|| (form_is_special & SPECIAL_FORM) != 0)) {
+	    if (target_cs >= 0 && target_csname) {
+		if (Boundary == NULL) {
+		    if ((HTMainText->node_anchor->charset &&
+			 (strcmp(HTMainText->node_anchor->charset,
+				 "iso-8859-1") ||
+			  strcmp(target_csname, "iso-8859-1"))) ||
+			(!HTMainText->node_anchor->charset &&
+			 target_cs != UCLYhndl_for_unspec)) {
+			HTSprintf(&content_type_out, "; charset=%s", target_csname);
+		    }
+		}
+	    } else {
+		cannot_transcode(&had_chartrans_warning, target_csname);
+	    }
+	}
+    }
+
+    out_csname = target_csname;
+
+    /*
+     * Build up a list of the input fields and their associated values.
+     */
+    for (anchor_ptr = HTMainText->first_anchor;
+	 anchor_ptr != NULL;
+	 anchor_ptr = anchor_ptr->next) {
+
+	if (anchor_ptr->link_type != INPUT_ANCHOR)
+	    continue;
+
+	if (anchor_ptr->input_field->number == form_number &&
+	    !anchor_ptr->input_field->disabled) {
+
+	    FormInfo *form_ptr = anchor_ptr->input_field;
+	    int out_cs;
+	    QuoteData quoting = (PlainText
+				 ? NO_QUOTE
+				 : (Boundary
+				    ? QUOTE_MULTI
+				    : QUOTE_SPECIAL));
+
+	    assert(my_data != NULL);
+
+	    if (form_ptr->type != F_TEXTAREA_TYPE)
+		textarea_lineno = 0;
+
+	    CTRACE((tfp, "SubmitForm[%d/%d]: ",
+		    anchor_count + 1, anchor_limit));
+
+	    name_used = NonNull(form_ptr->name);
+
+	    switch (form_ptr->type) {
+	    case F_RESET_TYPE:
+		CTRACE((tfp, "reset\n"));
+		break;
+#ifdef USE_FILE_UPLOAD
+	    case F_FILE_TYPE:
+		val_used = NonNull(form_ptr->value);
+		CTRACE((tfp, "I will submit %s (from %s)\n",
+			val_used, name_used));
+		break;
+#endif
+	    case F_SUBMIT_TYPE:
+	    case F_TEXT_SUBMIT_TYPE:
+	    case F_IMAGE_SUBMIT_TYPE:
+		if (!(non_empty(form_ptr->name) &&
+		      !strcmp(form_ptr->name, link_name))) {
+		    CTRACE((tfp, "skipping submit field with "));
+		    CTRACE((tfp, "name \"%s\" for link_name \"%s\", %s.\n",
+			    form_ptr->name ? form_ptr->name : "???",
+			    link_name ? link_name : "???",
+			    non_empty(form_ptr->name) ?
+			    "not current link" : "no field name"));
+		    break;
+		}
+		if (!(form_ptr->type == F_TEXT_SUBMIT_TYPE ||
+		      (non_empty(form_ptr->value) &&
+		       !strcmp(form_ptr->value, link_value)))) {
+		    CTRACE((tfp, "skipping submit field with "));
+		    CTRACE((tfp, "name \"%s\" for link_name \"%s\", %s!\n",
+			    form_ptr->name ? form_ptr->name : "???",
+			    link_name ? link_name : "???",
+			    "values are different"));
+		    break;
+		}
+		/* FALLTHRU */
+	    case F_RADIO_TYPE:
+	    case F_CHECKBOX_TYPE:
+	    case F_TEXTAREA_TYPE:
+	    case F_PASSWORD_TYPE:
+	    case F_TEXT_TYPE:
+	    case F_OPTION_LIST_TYPE:
+	    case F_HIDDEN_TYPE:
+		/*
+		 * Be sure to actually look at the option submit value.
+		 */
+		if (form_ptr->cp_submit_value != NULL) {
+		    val_used = form_ptr->cp_submit_value;
+		} else {
+		    val_used = form_ptr->value;
+		}
+
+		/*
+		 * Charset-translate value now, because we need to know the
+		 * charset parameter for multipart bodyparts.  - kw
+		 */
+		if (check_form_specialchars(val_used) != 0) {
+		    /*  We should translate back. */
+		    StrAllocCopy(copied_val_used, val_used);
+		    success = LYUCTranslateBackFormData(&copied_val_used,
+							form_ptr->value_cs,
+							target_cs, PlainText);
+		    CTRACE((tfp, "field \"%s\" %d %s -> %d %s %s\n",
+			    NonNull(form_ptr->name),
+			    form_ptr->value_cs,
+			    ((form_ptr->value_cs >= 0)
+			     ? LYCharSet_UC[form_ptr->value_cs].MIMEname
+			     : "???"),
+			    target_cs,
+			    target_csname ? target_csname : "???",
+			    success ? "OK" : "FAILED"));
+		    if (success) {
+			val_used = copied_val_used;
+		    }
+		} else {	/* We can use the value directly. */
+		    CTRACE((tfp, "field \"%s\" %d %s OK\n",
+			    NonNull(form_ptr->name),
+			    target_cs,
+			    target_csname ? target_csname : "???"));
+		    success = YES;
+		}
+		if (!success) {
+		    cannot_transcode(&had_chartrans_warning, target_csname);
+		    out_cs = form_ptr->value_cs;
+		} else {
+		    out_cs = target_cs;
+		}
+		if (out_cs >= 0)
+		    out_csname = LYCharSet_UC[out_cs].MIMEname;
+		if (Boundary) {
+		    StrAllocCopy(MultipartContentType,
+				 "\r\nContent-Type: %s");
+		    if (!success && form_ptr->value_cs < 0) {
+			/*  This is weird. */
+			out_csname = "UNKNOWN-8BIT";
+		    } else if (!success) {
+			target_csname = NULL;
+		    } else {
+			if (!target_csname) {
+			    target_csname = LYCharSet_UC[target_cs].MIMEname;
+			}
+		    }
+		    if (strcmp(out_csname, "iso-8859-1"))
+			HTSprintf(&MultipartContentType, "; charset=%s", out_csname);
+		}
+
+		/*
+		 * Charset-translate name now, because we need to know the
+		 * charset parameter for multipart bodyparts.  - kw
+		 */
+		if (form_ptr->type == F_TEXTAREA_TYPE) {
+		    textarea_lineno++;
+		    if (textarea_lineno > 1 &&
+			last_textarea_name && form_ptr->name &&
+			!strcmp(last_textarea_name, form_ptr->name)) {
+			break;
+		    }
+		}
+
+		if (check_form_specialchars(name_used) != 0) {
+		    /*  We should translate back. */
+		    StrAllocCopy(copied_name_used, name_used);
+		    success = LYUCTranslateBackFormData(&copied_name_used,
+							form_ptr->name_cs,
+							target_cs, PlainText);
+		    CTRACE((tfp, "name \"%s\" %d %s -> %d %s %s\n",
+			    NonNull(form_ptr->name),
+			    form_ptr->name_cs,
+			    ((form_ptr->name_cs >= 0)
+			     ? LYCharSet_UC[form_ptr->name_cs].MIMEname
+			     : "???"),
+			    target_cs,
+			    target_csname ? target_csname : "???",
+			    success ? "OK" : "FAILED"));
+		    if (success) {
+			name_used = copied_name_used;
+		    }
+		    if (Boundary) {
+			if (!success) {
+			    StrAllocCopy(MultipartContentType, "");
+			    target_csname = NULL;
+			} else {
+			    if (!target_csname)
+				target_csname = LYCharSet_UC[target_cs].MIMEname;
+			}
+		    }
+		} else {	/* We can use the name directly. */
+		    CTRACE((tfp, "name \"%s\" %d %s OK\n",
+			    NonNull(form_ptr->name),
+			    target_cs,
+			    target_csname ? target_csname : "???"));
+		    success = YES;
+		    if (Boundary) {
+			StrAllocCopy(copied_name_used, name_used);
+		    }
+		}
+		if (!success) {
+		    cannot_transcode(&had_chartrans_warning, target_csname);
+		}
+		if (Boundary) {
+		    /*
+		     * According to RFC 1867, Non-ASCII field names
+		     * "should be encoded according to the prescriptions
+		     * of RFC 1522 [...].  I don't think RFC 1522 actually
+		     * is meant to apply to parameters like this, and it
+		     * is unknown whether any server would make sense of
+		     * it, so for now just use some quoting/escaping and
+		     * otherwise leave 8-bit values as they are.
+		     * Non-ASCII characters in form field names submitted
+		     * as multipart/form-data can only occur if the form
+		     * provider specifically asked for it anyway.  - kw
+		     */
+		    HTMake822Word(&copied_name_used, FALSE);
+		    name_used = copied_name_used;
+		}
+
+		break;
+	    default:
+		CTRACE((tfp, "What type is %d?\n", form_ptr->type));
+		break;
+	    }
+
+	    skip_field = FALSE;
+	    my_data[anchor_count].first = TRUE;
+	    my_data[anchor_count].type = form_ptr->type;
+
+	    /*
+	     * Using the values of 'name_used' and 'val_used' computed in the
+	     * previous case-statement, compute the 'first' and 'data' values
+	     * for the current input field.
+	     */
+	    switch (form_ptr->type) {
+
+	    default:
+		skip_field = TRUE;
+		break;
+
+#ifdef USE_FILE_UPLOAD
+	    case F_FILE_TYPE:
+		load_a_file(val_used, &(my_data[anchor_count].data));
+		break;
+#endif /* USE_FILE_UPLOAD */
+
+	    case F_SUBMIT_TYPE:
+	    case F_TEXT_SUBMIT_TYPE:
+	    case F_IMAGE_SUBMIT_TYPE:
+		if ((non_empty(form_ptr->name) &&
+		     !strcmp(form_ptr->name, link_name)) &&
+		    (form_ptr->type == F_TEXT_SUBMIT_TYPE ||
+		     (non_empty(form_ptr->value) &&
+		      !strcmp(form_ptr->value, link_value)))) {
+		    ;
+		} else {
+		    skip_field = TRUE;
+		}
+		break;
+
+	    case F_RADIO_TYPE:
+	    case F_CHECKBOX_TYPE:
+		/*
+		 * Only add if selected.
+		 */
+		if (form_ptr->num_value) {
+		    ;
+		} else {
+		    skip_field = TRUE;
+		}
+		break;
+
+	    case F_TEXTAREA_TYPE:
+		if (!last_textarea_name ||
+		    strcmp(last_textarea_name, form_ptr->name)) {
+		    textarea_lineno = 1;
+		    last_textarea_name = form_ptr->name;
+		} else {
+		    my_data[anchor_count].first = FALSE;
+		}
+		break;
+
+	    case F_PASSWORD_TYPE:
+	    case F_TEXT_TYPE:
+	    case F_OPTION_LIST_TYPE:
+	    case F_HIDDEN_TYPE:
+		break;
+	    }
+
+	    /*
+	     * If we did not decide to skip the current field, populate the
+	     * values in the array for it.
+	     */
+	    if (!skip_field) {
+		StrAllocCopy(my_data[anchor_count].name, name_used);
+		StrAllocCopy(my_data[anchor_count].value, val_used);
+		if (my_data[anchor_count].data == 0)
+		    BStrCat0(my_data[anchor_count].data, val_used);
+		my_data[anchor_count].quote = quoting;
+		if (quoting == QUOTE_MULTI
+		    && check_if_base64_needed(submit_item->submit_method,
+					      my_data[anchor_count].data)) {
+		    CTRACE((tfp, "will encode as base64\n"));
+		    my_data[anchor_count].quote = QUOTE_BASE64;
+		    escaped2 =
+			convert_to_base64(BStrData(my_data[anchor_count].data),
+					  BStrLen(my_data[anchor_count].data));
+		    BStrCopy0(my_data[anchor_count].data, escaped2);
+		    FREE(escaped2);
+		}
+	    }
+	    ++anchor_count;
+
+	    FREE(copied_name_used);
+	    FREE(copied_val_used);
+
+	} else if (anchor_ptr->input_field->number > form_number) {
+	    break;
+	}
+    }
+
+    FREE(copied_name_used);
+
+    if (my_data != 0) {
+	BOOL first_one = TRUE;
+
+	/*
+	 * If we're using a MIME-boundary, make it unique.
+	 */
+	if (content_type_out != 0 && Boundary != 0) {
+	    Boundary = 0;
+	    StrAllocCopy(Boundary, "LYNX");
+	    for (anchor_count = 0; anchor_count < anchor_limit; ++anchor_count) {
+		if (my_data[anchor_count].data != 0) {
+		    UpdateBoundary(&Boundary, my_data[anchor_count].data);
+		}
+	    }
+	    HTSprintf(&content_type_out, "; boundary=%s", Boundary);
+	}
+
+	for (anchor_count = 0; anchor_count < anchor_limit; ++anchor_count) {
+
+	    if (my_data[anchor_count].name != 0
+		&& my_data[anchor_count].value != 0) {
+
+		CTRACE((tfp,
+			"processing [%d:%d] name=%s(first:%d, value=%s, data=%p)\n",
+			anchor_count + 1,
+			anchor_limit,
+			NonNull(my_data[anchor_count].name),
+			my_data[anchor_count].first,
+			NonNull(my_data[anchor_count].value),
+			(void *) my_data[anchor_count].data));
+
+		if (my_data[anchor_count].first) {
+		    if (first_one) {
+			if (Boundary) {
+			    HTBprintf(&my_query, "--%s\r\n", Boundary);
+			}
+			first_one = FALSE;
+		    } else {
+			if (PlainText) {
+			    BStrCat0(my_query, "\n");
+			} else if (SemiColon) {
+			    BStrCat0(my_query, ";");
+			} else if (Boundary) {
+			    HTBprintf(&my_query, "\r\n--%s\r\n", Boundary);
+			} else {
+			    BStrCat0(my_query, "&");
+			}
+		    }
+		}
+
+		/* append a null to the string */
+		HTSABCat(&(my_data[anchor_count].data), "", 1);
+		name_used = my_data[anchor_count].name;
+		val_used = my_data[anchor_count].value;
+
+	    } else {
+		/* there is no data to send */
+		continue;
+	    }
+
+	    switch (my_data[anchor_count].type) {
+	    case F_TEXT_TYPE:
+	    case F_PASSWORD_TYPE:
+	    case F_OPTION_LIST_TYPE:
+	    case F_HIDDEN_TYPE:
+		escaped1 = escape_or_quote_name(my_data[anchor_count].name,
+						my_data[anchor_count].quote,
+						MultipartContentType);
+
+		escaped2 = escape_or_quote_value(val_used,
+						 my_data[anchor_count].quote);
+
+		HTBprintf(&my_query,
+			  "%s%s%s%s%s",
+			  escaped1,
+			  (Boundary ? "" : "="),
+			  (PlainText ? "\n" : ""),
+			  escaped2,
+			  ((PlainText && *escaped2) ? "\n" : ""));
+		break;
+	    case F_CHECKBOX_TYPE:
+	    case F_RADIO_TYPE:
+		escaped1 = escape_or_quote_name(my_data[anchor_count].name,
+						my_data[anchor_count].quote,
+						MultipartContentType);
+
+		escaped2 = escape_or_quote_value(val_used,
+						 my_data[anchor_count].quote);
+
+		HTBprintf(&my_query,
+			  "%s%s%s%s%s",
+			  escaped1,
+			  (Boundary ? "" : "="),
+			  (PlainText ? "\n" : ""),
+			  escaped2,
+			  ((PlainText && *escaped2) ? "\n" : ""));
+		break;
+	    case F_SUBMIT_TYPE:
+	    case F_TEXT_SUBMIT_TYPE:
+	    case F_IMAGE_SUBMIT_TYPE:
+		/*
+		 * If it has a non-zero length name (e.g., because
+		 * its IMAGE_SUBMIT_TYPE is to be handled homologously
+		 * to an image map, or a SUBMIT_TYPE in a set of
+		 * multiple submit buttons, or a single type="text"
+		 * that's been converted to a TEXT_SUBMIT_TYPE),
+		 * include the name=value pair, or fake name.x=0 and
+		 * name.y=0 pairs for IMAGE_SUBMIT_TYPE.  -FM
+		 */
+		escaped1 = escape_or_quote_name(my_data[anchor_count].name,
+						my_data[anchor_count].quote,
+						MultipartContentType);
+
+		escaped2 = escape_or_quote_value(val_used,
+						 my_data[anchor_count].quote);
+
+		if (my_data[anchor_count].type == F_IMAGE_SUBMIT_TYPE) {
+		    /*
+		     * It's a clickable image submit button.  Fake a 0,0
+		     * coordinate pair, which typically returns the image's
+		     * default.  -FM
+		     */
+		    if (Boundary) {
+			*(strchr(escaped1, '=') + 1) = '\0';
+			HTBprintf(&my_query,
+				  "%s\"%s.x\"\r\n\r\n0\r\n--%s\r\n%s\"%s.y\"\r\n\r\n0",
+				  escaped1,
+				  my_data[anchor_count].name,
+				  Boundary,
+				  escaped1,
+				  my_data[anchor_count].name);
+		    } else {
+			HTBprintf(&my_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
+		     */
+		    HTBprintf(&my_query,
+			      "%s%s%s%s%s",
+			      escaped1,
+			      (Boundary ? "" : "="),
+			      (PlainText ? "\n" : ""),
+			      escaped2,
+			      ((PlainText && *escaped2) ? "\n" : ""));
+		}
+		break;
+	    case F_RESET_TYPE:
+		/* ignore */
+		break;
+	    case F_TEXTAREA_TYPE:
+		escaped2 = escape_or_quote_value(val_used,
+						 my_data[anchor_count].quote);
+
+		if (my_data[anchor_count].first) {
+		    /*
+		     * Names are different so this is the first textarea or a
+		     * different one from any before it.
+		     */
+		    if (PlainText) {
+			FREE(previous_blanks);
+		    } else if (Boundary) {
+			StrAllocCopy(previous_blanks, "\r\n");
+		    } else {
+			StrAllocCopy(previous_blanks, "%0d%0a");
+		    }
+		    escaped1 = escape_or_quote_name(name_used,
+						    my_data[anchor_count].quote,
+						    MultipartContentType);
+
+		    HTBprintf(&my_query,
+			      "%s%s%s%s%s",
+			      escaped1,
+			      (Boundary ? "" : "="),
+			      (PlainText ? "\n" : ""),
+			      escaped2,
+			      ((PlainText && *escaped2) ? "\n" : ""));
+		} else {
+		    const char *marker = (PlainText
+					  ? "\n"
+					  : (Boundary
+					     ? "\r\n"
+					     : "%0d%0a"));
+
+		    /*
+		     * This is a continuation of a previous textarea.
+		     */
+		    if (escaped2[0] != '\0') {
+			if (previous_blanks) {
+			    BStrCat0(my_query, previous_blanks);
+			    FREE(previous_blanks);
+			}
+			BStrCat0(my_query, escaped2);
+			if (PlainText || Boundary)
+			    BStrCat0(my_query, marker);
+			else
+			    StrAllocCopy(previous_blanks, marker);
+		    } else {
+			StrAllocCat(previous_blanks, marker);
+		    }
+		}
+		break;
+	    case F_RANGE_TYPE:
+		/* not implemented */
+		break;
+#ifdef USE_FILE_UPLOAD
+	    case F_FILE_TYPE:
+		if (PlainText) {
+		    StrAllocCopy(escaped1, my_data[anchor_count].name);
+		} else if (Boundary) {
+		    const char *t = guess_content_type(val_used);
+		    char *copied_fname = NULL;
+
+		    StrAllocCopy(escaped1, "Content-Disposition: form-data");
+		    HTSprintf(&escaped1, "; name=\"%s\"",
+			      my_data[anchor_count].name);
+
+		    StrAllocCopy(copied_fname, val_used);
+		    HTMake822Word(&copied_fname, FALSE);
+		    HTSprintf(&escaped1, "; filename=\"%s\"", copied_fname);
+		    FREE(copied_fname);
+
+		    /* Should we take into account the encoding? */
+		    HTSprintf(&escaped1, "\r\nContent-Type: %s", t);
+		    if (my_data[anchor_count].quote == QUOTE_BASE64)
+			StrAllocCat(escaped1,
+				    "\r\nContent-Transfer-Encoding: base64");
+		    StrAllocCat(escaped1, "\r\n\r\n");
+		} else {
+		    escaped1 = HTEscapeSP(my_data[anchor_count].name, URL_XALPHAS);
+		}
+
+		HTBprintf(&my_query,
+			  "%s%s%s",
+			  escaped1,
+			  (Boundary ? "" : "="),
+			  (PlainText ? "\n" : ""));
+		/*
+		 * If we have anything more than the trailing null we added,
+		 * append the file-data to the query.
+		 */
+		if (BStrLen(my_data[anchor_count].data) > 1) {
+		    HTSABCat(&my_query,
+			     BStrData(my_data[anchor_count].data),
+			     BStrLen(my_data[anchor_count].data) - 1);
+		    if (PlainText)
+			HTBprintf(&my_query, "\n");
+		}
+		break;
+#endif /* USE_FILE_UPLOAD */
+	    case F_KEYGEN_TYPE:
+	    case F_BUTTON_TYPE:
+		/* not implemented */
+		break;
+	    }
+
+	    FREE(escaped1);
+	    FREE(escaped2);
+	}
+	if (Boundary) {
+	    HTBprintf(&my_query, "\r\n--%s--\r\n", Boundary);
+	}
+	/*
+	 * The data may contain a null - so we use fwrite().
+	 */
+	if (TRACE) {
+	    CTRACE((tfp, "Query %d{", BStrLen(my_query)));
+	    trace_bstring(my_query);
+	    CTRACE((tfp, "}\n"));
+	}
+    }
+
+    if (submit_item->submit_method == URL_MAIL_METHOD) {
+	HTUserMsg2(gettext("Submitting %s"), submit_item->submit_action);
+	HTSABCat(&my_query, "", 1);	/* append null */
+	mailform((submit_item->submit_action + 7),
+		 (isEmpty(submit_item->submit_title)
+		  ? NonNull(HText_getTitle())
+		  : submit_item->submit_title),
+		 BStrData(my_query),
+		 content_type_out);
+	result = 0;
+	BStrFree(my_query);
+	FREE(content_type_out);
+    } else {
+	_statusline(SUBMITTING_FORM);
+
+	if (submit_item->submit_method == URL_POST_METHOD || Boundary) {
+	    LYFreePostData(doc);
+	    doc->post_data = my_query;
+	    doc->post_content_type = content_type_out;	/* don't free c_t_out */
+	    CTRACE((tfp, "GridText - post_data set:\n%s\n", content_type_out));
+	    StrAllocCopy(doc->address, submit_item->submit_action);
+	} else {		/* GET_METHOD */
+	    HTSABCat(&my_query, "", 1);		/* append null */
+	    StrAllocCopy(doc->address, BStrData(my_query));	/* FIXME? */
+	    LYFreePostData(doc);
+	    FREE(content_type_out);
+	}
+	result = 1;
+    }
+
+    FREE(MultipartContentType);
+    FREE(previous_blanks);
+    FREE(Boundary);
+    if (my_data != 0) {
+	for (anchor_count = 0; anchor_count < anchor_limit; ++anchor_count) {
+	    FREE(my_data[anchor_count].name);
+	    FREE(my_data[anchor_count].value);
+	    BStrFree(my_data[anchor_count].data);
+	}
+	FREE(my_data);
+    }
+
+    return (result);
+}
+
+void HText_DisableCurrentForm(void)
+{
+    TextAnchor *anchor_ptr;
+
+    HTFormDisabled = TRUE;
+    if (!HTMainText)
+	return;
+
+    /*
+     * Go through list of anchors and set the disabled flag.
+     */
+    for (anchor_ptr = HTMainText->first_anchor;
+	 anchor_ptr != NULL;
+	 anchor_ptr = anchor_ptr->next) {
+	if (anchor_ptr->link_type == INPUT_ANCHOR &&
+	    anchor_ptr->input_field->number == HTFormNumber) {
+
+	    anchor_ptr->input_field->disabled = TRUE;
+	}
+    }
+
+    return;
+}
+
+void HText_ResetForm(FormInfo * form)
+{
+    TextAnchor *anchor_ptr;
+
+    _statusline(RESETTING_FORM);
+    if (HTMainText == 0)
+	return;
+
+    /*
+     * Go through list of anchors and reset values.
+     */
+    for (anchor_ptr = HTMainText->first_anchor;
+	 anchor_ptr != NULL;
+	 anchor_ptr = anchor_ptr->next) {
+	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;
+	    }
+	}
+    }
+}
+
+/*
+ * This function is called before reloading/reparsing current document to find
+ * whether any forms content was changed by user so any information will be
+ * lost.
+ */
+BOOLEAN HText_HaveUserChangedForms(HText *text)
+{
+    TextAnchor *anchor_ptr;
+
+    if (text == 0)
+	return FALSE;
+
+    /*
+     * Go through list of anchors to check if any value was changed.
+     * This code based on HText_ResetForm()
+     */
+    for (anchor_ptr = text->first_anchor;
+	 anchor_ptr != NULL;
+	 anchor_ptr = anchor_ptr->next) {
+	if (anchor_ptr->link_type == INPUT_ANCHOR) {
+
+	    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 == 1) ||
+		    (anchor_ptr->input_field->orig_value[0] != '0' &&
+		     anchor_ptr->input_field->num_value == 0))
+		    return TRUE;
+
+	    } else if (anchor_ptr->input_field->type == F_OPTION_LIST_TYPE) {
+		if (strcmp(anchor_ptr->input_field->value,
+			   anchor_ptr->input_field->orig_value))
+		    return TRUE;
+
+		if (strcmp(anchor_ptr->input_field->cp_submit_value,
+			   anchor_ptr->input_field->orig_submit_value))
+		    return TRUE;
+
+	    } else {
+		if (strcmp(anchor_ptr->input_field->value,
+			   anchor_ptr->input_field->orig_value))
+		    return TRUE;
+	    }
+	}
+    }
+    return FALSE;
+}
+
+void HText_activateRadioButton(FormInfo * form)
+{
+    TextAnchor *anchor_ptr;
+    int form_number = form->number;
+
+    if (!HTMainText)
+	return;
+    for (anchor_ptr = HTMainText->first_anchor;
+	 anchor_ptr != NULL;
+	 anchor_ptr = anchor_ptr->next) {
+	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;
+	    }
+
+	}
+    }
+
+    form->num_value = 1;
+}
+
+#ifdef LY_FIND_LEAKS
+/*
+ *	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
+ */
+static void free_all_texts(void)
+{
+    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);
+    FREE(HTFormAcceptCharset);
+    PerFormInfo_free(HTCurrentForm);
+
+    return;
+}
+#endif /* LY_FIND_LEAKS */
+
+/*
+ *  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.
+ */
+char *stub_HTAnchor_address(HTAnchor * me)
+{
+    char *addr = NULL;
+
+    if (me)
+	StrAllocCopy(addr, me->parent->address);
+    return addr;
+}
+
+void HText_setToolbar(HText *text)
+{
+    if (text)
+	text->toolbar = TRUE;
+    return;
+}
+
+BOOL HText_hasToolbar(HText *text)
+{
+    return (BOOL) ((text && text->toolbar) ? TRUE : FALSE);
+}
+
+void HText_setNoCache(HText *text)
+{
+    if (text)
+	text->no_cache = TRUE;
+    return;
+}
+
+BOOL HText_hasNoCacheSet(HText *text)
+{
+    return (BOOL) ((text && text->no_cache) ? TRUE : FALSE);
+}
+
+BOOL HText_hasUTF8OutputSet(HText *text)
+{
+    return (BOOL) ((text && text->T.output_utf8) ? TRUE : FALSE);
+}
+
+/*
+ *  Check charset and set the kcode element. -FM
+ *  Info on the input charset may be passed in in two forms,
+ *  as a string (if given explicitly) and as a pointer to
+ *  a LYUCcharset (from chartrans mechanism); either can be NULL.
+ *  For Japanese the kcode will be reset at a space or explicit
+ *  line or paragraph break, so what we set here may not last for
+ *  long.  It's potentially more important not to set HTCJK to
+ *  NOCJK unless we are sure. - kw
+ */
+void HText_setKcode(HText *text, const char *charset,
+		    LYUCcharset *p_in)
+{
+    BOOL charset_explicit;
+
+    if (!text)
+	return;
+
+    /*
+     * Check whether we have some kind of info.  - kw
+     */
+    if (!charset && !p_in) {
+	return;
+    }
+    charset_explicit = (BOOLEAN) (charset ? TRUE : FALSE);
+    /*
+     * If no explicit charset string, use the implied one.  - kw
+     */
+    if (isEmpty(charset)) {
+	charset = p_in->MIMEname;
+    }
+    /*
+     * Check whether we have a specified charset.  -FM
+     */
+    if (isEmpty(charset)) {
+	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 charset isn't specified explicitely nor assumed,
+     * p_in->MIMEname would be set as display charset.
+     * So text->kcode sholud be set as SJIS or EUC here only if charset
+     * is specified explicitely, otherwise text->kcode would cause
+     * mishandling Japanese strings.  -- TH
+     */
+    if (charset_explicit && (!strcmp(charset, "shift_jis") ||
+			     !strcmp(charset, "x-sjis") ||	/* 1997/11/28 (Fri) 18:11:33 */
+			     !strcmp(charset, "x-shift-jis"))) {
+	text->kcode = SJIS;
+    } else if (charset_explicit
+#ifdef EXP_JAPANESEUTF8_SUPPORT
+	       && strcmp(charset, "utf-8")
+#endif
+	       && ((p_in && (p_in->enc == UCT_ENC_CJK)) ||
+		   !strcmp(charset, "x-euc") ||		/* 1997/11/28 (Fri) 18:11:24 */
+		   !strcmp(charset, "euc-jp") ||
+		   !strncmp(charset, "x-euc-", 6) ||
+		   !strcmp(charset, "euc-kr") ||
+		   !strcmp(charset, "iso-2022-kr") ||
+		   !strcmp(charset, "big5") ||
+		   !strcmp(charset, "cn-big5") ||
+		   !strcmp(charset, "euc-cn") ||
+		   !strcmp(charset, "gb2312") ||
+		   !strncmp(charset, "cn-gb", 5) ||
+		   !strcmp(charset, "iso-2022-cn"))) {
+	text->kcode = EUC;
+    } else {
+	/*
+	 * If we get to here, it's not CJK, so disable that if
+	 * it is enabled.  But only if we are quite sure.  -FM & kw
+	 */
+	text->kcode = NOKANJI;
+	if (IS_CJK_TTY) {
+	    if (!p_in || ((p_in->enc != UCT_ENC_CJK)
+#ifdef EXP_JAPANESEUTF8_SUPPORT
+			  && (p_in->enc != UCT_ENC_UTF8)
+#endif
+		)) {
+		HTCJK = NOCJK;
+	    }
+	}
+    }
+
+    if (charset_explicit
+#ifdef EXP_JAPANESEUTF8_SUPPORT
+	&& strcmp(charset, "utf-8")
+#endif
+	) {
+	text->specified_kcode = text->kcode;
+    } else {
+	if (UCAssume_MIMEcharset) {
+	    if (!strcmp(UCAssume_MIMEcharset, "euc-jp"))
+		text->kcode = text->specified_kcode = EUC;
+	    else if (!strcmp(UCAssume_MIMEcharset, "shift_jis"))
+		text->kcode = text->specified_kcode = SJIS;
+	}
+    }
+
+    return;
+}
+
+/*
+ *  Set a permissible split at the current end of the last line. -FM
+ */
+void HText_setBreakPoint(HText *text)
+{
+    if (!text)
+	return;
+
+    /*
+     * Can split here.  -FM
+     */
+    text->permissible_split = text->last_line->size;
+
+    return;
+}
+
+/*
+ *  This function determines whether a document which
+ *  would be sought via the a URL that has a fragment
+ *  directive appended is otherwise identical to the
+ *  currently loaded document, and if so, returns
+ *  FALSE, so that any no_cache directives can be
+ *  overridden "safely", on the grounds that we are
+ *  simply acting on the equivalent of a paging
+ *  command.  Otherwise, it returns TRUE, i.e, that
+ *  the target document might differ from the current,
+ *  based on any caching directives or analyses which
+ *  claimed or suggested this. -FM
+ */
+BOOL HText_AreDifferent(HTParentAnchor *anchor,
+			const char *full_address)
+{
+    HTParentAnchor *MTanc;
+    char *MTaddress;
+    char *MTpound;
+
+    /*
+     * Do we have a loaded document and both
+     * arguments for this function?
+     */
+    if (!(HTMainText && anchor && full_address))
+	return TRUE;
+
+    /*
+     * Do we have both URLs?
+     */
+    MTanc = HTMainText->node_anchor;
+    if (!(MTanc->address && anchor->address))
+	return (TRUE);
+
+    /*
+     * Do we have a fragment associated with the target?
+     */
+    if (findPoundSelector(full_address) == NULL)
+	return (TRUE);
+
+    /*
+     * Always treat client-side image map menus
+     * as potentially stale, so we'll create a
+     * fresh menu from the LynxMaps HTList.
+     */
+    if (isLYNXIMGMAP(anchor->address))
+	return (TRUE);
+
+    /*
+     * Do the docs differ in the type of request?
+     */
+    if (MTanc->isHEAD != anchor->isHEAD)
+	return (TRUE);
+
+    /*
+     * Are the actual URLs different, after factoring
+     * out a "LYNXIMGMAP:" leader in the MainText URL
+     * and its fragment, if present?
+     */
+    MTaddress = (isLYNXIMGMAP(MTanc->address)
+		 ? MTanc->address + LEN_LYNXIMGMAP
+		 : MTanc->address);
+    MTpound = trimPoundSelector(MTaddress);
+    if (strcmp(MTaddress, anchor->address)) {
+	restorePoundSelector(MTpound);
+	return (TRUE);
+    }
+    restorePoundSelector(MTpound);
+
+    /*
+     * If the MainText is not an image map menu,
+     * do the docs have different POST contents?
+     */
+    if (MTaddress == MTanc->address) {
+	if (MTanc->post_data) {
+	    if (anchor->post_data) {
+		if (!BINEQ(MTanc->post_data, anchor->post_data)) {
+		    /*
+		     * Both have contents, and they differ.
+		     */
+		    return (TRUE);
+		}
+	    } else {
+		/*
+		 * The loaded document has content, but the
+		 * target doesn't, so they're different.
+		 */
+		return (TRUE);
+	    }
+	} else if (anchor->post_data) {
+	    /*
+	     * The loaded document does not have content, but
+	     * the target does, so they're different.
+	     */
+	    return (TRUE);
+	}
+    }
+
+    /*
+     * We'll assume the target is a position in the currently
+     * displayed document, and thus can ignore any header, META,
+     * or other directives not to use a cached rendition.  -FM
+     */
+    return (FALSE);
+}
+
+#define CanTrimTextArea(c) \
+    (LYtrimInputFields ? isspace(c) : ((c) == '\r' || (c) == '\n'))
+
+/*
+ * Re-render the text of a tagged ("[123]") HTLine (arg1), with the tag
+ * number incremented by some value (arg5).  The re-rendered string may
+ * be allowed to expand in the event of a tag width change (eg, 99 -> 100)
+ * as controlled by arg6 (CHOP or NOCHOP).  Arg4 is either (the address
+ * of) a value which must match, in order for the tag to be incremented,
+ * or (the address of) a 0-value, which will match any value, and cause
+ * any valid tag to be incremented.  Arg2 is a pointer to the first/only
+ * anchor that exists on the line; we may need to adjust their position(s)
+ * on the line.  Arg3 when non-0 indicates the number of new digits that
+ * were added to the 2nd line in a line crossing pair.
+ *
+ * All tags fields in a line which individually match an expected new value,
+ * are incremented.  Line crossing [tags] are handled (PITA).
+ *
+ * Untagged or improperly tagged lines are not altered.
+ *
+ * Returns the number of chars added to the original string's length, if
+ * any.
+ *
+ * --KED 02/03/99
+ */
+static int increment_tagged_htline(HTLine *ht, TextAnchor *a, int *lx_val,
+				   int *old_val,
+				   int incr,
+				   int mode)
+{
+    char buf[MAX_LINE];
+    char lxbuf[MAX_LINE * 2];
+
+    TextAnchor *st_anchor = a;
+    TextAnchor *nxt_anchor;
+
+    char *p = ht->data;
+    char *s = buf;
+    char *lx = lxbuf;
+    char *t;
+
+    BOOLEAN plx = FALSE;
+    BOOLEAN valid;
+
+    int val;
+    int n;
+    int new_n;
+    int pre_n;
+    int post_n;
+    int fixup = 0;
+
+    /*
+     * Cleanup for the 2nd half of a line crosser, whose number of tag
+     * digits grew by some number of places (usually 1 when it does
+     * happen, though it *could* be more).  The tag chars were already
+     * rendered into the 2nd line of the pair, but the positioning and
+     * other effects haven't been rippled through any other anchors on
+     * the (2nd) line.  So we do that here, as a special case, since
+     * the part of the tag that's in the 2nd line of the pair, will not
+     * be found by the tag string parsing code.  Double PITA.
+     *
+     * [see comments below on line crosser caused problems]
+     */
+    if (*lx_val != 0) {
+	nxt_anchor = st_anchor;
+	while ((nxt_anchor) && (nxt_anchor->line_num == a->line_num)) {
+	    nxt_anchor->line_pos += *lx_val;
+	    nxt_anchor = nxt_anchor->next;
+	}
+	fixup = *lx_val;
+	*lx_val = 0;
+	if (st_anchor)
+	    st_anchor = st_anchor->next;
+    }
+
+    /*
+     * Walk thru the line looking for tags (ie, "[nnn]" strings).
+     */
+    while (*p != '\0') {
+	if (*p != '[') {
+	    *s++ = *p++;
+	    continue;
+
+	} else {
+	    *s++ = *p++;
+	    t = p;
+	    n = 0;
+	    valid = TRUE;	/* p = t = byte after '[' */
+
+	    /*
+	     * Make sure there are only digits between "[" and "]".
+	     */
+	    while (*t != ']') {
+		if (*t == '\0') {	/* uhoh - we have a potential line crosser */
+		    valid = FALSE;
+		    plx = TRUE;
+		    break;
+		}
+		if (isdigit(UCH(*t++))) {
+		    n++;
+		    continue;
+		} else {
+		    valid = FALSE;
+		    break;
+		}
+	    }
+
+	    /*
+	     * If the format is OK, we check to see if the value is what
+	     * we expect.  If not, we have a random [nn] string in the text,
+	     * and leave it alone.
+	     *
+	     * [It is *possible* to have a false match here, *if* there are
+	     * two identical [nn] strings (including the numeric value of
+	     * nn), one of which is the [tag], and the other being part of
+	     * a document.  In such a case, the 1st [nn] string will get
+	     * incremented; the 2nd one won't, which makes it a 50-50 chance
+	     * of being correct, if and when such an unlikely juxtaposition
+	     * of text ever occurs.  Further validation tests of the [nnn]
+	     * string are probably not possible, since little of the actual
+	     * anchor-associated-text is retained in the TextAnchor or the
+	     * FormInfo structs.  Fortunately, I think the current method is
+	     * more than adequate to weed out 99.999% of any possible false
+	     * matches, just as it stands.  Caveat emptor.]
+	     */
+	    if ((valid) && (n > 0)) {
+		val = atoi(p);
+		if ((val == *old_val) || (*old_val == 0)) {	/* 0 matches all */
+		    if (*old_val != 0)
+			(*old_val)++;
+		    val += incr;
+		    sprintf(s, "%d", val);
+		    new_n = strlen(s);
+		    s += new_n;
+		    p += n;
+
+		    /*
+		     * If the number of digits in an existing [tag] increased
+		     * (eg, [99] --> [100], etc), we need to "adjust" its
+		     * horizontal position, and that of all subsequent tags
+		     * that may be on the same line.  PITA.
+		     *
+		     * [This seems to work as long as a tag isn't a line
+		     * crosser; when it is, the position of anchors on either
+		     * side of the split tag, seem to "float" and try to be
+		     * as "centered" as possible.  Which means that simply
+		     * incrementing the line_pos by the fixed value of the
+		     * number of digits that got added to some tag in either
+		     * line doesn't work quite right, and the text for (say)
+		     * a button may get stomped on by another copy of itself,
+		     * but offset by a few chars, when it is selected (eg,
+		     * "Box Office" may end up looking like "BoBox Office" or
+		     * "Box Officece", etc.
+		     *
+		     * Dunno how to fix that behavior ATT, but at least the
+		     * tag numbers themselves are correct.  -KED /\oo/\ ]
+		     */
+		    if ((new_n -= n) != 0) {
+			nxt_anchor = st_anchor;
+			while ((nxt_anchor) &&
+			       (nxt_anchor->line_num == a->line_num)) {
+			    nxt_anchor->line_pos += new_n;
+			    nxt_anchor = nxt_anchor->next;
+			}
+			if (st_anchor)
+			    st_anchor = st_anchor->next;
+		    }
+		}
+	    }
+
+	    /*
+	     * Unfortunately, valid [tag] strings *can* be split across two
+	     * lines.  Perhaps it would be best to just prevent that from
+	     * happening, but a look into that code, makes me wonder.  Anyway,
+	     * we can handle such tags without *too* much trouble in here [I
+	     * think], though since such animals are rather rare, it makes it
+	     * a bit difficult to test thoroughly (ie, Beyond here, there be
+	     * Dragons).
+	     *
+	     * We use lxbuf[] to deal with the two lines involved.
+	     */
+	    pre_n = strlen(p);	/* count of 1st part chars in this line */
+	    post_n = strlen(ht->next->data);
+	    if (plx
+		&& (pre_n + post_n + 2 < (int) sizeof(lxbuf))) {
+		strcpy(lx, p);	/* <- 1st part of a possible lx'ing tag */
+		strcat(lx, ht->next->data);	/* tack on NEXT line          */
+
+		t = lx;
+		n = 0;
+		valid = TRUE;
+
+		/*
+		 * Go hunting again for just digits, followed by tag end ']'.
+		 */
+		while (*t != ']') {
+		    if (isdigit(UCH(*t++))) {
+			n++;
+			continue;
+		    } else {
+			valid = FALSE;
+			break;
+		    }
+		}
+
+		/*
+		 * It *looks* like a line crosser; now we value test it to
+		 * find out for sure [but see the "false match" warning,
+		 * above], and if it matches, increment it into the buffer,
+		 * along with the 2nd line's text.
+		 */
+		if ((valid)
+		    && (n > 0)
+		    && (n + post_n + 2) < MAX_LINE) {
+		    val = atoi(lx);
+		    if ((val == *old_val) || (*old_val == 0)) {
+			if (*old_val != 0)
+			    (*old_val)++;
+			val += incr;
+			sprintf(lx, "%d", val);
+			new_n = strlen(lx);
+			strcat(lx, strchr(ht->next->data, ']'));
+
+			/*
+			 * We keep the the same number of chars from the
+			 * adjusted tag number in the current line; any
+			 * extra chars due to a digits increase, will be
+			 * stuffed into the next line.
+			 *
+			 * Keep track of any digits added, for the next
+			 * pass through.
+			 */
+			s = strncpy(s, lx, pre_n) + pre_n;
+			lx += pre_n;
+			strcpy(ht->next->data, lx);
+
+			*lx_val = new_n - n;
+		    }
+		}
+		break;		/* had an lx'er, so we're done with this line */
+	    }
+	}
+    }
+
+    *s = '\0';
+
+    n = strlen(ht->data);
+    if (mode == CHOP) {
+	*(buf + n) = '\0';
+    } else if (strlen(buf) > ht->size) {
+	/* we didn't allocate enough space originally - increase it */
+	HTLine *temp;
+
+	allocHTLine(temp, strlen(buf));
+	if (!temp)
+	    outofmem(__FILE__, "increment_tagged_htline");
+	assert(temp != NULL);
+
+	memcpy(temp, ht, LINE_SIZE(0));
+#if defined(USE_COLOR_STYLE)
+	POOLallocstyles(temp->styles, ht->numstyles);
+	if (!temp->styles)
+	    outofmem(__FILE__, "increment_tagged_htline");
+	memcpy(temp->styles, ht->styles, sizeof(HTStyleChange) * ht->numstyles);
+#endif
+	ht = temp;
+	ht->prev->next = ht;	/* Link in new line */
+	ht->next->prev = ht;	/* Could be same node of course */
+    }
+    strcpy(ht->data, buf);
+
+    return (strlen(buf) - n + fixup);
+}
+
+/*
+ * Creates a new anchor and associated struct's appropriate for a form
+ * TEXTAREA, and links them into the lists following the current anchor
+ * position (as specified by arg1).
+ *
+ * Exits with arg1 now pointing at the new TextAnchor, and arg2 pointing
+ * at the new, associated HTLine.
+ *
+ * --KED 02/13/99
+ */
+static void insert_new_textarea_anchor(TextAnchor **curr_anchor, HTLine **exit_htline)
+{
+    TextAnchor *anchor = *curr_anchor;
+    HTLine *htline;
+
+    TextAnchor *a = 0;
+    FormInfo *f = 0;
+    HTLine *l = 0;
+
+    int curr_tag = 0;		/* 0 ==> match any [tag] number */
+    int lx = 0;			/* 0 ==> no line crossing [tag]; it's a new line */
+    int i;
+
+    /*
+     * Find line in the text that matches ending anchorline of
+     * the TEXTAREA.
+     *
+     * [Yes, Virginia ...  we *do* have to go thru this for each
+     * anchor being added, since there is NOT a 1-to-1 mapping
+     * between anchors and htlines.  I suppose we could create
+     * YAS (Yet Another Struct), but there are too many structs{}
+     * floating around in here, as it is.  IMNSHO.]
+     */
+    for (htline = FirstHTLine(HTMainText), i = 0;
+	 anchor->line_num != i; i++) {
+	htline = htline->next;
+	if (htline == HTMainText->last_line)
+	    break;
+    }
+
+    /*
+     * Clone and initialize the struct's needed to add a new TEXTAREA
+     * anchor.
+     */
+    allocHTLine(l, MAX_LINE);
+    POOLtypecalloc(TextAnchor, a);
+
+    POOLtypecalloc(FormInfo, f);
+    if (a == NULL || l == NULL || f == NULL)
+	outofmem(__FILE__, "insert_new_textarea_anchor");
+
+    assert(a != NULL);
+    assert(f != NULL);
+    assert(l != NULL);
+
+    /*  Init all the fields in the new TextAnchor.                 */
+    /*  [anything "special" needed based on ->show_anchor value ?] */
+    a->next = anchor->next;
+    a->number = anchor->number;
+    a->line_pos = anchor->line_pos;
+    a->extent = anchor->extent;
+    a->sgml_offset = SGML_offset();
+    a->line_num = anchor->line_num + 1;
+    LYCopyHiText(a, anchor);
+    a->link_type = anchor->link_type;
+    a->input_field = f;
+    a->show_anchor = anchor->show_anchor;
+    a->inUnderline = anchor->inUnderline;
+    a->expansion_anch = TRUE;
+    a->anchor = NULL;
+
+    /*  Just the (seemingly) relevant fields in the new FormInfo.  */
+    /*  [do we need to do anything "special" based on ->disabled]  */
+    StrAllocCopy(f->name, anchor->input_field->name);
+    f->number = anchor->input_field->number;
+    f->type = anchor->input_field->type;
+    StrAllocCopy(f->orig_value, "");
+    f->size = anchor->input_field->size;
+    f->maxlength = anchor->input_field->maxlength;
+    f->no_cache = anchor->input_field->no_cache;
+    f->disabled = anchor->input_field->disabled;
+    f->value_cs = current_char_set;	/* use current setting - kw */
+
+    /*  Init all the fields in the new HTLine (but see the #if).   */
+    l->next = htline->next;
+    l->prev = htline;
+    l->offset = htline->offset;
+    l->size = htline->size;
+#if defined(USE_COLOR_STYLE)
+    /* dup styles[] if needed [no need in TEXTAREA (?); leave 0's] */
+    l->numstyles = htline->numstyles;
+    /*we fork the pointers! */
+    l->styles = htline->styles;
+#endif
+    strcpy(l->data, htline->data);
+
+    /*
+     * Link in the new HTLine.
+     */
+    htline->next->prev = l;
+    htline->next = l;
+
+    if (fields_are_numbered()) {
+	a->number++;
+	increment_tagged_htline(l, a, &lx, &curr_tag, 1, CHOP);
+    }
+
+    /*
+     * If we're at the tail end of the TextAnchor or HTLine list(s),
+     * the new node becomes the last node.
+     */
+    if (anchor == HTMainText->last_anchor)
+	HTMainText->last_anchor = a;
+    if (htline == HTMainText->last_line)
+	HTMainText->last_line = l;
+
+    /*
+     * Link in the new TextAnchor and point the entry anchor arg at it;
+     * point the entry HTLine arg at it, too.
+     */
+    anchor->next = a;
+    *curr_anchor = a;
+
+    *exit_htline = l->next;
+
+    return;
+}
+
+/*
+ * If new anchors were added to expand a TEXTAREA, we need to ripple the
+ * new line numbers [and char counts ?] thru the subsequent anchors.
+ *
+ * If form lines are getting [nnn] tagged, we need to update the displayed
+ * tag values to match (which means rerendering them ...  sigh).
+ *
+ * Finally, we need to update various HTMainText and other counts, etc.
+ *
+ * [dunno if the char counts really *need* to be done, or if we're using
+ * the exactly proper values/algorithms ...  seems to be OK though ...]
+ *
+ * --KED 02/13/99
+ */
+static void update_subsequent_anchors(int newlines,
+				      TextAnchor *start_anchor,
+				      HTLine *start_htline,
+				      int start_tag)
+{
+    TextAnchor *anchor;
+    HTLine *htline = start_htline;
+
+    int line_adj = 0;
+    int tag_adj = 0;
+    int lx = 0;
+    int hang = 0;		/* for HANG detection of a nasty intermittent */
+    int hang_detect = 100000;	/* ditto */
+
+    CTRACE((tfp, "GridText: adjusting struct's to add %d new line(s)\n", newlines));
+
+    /*
+     * Update numeric fields of the rest of the anchors.
+     *
+     * [We bypass bumping ->number if it has a value of 0, which takes care
+     * of the ->input_field->type == F_HIDDEN_TYPE (as well as any other
+     * "hidden" anchors, if such things exist).  Seems like the "right
+     * thing" to do.  I think.]
+     */
+    anchor = start_anchor->next;	/* begin updating with the NEXT anchor */
+    while (anchor) {
+	if (fields_are_numbered() &&
+	    (anchor->number != 0))
+	    anchor->number += newlines;
+	anchor->line_num += newlines;
+	anchor = anchor->next;
+    }
+
+    /*
+     * Update/rerender anchor [tags], if they are being numbered.
+     *
+     * [If a number tag (eg, "[177]") is itself broken across a line
+     * boundary, this fixup only partially works.  While the tag
+     * numbering is done properly across the pair of lines, the
+     * horizontal positioning on *either* side of the split, can get
+     * out of sync by a char or two when it gets selected.  See the
+     * [comments] in increment_tagged_htline() for some more detail.
+     *
+     * I suppose THE fix is to prevent such tag-breaking in the first
+     * place (dunno where yet, though).  Ah well ...  at least the tag
+     * numbers themselves are correct from top to bottom now.
+     *
+     * All that said, about the only time this will be a problem in
+     * *practice*, is when a page has near 1000 links or more (possibly
+     * after a TEXTAREA expansion), and has line crossing tag(s), and
+     * the tag numbers in a line crosser go from initially all 3 digit
+     * numbers, to some mix of 3 and 4 digits (or all 4 digits) as a
+     * result of the expansion process.  Oh, you also need a "clump" of
+     * anchors all on the same lines.
+     *
+     * Yes, it *can* happen, but in real life, it probably won't be
+     * seen very much ...]
+     *
+     * [This may also be an artifact of bumping into the right hand
+     * screen edge (or RHS margin), since we don't even *think* about
+     * relocating an anchor to the following line, when [tag] digits
+     * expansion pushes things too far in that direction.]
+     */
+    if (fields_are_numbered()) {
+	anchor = start_anchor->next;
+	while (htline != FirstHTLine(HTMainText)) {
+
+	    while (anchor) {
+		if ((anchor->number - newlines) == start_tag)
+		    break;
+
+		/*** A HANG (infinite loop) *has* occurred here, with */
+		/*** the values of anchor and anchor->next being the  */
+		/*** the same, OR with anchor->number "magically" and */
+		/*** suddenly taking on an anchor-pointer-like value. */
+		/***                                                  */
+		/*** The same code and same doc have both passed and  */
+		/*** failed at different times, which indicates some  */
+		/*** sort of content/html dependency, or some kind of */
+		/*** a "race" condition, but I'll be damned if I can  */
+		/*** find it after tons of CTRACE's, printf()'s, gdb  */
+		/*** breakpoints and watchpoints, etc.                */
+		/***                                                  */
+		/*** I have added a hang detector (with error msg and */
+		/*** beep) here, to break the loop and warn the user, */
+		/*** until it can be isolated and fixed.              */
+		/***                                                  */
+		/*** [One UGLY intermittent .. gak ..!  02/22/99 KED] */
+
+		hang++;
+		if ((anchor == anchor->next) || (hang >= hang_detect))
+		    goto hang_detected;
+
+		anchor = anchor->next;
+	    }
+
+	    if (anchor) {
+		line_adj = increment_tagged_htline(htline, anchor, &lx,
+						   &start_tag, newlines,
+						   NOCHOP);
+		htline->size += line_adj;
+		tag_adj += line_adj;
+
+	    } else {
+
+		break;		/* out of anchors ... we're done */
+	    }
+
+	    htline = htline->next;
+	}
+    }
+
+  finish:
+    /*
+     * Fixup various global variables.
+     */
+    nlinks += newlines;
+    HTMainText->Lines += newlines;
+    HTMainText->last_anchor_number += newlines;
+
+    more_text = HText_canScrollDown();
+
+    CTRACE((tfp, "GridText: TextAnchor and HTLine struct's adjusted\n"));
+
+    return;
+
+  hang_detected:		/* ugliness has happened; inform user and do the best we can */
+
+    HTAlert(gettext("Hang Detect: TextAnchor struct corrupted - suggest aborting!"));
+    goto finish;
+}
+
+/*
+ * Check if the given anchor is a TEXTAREA belonging to the given form.
+ *
+ * KED's note -
+ * [Finding the TEXTAREA we're actually *in* with these attributes isn't
+ * foolproof.  The form number isn't unique to a given TEXTAREA, and there
+ * *could* be TEXTAREA's with the same "name".  If that should ever be true,
+ * we'll actually get the data from the *1st* TEXTAREA in the page that
+ * matches.  We should probably assign a unique id to each TEXTAREA in a page,
+ * and match on that, to avoid this (potential) problem.
+ *
+ * Since the odds of "false matches" *actually* happening in real life seem
+ * rather small though, we'll hold off doing this, for a rainy day ...]
+ */
+static BOOLEAN IsFormsTextarea(FormInfo * form, TextAnchor *anchor_ptr)
+{
+    return (BOOLEAN) ((anchor_ptr->link_type == INPUT_ANCHOR) &&
+		      (anchor_ptr->input_field->type == F_TEXTAREA_TYPE) &&
+		      (anchor_ptr->input_field->number == form->number) &&
+		      !strcmp(anchor_ptr->input_field->name, form->name));
+}
+
+static int finish_ExtEditForm(LinkInfo * form_link, TextAnchor *start_anchor,
+			      char *ed_temp,
+			      int orig_cnt)
+{
+    struct stat stat_info;
+    size_t size;
+
+    FILE *fp;
+
+    TextAnchor *anchor_ptr;
+    TextAnchor *end_anchor = NULL;
+    BOOLEAN wrapalert = FALSE;
+
+    int entry_line = form_link->anchor_line_num;
+    int exit_line = 0;
+    int line_cnt = 1;
+
+    HTLine *htline = NULL;
+
+    char *ebuf;
+    char *line;
+    char *lp;
+    char *cp;
+    int match_tag = 0;
+    int newlines = 0;
+    int len, len0;
+    int wanted_fieldlen_wrap = -1;	/* not yet asked; 0 means don't. */
+    char *skip_at = NULL;
+    int skip_num = 0, i;
+
+    CTRACE((tfp, "GridText: entered HText_ExtEditForm()\n"));
+
+    /*
+     * Read back the edited temp file into our buffer.
+     */
+    if ((stat(ed_temp, &stat_info) < 0) ||
+	!S_ISREG(stat_info.st_mode) ||
+	((size = stat_info.st_size) == 0)) {
+	size = 0;
+	ebuf = typecalloc(char);
+
+	if (!ebuf)
+	    outofmem(__FILE__, "HText_ExtEditForm");
+
+	assert(ebuf != NULL);
+    } else {
+	ebuf = typecallocn(char, size + 1);
+
+	if (!ebuf) {
+	    /*
+	     * This could be huge - don't exit if we don't have enough
+	     * memory for it.  With some luck, the user may be even able
+	     * to recover the file manually from the temp space while
+	     * the lynx session is not over.  - kw
+	     */
+	    HTAlwaysAlert(NULL, MEMORY_EXHAUSTED_FILE);
+	    return 0;
+	}
+	assert(ebuf != NULL);
+
+	fp = fopen(ed_temp, "r");
+	size = fread(ebuf, 1, size, fp);
+	LYCloseInput(fp);
+	ebuf[size] = '\0';	/* Terminate! - kw */
+    }
+
+    /*
+     * Nuke any blank lines from the end of the edited data.
+     */
+    while ((size != 0)
+	   && (CanTrimTextArea(UCH(ebuf[size - 1])) || (ebuf[size - 1] == '\0')))
+	ebuf[--size] = '\0';
+
+    /*
+     * Copy each line from the temp file into the corresponding anchor
+     * struct.  Add new lines to the TEXTAREA if needed.  (Always leave
+     * the user with a blank line at the end of the TEXTAREA.)
+     */
+    if ((line = (char *) malloc(MAX_LINE)) == 0)
+	outofmem(__FILE__, "HText_ExtEditForm");
+
+    assert(line != NULL);
+
+    anchor_ptr = start_anchor;
+    if (anchor_ptr->input_field->size <= 4 ||
+	anchor_ptr->input_field->size >= MAX_LINE)
+	wanted_fieldlen_wrap = 0;
+
+    len = 0;
+    lp = ebuf;
+
+    while ((line_cnt <= orig_cnt) || (*lp) || ((len != 0) && (*lp == '\0'))) {
+
+	if (skip_at) {
+	    len0 = skip_at - lp;
+	    strncpy(line, lp, len0);
+	    line[len0] = '\0';
+	    lp = skip_at + skip_num;
+	    skip_at = NULL;
+	    skip_num = 0;
+
+	    assert(lp != NULL);
+	} else {
+	    len0 = 0;
+	}
+	line[len0] = '\0';
+
+	if ((cp = strchr(lp, '\n')) != 0)
+	    len = cp - lp;
+	else
+	    len = strlen(lp);
+
+	if (wanted_fieldlen_wrap < 0 && !wrapalert &&
+	    len0 + len >= start_anchor->input_field->size &&
+	    (cp = strchr(lp, ' ')) != NULL &&
+	    (cp - lp) < start_anchor->input_field->size - 1) {
+	    LYFixCursesOn("ask for confirmation:");
+	    LYerase();		/* don't show previous state */
+	    if (HTConfirmDefault(gettext("Wrap lines to fit displayed area?"),
+				 NO)) {
+		wanted_fieldlen_wrap = start_anchor->input_field->size - 1;
+	    } else {
+		wanted_fieldlen_wrap = 0;
+	    }
+	}
+	if (wanted_fieldlen_wrap > 0 && len0 + len > wanted_fieldlen_wrap) {
+	    for (i = wanted_fieldlen_wrap - len0;
+		 i + len0 >= wanted_fieldlen_wrap / 4; i--) {
+		if (isspace(UCH(lp[i]))) {
+		    len = i + 1;
+		    cp = lp + i;
+		    if (cp[1] != '\n' &&
+			isspace(UCH(cp[1])) &&
+			!isspace(UCH(cp[2]))) {
+			len++;
+			cp++;
+		    }
+		    if (!isspace(UCH(cp[1]))) {
+			while (*cp && *cp != '\r' && *cp != '\n' &&
+			       (cp - lp) <= len + (3 * wanted_fieldlen_wrap / 4))
+			    cp++;	/* search for next line break */
+			if (*cp == '\r' && cp[1] == '\n')
+			    cp++;
+			if (*cp == '\n' &&
+			    (cp[1] == '\r' || cp[1] == '\n' ||
+			     !isspace(UCH(cp[1])))) {
+			    *cp = ' ';
+			    while (isspace(UCH(*(cp - 1)))) {
+				skip_num++;
+				cp--;
+			    }
+			    skip_at = cp;
+			}
+		    }
+		    break;
+		}
+	    }
+	}
+	if (wanted_fieldlen_wrap > 0 && len0 + len > wanted_fieldlen_wrap) {
+	    i = len - 1;
+	    while (len0 + i + 1 > wanted_fieldlen_wrap &&
+		   isspace(UCH(lp[i])))
+		i--;
+	    if (len0 + i + 1 > wanted_fieldlen_wrap)
+		len = wanted_fieldlen_wrap - len0;
+	}
+
+	if (len0 + len >= MAX_LINE) {
+	    if (!wrapalert) {
+		LYFixCursesOn("show alert:");
+		HTAlert(gettext("Very long lines have been wrapped!"));
+		wrapalert = TRUE;
+	    }
+	    /*
+	     * First try to find a space character for wrapping - kw
+	     */
+	    for (i = MAX_LINE - len0 - 1; i > 0; i--) {
+		if (isspace(UCH(lp[i]))) {
+		    len = i;
+		    break;
+		}
+	    }
+	    if (len0 + len >= MAX_LINE)
+		len = MAX_LINE - len0 - 1;
+	}
+
+	strncat(line, lp, len);
+	*(line + len0 + len) = '\0';
+
+	/*
+	 * If there are more lines in the edit buffer than were in the
+	 * original TEXTAREA, we need to add a new line/anchor, continuing
+	 * on until the edit buffer is empty.
+	 */
+	if (line_cnt > orig_cnt) {
+	    insert_new_textarea_anchor(&end_anchor, &htline);
+
+	    assert(end_anchor != NULL);
+	    assert(end_anchor->input_field != NULL);
+
+	    anchor_ptr = end_anchor;	/* make the new anchor current */
+	    newlines++;
+	}
+
+	assert(anchor_ptr != NULL);
+
+	/*
+	 * Finally copy the new line from the edit buffer into the anchor.
+	 */
+	StrAllocCopy(anchor_ptr->input_field->value, line);
+
+	/*
+	 * Keep track of 1st blank line in any trailing blank lines, for
+	 * later cursor repositioning.
+	 */
+	if (len0 + len > 0)
+	    exit_line = 0;
+	else if (exit_line == 0)
+	    exit_line = anchor_ptr->line_num;
+
+	/*
+	 * And do the next line of edited text, for the next anchor ...
+	 */
+	lp += len;
+	if (*lp && isspace(UCH(*lp)))
+	    lp++;
+
+	end_anchor = anchor_ptr;
+	anchor_ptr = anchor_ptr->next;
+
+	if (anchor_ptr)
+	    match_tag = anchor_ptr->number;
+
+	line_cnt++;
+    }
+
+    CTRACE((tfp, "GridText: edited text inserted into lynx struct's\n"));
+
+    /*
+     * If we've added any new lines/anchors, we need to adjust various
+     * things in all anchor-bearing lines following the last newly added
+     * line/anchor.  The fun stuff starts here ...
+     */
+    if (newlines > 0)
+	update_subsequent_anchors(newlines, end_anchor, htline, match_tag);
+
+    /*
+     * Cleanup time.
+     */
+    FREE(line);
+    FREE(ebuf);
+
+    /*
+     * Return the offset needed to move the cursor from its current
+     * (on entry) line number, to the 1st blank line of the trailing
+     * (group of) blank line(s), which is where we want to be.  Let
+     * the caller deal with moving us there, however ...  :-) ...
+     */
+    return (exit_line - entry_line);
+}
+
+/*
+ * Transfer the initial contents of a TEXTAREA to a temp file, invoke the
+ * user's editor on that file, then transfer the contents of the resultant
+ * edited file back into the TEXTAREA (expanding the size of the area, if
+ * required).
+ *
+ * Returns the number of lines that the cursor should be moved so that it
+ * will end up on the 1st blank line of whatever number of trailing blank
+ * lines there are in the TEXTAREA (there will *always* be at least one).
+ *
+ * --KED 02/01/99
+ */
+int HText_ExtEditForm(LinkInfo * form_link)
+{
+    char *ed_temp;
+    FILE *fp;
+
+    TextAnchor *anchor_ptr;
+    TextAnchor *start_anchor = NULL;
+    BOOLEAN firstanchor = TRUE;
+
+    char ed_offset[10];
+    int start_line = 0;
+    int entry_line = form_link->anchor_line_num;
+    int orig_cnt = 0;
+    int offset;
+
+    FormInfo *form = form_link->l_form;
+
+    CTRACE((tfp, "GridText: entered HText_ExtEditForm()\n"));
+
+    ed_temp = (char *) malloc(LY_MAXPATH);
+    if ((fp = LYOpenTemp(ed_temp, "", "w")) == 0) {
+	FREE(ed_temp);
+	return (0);
+    }
+
+    /*
+     * Begin at the beginning, to find 1st anchor in the TEXTAREA, then
+     * write all of its lines (anchors) out to the edit temp file.
+     */
+    anchor_ptr = HTMainText->first_anchor;
+
+    while (anchor_ptr) {
+
+	if (IsFormsTextarea(form, anchor_ptr)) {
+
+	    if (firstanchor) {
+		firstanchor = FALSE;
+		start_anchor = anchor_ptr;
+		start_line = anchor_ptr->line_num;
+	    }
+	    orig_cnt++;
+
+	    /*
+	     * Write the anchors' text to the temp edit file.
+	     */
+	    fputs(anchor_ptr->input_field->value, fp);
+	    fputc('\n', fp);
+
+	} else {
+
+	    if (!firstanchor)
+		break;
+	}
+	anchor_ptr = anchor_ptr->next;
+    }
+    LYCloseTempFP(fp);
+
+    CTRACE((tfp, "GridText: TEXTAREA name=|%s| dumped to tempfile\n", form->name));
+    CTRACE((tfp, "GridText: invoking editor (%s) on tempfile\n", editor));
+
+    /*
+     * Go edit the TEXTAREA temp file, with the initial editor line
+     * corresponding to the TEXTAREA line the cursor is on (if such
+     * positioning is supported by the editor [as lynx knows it]).
+     */
+    ed_offset[0] = 0;		/* pre-ANSI compilers don't initialize aggregates - TD */
+    if (((entry_line - start_line) > 0) && editor_can_position())
+	sprintf(ed_offset, "%d", ((entry_line - start_line) + 1));
+
+    edit_temporary_file(ed_temp, ed_offset, NULL);
+
+    CTRACE((tfp, "GridText: returned from editor (%s)\n", editor));
+
+    if (form->disabled)
+	offset = 0;
+    else
+	offset = finish_ExtEditForm(form_link, start_anchor, ed_temp, orig_cnt);
+
+    LYRemoveTemp(ed_temp);
+    FREE(ed_temp);
+
+    CTRACE((tfp, "GridText: exiting HText_ExtEditForm()\n"));
+
+    /*
+     * Return the offset needed to move the cursor from its current
+     * (on entry) line number, to the 1st blank line of the trailing
+     * (group of) blank line(s), which is where we want to be.  Let
+     * the caller deal with moving us there, however ...  :-) ...
+     */
+    return offset;
+}
+
+/*
+ * Expand the size of a TEXTAREA by a fixed number of lines (as specified
+ * by arg2).
+ *
+ * --KED 02/14/99
+ */
+void HText_ExpandTextarea(LinkInfo * form_link, int newlines)
+{
+    TextAnchor *anchor_ptr;
+    TextAnchor *end_anchor = NULL;
+    BOOLEAN firstanchor = TRUE;
+
+    FormInfo *form = form_link->l_form;
+
+    HTLine *htline = NULL;
+
+    int match_tag = 0;
+    int i;
+
+    CTRACE((tfp, "GridText: entered HText_ExpandTextarea()\n"));
+
+    if (newlines < 1)
+	return;
+
+    /*
+     * Begin at the beginning, to find the TEXTAREA, then on to find
+     * the last line (anchor) in it.
+     */
+    anchor_ptr = HTMainText->first_anchor;
+
+    while (anchor_ptr) {
+
+	if (IsFormsTextarea(form, anchor_ptr)) {
+
+	    if (firstanchor)
+		firstanchor = FALSE;
+
+	    end_anchor = anchor_ptr;
+
+	} else {
+
+	    if (!firstanchor)
+		break;
+	}
+	anchor_ptr = anchor_ptr->next;
+    }
+
+    for (i = 1; i <= newlines; i++) {
+	insert_new_textarea_anchor(&end_anchor, &htline);
+
+	/*
+	 * Make the new line blank.
+	 */
+	StrAllocCopy(end_anchor->input_field->value, "");
+
+	/*
+	 * And go add another line ...
+	 */
+	if (end_anchor->next)
+	    match_tag = end_anchor->next->number;
+    }
+
+    CTRACE((tfp, "GridText: %d blank line(s) added to TEXTAREA name=|%s|\n",
+	    newlines, form->name));
+
+    /*
+     * We need to adjust various things in all anchor bearing lines
+     * following the last newly added line/anchor.  Fun stuff.
+     */
+    update_subsequent_anchors(newlines, end_anchor, htline, match_tag);
+
+    CTRACE((tfp, "GridText: exiting HText_ExpandTextarea()\n"));
+
+    return;
+}
+
+/*
+ * Insert the contents of a file into a TEXTAREA between the cursor line,
+ * and the line preceding it.
+ *
+ * Returns the number of lines that the cursor should be moved so that it
+ * will end up on the 1st line in the TEXTAREA following the inserted file
+ * (if we decide to do that).
+ *
+ * --KED 02/21/99
+ */
+int HText_InsertFile(LinkInfo * form_link)
+{
+    struct stat stat_info;
+    size_t size;
+
+    FILE *fp;
+    char *fn;
+
+    TextAnchor *anchor_ptr;
+    TextAnchor *prev_anchor = NULL;
+    TextAnchor *end_anchor = NULL;
+    BOOLEAN firstanchor = TRUE;
+    BOOLEAN truncalert = FALSE;
+
+    FormInfo *form = form_link->l_form;
+
+    HTLine *htline = NULL;
+
+    TextAnchor *a = 0;
+    FormInfo *f = 0;
+    HTLine *l = 0;
+
+    char *fbuf;
+    char *line;
+    char *lp;
+    char *cp;
+    int entry_line = form_link->anchor_line_num;
+    int file_cs;
+    int match_tag = 0;
+    int newlines = 0;
+    int len;
+    int i;
+
+    CTRACE((tfp, "GridText: entered HText_InsertFile()\n"));
+
+    /*
+     * Get the filename of the insert file.
+     */
+    if (!(fn = GetFileName())) {
+	HTInfoMsg(FILE_INSERT_CANCELLED);
+	CTRACE((tfp,
+		"GridText: file insert cancelled - no filename provided\n"));
+	return (0);
+    }
+    if (no_dotfiles || !show_dotfiles) {
+	if (*LYPathLeaf(fn) == '.') {
+	    HTUserMsg(FILENAME_CANNOT_BE_DOT);
+	    return (0);
+	}
+    }
+
+    /*
+     * Read it into our buffer (abort on 0-length file).
+     */
+    if ((stat(fn, &stat_info) < 0) ||
+	((size = (size_t) stat_info.st_size) == 0)) {
+	HTInfoMsg(FILE_INSERT_0_LENGTH);
+	CTRACE((tfp,
+		"GridText: file insert aborted - file=|%s|- was 0-length\n",
+		fn));
+	FREE(fn);
+	return (0);
+
+    } else {
+
+	if ((fbuf = typecallocn(char, size + 1)) == NULL) {
+	    /*
+	     * This could be huge - don't exit if we don't have enough
+	     * memory for it.  - kw
+	     */
+	    free(fn);
+	    HTAlert(MEMORY_EXHAUSTED_FILE);
+	    return 0;
+	}
+
+	/* Try to make the same assumption for the charset of the inserted
+	 * file as we would for normal loading of that file, i.e. taking
+	 * assume_local_charset and suffix mappings into account.
+	 * If there is a mismatch with the display character set, characters
+	 * may be displayed wrong, too bad; but the user has a chance to
+	 * correct this by editing the lines, which will update f->value_cs
+	 * again. - kw
+	 */
+	LYGetFileInfo(fn, 0, 0, 0, 0, 0, &file_cs);
+
+	fp = fopen(fn, "r");
+	if (!fp) {
+	    free(fbuf);
+	    free(fn);
+	    HTAlert(FILE_CANNOT_OPEN_R);
+	    return 0;
+	}
+	size = fread(fbuf, 1, size, fp);
+	LYCloseInput(fp);
+	FREE(fn);
+	fbuf[size] = '\0';	/* Terminate! - kw */
+    }
+
+    /*
+     * Begin at the beginning, to find the TEXTAREA we're in, then
+     * the current cursorline.
+     */
+    anchor_ptr = HTMainText->first_anchor;
+
+    while (anchor_ptr) {
+
+	if (IsFormsTextarea(form, anchor_ptr)) {
+	    if (anchor_ptr->line_num == entry_line)
+		break;
+	}
+	prev_anchor = anchor_ptr;
+	anchor_ptr = anchor_ptr->next;
+    }
+
+    if (anchor_ptr == NULL) {
+	CTRACE((tfp, "BUG: could not find anchor for TEXTAREA.\n"));
+	return 0;
+    }
+
+    /*
+     * Clone a new TEXTAREA line/anchor using the cursorline anchor as
+     * a template, but link it in BEFORE the cursorline anchor/htline.
+     *
+     * [We can probably combine this with insert_new_textarea_anchor()
+     * along with a flag to indicate "insert before" as we do here,
+     * or the "normal" mode of operation (add after "current" anchor/
+     * line).  Beware of the differences ...  some are a bit subtle to
+     * notice.]
+     */
+    for (htline = FirstHTLine(HTMainText), i = 0;
+	 anchor_ptr->line_num != i; i++) {
+	htline = htline->next;
+	if (htline == HTMainText->last_line)
+	    break;
+    }
+
+    allocHTLine(l, MAX_LINE);
+    POOLtypecalloc(TextAnchor, a);
+
+    POOLtypecalloc(FormInfo, f);
+    if (a == NULL || l == NULL || f == NULL)
+	outofmem(__FILE__, "HText_InsertFile");
+
+    assert(a != NULL);
+    assert(f != NULL);
+    assert(l != NULL);
+
+    /*  Init all the fields in the new TextAnchor.                 */
+    /*  [anything "special" needed based on ->show_anchor value ?] */
+    a->next = anchor_ptr;
+    a->number = anchor_ptr->number;
+    a->line_pos = anchor_ptr->line_pos;
+    a->extent = anchor_ptr->extent;
+    a->sgml_offset = SGML_offset();
+    a->line_num = anchor_ptr->line_num;
+    LYCopyHiText(a, anchor_ptr);
+    a->link_type = anchor_ptr->link_type;
+    a->input_field = f;
+    a->show_anchor = anchor_ptr->show_anchor;
+    a->inUnderline = anchor_ptr->inUnderline;
+    a->expansion_anch = TRUE;
+    a->anchor = NULL;
+
+    /*  Just the (seemingly) relevant fields in the new FormInfo.  */
+    /*  [do we need to do anything "special" based on ->disabled]  */
+    StrAllocCopy(f->name, anchor_ptr->input_field->name);
+    f->number = anchor_ptr->input_field->number;
+    f->type = anchor_ptr->input_field->type;
+    StrAllocCopy(f->orig_value, "");
+    f->size = anchor_ptr->input_field->size;
+    f->maxlength = anchor_ptr->input_field->maxlength;
+    f->no_cache = anchor_ptr->input_field->no_cache;
+    f->disabled = anchor_ptr->input_field->disabled;
+    f->value_cs = (file_cs >= 0) ? file_cs : current_char_set;
+
+    /*  Init all the fields in the new HTLine (but see the #if).   */
+    l->offset = htline->offset;
+    l->size = htline->size;
+#if defined(USE_COLOR_STYLE)
+    /* dup styles[] if needed [no need in TEXTAREA (?); leave 0's] */
+    l->numstyles = htline->numstyles;
+    /*we fork the pointers! */
+    l->styles = htline->styles;
+#endif
+    strcpy(l->data, htline->data);
+
+    /*
+     * If we're at the head of the TextAnchor list, the new node becomes
+     * the first node.
+     */
+    if (anchor_ptr == HTMainText->first_anchor)
+	HTMainText->first_anchor = a;
+
+    /*
+     * Link in the new TextAnchor, and corresponding HTLine.
+     */
+    if (prev_anchor)
+	prev_anchor->next = a;
+
+    htline = htline->prev;
+    l->next = htline->next;
+    l->prev = htline;
+    htline->next->prev = l;
+    htline->next = l;
+
+    /*
+     * update_subsequent_anchors() expects htline to point to 1st potential
+     * line needing fixup; we need to do this just in case the inserted file
+     * was only a single line (yes, it's pathological ...  ).
+     */
+    htline = htline->next;	/* ->new (current) htline, for 1st inserted line  */
+    htline = htline->next;	/* ->1st potential (following) [tag] fixup htline */
+
+    anchor_ptr = a;
+    newlines++;
+
+    /*
+     * Copy each line from the insert file into the corresponding anchor
+     * struct.
+     *
+     * Begin with the new line/anchor we just added (above the cursorline).
+     */
+    if ((line = (char *) malloc(MAX_LINE)) == 0)
+	outofmem(__FILE__, "HText_InsertFile");
+
+    assert(line != NULL);
+
+    match_tag = anchor_ptr->number;
+
+    lp = fbuf;
+
+    while (*lp) {
+
+	if ((cp = strchr(lp, '\n')) != 0)
+	    len = cp - lp;
+	else
+	    len = strlen(lp);
+
+	if (len >= MAX_LINE) {
+	    if (!truncalert) {
+		HTAlert(gettext("Very long lines have been truncated!"));
+		truncalert = TRUE;
+	    }
+	    len = MAX_LINE - 1;
+	    if (lp[len])
+		lp[len + 1] = '\0';	/* prevent next iteration */
+	}
+	strncpy(line, lp, len);
+	*(line + len) = '\0';
+
+	/*
+	 * If not the first line from the insert file, we need to add
+	 * a new line/anchor, continuing on until the buffer is empty.
+	 */
+	if (!firstanchor) {
+	    insert_new_textarea_anchor(&end_anchor, &htline);
+	    anchor_ptr = end_anchor;	/* make the new anchor current */
+	    newlines++;
+	}
+
+	/*
+	 * Copy the new line from the buffer into the anchor.
+	 */
+	StrAllocCopy(anchor_ptr->input_field->value, line);
+
+	/*
+	 * insert_new_textarea_anchor always uses current_char_set,
+	 * we may want something else, so fix it up.  - kw
+	 */
+	if (file_cs >= 0)
+	    anchor_ptr->input_field->value_cs = file_cs;
+
+	/*
+	 * And do the next line of insert text, for the next anchor ...
+	 */
+	lp += len;
+	if (*lp)
+	    lp++;
+
+	firstanchor = FALSE;
+	end_anchor = anchor_ptr;
+	anchor_ptr = anchor_ptr->next;
+    }
+
+    CTRACE((tfp, "GridText: file inserted into lynx struct's\n"));
+
+    /*
+     * Now adjust various things in all anchor-bearing lines following the
+     * last newly added line/anchor.  Some say this is the fun part ...
+     */
+    update_subsequent_anchors(newlines, end_anchor, htline, match_tag);
+
+    /*
+     * Cleanup time.
+     */
+    FREE(line);
+    FREE(fbuf);
+
+    CTRACE((tfp, "GridText: exiting HText_InsertFile()\n"));
+
+    return (newlines);
+}
+
+#ifdef USE_COLOR_STYLE
+static int GetColumn(void)
+{
+    int result;
+
+#ifdef USE_SLANG
+    result = SLsmg_get_column();
+#else
+    int y, x;
+
+    LYGetYX(y, x);
+    result = x;
+    (void) y;
+#endif
+    return result;
+}
+
+static BOOL DidWrap(int y0, int x0)
+{
+    BOOL result = NO;
+
+#ifndef USE_SLANG
+    int y, x;
+
+    LYGetYX(y, x);
+    (void) x0;
+    if (x >= DISPLAY_COLS || ((x == 0) && (y != y0)))
+	result = YES;
+#endif
+    return result;
+}
+#endif /* USE_COLOR_STYLE */
+
+/*
+ * This function draws the part of line 'line', pointed by 'str' (which can be
+ * non terminated with null - i.e., is line->data+N) drawing 'len' bytes (not
+ * characters) of it.  It doesn't check whether the 'len' bytes crosses a
+ * character boundary (if multibyte chars are in string).  Assumes that the
+ * cursor is positioned in the place where the 1st char of string should be
+ * drawn.
+ *
+ * This code is based on display_line.  This code was tested with ncurses only
+ * (since no support for lss is availble for Slang) -HV.
+ */
+#ifdef USE_COLOR_STYLE
+static void redraw_part_of_line(HTLine *line, const char *str,
+				int len,
+				HText *text)
+{
+    register int i;
+    char buffer[7];
+    const char *data, *end_of_data;
+    size_t utf_extra = 0;
+
+#ifdef USE_COLOR_STYLE
+    int current_style = 0;
+    int tcols, scols;
+#endif
+    char LastDisplayChar = ' ';
+    int YP, XP;
+
+    LYGetYX(YP, XP);
+
+    i = XP;
+
+    /* Set up the multibyte character buffer  */
+    buffer[0] = buffer[1] = buffer[2] = '\0';
+
+    data = str;
+    end_of_data = data + len;
+    i++;
+
+    /* this assumes that the part of line to be drawn fits in the screen */
+    while (data < end_of_data) {
+	buffer[0] = *data;
+	data++;
+
+#if defined(USE_COLOR_STYLE)
+#define CStyle line->styles[current_style]
+
+	tcols = GetColumn();
+	scols = StyleToCols(text, line, current_style);
+
+	while (current_style < line->numstyles &&
+	       tcols >= scols) {
+	    LynxChangeStyle(CStyle.sc_style, CStyle.sc_direction);
+	    current_style++;
+	    scols = StyleToCols(text, line, current_style);
+	}
+#endif
+	switch (buffer[0]) {
+
+#ifndef USE_COLOR_STYLE
+	case LY_UNDERLINE_START_CHAR:
+	    if (dump_output_immediately && use_underscore) {
+		LYaddch('_');
+		i++;
+	    } else {
+		lynx_start_underline();
+	    }
+	    break;
+
+	case LY_UNDERLINE_END_CHAR:
+	    if (dump_output_immediately && use_underscore) {
+		LYaddch('_');
+		i++;
+	    } else {
+		lynx_stop_underline();
+	    }
+	    break;
+
+	case LY_BOLD_START_CHAR:
+	    lynx_start_bold();
+	    break;
+
+	case LY_BOLD_END_CHAR:
+	    lynx_stop_bold();
+	    break;
+
+#endif
+	case LY_SOFT_NEWLINE:
+	    if (!dump_output_immediately) {
+		LYaddch('+');
+		i++;
+	    }
+	    break;
+
+	case LY_SOFT_HYPHEN:
+	    if (*data != '\0' ||
+		isspace(UCH(LastDisplayChar)) ||
+		LastDisplayChar == '-') {
+		/*
+		 * Ignore the soft hyphen if it is not the last character in
+		 * the line.  Also ignore it if it first character following
+		 * the margin, or if it is preceded by a white character (we
+		 * loaded 'M' into LastDisplayChar if it was a multibyte
+		 * character) or hyphen, though it should have been excluded by
+		 * HText_appendCharacter() or by split_line() in those cases. 
+		 * -FM
+		 */
+		break;
+	    } else {
+		/*
+		 * Make it a hard hyphen and fall through.  -FM
+		 */
+		buffer[0] = '-';
+	    }
+	    /* FALLTHRU */
+
+	default:
+	    if (text->T.output_utf8 && is8bits(buffer[0])) {
+		utf_extra = utf8_length(text->T.output_utf8, data - 1);
+		LastDisplayChar = 'M';
+	    }
+	    if (utf_extra) {
+		strncpy(&buffer[1], data, utf_extra);
+		buffer[utf_extra + 1] = '\0';
+		LYaddstr(buffer);
+		buffer[1] = '\0';
+		data += utf_extra;
+		utf_extra = 0;
+	    } else if (is_CJK2(buffer[0])) {
+		/*
+		 * For CJK strings, by Masanobu Kimura.
+		 */
+		if (i <= DISPLAY_COLS) {
+		    buffer[1] = *data;
+		    buffer[2] = '\0';
+		    data++;
+		    i++;
+		    LYaddstr(buffer);
+		    buffer[1] = '\0';
+		    /*
+		     * For now, load 'M' into LastDisplayChar, but we should
+		     * check whether it's white and if so, use ' '.  I don't
+		     * know if there actually are white CJK characters, and
+		     * we're loading ' ' for multibyte spacing characters in
+		     * this code set, but this will become an issue when the
+		     * development code set's multibyte character handling is
+		     * used.  -FM
+		     */
+		    LastDisplayChar = 'M';
+		}
+	    } else {
+		LYaddstr(buffer);
+		LastDisplayChar = buffer[0];
+	    }
+	    if (DidWrap(YP, XP))
+		break;
+	    i++;
+	}			/* end of switch */
+    }				/* end of while */
+
+#ifndef USE_COLOR_STYLE
+    lynx_stop_underline();
+    lynx_stop_bold();
+#else
+
+    while (current_style < line->numstyles) {
+	LynxChangeStyle(CStyle.sc_style, CStyle.sc_direction);
+	current_style++;
+    }
+
+#undef CStyle
+#endif
+    return;
+}
+#endif /* USE_COLOR_STYLE */
+
+#ifndef USE_COLOR_STYLE
+/*
+ * Function move_to_glyph is called from LYMoveToLink and does all
+ * the real work for it.
+ * The pair LYMoveToLink()/move_to_glyph() is similar to the pair
+ * redraw_lines_of_link()/redraw_part_of_line(), some key differences:
+ * LYMoveToLink/move_to_glyph redraw_*
+ * -----------------------------------------------------------------
+ * - used without color style           - used with color style
+ * - handles showing WHEREIS target     - WHEREIS handled elsewhere
+ * - handles only one line              - handles first two lines for
+ *                                        hypertext anchors
+ * - right columns position for UTF-8
+ *   by redrawing as necessary
+ * - currently used for highlight       - currently used for highlight
+ *   ON and OFF                         OFF
+ *
+ * Eventually the two sets of function should be unified, and should handle
+ * UTF-8 positioning, both lines of hypertext anchors, and WHEREIS in all
+ * cases.  If possible.  The complex WHEREIS target logic in LYhighlight()
+ * could then be completely removed.  - kw
+ */
+static void move_to_glyph(int YP,
+			  int XP,
+			  int XP_draw_min,
+			  const char *data,
+			  int datasize,
+			  unsigned offset,
+			  const char *target,
+			  const char *hightext,
+			  int flags,
+			  BOOL utf_flag)
+{
+    char buffer[7];
+    const char *end_of_data;
+    size_t utf_extra = 0;
+
+#if defined(SHOW_WHEREIS_TARGETS)
+    const char *cp_tgt;
+    int i_start_tgt = 0, i_after_tgt;
+    int HitOffset, LenNeeded;
+#endif /* SHOW_WHEREIS_TARGETS */
+    BOOL intarget = NO;
+    BOOL inunderline = NO;
+    BOOL inbold = NO;
+    BOOL drawing = NO;
+    BOOL inU = NO;
+    BOOL hadutf8 = NO;
+    BOOL incurlink = NO;
+    BOOL drawingtarget = NO;
+    BOOL flag = NO;
+    const char *sdata = data;
+    char LastDisplayChar = ' ';
+
+    int i = (int) offset;	/* FIXME: should be columns, not offset? */
+    int last_i = DISPLAY_COLS;
+    int XP_link = XP;		/* column of link */
+    int XP_next = XP;		/* column to move to when done drawing */
+    int linkvlen;
+
+    int len;
+
+    if (no_title)
+	YP -= TITLE_LINES;
+
+    if (flags & 1)
+	flag = YES;
+    if (flags & 2)
+	inU = YES;
+    /* Set up the multibyte character buffer  */
+    buffer[0] = buffer[1] = buffer[2] = '\0';
+    /*
+     * Add offset, making sure that we do not
+     * go over the COLS limit on the display.
+     */
+    if (hightext != 0) {
+#ifdef WIDEC_CURSES
+	last_i = i + LYstrExtent2(data, datasize);
+#endif
+	linkvlen = LYmbcsstrlen(hightext, utf_flag, YES);
+    } else {
+	linkvlen = 0;
+    }
+    if (i >= last_i)
+	i = last_i - 1;
+
+    /*
+     * Scan through the data, making sure that we do not
+     * go over the COLS limit on the display etc.
+     */
+    len = datasize;
+    end_of_data = data + len;
+
+#if defined(SHOW_WHEREIS_TARGETS)
+    /*
+     * If the target overlaps with the part of this line that
+     * we are drawing, it will be emphasized.
+     */
+    i_after_tgt = i;
+    if (target) {
+	cp_tgt = LYno_attr_mb_strstr(sdata,
+				     target,
+				     utf_flag, YES,
+				     &HitOffset,
+				     &LenNeeded);
+	if (cp_tgt) {
+	    if ((int) offset + LenNeeded > last_i ||
+		((int) offset + HitOffset >= XP + linkvlen)) {
+		cp_tgt = NULL;
+	    } else {
+		i_start_tgt = i + HitOffset;
+		i_after_tgt = i + LenNeeded;
+	    }
+	}
+    } else {
+	cp_tgt = NULL;
+    }
+#endif /* SHOW_WHEREIS_TARGETS */
+
+    /*
+     * Iterate through the line data from the start, keeping track of
+     * the display ("glyph") position in i.  Drawing will be turned
+     * on when either the first UTF-8 sequence (that occurs after
+     * XP_draw_min) is found, or when we reach the link itself (if
+     * highlight is non-NULL).  - kw
+     */
+    while ((i <= last_i) && data < end_of_data && (*data != '\0')) {
+
+	if (data && hightext && i >= XP && !incurlink) {
+
+	    /*
+	     * We reached the position of link itself, and hightext is
+	     * non-NULL.  We switch data from being a pointer into the HTLine
+	     * to being a pointer into hightext.  Normally (as long as this
+	     * routine is applied to normal hyperlink anchors) the text in
+	     * hightext will be identical to that part of the HTLine that
+	     * data was already pointing to, except that special attribute
+	     * chars LY_BOLD_START_CHAR etc., have been stripped out (see
+	     * HText_trimHightext).  So the switching should not result in
+	     * any different display, but it ensures that it doesn't go
+	     * unnoticed if somehow hightext got messed up somewhere else.
+	     * This is also useful in preparation for using this function
+	     * for something else than normal hyperlink anchors, i.e., form
+	     * fields.
+	     * Turn on drawing here or make sure it gets turned on before the
+	     * next actual normal character is handled.  - kw
+	     */
+	    data = hightext;
+	    len = strlen(hightext);
+	    end_of_data = hightext + len;
+	    last_i = i + len;
+	    XP_next += linkvlen;
+	    incurlink = YES;
+#ifdef SHOW_WHEREIS_TARGETS
+	    if (cp_tgt) {
+		if (flag && i_after_tgt >= XP)
+		    i_after_tgt = XP - 1;
+	    }
+#endif
+	    /*
+	     * The logic of where to set in-target drawing target etc.
+	     * and when to react to it should be cleaned up (here and
+	     * further below).  For now this seems to work but isn't
+	     * very clear.  The complications arise from reproducing
+	     * the behavior (previously done in LYhighlight()) for target
+	     * strings that fall into or overlap a link:  use target
+	     * emphasis for the target string, except for the first
+	     * and last character of the anchor text if the anchor is
+	     * highlighted as "current link".  - kw
+	     */
+	    if (!drawing) {
+#ifdef SHOW_WHEREIS_TARGETS
+		if (intarget) {
+		    if (i_after_tgt > i) {
+			LYmove(YP, i);
+			if (flag) {
+			    drawing = YES;
+			    drawingtarget = NO;
+			    if (inunderline)
+				inU = YES;
+			    lynx_start_link_color(flag, inU);
+			} else {
+			    drawing = YES;
+			    drawingtarget = YES;
+			    LYstartTargetEmphasis();
+			}
+		    }
+		}
+#endif /* SHOW_WHEREIS_TARGETS */
+	    } else {
+#ifdef SHOW_WHEREIS_TARGETS
+		if (intarget && i_after_tgt > i) {
+		    if (flag && (data == hightext)) {
+			drawingtarget = NO;
+			LYstopTargetEmphasis();
+		    }
+		} else if (!intarget)
+#endif /* SHOW_WHEREIS_TARGETS */
+		{
+		    if (inunderline)
+			inU = YES;
+		    if (inunderline)
+			lynx_stop_underline();
+		    if (inbold)
+			lynx_stop_bold();
+		    lynx_start_link_color(flag, inU);
+		}
+
+	    }
+	}
+	if (i >= last_i || data >= end_of_data)
+	    break;
+	if ((buffer[0] = *data) == '\0')
+	    break;
+#if defined(SHOW_WHEREIS_TARGETS)
+	/*
+	 * Look for a subsequent occurrence of the target string,
+	 * if we had a previous one and have now stepped past it.  - kw
+	 */
+	if (cp_tgt && i >= i_after_tgt) {
+	    if (intarget) {
+
+		if (incurlink && flag && i == last_i - 1)
+		    cp_tgt = NULL;
+		else
+		    cp_tgt = LYno_attr_mb_strstr(sdata,
+						 target,
+						 utf_flag, YES,
+						 &HitOffset,
+						 &LenNeeded);
+		if (cp_tgt) {
+		    i_start_tgt = i + HitOffset;
+		    i_after_tgt = i + LenNeeded;
+		    if (incurlink) {
+			if (flag && i_start_tgt == XP_link)
+			    i_start_tgt++;
+			if (flag && i_start_tgt == last_i - 1)
+			    i_start_tgt++;
+			if (flag && i_after_tgt >= last_i)
+			    i_after_tgt = last_i - 1;
+			if (flag && i_start_tgt >= last_i)
+			    cp_tgt = NULL;
+		    } else if (i_start_tgt == last_i) {
+			if (flag)
+			    i_start_tgt++;
+		    }
+		}
+		if (!cp_tgt || i_start_tgt != i) {
+		    intarget = NO;
+		    if (drawing) {
+			if (drawingtarget) {
+			    drawingtarget = NO;
+			    LYstopTargetEmphasis();
+			    if (incurlink) {
+				lynx_start_link_color(flag, inU);
+			    }
+			}
+			if (!incurlink) {
+			    if (inbold)
+				lynx_start_bold();
+			    if (inunderline)
+				lynx_start_underline();
+			}
+		    }
+		}
+	    }
+	}
+#endif /* SHOW_WHEREIS_TARGETS */
+
+	/*
+	 * Advance data to point to the next input char (for the
+	 * next round).  Advance sdata, used for searching for a
+	 * target string, so that they stay in synch.  As long
+	 * as we are not within the highlight text, data and sdata
+	 * have identical values.  After we have switched data to
+	 * point into hightext, sdata remains a pointer into the
+	 * HTLine (so that we don't miss a partial target match at
+	 * the end of the anchor text).  So sdata has to sometimes
+	 * skip additional special attribute characters that are
+	 * not present in highlight in order to stay in synch.  - kw
+	 */
+	data++;
+	if (incurlink) {
+	    while (IsNormalChar(*sdata)) {
+		++sdata;
+	    }
+	}
+
+	switch (buffer[0]) {
+
+	case LY_UNDERLINE_START_CHAR:
+	    if (!drawing || !incurlink)
+		inunderline = YES;
+	    if (drawing && !intarget && !incurlink)
+		lynx_start_underline();
+	    break;
+
+	case LY_UNDERLINE_END_CHAR:
+	    inunderline = NO;
+	    if (drawing && !intarget && !incurlink)
+		lynx_stop_underline();
+	    break;
+
+	case LY_BOLD_START_CHAR:
+	    if (!drawing || !incurlink)
+		inbold = YES;
+	    if (drawing && !intarget && !incurlink)
+		lynx_start_bold();
+	    break;
+
+	case LY_BOLD_END_CHAR:
+	    inbold = NO;
+	    if (drawing && !intarget && !incurlink)
+		lynx_stop_bold();
+	    break;
+
+	case LY_SOFT_NEWLINE:
+	    if (drawing) {
+		LYaddch('+');
+	    }
+	    i++;
+	    break;
+
+	case LY_SOFT_HYPHEN:
+	    if (*data != '\0' ||
+		isspace(UCH(LastDisplayChar)) ||
+		LastDisplayChar == '-') {
+		/*
+		 * Ignore the soft hyphen if it is not the last
+		 * character in the line.  Also ignore it if it
+		 * first character following the margin, or if it
+		 * is preceded by a white character (we loaded 'M'
+		 * into LastDisplayChar if it was a multibyte
+		 * character) or hyphen, though it should have
+		 * been excluded by HText_appendCharacter() or by
+		 * split_line() in those cases.  -FM
+		 */
+		break;
+	    } else {
+		/*
+		 * Make it a hard hyphen and fall through.  -FM
+		 */
+		buffer[0] = '-';
+	    }
+	    /* FALLTHRU */
+
+	default:
+	    /*
+	     * We have got an actual normal displayable character, or
+	     * the start of one.  Before proceeding check whether
+	     * drawing needs to be turned on now.  - kw
+	     */
+#if defined(SHOW_WHEREIS_TARGETS)
+	    if (incurlink && intarget && flag && i_after_tgt > i) {
+		if (i == last_i - 1) {
+		    i_after_tgt = i;
+		} else if (i == last_i - 2 && IS_CJK_TTY &&
+			   is8bits(buffer[0])) {
+		    i_after_tgt = i;
+		    cp_tgt = NULL;
+		    if (drawing) {
+			if (drawingtarget) {
+			    LYstopTargetEmphasis();
+			    drawingtarget = NO;
+			    lynx_start_link_color(flag, inU);
+			}
+		    }
+		}
+	    }
+	    if (cp_tgt && i >= i_start_tgt && sdata > cp_tgt) {
+		if (!intarget ||
+		    (intarget && incurlink && !drawingtarget)) {
+
+		    if (incurlink && drawing &&
+			!(flag &&
+			  (i == XP_link || i == last_i - 1))) {
+			lynx_stop_link_color(flag, inU);
+		    }
+		    if (incurlink && !drawing) {
+			LYmove(YP, i);
+			if (inunderline)
+			    inU = YES;
+			if (flag && (i == XP_link || i == last_i - 1)) {
+			    lynx_start_link_color(flag, inU);
+			    drawingtarget = NO;
+			} else {
+			    LYstartTargetEmphasis();
+			    drawingtarget = YES;
+			}
+			drawing = YES;
+		    } else if (incurlink && drawing &&
+			       intarget && !drawingtarget &&
+			       (flag &&
+				(i == XP_link))) {
+			if (inunderline)
+			    inU = YES;
+			lynx_start_link_color(flag, inU);
+		    } else if (drawing &&
+			       !(flag &&
+				 (i == XP_link || (incurlink && i == last_i - 1)))) {
+			LYstartTargetEmphasis();
+			drawingtarget = YES;
+		    }
+		    intarget = YES;
+		}
+	    } else
+#endif /* SHOW_WHEREIS_TARGETS */
+	    if (incurlink) {
+		if (!drawing) {
+		    LYmove(YP, i);
+		    if (inunderline)
+			inU = YES;
+		    lynx_start_link_color(flag, inU);
+		    drawing = YES;
+		}
+	    }
+
+	    i++;
+#ifndef WIDEC_CURSES
+	    if (utf_flag && is8bits(buffer[0])) {
+		hadutf8 = YES;
+		utf_extra = utf8_length(utf_flag, data - 1);
+		LastDisplayChar = 'M';
+	    }
+#endif
+	    if (utf_extra) {
+		strncpy(&buffer[1], data, utf_extra);
+		buffer[utf_extra + 1] = '\0';
+		if (!drawing && i >= XP_draw_min) {
+		    LYmove(YP, i - 1);
+		    drawing = YES;
+#if defined(SHOW_WHEREIS_TARGETS)
+		    if (intarget) {
+			drawingtarget = YES;
+			LYstartTargetEmphasis();
+		    } else
+#endif /* SHOW_WHEREIS_TARGETS */
+		    {
+			if (inbold)
+			    lynx_start_bold();
+			if (inunderline)
+			    lynx_start_underline();
+		    }
+		}
+		LYaddstr(buffer);
+		buffer[1] = '\0';
+		sdata += utf_extra;
+		data += utf_extra;
+		utf_extra = 0;
+	    } else if (IS_CJK_TTY && is8bits(buffer[0])) {
+		/*
+		 * For CJK strings, by Masanobu Kimura.
+		 */
+		if (drawing && (i <= last_i)) {
+		    buffer[1] = *data;
+		    LYaddstr(buffer);
+		    buffer[1] = '\0';
+		}
+		i++;
+		sdata++;
+		data++;
+		/*
+		 * For now, load 'M' into LastDisplayChar, but we should
+		 * check whether it's white and if so, use ' '.  I don't
+		 * know if there actually are white CJK characters, and
+		 * we're loading ' ' for multibyte spacing characters in
+		 * this code set, but this will become an issue when the
+		 * development code set's multibyte character handling is
+		 * used.  -FM
+		 */
+		LastDisplayChar = 'M';
+	    } else {
+		if (drawing) {
+		    LYaddstr(buffer);
+		}
+		LastDisplayChar = buffer[0];
+	    }
+	}			/* end of switch */
+    }				/* end of while */
+
+    if (!drawing) {
+	LYmove(YP, XP_next);
+	lynx_start_link_color(flag, inU);
+    } else {
+#if defined(SHOW_WHEREIS_TARGETS)
+	if (drawingtarget) {
+	    LYstopTargetEmphasis();
+	    lynx_start_link_color(flag, inU);
+	}
+#endif /* SHOW_WHEREIS_TARGETS */
+	if (hadutf8) {
+	    LYtouchline(YP);
+	}
+    }
+    return;
+}
+#endif /* !USE_COLOR_STYLE */
+
+#ifndef USE_COLOR_STYLE
+/*
+ * Move cursor position to a link's place in the display.
+ * The "moving to" is done by scanning through the line's
+ * character data in the corresponding HTLine of HTMainText,
+ * and starting to draw when a UTF-8 encoded non-ASCII character
+ * is encountered before the link (with some protection against
+ * overwriting form fields).  This refreshing of preceding data is
+ * necessary for preventing curses's or slang's display logic from
+ * getting too clever; their logic counts character positions wrong
+ * since they don't know about multi-byte characters that take up
+ * only one screen position.  So we have to make them forget their
+ * idea of what's in a screen line drawn previously.
+ * If hightext is non-NULL, it should be the anchor text for a normal
+ * link as stored in a links[] element, and the anchor text will be
+ * drawn too, with appropriate attributes.  - kw
+ */
+void LYMoveToLink(int cur,
+		  const char *target,
+		  const char *hightext,
+		  int flag,
+		  BOOL inU,
+		  BOOL utf_flag)
+{
+#define pvtTITLE_HEIGHT 1
+    HTLine *todr;
+    int i, n = 0;
+    int XP_draw_min = 0;
+    int flags = ((flag == ON) ? 1 : 0) | (inU ? 2 : 0);
+
+    /*
+     * We need to protect changed form text fields preceding this
+     * link on the same line against overwriting.  - kw
+     */
+    for (i = cur - 1; i >= 0; i++) {
+	if (links[i].ly < links[cur].ly)
+	    break;
+	if (links[i].type == WWW_FORM_LINK_TYPE) {
+	    XP_draw_min = links[i].ly + links[i].l_form->size;
+	    break;
+	}
+    }
+
+    /*  Find the right HTLine. */
+    if (!HTMainText) {
+	todr = NULL;
+    } else if (HTMainText->stale) {
+	todr = FirstHTLine(HTMainText);
+	n = links[cur].ly - pvtTITLE_HEIGHT + HTMainText->top_of_screen;
+    } else {
+	todr = HTMainText->top_of_screen_line;
+	n = links[cur].ly - pvtTITLE_HEIGHT;
+    }
+    for (i = 0; i < n && todr; i++) {
+	todr = (todr == HTMainText->last_line) ? NULL : todr->next;
+    }
+    if (todr) {
+	if (target && *target == '\0')
+	    target = NULL;
+	move_to_glyph(links[cur].ly, links[cur].lx, XP_draw_min,
+		      todr->data, todr->size, todr->offset,
+		      target, hightext, flags, utf_flag);
+    } else {
+	/*  This should not happen. */
+	move_to_glyph(links[cur].ly, links[cur].lx, XP_draw_min,
+		      "", 0, links[cur].lx,
+		      target, hightext, flags, utf_flag);
+    }
+}
+#endif /* !USE_COLOR_STYLE */
+
+/*
+ * This is used only if compiled with lss support.  It's called to redraw a
+ * regular link when it's being unhighlighted in LYhighlight().
+ */
+#ifdef USE_COLOR_STYLE
+void redraw_lines_of_link(int cur)
+{
+#define pvtTITLE_HEIGHT 1
+    HTLine *todr1;
+    int lines_back;
+    int row, col, count;
+    const char *text;
+
+    if (HTMainText->next_line == HTMainText->last_line) {
+	/* we are at the last page - that is partially filled */
+	lines_back = HTMainText->Lines - (links[cur].ly - pvtTITLE_HEIGHT +
+					  HTMainText->top_of_screen);
+    } else {
+	lines_back = display_lines - (links[cur].ly - pvtTITLE_HEIGHT);
+    }
+    todr1 = HTMainText->next_line;
+    while (lines_back-- > 0)
+	todr1 = todr1->prev;
+
+    row = links[cur].ly;
+    if (no_title)
+	row -= TITLE_LINES;
+
+    for (count = 0;
+	 row <= display_lines && (text = LYGetHiliteStr(cur, count)) != NULL;
+	 ++count) {
+	col = LYGetHilitePos(cur, count);
+	LYmove(row++, col);
+	redraw_part_of_line(todr1, text, (int) strlen(text), HTMainText);
+	todr1 = todr1->next;
+    }
+#undef pvtTITLE_HEIGHT
+    return;
+}
+#endif
+
+#ifdef USE_PRETTYSRC
+void HTMark_asSource(void)
+{
+    if (HTMainText)
+	HTMainText->source = TRUE;
+}
+#endif
+
+HTkcode HText_getKcode(HText *text)
+{
+    return text->kcode;
+}
+
+void HText_updateKcode(HText *text, HTkcode kcode)
+{
+    text->kcode = kcode;
+}
+
+HTkcode HText_getSpecifiedKcode(HText *text)
+{
+    return text->specified_kcode;
+}
+
+void HText_updateSpecifiedKcode(HText *text, HTkcode kcode)
+{
+    text->specified_kcode = kcode;
+}
+
+int HTMainText_Get_UCLYhndl(void)
+{
+    return (HTMainText ?
+	    HTAnchor_getUCLYhndl(HTMainText->node_anchor, UCT_STAGE_MIME)
+	    : -1);
+}
+
+#ifdef USE_CACHEJAR
+static int LYHandleCache(const char *arg,
+			 HTParentAnchor *anAnchor,
+			 HTFormat format_out,
+			 HTStream *sink)
+{
+    HTFormat format_in = WWW_HTML;
+    HTStream *target = NULL;
+    char c;
+    char *buf = NULL;
+    char *title = NULL;
+    char *address = NULL;
+    char *content_type = NULL;
+    char *content_language = NULL;
+    char *content_encoding = NULL;
+    char *content_location = NULL;
+    char *content_disposition = NULL;
+    char *content_md5 = NULL;
+    char *message_id = NULL;
+    char *date = NULL;
+    char *owner = NULL;
+    char *subject = NULL;
+    char *expires = NULL;
+    char *ETag = NULL;
+    char *server = NULL;
+    char *FileCache = NULL;
+    char *last_modified = NULL;
+    char *cache_control = NULL;
+
+#ifdef USE_SOURCE_CACHE
+    char *source_cache_file = NULL;
+#endif
+    int Size = 0;
+    int x = -1;
+
+    /*
+     * Check if there is something to do.
+     */
+    if (HTList_count(loaded_texts) == 0) {
+	HTProgress(CACHE_JAR_IS_EMPTY);
+	LYSleepMsg();
+	HTNoDataOK = 1;
+	return (HT_NO_DATA);
+    }
+
+    /*
+     * If # of LYNXCACHE:/# is number ask user if he/she want to delete it.
+     */
+    if (sscanf(arg, STR_LYNXCACHE "/%d", &x) == 1 && x > 0) {
+	CTRACE((tfp, "LYNXCACHE number is %d\n", x));
+	_statusline(CACHE_D_OR_CANCEL);
+	c = (char) LYgetch_single();
+	if (c == 'D') {
+	    HText *t = (HText *) HTList_objectAt(loaded_texts, x - 1);
+
+	    HTList_removeObjectAt(loaded_texts, x - 1);
+	    HText_free(t);
+	}
+	return (HT_NO_DATA);
+    }
+
+    /*
+     * If we get to here, it was a LYNXCACHE:/ URL for creating and displaying
+     * the Cache Jar Page.
+     * Set up an HTML stream and return an updated Cache Jar Page.
+     */
+    target = HTStreamStack(format_in,
+			   format_out,
+			   sink, anAnchor);
+    if (!target || target == NULL) {
+	HTSprintf0(&buf, CANNOT_CONVERT_I_TO_O,
+		   HTAtom_name(format_in), HTAtom_name(format_out));
+	HTAlert(buf);
+	FREE(buf);
+	return (HT_NOT_LOADED);
+    }
+
+    /*
+     * Load HTML strings into buf and pass buf to the target for parsing and
+     * rendering.
+     */
+#define PUTS(buf)    (*target->isa->put_block)(target, buf, (int) strlen(buf))
+
+    HTSprintf0(&buf,
+	       "<html>\n<head>\n<title>%s</title>\n</head>\n<body>\n",
+	       CACHE_JAR_TITLE);
+    PUTS(buf);
+    HTSprintf0(&buf, "<h1>%s (%s)%s<a href=\"%s%s\">%s</a></h1>\n",
+	       LYNX_NAME, LYNX_VERSION,
+	       HELP_ON_SEGMENT,
+	       helpfilepath, CACHE_JAR_HELP, CACHE_JAR_TITLE);
+    PUTS(buf);
+
+    /*
+     * Max number of cached documents is always same as HTCacheSize.
+     * We count them from oldest to newest. Currently cached document
+     * is *never* listed, resulting in maximal entries of Cache Jar
+     * to be HTCacheSize - 1
+     */
+    for (x = HTList_count(loaded_texts) - 1; x > 0; x--) {
+	/*
+	 * The number of the document in the cache list, its title in a link,
+	 * and its address and memory allocated for each cached document.
+	 */
+	HText *cachedoc = (HText *) HTList_objectAt(loaded_texts, x);
+
+	if (cachedoc != 0) {
+	    HTParentAnchor *docanchor = cachedoc->node_anchor;
+
+	    if (docanchor != 0) {
+#ifdef USE_SOURCE_CACHE
+		source_cache_file = docanchor->source_cache_file;
+#endif
+		Size = docanchor->content_length;
+		StrAllocCopy(title, docanchor->title);
+		StrAllocCopy(address, docanchor->address);
+		content_type = docanchor->content_type;
+		content_language = docanchor->content_language;
+		content_encoding = docanchor->content_encoding;
+		content_location = docanchor->content_location;
+		content_disposition = docanchor->content_disposition;
+		content_md5 = docanchor->content_md5;
+		message_id = docanchor->message_id;
+		owner = docanchor->owner;
+		StrAllocCopy(subject, docanchor->subject);
+		date = docanchor->date;
+		expires = docanchor->expires;
+		ETag = docanchor->ETag;
+		StrAllocCopy(server, docanchor->server);
+		FileCache = docanchor->FileCache;
+		last_modified = docanchor->last_modified;
+		cache_control = docanchor->cache_control;
+	    }
+	}
+
+	LYEntify(&address, TRUE);
+	if (isEmpty(title))
+	    StrAllocCopy(title, NO_TITLE);
+	else
+	    LYEntify(&title, TRUE);
+
+	HTSprintf0(&buf,
+		   "<p><em>%d.</em> Title: <a href=\"%s%d\">%s</a><br />URL: <a href=\"%s\">%s</a><br />",
+		   x, STR_LYNXCACHE, x, title, address, address);
+	PUTS(buf);
+	if (Size > 0) {
+	    HTSprintf0(&buf, "Size: %d  ", Size);
+	    PUTS(buf);
+	}
+	if (cachedoc != NULL && cachedoc->Lines > 0) {
+	    HTSprintf0(&buf, "Lines: %d  ", cachedoc->Lines);
+	    PUTS(buf);
+	}
+	if (FileCache != NULL) {
+	    HTSprintf0(&buf, "File-Cache: <a href=\"file://%s\">%s</a>  ",
+		       FileCache, FileCache);
+	    PUTS(buf);
+	}
+	if (cache_control != NULL) {
+	    HTSprintf0(&buf, "Cache-Control: %s  ", cache_control);
+	    PUTS(buf);
+	}
+	if (content_type != NULL) {
+	    HTSprintf0(&buf, "Content-Type: %s  ", content_type);
+	    PUTS(buf);
+	}
+	if (content_language != NULL) {
+	    HTSprintf0(&buf, "Content-Language: %s  ", content_language);
+	    PUTS(buf);
+	}
+	if (content_encoding != NULL) {
+	    HTSprintf0(&buf, "Content-Encoding: %s  ", content_encoding);
+	    PUTS(buf);
+	}
+	if (content_location != NULL) {
+	    HTSprintf0(&buf, "Content-Location: %s  ", content_location);
+	    PUTS(buf);
+	}
+	if (content_disposition != NULL) {
+	    HTSprintf0(&buf, "Content-Disposition: %s  ", content_disposition);
+	    PUTS(buf);
+	}
+	if (content_md5 != NULL) {
+	    HTSprintf0(&buf, "Content-MD5: %s  ", content_md5);
+	    PUTS(buf);
+	}
+	if (message_id != NULL) {
+	    HTSprintf0(&buf, "Message-ID: %s  ", message_id);
+	    PUTS(buf);
+	}
+	if (subject != NULL) {
+	    LYEntify(&subject, TRUE);
+	    HTSprintf0(&buf, "Subject: %s  ", subject);
+	    PUTS(buf);
+	}
+	if (owner != NULL) {
+	    HTSprintf0(&buf, "Owner: <a href=%s>%s</a>  ", owner, owner);
+	    PUTS(buf);
+	}
+	if (date != NULL) {
+	    HTSprintf0(&buf, "Date: %s  ", date);
+	    PUTS(buf);
+	}
+	if (expires != NULL) {
+	    HTSprintf0(&buf, "Expires: %s  ", expires);
+	    PUTS(buf);
+	}
+	if (last_modified != NULL) {
+	    HTSprintf0(&buf, "Last-Modified: %s  ", last_modified);
+	    PUTS(buf);
+	}
+	if (ETag != NULL) {
+	    HTSprintf0(&buf, "ETag: %s  ", ETag);
+	    PUTS(buf);
+	}
+	if (server != NULL) {
+	    LYEntify(&server, TRUE);
+	    HTSprintf0(&buf, "Server: <em>%s</em>  ", server);
+	    PUTS(buf);
+	}
+#ifdef USE_SOURCE_CACHE
+	if (source_cache_file != NULL) {
+	    HTSprintf0(&buf,
+		       "Source-Cache-File: <a href=\"file://%s\">%s</a>",
+		       source_cache_file, source_cache_file);
+	    PUTS(buf);
+	}
+#endif
+	HTSprintf0(&buf, "<br />");
+	PUTS(buf);
+    }
+    HTSprintf0(&buf, "</body></html>");
+    PUTS(buf);
+    FREE(subject);
+    FREE(title);
+    FREE(address);
+    FREE(server);
+
+    /*
+     * Free the target to complete loading of the Cache Jar Page, and report a
+     * successful load.
+     */
+    (*target->isa->_free) (target);
+    FREE(buf);
+    return (HT_LOADED);
+}
+
+#ifdef GLOBALDEF_IS_MACRO
+#define _LYCACHE_C_GLOBALDEF_1_INIT { "LYNXCACHE",LYHandleCache,0}
+GLOBALDEF(HTProtocol, LYLynxCache, _LYCACHE_C_GLOBALDEF_1_INIT);
+#else
+GLOBALDEF HTProtocol LYLynxCache =
+{"LYNXCACHE", LYHandleCache, 0};
+#endif /* GLOBALDEF_IS_MACRO */
+#endif /* USE_CACHEJAR */
diff --git a/src/GridText.h b/src/GridText.h
new file mode 100644
index 00000000..a3ed2b6e
--- /dev/null
+++ b/src/GridText.h
@@ -0,0 +1,292 @@
+/*
+ * $LynxId: GridText.h,v 1.61 2009/01/01 23:02:02 tom Exp $
+ *
+ * Specialities of GridText as subclass of HText
+ */
+#ifndef LYGRIDTEXT_H
+#define LYGRIDTEXT_H
+
+#include <HText.h>		/* Superclass */
+
+#ifndef HTFORMS_H
+#include <HTForms.h>
+#endif /* HTFORMS_H */
+
+#include <HTFont.h>
+
+#include <HTCJK.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#define TABSTOP 8
+#define SPACES  "        "	/* must be at least TABSTOP spaces long */
+#define SPLAT   '.'
+#define NOCHOP 0
+#define CHOP   1
+/* just for information:
+US-ASCII control characters <32 which are not defined in Unicode standard
+=00	U+0000	NULL
+=01	U+0001	START OF HEADING
+=02	U+0002	START OF TEXT
+=03	U+0003	END OF TEXT
+=04	U+0004	END OF TRANSMISSION
+=05	U+0005	ENQUIRY
+=06	U+0006	ACKNOWLEDGE
+=07	U+0007	BELL
+=08	U+0008	BACKSPACE
+=09	U+0009	HORIZONTAL TABULATION
+=0A	U+000A	LINE FEED
+=0B	U+000B	VERTICAL TABULATION
+=0C	U+000C	FORM FEED
+=0D	U+000D	CARRIAGE RETURN
+=0E	U+000E	SHIFT OUT
+=0F	U+000F	SHIFT IN
+=10	U+0010	DATA LINK ESCAPE
+=11	U+0011	DEVICE CONTROL ONE
+=12	U+0012	DEVICE CONTROL TWO
+=13	U+0013	DEVICE CONTROL THREE
+=14	U+0014	DEVICE CONTROL FOUR
+=15	U+0015	NEGATIVE ACKNOWLEDGE
+=16	U+0016	SYNCHRONOUS IDLE
+=17	U+0017	END OF TRANSMISSION BLOCK
+=18	U+0018	CANCEL
+=19	U+0019	END OF MEDIUM
+=1A	U+001A	SUBSTITUTE
+=1B	U+001B	ESCAPE
+=1C	U+001C	FILE SEPARATOR
+=1D	U+001D	GROUP SEPARATOR
+=1E	U+001E	RECORD SEPARATOR
+=1F	U+001F	UNIT SEPARATOR
+=7F	U+007F	DELETE
+*/ extern int HTCurSelectGroupType;
+    extern char *HTCurSelectGroupSize;
+
+#if defined(VMS) && defined(VAXC) && !defined(__DECC)
+    extern int HTVirtualMemorySize;
+#endif				/* VMS && VAXC && !__DECC */
+
+    extern HTChildAnchor *HText_childNextNumber(int n, void **prev);
+    extern void HText_FormDescNumber(int n, const char **desc);
+
+/*	Is there any file left?
+*/
+    extern BOOL HText_canScrollUp(HText *text);
+    extern BOOL HText_canScrollDown(void);
+
+/*	Move display within window
+*/
+    extern void HText_scrollUp(HText *text);	/* One page */
+    extern void HText_scrollDown(HText *text);	/* One page */
+    extern void HText_scrollTop(HText *text);
+    extern void HText_scrollBottom(HText *text);
+    extern void HText_pageDisplay(int line_num, char *target);
+    extern BOOL HText_pageHasPrevTarget(void);
+
+    extern int HText_LinksInLines(HText *text, int line_num, int Lines);
+
+    extern int HText_getAbsLineNumber(HText *text, int anchor_number);
+    extern int HText_closestAnchor(HText *text, int offset);
+    extern int HText_locateAnchor(HText *text, int anchor_number);
+    extern int HText_anchorRelativeTo(HText *text, int top_lineno, int anchor_num);
+
+    extern void HText_setLastChar(HText *text, char ch);
+    extern char HText_getLastChar(HText *text);
+
+    extern int HText_sourceAnchors(HText *text);
+    extern void HText_setStale(HText *text);
+    extern void HText_refresh(HText *text);
+    extern const char *HText_getTitle(void);
+    extern const char *HText_getSugFname(void);
+    extern void HTCheckFnameForCompression(char **fname,
+					   HTParentAnchor *anchor,
+					   BOOLEAN strip_ok);
+    extern const char *HText_getLastModified(void);
+    extern const char *HText_getDate(void);
+    extern const char *HText_getHttpHeaders(void);
+    extern const char *HText_getServer(void);
+    extern const char *HText_getOwner(void);
+    extern const char *HText_getContentBase(void);
+    extern const char *HText_getContentLocation(void);
+    extern const char *HText_getMessageID(void);
+    extern const char *HText_getRevTitle(void);
+
+#ifdef USE_COLOR_STYLE
+    extern const char *HText_getStyle(void);
+#endif
+    extern void HText_setMainTextOwner(const char *owner);
+    extern void print_wwwfile_to_fd(FILE *fp, BOOLEAN is_email, BOOLEAN is_reply);
+    extern BOOL HText_select(HText *text);
+    extern BOOL HText_POSTReplyLoaded(DocInfo *doc);
+    extern BOOL HTFindPoundSelector(const char *selector);
+    extern int HTGetRelLinkNum(int num, int rel, int cur);
+    extern int HTGetLinkInfo(int number,
+			     int want_go,
+			     int *go_line,
+			     int *linknum,
+			     char **hightext,
+			     char **lname);
+    extern BOOL HText_TAHasMoreLines(int curlink,
+				     int direction);
+    extern int HTGetLinkOrFieldStart(int curlink,
+				     int *go_line,
+				     int *linknum,
+				     int direction,
+				     BOOLEAN ta_skip);
+    extern BOOL HText_getFirstTargetInLine(HText *text,
+					   int line_num,
+					   BOOL utf_flag,
+					   int *offset,
+					   int *tLen,
+					   char **data,
+					   const char *target);
+    extern int HTisDocumentSource(void);
+    extern void HTuncache_current_document(void);
+
+#ifdef USE_SOURCE_CACHE
+    extern BOOLEAN HTreparse_document(void);
+    extern BOOLEAN HTcan_reparse_document(void);
+    extern BOOLEAN HTdocument_settings_changed(void);
+#endif
+
+    extern BOOL HTLoadedDocumentEightbit(void);
+    extern BOOL HText_LastLineEmpty(HText *me, BOOL IgnoreSpaces);
+    extern BOOL HText_PreviousLineEmpty(HText *me, BOOL IgnoreSpaces);
+    extern BOOL HText_inLineOne(HText *text);
+    extern BOOLEAN HTLoadedDocumentIsHEAD(void);
+    extern BOOLEAN HTLoadedDocumentIsSafe(void);
+    extern bstring *HTLoadedDocumentPost_data(void);
+    extern const char *HTLoadedDocumentBookmark(void);
+    extern const char *HTLoadedDocumentCharset(void);
+    extern const char *HTLoadedDocumentTitle(void);
+    extern const char *HTLoadedDocumentURL(void);
+    extern const char *HText_HiddenLinkAt(HText *text, int number);
+    extern int HText_HiddenLinkCount(HText *text);
+    extern int HText_LastLineOffset(HText *me);
+    extern int HText_LastLineSize(HText *me, BOOL IgnoreSpaces);
+    extern int HText_PreviousLineSize(HText *me, BOOL IgnoreSpaces);
+    extern int HText_getCurrentColumn(HText *text);
+    extern int HText_getLines(HText *text);
+    extern int HText_getMaximumColumn(HText *text);
+    extern int HText_getNumOfBytes(void);
+    extern int HText_getNumOfLines(void);
+    extern int HText_getPreferredTopLine(HText *text, int line_number);
+    extern int HText_getTabIDColumn(HText *text, const char *name);
+    extern int HText_getTopOfScreen(void);
+    extern int do_www_search(DocInfo *doc);
+    extern void HText_NegateLineOne(HText *text);
+    extern void HText_RemovePreviousLine(HText *text);
+    extern void HText_setNodeAnchorBookmark(const char *bookmark);
+    extern void HText_setTabID(HText *text, const char *name);
+    extern void *HText_pool_calloc(HText *text, unsigned size);
+
+/* "simple table" stuff */
+    extern BOOLEAN HText_endStblTABLE(HText *);
+    extern int HText_trimCellLines(HText *text);
+    extern void HText_cancelStbl(HText *);
+    extern void HText_endStblCOLGROUP(HText *);
+    extern void HText_endStblTD(HText *);
+    extern void HText_endStblTR(HText *);
+    extern void HText_startStblCOL(HText *, int, short, BOOL);
+    extern void HText_startStblRowGroup(HText *, short);
+    extern void HText_startStblTABLE(HText *, short);
+    extern void HText_startStblTD(HText *, int, int, short, BOOL);
+    extern void HText_startStblTR(HText *, short);
+
+/* forms stuff */
+    extern void HText_beginForm(char *action,
+				char *method,
+				char *enctype,
+				char *title,
+				const char *accept_cs);
+    extern void HText_endForm(HText *text);
+    extern void HText_beginSelect(char *name,
+				  int name_cs,
+				  BOOLEAN multiple,
+				  char *len);
+    extern int HText_getOptionNum(HText *text);
+    extern char *HText_setLastOptionValue(HText *text,
+					  char *value,
+					  char *submit_value,
+					  int order,
+					  BOOLEAN checked,
+					  int val_cs,
+					  int submit_val_cs);
+    extern int HText_beginInput(HText *text,
+				BOOL underline,
+				InputFieldData * I);
+    extern void HText_endInput(HText *text);
+    extern int HText_SubmitForm(FormInfo * submit_item, DocInfo *doc,
+				char *link_name,
+				char *link_value);
+    extern void HText_DisableCurrentForm(void);
+    extern void HText_ResetForm(FormInfo * form);
+    extern void HText_activateRadioButton(FormInfo * form);
+    extern BOOLEAN HText_HaveUserChangedForms(HText *text);
+
+    extern HTList *search_queries;	/* Previous isindex and whereis queries */
+    extern void HTSearchQueries_free(void);
+    extern void HTAddSearchQuery(char *query);
+
+    extern void user_message(const char *message,
+			     const char *argument);
+
+#define _user_message(msg, arg)	mustshow = TRUE, user_message(msg, arg)
+
+    extern void www_user_search(int start_line,
+				DocInfo *doc,
+				char *target,
+				int direction);
+
+    extern void print_crawl_to_fd(FILE *fp,
+				  char *thelink,
+				  char *thetitle);
+    extern char *stub_HTAnchor_address(HTAnchor * me);
+
+    extern void HText_setToolbar(HText *text);
+    extern BOOL HText_hasToolbar(HText *text);
+
+    extern void HText_setNoCache(HText *text);
+    extern BOOL HText_hasNoCacheSet(HText *text);
+
+    extern BOOL HText_hasUTF8OutputSet(HText *text);
+    extern void HText_setKcode(HText *text,
+			       const char *charset,
+			       LYUCcharset *p_in);
+
+    extern void HText_setBreakPoint(HText *text);
+
+    extern BOOL HText_AreDifferent(HTParentAnchor *anchor,
+				   const char *full_address);
+
+    extern int HText_ExtEditForm(LinkInfo * form_link);
+    extern void HText_ExpandTextarea(LinkInfo * form_link, int newlines);
+    extern int HText_InsertFile(LinkInfo * form_link);
+
+    extern void redraw_lines_of_link(int cur);
+    extern void LYMoveToLink(int cur,
+			     const char *target,
+			     const char *hightext,
+			     int flag,
+			     BOOL inU,
+			     BOOL utf_flag);
+
+#ifdef USE_PRETTYSRC
+    extern void HTMark_asSource(void);
+#endif
+
+    extern int HTMainText_Get_UCLYhndl(void);
+
+#ifdef KANJI_CODE_OVERRIDE
+    extern HTkcode last_kcode;
+#endif
+
+    extern HTkcode HText_getKcode(HText *text);
+    extern void HText_updateKcode(HText *text, HTkcode kcode);
+    extern HTkcode HText_getSpecifiedKcode(HText *text);
+    extern void HText_updateSpecifiedKcode(HText *text, HTkcode kcode);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYGRIDTEXT_H */
diff --git a/src/HTAlert.c b/src/HTAlert.c
new file mode 100644
index 00000000..ce237e54
--- /dev/null
+++ b/src/HTAlert.c
@@ -0,0 +1,1174 @@
+/*
+ * $LynxId: HTAlert.c,v 1.86 2009/11/21 16:32:23 tom Exp $
+ *
+ *	Displaying messages and getting input for Lynx Browser
+ *	==========================================================
+ *
+ *	REPLACE THIS MODULE with a GUI version in a GUI environment!
+ *
+ * History:
+ *	   Jun 92 Created May 1992 By C.T. Barker
+ *	   Feb 93 Simplified, portablised TBL
+ *
+ */
+
+#include <HTUtils.h>
+#include <HTAlert.h>
+#include <LYGlobalDefs.h>
+#include <LYCurses.h>
+#include <LYStrings.h>
+#include <LYUtils.h>
+#include <LYClean.h>
+#include <GridText.h>
+#include <LYCookie.h>
+#include <LYHistory.h>		/* store statusline messages */
+
+#include <LYLeaks.h>
+
+#include <HTParse.h>
+
+#undef timezone			/* U/Win defines this in time.h, hides implementation detail */
+
+#if defined(HAVE_FTIME) && defined(HAVE_SYS_TIMEB_H)
+#include <sys/timeb.h>
+#endif
+
+/*
+ * 'napms()' is preferable to 'sleep()' in any case because it does not
+ * interfere with output, but also because it can be less than a second.
+ */
+#ifdef HAVE_NAPMS
+#define LYSleep(n) napms(n)
+#else
+#define LYSleep(n) sleep(n)
+#endif
+
+/*	Issue a message about a problem.		HTAlert()
+ *	--------------------------------
+ */
+void HTAlert(const char *Msg)
+{
+    CTRACE((tfp, "\nAlert!: %s\n\n", Msg));
+    CTRACE_FLUSH(tfp);
+    _user_message(ALERT_FORMAT, Msg);
+    LYstore_message2(ALERT_FORMAT, Msg);
+
+    if (dump_output_immediately && dump_to_stderr) {
+	fflush(stdout);
+	fprintf(stderr, ALERT_FORMAT, Msg);
+	fputc('\n', stderr);
+	fflush(stderr);
+    }
+
+    LYSleepAlert();
+}
+
+void HTAlwaysAlert(const char *extra_prefix,
+		   const char *Msg)
+{
+    if (!dump_output_immediately && LYCursesON) {
+	HTAlert(Msg);
+    } else {
+	if (extra_prefix) {
+	    fprintf(((TRACE) ? stdout : stderr),
+		    "%s %s!\n",
+		    extra_prefix, Msg);
+	    fflush(stdout);
+	    LYstore_message2(ALERT_FORMAT, Msg);
+	    LYSleepAlert();
+	} else {
+	    fprintf(((TRACE) ? stdout : stderr), ALERT_FORMAT, NonNull(Msg));
+	    fflush(stdout);
+	    LYstore_message2(ALERT_FORMAT, Msg);
+	    LYSleepAlert();
+	    fprintf(((TRACE) ? stdout : stderr), "\n");
+	}
+	CTRACE((tfp, "\nAlert!: %s\n\n", Msg));
+	CTRACE_FLUSH(tfp);
+    }
+}
+
+/*	Issue an informational message.			HTInfoMsg()
+ *	--------------------------------
+ */
+void HTInfoMsg(const char *Msg)
+{
+    _statusline(Msg);
+    if (non_empty(Msg)) {
+	CTRACE((tfp, "Info message: %s\n", Msg));
+	LYstore_message(Msg);
+	LYSleepInfo();
+    }
+}
+
+void HTInfoMsg2(const char *Msg2, const char *Arg)
+{
+    _user_message(Msg2, Arg);
+    if (non_empty(Msg2)) {
+	CTRACE((tfp, "Info message: "));
+	CTRACE((tfp, Msg2, Arg));
+	CTRACE((tfp, "\n"));
+	LYstore_message2(Msg2, Arg);
+	LYSleepInfo();
+    }
+}
+
+/*	Issue an important message.			HTUserMsg()
+ *	--------------------------------
+ */
+void HTUserMsg(const char *Msg)
+{
+    _statusline(Msg);
+    if (non_empty(Msg)) {
+	CTRACE((tfp, "User message: %s\n", Msg));
+	LYstore_message(Msg);
+#if !(defined(USE_SLANG) || defined(WIDEC_CURSES))
+	if (IS_CJK_TTY) {
+	    clearok(curscr, TRUE);
+	    LYrefresh();
+	}
+#endif
+	LYSleepMsg();
+    }
+}
+
+void HTUserMsg2(const char *Msg2, const char *Arg)
+{
+    _user_message(Msg2, Arg);
+    if (non_empty(Msg2)) {
+	CTRACE((tfp, "User message: "));
+	CTRACE((tfp, Msg2, Arg));
+	CTRACE((tfp, "\n"));
+	LYstore_message2(Msg2, Arg);
+	LYSleepMsg();
+    }
+}
+
+/*	Issue a progress message.			HTProgress()
+ *	-------------------------
+ */
+void HTProgress(const char *Msg)
+{
+    statusline(Msg);
+    LYstore_message(Msg);
+    CTRACE((tfp, "%s\n", Msg));
+    LYSleepDebug();
+}
+
+const char *HTProgressUnits(int rate)
+{
+    static const char *bunits = 0;
+    static const char *kbunits = 0;
+
+    if (!bunits) {
+	bunits = gettext("bytes");
+	kbunits = gettext(LYTransferName);
+    }
+    return ((rate == rateKB)
+#ifdef USE_READPROGRESS
+	    || (rate == rateEtaKB)
+#endif
+	)? kbunits : bunits;
+}
+
+static const char *sprint_bytes(char *s, off_t n, const char *was_units)
+{
+    static off_t kb_units = 1024;
+    const char *u = HTProgressUnits(LYTransferRate);
+
+    if (LYTransferRate == rateKB || LYTransferRate == rateEtaKB_maybe) {
+	if (n >= 10 * kb_units) {
+	    sprintf(s, "%" PRI_off_t, CAST_off_t(n / kb_units));
+	} else if (n > 999) {	/* Avoid switching between 1016b/s and 1K/s */
+	    sprintf(s, "%.2g", ((double) n) / kb_units);
+	} else {
+	    sprintf(s, "%" PRI_off_t, CAST_off_t(n));
+	    u = HTProgressUnits(rateBYTES);
+	}
+    } else {
+	sprintf(s, "%" PRI_off_t, CAST_off_t(n));
+    }
+
+    if (!was_units || was_units != u)
+	sprintf(s + strlen(s), " %s", u);
+    return u;
+}
+
+#ifdef USE_READPROGRESS
+#define TIME_HMS_LENGTH (16)
+static char *sprint_tbuf(char *s, long t)
+{
+    if (t > 3600)
+	sprintf(s, "%ldh%ldm%lds", t / 3600, (t / 60) % 60, t % 60);
+    else if (t > 60)
+	sprintf(s, "%ldm%lds", t / 60, t % 60);
+    else
+	sprintf(s, "%ld sec", t);
+    return s;
+}
+#endif /* USE_READPROGRESS */
+
+/*	Issue a read-progress message.			HTReadProgress()
+ *	------------------------------
+ */
+void HTReadProgress(off_t bytes, off_t total)
+{
+    static off_t bytes_last, total_last;
+    static off_t transfer_rate = 0;
+    static char *line = NULL;
+    char bytesp[80], totalp[80], transferp[80];
+    int renew = 0;
+    const char *was_units;
+
+#ifdef HAVE_GETTIMEOFDAY
+    struct timeval tv;
+    double now;
+    static double first, last, last_active;
+
+    gettimeofday(&tv, (struct timezone *) 0);
+    now = tv.tv_sec + tv.tv_usec / 1000000.;
+#else
+#if defined(HAVE_FTIME) && defined(HAVE_SYS_TIMEB_H)
+    static double now, first, last, last_active;
+    struct timeb tb;
+
+    ftime(&tb);
+    now = tb.time + (double) tb.millitm / 1000;
+#else
+    time_t now = time((time_t *) 0);	/* once per second */
+    static time_t first, last, last_active;
+#endif
+#endif
+
+    if (!LYShowTransferRate)
+	LYTransferRate = rateOFF;
+
+    if (bytes == 0) {
+	first = last = last_active = now;
+	bytes_last = bytes;
+    } else if (bytes < 0) {	/* stalled */
+	bytes = bytes_last;
+	total = total_last;
+    }
+
+    /* 1 sec delay for transfer_rate calculation without g-t-o-d */
+    if ((bytes > 0) &&
+	(now > first)) {
+	if (transfer_rate <= 0)	/* the very first time */
+	    transfer_rate = (off_t) ((bytes) / (now - first));	/* bytes/sec */
+	total_last = total;
+
+	/*
+	 * Optimal refresh time:  every 0.2 sec
+	 */
+#if defined(HAVE_GETTIMEOFDAY) || (defined(HAVE_FTIME) && defined(HAVE_SYS_TIMEB_H))
+	if (now >= last + 0.2)
+	    renew = 1;
+#else
+	/*
+	 * Use interpolation.  (The transfer rate may be not constant
+	 * when we have partial content in a proxy.  We adjust transfer_rate
+	 * once a second to minimize interpolation error below.)
+	 */
+	if ((now != last) || ((bytes - bytes_last) > (transfer_rate / 5))) {
+	    renew = 1;
+	    bytes_last += (transfer_rate / 5);	/* until we got next second */
+	}
+#endif
+	if (renew) {
+	    if (now > last) {
+		last = now;
+		if (bytes_last != bytes)
+		    last_active = now;
+		bytes_last = bytes;
+		transfer_rate = (off_t) (bytes / (now - first));	/* more accurate value */
+	    }
+
+	    if (total > 0)
+		was_units = sprint_bytes(totalp, total, 0);
+	    else
+		was_units = 0;
+	    sprint_bytes(bytesp, bytes, was_units);
+
+	    switch ((TransferRate) LYTransferRate) {
+#ifdef USE_PROGRESSBAR
+	    case rateBAR:
+		/*
+		 * If we know the total size of the file, we can compute
+		 * a percentage, and show a corresponding progress bar.
+		 */
+		HTSprintf0(&line, gettext("Read %s of data"), bytesp);
+
+		if (total > 0) {
+		    float percent = bytes / (float) total;
+		    int meter = (LYcolLimit * percent) - 5;
+
+		    CTRACE((tfp, "rateBAR: bytes: %" PRI_off_t ", total: "
+			    "%" PRI_off_t "\n", bytes, total));
+		    CTRACE((tfp, "meter = %d\n", meter));
+
+		    HTSprintf0(&line, "%d%% ", (int) (percent * 100));
+		    while (meter-- > 0)
+			StrAllocCat(line, "I");
+
+		    CTRACE((tfp, "%s\n", line));
+		    CTRACE_FLUSH(tfp);
+		}
+		break;
+#endif
+	    default:
+		if (total > 0) {
+		    HTSprintf0(&line, gettext("Read %s of %s of data"),
+			       bytesp, totalp);
+		} else {
+		    HTSprintf0(&line, gettext("Read %s of data"), bytesp);
+		}
+
+		if (LYTransferRate != rateOFF
+		    && transfer_rate > 0) {
+		    sprint_bytes(transferp, transfer_rate, 0);
+		    HTSprintf(&line, gettext(", %s/sec"), transferp);
+		}
+		break;
+	    }
+
+#ifdef USE_READPROGRESS
+	    if (LYTransferRate == rateEtaBYTES
+		|| LYTransferRate == rateEtaKB) {
+		char tbuf[TIME_HMS_LENGTH];
+
+		if (now - last_active >= 5)
+		    HTSprintf(&line,
+			      gettext(" (stalled for %s)"),
+			      sprint_tbuf(tbuf, (long) (now - last_active)));
+		if (total > 0 && transfer_rate)
+		    HTSprintf(&line,
+			      gettext(", ETA %s"),
+			      sprint_tbuf(tbuf, (long) ((total - bytes) / transfer_rate)));
+	    }
+#endif
+
+	    switch ((TransferRate) LYTransferRate) {
+#ifdef USE_PROGRESSBAR
+	    case rateBAR:
+		/*
+		 * If we were not able to show a progress bar, just show
+		 * a "." for progress.
+		 */
+		if (total <= 0)
+		    StrAllocCat(line, ".");
+		break;
+#endif
+	    default:
+		StrAllocCat(line, ".");
+		break;
+	    }
+
+	    if (total < -1)
+		StrAllocCat(line, gettext(" (Press 'z' to abort)"));
+
+	    /* do not store the message for history page. */
+	    statusline(line);
+	    CTRACE((tfp, "%s\n", line));
+	}
+    }
+#ifdef LY_FIND_LEAKS
+    FREE(line);
+#endif
+}
+
+static BOOL conf_cancelled = NO;	/* used by HTConfirm only - kw */
+
+BOOL HTLastConfirmCancelled(void)
+{
+    if (conf_cancelled) {
+	conf_cancelled = NO;	/* reset */
+	return (YES);
+    } else {
+	return (NO);
+    }
+}
+
+/*
+ * Prompt for yes/no response, but let a configuration variable override
+ * the prompt entirely.
+ */
+int HTForcedPrompt(int option, const char *msg, int dft)
+{
+    int result = FALSE;
+    const char *show = NULL;
+    char *msg2 = NULL;
+
+    if (option == FORCE_PROMPT_DFT) {
+	result = HTConfirmDefault(msg, dft);
+    } else {
+	if (option == FORCE_PROMPT_YES) {
+	    show = gettext("yes");
+	    result = YES;
+	} else if (option == FORCE_PROMPT_NO) {
+	    show = gettext("no");
+	    result = NO;
+	} else {
+	    return HTConfirmDefault(msg, dft);	/* bug... */
+	}
+	HTSprintf(&msg2, "%s %s", msg, show);
+	HTUserMsg(msg2);
+	free(msg2);
+    }
+    return result;
+}
+
+#define DFT_CONFIRM ~(YES|NO)
+
+/*	Seek confirmation with default answer.		HTConfirmDefault()
+ *	--------------------------------------
+ */
+int HTConfirmDefault(const char *Msg, int Dft)
+{
+/* Meta-note: don't move the following note from its place right
+   in front of the first gettext().  As it is now, it should
+   automatically appear in generated lynx.pot files. - kw
+ */
+
+/* NOTE TO TRANSLATORS:  If you provide a translation for "yes", lynx
+ * will take the first byte of the translation as a positive response
+ * to Yes/No questions.  If you provide a translation for "no", lynx
+ * will take the first byte of the translation as a negative response
+ * to Yes/No questions.  For both, lynx will also try to show the
+ * first byte in the prompt as a character, instead of (y) or (n),
+ * respectively.  This will not work right for multibyte charsets!
+ * Don't translate "yes" and "no" for CJK character sets (or translate
+ * them to "yes" and "no").  For a translation using UTF-8, don't
+ * translate if the translation would begin with anything but a 7-bit
+ * (US_ASCII) character.  That also means do not translate if the
+ * translation would begin with anything but a 7-bit character, if
+ * you use a single-byte character encoding (a charset like ISO-8859-n)
+ * but anticipate that the message catalog may be used re-encoded in
+ * UTF-8 form.
+ * For translations using other character sets, you may also wish to
+ * leave "yes" and "no" untranslated, if using (y) and (n) is the
+ * preferred behavior.
+ * Lynx will also accept y Y n N as responses unless there is a conflict
+ * with the first letter of the "yes" or "no" translation.
+ */
+    const char *msg_yes = gettext("yes");
+    const char *msg_no = gettext("no");
+    int result = -1;
+
+    /* If they're not really distinct in the first letter, revert to English */
+    if (TOUPPER(*msg_yes) == TOUPPER(*msg_no)) {
+	msg_yes = "yes";
+	msg_no = "no";
+    }
+
+    conf_cancelled = NO;
+    if (dump_output_immediately) {	/* Non-interactive, can't respond */
+	if (Dft == DFT_CONFIRM) {
+	    CTRACE((tfp, "Confirm: %s (%c/%c) ", Msg, *msg_yes, *msg_no));
+	} else {
+	    CTRACE((tfp, "Confirm: %s (%c) ", Msg, (Dft == YES) ? *msg_yes : *msg_no));
+	}
+	CTRACE((tfp, "- NO, not interactive.\n"));
+	result = NO;
+    } else {
+	char *msg = NULL;
+	char fallback_y = 'y';	/* English letter response as fallback */
+	char fallback_n = 'n';	/* English letter response as fallback */
+
+	if (fallback_y == *msg_yes || fallback_y == *msg_no)
+	    fallback_y = '\0';	/* conflict or duplication, don't use */
+	if (fallback_n == *msg_yes || fallback_n == *msg_no)
+	    fallback_n = '\0';	/* conflict or duplication, don't use */
+
+	if (Dft == DFT_CONFIRM)
+	    HTSprintf0(&msg, "%s (%c/%c) ", Msg, *msg_yes, *msg_no);
+	else
+	    HTSprintf0(&msg, "%s (%c) ", Msg, (Dft == YES) ? *msg_yes : *msg_no);
+	if (LYTraceLogFP) {
+	    CTRACE((tfp, "Confirm: %s", msg));
+	}
+	_statusline(msg);
+	FREE(msg);
+
+	while (result < 0) {
+	    int c = LYgetch_single();
+
+#ifdef VMS
+	    if (HadVMSInterrupt) {
+		HadVMSInterrupt = FALSE;
+		c = TOUPPER(*msg_no);
+	    }
+#endif /* VMS */
+	    if (c == TOUPPER(*msg_yes)) {
+		result = YES;
+	    } else if (c == TOUPPER(*msg_no)) {
+		result = NO;
+	    } else if (fallback_y && c == fallback_y) {
+		result = YES;
+	    } else if (fallback_n && c == fallback_n) {
+		result = NO;
+	    } else if (LYCharIsINTERRUPT(c)) {	/* remember we had ^G or ^C */
+		conf_cancelled = YES;
+		result = NO;
+	    } else if (Dft != DFT_CONFIRM) {
+		result = Dft;
+		break;
+	    }
+	}
+	CTRACE((tfp, "- %s%s.\n",
+		(result != NO) ? "YES" : "NO",
+		conf_cancelled ? ", cancelled" : ""));
+    }
+    return (result);
+}
+
+/*	Seek confirmation.				HTConfirm()
+ *	------------------
+ */
+BOOL HTConfirm(const char *Msg)
+{
+    return (BOOL) HTConfirmDefault(Msg, DFT_CONFIRM);
+}
+
+/*
+ * Ask a post resubmission prompt with some indication of what would
+ * be resubmitted, useful especially for going backward in history.
+ * Try to use parts of the address or, if given, the title, depending
+ * on how much fits on the statusline.
+ * if_imgmap and if_file indicate how to handle an address that is
+ * a "LYNXIMGMAP:", or a "file:" URL (presumably the List Page file),
+ * respectively:  0:  auto-deny, 1:  auto-confirm, 2:  prompt.
+ * - kw
+ */
+
+BOOL confirm_post_resub(const char *address,
+			const char *title,
+			int if_imgmap,
+			int if_file)
+{
+    size_t len1;
+    const char *msg = CONFIRM_POST_RESUBMISSION_TO;
+    char buf[240];
+    char *temp = NULL;
+    BOOL res;
+    size_t maxlen = (size_t) (LYcolLimit - 5);
+
+    if (!address) {
+	return (NO);
+    } else if (isLYNXIMGMAP(address)) {
+	if (if_imgmap <= 0)
+	    return (NO);
+	else if (if_imgmap == 1)
+	    return (YES);
+	else
+	    msg = CONFIRM_POST_LIST_RELOAD;
+    } else if (isFILE_URL(address)) {
+	if (if_file <= 0)
+	    return (NO);
+	else if (if_file == 1)
+	    return (YES);
+	else
+	    msg = CONFIRM_POST_LIST_RELOAD;
+    } else if (dump_output_immediately) {
+	return (NO);
+    }
+    if (maxlen >= sizeof(buf))
+	maxlen = sizeof(buf) - 1;
+    if ((len1 = strlen(msg)) +
+	strlen(address) <= maxlen) {
+	sprintf(buf, msg, address);
+	return HTConfirm(buf);
+    }
+    if (len1 + strlen(temp = HTParse(address, "",
+				     PARSE_ACCESS + PARSE_HOST + PARSE_PATH
+				     + PARSE_PUNCTUATION)) <= maxlen) {
+	sprintf(buf, msg, temp);
+	res = HTConfirm(buf);
+	FREE(temp);
+	return (res);
+    }
+    FREE(temp);
+    if (title && (len1 + strlen(title) <= maxlen)) {
+	sprintf(buf, msg, title);
+	return HTConfirm(buf);
+    }
+    if (len1 + strlen(temp = HTParse(address, "",
+				     PARSE_ACCESS + PARSE_HOST
+				     + PARSE_PUNCTUATION)) <= maxlen) {
+	sprintf(buf, msg, temp);
+	res = HTConfirm(buf);
+	FREE(temp);
+	return (res);
+    }
+    FREE(temp);
+    if ((temp = HTParse(address, "", PARSE_HOST)) && *temp &&
+	len1 + strlen(temp) <= maxlen) {
+	sprintf(buf, msg, temp);
+	res = HTConfirm(buf);
+	FREE(temp);
+	return (res);
+    }
+    FREE(temp);
+    return HTConfirm(CONFIRM_POST_RESUBMISSION);
+}
+
+/*	Prompt for answer and get text back.		HTPrompt()
+ *	------------------------------------
+ */
+char *HTPrompt(const char *Msg, const char *deflt)
+{
+    char *rep = NULL;
+    char Tmp[200];
+
+    Tmp[0] = '\0';
+    Tmp[sizeof(Tmp) - 1] = '\0';
+
+    _statusline(Msg);
+    if (deflt)
+	strncpy(Tmp, deflt, sizeof(Tmp) - 1);
+
+    if (!dump_output_immediately)
+	LYgetstr(Tmp, VISIBLE, sizeof(Tmp), NORECALL);
+
+    StrAllocCopy(rep, Tmp);
+
+    return rep;
+}
+
+/*
+ *	Prompt for password without echoing the reply.	HTPromptPassword()
+ *	----------------------------------------------
+ */
+char *HTPromptPassword(const char *Msg)
+{
+    char *result = NULL;
+    char pw[120];
+
+    pw[0] = '\0';
+
+    if (!dump_output_immediately) {
+	_statusline(Msg ? Msg : PASSWORD_PROMPT);
+	LYgetstr(pw, HIDDEN, sizeof(pw), NORECALL);	/* hidden */
+	StrAllocCopy(result, pw);
+    } else {
+	printf("\n%s\n", PASSWORD_REQUIRED);
+	StrAllocCopy(result, "");
+    }
+    return result;
+}
+
+/*	Prompt both username and password.	 HTPromptUsernameAndPassword()
+ *	----------------------------------
+ *
+ *  On entry,
+ *	Msg		is the prompting message.
+ *	*username and
+ *	*password	are char pointers which contain default
+ *			or zero-length strings; they are changed
+ *			to point to result strings.
+ *	IsProxy 	should be TRUE if this is for
+ *			proxy authentication.
+ *
+ *			If *username is not NULL, it is taken
+ *			to point to a default value.
+ *			Initial value of *password is
+ *			completely discarded.
+ *
+ *  On exit,
+ *	*username and *password point to newly allocated
+ *	strings -- original strings pointed to by them
+ *	are NOT freed.
+ *
+ */
+void HTPromptUsernameAndPassword(const char *Msg,
+				 char **username,
+				 char **password,
+				 BOOL IsProxy)
+{
+    if ((IsProxy == FALSE &&
+	 authentication_info[0] && authentication_info[1]) ||
+	(IsProxy == TRUE &&
+	 proxyauth_info[0] && proxyauth_info[1])) {
+	/*
+	 * The -auth or -pauth parameter gave us both the username
+	 * and password to use for the first realm or proxy server,
+	 * respectively, so just use them without any prompting.  - FM
+	 */
+	StrAllocCopy(*username, (IsProxy ?
+				 proxyauth_info[0] : authentication_info[0]));
+	if (IsProxy) {
+	    FREE(proxyauth_info[0]);
+	} else {
+	    FREE(authentication_info[0]);
+	}
+	StrAllocCopy(*password, (IsProxy ?
+				 proxyauth_info[1] : authentication_info[1]));
+	if (IsProxy) {
+	    FREE(proxyauth_info[1]);
+	} else {
+	    FREE(authentication_info[1]);
+	}
+    } else if (dump_output_immediately) {
+	/*
+	 * We are not interactive and don't have both the
+	 * username and password from the command line,
+	 * but might have one or the other.  - FM
+	 */
+	if ((IsProxy == FALSE && authentication_info[0]) ||
+	    (IsProxy == TRUE && proxyauth_info[0])) {
+	    /*
+	     * Use the command line username.  - FM
+	     */
+	    StrAllocCopy(*username, (IsProxy ?
+				     proxyauth_info[0] : authentication_info[0]));
+	    if (IsProxy) {
+		FREE(proxyauth_info[0]);
+	    } else {
+		FREE(authentication_info[0]);
+	    }
+	} else {
+	    /*
+	     * Default to "WWWuser".  - FM
+	     */
+	    StrAllocCopy(*username, "WWWuser");
+	}
+	if ((IsProxy == FALSE && authentication_info[1]) ||
+	    (IsProxy == TRUE && proxyauth_info[1])) {
+	    /*
+	     * Use the command line password.  - FM
+	     */
+	    StrAllocCopy(*password, (IsProxy ?
+				     proxyauth_info[1] : authentication_info[1]));
+	    if (IsProxy) {
+		FREE(proxyauth_info[1]);
+	    } else {
+		FREE(authentication_info[1]);
+	    }
+	} else {
+	    /*
+	     * Default to a zero-length string.  - FM
+	     */
+	    StrAllocCopy(*password, "");
+	}
+	printf("\n%s\n", USERNAME_PASSWORD_REQUIRED);
+
+    } else {
+	/*
+	 * We are interactive and don't have both the
+	 * username and password from the command line,
+	 * but might have one or the other.  - FM
+	 */
+	if ((IsProxy == FALSE && authentication_info[0]) ||
+	    (IsProxy == TRUE && proxyauth_info[0])) {
+	    /*
+	     * Offer the command line username in the
+	     * prompt for the first realm.  - FM
+	     */
+	    StrAllocCopy(*username, (IsProxy ?
+				     proxyauth_info[0] : authentication_info[0]));
+	    if (IsProxy) {
+		FREE(proxyauth_info[0]);
+	    } else {
+		FREE(authentication_info[0]);
+	    }
+	}
+	/*
+	 * Prompt for confirmation or entry of the username.  - FM
+	 */
+	if (Msg != NULL) {
+	    *username = HTPrompt(Msg, *username);
+	} else {
+	    *username = HTPrompt(USERNAME_PROMPT, *username);
+	}
+	if ((IsProxy == FALSE && authentication_info[1]) ||
+	    (IsProxy == TRUE && proxyauth_info[1])) {
+	    /*
+	     * Use the command line password for the first realm.  - FM
+	     */
+	    StrAllocCopy(*password, (IsProxy ?
+				     proxyauth_info[1] : authentication_info[1]));
+	    if (IsProxy) {
+		FREE(proxyauth_info[1]);
+	    } else {
+		FREE(authentication_info[1]);
+	    }
+	} else if (non_empty(*username)) {
+	    /*
+	     * We have a non-zero length username,
+	     * so prompt for the password.  - FM
+	     */
+	    *password = HTPromptPassword(PASSWORD_PROMPT);
+	} else {
+	    /*
+	     * Return a zero-length password.  - FM
+	     */
+	    StrAllocCopy(*password, "");
+	}
+    }
+}
+
+/*	Confirm a cookie operation.			HTConfirmCookie()
+ *	---------------------------
+ *
+ *  On entry,
+ *	server			is the server sending the Set-Cookie.
+ *	domain			is the domain of the cookie.
+ *	path			is the path of the cookie.
+ *	name			is the name of the cookie.
+ *	value			is the value of the cookie.
+ *
+ *  On exit,
+ *	Returns FALSE on cancel,
+ *		TRUE if the cookie should be set.
+ */
+BOOL HTConfirmCookie(domain_entry * de, const char *server,
+		     const char *name,
+		     const char *value)
+{
+    int ch;
+    const char *prompt = ADVANCED_COOKIE_CONFIRMATION;
+
+    if (de == NULL)
+	return FALSE;
+
+    /* If the user has specified a list of domains to allow or deny
+     * from the config file, then they'll already have de->bv set to
+     * ACCEPT_ALWAYS or REJECT_ALWAYS so we can relax and let the
+     * default cookie handling code cope with this fine.
+     */
+
+    /*
+     * If the user has specified a constant action, don't prompt at all.
+     */
+    if (de->bv == ACCEPT_ALWAYS)
+	return TRUE;
+    if (de->bv == REJECT_ALWAYS)
+	return FALSE;
+
+    if (dump_output_immediately) {
+	/*
+	 * Non-interactive, can't respond.  Use the LYSetCookies value
+	 * based on its compilation or configuration setting, or on the
+	 * command line toggle.  - FM
+	 */
+	return LYSetCookies;
+    }
+
+    /*
+     * Estimate how much of the cookie we can show.
+     */
+    if (!LYAcceptAllCookies) {
+	int namelen, valuelen, space_free, percentage;
+	char *message = 0;
+
+	space_free = (LYcolLimit
+		      - (LYstrCells(prompt)
+			 - 10)	/* %s and %.*s and %.*s chars */
+		      -(int) strlen(server));
+	if (space_free < 0)
+	    space_free = 0;
+	namelen = (int) strlen(name);
+	valuelen = (int) strlen(value);
+	if ((namelen + valuelen) > space_free) {
+	    /*
+	     * Argh...  there isn't enough space on our single line for
+	     * the whole cookie.  Reduce them both by a percentage.
+	     * This should be smarter.
+	     */
+	    percentage = (100 * space_free) / (namelen + valuelen);
+	    namelen = (percentage * namelen) / 100;
+	    valuelen = (percentage * valuelen) / 100;
+	}
+	HTSprintf(&message, prompt, server, namelen, name, valuelen, value);
+	_statusline(message);
+	FREE(message);
+    }
+    for (;;) {
+	if (LYAcceptAllCookies) {
+	    ch = 'A';
+	} else {
+	    ch = LYgetch_single();
+#if defined(LOCALE) && defined(HAVE_GETTEXT)
+	    {
+#define L_PAREN '('
+#define R_PAREN ')'
+		/*
+		 * Special-purpose workaround for gettext support (we should do
+		 * this in a more general way) -TD
+		 *
+		 * NOTE TO TRANSLATORS:  If the prompt has been rendered into
+		 * another language, and if yes/no are distinct, assume the
+		 * translator can make an ordered list in parentheses with one
+		 * capital letter for each as we assumed in HTConfirmDefault().
+		 * The list has to be in the same order as in the original message,
+		 * and the four capital letters chosen to not match those in the
+		 * original unless they have the same position.
+		 *
+		 * Example:
+		 * (Y/N/Always/neVer)              - English (original)
+		 * (O/N/Toujours/Jamais)           - French
+		 */
+		char *p = gettext("Y/N/A/V");	/* placeholder for comment */
+		char *s = "YNAV\007\003";	/* see ADVANCED_COOKIE_CONFIRMATION */
+
+		if (strchr(s, ch) == 0
+		    && isalpha(ch)
+		    && (p = strrchr(prompt, L_PAREN)) != 0) {
+
+		    CTRACE((tfp, "Looking for %c in %s\n", ch, p));
+		    while (*p != R_PAREN && *p != 0 && isalpha(UCH(*s))) {
+			if (isalpha(UCH(*p)) && (*p == TOUPPER(*p))) {
+			    CTRACE((tfp, "...testing %c/%c\n", *p, *s));
+			    if (*p == ch) {
+				ch = *s;
+				break;
+			    }
+			    ++s;
+			}
+			++p;
+		    }
+		}
+	    }
+#endif
+	}
+#ifdef VMS
+	if (HadVMSInterrupt) {
+	    HadVMSInterrupt = FALSE;
+	    ch = 'N';
+	}
+#endif /* VMS */
+	switch (ch) {
+	case 'A':
+	    /*
+	     * Set to accept all cookies for this domain.
+	     */
+	    de->bv = ACCEPT_ALWAYS;
+	    HTUserMsg2(ALWAYS_ALLOWING_COOKIES, de->domain);
+	    return TRUE;
+
+	case 'N':
+	    /*
+	     * Reject the cookie.
+	     */
+	  reject:
+	    HTUserMsg(REJECTING_COOKIE);
+	    return FALSE;
+
+	case 'V':
+	    /*
+	     * Set to reject all cookies from this domain.
+	     */
+	    de->bv = REJECT_ALWAYS;
+	    HTUserMsg2(NEVER_ALLOWING_COOKIES, de->domain);
+	    return FALSE;
+
+	case 'Y':
+	    /*
+	     * Accept the cookie.
+	     */
+	    HTInfoMsg(ALLOWING_COOKIE);
+	    return TRUE;
+
+	default:
+	    if (LYCharIsINTERRUPT(ch))
+		goto reject;
+	    continue;
+	}
+    }
+}
+
+/*	Confirm redirection of POST.		HTConfirmPostRedirect()
+ *	----------------------------
+ *
+ *  On entry,
+ *	Redirecting_url 	    is the Location.
+ *	server_status		    is the server status code.
+ *
+ *  On exit,
+ *	Returns 0 on cancel,
+ *	  1 for redirect of POST with content,
+ *	303 for redirect as GET without content
+ */
+int HTConfirmPostRedirect(const char *Redirecting_url, int server_status)
+{
+    int result = -1;
+    char *show_POST_url = NULL;
+    char *StatusInfo = 0;
+    char *url = 0;
+    int on_screen = 0;		/* 0 - show menu
+
+				 * 1 - show url
+				 * 2 - menu is already on screen */
+
+    if (server_status == 303 ||
+	server_status == 302) {
+	/*
+	 * HTTP.c should not have called us for either of
+	 * these because we're treating 302 as historical,
+	 * so just return 303.  - FM
+	 */
+	return 303;
+    }
+
+    if (dump_output_immediately) {
+	if (server_status == 301) {
+	    /*
+	     * Treat 301 as historical, i.e., like 303 (GET
+	     * without content), when not interactive.  - FM
+	     */
+	    return 303;
+	} else {
+	    /*
+	     * Treat anything else (e.g., 305, 306 or 307) as too
+	     * dangerous to redirect without confirmation, and thus
+	     * cancel when not interactive.  - FM
+	     */
+	    return 0;
+	}
+    }
+
+    if (user_mode == NOVICE_MODE) {
+	on_screen = 2;
+	LYmove(LYlines - 2, 0);
+	HTSprintf0(&StatusInfo, SERVER_ASKED_FOR_REDIRECTION, server_status);
+	LYaddstr(StatusInfo);
+	LYclrtoeol();
+	LYmove(LYlines - 1, 0);
+	HTSprintf0(&url, "URL: %.*s",
+		   (LYcols < 250 ? LYcolLimit - 5 : 250), Redirecting_url);
+	LYaddstr(url);
+	LYclrtoeol();
+	if (server_status == 301) {
+	    _statusline(PROCEED_GET_CANCEL);
+	} else {
+	    _statusline(PROCEED_OR_CANCEL);
+	}
+    } else {
+	HTSprintf0(&StatusInfo, "%d %.*s",
+		   server_status,
+		   251,
+		   ((server_status == 301) ?
+		    ADVANCED_POST_GET_REDIRECT :
+		    ADVANCED_POST_REDIRECT));
+	StrAllocCopy(show_POST_url, LOCATION_HEADER);
+	StrAllocCat(show_POST_url, Redirecting_url);
+    }
+    while (result < 0) {
+	int c;
+
+	switch (on_screen) {
+	case 0:
+	    _statusline(StatusInfo);
+	    break;
+	case 1:
+	    _statusline(show_POST_url);
+	}
+	c = LYgetch_single();
+	switch (c) {
+	case 'P':
+	    /*
+	     * Proceed with 301 or 307 redirect of POST
+	     * with same method and POST content.  - FM
+	     */
+	    FREE(show_POST_url);
+	    result = 1;
+	    break;
+
+	case 7:
+	case 'C':
+	    /*
+	     * Cancel request.
+	     */
+	    FREE(show_POST_url);
+	    result = 0;
+	    break;
+
+	case 'U':
+	    /*
+	     * Show URL for intermediate or advanced mode.
+	     */
+	    if (user_mode != NOVICE_MODE) {
+		if (on_screen == 1) {
+		    on_screen = 0;
+		} else {
+		    on_screen = 1;
+		}
+	    }
+	    break;
+
+	case 'G':
+	    if (server_status == 301) {
+		/*
+		 * Treat as 303 (GET without content).
+		 */
+		FREE(show_POST_url);
+		result = 303;
+		break;
+	    }
+	    /* fall through to default */
+
+	default:
+	    /*
+	     * Get another character.
+	     */
+	    if (on_screen == 1) {
+		on_screen = 0;
+	    } else {
+		on_screen = 2;
+	    }
+	}
+    }
+    FREE(StatusInfo);
+    FREE(url);
+    return (result);
+}
+
+#define okToSleep() (!crawl && !traversal && LYCursesON && !no_pause)
+
+/*
+ * Sleep for the given message class's time.
+ */
+void LYSleepAlert(void)
+{
+    if (okToSleep())
+	LYSleep(AlertSecs);
+}
+
+void LYSleepDebug(void)
+{
+    if (okToSleep())
+	LYSleep(DebugSecs);
+}
+
+void LYSleepInfo(void)
+{
+    if (okToSleep())
+	LYSleep(InfoSecs);
+}
+
+void LYSleepMsg(void)
+{
+    if (okToSleep())
+	LYSleep(MessageSecs);
+}
+
+#ifdef USE_CMD_LOGGING
+void LYSleepReplay(void)
+{
+    if (okToSleep())
+	LYSleep(ReplaySecs);
+}
+#endif /* USE_CMD_LOGGING */
+
+/*
+ * LYstrerror emulates the ANSI strerror() function.
+ */
+#ifndef LYStrerror
+char *LYStrerror(int code)
+{
+    static char temp[80];
+
+    sprintf(temp, "System errno is %d.\r\n", code);
+    return temp;
+}
+#endif /* HAVE_STRERROR */
diff --git a/src/HTAlert.h b/src/HTAlert.h
new file mode 100644
index 00000000..0e093bbd
--- /dev/null
+++ b/src/HTAlert.h
@@ -0,0 +1,165 @@
+/*      Displaying messages and getting input for WWW Library
+ *      =====================================================
+ *
+ *         May 92 Created By C.T. Barker
+ *         Feb 93 Portablized etc TBL
+ */
+
+#ifndef HTALERT_H
+#define HTALERT_H 1
+
+#include <LYCookie.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#define ALERT_PREFIX_LEN 5
+/*      Display a message and get the input
+ *
+ *      On entry,
+ *              Msg is the message.
+ *
+ *      On exit,
+ *              Return value is malloc'd string which must be freed.
+ */ extern char *HTPrompt(const char *Msg, const char *deflt);
+
+/*      Display a message, don't wait for input
+ *
+ *      On entry,
+ *              The input is a list of parameters for printf.
+ */
+    extern void HTAlert(const char *Msg);
+    extern void HTAlwaysAlert(const char *extra_prefix, const char *Msg);
+    extern void HTInfoMsg(const char *Msg);
+    extern void HTInfoMsg2(const char *Msg, const char *Arg);
+    extern void HTUserMsg(const char *Msg);
+    extern void HTUserMsg2(const char *Msg, const char *Arg);
+
+/*      Display a progress message for information (and diagnostics) only
+ *
+ *      On entry,
+ *              The input is a list of parameters for printf.
+ */
+    extern const char *HTProgressUnits(int kilobytes);
+    extern void HTProgress(const char *Msg);
+    extern void HTReadProgress(off_t bytes, off_t total);
+
+#define _HTProgress(msg)	mustshow = TRUE, HTProgress(msg)
+
+/*
+ *  Indicates whether last HTConfirm was cancelled (^G or ^C) and
+ *  resets flag. (so only call once!) - kw
+ */
+    extern BOOL HTLastConfirmCancelled(void);
+
+/*
+ *	Supports logic for forced yes/no prompt results.
+ */
+    extern int HTForcedPrompt(int Opt, const char *Msg, int Dft);
+
+/*      Display a message, then wait for 'yes' or 'no', allowing default
+ *	response if a return or left-arrow is used.
+ *
+ *      On entry,
+ *              Takes a list of parameters for printf.
+ *
+ *      On exit,
+ *              If the user enters 'YES', returns TRUE, returns FALSE
+ *              otherwise.
+ */
+    extern int HTConfirmDefault(const char *Msg, int Dft);
+
+/*      Display a message, then wait for 'yes' or 'no'.
+ *
+ *      On entry,
+ *              Takes a list of parameters for printf.
+ *
+ *      On exit,
+ *              If the user enters 'YES', returns TRUE, returns FALSE
+ *              otherwise.
+ */
+    extern BOOL HTConfirm(const char *Msg);
+
+    extern BOOL confirm_post_resub(const char *address,
+				   const char *title,
+				   int if_imgmap,
+				   int if_file);
+
+/*      Prompt for password without echoing the reply
+ */
+    extern char *HTPromptPassword(const char *Msg);
+
+/*      Prompt both username and password       HTPromptUsernameAndPassword()
+ *      ---------------------------------
+ * On entry,
+ *      Msg             is the prompting message.
+ *      *username and
+ *      *password       are char pointers; they are changed
+ *                      to point to result strings.
+ *	IsProxy		should be TRUE if this is for
+ *			proxy authentication.
+ *
+ *                      If *username is not NULL, it is taken
+ *                      to point to  a default value.
+ *                      Initial value of *password is
+ *                      completely discarded.
+ *
+ * On exit,
+ *      *username and *password point to newly allocated
+ *      strings -- original strings pointed to by them
+ *      are NOT freed.
+ *
+ */
+    extern void HTPromptUsernameAndPassword(const char *Msg,
+					    char **username,
+					    char **password,
+					    BOOL IsProxy);
+
+/*	Confirm a cookie operation.			HTConfirmCookie()
+ *	---------------------------
+ *
+ *  On entry,
+ *	server			is the server sending the Set-Cookie.
+ *	domain			is the domain of the cookie.
+ *	path			is the path of the cookie.
+ *	name			is the name of the cookie.
+ *	value			is the value of the cookie.
+ *
+ *  On exit,
+ *	Returns FALSE on cancel,
+ *		TRUE if the cookie should be set.
+ */
+    extern BOOL HTConfirmCookie(domain_entry * dp, const char *server,
+				const char *name,
+				const char *value);
+
+/*      Confirm redirection of POST.		HTConfirmPostRedirect()
+ *	----------------------------
+ *  On entry,
+ *      Redirecting_url             is the Location.
+ *	server_status		    is the server status code.
+ *
+ *  On exit,
+ *      Returns 0 on cancel,
+ *	  1 for redirect of POST with content,
+ *	303 for redirect as GET without content
+ */
+    extern int HTConfirmPostRedirect(const char *Redirecting_url,
+				     int server_status);
+
+    extern void LYSleepAlert(void);
+    extern void LYSleepDebug(void);
+    extern void LYSleepInfo(void);
+    extern void LYSleepMsg(void);
+    extern void LYSleepReplay(void);
+
+#ifdef HAVE_STRERROR
+#define LYStrerror strerror
+#else
+    extern char *LYStrerror(int code);
+#endif				/* HAVE_STRERROR */
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* HTALERT_H */
diff --git a/src/HTFWriter.c b/src/HTFWriter.c
new file mode 100644
index 00000000..918da49f
--- /dev/null
+++ b/src/HTFWriter.c
@@ -0,0 +1,1410 @@
+/*
+ * $LynxId: HTFWriter.c,v 1.93 2010/04/30 00:01:09 tom Exp $
+ *
+ *		FILE WRITER				HTFWrite.h
+ *		===========
+ *
+ *	This version of the stream object just writes to a C file.
+ *	The file is assumed open and left open.
+ *
+ *	Bugs:
+ *		strings written must be less than buffer size.
+ */
+
+#include <HTUtils.h>
+#include <LYCurses.h>
+#include <HTFWriter.h>
+#include <HTSaveToFile.h>
+
+#ifdef WIN_EX
+#include <HTParse.h>
+#endif
+
+#include <HTFormat.h>
+#include <UCDefs.h>
+#include <HTAlert.h>
+#include <HTFile.h>
+#include <HTInit.h>
+#include <HTPlain.h>
+
+#include <LYStrings.h>
+#include <LYUtils.h>
+#include <LYGlobalDefs.h>
+#include <LYClean.h>
+#include <GridText.h>
+#include <LYExtern.h>
+#include <LYexit.h>
+#include <LYLeaks.h>
+#include <LYKeymap.h>
+#include <LYGetFile.h>
+#include <LYHistory.h>		/* store statusline messages */
+
+#ifdef USE_PERSISTENT_COOKIES
+#include <LYCookie.h>
+#endif
+
+/* contains the name of the temp file which is being downloaded into */
+char *WWW_Download_File = NULL;
+BOOLEAN LYCancelDownload = FALSE;	/* exported to HTFormat.c in libWWW */
+
+#ifdef VMS
+static char *FIXED_RECORD_COMMAND = NULL;
+
+#ifdef USE_COMMAND_FILE		/* Keep this as an option. - FM    */
+#define FIXED_RECORD_COMMAND_MASK "@Lynx_Dir:FIXED512 %s"
+#else
+#define FIXED_RECORD_COMMAND_MASK "%s"
+static unsigned long LYVMS_FixedLengthRecords(char *filename);
+#endif /* USE_COMMAND_FILE */
+#endif /* VMS */
+
+HTStream *HTSaveToFile(HTPresentation *pres,
+		       HTParentAnchor *anchor,
+		       HTStream *sink);
+
+/*	Stream Object
+ *	-------------
+ */
+struct _HTStream {
+    const HTStreamClass *isa;
+
+    FILE *fp;			/* The file we've opened */
+    char *end_command;		/* What to do on _free.  */
+    char *remove_command;	/* What to do on _abort. */
+    char *viewer_command;	/* Saved external viewer */
+    HTFormat input_format;	/* Original pres->rep     */
+    HTFormat output_format;	/* Original pres->rep_out */
+    HTParentAnchor *anchor;	/* Original stream's anchor. */
+    HTStream *sink;		/* Original stream's sink.   */
+#ifdef FNAMES_8_3
+    BOOLEAN idash;		/* remember position to become '.' */
+#endif
+};
+
+/*_________________________________________________________________________
+ *
+ *			A C T I O N	R O U T I N E S
+ *  Bug:
+ *	Most errors are ignored.
+ */
+
+/*	Error handling
+ *	------------------
+ */
+static void HTFWriter_error(HTStream *me, char *id)
+{
+    char buf[200];
+
+    sprintf(buf, "%.60s: %.60s: %.60s",
+	    id,
+	    me->isa->name,
+	    LYStrerror(errno));
+    HTAlert(buf);
+/*
+ * Only disaster results from:
+ *	me->isa->_abort(me, NULL);
+ */
+}
+
+/*	Character handling
+ *	------------------
+ */
+static void HTFWriter_put_character(HTStream *me, char c)
+{
+    if (me->fp) {
+	putc(c, me->fp);
+    }
+}
+
+/*	String handling
+ *	---------------
+ */
+static void HTFWriter_put_string(HTStream *me, const char *s)
+{
+    if (me->fp) {
+	fputs(s, me->fp);
+    }
+}
+
+/*	Buffer write.  Buffers can (and should!) be big.
+ *	------------
+ */
+static void HTFWriter_write(HTStream *me, const char *s, int l)
+{
+    size_t result;
+
+    if (me->fp) {
+	result = fwrite(s, 1, (unsigned) l, me->fp);
+	if (result != (size_t) l) {
+	    HTFWriter_error(me, "HTFWriter_write");
+	}
+    }
+}
+
+/*	Free an HTML object
+ *	-------------------
+ *
+ *	Note that the SGML parsing context is freed, but the created
+ *	object is not,
+ *	as it takes on an existence of its own unless explicitly freed.
+ */
+static void HTFWriter_free(HTStream *me)
+{
+    int len;
+    char *path = NULL;
+    char *addr = NULL;
+    BOOL use_zread = NO;
+    BOOLEAN found = FALSE;
+
+#ifdef WIN_EX
+    int status;
+    HANDLE cur_handle;
+
+    cur_handle = GetForegroundWindow();
+#endif
+
+    if (me->fp)
+	fflush(me->fp);
+    if (me->end_command) {	/* Temp file */
+	LYCloseTempFP(me->fp);
+#ifdef VMS
+	if (0 == strcmp(me->end_command, "SaveVMSBinaryFile")) {
+	    /*
+	     * It's a binary file saved to disk on VMS, which
+	     * we want to convert to fixed records format.  - FM
+	     */
+#ifdef USE_COMMAND_FILE
+	    LYSystem(FIXED_RECORD_COMMAND);
+#else
+	    LYVMS_FixedLengthRecords(FIXED_RECORD_COMMAND);
+#endif /* USE_COMMAND_FILE */
+	    FREE(FIXED_RECORD_COMMAND);
+
+	    if (me->remove_command) {
+		/* NEVER REMOVE THE FILE unless during an abort! */
+		FREE(me->remove_command);
+	    }
+	} else
+#endif /* VMS */
+	if (me->input_format == HTAtom_for("www/compressed")) {
+	    /*
+	     * It's a compressed file supposedly cached to
+	     * a temporary file for uncompression.  - FM
+	     */
+	    if (me->anchor->FileCache != NULL) {
+		BOOL skip_loadfile = (BOOL) (me->viewer_command != NULL);
+
+		/*
+		 * Save the path with the "gz" or "Z" suffix trimmed,
+		 * and remove any previous uncompressed copy.  - FM
+		 */
+		StrAllocCopy(path, me->anchor->FileCache);
+		if ((len = (int) strlen(path)) > 3 &&
+		    (!strcasecomp(&path[len - 2], "gz") ||
+		     !strcasecomp(&path[len - 2], "zz"))) {
+#ifdef USE_ZLIB
+		    if (!skip_loadfile) {
+			use_zread = YES;
+		    } else
+#endif /* USE_ZLIB */
+		    {
+			path[len - 3] = '\0';
+			remove(path);
+		    }
+		} else if (len > 4 && !strcasecomp(&path[len - 3], "bz2")) {
+#ifdef USE_BZLIB
+		    if (!skip_loadfile) {
+			use_zread = YES;
+		    } else
+#endif /* USE_BZLIB */
+		    {
+			path[len - 4] = '\0';
+			remove(path);
+		    }
+		} else if (len > 2 && !strcasecomp(&path[len - 1], "Z")) {
+		    path[len - 2] = '\0';
+		    remove(path);
+		}
+		if (!use_zread) {
+		    if (!dump_output_immediately) {
+			/*
+			 * Tell user what's happening.  - FM
+			 */
+			_HTProgress(me->end_command);
+		    }
+		    /*
+		     * Uncompress it.  - FM
+		     */
+		    if (me->end_command && me->end_command[0])
+			LYSystem(me->end_command);
+		    found = LYCanReadFile(me->anchor->FileCache);
+		}
+		if (found) {
+		    /*
+		     * It's still there with the "gz" or "Z" suffix,
+		     * so the uncompression failed.  - FM
+		     */
+		    if (!dump_output_immediately) {
+			lynx_force_repaint();
+			LYrefresh();
+		    }
+		    HTAlert(ERROR_UNCOMPRESSING_TEMP);
+		    LYRemoveTemp(me->anchor->FileCache);
+		    FREE(me->anchor->FileCache);
+		} else {
+		    /*
+		     * Succeeded!  Create a complete address
+		     * for the uncompressed file and invoke
+		     * HTLoadFile() to handle it.  - FM
+		     */
+#ifdef FNAMES_8_3
+		    /*
+		     * Assuming we have just uncompressed e.g.
+		     * FILE-mpeg.gz -> FILE-mpeg, restore/shorten
+		     * the name to be fit for passing to an external
+		     * viewer, by renaming FILE-mpeg -> FILE.mpe - kw
+		     */
+		    if (skip_loadfile) {
+			char *new_path = NULL;
+			char *the_dash = me->idash ? strrchr(path, '-') : 0;
+
+			if (the_dash != 0) {
+			    unsigned off = (the_dash - path);
+
+			    StrAllocCopy(new_path, path);
+			    new_path[off] = '.';
+			    if (strlen(new_path + off) > 4)
+				new_path[off + 4] = '\0';
+			    if (rename(path, new_path) == 0) {
+				FREE(path);
+				path = new_path;
+			    } else {
+				FREE(new_path);
+			    }
+			}
+		    }
+#endif /* FNAMES_8_3 */
+		    LYLocalFileToURL(&addr, path);
+		    if (!use_zread) {
+			LYRenamedTemp(me->anchor->FileCache, path);
+			StrAllocCopy(me->anchor->FileCache, path);
+			StrAllocCopy(me->anchor->content_encoding, "binary");
+		    }
+		    FREE(path);
+		    if (!skip_loadfile) {
+			/*
+			 * Lock the chartrans info we may possibly have,
+			 * so HTCharsetFormat() will not apply the default
+			 * for local files.  - KW
+			 */
+			if (HTAnchor_getUCLYhndl(me->anchor,
+						 UCT_STAGE_PARSER) < 0) {
+			    /*
+			     * If not yet set - KW
+			     */
+			    HTAnchor_copyUCInfoStage(me->anchor,
+						     UCT_STAGE_PARSER,
+						     UCT_STAGE_MIME,
+						     UCT_SETBY_DEFAULT + 1);
+			}
+			HTAnchor_copyUCInfoStage(me->anchor,
+						 UCT_STAGE_PARSER,
+						 UCT_STAGE_MIME, -1);
+		    }
+		    /*
+		     * Create a complete address for
+		     * the uncompressed file.  - FM
+		     */
+		    if (!dump_output_immediately) {
+			/*
+			 * Tell user what's happening.  - FM
+			 * HTInfoMsg2(WWW_USING_MESSAGE, addr);
+			 * but only in the history, not on screen -RS
+			 */
+			LYstore_message2(WWW_USING_MESSAGE, addr);
+		    }
+
+		    if (skip_loadfile) {
+			/*
+			 * It's a temporary file we're passing to a viewer or
+			 * helper application.  Loading the temp file through
+			 * HTLoadFile() would result in yet another HTStream
+			 * (created with HTSaveAndExecute()) which would just
+			 * copy the temp file to another temp file (or even the
+			 * same!).  We can skip this needless duplication by
+			 * using the viewer_command which has already been
+			 * determined when the HTCompressed stream was created. 
+			 * - kw
+			 */
+			FREE(me->end_command);
+
+			HTAddParam(&(me->end_command), me->viewer_command, 1, me->anchor->FileCache);
+			HTEndParam(&(me->end_command), me->viewer_command, 1);
+
+			if (!dump_output_immediately) {
+			    /*
+			     * Tell user what's happening.  - FM
+			     */
+			    HTProgress(me->end_command);
+#ifndef WIN_EX
+			    stop_curses();
+#endif
+			}
+#ifdef _WIN_CC
+			exec_command(me->end_command, FALSE);
+#else
+			LYSystem(me->end_command);
+#endif
+			if (me->remove_command) {
+			    /* NEVER REMOVE THE FILE unless during an abort!!! */
+			    FREE(me->remove_command);
+			}
+			if (!dump_output_immediately) {
+#ifdef WIN_EX
+			    if (focus_window) {
+				HTInfoMsg(gettext("Set focus1"));
+				status = SetForegroundWindow(cur_handle);
+			    }
+#else
+			    start_curses();
+#endif
+			}
+		    } else {
+			(void) HTLoadFile(addr,
+					  me->anchor,
+					  me->output_format,
+					  me->sink);
+		    }
+		    if (dump_output_immediately &&
+			me->output_format == HTAtom_for("www/present")) {
+			FREE(addr);
+			remove(me->anchor->FileCache);
+			FREE(me->anchor->FileCache);
+			FREE(me->remove_command);
+			FREE(me->end_command);
+			FREE(me->viewer_command);
+			FREE(me);
+			return;
+		    }
+		}
+		FREE(addr);
+	    }
+	    if (me->remove_command) {
+		/* NEVER REMOVE THE FILE unless during an abort!!! */
+		FREE(me->remove_command);
+	    }
+	} else if (strcmp(me->end_command, "SaveToFile")) {
+	    /*
+	     * It's a temporary file we're passing to a viewer or helper
+	     * application.  - FM
+	     */
+	    if (!dump_output_immediately) {
+		/*
+		 * Tell user what's happening.  - FM
+		 */
+		_HTProgress(me->end_command);
+#ifndef WIN_EX
+		stop_curses();
+#endif
+	    }
+#ifdef _WIN_CC
+	    exec_command(me->end_command, FALSE);
+#else
+	    LYSystem(me->end_command);
+#endif
+
+	    if (me->remove_command) {
+		/* NEVER REMOVE THE FILE unless during an abort!!! */
+		FREE(me->remove_command);
+	    }
+	    if (!dump_output_immediately) {
+#ifdef WIN_EX
+		if (focus_window) {
+		    HTInfoMsg(gettext("Set focus2"));
+		    status = SetForegroundWindow(cur_handle);
+		}
+#else
+		start_curses();
+#endif
+	    }
+	} else {
+	    /*
+	     * It's a file we saved to disk for handling via a menu.  - FM
+	     */
+	    if (me->remove_command) {
+		/* NEVER REMOVE THE FILE unless during an abort!!! */
+		FREE(me->remove_command);
+	    }
+	    if (!dump_output_immediately) {
+#ifdef WIN_EX
+		if (focus_window) {
+		    HTInfoMsg(gettext("Set focus3"));
+		    status = SetForegroundWindow(cur_handle);
+		}
+#else
+		start_curses();
+#endif
+	    }
+	}
+	FREE(me->end_command);
+    }
+    FREE(me->viewer_command);
+
+    if (dump_output_immediately) {
+	if (me->anchor->FileCache)
+	    remove(me->anchor->FileCache);
+	FREE(me);
+#ifdef USE_PERSISTENT_COOKIES
+	/*
+	 * We want to save cookies picked up when in source mode.  ...
+	 */
+	if (persistent_cookies)
+	    LYStoreCookies(LYCookieSaveFile);
+#endif /* USE_PERSISTENT_COOKIES */
+	exit_immediately(EXIT_SUCCESS);
+    }
+
+    FREE(me);
+    return;
+}
+
+#ifdef VMS
+#  define REMOVE_COMMAND "delete/noconfirm/nolog %s;"
+#else
+#  define REMOVE_COMMAND "%s"
+#endif /* VMS */
+
+/*	Abort writing
+ *	-------------
+ */
+static void HTFWriter_abort(HTStream *me, HTError e GCC_UNUSED)
+{
+    CTRACE((tfp, "HTFWriter_abort called\n"));
+    LYCloseTempFP(me->fp);
+    FREE(me->viewer_command);
+    if (me->end_command) {	/* Temp file */
+	CTRACE((tfp, "HTFWriter: Aborting: file not executed or saved.\n"));
+	FREE(me->end_command);
+	if (me->remove_command) {
+#ifdef VMS
+	    LYSystem(me->remove_command);
+#else
+	    chmod(me->remove_command, 0600);	/* Ignore errors */
+	    if (0 != unlink(me->remove_command)) {
+		char buf[560];
+
+		sprintf(buf, "%.60s '%.400s': %.60s",
+			gettext("Error deleting file"),
+			me->remove_command, LYStrerror(errno));
+		HTAlert(buf);
+	    }
+#endif
+	    FREE(me->remove_command);
+	}
+    }
+
+    FREE(WWW_Download_File);
+
+    FREE(me);
+}
+
+/*	Structured Object Class
+ *	-----------------------
+ */
+static const HTStreamClass HTFWriter =	/* As opposed to print etc */
+{
+    "FileWriter",
+    HTFWriter_free,
+    HTFWriter_abort,
+    HTFWriter_put_character,
+    HTFWriter_put_string,
+    HTFWriter_write
+};
+
+/*	Subclass-specific Methods
+ *	-------------------------
+ */
+HTStream *HTFWriter_new(FILE *fp)
+{
+    HTStream *me;
+
+    if (!fp)
+	return NULL;
+
+    me = typecalloc(HTStream);
+    if (me == NULL)
+	outofmem(__FILE__, "HTFWriter_new");
+
+    assert(me != NULL);
+
+    me->isa = &HTFWriter;
+
+    me->fp = fp;
+    me->end_command = NULL;
+    me->remove_command = NULL;
+    me->anchor = NULL;
+    me->sink = NULL;
+
+    return me;
+}
+
+/*	Make system command from template
+ *	---------------------------------
+ *
+ *	See mailcap spec for description of template.
+ */
+static char *mailcap_substitute(HTParentAnchor *anchor,
+				HTPresentation *pres,
+				char *fnam)
+{
+    char *result = LYMakeMailcapCommand(pres->command,
+					anchor->content_type_params,
+					fnam);
+
+#if defined(UNIX)
+    /* if we don't have a "%s" token, expect to provide the file via stdin */
+    if (!LYMailcapUsesPctS(pres->command)) {
+	char *prepend = 0;
+	const char *format = "( %s ) < %s";
+
+	HTSprintf(&prepend, "( %s", result);	/* ...avoid quoting */
+	HTAddParam(&prepend, format, 2, fnam);	/* ...to quote if needed */
+	FREE(result);
+	result = prepend;
+    }
+#endif
+    return result;
+}
+
+/*	Take action using a system command
+ *	----------------------------------
+ *
+ *	originally from Ghostview handling by Marc Andreseen.
+ *	Creates temporary file, writes to it, executes system command
+ *	on end-document.  The suffix of the temp file can be given
+ *	in case the application is fussy, or so that a generic opener can
+ *	be used.
+ */
+HTStream *HTSaveAndExecute(HTPresentation *pres,
+			   HTParentAnchor *anchor,
+			   HTStream *sink)
+{
+    char fnam[LY_MAXPATH];
+    const char *suffix;
+    HTStream *me;
+
+    if (traversal) {
+	LYCancelledFetch = TRUE;
+	return (NULL);
+    }
+#if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)
+    if (pres->quality >= 999.0) {	/* exec link */
+	if (dump_output_immediately) {
+	    LYCancelledFetch = TRUE;
+	    return (NULL);
+	}
+	if (no_exec) {
+	    HTAlert(EXECUTION_DISABLED);
+	    return HTPlainPresent(pres, anchor, sink);
+	}
+	if (!local_exec) {
+	    if (local_exec_on_local_files &&
+		(LYJumpFileURL ||
+		 !strncmp(anchor->address, "file://localhost", 16))) {
+		/* allow it to continue */
+		;
+	    } else {
+		char *buf = 0;
+
+		HTSprintf0(&buf, EXECUTION_DISABLED_FOR_FILE,
+			   key_for_func(LYK_OPTIONS));
+		HTAlert(buf);
+		FREE(buf);
+		return HTPlainPresent(pres, anchor, sink);
+	    }
+	}
+    }
+#endif /* EXEC_LINKS || EXEC_SCRIPTS */
+
+    if (dump_output_immediately) {
+	return (HTSaveToFile(pres, anchor, sink));
+    }
+
+    me = typecalloc(HTStream);
+    if (me == NULL)
+	outofmem(__FILE__, "HTSaveAndExecute");
+
+    assert(me != NULL);
+
+    me->isa = &HTFWriter;
+    me->input_format = pres->rep;
+    me->output_format = pres->rep_out;
+    me->anchor = anchor;
+    me->sink = sink;
+
+    if (LYCachedTemp(fnam, &(anchor->FileCache))) {
+	/* This used to be LYNewBinFile(fnam); changed to a different call so
+	 * that the open fp gets registered in the list keeping track of temp
+	 * files, equivalent to when LYOpenTemp() gets called below.  This
+	 * avoids a file descriptor leak caused by LYCloseTempFP() not being
+	 * able to find the fp.  The binary suffix is expected to not be used,
+	 * it's only for fallback in unusual error cases.  - kw
+	 */
+	me->fp = LYOpenTempRewrite(fnam, BIN_SUFFIX, BIN_W);
+    } else {
+#if defined(WIN_EX) && !defined(__CYGWIN__)	/* 1998/01/04 (Sun) */
+	if (!strncmp(anchor->address, "file://localhost", 16)) {
+
+	    /* 1998/01/23 (Fri) 17:38:26 */
+	    unsigned char *cp, *view_fname;
+
+#define IS_SJIS_HI1(hi) ((0x81<=hi)&&(hi<=0x9F))	/* 1st lev. */
+#define IS_SJIS_HI2(hi) ((0xE0<=hi)&&(hi<=0xEF))	/* 2nd lev. */
+
+	    me->fp = NULL;
+
+	    view_fname = fnam + 3;
+	    LYstrncpy(view_fname, anchor->address + 17, sizeof(fnam) - 5);
+	    HTUnEscape(view_fname);
+
+	    if (strchr(view_fname, ':') == NULL) {
+		fnam[0] = windows_drive[0];
+		fnam[1] = windows_drive[1];
+		fnam[2] = '/';
+		view_fname = fnam;
+	    }
+
+	    /* 1998/04/21 (Tue) 11:04:16 */
+	    cp = view_fname;
+	    while (*cp) {
+		if (IS_SJIS_HI1(*cp) || IS_SJIS_HI2(*cp)) {
+		    cp += 2;
+		    continue;
+		} else if (*cp == '/') {
+		    *cp = '\\';
+		}
+		cp++;
+	    }
+	    if (strchr(view_fname, ' '))
+		view_fname = (unsigned char *) quote_pathname(view_fname);
+
+	    StrAllocCopy(me->viewer_command, pres->command);
+
+	    me->end_command = mailcap_substitute(anchor, pres, view_fname);
+	    me->remove_command = NULL;
+
+	    return me;
+	}
+#endif
+	/*
+	 * Check for a suffix.
+	 * Save the file under a suitably suffixed name.
+	 */
+	if (!strcasecomp(pres->rep->name, "text/html")) {
+	    suffix = HTML_SUFFIX;
+	} else if (!strncasecomp(pres->rep->name, "text/", 5)) {
+	    suffix = TEXT_SUFFIX;
+	} else if ((suffix = HTFileSuffix(pres->rep,
+					  anchor->content_encoding)) == 0
+		   || *suffix != '.') {
+	    if (!strncasecomp(pres->rep->name, "application/", 12)) {
+		suffix = BIN_SUFFIX;
+	    } else {
+		suffix = HTML_SUFFIX;
+	    }
+	}
+	me->fp = LYOpenTemp(fnam, suffix, BIN_W);
+    }
+
+    if (!me->fp) {
+	HTAlert(CANNOT_OPEN_TEMP);
+	FREE(me);
+	return NULL;
+    }
+
+    StrAllocCopy(me->viewer_command, pres->command);
+    /*
+     * Make command to process file.
+     */
+    me->end_command = mailcap_substitute(anchor, pres, fnam);
+
+    /*
+     * Make command to delete file.
+     */
+    me->remove_command = 0;
+    HTAddParam(&(me->remove_command), REMOVE_COMMAND, 1, fnam);
+    HTEndParam(&(me->remove_command), REMOVE_COMMAND, 1);
+
+    StrAllocCopy(anchor->FileCache, fnam);
+    return me;
+}
+
+/*	Format Converter using system command
+ *	-------------------------------------
+ */
+
+/* @@@@@@@@@@@@@@@@@@@@@@ */
+
+/*	Save to a local file   LJM!!!
+ *	--------------------
+ *
+ *	usually a binary file that can't be displayed
+ *
+ *	originally from Ghostview handling by Marc Andreseen.
+ *	Asks the user if he wants to continue, creates a temporary
+ *	file, and writes to it.  In HTSaveToFile_Free
+ *	the user will see a list of choices for download
+ */
+HTStream *HTSaveToFile(HTPresentation *pres,
+		       HTParentAnchor *anchor,
+		       HTStream *sink)
+{
+    HTStream *ret_obj;
+    char fnam[LY_MAXPATH];
+    const char *suffix;
+    char *cp;
+    int c = 0;
+
+#ifdef VMS
+    BOOL IsBinary = TRUE;
+#endif
+
+    ret_obj = typecalloc(HTStream);
+
+    if (ret_obj == NULL)
+	outofmem(__FILE__, "HTSaveToFile");
+
+    assert(ret_obj != NULL);
+
+    ret_obj->isa = &HTFWriter;
+    ret_obj->remove_command = NULL;
+    ret_obj->end_command = NULL;
+    ret_obj->input_format = pres->rep;
+    ret_obj->output_format = pres->rep_out;
+    ret_obj->anchor = anchor;
+    ret_obj->sink = sink;
+
+    if (dump_output_immediately) {
+	ret_obj->fp = stdout;	/* stdout */
+	if (HTOutputFormat == HTAtom_for("www/download"))
+	    goto Prepend_BASE;
+	return ret_obj;
+    }
+
+    LYCancelDownload = FALSE;
+    if (HTOutputFormat != HTAtom_for("www/download")) {
+	if (traversal ||
+	    (no_download && !override_no_download && no_disk_save)) {
+	    if (!traversal) {
+		HTAlert(CANNOT_DISPLAY_FILE);
+	    }
+	    LYCancelDownload = TRUE;
+	    if (traversal)
+		LYCancelledFetch = TRUE;
+	    FREE(ret_obj);
+	    return (NULL);
+	}
+
+	if (((cp = strchr(pres->rep->name, ';')) != NULL) &&
+	    strstr((cp + 1), "charset") != NULL) {
+	    _user_message(MSG_DOWNLOAD_OR_CANCEL, pres->rep->name);
+	} else if (*(pres->rep->name) != '\0') {
+	    _user_message(MSG_DOWNLOAD_OR_CANCEL, pres->rep->name);
+	} else {
+	    _statusline(CANNOT_DISPLAY_FILE_D_OR_C);
+	}
+
+	while (c != 'D' && c != 'C' && !LYCharIsINTERRUPT(c)) {
+	    c = LYgetch_single();
+#ifdef VMS
+	    /*
+	     * 'C'ancel on Control-C or Control-Y and
+	     * a 'N'o to the "really exit" query.  - FM
+	     */
+	    if (HadVMSInterrupt) {
+		HadVMSInterrupt = FALSE;
+		c = 'C';
+	    }
+#endif /* VMS */
+	}
+
+	/*
+	 * Cancel on 'C', 'c' or Control-G or Control-C.
+	 */
+	if (c == 'C' || LYCharIsINTERRUPT(c)) {
+	    _statusline(CANCELLING_FILE);
+	    LYCancelDownload = TRUE;
+	    FREE(ret_obj);
+	    return (NULL);
+	}
+    }
+
+    /*
+     * Set up a 'D'ownload.
+     */
+    if (LYCachedTemp(fnam, &(anchor->FileCache))) {
+	/* This used to be LYNewBinFile(fnam); changed to a different call so
+	 * that the open fp gets registered in the list keeping track of temp
+	 * files, equivalent to when LYOpenTemp() gets called below.  This
+	 * avoids a file descriptor leak caused by LYCloseTempFP() not being
+	 * able to find the fp.  The binary suffix is expected to not be used,
+	 * it's only for fallback in unusual error cases.  - kw
+	 */
+	ret_obj->fp = LYOpenTempRewrite(fnam, BIN_SUFFIX, BIN_W);
+    } else {
+	/*
+	 * Check for a suffix.
+	 * Save the file under a suitably suffixed name.
+	 */
+	if (!strcasecomp(pres->rep->name, "text/html")) {
+	    suffix = HTML_SUFFIX;
+	} else if (!strncasecomp(pres->rep->name, "text/", 5)) {
+	    suffix = TEXT_SUFFIX;
+	} else if (!strncasecomp(pres->rep->name, "application/", 12)) {
+	    suffix = BIN_SUFFIX;
+	} else if ((suffix = HTFileSuffix(pres->rep,
+					  anchor->content_encoding)) == 0
+		   || *suffix != '.') {
+	    suffix = HTML_SUFFIX;
+	}
+	ret_obj->fp = LYOpenTemp(fnam, suffix, BIN_W);
+    }
+
+    if (!ret_obj->fp) {
+	HTAlert(CANNOT_OPEN_OUTPUT);
+	FREE(ret_obj);
+	return NULL;
+    }
+
+    if (0 == strncasecomp(pres->rep->name, "text/", 5) ||
+	0 == strcasecomp(pres->rep->name, "application/postscript") ||
+	0 == strcasecomp(pres->rep->name, "application/x-RUNOFF-MANUAL"))
+	/*
+	 * It's a text file requested via 'd'ownload.  Keep adding others to
+	 * the above list, 'til we add a configurable procedure.  - FM
+	 */
+#ifdef VMS
+	IsBinary = FALSE;
+#endif
+
+    /*
+     * Any "application/foo" or other non-"text/foo" types that are actually
+     * text but not checked, above, will be treated as binary, so show the type
+     * to help sort that out later.  Unix folks don't need to know this, but
+     * we'll show it to them, too.  - FM
+     */
+    HTInfoMsg2(CONTENT_TYPE_MSG, pres->rep->name);
+
+    StrAllocCopy(WWW_Download_File, fnam);
+
+    /*
+     * Make command to delete file.
+     */
+    ret_obj->remove_command = 0;
+    HTAddParam(&(ret_obj->remove_command), REMOVE_COMMAND, 1, fnam);
+    HTEndParam(&(ret_obj->remove_command), REMOVE_COMMAND, 1);
+
+#ifdef VMS
+    if (IsBinary && UseFixedRecords) {
+	StrAllocCopy(ret_obj->end_command, "SaveVMSBinaryFile");
+	FIXED_RECORD_COMMAND = 0;
+	HTAddParam(&FIXED_RECORD_COMMAND, FIXED_RECORD_COMMAND_MASK, 1, fnam);
+	HTEndParam(&FIXED_RECORD_COMMAND, FIXED_RECORD_COMMAND_MASK, 1);
+
+    } else {
+#endif /* VMS */
+	StrAllocCopy(ret_obj->end_command, "SaveToFile");
+#ifdef VMS
+    }
+#endif /* VMS */
+
+    _statusline(RETRIEVING_FILE);
+
+    StrAllocCopy(anchor->FileCache, fnam);
+  Prepend_BASE:
+    if (LYPrependBaseToSource &&
+	!strncasecomp(pres->rep->name, "text/html", 9) &&
+	!anchor->content_encoding) {
+	/*
+	 * Add the document's base as a BASE tag at the top of the file, so
+	 * that any partial or relative URLs within it will be resolved
+	 * relative to that if no BASE tag is present and replaces it.  Note
+	 * that the markup will be technically invalid if a DOCTYPE
+	 * declaration, or HTML or HEAD tags, are present, and thus the file
+	 * may need editing for perfection.  - FM
+	 *
+	 * Add timestamp (last reload).
+	 */
+	char *temp = NULL;
+
+	if (non_empty(anchor->content_base)) {
+	    StrAllocCopy(temp, anchor->content_base);
+	} else if (non_empty(anchor->content_location)) {
+	    StrAllocCopy(temp, anchor->content_location);
+	}
+	if (temp) {
+	    LYRemoveBlanks(temp);
+	    if (!is_url(temp)) {
+		FREE(temp);
+	    }
+	}
+
+	fprintf(ret_obj->fp,
+		"<!-- X-URL: %s -->\n", anchor->address);
+	if (non_empty(anchor->date)) {
+	    fprintf(ret_obj->fp,
+		    "<!-- Date: %s -->\n", anchor->date);
+	    if (non_empty(anchor->last_modified)
+		&& strcmp(anchor->last_modified, anchor->date)
+		&& strcmp(anchor->last_modified,
+			  "Thu, 01 Jan 1970 00:00:01 GMT")) {
+		fprintf(ret_obj->fp,
+			"<!-- Last-Modified: %s -->\n", anchor->last_modified);
+	    }
+	}
+	fprintf(ret_obj->fp,
+		"<BASE HREF=\"%s\">\n\n", (temp ? temp : anchor->address));
+	FREE(temp);
+    }
+    if (LYPrependCharsetToSource &&
+	!strncasecomp(pres->rep->name, "text/html", 9) &&
+	!anchor->content_encoding) {
+	/*
+	 * Add the document's charset as a META CHARSET tag at the top of the
+	 * file, so HTTP charset header will not be forgotten when a document
+	 * saved as local file.  We add this line only(!) if HTTP charset
+	 * present.  - LP Note that the markup will be technically invalid if a
+	 * DOCTYPE declaration, or HTML or HEAD tags, are present, and thus the
+	 * file may need editing for perfection.  - FM
+	 */
+	char *temp = NULL;
+
+	if (non_empty(anchor->charset)) {
+	    StrAllocCopy(temp, anchor->charset);
+	    LYRemoveBlanks(temp);
+	    fprintf(ret_obj->fp,
+		    "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=%s\">\n\n",
+		    temp);
+	}
+	FREE(temp);
+    }
+    return ret_obj;
+}
+
+/*	Set up stream for uncompressing - FM
+ *	-------------------------------
+ */
+HTStream *HTCompressed(HTPresentation *pres,
+		       HTParentAnchor *anchor,
+		       HTStream *sink)
+{
+    HTStream *me;
+    HTFormat format;
+    char *type = NULL;
+    HTPresentation *Pres = NULL;
+    HTPresentation *Pnow = NULL;
+    int n, i;
+    BOOL can_present = FALSE;
+    char fnam[LY_MAXPATH];
+    char temp[LY_MAXPATH];	/* actually stores just a suffix */
+    const char *suffix;
+    char *uncompress_mask = NULL;
+    const char *compress_suffix = "";
+    const char *middle;
+
+    /*
+     * Deal with any inappropriate invocations of this function, or a download
+     * request, in which case we won't bother to uncompress the file.  - FM
+     */
+    if (!(anchor && anchor->content_encoding && anchor->content_type)) {
+	/*
+	 * We have no idea what we're dealing with, so treat it as a binary
+	 * stream.  - FM
+	 */
+	format = HTAtom_for("application/octet-stream");
+	me = HTStreamStack(format, pres->rep_out, sink, anchor);
+	return me;
+    }
+    n = HTList_count(HTPresentations);
+    for (i = 0; i < n; i++) {
+	Pnow = (HTPresentation *) HTList_objectAt(HTPresentations, i);
+	if (!strcasecomp(Pnow->rep->name, anchor->content_type) &&
+	    Pnow->rep_out == WWW_PRESENT) {
+	    const char *program = "";
+
+	    /*
+	     * Pick the best presentation.  User-defined mappings are at the
+	     * end of the list, and unless the quality is lower, we prefer
+	     * those.
+	     */
+	    if (Pres == 0)
+		Pres = Pnow;
+	    else if (Pres->quality > Pnow->quality)
+		continue;
+	    else
+		Pres = Pnow;
+	    /*
+	     * We have a presentation mapping for it.  - FM
+	     */
+	    can_present = TRUE;
+	    switch (HTEncodingToCompressType(anchor->content_encoding)) {
+	    case cftGzip:
+		if ((program = HTGetProgramPath(ppGZIP)) != NULL) {
+		    /*
+		     * It's compressed with the modern gzip.  - FM
+		     */
+		    StrAllocCopy(uncompress_mask, program);
+		    StrAllocCat(uncompress_mask, " -d --no-name %s");
+		    compress_suffix = "gz";
+		}
+		break;
+	    case cftDeflate:
+		if ((program = HTGetProgramPath(ppINFLATE)) != NULL) {
+		    /*
+		     * It's compressed with a zlib wrapper.
+		     */
+		    StrAllocCopy(uncompress_mask, program);
+		    StrAllocCat(uncompress_mask, " %s");
+		    compress_suffix = "zz";
+		}
+		break;
+	    case cftBzip2:
+		if ((program = HTGetProgramPath(ppBZIP2)) != NULL) {
+		    StrAllocCopy(uncompress_mask, program);
+		    StrAllocCat(uncompress_mask, " -d %s");
+		    compress_suffix = "bz2";
+		}
+		break;
+	    case cftCompress:
+		if ((program = HTGetProgramPath(ppUNCOMPRESS)) != NULL) {
+		    /*
+		     * It's compressed the old fashioned Unix way.  - FM
+		     */
+		    StrAllocCopy(uncompress_mask, program);
+		    StrAllocCat(uncompress_mask, " %s");
+		    compress_suffix = "Z";
+		}
+		break;
+	    case cftNone:
+		break;
+	    }
+	}
+    }
+    if (can_present == FALSE ||	/* no presentation mapping */
+	uncompress_mask == NULL ||	/* not gzip or compress */
+	strchr(anchor->content_type, ';') ||	/* wrong charset */
+	HTOutputFormat == HTAtom_for("www/download") ||		/* download */
+	!strcasecomp(pres->rep_out->name, "www/download") ||	/* download */
+	(traversal &&		/* only handle html or plain text for traversals */
+	 strcasecomp(anchor->content_type, "text/html") &&
+	 strcasecomp(anchor->content_type, "text/plain"))) {
+	/*
+	 * Cast the Content-Encoding to a Content-Type and pass it back to be
+	 * handled as that type.  - FM
+	 */
+	if (strchr(anchor->content_encoding, '/') == NULL) {
+	    /*
+	     * Use "x-" prefix, none of the types we are likely to construct
+	     * here are official.  That is we generate "application/x-gzip" and
+	     * so on.  - kw
+	     */
+	    if (!strncasecomp(anchor->content_encoding, "x-", 2))
+		StrAllocCopy(type, "application/");
+	    else
+		StrAllocCopy(type, "application/x-");
+	    StrAllocCat(type, anchor->content_encoding);
+	} else {
+	    StrAllocCopy(type, anchor->content_encoding);
+	}
+	format = HTAtom_for(type);
+	FREE(type);
+	FREE(uncompress_mask);
+	me = HTStreamStack(format, pres->rep_out, sink, anchor);
+	return me;
+    }
+
+    /*
+     * Set up the stream structure for uncompressing and then handling based on
+     * the uncompressed Content-Type.- FM
+     */
+    me = typecalloc(HTStream);
+    if (me == NULL)
+	outofmem(__FILE__, "HTCompressed");
+
+    assert(me != NULL);
+
+    me->isa = &HTFWriter;
+    me->input_format = pres->rep;
+    me->output_format = pres->rep_out;
+    me->anchor = anchor;
+    me->sink = sink;
+#ifdef FNAMES_8_3
+    me->idash = FALSE;
+#endif
+
+    /*
+     * Remove any old versions of the file.  - FM
+     */
+    if (anchor->FileCache) {
+	LYRemoveTemp(anchor->FileCache);
+	FREE(anchor->FileCache);
+    }
+
+    /*
+     * Get a new temporary filename and substitute a suitable suffix.  - FM
+     */
+    middle = NULL;
+    if (!strcasecomp(anchor->content_type, "text/html")) {
+	middle = HTML_SUFFIX;
+	middle++;		/* point to 'h' of .htm(l) - kw */
+    } else if (!strncasecomp(anchor->content_type, "text/", 5)) {
+	middle = TEXT_SUFFIX + 1;
+    } else if (!strncasecomp(anchor->content_type, "application/", 12)) {
+	/* FIXME: why is this BEFORE HTFileSuffix? */
+	middle = BIN_SUFFIX + 1;
+    } else if ((suffix =
+		HTFileSuffix(HTAtom_for(anchor->content_type), NULL)) &&
+	       *suffix == '.') {
+#if defined(VMS) || defined(FNAMES_8_3)
+	if (strchr(suffix + 1, '.') == NULL)
+#endif
+	    middle = suffix + 1;
+    }
+
+    temp[0] = 0;		/* construct the suffix */
+    if (middle) {
+#ifdef FNAMES_8_3
+	me->idash = TRUE;	/* remember position of '-'  - kw */
+	strcat(temp, "-");	/* NAME-htm,  NAME-txt, etc. - hack for DOS */
+#else
+	strcat(temp, ".");	/* NAME.html, NAME-txt etc. */
+#endif /* FNAMES_8_3 */
+	strcat(temp, middle);
+#ifdef VMS
+	strcat(temp, "-");	/* NAME.html-gz, NAME.txt-gz, NAME.txt-Z etc. */
+#else
+	strcat(temp, ".");	/* NAME-htm.gz (DOS), NAME.html.gz (UNIX)etc. */
+#endif /* VMS */
+    }
+    strcat(temp, compress_suffix);
+
+    /*
+     * Open the file for receiving the compressed input stream.  - FM
+     */
+    me->fp = LYOpenTemp(fnam, temp, BIN_W);
+    if (!me->fp) {
+	HTAlert(CANNOT_OPEN_TEMP);
+	FREE(uncompress_mask);
+	FREE(me);
+	return NULL;
+    }
+
+    /*
+     * me->viewer_command will be NULL if the converter Pres found above is not
+     * for an external viewer but an internal HTStream converter.  We also
+     * don't set it under conditions where HTSaveAndExecute would disallow
+     * execution of the command.  - KW
+     */
+    if (!dump_output_immediately && !traversal
+#if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)
+	&& (Pres->quality < 999.0 ||
+	    (!no_exec &&	/* allowed exec link or script ? */
+	     (local_exec ||
+	      (local_exec_on_local_files &&
+	       (LYJumpFileURL ||
+		!strncmp(anchor->address, "file://localhost", 16))))))
+#endif /* EXEC_LINKS || EXEC_SCRIPTS */
+	) {
+	StrAllocCopy(me->viewer_command, Pres->command);
+    }
+
+    /*
+     * Make command to process file.  - FM
+     */
+#ifdef USE_BZLIB
+    if (compress_suffix[0] == 'b'	/* must be bzip2 */
+	&& !me->viewer_command) {
+	/*
+	 * We won't call bzip2 externally, so we don't need to supply a command
+	 * for it.
+	 */
+	StrAllocCopy(me->end_command, "");
+    } else
+#endif
+#ifdef USE_ZLIB
+	/* FIXME: allow deflate here, e.g., 'z' */
+	if (compress_suffix[0] == 'g'	/* must be gzip */
+	    && !me->viewer_command) {
+	/*
+	 * We won't call gzip or compress externally, so we don't need to
+	 * supply a command for it.
+	 */
+	StrAllocCopy(me->end_command, "");
+    } else
+#endif /* USE_ZLIB */
+    {
+	me->end_command = 0;
+	HTAddParam(&(me->end_command), uncompress_mask, 1, fnam);
+	HTEndParam(&(me->end_command), uncompress_mask, 1);
+    }
+    FREE(uncompress_mask);
+
+    /*
+     * Make command to delete file.  - FM
+     */
+    me->remove_command = 0;
+    HTAddParam(&(me->remove_command), REMOVE_COMMAND, 1, fnam);
+    HTEndParam(&(me->remove_command), REMOVE_COMMAND, 1);
+
+    /*
+     * Save the filename and return the structure.  - FM
+     */
+    StrAllocCopy(anchor->FileCache, fnam);
+    return me;
+}
+
+/*	Dump output to stdout - LJM & FM
+ *	---------------------
+ *
+ */
+HTStream *HTDumpToStdout(HTPresentation *pres GCC_UNUSED,
+			 HTParentAnchor *anchor,
+			 HTStream *sink GCC_UNUSED)
+{
+    HTStream *ret_obj;
+
+    ret_obj = typecalloc(HTStream);
+
+    if (ret_obj == NULL)
+	outofmem(__FILE__, "HTDumpToStdout");
+
+    assert(ret_obj != NULL);
+
+    ret_obj->isa = &HTFWriter;
+    ret_obj->remove_command = NULL;
+    ret_obj->end_command = NULL;
+    ret_obj->anchor = anchor;
+
+    ret_obj->fp = stdout;	/* stdout */
+    return ret_obj;
+}
+
+#if defined(VMS) && !defined(USE_COMMAND_FILE)
+#include <fab.h>
+#include <rmsdef.h>		/* RMS status codes */
+#include <iodef.h>		/* I/O function codes */
+#include <fibdef.h>		/* file information block defs */
+#include <atrdef.h>		/* attribute request codes */
+#ifdef NOTDEFINED /*** Not all versions of VMS compilers have these.	 ***/
+#include <fchdef.h>		/* file characteristics */
+#include <fatdef.h>		/* file attribute defs */
+#else		  /*** So we'll define what we need from them ourselves. ***/
+#define FCH$V_CONTIGB	0x005	/* pos of cont best try bit */
+#define FCH$M_CONTIGB	(1 << FCH$V_CONTIGB)	/* contig best try bit mask */
+/* VMS I/O User's Reference Manual: Part I (V5.x doc set) */
+struct fatdef {
+    unsigned char fat$b_rtype, fat$b_rattrib;
+    unsigned short fat$w_rsize;
+    unsigned long fat$l_hiblk, fat$l_efblk;
+    unsigned short fat$w_ffbyte;
+    unsigned char fat$b_bktsize, fat$b_vfcsize;
+    unsigned short fat$w_maxrec, fat$w_defext, fat$w_gbc;
+    unsigned:16,:32,:16;	/* 6 bytes reserved, 2 bytes not used */
+    unsigned short fat$w_versions;
+};
+#endif /* NOTDEFINED */
+
+/* arbitrary descriptor without type and class info */
+typedef struct dsc {
+    unsigned short len, mbz;
+    void *adr;
+} Desc;
+
+extern unsigned long sys$open(), sys$qiow(), sys$dassgn();
+
+#define syswork(sts)	((sts) & 1)
+#define sysfail(sts)	(!syswork(sts))
+
+/*
+ * 25-Jul-1995 - Pat Rankin (rankin@eql.caltech.edu)
+ *
+ * Force a file to be marked as having fixed-length, 512 byte records
+ * without implied carriage control, and with best_try_contiguous set.
+ */
+static unsigned long LYVMS_FixedLengthRecords(char *filename)
+{
+    struct FAB fab;		/* RMS file access block */
+    struct fibdef fib;		/* XQP file information block */
+    struct fatdef recattr;	/* XQP file "record" attributes */
+    struct atrdef attr_rqst_list[3];	/* XQP attribute request itemlist */
+
+    Desc fib_dsc;
+    unsigned short channel, iosb[4];
+    unsigned long fchars, sts, tmp;
+
+    /* initialize file access block */
+    fab = cc$rms_fab;
+    fab.fab$l_fna = filename;
+    fab.fab$b_fns = (unsigned char) strlen(filename);
+    fab.fab$l_fop = FAB$M_UFO;	/* user file open; no further RMS processing */
+    fab.fab$b_fac = FAB$M_PUT;	/* need write access */
+    fab.fab$b_shr = FAB$M_NIL;	/* exclusive access */
+
+    sts = sys$open(&fab);	/* channel in stv; $dassgn to close */
+    if (sts == RMS$_FLK) {
+	/* For MultiNet, at least, if the file was just written by a remote
+	   NFS client, the local NFS server might still have it open, and the
+	   failed access attempt will provoke it to be closed, so try again. */
+	sts = sys$open(&fab);
+    }
+    if (sysfail(sts))
+	return sts;
+
+    /* RMS supplies a user-mode channel (see FAB$L_FOP FAB$V_UFO doc) */
+    channel = (unsigned short) fab.fab$l_stv;
+
+    /* set up ACP interface structures */
+    /* file information block, passed by descriptor; it's okay to start with
+       an empty FIB after RMS has accessed the file for us */
+    fib_dsc.len = sizeof fib;
+    fib_dsc.mbz = 0;
+    fib_dsc.adr = &fib;
+    memset((void *) &fib, 0, sizeof fib);
+    /* attribute request list */
+    attr_rqst_list[0].atr$w_size = sizeof recattr;
+    attr_rqst_list[0].atr$w_type = ATR$C_RECATTR;
+    *(void **) &attr_rqst_list[0].atr$l_addr = (void *) &recattr;
+    attr_rqst_list[1].atr$w_size = sizeof fchars;
+    attr_rqst_list[1].atr$w_type = ATR$C_UCHAR;
+    *(void **) &attr_rqst_list[1].atr$l_addr = (void *) &fchars;
+    attr_rqst_list[2].atr$w_size = attr_rqst_list[2].atr$w_type = 0;
+    attr_rqst_list[2].atr$l_addr = 0;
+    /* file "record" attributes */
+    memset((void *) &recattr, 0, sizeof recattr);
+    fchars = 0;			/* file characteristics */
+
+    /* get current attributes */
+    sts = sys$qiow(0, channel, IO$_ACCESS, iosb, (void (*)()) 0, 0,
+		   &fib_dsc, 0, 0, 0, attr_rqst_list, 0);
+    if (syswork(sts))
+	sts = iosb[0];
+
+    /* set desired attributes */
+    if (syswork(sts)) {
+	recattr.fat$b_rtype = FAB$C_SEQ | FAB$C_FIX;	/* org=seq, rfm=fix */
+	recattr.fat$w_rsize = recattr.fat$w_maxrec = 512;	/* lrl=mrs=512 */
+	recattr.fat$b_rattrib = 0;	/* rat=none */
+	fchars |= FCH$M_CONTIGB;	/* contiguous-best-try */
+	sts = sys$qiow(0, channel, IO$_DEACCESS, iosb, (void (*)()) 0, 0,
+		       &fib_dsc, 0, 0, 0, attr_rqst_list, 0);
+	if (syswork(sts))
+	    sts = iosb[0];
+    }
+
+    /* all done */
+    tmp = sys$dassgn(channel);
+    if (syswork(sts))
+	sts = tmp;
+    return sts;
+}
+#endif /* VMS && !USE_COMMAND_FILE */
diff --git a/src/HTFont.h b/src/HTFont.h
new file mode 100644
index 00000000..90c2b9ec
--- /dev/null
+++ b/src/HTFont.h
@@ -0,0 +1,50 @@
+/*		The portable font concept (!?*)
+*/
+
+/*	Line mode browser version:
+*/
+#ifndef HTFONT_H
+#define HTFONT_H
+
+typedef long int HTMLFont;	/* For now */
+
+#define HT_FONT		0
+#define HT_CAPITALS	1
+#define HT_BOLD		2
+#define HT_UNDERLINE	4
+#define HT_INVERSE	8
+#define HT_DOUBLE	0x10
+
+#define HT_BLACK	0
+#define HT_WHITE	1
+
+/*
+ *  Lynx internal character representations.
+ */
+#define HT_NON_BREAK_SPACE      ((char)1)
+#define HT_EN_SPACE             ((char)2)
+#define LY_UNDERLINE_START_CHAR	'\003'
+#define LY_UNDERLINE_END_CHAR	'\004'
+
+/* Turn about is fair play ASCII platforms use EBCDIC tab;
+   EBCDIC platforms use ASCII tab for LY_BOLD_START_CHAR.
+*/
+#ifdef EBCDIC
+#define LY_BOLD_START_CHAR	'\011'
+#else
+#define LY_BOLD_START_CHAR	'\005'
+#endif
+
+#define LY_BOLD_END_CHAR	'\006'
+#define LY_SOFT_HYPHEN		((char)7)
+#define LY_SOFT_NEWLINE		((char)8)
+
+#ifdef EBCDIC
+#define IsSpecialAttrChar(a)	(((a) > '\002') && ((a) <= '\011') && ((a)!='\t'))
+#else
+#define IsSpecialAttrChar(a)	(((a) > '\002') && ((a) <= '\010'))
+#endif
+
+#define IsNormalChar(a)		((a) != '\0' && !IsSpecialAttrChar(a))
+
+#endif /* HTFONT_H */
diff --git a/src/HTForms.h b/src/HTForms.h
new file mode 100644
index 00000000..ec994051
--- /dev/null
+++ b/src/HTForms.h
@@ -0,0 +1,166 @@
+/*
+ * $LynxId: HTForms.h,v 1.27 2009/05/28 22:49:51 tom Exp $
+ */
+#ifndef HTFORMS_H
+#define HTFORMS_H
+
+#ifndef LYSTRUCTS_H
+#include <LYStructs.h>
+#endif /* LYSTRUCTS_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* change_form_link() calls change_form_link_ex() with all its args and FALSE
+ * as last arg
+ */ extern int change_form_link(int cur,
+				DocInfo *newdoc,
+				BOOLEAN *refresh_screen,
+				BOOLEAN use_last_tfpos,
+				BOOLEAN immediate_submit);
+
+    extern int change_form_link_ex(int cur,
+				   DocInfo *newdoc,
+				   BOOLEAN *refresh_screen,
+				   BOOLEAN use_last_tfpos,
+				   BOOLEAN immediate_submit,
+				   BOOLEAN draw_only);
+
+/* InputFieldData is used to pass the info between HTML.c and Gridtext.c in
+ * HText_beginInput()
+ */
+    typedef struct _InputFieldData {
+	const char *accept;
+	const char *align;
+	int checked;
+	const char *iclass;
+	int disabled;
+	const char *error;
+	const char *height;
+	const char *id;
+	const char *lang;
+	const char *max;
+	const char *maxlength;
+	const char *md;
+	const char *min;
+	const char *name;
+	int size;
+	const char *src;
+	const char *type;
+	char *value;
+	const char *width;
+	int name_cs;		/* charset handle for name */
+	int value_cs;		/* charset handle for value */
+	const char *accept_cs;
+    } InputFieldData;
+
+/* The OptionType structure is for a linked list of option entries
+ */
+    typedef struct _OptionType {
+	char *name;		/* the name of the entry */
+	char *cp_submit_value;	/* the value to submit   */
+	int value_cs;		/* charset value is in   */
+	struct _OptionType *next;	/* the next entry        */
+    } OptionType;
+
+/*
+ * The FormInfo structure is used to contain the form field data within each
+ * anchor.  A pointer to this structure is in the TextAnchor struct.
+ */
+    typedef struct _FormInfo {
+	char *name;		/* the name of the link */
+	int number;		/* which form is the link within */
+	int type;		/* string, int, etc. */
+	char *value;		/* user entered string data */
+	char *orig_value;	/* the original value */
+	int size;		/* width on the screen */
+	unsigned maxlength;	/* max width of data */
+	int group;		/* a group associated with the link
+				 *  this is used for select's
+				 */
+	int num_value;		/* value of the numerical fields */
+	int hrange;		/* high numerical range */
+	int lrange;		/* low numerical range */
+	OptionType *select_list;	/* array of option choices */
+	char *submit_action;	/* form's action */
+	int submit_method;	/* form's method */
+	char *submit_enctype;	/* form's entype */
+	char *submit_title;	/* form's title */
+	BOOL no_cache;		/* Always resubmit? */
+	char *cp_submit_value;	/* option value to submit */
+	char *orig_submit_value;	/* original submit value */
+	int size_l;		/* The length of the option list */
+	int disabled;		/* If YES, can't change values */
+	int name_cs;
+	int value_cs;
+	char *accept_cs;
+    } FormInfo;
+
+/*
+ * As structure for info associated with a form.  There is some redundancy
+ * here, this shouldn't waste too much memory since the total number of forms
+ * (as opposed to form fields) per doc is expected to be rather small.  More
+ * things which are per form rather than per field could be moved here.  - kw
+ */
+    typedef struct _PerFormInfo {
+	int number;		/* form number, see GridText.c */
+	/* except for the last two, the following fields aren't actually used.. */
+	int disabled;		/* If YES, can't change values */
+	struct _PerFormInfo *next;	/* pointer to next form in doc */
+	int nfields;		/* number of fields */
+	FormInfo *first_field;
+	FormInfo *last_field;	/* pointer to last field in form */
+	char *accept_cs;
+	char *thisacceptcs;	/* used during submit */
+    } PerFormInfo;
+
+#define HYPERTEXT_ANCHOR 1
+#define INPUT_ANCHOR     2	/* forms mode input fields */
+#define INTERNAL_LINK_ANCHOR 5	/* 1+4, can be used as bitflag... - kw */
+
+    typedef enum {
+	F_UNKNOWN = 0,
+	F_TEXT_TYPE,
+	F_PASSWORD_TYPE,
+	F_CHECKBOX_TYPE,
+	F_RADIO_TYPE,
+	F_SUBMIT_TYPE,
+	F_RESET_TYPE,
+	F_OPTION_LIST_TYPE,
+	F_HIDDEN_TYPE,
+	F_TEXTAREA_TYPE,
+	F_RANGE_TYPE,
+	F_FILE_TYPE,
+	F_TEXT_SUBMIT_TYPE,
+	F_IMAGE_SUBMIT_TYPE,
+	F_KEYGEN_TYPE,
+	F_BUTTON_TYPE
+    } FieldTypes;
+
+#define F_TEXTLIKE(type) ((type) == F_TEXT_TYPE || \
+			  (type) == F_TEXT_SUBMIT_TYPE || \
+			  (type) == F_PASSWORD_TYPE || \
+			  (type) == F_FILE_TYPE || \
+			  (type) == F_TEXTAREA_TYPE)
+
+#define WWW_FORM_LINK_TYPE  1
+#define WWW_LINK_TYPE   2
+#define WWW_INTERN_LINK_TYPE   6	/* can be used as a bitflag... - kw */
+#define LINK_LINE_FOUND	8	/* used in follow_link_number, others - kw */
+#define LINK_DO_ARROWUP	16	/* returned by HTGetLinkOrFieldStart - kw */
+
+/* #define different lynx modes */
+#define NORMAL_LYNX_MODE 1
+#define FORMS_LYNX_MODE  2
+
+#define FIRST_ORDER  1
+#define MIDDLE_ORDER 2
+#define LAST_ORDER   3
+
+/* in LYForms.c */
+    extern void show_formlink_statusline(const FormInfo * form,
+					 int for_what);
+#ifdef __cplusplus
+}
+#endif
+#endif				/* HTFORMS_H */
diff --git a/src/HTInit.c b/src/HTInit.c
new file mode 100644
index 00000000..c0e1203e
--- /dev/null
+++ b/src/HTInit.c
@@ -0,0 +1,1469 @@
+/*
+ * $LynxId: HTInit.c,v 1.72 2010/04/29 23:50:21 tom Exp $
+ *
+ *		Configuration-specific Initialization		HTInit.c
+ *		----------------------------------------
+ */
+
+/*	Define a basic set of suffixes and presentations
+ *	------------------------------------------------
+ */
+
+#include <HTUtils.h>
+
+/* Implements:
+*/
+#include <HTInit.h>
+
+#include <HTML.h>
+#include <HTPlain.h>
+#include <HTMLGen.h>
+#include <HTFile.h>
+#include <HTFormat.h>
+#include <HTMIME.h>
+#include <HTWSRC.h>
+
+#include <HTSaveToFile.h>	/* LJM */
+#include <LYStrings.h>
+#include <LYUtils.h>
+#include <LYGlobalDefs.h>
+
+#include <LYexit.h>
+#include <LYLeaks.h>
+
+#define CTrace(p) CTRACE2(TRACE_CFG, p)
+
+static int HTLoadTypesConfigFile(char *fn, AcceptMedia media);
+static int HTLoadExtensionsConfigFile(char *fn);
+
+#define SET_SUFFIX1(suffix, description, type) \
+	HTSetSuffix(suffix, description, type, 1.0)
+
+#define SET_SUFFIX5(suffix, mimetype, type, description) \
+       HTSetSuffix5(suffix, mimetype, type, description, 1.0)
+
+#define SET_PRESENT(mimetype, command, quality, delay) \
+  HTSetPresentation(mimetype, command, 0, quality, delay, 0.0, 0, media)
+
+#define SET_EXTERNL(rep_in, rep_out, command, quality) \
+    HTSetConversion(rep_in, rep_out, command, quality, 3.0, 0.0, 0, mediaEXT)
+
+#define SET_INTERNL(rep_in, rep_out, command, quality) \
+    HTSetConversion(rep_in, rep_out, command, quality, 0.0, 0.0, 0, mediaINT)
+
+void HTFormatInit(void)
+{
+    AcceptMedia media = mediaEXT;
+
+    CTrace((tfp, "HTFormatInit\n"));
+#ifdef NeXT
+    SET_PRESENT("application/postscript", "open %s", 1.0, 2.0);
+    SET_PRESENT("image/x-tiff", "open %s", 2.0, 2.0);
+    SET_PRESENT("image/tiff", "open %s", 1.0, 2.0);
+    SET_PRESENT("audio/basic", "open %s", 1.0, 2.0);
+    SET_PRESENT("*", "open %s", 1.0, 0.0);
+#else
+    if (LYgetXDisplay() != 0) {	/* Must have X11 */
+	SET_PRESENT("application/postscript", "ghostview %s&", 1.0, 3.0);
+	if (non_empty(XLoadImageCommand)) {
+	    /* *INDENT-OFF* */
+	    SET_PRESENT("image/gif",	   XLoadImageCommand, 1.0, 3.0);
+	    SET_PRESENT("image/x-xbm",	   XLoadImageCommand, 1.0, 3.0);
+	    SET_PRESENT("image/x-xbitmap", XLoadImageCommand, 1.0, 3.0);
+	    SET_PRESENT("image/x-png",	   XLoadImageCommand, 2.0, 3.0);
+	    SET_PRESENT("image/png",	   XLoadImageCommand, 1.0, 3.0);
+	    SET_PRESENT("image/x-rgb",	   XLoadImageCommand, 1.0, 3.0);
+	    SET_PRESENT("image/x-tiff",	   XLoadImageCommand, 2.0, 3.0);
+	    SET_PRESENT("image/tiff",	   XLoadImageCommand, 1.0, 3.0);
+	    SET_PRESENT("image/jpeg",	   XLoadImageCommand, 1.0, 3.0);
+	    /* *INDENT-ON* */
+
+	}
+	SET_PRESENT("video/mpeg", "mpeg_play %s &", 1.0, 3.0);
+
+    }
+#endif
+
+#ifdef EXEC_SCRIPTS
+    /* set quality to 999.0 for protected exec applications */
+#ifndef VMS
+    SET_PRESENT("application/x-csh", "csh %s", 999.0, 3.0);
+    SET_PRESENT("application/x-sh", "sh %s", 999.0, 3.0);
+    SET_PRESENT("application/x-ksh", "ksh %s", 999.0, 3.0);
+#else
+    SET_PRESENT("application/x-VMS_script", "@%s", 999.0, 3.0);
+#endif /* not VMS */
+#endif /* EXEC_SCRIPTS */
+
+    /*
+     * Add our header handlers.
+     */
+    SET_INTERNL("message/x-http-redirection", "*", HTMIMERedirect, 2.0);
+    SET_INTERNL("message/x-http-redirection", "www/present", HTMIMERedirect, 2.0);
+    SET_INTERNL("message/x-http-redirection", "www/debug", HTMIMERedirect, 1.0);
+    SET_INTERNL("www/mime", "www/present", HTMIMEConvert, 1.0);
+    SET_INTERNL("www/mime", "www/download", HTMIMEConvert, 1.0);
+    SET_INTERNL("www/mime", "www/source", HTMIMEConvert, 1.0);
+    SET_INTERNL("www/mime", "www/dump", HTMIMEConvert, 1.0);
+
+    /*
+     * Add our compressed file handlers.
+     */
+    SET_INTERNL("www/compressed", "www/download", HTCompressed, 1.0);
+    SET_INTERNL("www/compressed", "www/present", HTCompressed, 1.0);
+    SET_INTERNL("www/compressed", "www/source", HTCompressed, 1.0);
+    SET_INTERNL("www/compressed", "www/dump", HTCompressed, 1.0);
+
+    /*
+     * Added the following to support some content types beginning to surface.
+     */
+    SET_INTERNL("application/html", "text/x-c", HTMLToC, 0.5);
+    SET_INTERNL("application/html", "text/plain", HTMLToPlain, 0.5);
+    SET_INTERNL("text/css", "text/plain", HTMLToPlain, 0.5);
+    SET_INTERNL("application/html", "www/present", HTMLPresent, 2.0);
+    SET_INTERNL("application/xhtml+xml", "www/present", HTMLPresent, 2.0);
+    SET_INTERNL("application/xml", "www/present", HTMLPresent, 2.0);
+    SET_INTERNL("application/html", "www/source", HTPlainPresent, 1.0);
+    SET_INTERNL("application/x-wais-source", "www/source", HTPlainPresent, 1.0);
+    SET_INTERNL("application/x-wais-source", "www/present", HTWSRCConvert, 2.0);
+    SET_INTERNL("application/x-wais-source", "www/download", HTWSRCConvert, 1.0);
+    SET_INTERNL("application/x-wais-source", "www/dump", HTWSRCConvert, 1.0);
+
+    /*
+     * Save all unknown mime types to disk.
+     */
+    SET_EXTERNL("www/source", "www/present", HTSaveToFile, 1.0);
+    SET_EXTERNL("www/source", "www/source", HTSaveToFile, 1.0);
+    SET_EXTERNL("www/source", "www/download", HTSaveToFile, 1.0);
+    SET_EXTERNL("www/source", "*", HTSaveToFile, 1.0);
+
+    /*
+     * Output all www/dump presentations to stdout.
+     */
+    SET_EXTERNL("www/source", "www/dump", HTDumpToStdout, 1.0);
+
+    /*
+     * Now add our basic conversions.
+     */
+    SET_INTERNL("text/x-sgml", "www/source", HTPlainPresent, 1.0);
+    SET_INTERNL("text/x-sgml", "www/present", HTMLPresent, 2.0);
+    SET_INTERNL("text/sgml", "www/source", HTPlainPresent, 1.0);
+    SET_INTERNL("text/sgml", "www/present", HTMLPresent, 1.0);
+    SET_INTERNL("text/css", "www/present", HTPlainPresent, 1.0);
+    SET_INTERNL("text/plain", "www/present", HTPlainPresent, 1.0);
+    SET_INTERNL("text/plain", "www/source", HTPlainPresent, 1.0);
+    SET_INTERNL("text/html", "www/source", HTPlainPresent, 1.0);
+    SET_INTERNL("text/html", "text/x-c", HTMLToC, 0.5);
+    SET_INTERNL("text/html", "text/plain", HTMLToPlain, 0.5);
+    SET_INTERNL("text/html", "www/present", HTMLPresent, 1.0);
+    SET_INTERNL("text/xml", "www/present", HTMLPresent, 2.0);
+
+    if (LYisAbsPath(global_type_map)) {
+	/* These should override the default types as necessary.  */
+	HTLoadTypesConfigFile(global_type_map, mediaSYS);
+    }
+
+    /*
+     * Load the local maps.
+     */
+    if (IsOurFile(LYAbsOrHomePath(&personal_type_map))
+	&& LYCanReadFile(personal_type_map)) {
+	/* These should override everything else. */
+	HTLoadTypesConfigFile(personal_type_map, mediaUSR);
+    }
+
+    /*
+     * Put text/html and text/plain at beginning of list.  - kw
+     */
+    HTReorderPresentation(WWW_PLAINTEXT, WWW_PRESENT);
+    HTReorderPresentation(WWW_HTML, WWW_PRESENT);
+
+    /*
+     * Analyze the list, and set 'get_accept' for those whose representations
+     * are not redundant.
+     */
+    HTFilterPresentations();
+}
+
+void HTPreparsedFormatInit(void)
+{
+    CTrace((tfp, "HTPreparsedFormatInit\n"));
+    if (LYPreparsedSource) {
+	SET_INTERNL("text/html", "www/source", HTMLParsedPresent, 1.0);
+	SET_INTERNL("text/html", "www/dump", HTMLParsedPresent, 1.0);
+    }
+}
+
+/* Some of the following is taken from: */
+
+/*
+Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
+
+Permission to use, copy, modify, and distribute this material
+for any purpose and without fee is hereby granted, provided
+that the above copyright notice and this permission notice
+appear in all copies, and that the name of Bellcore not be
+used in advertising or publicity pertaining to this
+material without the specific, prior written permission
+of an authorized representative of Bellcore.  BELLCORE
+MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY
+OF THIS MATERIAL FOR ANY PURPOSE.  IT IS PROVIDED "AS IS",
+WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
+*/
+/******************************************************
+    Metamail -- A tool to help diverse mail readers
+                cope with diverse multimedia mail formats.
+
+    Author:  Nathaniel S. Borenstein, Bellcore
+
+ ******************************************************* */
+
+struct MailcapEntry {
+    char *contenttype;
+    char *command;
+    char *testcommand;
+    int needsterminal;
+    int copiousoutput;
+    int needtofree;
+    char *label;
+    char *printcommand;
+    char *nametemplate;
+    float quality;
+    long int maxbytes;
+};
+
+static int ExitWithError(const char *txt);
+static int PassesTest(struct MailcapEntry *mc);
+
+static char *GetCommand(char *s, char **t)
+{
+    char *s2;
+    int quoted = 0;
+
+    s = LYSkipBlanks(s);
+    /* marca -- added + 1 for error case -- oct 24, 1993. */
+    s2 = typeMallocn(char, strlen(s) * 2 + 1);	/* absolute max, if all % signs */
+
+    if (!s2)
+	ExitWithError(MEMORY_EXHAUSTED_ABORT);
+
+    assert(s2 != NULL);
+
+    *t = s2;
+    while (non_empty(s)) {
+	if (quoted) {
+	    if (*s == '%')
+		*s2++ = '%';	/* Quote through next level, ugh! */
+
+	    *s2++ = *s++;
+	    quoted = 0;
+	} else {
+	    if (*s == ';') {
+		*s2 = '\0';
+		return (++s);
+	    }
+	    if (*s == '\\') {
+		quoted = 1;
+		++s;
+	    } else {
+		*s2++ = *s++;
+	    }
+	}
+    }
+    *s2 = '\0';
+    return (NULL);
+}
+
+/* no leading or trailing space, all lower case */
+static char *Cleanse(char *s)
+{
+    LYTrimLeading(s);
+    LYTrimTrailing(s);
+    LYLowerCase(s);
+    return (s);
+}
+
+/* remove unnecessary (unquoted) blanks in a shell command */
+static void TrimCommand(char *command)
+{
+    LYTrimTrailing(command);
+#ifdef UNIX
+    {
+	char *s = command;
+	char *d = command;
+	int ch;
+	int c0 = ' ';
+	BOOL escape = FALSE;
+	BOOL dquote = FALSE;
+	BOOL squote = FALSE;
+
+	while ((ch = *s++) != '\0') {
+	    if (escape) {
+		escape = FALSE;
+	    } else if (squote) {
+		if (ch == '\'')
+		    squote = FALSE;
+	    } else if (dquote) {
+		if (ch == '"')
+		    dquote = FALSE;
+	    } else {
+		switch (ch) {
+		case '"':
+		    dquote = TRUE;
+		    break;
+		case '\'':
+		    squote = TRUE;
+		    break;
+		case '\\':
+		    if (dquote)
+			escape = TRUE;
+		    break;
+		}
+	    }
+	    if (!escape && !dquote && !squote) {
+		if (ch == '\t')
+		    ch = ' ';
+		if (ch == ' ') {
+		    if (c0 == ' ')
+			continue;
+		}
+	    }
+	    *d++ = (char) ch;
+	    c0 = ch;
+	}
+	*d = '\0';
+    }
+#endif
+}
+
+static int ProcessMailcapEntry(FILE *fp, struct MailcapEntry *mc, AcceptMedia media)
+{
+    size_t rawentryalloc = 2000, len, need;
+    char *rawentry, *s, *t;
+    char *LineBuf = NULL;
+
+    rawentry = (char *) malloc(rawentryalloc);
+    if (!rawentry)
+	ExitWithError(MEMORY_EXHAUSTED_ABORT);
+
+    assert(rawentry != NULL);
+
+    *rawentry = '\0';
+    while (LYSafeGets(&LineBuf, fp) != 0) {
+	LYTrimNewline(LineBuf);
+	if (LineBuf[0] == '#' || LineBuf[0] == '\0')
+	    continue;
+	len = strlen(LineBuf);
+	need = len + strlen(rawentry) + 1;
+	if (need > rawentryalloc) {
+	    rawentryalloc += (2000 + need);
+	    rawentry = typeRealloc(char, rawentry, rawentryalloc);
+
+	    if (!rawentry)
+		ExitWithError(MEMORY_EXHAUSTED_ABORT);
+
+	    assert(rawentry != NULL);
+	}
+	if (len > 0 && LineBuf[len - 1] == '\\') {
+	    LineBuf[len - 1] = '\0';
+	    strcat(rawentry, LineBuf);
+	} else {
+	    strcat(rawentry, LineBuf);
+	    break;
+	}
+    }
+    FREE(LineBuf);
+
+    t = s = LYSkipBlanks(rawentry);
+    if (!*s) {
+	/* totally blank entry -- quietly ignore */
+	FREE(rawentry);
+	return (0);
+    }
+    s = strchr(rawentry, ';');
+    if (s == NULL) {
+	CTrace((tfp,
+		"ProcessMailcapEntry: Ignoring invalid mailcap entry: %s\n",
+		rawentry));
+	FREE(rawentry);
+	return (0);
+    }
+    *s++ = '\0';
+    if (!strncasecomp(t, "text/html", 9) ||
+	!strncasecomp(t, "text/plain", 10)) {
+	--s;
+	*s = ';';
+	CTrace((tfp, "ProcessMailcapEntry: Ignoring mailcap entry: %s\n",
+		rawentry));
+	FREE(rawentry);
+	return (0);
+    }
+    LYRemoveBlanks(rawentry);
+    LYLowerCase(rawentry);
+
+    mc->needsterminal = 0;
+    mc->copiousoutput = 0;
+    mc->needtofree = 1;
+    mc->testcommand = NULL;
+    mc->label = NULL;
+    mc->printcommand = NULL;
+    mc->contenttype = NULL;
+    StrAllocCopy(mc->contenttype, rawentry);
+    mc->quality = (float) 1.0;
+    mc->maxbytes = 0;
+    t = GetCommand(s, &mc->command);
+    if (!t) {
+	goto assign_presentation;
+    }
+    s = LYSkipBlanks(t);
+    while (s) {
+	char *arg, *eq, *mallocd_string;
+
+	t = GetCommand(s, &mallocd_string);
+	arg = mallocd_string;
+	eq = strchr(arg, '=');
+	if (eq) {
+	    *eq++ = '\0';
+	    eq = LYSkipBlanks(eq);
+	}
+	if (non_empty(arg)) {
+	    arg = Cleanse(arg);
+	    if (!strcmp(arg, "needsterminal")) {
+		mc->needsterminal = 1;
+	    } else if (!strcmp(arg, "copiousoutput")) {
+		mc->copiousoutput = 1;
+	    } else if (eq && !strcmp(arg, "test")) {
+		mc->testcommand = NULL;
+		StrAllocCopy(mc->testcommand, eq);
+		TrimCommand(mc->testcommand);
+		CTrace((tfp, "ProcessMailcapEntry: Found testcommand:%s\n",
+			mc->testcommand));
+	    } else if (eq && !strcmp(arg, "description")) {
+		mc->label = eq;	/* ignored */
+	    } else if (eq && !strcmp(arg, "label")) {
+		mc->label = eq;	/* ignored: bogus old name for description */
+	    } else if (eq && !strcmp(arg, "print")) {
+		mc->printcommand = eq;	/* ignored */
+	    } else if (eq && !strcmp(arg, "textualnewlines")) {
+		/* no support for now.  What does this do anyways? */
+		/* ExceptionalNewline(mc->contenttype, atoi(eq)); */
+	    } else if (eq && !strcmp(arg, "q")) {
+		mc->quality = (float) atof(eq);
+		if (mc->quality > 0.000 && mc->quality < 0.001)
+		    mc->quality = (float) 0.001;
+	    } else if (eq && !strcmp(arg, "mxb")) {
+		mc->maxbytes = atol(eq);
+		if (mc->maxbytes < 0)
+		    mc->maxbytes = 0;
+	    } else if (strcmp(arg, "notes")) {	/* IGNORE notes field */
+		if (*arg)
+		    CTrace((tfp,
+			    "ProcessMailcapEntry: Ignoring mailcap flag '%s'.\n",
+			    arg));
+	    }
+
+	}
+	FREE(mallocd_string);
+	s = t;
+    }
+
+  assign_presentation:
+    FREE(rawentry);
+
+    if (PassesTest(mc)) {
+	CTrace((tfp, "ProcessMailcapEntry Setting up conversion %s : %s\n",
+		mc->contenttype, mc->command));
+	HTSetPresentation(mc->contenttype,
+			  mc->command,
+			  mc->testcommand,
+			  mc->quality,
+			  3.0, 0.0, mc->maxbytes, media);
+    }
+    FREE(mc->command);
+    FREE(mc->testcommand);
+    FREE(mc->contenttype);
+
+    return (1);
+}
+
+#define L_CURL '{'
+#define R_CURL '}'
+
+static const char *LYSkipQuoted(const char *s)
+{
+    int escaped = 0;
+
+    ++s;			/* skip first quote */
+    while (*s != 0) {
+	if (escaped) {
+	    escaped = 0;
+	} else if (*s == '\\') {
+	    escaped = 1;
+	} else if (*s == '"') {
+	    ++s;
+	    break;
+	}
+	++s;
+    }
+    return s;
+}
+
+/*
+ * Note: the tspecials[] here are those defined for Content-Type header, so
+ * this function is not really general-purpose.
+ */
+static const char *LYSkipToken(const char *s)
+{
+    static const char tspecials[] = "\"()<>@,;:\\/[]?.=";
+
+    while (*s != '\0' && !WHITE(*s) && strchr(tspecials, *s) == 0) {
+	++s;
+    }
+    return s;
+}
+
+static const char *LYSkipValue(const char *s)
+{
+    if (*s == '"')
+	s = LYSkipQuoted(s);
+    else
+	s = LYSkipToken(s);
+    return s;
+}
+
+/*
+ * Copy the value from the source, dequoting if needed.
+ */
+static char *LYCopyValue(const char *s)
+{
+    const char *t;
+    char *result = 0;
+    int j, k;
+
+    if (*s == '"') {
+	t = LYSkipQuoted(s);
+	StrAllocCopy(result, s + 1);
+	result[t - s - 2] = '\0';
+	for (j = k = 0;; ++j, ++k) {
+	    if (result[j] == '\\') {
+		++j;
+	    }
+	    if ((result[k] = result[j]) == '\0')
+		break;
+	}
+    } else {
+	t = LYSkipToken(s);
+	StrAllocCopy(result, s);
+	result[t - s] = '\0';
+    }
+    return result;
+}
+
+/*
+ * The "Content-Type:" field, contains zero or more parameters after a ';'.
+ * Return the value of the named parameter, or null.
+ */
+static char *LYGetContentType(const char *name,
+			      const char *params)
+{
+    char *result = 0;
+
+    if (params != 0) {
+	if (name != 0) {
+	    size_t length = strlen(name);
+	    const char *test = strchr(params, ';');	/* skip type/subtype */
+	    const char *next;
+
+	    while (test != 0) {
+		BOOL found = FALSE;
+
+		++test;		/* skip the ';' */
+		test = LYSkipCBlanks(test);
+		next = LYSkipToken(test);
+		if ((next - test) == (int) length
+		    && !strncmp(test, name, length)) {
+		    found = TRUE;
+		}
+		test = LYSkipCBlanks(next);
+		if (*test == '=') {
+		    ++test;
+		    test = LYSkipCBlanks(test);
+		    if (found) {
+			result = LYCopyValue(test);
+			break;
+		    } else {
+			test = LYSkipValue(test);
+		    }
+		    test = LYSkipCBlanks(test);
+		}
+		if (*test != ';') {
+		    break;	/* we're lost */
+		}
+	    }
+	} else {		/* return the content-type */
+	    StrAllocCopy(result, params);
+	    *LYSkipNonBlanks(result) = '\0';
+	}
+    }
+    return result;
+}
+
+/*
+ * Check if the command uses a "%s" substitution.  We need to know this, to
+ * decide when to create temporary files, etc.
+ */
+BOOL LYMailcapUsesPctS(const char *controlstring)
+{
+    BOOL result = FALSE;
+    const char *from;
+    const char *next;
+    int prefixed = 0;
+    int escaped = 0;
+
+    for (from = controlstring; *from != '\0'; from++) {
+	if (escaped) {
+	    escaped = 0;
+	} else if (*from == '\\') {
+	    escaped = 1;
+	} else if (prefixed) {
+	    prefixed = 0;
+	    switch (*from) {
+	    case '%':		/* not defined */
+	    case 'n':
+	    case 'F':
+	    case 't':
+		break;
+	    case 's':
+		result = TRUE;
+		break;
+	    case L_CURL:
+		next = strchr(from, R_CURL);
+		if (next != 0) {
+		    from = next;
+		    break;
+		}
+		/* FALLTHRU */
+	    default:
+		break;
+	    }
+	} else if (*from == '%') {
+	    prefixed = 1;
+	}
+    }
+    return result;
+}
+
+/*
+ * Build the command string for testing or executing a mailcap entry.
+ * If a substitution from the Content-Type header is requested but no
+ * parameters are available, return -1, otherwise 0.
+ *
+ * This does not support multipart %n or %F (does this apply to lynx?)
+ */
+static int BuildCommand(HTChunk *cmd,
+			const char *controlstring,
+			const char *TmpFileName,
+			const char *params)
+{
+    int result = 0;
+    size_t TmpFileLen = strlen(TmpFileName);
+    const char *from;
+    const char *next;
+    char *name, *value;
+    int prefixed = 0;
+    int escaped = 0;
+
+    for (from = controlstring; *from != '\0'; from++) {
+	if (escaped) {
+	    escaped = 0;
+	    HTChunkPutc(cmd, *from);
+	} else if (*from == '\\') {
+	    escaped = 1;
+	} else if (prefixed) {
+	    prefixed = 0;
+	    switch (*from) {
+	    case '%':		/* not defined */
+		HTChunkPutc(cmd, *from);
+		break;
+	    case 'n':
+		/* FALLTHRU */
+	    case 'F':
+		CTrace((tfp, "BuildCommand: Bad mailcap \"test\" clause: %s\n",
+			controlstring));
+		break;
+	    case 't':
+		if ((value = LYGetContentType(NULL, params)) != 0) {
+		    HTChunkPuts(cmd, value);
+		    FREE(value);
+		}
+		break;
+	    case 's':
+		if (TmpFileLen && TmpFileName) {
+		    HTChunkPuts(cmd, TmpFileName);
+		}
+		break;
+	    case L_CURL:
+		next = strchr(from, R_CURL);
+		if (next != 0) {
+		    if (params != 0) {
+			++from;
+			name = 0;
+			HTSprintf0(&name, "%.*s", (int) (next - from), from);
+			if ((value = LYGetContentType(name, params)) != 0) {
+			    HTChunkPuts(cmd, value);
+			    FREE(value);
+			} else {
+			    if (!strcmp(name, "charset")) {
+				HTChunkPuts(cmd, "ISO-8859-1");
+			    } else {
+				CTrace((tfp, "BuildCommand no value for %s\n", name));
+			    }
+			}
+			FREE(name);
+		    } else {
+			result = -1;
+		    }
+		    from = next;
+		    break;
+		}
+		/* FALLTHRU */
+	    default:
+		CTrace((tfp,
+			"BuildCommand: Ignoring unrecognized format code in mailcap file '%%%c'.\n",
+			*from));
+		break;
+	    }
+	} else if (*from == '%') {
+	    prefixed = 1;
+	} else {
+	    HTChunkPutc(cmd, *from);
+	}
+    }
+    HTChunkTerminate(cmd);
+    return result;
+}
+
+/*
+ * Build the mailcap test-command and execute it.  This is only invoked when
+ * we cannot tell just by looking at the command if it would succeed.
+ *
+ * Returns 0 for success, -1 for error and 1 for deferred.
+ */
+int LYTestMailcapCommand(const char *testcommand,
+			 const char *params)
+{
+    int result;
+    char TmpFileName[LY_MAXPATH];
+    HTChunk *expanded = 0;
+
+    if (LYMailcapUsesPctS(testcommand)) {
+	if (LYOpenTemp(TmpFileName, HTML_SUFFIX, "w") == 0)
+	    ExitWithError(CANNOT_OPEN_TEMP);
+	LYCloseTemp(TmpFileName);
+    } else {
+	/* We normally don't need a temp file name - kw */
+	TmpFileName[0] = '\0';
+    }
+    expanded = HTChunkCreate(1024);
+    if (BuildCommand(expanded, testcommand, TmpFileName, params) != 0) {
+	result = 1;
+	CTrace((tfp, "PassesTest: Deferring test command: %s\n", expanded->data));
+    } else {
+	CTrace((tfp, "PassesTest: Executing test command: %s\n", expanded->data));
+	if ((result = LYSystem(expanded->data)) != 0) {
+	    result = -1;
+	    CTrace((tfp, "PassesTest: Test failed!\n"));
+	} else {
+	    CTrace((tfp, "PassesTest: Test passed!\n"));
+	}
+    }
+
+    HTChunkFree(expanded);
+    LYRemoveTemp(TmpFileName);
+
+    return result;
+}
+
+char *LYMakeMailcapCommand(const char *command,
+			   const char *params,
+			   const char *filename)
+{
+    HTChunk *expanded = 0;
+    char *result = 0;
+
+    expanded = HTChunkCreate(1024);
+    BuildCommand(expanded, command, filename, params);
+    StrAllocCopy(result, expanded->data);
+    HTChunkFree(expanded);
+    return result;
+}
+
+#define RTR_forget      0
+#define RTR_lookup      1
+#define RTR_add         2
+
+static int RememberTestResult(int mode, char *cmd, int result)
+{
+    struct cmdlist_s {
+	char *cmd;
+	int result;
+	struct cmdlist_s *next;
+    };
+    static struct cmdlist_s *cmdlist = NULL;
+    struct cmdlist_s *cur;
+
+    switch (mode) {
+    case RTR_forget:
+	while (cmdlist) {
+	    cur = cmdlist->next;
+	    FREE(cmdlist->cmd);
+	    FREE(cmdlist);
+	    cmdlist = cur;
+	}
+	break;
+    case RTR_lookup:
+	for (cur = cmdlist; cur; cur = cur->next)
+	    if (!strcmp(cmd, cur->cmd))
+		return cur->result;
+	return -1;
+    case RTR_add:
+	cur = typecalloc(struct cmdlist_s);
+
+	if (cur == NULL)
+	    outofmem(__FILE__, "RememberTestResult");
+
+	assert(cur != NULL);
+
+	cur->next = cmdlist;
+	StrAllocCopy(cur->cmd, cmd);
+	cur->result = result;
+	cmdlist = cur;
+	break;
+    }
+    return 0;
+}
+
+/* FIXME: this sometimes used caseless comparison, e.g., strcasecomp */
+#define SameCommand(tst,ref) !strcmp(tst,ref)
+
+static int PassesTest(struct MailcapEntry *mc)
+{
+    int result;
+
+    /*
+     *  Make sure we have a command
+     */
+    if (!mc->testcommand)
+	return (1);
+
+    /*
+     *  Save overhead of system() calls by faking these. - FM
+     */
+    if (SameCommand(mc->testcommand, "test \"$DISPLAY\"") ||
+	SameCommand(mc->testcommand, "test \"$DISPLAY\" != \"\"") ||
+	SameCommand(mc->testcommand, "test -n \"$DISPLAY\"")) {
+	FREE(mc->testcommand);
+	CTrace((tfp, "PassesTest: Testing for XWINDOWS environment.\n"));
+	if (LYgetXDisplay() != NULL) {
+	    CTrace((tfp, "PassesTest: Test passed!\n"));
+	    return (0 == 0);
+	} else {
+	    CTrace((tfp, "PassesTest: Test failed!\n"));
+	    return (-1 == 0);
+	}
+    }
+    if (SameCommand(mc->testcommand, "test -z \"$DISPLAY\"")) {
+	FREE(mc->testcommand);
+	CTrace((tfp, "PassesTest: Testing for NON_XWINDOWS environment.\n"));
+	if (LYgetXDisplay() == NULL) {
+	    CTrace((tfp, "PassesTest: Test passed!\n"));
+	    return (0 == 0);
+	} else {
+	    CTrace((tfp, "PassesTest: Test failed!\n"));
+	    return (-1 == 0);
+	}
+    }
+
+    /*
+     *  Why do anything but return success for this one! - FM
+     */
+    if (SameCommand(mc->testcommand, "test -n \"$LYNX_VERSION\"")) {
+	FREE(mc->testcommand);
+	CTrace((tfp, "PassesTest: Testing for LYNX environment.\n"));
+	CTrace((tfp, "PassesTest: Test passed!\n"));
+	return (0 == 0);
+    } else
+	/*
+	 *  ... or failure for this one! - FM
+	 */
+    if (SameCommand(mc->testcommand, "test -z \"$LYNX_VERSION\"")) {
+	FREE(mc->testcommand);
+	CTrace((tfp, "PassesTest: Testing for non-LYNX environment.\n"));
+	CTrace((tfp, "PassesTest: Test failed!\n"));
+	return (-1 == 0);
+    }
+
+    result = RememberTestResult(RTR_lookup, mc->testcommand, 0);
+    if (result == -1) {
+	result = LYTestMailcapCommand(mc->testcommand, NULL);
+	RememberTestResult(RTR_add, mc->testcommand, result ? 1 : 0);
+    }
+
+    /*
+     *  Free the test command as well since
+     *  we wont be needing it anymore.
+     */
+    if (result != 1)
+	FREE(mc->testcommand);
+
+    if (result < 0) {
+	CTrace((tfp, "PassesTest: Test failed!\n"));
+    } else if (result == 0) {
+	CTrace((tfp, "PassesTest: Test passed!\n"));
+    }
+
+    return (result >= 0);
+}
+
+static int ProcessMailcapFile(char *file, AcceptMedia media)
+{
+    struct MailcapEntry mc;
+    FILE *fp;
+
+    CTrace((tfp, "ProcessMailcapFile: Loading file '%s'.\n",
+	    file));
+    if ((fp = fopen(file, TXT_R)) == NULL) {
+	CTrace((tfp, "ProcessMailcapFile: Could not open '%s'.\n",
+		file));
+	return (-1 == 0);
+    }
+
+    while (fp && !feof(fp)) {
+	ProcessMailcapEntry(fp, &mc, media);
+    }
+    LYCloseInput(fp);
+    RememberTestResult(RTR_forget, NULL, 0);
+    return (0 == 0);
+}
+
+static int ExitWithError(const char *txt)
+{
+    if (txt)
+	fprintf(tfp, "Lynx: %s\n", txt);
+    exit_immediately(EXIT_FAILURE);
+    return (-1);
+}
+
+/* Reverse the entries from each mailcap after it has been read, so that
+ * earlier entries have precedence.  Set to 0 to get traditional lynx
+ * behavior, which means that the last match wins. - kw */
+static int reverse_mailcap = 1;
+
+static int HTLoadTypesConfigFile(char *fn, AcceptMedia media)
+{
+    int result = 0;
+    HTList *saved = HTPresentations;
+
+    if (reverse_mailcap) {	/* temporarily hide existing list */
+	HTPresentations = NULL;
+    }
+
+    result = ProcessMailcapFile(fn, media);
+
+    if (reverse_mailcap) {
+	if (result && HTPresentations) {
+	    HTList_reverse(HTPresentations);
+	    HTList_appendList(HTPresentations, saved);
+	    FREE(saved);
+	} else {
+	    HTPresentations = saved;
+	}
+    }
+    return result;
+}
+
+/* ------------------------------------------------------------------------ */
+/* ------------------------------------------------------------------------ */
+/* ------------------------------------------------------------------------ */
+
+/*	Define a basic set of suffixes
+ *	------------------------------
+ *
+ *	The LAST suffix for a type is that used for temporary files
+ *	of that type.
+ *	The quality is an apriori bias as to whether the file should be
+ *	used.  Not that different suffixes can be used to represent files
+ *	which are of the same format but are originals or regenerated,
+ *	with different values.
+ */
+/*
+ * Additional notes:  the encoding parameter may be taken into account when
+ * looking for a match; for that purpose "7bit", "8bit", and "binary" are
+ * equivalent.
+ *
+ * Use of mixed case and of pseudo MIME types with embedded spaces should be
+ * avoided.  It was once necessary for getting the fancy strings into type
+ * labels in FTP directory listings, but that can now be done with the
+ * description field (using HTSetSuffix5).  AFAIK the only effect of such
+ * "fancy" (and mostly invalid) types that cannot be reproduced by using a
+ * description fields is some statusline messages in SaveToFile (HTFWriter.c). 
+ * And showing the user an invalid MIME type as the 'Content-type:' is not such
+ * a hot idea anyway, IMO.  Still, if you want it, it is still possible (even
+ * in lynx.cfg now), but use of it in the defaults below has been reduced.
+ *
+ * Case variations rely on peculiar behavior of HTAtom.c for matching.  They
+ * lead to surprising behavior, Lynx retains the case of a string in the form
+ * first encountered after starting up.  So while later suffix rules generally
+ * override or modify earlier ones, the case used for a MIME time is determined
+ * by the first suffix rule (or other occurrence).  Matching in HTAtom_for is
+ * effectively case insensitive, except for the first character of the string
+ * which is treated as case-sensitive by the hash function there; best not to
+ * rely on that, rather convert MIME types to lowercase on input as is already
+ * done in most places (And HTAtom could become consistently case-sensitive, as
+ * in newer W3C libwww).
+ *  - kw 1999-10-12
+ */
+void HTFileInit(void)
+{
+#ifdef BUILTIN_SUFFIX_MAPS
+    if (LYUseBuiltinSuffixes) {
+	CTrace((tfp, "HTFileInit: Loading default (HTInit) extension maps.\n"));
+
+	/* default suffix interpretation */
+	SET_SUFFIX1("*", "text/plain", "8bit");
+	SET_SUFFIX1("*.*", "text/plain", "8bit");
+
+#ifdef EXEC_SCRIPTS
+	/*
+	 * define these extensions for exec scripts.
+	 */
+#ifndef VMS
+	/* for csh exec links */
+	HTSetSuffix(".csh", "application/x-csh", "8bit", 0.8);
+	HTSetSuffix(".sh", "application/x-sh", "8bit", 0.8);
+	HTSetSuffix(".ksh", "application/x-ksh", "8bit", 0.8);
+#else
+	HTSetSuffix(".com", "application/x-VMS_script", "8bit", 0.8);
+#endif /* !VMS */
+#endif /* EXEC_SCRIPTS */
+
+	/*
+	 * Some of the old incarnation of the mappings is preserved and can be had
+	 * by defining TRADITIONAL_SUFFIXES.  This is for some cases where I felt
+	 * the old rules might be preferred by someone, for some reason.  It's not
+	 * done consistently.  A lot more of this stuff could probably be changed
+	 * too or omitted, now that nearly the equivalent functionality is
+	 * available in lynx.cfg.  - kw 1999-10-12
+	 */
+	/* *INDENT-OFF* */
+	SET_SUFFIX1(".saveme",	"application/x-Binary",		"binary");
+	SET_SUFFIX1(".dump",	"application/x-Binary",		"binary");
+	SET_SUFFIX1(".bin",	"application/x-Binary",		"binary");
+
+	SET_SUFFIX1(".arc",	"application/x-Compressed",	"binary");
+
+	SET_SUFFIX1(".alpha-exe", "application/x-Executable",	"binary");
+	SET_SUFFIX1(".alpha_exe", "application/x-Executable",	"binary");
+	SET_SUFFIX1(".AXP-exe", "application/x-Executable",	"binary");
+	SET_SUFFIX1(".AXP_exe", "application/x-Executable",	"binary");
+	SET_SUFFIX1(".VAX-exe", "application/x-Executable",	"binary");
+	SET_SUFFIX1(".VAX_exe", "application/x-Executable",	"binary");
+	SET_SUFFIX5(".exe",	"application/octet-stream",	"binary", "Executable");
+
+#ifdef TRADITIONAL_SUFFIXES
+	SET_SUFFIX1(".exe.Z",	"application/x-Comp. Executable", "binary");
+	SET_SUFFIX1(".Z",	"application/UNIX Compressed",	"binary");
+	SET_SUFFIX1(".tar_Z",	"application/UNIX Compr. Tar",	"binary");
+	SET_SUFFIX1(".tar.Z",	"application/UNIX Compr. Tar",	"binary");
+#else
+	SET_SUFFIX5(".Z",	"application/x-compress",	"binary", "UNIX Compressed");
+	SET_SUFFIX5(".Z",	NULL,				"compress", "UNIX Compressed");
+	SET_SUFFIX5(".exe.Z",	"application/octet-stream",	"compress", "Executable");
+	SET_SUFFIX5(".tar_Z",	"application/x-tar",		"compress", "UNIX Compr. Tar");
+	SET_SUFFIX5(".tar.Z",	"application/x-tar",		"compress", "UNIX Compr. Tar");
+#endif
+
+#ifdef TRADITIONAL_SUFFIXES
+	SET_SUFFIX1("-gz",	"application/GNU Compressed",	"binary");
+	SET_SUFFIX1("_gz",	"application/GNU Compressed",	"binary");
+	SET_SUFFIX1(".gz",	"application/GNU Compressed",	"binary");
+
+	SET_SUFFIX5(".tar.gz",	"application/x-tar",		"binary", "GNU Compr. Tar");
+	SET_SUFFIX5(".tgz",	"application/x-tar",		"gzip", "GNU Compr. Tar");
+#else
+	SET_SUFFIX5("-gz",	"application/x-gzip",		"binary", "GNU Compressed");
+	SET_SUFFIX5("_gz",	"application/x-gzip",		"binary", "GNU Compressed");
+	SET_SUFFIX5(".gz",	"application/x-gzip",		"binary", "GNU Compressed");
+	SET_SUFFIX5("-gz",	NULL,				"gzip", "GNU Compressed");
+	SET_SUFFIX5("_gz",	NULL,				"gzip", "GNU Compressed");
+	SET_SUFFIX5(".gz",	NULL,				"gzip", "GNU Compressed");
+
+	SET_SUFFIX5(".tar.gz",	"application/x-tar",		"gzip", "GNU Compr. Tar");
+	SET_SUFFIX5(".tgz",	"application/x-tar",		"gzip", "GNU Compr. Tar");
+#endif
+
+#ifdef TRADITIONAL_SUFFIXES
+	SET_SUFFIX1(".src",	"application/x-WAIS-source",	"8bit");
+	SET_SUFFIX1(".wsrc",	"application/x-WAIS-source",	"8bit");
+#else
+	SET_SUFFIX5(".wsrc",	"application/x-wais-source",	"8bit", "WAIS-source");
+#endif
+
+	SET_SUFFIX5(".zip",	"application/zip",		"binary", "Zip File");
+
+	SET_SUFFIX1(".zz",	"application/x-deflate",	"binary");
+	SET_SUFFIX1(".zz",	"application/deflate",		"binary");
+
+	SET_SUFFIX1(".bz2",	"application/x-bzip2",		"binary");
+	SET_SUFFIX1(".bz2",	"application/bzip2",		"binary");
+
+#ifdef TRADITIONAL_SUFFIXES
+	SET_SUFFIX1(".uu",	"application/x-UUencoded",	"8bit");
+
+	SET_SUFFIX1(".hqx",	"application/x-Binhex",		"8bit");
+
+	SET_SUFFIX1(".o",	"application/x-Prog. Object",	"binary");
+	SET_SUFFIX1(".a",	"application/x-Prog. Library",	"binary");
+#else
+	SET_SUFFIX5(".uu",	"application/x-uuencoded",	"7bit", "UUencoded");
+
+	SET_SUFFIX5(".hqx",	"application/mac-binhex40",	"8bit", "Mac BinHex");
+
+	HTSetSuffix5(".o",	"application/octet-stream",	"binary", "Prog. Object", 0.5);
+	HTSetSuffix5(".a",	"application/octet-stream",	"binary", "Prog. Library", 0.5);
+	HTSetSuffix5(".so",	"application/octet-stream",	"binary", "Shared Lib", 0.5);
+#endif
+
+	SET_SUFFIX5(".oda",	"application/oda",		"binary", "ODA");
+
+	SET_SUFFIX5(".pdf",	"application/pdf",		"binary", "PDF");
+
+	SET_SUFFIX5(".eps",	"application/postscript",	"8bit", "Postscript");
+	SET_SUFFIX5(".ai",	"application/postscript",	"8bit", "Postscript");
+	SET_SUFFIX5(".ps",	"application/postscript",	"8bit", "Postscript");
+
+	SET_SUFFIX5(".rtf",	"application/rtf",		"8bit", "RTF");
+
+	SET_SUFFIX5(".dvi",	"application/x-dvi",		"8bit", "DVI");
+
+	SET_SUFFIX5(".hdf",	"application/x-hdf",		"8bit", "HDF");
+
+	SET_SUFFIX1(".cdf",	"application/x-netcdf",		"8bit");
+	SET_SUFFIX1(".nc",	"application/x-netcdf",		"8bit");
+
+#ifdef TRADITIONAL_SUFFIXES
+	SET_SUFFIX1(".latex",	"application/x-Latex",		"8bit");
+	SET_SUFFIX1(".tex",	"application/x-Tex",		"8bit");
+	SET_SUFFIX1(".texinfo", "application/x-Texinfo",	"8bit");
+	SET_SUFFIX1(".texi",	"application/x-Texinfo",	"8bit");
+#else
+	SET_SUFFIX5(".latex",	"application/x-latex",		"8bit", "LaTeX");
+	SET_SUFFIX5(".tex",	"text/x-tex",			"8bit", "TeX");
+	SET_SUFFIX5(".texinfo", "application/x-texinfo",	"8bit", "Texinfo");
+	SET_SUFFIX5(".texi",	"application/x-texinfo",	"8bit", "Texinfo");
+#endif
+
+#ifdef TRADITIONAL_SUFFIXES
+	SET_SUFFIX1(".t",	"application/x-Troff",		"8bit");
+	SET_SUFFIX1(".tr",	"application/x-Troff",		"8bit");
+	SET_SUFFIX1(".roff",	"application/x-Troff",		"8bit");
+
+	SET_SUFFIX1(".man",	"application/x-Troff-man",	"8bit");
+	SET_SUFFIX1(".me",	"application/x-Troff-me",	"8bit");
+	SET_SUFFIX1(".ms",	"application/x-Troff-ms",	"8bit");
+#else
+	SET_SUFFIX5(".t",	"application/x-troff",		"8bit", "Troff");
+	SET_SUFFIX5(".tr",	"application/x-troff",		"8bit", "Troff");
+	SET_SUFFIX5(".roff",	"application/x-troff",		"8bit", "Troff");
+
+	SET_SUFFIX5(".man",	"application/x-troff-man",	"8bit", "Man Page");
+	SET_SUFFIX5(".me",	"application/x-troff-me",	"8bit", "Troff me");
+	SET_SUFFIX5(".ms",	"application/x-troff-ms",	"8bit", "Troff ms");
+#endif
+
+	SET_SUFFIX1(".zoo",	"application/x-Zoo File",	"binary");
+
+#if defined(TRADITIONAL_SUFFIXES) || defined(VMS)
+	SET_SUFFIX1(".bak",	"application/x-VMS BAK File",	"binary");
+	SET_SUFFIX1(".bkp",	"application/x-VMS BAK File",	"binary");
+	SET_SUFFIX1(".bck",	"application/x-VMS BAK File",	"binary");
+
+	SET_SUFFIX5(".bkp_gz",	"application/octet-stream",	"gzip", "GNU BAK File");
+	SET_SUFFIX5(".bkp-gz",	"application/octet-stream",	"gzip", "GNU BAK File");
+	SET_SUFFIX5(".bck_gz",	"application/octet-stream",	"gzip", "GNU BAK File");
+	SET_SUFFIX5(".bck-gz",	"application/octet-stream",	"gzip", "GNU BAK File");
+
+	SET_SUFFIX5(".bkp-Z",	"application/octet-stream",	"compress", "Comp. BAK File");
+	SET_SUFFIX5(".bkp_Z",	"application/octet-stream",	"compress", "Comp. BAK File");
+	SET_SUFFIX5(".bck-Z",	"application/octet-stream",	"compress", "Comp. BAK File");
+	SET_SUFFIX5(".bck_Z",	"application/octet-stream",	"compress", "Comp. BAK File");
+#else
+	HTSetSuffix5(".bak",	NULL,				"binary", "Backup", 0.5);
+	SET_SUFFIX5(".bkp",	"application/octet-stream",	"binary", "VMS BAK File");
+	SET_SUFFIX5(".bck",	"application/octet-stream",	"binary", "VMS BAK File");
+#endif
+
+#if defined(TRADITIONAL_SUFFIXES) || defined(VMS)
+	SET_SUFFIX1(".hlb",	"application/x-VMS Help Libr.", "binary");
+	SET_SUFFIX1(".olb",	"application/x-VMS Obj. Libr.", "binary");
+	SET_SUFFIX1(".tlb",	"application/x-VMS Text Libr.", "binary");
+	SET_SUFFIX1(".obj",	"application/x-VMS Prog. Obj.", "binary");
+	SET_SUFFIX1(".decw$book", "application/x-DEC BookReader", "binary");
+	SET_SUFFIX1(".mem",	"application/x-RUNOFF-MANUAL", "8bit");
+#else
+	SET_SUFFIX5(".hlb",	"application/octet-stream",	"binary", "VMS Help Libr.");
+	SET_SUFFIX5(".olb",	"application/octet-stream",	"binary", "VMS Obj. Libr.");
+	SET_SUFFIX5(".tlb",	"application/octet-stream",	"binary", "VMS Text Libr.");
+	SET_SUFFIX5(".obj",	"application/octet-stream",	"binary", "Prog. Object");
+	SET_SUFFIX5(".decw$book", "application/octet-stream",	"binary", "DEC BookReader");
+	SET_SUFFIX5(".mem",	"text/x-runoff-manual",		"8bit", "RUNOFF-MANUAL");
+#endif
+
+	SET_SUFFIX1(".vsd",	"application/visio",		"binary");
+
+	SET_SUFFIX5(".lha",	"application/x-lha",		"binary", "lha File");
+	SET_SUFFIX5(".lzh",	"application/x-lzh",		"binary", "lzh File");
+	SET_SUFFIX5(".sea",	"application/x-sea",		"binary", "sea File");
+#ifdef TRADITIONAL_SUFFIXES
+	SET_SUFFIX5(".sit",	"application/x-sit",		"binary", "sit File");
+#else
+	SET_SUFFIX5(".sit",	"application/x-stuffit",	"binary", "StuffIt");
+#endif
+	SET_SUFFIX5(".dms",	"application/x-dms",		"binary", "dms File");
+	SET_SUFFIX5(".iff",	"application/x-iff",		"binary", "iff File");
+
+	SET_SUFFIX1(".bcpio",	"application/x-bcpio",		"binary");
+	SET_SUFFIX1(".cpio",	"application/x-cpio",		"binary");
+
+#ifdef TRADITIONAL_SUFFIXES
+	SET_SUFFIX1(".gtar",	"application/x-gtar",		"binary");
+#endif
+
+	SET_SUFFIX1(".shar",	"application/x-shar",		"8bit");
+	SET_SUFFIX1(".share",	"application/x-share",		"8bit");
+
+#ifdef TRADITIONAL_SUFFIXES
+	SET_SUFFIX1(".sh",	"application/x-sh",		"8bit"); /* xtra */
+#endif
+
+	SET_SUFFIX1(".sv4cpio", "application/x-sv4cpio",	"binary");
+	SET_SUFFIX1(".sv4crc",	"application/x-sv4crc",		"binary");
+
+	SET_SUFFIX5(".tar",	"application/x-tar",		"binary", "Tar File");
+	SET_SUFFIX1(".ustar",	"application/x-ustar",		"binary");
+
+	SET_SUFFIX1(".snd",	"audio/basic",			"binary");
+	SET_SUFFIX1(".au",	"audio/basic",			"binary");
+
+	SET_SUFFIX1(".aifc",	"audio/x-aiff",			"binary");
+	SET_SUFFIX1(".aif",	"audio/x-aiff",			"binary");
+	SET_SUFFIX1(".aiff",	"audio/x-aiff",			"binary");
+	SET_SUFFIX1(".wav",	"audio/x-wav",			"binary");
+	SET_SUFFIX1(".midi",	"audio/midi",			"binary");
+	SET_SUFFIX1(".mod",	"audio/mod",			"binary");
+
+	SET_SUFFIX1(".gif",	"image/gif",			"binary");
+	SET_SUFFIX1(".ief",	"image/ief",			"binary");
+	SET_SUFFIX1(".jfif",	"image/jpeg",			"binary"); /* xtra */
+	SET_SUFFIX1(".jfif-tbnl", "image/jpeg",			"binary"); /* xtra */
+	SET_SUFFIX1(".jpe",	"image/jpeg",			"binary");
+	SET_SUFFIX1(".jpg",	"image/jpeg",			"binary");
+	SET_SUFFIX1(".jpeg",	"image/jpeg",			"binary");
+	SET_SUFFIX1(".tif",	"image/tiff",			"binary");
+	SET_SUFFIX1(".tiff",	"image/tiff",			"binary");
+	SET_SUFFIX1(".ham",	"image/ham",			"binary");
+	SET_SUFFIX1(".ras",	"image/x-cmu-rast",		"binary");
+	SET_SUFFIX1(".pnm",	"image/x-portable-anymap",	"binary");
+	SET_SUFFIX1(".pbm",	"image/x-portable-bitmap",	"binary");
+	SET_SUFFIX1(".pgm",	"image/x-portable-graymap",	"binary");
+	SET_SUFFIX1(".ppm",	"image/x-portable-pixmap",	"binary");
+	SET_SUFFIX1(".png",	"image/png",			"binary");
+	SET_SUFFIX1(".rgb",	"image/x-rgb",			"binary");
+	SET_SUFFIX1(".xbm",	"image/x-xbitmap",		"binary");
+	SET_SUFFIX1(".xpm",	"image/x-xpixmap",		"binary");
+	SET_SUFFIX1(".xwd",	"image/x-xwindowdump",		"binary");
+
+	SET_SUFFIX1(".rtx",	"text/richtext",		"8bit");
+	SET_SUFFIX1(".tsv",	"text/tab-separated-values",	"8bit");
+	SET_SUFFIX1(".etx",	"text/x-setext",		"8bit");
+
+	SET_SUFFIX1(".mpg",	"video/mpeg",			"binary");
+	SET_SUFFIX1(".mpe",	"video/mpeg",			"binary");
+	SET_SUFFIX1(".mpeg",	"video/mpeg",			"binary");
+	SET_SUFFIX1(".mov",	"video/quicktime",		"binary");
+	SET_SUFFIX1(".qt",	"video/quicktime",		"binary");
+	SET_SUFFIX1(".avi",	"video/x-msvideo",		"binary");
+	SET_SUFFIX1(".movie",	"video/x-sgi-movie",		"binary");
+	SET_SUFFIX1(".mv",	"video/x-sgi-movie",		"binary");
+
+	SET_SUFFIX1(".mime",	"message/rfc822",		"8bit");
+
+	SET_SUFFIX1(".c",	"text/plain",			"8bit");
+	SET_SUFFIX1(".cc",	"text/plain",			"8bit");
+	SET_SUFFIX1(".c++",	"text/plain",			"8bit");
+	SET_SUFFIX1(".css",	"text/plain",			"8bit");
+	SET_SUFFIX1(".h",	"text/plain",			"8bit");
+	SET_SUFFIX1(".pl",	"text/plain",			"8bit");
+	SET_SUFFIX1(".text",	"text/plain",			"8bit");
+	SET_SUFFIX1(".txt",	"text/plain",			"8bit");
+
+	SET_SUFFIX1(".php",	"text/html",			"8bit");
+	SET_SUFFIX1(".php3",	"text/html",			"8bit");
+	SET_SUFFIX1(".html3",	"text/html",			"8bit");
+	SET_SUFFIX1(".ht3",	"text/html",			"8bit");
+	SET_SUFFIX1(".phtml",	"text/html",			"8bit");
+	SET_SUFFIX1(".shtml",	"text/html",			"8bit");
+	SET_SUFFIX1(".sht",	"text/html",			"8bit");
+	SET_SUFFIX1(".htmlx",	"text/html",			"8bit");
+	SET_SUFFIX1(".htm",	"text/html",			"8bit");
+	SET_SUFFIX1(".html",	"text/html",			"8bit");
+	/* *INDENT-ON* */
+
+    } else {			/* LYSuffixRules */
+	/*
+	 * Note that even .html -> text/html, .htm -> text/html are omitted if
+	 * default maps are compiled in but then skipped because of a
+	 * configuration file directive.  Whoever changes the config file in
+	 * this way can easily also add the SUFFIX rules there.  - kw
+	 */
+	CTrace((tfp,
+		"HTFileInit: Skipping all default (HTInit) extension maps!\n"));
+    }				/* LYSuffixRules */
+
+#else /* BUILTIN_SUFFIX_MAPS */
+
+    CTrace((tfp,
+	    "HTFileInit: Default (HTInit) extension maps not compiled in.\n"));
+    /*
+     * The following two are still used if BUILTIN_SUFFIX_MAPS was undefined. 
+     * Without one of them, lynx would always need to have a mapping specified
+     * in a lynx.cfg or mime.types file to be usable for local HTML files at
+     * all.  That includes many of the generated user interface pages.  - kw
+     */
+    SET_SUFFIX1(".htm", "text/html", "8bit");
+    SET_SUFFIX1(".html", "text/html", "8bit");
+#endif /* BUILTIN_SUFFIX_MAPS */
+
+    if (LYisAbsPath(global_extension_map)) {
+	/* These should override the default extensions as necessary. */
+	HTLoadExtensionsConfigFile(global_extension_map);
+    }
+
+    /*
+     * Load the local maps.
+     */
+    if (IsOurFile(LYAbsOrHomePath(&personal_extension_map))
+	&& LYCanReadFile(personal_extension_map)) {
+	/* These should override everything else. */
+	HTLoadExtensionsConfigFile(personal_extension_map);
+    }
+}
+
+/* -------------------- Extension config file reading --------------------- */
+
+/*
+ *  The following is lifted from NCSA httpd 1.0a1, by Rob McCool;
+ *  NCSA httpd is in the public domain, as is this code.
+ *
+ *  Modified Oct 97 - KW
+ */
+
+#define MAX_STRING_LEN 256
+
+static int HTGetLine(char *s, int n, FILE *f)
+{
+    register int i = 0, r;
+
+    if (!f)
+	return (1);
+
+    while (1) {
+	r = fgetc(f);
+	s[i] = (char) r;
+
+	if (s[i] == CR) {
+	    r = fgetc(f);
+	    if (r == LF)
+		s[i] = (char) r;
+	    else if (r != EOF)
+		ungetc(r, f);
+	}
+
+	if ((r == EOF) || (s[i] == LF) || (s[i] == CR) || (i == (n - 1))) {
+	    s[i] = '\0';
+	    return (feof(f) ? 1 : 0);
+	}
+	++i;
+    }
+}
+
+static void HTGetWord(char *word, char *line, char stop, char stop2)
+{
+    int x = 0, y;
+
+    for (x = 0; line[x] && line[x] != stop && line[x] != stop2; x++) {
+	word[x] = line[x];
+    }
+
+    word[x] = '\0';
+    if (line[x])
+	++x;
+    y = 0;
+
+    while ((line[y++] = line[x++])) ;
+
+    return;
+}
+
+static int HTLoadExtensionsConfigFile(char *fn)
+{
+    char line[MAX_STRING_LEN];
+    char word[MAX_STRING_LEN];
+    char *ct;
+    FILE *f;
+    int count = 0;
+
+    CTrace((tfp, "HTLoadExtensionsConfigFile: Loading file '%s'.\n", fn));
+
+    if ((f = fopen(fn, TXT_R)) == NULL) {
+	CTrace((tfp, "HTLoadExtensionsConfigFile: Could not open '%s'.\n", fn));
+	return count;
+    }
+
+    while (!(HTGetLine(line, sizeof(line), f))) {
+	HTGetWord(word, line, ' ', '\t');
+	if (line[0] == '\0' || word[0] == '#')
+	    continue;
+	ct = NULL;
+	StrAllocCopy(ct, word);
+	LYLowerCase(ct);
+
+	while (line[0]) {
+	    HTGetWord(word, line, ' ', '\t');
+	    if (word[0] && (word[0] != ' ')) {
+		char *ext = NULL;
+
+		HTSprintf0(&ext, ".%s", word);
+		LYLowerCase(ext);
+
+		CTrace((tfp, "setting suffix '%s' to '%s'.\n", ext, ct));
+
+		if (strstr(ct, "tex") != NULL ||
+		    strstr(ct, "postscript") != NULL ||
+		    strstr(ct, "sh") != NULL ||
+		    strstr(ct, "troff") != NULL ||
+		    strstr(ct, "rtf") != NULL)
+		    SET_SUFFIX1(ext, ct, "8bit");
+		else
+		    SET_SUFFIX1(ext, ct, "binary");
+		count++;
+
+		FREE(ext);
+	    }
+	}
+	FREE(ct);
+    }
+    LYCloseInput(f);
+
+    return count;
+}
diff --git a/src/HTML.c b/src/HTML.c
new file mode 100644
index 00000000..7367ba81
--- /dev/null
+++ b/src/HTML.c
@@ -0,0 +1,8304 @@
+/*
+ * $LynxId: HTML.c,v 1.137 2010/05/02 20:30:21 tom Exp $
+ *
+ *		Structured stream to Rich hypertext converter
+ *		============================================
+ *
+ *	This generates a hypertext object.  It converts from the
+ *	structured stream interface of HTML events into the style-
+ *	oriented interface of the HText.h interface.  This module is
+ *	only used in clients and should not be linked into servers.
+ *
+ *	Override this module if making a new GUI browser.
+ *
+ *   Being Overidden
+ *
+ */
+
+#include <HTUtils.h>
+
+#define Lynx_HTML_Handler
+#include <HTChunk.h>
+#include <HText.h>
+#include <HTStyle.h>
+#include <HTML.h>
+
+#include <HTCJK.h>
+#include <HTAtom.h>
+#include <HTAnchor.h>
+#include <HTMLGen.h>
+#include <HTParse.h>
+#include <HTList.h>
+#include <UCMap.h>
+#include <UCDefs.h>
+#include <UCAux.h>
+
+#include <LYGlobalDefs.h>
+#include <LYCharUtils.h>
+#include <LYCharSets.h>
+
+#include <HTAlert.h>
+#include <HTForms.h>
+#include <HTNestedList.h>
+#include <GridText.h>
+#include <LYStrings.h>
+#include <LYUtils.h>
+#include <LYMap.h>
+#include <LYList.h>
+#include <LYBookmark.h>
+#include <LYHistory.h>
+
+#ifdef VMS
+#include <LYCurses.h>
+#endif /* VMS */
+
+#ifdef USE_PRETTYSRC
+#include <LYPrettySrc.h>
+#endif
+
+#ifdef USE_COLOR_STYLE
+#include <SGML.h>
+#include <AttrList.h>
+#include <LYHash.h>
+#include <LYStyle.h>
+#undef SELECTED_STYLES
+#define pHText_changeStyle(X,Y,Z) {}
+
+#if OMIT_SCN_KEEPING
+# define HCODE_TO_STACK_OFF(x) /*(CSHASHSIZE+1)*/ 88888		/*special value. */
+#else
+# define HCODE_TO_STACK_OFF(x) x	/*pass computed value */
+#endif
+
+#endif /* USE_COLOR_STYLE */
+
+#ifdef USE_SOURCE_CACHE
+#include <HTAccess.h>
+#endif
+
+#include <LYCurses.h>
+#include <LYJustify.h>
+
+#include <LYexit.h>
+#include <LYLeaks.h>
+
+#define STACKLEVEL(me) ((me->stack + MAX_NESTING - 1) - me->sp)
+
+#define DFT_TEXTAREA_COLS 60
+#define DFT_TEXTAREA_ROWS 4
+
+#define MAX_TEXTAREA_COLS LYcolLimit
+#define MAX_TEXTAREA_ROWS (3 * LYlines)
+
+#define LimitValue(name, value) \
+ 	if (name > value) { \
+		CTRACE((tfp, "Limited " #name " to %d, was %d\n", \
+			value, name)); \
+		name = value; \
+	}
+
+struct _HTStream {
+    const HTStreamClass *isa;
+#ifdef USE_SOURCE_CACHE
+    HTParentAnchor *anchor;
+    FILE *fp;
+    char *filename;
+    HTChunk *chunk;
+    HTChunk *last_chunk;	/* the last chunk in a chain! */
+    const HTStreamClass *actions;
+    HTStream *target;
+    int status;
+#else
+    /* .... */
+#endif
+};
+
+static HTStyleSheet *styleSheet = NULL;		/* Application-wide */
+
+/*	Module-wide style cache
+*/
+static HTStyle *styles[HTML_ELEMENTS + LYNX_HTML_EXTRA_ELEMENTS];
+
+					   /* adding 24 nested list styles  */
+					   /* and 3 header alignment styles */
+					   /* and 3 div alignment styles    */
+static HTStyle *default_style = NULL;
+
+const char *LYToolbarName = "LynxPseudoToolbar";
+
+/* used to turn off a style if the HTML author forgot to
+static int i_prior_style = -1;
+ */
+
+/*
+ *	Private function....
+ */
+static int HTML_end_element(HTStructured * me, int element_number,
+			    char **include);
+
+static int HTML_start_element(HTStructured * me, int element_number,
+			      const BOOL *present,
+			      const char **value,
+			      int tag_charset,
+			      char **include);
+
+/*
+ * If we have verbose_img set, display labels for images.
+ */
+#define VERBOSE_IMG(value,src_type,string) \
+      ((verbose_img) ? (newtitle = MakeNewTitle(value,src_type)): string)
+
+static char *MakeNewTitle(const char **value, int src_type);
+static char *MakeNewImageValue(const char **value);
+static char *MakeNewMapValue(const char **value, const char *mapstr);
+
+/*	Set an internal flag that the next call to a stack-affecting method
+ *	is only internal and the stack manipulation should be skipped. - kw
+ */
+#define SET_SKIP_STACK(el_num) if (HTML_dtd.tags[el_num].contents != SGML_EMPTY) \
+						{ me->skip_stack++; }
+
+void strtolower(char *i)
+{
+    if (!i)
+	return;
+    while (*i) {
+	*i = (char) TOLOWER(*i);
+	i++;
+    }
+}
+
+/*		Flattening the style structure
+ *		------------------------------
+ *
+ * On the NeXT, and on any read-only browser, it is simpler for the text to
+ * have a sequence of styles, rather than a nested tree of styles.  In this
+ * case we have to flatten the structure as it arrives from SGML tags into a
+ * sequence of styles.
+ */
+
+/*
+ *  If style really needs to be set, call this.
+ */
+void actually_set_style(HTStructured * me)
+{
+    if (!me->text) {		/* First time through */
+	LYGetChartransInfo(me);
+	UCSetTransParams(&me->T,
+			 me->UCLYhndl, me->UCI,
+			 HTAnchor_getUCLYhndl(me->node_anchor,
+					      UCT_STAGE_HTEXT),
+			 HTAnchor_getUCInfoStage(me->node_anchor,
+						 UCT_STAGE_HTEXT));
+	me->text = HText_new2(me->node_anchor, me->target);
+	HText_beginAppend(me->text);
+	HText_setStyle(me->text, me->new_style);
+	me->in_word = NO;
+	LYCheckForContentBase(me);
+    } else {
+	HText_setStyle(me->text, me->new_style);
+    }
+
+    me->old_style = me->new_style;
+    me->style_change = NO;
+}
+
+/*
+ *  If you THINK you need to change style, call this.
+ */
+static void change_paragraph_style(HTStructured * me, HTStyle *style)
+{
+    if (me->new_style != style) {
+	me->style_change = YES;
+	me->new_style = style;
+    }
+    me->in_word = NO;
+}
+
+/*
+ * Return true if we should write a message (to LYNXMESSAGES, or the trace
+ * file) telling about some bad HTML that we've found.
+ */
+BOOL LYBadHTML(HTStructured * me)
+{
+    BOOL code = FALSE;
+
+    switch ((enumBadHtml) cfg_bad_html) {
+    case BAD_HTML_IGNORE:
+	break;
+    case BAD_HTML_TRACE:
+	code = TRUE;
+	break;
+    case BAD_HTML_MESSAGE:
+	code = TRUE;
+	break;
+    case BAD_HTML_WARN:
+	/*
+	 * If we're already tracing, do not add a warning.
+	 */
+	if (!TRACE && !me->inBadHTML) {
+	    HTUserMsg(BAD_HTML_USE_TRACE);
+	    me->inBadHTML = TRUE;
+	}
+	code = TRACE;
+	break;
+    }
+    return code;
+}
+
+/*
+ * Handle the formatted message.
+ */
+void LYShowBadHTML(const char *message)
+{
+    switch ((enumBadHtml) cfg_bad_html) {
+    case BAD_HTML_IGNORE:
+	break;
+    case BAD_HTML_TRACE:
+	CTRACE((tfp, "%s", message));
+	break;
+    case BAD_HTML_MESSAGE:
+	CTRACE((tfp, "%s", message));
+	LYstore_message(message);
+	break;
+    case BAD_HTML_WARN:
+	CTRACE((tfp, "%s", message));
+	break;
+    }
+}
+
+/*_________________________________________________________________________
+ *
+ *			A C T I O N	R O U T I N E S
+ */
+
+/* FIXME:  this should be amended to do the substitution only when not in a
+ * multibyte stream.
+ */
+#ifdef EXP_JAPANESE_SPACES
+#define FIX_JAPANESE_SPACES \
+	(HTCJK == CHINESE || HTCJK == JAPANESE || HTCJK == TAIPEI)
+	/* don't replace '\n' with ' ' if Chinese or Japanese - HN
+	 */
+#else
+#define FIX_JAPANESE_SPACES 0
+#endif
+
+/*	Character handling
+ *	------------------
+ */
+void HTML_put_character(HTStructured * me, char c)
+{
+    /*
+     * Ignore all non-MAP content when just scanning a document for MAPs.  - FM
+     */
+    if (LYMapsOnly && me->sp[0].tag_number != HTML_OBJECT)
+	return;
+
+    /*
+     * Do EOL conversion if needed.  - FM
+     *
+     * Convert EOL styles:
+     *   macintosh:  cr    --> lf
+     *   ascii:      cr-lf --> lf
+     *   unix:       lf    --> lf
+     */
+    if ((me->lastraw == '\r') && c == '\n') {
+	me->lastraw = -1;
+	return;
+    }
+    me->lastraw = c;
+    if (c == '\r')
+	c = '\n';
+
+    /*
+     * Handle SGML_LITTERAL tags that have HTChunk elements.  - FM
+     */
+    switch (me->sp[0].tag_number) {
+
+    case HTML_COMMENT:
+	return;			/* Do Nothing */
+
+    case HTML_TITLE:
+	if (c == LY_SOFT_HYPHEN)
+	    return;
+	if (c != '\n' && c != '\t' && c != '\r') {
+	    HTChunkPutc(&me->title, c);
+	} else if (FIX_JAPANESE_SPACES) {
+	    if (c == '\t') {
+		HTChunkPutc(&me->title, ' ');
+	    } else {
+		return;
+	    }
+	} else {
+	    HTChunkPutc(&me->title, ' ');
+	}
+	return;
+
+    case HTML_STYLE:
+	HTChunkPutc(&me->style_block, c);
+	return;
+
+    case HTML_SCRIPT:
+	HTChunkPutc(&me->script, c);
+	return;
+
+    case HTML_OBJECT:
+	HTChunkPutc(&me->object, c);
+	return;
+
+    case HTML_TEXTAREA:
+	HTChunkPutc(&me->textarea, c);
+	return;
+
+    case HTML_SELECT:
+    case HTML_OPTION:
+	HTChunkPutc(&me->option, c);
+	return;
+
+    case HTML_MATH:
+	HTChunkPutc(&me->math, c);
+	return;
+
+    default:
+	if (me->inSELECT) {
+	    /*
+	     * If we are within a SELECT not caught by the cases above -
+	     * HTML_SELECT or HTML_OPTION may not be the last element pushed on
+	     * the style stack if there were invalid markup tags within a
+	     * SELECT element.  For error recovery, treat text as part of the
+	     * OPTION text, it is probably meant to show up as user-visible
+	     * text.  Having A as an open element while in SELECT is really
+	     * sick, don't make anchor text part of the option text in that
+	     * case since the option text will probably just be discarded.  -
+	     * kw
+	     */
+	    if (me->sp[0].tag_number == HTML_A)
+		break;
+	    HTChunkPutc(&me->option, c);
+	    return;
+	}
+	break;
+    }				/* end first switch */
+
+    /*
+     * Handle all other tag content.  - FM
+     */
+    switch (me->sp[0].tag_number) {
+
+    case HTML_PRE:		/* Formatted text */
+	/*
+	 * We guarantee that the style is up-to-date in begin_litteral. But we
+	 * still want to strip \r's.
+	 */
+	if (c != '\r' &&
+	    !(c == '\n' && me->inLABEL && !me->inP) &&
+	    !(c == '\n' && !me->inPRE)) {
+	    me->inP = TRUE;
+	    me->inLABEL = FALSE;
+	    HText_appendCharacter(me->text, c);
+	}
+	me->inPRE = TRUE;
+	break;
+
+    case HTML_LISTING:		/* Literal text */
+    case HTML_XMP:
+    case HTML_PLAINTEXT:
+	/*
+	 * We guarantee that the style is up-to-date in begin_litteral.  But we
+	 * still want to strip \r's.
+	 */
+	if (c != '\r') {
+	    me->inP = TRUE;
+	    me->inLABEL = FALSE;
+	    HText_appendCharacter(me->text, c);
+	}
+	break;
+
+    default:
+	/*
+	 * Free format text.
+	 */
+	if (me->sp->style->id == ST_Preformatted) {
+	    if (c != '\r' &&
+		!(c == '\n' && me->inLABEL && !me->inP) &&
+		!(c == '\n' && !me->inPRE)) {
+		me->inP = TRUE;
+		me->inLABEL = FALSE;
+		HText_appendCharacter(me->text, c);
+	    }
+	    me->inPRE = TRUE;
+
+	} else if (me->sp->style->id == ST_Listing ||
+		   me->sp->style->id == ST_Example) {
+	    if (c != '\r') {
+		me->inP = TRUE;
+		me->inLABEL = FALSE;
+		HText_appendCharacter(me->text, c);
+	    }
+
+	} else {
+	    if (me->style_change) {
+		if ((c == '\n') || (c == ' '))
+		    return;	/* Ignore it */
+		UPDATE_STYLE;
+	    }
+	    if (c == '\n') {
+		if (!FIX_JAPANESE_SPACES) {
+		    if (me->in_word) {
+			if (HText_getLastChar(me->text) != ' ') {
+			    me->inP = TRUE;
+			    me->inLABEL = FALSE;
+			    HText_appendCharacter(me->text, ' ');
+			}
+			me->in_word = NO;
+		    }
+		}
+
+	    } else if (c == ' ' || c == '\t') {
+		if (HText_getLastChar(me->text) != ' ') {
+		    me->inP = TRUE;
+		    me->inLABEL = FALSE;
+		    HText_appendCharacter(me->text, ' ');
+		}
+
+	    } else if (c == '\r') {
+		/* ignore */
+
+	    } else {
+		me->inP = TRUE;
+		me->inLABEL = FALSE;
+		HText_appendCharacter(me->text, c);
+		me->in_word = YES;
+	    }
+	}
+    }				/* end second switch */
+
+    if (c == '\n' || c == '\t') {
+	HText_setLastChar(me->text, ' ');	/* set it to a generic separator */
+
+	/*
+	 * \r's are ignored.  In order to keep collapsing spaces correctly we
+	 * must default back to the previous separator if there was one.
+	 */
+    } else if (c == '\r' && HText_getLastChar(me->text) == ' ') {
+	HText_setLastChar(me->text, ' ');	/* set it to a generic separator */
+    } else {
+	HText_setLastChar(me->text, c);
+    }
+}
+
+/*	String handling
+ *	---------------
+ *
+ *	This is written separately from put_character because the loop can
+ *	in some cases be promoted to a higher function call level for speed.
+ */
+void HTML_put_string(HTStructured * me, const char *s)
+{
+#ifdef USE_PRETTYSRC
+    char *translated_string = NULL;
+#endif
+
+    if (s == NULL || (LYMapsOnly && me->sp[0].tag_number != HTML_OBJECT))
+	return;
+#ifdef USE_PRETTYSRC
+    if (psrc_convert_string) {
+	StrAllocCopy(translated_string, s);
+	TRANSLATE_AND_UNESCAPE_ENTITIES(&translated_string, TRUE, FALSE);
+	s = (const char *) translated_string;
+    }
+#endif
+
+    switch (me->sp[0].tag_number) {
+
+    case HTML_COMMENT:
+	break;			/* Do Nothing */
+
+    case HTML_TITLE:
+	HTChunkPuts(&me->title, s);
+	break;
+
+    case HTML_STYLE:
+	HTChunkPuts(&me->style_block, s);
+	break;
+
+    case HTML_SCRIPT:
+	HTChunkPuts(&me->script, s);
+	break;
+
+    case HTML_PRE:		/* Formatted text */
+    case HTML_LISTING:		/* Literal text */
+    case HTML_XMP:
+    case HTML_PLAINTEXT:
+	/*
+	 * We guarantee that the style is up-to-date in begin_litteral
+	 */
+	HText_appendText(me->text, s);
+	break;
+
+    case HTML_OBJECT:
+	HTChunkPuts(&me->object, s);
+	break;
+
+    case HTML_TEXTAREA:
+	HTChunkPuts(&me->textarea, s);
+	break;
+
+    case HTML_SELECT:
+    case HTML_OPTION:
+	HTChunkPuts(&me->option, s);
+	break;
+
+    case HTML_MATH:
+	HTChunkPuts(&me->math, s);
+	break;
+
+    default:			/* Free format text? */
+	if (!me->sp->style->freeFormat) {
+	    /*
+	     * If we are within a preformatted text style not caught by the
+	     * cases above (HTML_PRE or similar may not be the last element
+	     * pushed on the style stack).  - kw
+	     */
+#ifdef USE_PRETTYSRC
+	    if (psrc_view) {
+		/*
+		 * We do this so that a raw '\r' in the string will not be
+		 * interpreted as an internal request to break a line - passing
+		 * '\r' to HText_appendText is treated by it as a request to
+		 * insert a blank line - VH
+		 */
+		for (; *s; ++s)
+		    HTML_put_character(me, *s);
+	    } else
+#endif
+		HText_appendText(me->text, s);
+	    break;
+	} else {
+	    const char *p = s;
+	    char c;
+
+	    if (me->style_change) {
+		for (; *p && ((*p == '\n') || (*p == '\r') ||
+			      (*p == ' ') || (*p == '\t')); p++) ;	/* Ignore leaders */
+		if (!*p)
+		    break;
+		UPDATE_STYLE;
+	    }
+	    for (; *p; p++) {
+		if (*p == 13 && p[1] != 10) {
+		    /*
+		     * Treat any '\r' which is not followed by '\n' as '\n', to
+		     * account for macintosh lineend in ALT attributes etc.  -
+		     * kw
+		     */
+		    c = '\n';
+		} else {
+		    c = *p;
+		}
+		if (me->style_change) {
+		    if ((c == '\n') || (c == ' ') || (c == '\t'))
+			continue;	/* Ignore it */
+		    UPDATE_STYLE;
+		}
+		if (c == '\n') {
+		    if (!FIX_JAPANESE_SPACES) {
+			if (me->in_word) {
+			    if (HText_getLastChar(me->text) != ' ')
+				HText_appendCharacter(me->text, ' ');
+			    me->in_word = NO;
+			}
+		    }
+
+		} else if (c == ' ' || c == '\t') {
+		    if (HText_getLastChar(me->text) != ' ')
+			HText_appendCharacter(me->text, ' ');
+
+		} else if (c == '\r') {
+		    /* ignore */
+		} else {
+		    HText_appendCharacter(me->text, c);
+		    me->in_word = YES;
+		}
+
+		/* set the Last Character */
+		if (c == '\n' || c == '\t') {
+		    /* set it to a generic separator */
+		    HText_setLastChar(me->text, ' ');
+		} else if (c == '\r' &&
+			   HText_getLastChar(me->text) == ' ') {
+		    /*
+		     * \r's are ignored.  In order to keep collapsing spaces
+		     * correctly, we must default back to the previous
+		     * separator, if there was one.  So we set LastChar to a
+		     * generic separator.
+		     */
+		    HText_setLastChar(me->text, ' ');
+		} else {
+		    HText_setLastChar(me->text, c);
+		}
+
+	    }			/* for */
+	}
+    }				/* end switch */
+#ifdef USE_PRETTYSRC
+    if (psrc_convert_string) {
+	psrc_convert_string = FALSE;
+	FREE(translated_string);
+    }
+#endif
+}
+
+/*	Buffer write
+ *	------------
+ */
+void HTML_write(HTStructured * me, const char *s, int l)
+{
+    const char *p;
+    const char *e = s + l;
+
+    if (LYMapsOnly && me->sp[0].tag_number != HTML_OBJECT)
+	return;
+
+    for (p = s; p < e; p++)
+	HTML_put_character(me, *p);
+}
+
+/*
+ *  "Internal links" are hyperlinks whose source and destination are
+ *  within the same document, and for which the destination is given
+ *  as a URL Reference with an empty URL, but possibly with a non-empty
+ *  #fragment.	(This terminology re URL-Reference vs. URL follows the
+ *  Fielding URL syntax and semantics drafts).
+ *  Differences:
+ *  (1) The document's base (in whatever way it is given) is not used for
+ *	resolving internal link references.
+ *  (2) Activating an internal link should not result in a new retrieval
+ *	of a copy of the document.
+ *  (3) Internal links are the only way to refer with a hyperlink to a document
+ *	(or a location in it) which is only known as the result of a POST
+ *	request (doesn't have a URL from which the document can be retrieved
+ *	with GET), and can only be used from within that document.
+ *
+ * *If DONT_TRACK_INTERNAL_LINKS is not defined, we keep track of whether a
+ *  link destination was given as an internal link.  This information is
+ *  recorded in the type of the link between anchor objects, and is available
+ *  to the HText object and the mainloop from there.  URL References to
+ *  internal destinations are still resolved into an absolute form before
+ *  being passed on, but using the current stream's retrieval address instead
+ *  of the base URL.
+ *  Examples:  (replace [...] to have a valid absolute URL)
+ *  In document retrieved from [...]/mypath/mydoc.htm w/ base [...]/otherpath/
+ *  a. HREF="[...]/mypath/mydoc.htm"	  -> [...]/mypath/mydoc.htm
+ *  b. HREF="[...]/mypath/mydoc.htm#frag" -> [...]/mypath/mydoc.htm#frag
+ *  c. HREF="mydoc.htm"			  -> [...]/otherpath/mydoc.htm
+ *  d. HREF="mydoc.htm#frag"		  -> [...]/otherpath/mydoc.htm#frag
+ *  e. HREF=""		      -> [...]/mypath/mydoc.htm      (marked internal)
+ *  f. HREF="#frag"	      -> [...]/mypath/mydoc.htm#frag (marked internal)
+ *
+ * *If DONT_TRACK_INTERNAL_LINKS is defined, URL-less URL-References are
+ *  resolved differently from URL-References with a non-empty URL (using the
+ *  current stream's retrieval address instead of the base), but we make no
+ *  further distinction.  Resolution is then as in the examples above, execept
+ *  that there is no "(marked internal)".
+ *
+ * *Note that this doesn't apply to form ACTIONs (always resolved using base,
+ *  never marked internal).  Also other references encountered or generated
+ *  are not marked internal, whether they have a URL or not, if in a given
+ *  context an internal link makes no sense (e.g., IMG SRC=).
+ */
+
+/* A flag is used to keep track of whether an "URL reference" encountered
+   had a real "URL" or not.  In the latter case, it will be marked as
+   "internal".	The flag is set before we start messing around with the
+   string (resolution of relative URLs etc.).  This variable only used
+   locally here, don't confuse with LYinternal_flag which is for
+   overriding non-caching similar to LYoverride_no_cache. - kw */
+#define CHECK_FOR_INTERN(flag,s) \
+   	flag = (BOOLEAN) ((s && (*s=='#' || *s=='\0')) ? TRUE : FALSE)
+
+/* Last argument to pass to HTAnchor_findChildAndLink() calls,
+   just an abbreviation. - kw */
+#define INTERN_LT (HTLinkType *)(intern_flag ? HTInternalLink : NULL)
+
+#ifdef USE_COLOR_STYLE
+static char *Style_className = 0;
+static char *Style_className_end = 0;
+static unsigned Style_className_len = 0;
+static int hcode;
+
+#ifdef LY_FIND_LEAKS
+static void free_Style_className(void)
+{
+    FREE(Style_className);
+}
+#endif
+
+static void addClassName(const char *prefix,
+			 const char *actual,
+			 unsigned length)
+{
+    unsigned offset = strlen(prefix);
+    unsigned have = (unsigned) (Style_className_end - Style_className);
+    unsigned need = (offset + length + 1);
+
+    if ((have + need) >= Style_className_len) {
+	Style_className_len += 1024 + 2 * (have + need);
+	if (Style_className == 0) {
+	    Style_className = typeMallocn(char, Style_className_len);
+	} else {
+	    Style_className = typeRealloc(char, Style_className, Style_className_len);
+	}
+	if (Style_className == NULL)
+	    outofmem(__FILE__, "addClassName");
+	Style_className_end = Style_className + have;
+    }
+    if (offset)
+	strcpy(Style_className_end, prefix);
+    if (length)
+	memcpy(Style_className_end + offset, actual, length);
+    Style_className_end[offset + length] = '\0';
+    strtolower(Style_className_end);
+
+    Style_className_end += (offset + length);
+}
+#else
+#define addClassName(prefix, actual, length)	/* nothing */
+#endif
+
+#ifdef USE_PRETTYSRC
+
+static void HTMLSRC_apply_markup(HTStructured * context, HTlexeme lexeme, BOOL start,
+				 int tag_charset)
+{
+    HT_tagspec *ts = *((start ? lexeme_start : lexeme_end) + lexeme);
+
+    while (ts) {
+#ifdef USE_COLOR_STYLE
+	if (ts->start) {
+	    current_tag_style = ts->style;
+	    force_current_tag_style = TRUE;
+	    forced_classname = ts->class_name;
+	    force_classname = TRUE;
+	}
+#endif
+	CTRACE((tfp, ts->start ? "SRCSTART %d\n" : "SRCSTOP %d\n", (int) lexeme));
+	if (ts->start)
+	    HTML_start_element(context,
+			       ts->element,
+			       ts->present,
+			       (const char **) ts->value,
+			       tag_charset,
+			       NULL);
+	else
+	    HTML_end_element(context,
+			     ts->element,
+			     NULL);
+	ts = ts->next;
+    }
+}
+#  define START TRUE
+#  define STOP FALSE
+
+#  define PSRCSTART(x)	HTMLSRC_apply_markup(me,HTL_##x,START,tag_charset)
+#  define PSRCSTOP(x)  HTMLSRC_apply_markup(me,HTL_##x,STOP,tag_charset)
+
+#  define PUTC(x) HTML_put_character(me,x)
+#  define PUTS(x) HTML_put_string(me,x)
+
+#endif /* USE_PRETTYSRC */
+
+static void LYStartArea(HTStructured * obj, const char *href,
+			const char *alt,
+			const char *title,
+			int tag_charset)
+{
+    BOOL new_present[HTML_AREA_ATTRIBUTES];
+    const char *new_value[HTML_AREA_ATTRIBUTES];
+    int i;
+
+    for (i = 0; i < HTML_AREA_ATTRIBUTES; i++)
+	new_present[i] = NO;
+
+    if (alt) {
+	new_present[HTML_AREA_ALT] = YES;
+	new_value[HTML_AREA_ALT] = (const char *) alt;
+    }
+    if (non_empty(title)) {
+	new_present[HTML_AREA_TITLE] = YES;
+	new_value[HTML_AREA_TITLE] = (const char *) title;
+    }
+    if (href) {
+	new_present[HTML_AREA_HREF] = YES;
+	new_value[HTML_AREA_HREF] = (const char *) href;
+    }
+
+    (*obj->isa->start_element) (obj, HTML_AREA, new_present, new_value,
+				tag_charset, 0);
+}
+
+static void LYHandleFIG(HTStructured * me, const BOOL *present,
+			const char **value,
+			BOOL isobject,
+			BOOL imagemap,
+			const char *id,
+			const char *src,
+			BOOL convert,
+			BOOL start,
+			BOOL *intern_flag GCC_UNUSED)
+{
+    if (start == TRUE) {
+	me->inFIG = TRUE;
+	if (me->inA) {
+	    SET_SKIP_STACK(HTML_A);
+	    HTML_end_element(me, HTML_A, NULL);
+	}
+	if (!isobject) {
+	    LYEnsureDoubleSpace(me);
+	    LYResetParagraphAlignment(me);
+	    me->inFIGwithP = TRUE;
+	} else {
+	    me->inFIGwithP = FALSE;
+	    HTML_put_character(me, ' ');	/* space char may be ignored */
+	}
+	if (non_empty(id)) {
+	    if (present && convert) {
+		CHECK_ID(HTML_FIG_ID);
+	    } else
+		LYHandleID(me, id);
+	}
+	me->in_word = NO;
+	me->inP = FALSE;
+
+	if (clickable_images && non_empty(src)) {
+	    char *href = NULL;
+
+	    StrAllocCopy(href, src);
+	    CHECK_FOR_INTERN(*intern_flag, href);
+	    LYLegitimizeHREF(me, &href, TRUE, TRUE);
+	    if (*href) {
+		me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+							 NULL,	/* Tag */
+							 href,	/* Addresss */
+							 INTERN_LT);	/* Type */
+		HText_beginAnchor(me->text, me->inUnderline, me->CurrentA);
+		if (me->inBoldH == FALSE)
+		    HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
+		HTML_put_string(me, (isobject
+				     ? (imagemap
+					? "(IMAGE)"
+					: "(OBJECT)")
+				     : "[FIGURE]"));
+		if (me->inBoldH == FALSE)
+		    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+		HText_endAnchor(me->text, 0);
+		HTML_put_character(me, '-');
+		HTML_put_character(me, ' ');	/* space char may be ignored */
+		me->in_word = NO;
+	    }
+	    FREE(href);
+	}
+    } else {			/* handle end tag */
+	if (me->inFIGwithP) {
+	    LYEnsureDoubleSpace(me);
+	} else {
+	    HTML_put_character(me, ' ');	/* space char may be ignored */
+	}
+	LYResetParagraphAlignment(me);
+	me->inFIGwithP = FALSE;
+	me->inFIG = FALSE;
+	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
+	if (me->List_Nesting_Level >= 0) {
+	    UPDATE_STYLE;
+	    HText_NegateLineOne(me->text);
+	}
+    }
+}
+
+static void clear_objectdata(HTStructured * me)
+{
+    if (me) {
+	HTChunkClear(&me->object);
+	me->object_started = FALSE;
+	me->object_declare = FALSE;
+	me->object_shapes = FALSE;
+	me->object_ismap = FALSE;
+	FREE(me->object_usemap);
+	FREE(me->object_id);
+	FREE(me->object_title);
+	FREE(me->object_data);
+	FREE(me->object_type);
+	FREE(me->object_classid);
+	FREE(me->object_codebase);
+	FREE(me->object_codetype);
+	FREE(me->object_name);
+    }
+}
+
+#define HTParseALL(pp,pconst)  \
+	{ char* free_me = *pp; \
+	  *pp = HTParse(*pp, pconst, PARSE_ALL); \
+	  FREE(free_me);       \
+	}
+
+/*	Start Element
+ *	-------------
+ */
+static int HTML_start_element(HTStructured * me, int element_number,
+			      const BOOL *present,
+			      const char **value,
+			      int tag_charset,
+			      char **include)
+{
+    char *alt_string = NULL;
+    char *id_string = NULL;
+    char *newtitle = NULL;
+    char **pdoctitle = NULL;
+    char *href = NULL;
+    char *map_href = NULL;
+    char *title = NULL;
+    char *I_value = NULL;
+    char *I_name = NULL;
+    char *temp = NULL;
+    const char *Base = NULL;
+    int dest_char_set = -1;
+    HTParentAnchor *dest = NULL;	/* An anchor's destination */
+    BOOL dest_ismap = FALSE;	/* Is dest an image map script? */
+    HTChildAnchor *ID_A = NULL;	/* HTML_foo_ID anchor */
+    int url_type = 0, i = 0;
+    char *cp = NULL;
+    HTMLElement ElementNumber = (HTMLElement) element_number;
+    BOOL intern_flag = FALSE;
+    short stbl_align = HT_ALIGN_NONE;
+    int status = HT_OK;
+
+#ifdef USE_COLOR_STYLE
+    char *class_name;
+    int class_used = 0;
+#endif
+
+#ifdef USE_PRETTYSRC
+    if (psrc_view && !sgml_in_psrc_was_initialized) {
+	if (!psrc_nested_call) {
+	    HTTag *tag = &HTML_dtd.tags[element_number];
+	    char buf[200];
+	    const char *p;
+
+	    if (psrc_first_tag) {
+		psrc_first_tag = FALSE;
+		/* perform the special actions on the begining of the document.
+		   It's assumed that all lynx modules start generating html
+		   from tag (ie not a text) so we are able to trap this moment
+		   and initialize.
+		 */
+		psrc_nested_call = TRUE;
+		HTML_start_element(me, HTML_BODY, NULL, NULL, tag_charset, NULL);
+		HTML_start_element(me, HTML_PRE, NULL, NULL, tag_charset, NULL);
+		PSRCSTART(entire);
+		psrc_nested_call = FALSE;
+	    }
+
+	    psrc_nested_call = TRUE;
+	    /*write markup for tags and exit */
+	    PSRCSTART(abracket);
+	    PUTC('<');
+	    PSRCSTOP(abracket);
+	    PSRCSTART(tag);
+	    if (tagname_transform != 0)
+		PUTS(tag->name);
+	    else {
+		LYstrncpy(buf, tag->name, sizeof(buf) - 1);
+		LYLowerCase(buf);
+		PUTS(buf);
+	    }
+	    if (present) {
+		for (i = 0; i < tag->number_of_attributes; i++)
+		    if (present[i]) {
+			PUTC(' ');
+			PSRCSTART(attrib);
+			if (attrname_transform != 0)
+			    PUTS(tag->attributes[i].name);
+			else {
+			    LYstrncpy(buf,
+				      tag->attributes[i].name,
+				      sizeof(buf) - 1);
+			    LYLowerCase(buf);
+			    PUTS(buf);
+			}
+			if (value[i]) {
+			    char q = '"';
+
+			    /*0 in dquotes, 1 - in quotes, 2 mixed */
+			    char kind = (char) (!strchr(value[i], '"') ?
+						0 :
+						!strchr(value[i], '\'') ?
+						q = '\'', 1 :
+						2);
+
+			    PUTC('=');
+			    PSRCSTOP(attrib);
+			    PSRCSTART(attrval);
+			    PUTC(q);
+			    /*is it special ? */
+			    if (tag->attributes[i].type == HTMLA_ANAME) {
+				HTStartAnchor(me, value[i], NULL);
+				HTML_end_element(me, HTML_A, NULL);
+			    } else if (tag->attributes[i].type == HTMLA_HREF) {
+				PSRCSTART(href);
+				HTStartAnchor(me, NULL, value[i]);
+			    }
+			    if (kind != 2)
+				PUTS(value[i]);
+			    else
+				for (p = value[i]; *p; p++)
+				    if (*p != '"')
+					PUTC(*p);
+				    else
+					PUTS("&#34;");
+			    /*is it special ? */
+			    if (tag->attributes[i].type == HTMLA_HREF) {
+				HTML_end_element(me, HTML_A, NULL);
+				PSRCSTOP(href);
+			    }
+			    PUTC(q);
+			    PSRCSTOP(attrval);
+			}	/* if value */
+		    }		/* if present[i] */
+	    }			/* if present */
+	    PSRCSTOP(tag);
+	    PSRCSTART(abracket);
+	    PUTC('>');
+	    PSRCSTOP(abracket);
+	    psrc_nested_call = FALSE;
+	    return HT_OK;
+	}			/*if (!psrc_nested_call) */
+	/*fall through */
+    }
+#endif /* USE_PRETTYSRC */
+
+    if (LYMapsOnly) {
+	if (!(ElementNumber == HTML_MAP || ElementNumber == HTML_AREA ||
+	      ElementNumber == HTML_BASE || ElementNumber == HTML_OBJECT ||
+	      ElementNumber == HTML_A)) {
+	    return HT_OK;
+	}
+    } else if (!me->text) {
+	UPDATE_STYLE;
+    } {
+	/*  me->tag_charset  is charset for attribute values.  */
+	int j = ((tag_charset < 0) ? me->UCLYhndl : tag_charset);
+
+	if ((me->tag_charset != j) || (j < 0 /* for trace entry */ )) {
+	    CTRACE((tfp, "me->tag_charset: %d -> %d", me->tag_charset, j));
+	    CTRACE((tfp, " (me->UCLYhndl: %d, tag_charset: %d)\n",
+		    me->UCLYhndl, tag_charset));
+	    me->tag_charset = j;
+	}
+    }
+
+/* this should be done differently */
+#if defined(USE_COLOR_STYLE)
+
+    addClassName(";",
+		 HTML_dtd.tags[element_number].name,
+		 HTML_dtd.tags[element_number].name_len);
+
+    class_name = (force_classname ? forced_classname : class_string);
+    force_classname = FALSE;
+
+    if (force_current_tag_style == FALSE) {
+	current_tag_style = (class_name[0]
+			     ? -1
+			     : cached_tag_styles[element_number]);
+    } else {
+	force_current_tag_style = FALSE;
+    }
+
+    CTRACE2(TRACE_STYLE, (tfp, "CSS.elt:<%s>\n", HTML_dtd.tags[element_number].name));
+
+    if (current_tag_style == -1) {	/* Append class_name */
+	hcode = hash_code_lowercase_on_fly(HTML_dtd.tags[element_number].name);
+	if (class_name[0]) {
+	    int ohcode = hcode;
+
+	    hcode = hash_code_aggregate_char('.', hcode);
+	    hcode = hash_code_aggregate_lower_str(class_name, hcode);
+	    if (!hashStyles[hcode].name) {	/* None such -> classless version */
+		hcode = ohcode;
+		CTRACE2(TRACE_STYLE,
+			(tfp,
+			 "STYLE.start_element: <%s> (class <%s> not configured), hcode=%d.\n",
+			 HTML_dtd.tags[element_number].name, class_name, hcode));
+	    } else {
+		addClassName(".", class_name, strlen(class_name));
+
+		CTRACE2(TRACE_STYLE,
+			(tfp, "STYLE.start_element: <%s>.<%s>, hcode=%d.\n",
+			 HTML_dtd.tags[element_number].name, class_name, hcode));
+		class_used = 1;
+	    }
+	}
+
+	class_string[0] = '\0';
+
+    } else {			/* (current_tag_style!=-1)  */
+	if (class_name[0]) {
+	    addClassName(".", class_name, strlen(class_name));
+	    class_string[0] = '\0';
+	}
+	hcode = current_tag_style;
+	CTRACE2(TRACE_STYLE,
+		(tfp, "STYLE.start_element: <%s>, hcode=%d.\n",
+		 HTML_dtd.tags[element_number].name, hcode));
+	current_tag_style = -1;
+    }
+
+#if !OMIT_SCN_KEEPING		/* Can be done in other cases too... */
+    if (!class_used && ElementNumber == HTML_INPUT) {	/* For some other too? */
+	const char *type = "";
+	int ohcode = hcode;
+
+	if (present && present[HTML_INPUT_TYPE] && value[HTML_INPUT_TYPE])
+	    type = value[HTML_INPUT_TYPE];
+
+	hcode = hash_code_aggregate_lower_str(".type.", hcode);
+	hcode = hash_code_aggregate_lower_str(type, hcode);
+	if (!hashStyles[hcode].name) {	/* None such -> classless version */
+	    hcode = ohcode;
+	    CTRACE2(TRACE_STYLE,
+		    (tfp, "STYLE.start_element: type <%s> not configured.\n",
+		     type));
+	} else {
+	    addClassName(".type.", type, strlen(type));
+
+	    CTRACE2(TRACE_STYLE,
+		    (tfp, "STYLE.start_element: <%s>.type.<%s>, hcode=%d.\n",
+		     HTML_dtd.tags[element_number].name, type, hcode));
+	}
+    }
+#endif /* !OMIT_SCN_KEEPING */
+
+    HText_characterStyle(me->text, hcode, STACK_ON);
+#endif /* USE_COLOR_STYLE */
+
+    /*
+     * Handle the start tag.  - FM
+     */
+    switch (ElementNumber) {
+
+    case HTML_HTML:
+	break;
+
+    case HTML_HEAD:
+	break;
+
+    case HTML_BASE:
+	if (present && present[HTML_BASE_HREF] && !local_host_only &&
+	    non_empty(value[HTML_BASE_HREF])) {
+	    char *base = NULL;
+	    const char *related = NULL;
+
+	    StrAllocCopy(base, value[HTML_BASE_HREF]);
+	    CTRACE((tfp, "*HTML_BASE: initial href=`%s'\n", NonNull(base)));
+
+	    if (!(url_type = LYLegitimizeHREF(me, &base, TRUE, TRUE))) {
+		CTRACE((tfp, "HTML: BASE '%s' is not an absolute URL.\n",
+			NonNull(base)));
+		if (me->inBadBASE == FALSE)
+		    HTAlert(BASE_NOT_ABSOLUTE);
+		me->inBadBASE = TRUE;
+	    }
+
+	    if (url_type == LYNXIMGMAP_URL_TYPE) {
+		/*
+		 * These have a non-standard form, basically strip the prefix
+		 * or the code below would insert a nonsense host into the
+		 * pseudo URL.  These should never occur where they would be
+		 * used for resolution of relative URLs anyway.  We can also
+		 * strip the #map part.  - kw
+		 */
+		temp = base;
+		base = HTParse(base + 11, "", PARSE_ALL_WITHOUT_ANCHOR);
+		FREE(temp);
+	    }
+
+	    /*
+	     * Get parent's address for defaulted fields.
+	     */
+	    related = me->node_anchor->address;
+
+	    /*
+	     * Create the access field.
+	     */
+	    temp = HTParse(base, related, PARSE_ACCESS + PARSE_PUNCTUATION);
+	    StrAllocCopy(me->base_href, temp);
+	    FREE(temp);
+
+	    /*
+	     * Create the host[:port] field.
+	     */
+	    temp = HTParse(base, "", PARSE_HOST + PARSE_PUNCTUATION);
+	    if (!strncmp(temp, "//", 2)) {
+		StrAllocCat(me->base_href, temp);
+		if (!strcmp(me->base_href, "file://")) {
+		    StrAllocCat(me->base_href, "localhost");
+		}
+	    } else {
+		if (isFILE_URL(me->base_href)) {
+		    StrAllocCat(me->base_href, "//localhost");
+		} else if (strcmp(me->base_href, STR_NEWS_URL)) {
+		    FREE(temp);
+		    StrAllocCat(me->base_href, (temp = HTParse(related, "",
+							       PARSE_HOST + PARSE_PUNCTUATION)));
+		}
+	    }
+	    FREE(temp);
+
+	    /*
+	     * Create the path field.
+	     */
+	    temp = HTParse(base, "", PARSE_PATH + PARSE_PUNCTUATION);
+	    if (*temp != '\0') {
+		char *p = strchr(temp, '?');
+
+		if (p)
+		    *p = '\0';
+		p = strrchr(temp, '/');
+		if (p)
+		    *(p + 1) = '\0';	/* strip after the last slash */
+
+		StrAllocCat(me->base_href, temp);
+	    } else if (!strcmp(me->base_href, STR_NEWS_URL)) {
+		StrAllocCat(me->base_href, "*");
+	    } else if (isNEWS_URL(me->base_href) ||
+		       isNNTP_URL(me->base_href) ||
+		       isSNEWS_URL(me->base_href)) {
+		StrAllocCat(me->base_href, "/*");
+	    } else {
+		StrAllocCat(me->base_href, "/");
+	    }
+	    FREE(temp);
+	    FREE(base);
+
+	    me->inBASE = TRUE;
+	    me->node_anchor->inBASE = TRUE;
+	    StrAllocCopy(me->node_anchor->content_base, me->base_href);
+	    /* me->base_href is a valid URL */
+
+	    CTRACE((tfp, "*HTML_BASE: final href=`%s'\n", me->base_href));
+	}
+	break;
+
+    case HTML_META:
+	if (present)
+	    LYHandleMETA(me, present, value, include);
+	break;
+
+    case HTML_TITLE:
+	HTChunkClear(&me->title);
+	break;
+
+    case HTML_LINK:
+	intern_flag = FALSE;
+	if (present && present[HTML_LINK_HREF]) {
+	    CHECK_FOR_INTERN(intern_flag, value[HTML_LINK_HREF]);
+	    /*
+	     * Prepare to do housekeeping on the reference.  - FM
+	     */
+	    if (isEmpty(value[HTML_LINK_HREF])) {
+		Base = (me->inBASE)
+		    ? me->base_href
+		    : me->node_anchor->address;
+		StrAllocCopy(href, Base);
+	    } else {
+		StrAllocCopy(href, value[HTML_LINK_HREF]);
+		(void) LYLegitimizeHREF(me, &href, TRUE, TRUE);
+
+		Base = (me->inBASE && *href != '\0' && *href != '#')
+		    ? me->base_href
+		    : me->node_anchor->address;
+		HTParseALL(&href, Base);
+	    }
+
+	    /*
+	     * Handle links with a REV attribute.  - FM
+	     * Handle REV="made" or REV="owner".  - LM & FM
+	     * Handle REL="author" -TD
+	     */
+	    if (present &&
+		((present[HTML_LINK_REV] &&
+		  value[HTML_LINK_REV] &&
+		  (!strcasecomp("made", value[HTML_LINK_REV]) ||
+		   !strcasecomp("owner", value[HTML_LINK_REV]))) ||
+		 (present[HTML_LINK_REL] &&
+		  value[HTML_LINK_REL] &&
+		  (!strcasecomp("author", value[HTML_LINK_REL]))))) {
+		/*
+		 * Load the owner element.  - FM
+		 */
+		HTAnchor_setOwner(me->node_anchor, href);
+		CTRACE((tfp, "HTML: DOC OWNER '%s' found\n", href));
+		FREE(href);
+
+		/*
+		 * Load the RevTitle element if a TITLE attribute and value
+		 * are present.  - FM
+		 */
+		if (present && present[HTML_LINK_TITLE] &&
+		    value[HTML_LINK_TITLE] &&
+		    *value[HTML_LINK_TITLE] != '\0') {
+		    StrAllocCopy(title, value[HTML_LINK_TITLE]);
+		    TRANSLATE_AND_UNESCAPE_ENTITIES(&title, TRUE, FALSE);
+		    LYTrimHead(title);
+		    LYTrimTail(title);
+		    if (*title != '\0')
+			HTAnchor_setRevTitle(me->node_anchor, title);
+		    FREE(title);
+		}
+		break;
+	    }
+
+	    /*
+	     * Handle REL links.  - FM
+	     */
+
+	    if (present &&
+		present[HTML_LINK_REL] && value[HTML_LINK_REL]) {
+		/*
+		 * Ignore style sheets, for now.  - FM
+		 *
+		 * lss and css have different syntax - lynx shouldn't try to
+		 * parse them now (it tries to parse them as lss, so it exits
+		 * with error message on the 1st non-empty line) - VH
+		 */
+#ifndef USE_COLOR_STYLE
+		if (!strcasecomp(value[HTML_LINK_REL], "StyleSheet") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Style")) {
+		    CTRACE2(TRACE_STYLE,
+			    (tfp, "HTML: StyleSheet link found.\n"));
+		    CTRACE2(TRACE_STYLE,
+			    (tfp, "        StyleSheets not yet implemented.\n"));
+		    FREE(href);
+		    break;
+		}
+#endif /* ! USE_COLOR_STYLE */
+
+		/*
+		 * Ignore anything not registered in the 28-Mar-95 IETF HTML
+		 * 3.0 draft and W3C HTML 3.2 draft, or not appropriate for
+		 * Lynx banner links in the expired Maloney and Quin relrev
+		 * draft.  We'll make this more efficient when the situation
+		 * stabilizes, and for now, we'll treat "Banner" as another
+		 * toolbar element.  - FM
+		 */
+		if (!strcasecomp(value[HTML_LINK_REL], "Home") ||
+		    !strcasecomp(value[HTML_LINK_REL], "ToC") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Contents") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Index") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Glossary") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Copyright") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Help") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Search") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Bookmark") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Banner") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Top") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Origin") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Navigator") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Disclaimer") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Author") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Editor") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Publisher") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Trademark") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Hotlist") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Begin") ||
+		    !strcasecomp(value[HTML_LINK_REL], "First") ||
+		    !strcasecomp(value[HTML_LINK_REL], "End") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Last") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Documentation") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Biblioentry") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Bibliography") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Start") ||
+		    !strcasecomp(value[HTML_LINK_REL], "Appendix")) {
+		    StrAllocCopy(title, value[HTML_LINK_REL]);
+		    pdoctitle = &title;		/* for setting HTAnchor's title */
+		} else if (!strcasecomp(value[HTML_LINK_REL], "Up") ||
+			   !strcasecomp(value[HTML_LINK_REL], "Next") ||
+			   !strcasecomp(value[HTML_LINK_REL], "Previous") ||
+			   !strcasecomp(value[HTML_LINK_REL], "Prev") ||
+			   !strcasecomp(value[HTML_LINK_REL], "Child") ||
+			   !strcasecomp(value[HTML_LINK_REL], "Sibling") ||
+			   !strcasecomp(value[HTML_LINK_REL], "Parent") ||
+			   !strcasecomp(value[HTML_LINK_REL], "Meta") ||
+			   !strcasecomp(value[HTML_LINK_REL], "URC") ||
+			   !strcasecomp(value[HTML_LINK_REL], "Pointer") ||
+			   !strcasecomp(value[HTML_LINK_REL], "Translation") ||
+			   !strcasecomp(value[HTML_LINK_REL], "Definition") ||
+			   !strcasecomp(value[HTML_LINK_REL], "Alternate") ||
+			   !strcasecomp(value[HTML_LINK_REL], "Section") ||
+			   !strcasecomp(value[HTML_LINK_REL], "Subsection") ||
+			   !strcasecomp(value[HTML_LINK_REL], "Chapter")) {
+		    StrAllocCopy(title, value[HTML_LINK_REL]);
+		    /* not setting target HTAnchor's title, for these
+		       links of highly relative character.  Instead,
+		       try to remember the REL attribute as a property
+		       of the link (but not the destination), in the
+		       (otherwise underused) link type in a special format;
+		       the LIST page generation code may later use it. - kw */
+		    if (!intern_flag) {
+			StrAllocCopy(temp, "RelTitle: ");
+			StrAllocCat(temp, value[HTML_LINK_REL]);
+		    }
+#ifndef DISABLE_BIBP
+		} else if (!strcasecomp(value[HTML_LINK_REL], "citehost")) {
+		    /*  Citehost determination for bibp links. - RDC */
+		    HTAnchor_setCitehost(me->node_anchor, href);
+		    CTRACE((tfp, "HTML: citehost '%s' found\n", href));
+		    FREE(href);
+		    break;
+#endif
+		} else {
+		    CTRACE((tfp, "HTML: LINK with REL=\"%s\" ignored.\n",
+			    value[HTML_LINK_REL]));
+		    FREE(href);
+		    break;
+		}
+	    }
+	} else if (present &&
+		   present[HTML_LINK_REL] && value[HTML_LINK_REL]) {
+	    /*
+	     * If no HREF was specified, handle special REL links with
+	     * self-designated HREFs.  - FM
+	     */
+	    if (!strcasecomp(value[HTML_LINK_REL], "Home")) {
+		StrAllocCopy(href, LynxHome);
+	    } else if (!strcasecomp(value[HTML_LINK_REL], "Help")) {
+		StrAllocCopy(href, helpfile);
+	    } else if (!strcasecomp(value[HTML_LINK_REL], "Index")) {
+		StrAllocCopy(href, indexfile);
+	    } else {
+		CTRACE((tfp,
+			"HTML: LINK with REL=\"%s\" and no HREF ignored.\n",
+			value[HTML_LINK_REL]));
+		break;
+	    }
+	    StrAllocCopy(title, value[HTML_LINK_REL]);
+	    pdoctitle = &title;
+	}
+	if (href) {
+	    /*
+	     * Create a title (link name) from the TITLE value, if present, or
+	     * default to the REL value that was loaded into title.  - FM
+	     */
+	    if (present && present[HTML_LINK_TITLE] &&
+		non_empty(value[HTML_LINK_TITLE])) {
+		StrAllocCopy(title, value[HTML_LINK_TITLE]);
+		TRANSLATE_AND_UNESCAPE_ENTITIES(&title, TRUE, FALSE);
+		LYTrimHead(title);
+		LYTrimTail(title);
+		pdoctitle = &title;
+		FREE(temp);	/* forget about recording RelTitle - kw */
+	    }
+	    if (isEmpty(title)) {
+		FREE(href);
+		FREE(title);
+		break;
+	    }
+
+	    if (me->inA) {
+		/*
+		 * Ugh!  The LINK tag, which is a HEAD element, is in an
+		 * Anchor, which is BODY element.  All we can do is close the
+		 * Anchor and cross our fingers.  - FM
+		 */
+		SET_SKIP_STACK(HTML_A);
+		HTML_end_element(me, HTML_A, include);
+	    }
+
+	    /*
+	     * Create anchors for the links that simulate a toolbar.  - FM
+	     */
+	    me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+						     NULL,	/* Tag */
+						     href,	/* Addresss */
+						     (temp
+						      ? (HTLinkType *)
+						      HTAtom_for(temp)
+						      : INTERN_LT));	/* Type */
+	    FREE(temp);
+	    if ((dest = HTAnchor_parent(HTAnchor_followLink(me->CurrentA)
+		 )) != NULL) {
+		if (pdoctitle && !HTAnchor_title(dest))
+		    HTAnchor_setTitle(dest, *pdoctitle);
+
+		/* Don't allow CHARSET attribute to change *this* document's
+		   charset assumption. - kw */
+		if (dest == me->node_anchor)
+		    dest = NULL;
+		if (present[HTML_LINK_CHARSET] &&
+		    non_empty(value[HTML_LINK_CHARSET])) {
+		    dest_char_set = UCGetLYhndl_byMIME(value[HTML_LINK_CHARSET]);
+		    if (dest_char_set < 0)
+			dest_char_set = UCLYhndl_for_unrec;
+		}
+		if (dest && dest_char_set >= 0)
+		    HTAnchor_setUCInfoStage(dest, dest_char_set,
+					    UCT_STAGE_PARSER,
+					    UCT_SETBY_LINK);
+	    }
+	    UPDATE_STYLE;
+	    if (!HText_hasToolbar(me->text) &&
+		(ID_A = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+						  LYToolbarName,	/* Tag */
+						  NULL,		/* Addresss */
+						  (HTLinkType *) 0))) {		/* Type */
+		HText_appendCharacter(me->text, '#');
+		HText_setLastChar(me->text, ' ');	/* absorb white space */
+		HText_beginAnchor(me->text, me->inUnderline, ID_A);
+		HText_endAnchor(me->text, 0);
+		HText_setToolbar(me->text);
+	    } else {
+		/*
+		 * Add collapsible space to separate link from previous
+		 * generated links.  - kw
+		 */
+		HTML_put_character(me, ' ');
+	    }
+	    HText_beginAnchor(me->text, me->inUnderline, me->CurrentA);
+	    if (me->inBoldH == FALSE)
+		HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
+#ifdef USE_COLOR_STYLE
+	    if (present && present[HTML_LINK_CLASS] &&
+		non_empty(value[HTML_LINK_CLASS])) {
+		char *tmp = 0;
+
+		HTSprintf0(&tmp, "link.%s.%s", value[HTML_LINK_CLASS], title);
+		CTRACE2(TRACE_STYLE,
+			(tfp, "STYLE.link: using style <%s>\n", tmp));
+
+		HText_characterStyle(me->text, hash_code(tmp), STACK_ON);
+		HTML_put_string(me, title);
+		HTML_put_string(me, " (");
+		HTML_put_string(me, value[HTML_LINK_CLASS]);
+		HTML_put_string(me, ")");
+		HText_characterStyle(me->text, hash_code(tmp), STACK_OFF);
+		FREE(tmp);
+	    } else
+#endif
+		HTML_put_string(me, title);
+	    if (me->inBoldH == FALSE)
+		HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+	    HText_endAnchor(me->text, 0);
+	}
+	FREE(href);
+	FREE(title);
+	break;
+
+    case HTML_ISINDEX:
+	if (((present)) &&
+	    ((present[HTML_ISINDEX_HREF] && value[HTML_ISINDEX_HREF]) ||
+	     (present[HTML_ISINDEX_ACTION] && value[HTML_ISINDEX_ACTION]))) {
+	    /*
+	     * Lynx was supporting ACTION, which never made it into the HTML
+	     * 2.0 specs.  HTML 3.0 uses HREF, so we'll use that too, but allow
+	     * use of ACTION as an alternate until people have fully switched
+	     * over.  - FM
+	     */
+	    if (present[HTML_ISINDEX_HREF] && value[HTML_ISINDEX_HREF])
+		StrAllocCopy(href, value[HTML_ISINDEX_HREF]);
+	    else
+		StrAllocCopy(href, value[HTML_ISINDEX_ACTION]);
+	    LYLegitimizeHREF(me, &href, TRUE, TRUE);
+
+	    Base = (me->inBASE && *href != '\0' && *href != '#')
+		? me->base_href
+		: me->node_anchor->address;
+	    HTParseALL(&href, Base);
+	    HTAnchor_setIndex(me->node_anchor, href);
+	    FREE(href);
+
+	} else {
+	    Base = (me->inBASE) ?
+		me->base_href : me->node_anchor->address;
+	    HTAnchor_setIndex(me->node_anchor, Base);
+	}
+	/*
+	 * Support HTML 3.0 PROMPT attribute.  - FM
+	 */
+	if (present &&
+	    present[HTML_ISINDEX_PROMPT] &&
+	    non_empty(value[HTML_ISINDEX_PROMPT])) {
+	    StrAllocCopy(temp, value[HTML_ISINDEX_PROMPT]);
+	    TRANSLATE_AND_UNESCAPE_ENTITIES(&temp, TRUE, FALSE);
+	    LYTrimHead(temp);
+	    LYTrimTail(temp);
+	    if (*temp != '\0') {
+		StrAllocCat(temp, " ");
+		HTAnchor_setPrompt(me->node_anchor, temp);
+	    } else {
+		HTAnchor_setPrompt(me->node_anchor, ENTER_DATABASE_QUERY);
+	    }
+	    FREE(temp);
+	} else {
+	    HTAnchor_setPrompt(me->node_anchor, ENTER_DATABASE_QUERY);
+	}
+	break;
+
+    case HTML_NEXTID:
+	break;
+
+    case HTML_STYLE:
+	/*
+	 * We're getting it as Literal text, which, for now, we'll just ignore. 
+	 * - FM
+	 */
+	HTChunkClear(&me->style_block);
+	break;
+
+    case HTML_SCRIPT:
+	/*
+	 * We're getting it as Literal text, which, for now, we'll just ignore. 
+	 * - FM
+	 */
+	HTChunkClear(&me->script);
+	break;
+
+    case HTML_BODY:
+	CHECK_ID(HTML_BODY_ID);
+	if (HText_hasToolbar(me->text))
+	    HText_appendParagraph(me->text);
+	break;
+
+    case HTML_FRAMESET:
+	break;
+
+    case HTML_FRAME:
+	if (present && present[HTML_FRAME_NAME] &&
+	    non_empty(value[HTML_FRAME_NAME])) {
+	    StrAllocCopy(id_string, value[HTML_FRAME_NAME]);
+	    TRANSLATE_AND_UNESCAPE_ENTITIES(&id_string, TRUE, FALSE);
+	    LYTrimHead(id_string);
+	    LYTrimTail(id_string);
+	}
+	if (present && present[HTML_FRAME_SRC] &&
+	    non_empty(value[HTML_FRAME_SRC])) {
+	    StrAllocCopy(href, value[HTML_FRAME_SRC]);
+	    LYLegitimizeHREF(me, &href, TRUE, TRUE);
+
+	    if (me->inA) {
+		SET_SKIP_STACK(HTML_A);
+		HTML_end_element(me, HTML_A, include);
+	    }
+	    me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+						     NULL,	/* Tag */
+						     href,	/* Addresss */
+						     (HTLinkType *) 0);		/* Type */
+	    CAN_JUSTIFY_PUSH(FALSE);
+	    LYEnsureSingleSpace(me);
+	    if (me->inUnderline == FALSE)
+		HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
+	    HTML_put_string(me, "FRAME:");
+	    if (me->inUnderline == FALSE)
+		HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
+	    HTML_put_character(me, ' ');
+
+	    me->in_word = NO;
+	    CHECK_ID(HTML_FRAME_ID);
+	    HText_beginAnchor(me->text, me->inUnderline, me->CurrentA);
+	    if (me->inBoldH == FALSE)
+		HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
+	    HTML_put_string(me, (id_string ? id_string : href));
+	    FREE(href);
+	    if (me->inBoldH == FALSE)
+		HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+	    HText_endAnchor(me->text, 0);
+	    LYEnsureSingleSpace(me);
+	    CAN_JUSTIFY_POP;
+	} else {
+	    CHECK_ID(HTML_FRAME_ID);
+	}
+	FREE(id_string);
+	break;
+
+    case HTML_NOFRAMES:
+	LYEnsureDoubleSpace(me);
+	LYResetParagraphAlignment(me);
+	break;
+
+    case HTML_IFRAME:
+	if (present && present[HTML_IFRAME_NAME] &&
+	    non_empty(value[HTML_IFRAME_NAME])) {
+	    StrAllocCopy(id_string, value[HTML_IFRAME_NAME]);
+	    TRANSLATE_AND_UNESCAPE_ENTITIES(&id_string, TRUE, FALSE);
+	    LYTrimHead(id_string);
+	    LYTrimTail(id_string);
+	}
+	if (present && present[HTML_IFRAME_SRC] &&
+	    non_empty(value[HTML_IFRAME_SRC])) {
+	    StrAllocCopy(href, value[HTML_IFRAME_SRC]);
+	    LYLegitimizeHREF(me, &href, TRUE, TRUE);
+
+	    if (me->inA)
+		HTML_end_element(me, HTML_A, include);
+
+	    me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+						     NULL,	/* Tag */
+						     href,	/* Addresss */
+						     (HTLinkType *) 0);		/* Type */
+	    LYEnsureDoubleSpace(me);
+	    CAN_JUSTIFY_PUSH_F
+		LYResetParagraphAlignment(me);
+	    if (me->inUnderline == FALSE)
+		HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
+	    HTML_put_string(me, "IFRAME:");
+	    if (me->inUnderline == FALSE)
+		HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
+	    HTML_put_character(me, ' ');
+
+	    me->in_word = NO;
+	    CHECK_ID(HTML_IFRAME_ID);
+	    HText_beginAnchor(me->text, me->inUnderline, me->CurrentA);
+	    if (me->inBoldH == FALSE)
+		HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
+	    HTML_put_string(me, (id_string ? id_string : href));
+	    FREE(href);
+	    if (me->inBoldH == FALSE)
+		HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+	    HText_endAnchor(me->text, 0);
+	    LYEnsureSingleSpace(me);
+	    CAN_JUSTIFY_POP;
+	} else {
+	    CHECK_ID(HTML_IFRAME_ID);
+	}
+	FREE(id_string);
+	break;
+
+    case HTML_BANNER:
+    case HTML_MARQUEE:
+	change_paragraph_style(me, styles[HTML_BANNER]);
+	UPDATE_STYLE;
+	if (me->sp->tag_number == (int) ElementNumber)
+	    LYEnsureDoubleSpace(me);
+	/*
+	 * Treat this as a toolbar if we don't have one yet, and we are in the
+	 * first half of the first page.  - FM
+	 */
+	if ((!HText_hasToolbar(me->text) &&
+	     HText_getLines(me->text) < (display_lines / 2)) &&
+	    (ID_A = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+					      LYToolbarName,	/* Tag */
+					      NULL,	/* Addresss */
+					      (HTLinkType *) 0))) {	/* Type */
+	    HText_beginAnchor(me->text, me->inUnderline, ID_A);
+	    HText_endAnchor(me->text, 0);
+	    HText_setToolbar(me->text);
+	}
+	CHECK_ID(HTML_GEN_ID);
+	break;
+
+    case HTML_CENTER:
+    case HTML_DIV:
+	if (me->Division_Level < (MAX_NESTING - 1)) {
+	    me->Division_Level++;
+	} else {
+	    CTRACE((tfp,
+		    "HTML: ****** Maximum nesting of %d divisions exceeded!\n",
+		    MAX_NESTING));
+	}
+	if (me->inP)
+	    LYEnsureSingleSpace(me);	/* always at least break line - kw */
+	if (ElementNumber == HTML_CENTER) {
+	    me->DivisionAlignments[me->Division_Level] = HT_CENTER;
+	    change_paragraph_style(me, styles[HTML_DCENTER]);
+	    UPDATE_STYLE;
+	    me->current_default_alignment = styles[HTML_DCENTER]->alignment;
+	} else if (me->List_Nesting_Level >= 0 &&
+		   !(present && present[HTML_DIV_ALIGN] &&
+		     value[HTML_DIV_ALIGN] &&
+		     (!strcasecomp(value[HTML_DIV_ALIGN], "center") ||
+		      !strcasecomp(value[HTML_DIV_ALIGN], "right")))) {
+	    if (present && present[HTML_DIV_ALIGN])
+		me->current_default_alignment = HT_LEFT;
+	    else if (me->Division_Level == 0)
+		me->current_default_alignment = HT_LEFT;
+	    else if (me->sp[0].tag_number == HTML_UL ||
+		     me->sp[0].tag_number == HTML_OL ||
+		     me->sp[0].tag_number == HTML_MENU ||
+		     me->sp[0].tag_number == HTML_DIR ||
+		     me->sp[0].tag_number == HTML_LI ||
+		     me->sp[0].tag_number == HTML_LH ||
+		     me->sp[0].tag_number == HTML_DD)
+		me->current_default_alignment = HT_LEFT;
+	    LYHandlePlike(me, present, value, include, HTML_DIV_ALIGN, TRUE);
+	    me->DivisionAlignments[me->Division_Level] = (short)
+		me->current_default_alignment;
+	} else if (present && present[HTML_DIV_ALIGN] &&
+		   non_empty(value[HTML_DIV_ALIGN])) {
+	    if (!strcasecomp(value[HTML_DIV_ALIGN], "center")) {
+		me->DivisionAlignments[me->Division_Level] = HT_CENTER;
+		change_paragraph_style(me, styles[HTML_DCENTER]);
+		UPDATE_STYLE;
+		me->current_default_alignment = styles[HTML_DCENTER]->alignment;
+	    } else if (!strcasecomp(value[HTML_DIV_ALIGN], "right")) {
+		me->DivisionAlignments[me->Division_Level] = HT_RIGHT;
+		change_paragraph_style(me, styles[HTML_DRIGHT]);
+		UPDATE_STYLE;
+		me->current_default_alignment = styles[HTML_DRIGHT]->alignment;
+	    } else {
+		me->DivisionAlignments[me->Division_Level] = HT_LEFT;
+		change_paragraph_style(me, styles[HTML_DLEFT]);
+		UPDATE_STYLE;
+		me->current_default_alignment = styles[HTML_DLEFT]->alignment;
+	    }
+	} else {
+	    me->DivisionAlignments[me->Division_Level] = HT_LEFT;
+	    change_paragraph_style(me, styles[HTML_DLEFT]);
+	    UPDATE_STYLE;
+	    me->current_default_alignment = styles[HTML_DLEFT]->alignment;
+	}
+	CHECK_ID(HTML_DIV_ID);
+	break;
+
+    case HTML_H1:
+    case HTML_H2:
+    case HTML_H3:
+    case HTML_H4:
+    case HTML_H5:
+    case HTML_H6:
+	/*
+	 * Close the previous style if not done by HTML doc.  Added to get rid
+	 * of core dumps in BAD HTML on the net.
+	 *              GAB 07-07-94
+	 * But then again, these are actually allowed to nest.  I guess I have
+	 * to depend on the HTML writers correct style.
+	 *              GAB 07-12-94
+	 if (i_prior_style != -1) {
+	 HTML_end_element(me, i_prior_style);
+	 }
+	 i_prior_style = ElementNumber;
+	 */
+
+	/*
+	 * Check whether we have an H# in a list, and if so, treat it as an LH. 
+	 * - FM
+	 */
+	if ((me->List_Nesting_Level >= 0) &&
+	    (me->sp[0].tag_number == HTML_UL ||
+	     me->sp[0].tag_number == HTML_OL ||
+	     me->sp[0].tag_number == HTML_MENU ||
+	     me->sp[0].tag_number == HTML_DIR ||
+	     me->sp[0].tag_number == HTML_LI)) {
+	    if (HTML_dtd.tags[HTML_LH].contents == SGML_EMPTY) {
+		ElementNumber = HTML_LH;
+	    } else {
+		me->new_style = me->sp[0].style;
+		ElementNumber = (HTMLElement) me->sp[0].tag_number;
+		UPDATE_STYLE;
+	    }
+	    /*
+	     * Some authors use H# headers as a substitute for FONT, so check
+	     * if this one immediately followed an LI.  If so, both me->inP and
+	     * me->in_word will be FALSE (though the line might not be empty
+	     * due to a bullet and/or nbsp) and we can assume it is just for a
+	     * FONT change.  We thus will not create another line break nor add
+	     * to the current left indentation.  - FM
+	     */
+	    if (!(me->inP == FALSE && me->in_word == NO)) {
+		HText_appendParagraph(me->text);
+		HTML_put_character(me, HT_NON_BREAK_SPACE);
+		HText_setLastChar(me->text, ' ');
+		me->in_word = NO;
+		me->inP = FALSE;
+	    }
+	    CHECK_ID(HTML_H_ID);
+	    break;
+	}
+
+	if (present && present[HTML_H_ALIGN] &&
+	    non_empty(value[HTML_H_ALIGN])) {
+	    if (!strcasecomp(value[HTML_H_ALIGN], "center"))
+		change_paragraph_style(me, styles[HTML_HCENTER]);
+	    else if (!strcasecomp(value[HTML_H_ALIGN], "right"))
+		change_paragraph_style(me, styles[HTML_HRIGHT]);
+	    else if (!strcasecomp(value[HTML_H_ALIGN], "left") ||
+		     !strcasecomp(value[HTML_H_ALIGN], "justify"))
+		change_paragraph_style(me, styles[HTML_HLEFT]);
+	    else
+		change_paragraph_style(me, styles[ElementNumber]);
+	} else if (me->Division_Level >= 0) {
+	    if (me->DivisionAlignments[me->Division_Level] == HT_CENTER) {
+		change_paragraph_style(me, styles[HTML_HCENTER]);
+	    } else if (me->DivisionAlignments[me->Division_Level] == HT_LEFT) {
+		change_paragraph_style(me, styles[HTML_HLEFT]);
+	    } else if (me->DivisionAlignments[me->Division_Level] == HT_RIGHT) {
+		change_paragraph_style(me, styles[HTML_HRIGHT]);
+	    }
+	} else {
+	    change_paragraph_style(me, styles[ElementNumber]);
+	}
+	UPDATE_STYLE;
+	CHECK_ID(HTML_H_ID);
+
+	if ((bold_headers == TRUE ||
+	     (ElementNumber == HTML_H1 && bold_H1 == TRUE)) &&
+	    (styles[ElementNumber]->font & HT_BOLD)) {
+	    if (me->inBoldA == FALSE && me->inBoldH == FALSE) {
+		HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
+	    }
+	    me->inBoldH = TRUE;
+	}
+	break;
+
+    case HTML_P:
+	LYHandlePlike(me, present, value, include, HTML_P_ALIGN, TRUE);
+	CHECK_ID(HTML_P_ID);
+	break;
+
+    case HTML_BR:
+	UPDATE_STYLE;
+	CHECK_ID(HTML_GEN_ID);
+	/* Add a \r (new line) if these three conditions are true:
+	 *   1. We are not collapsing BR's, and
+	 *   2. The previous line has text on it, or
+	 *   3. This line has text on it.
+	 * Otherwise, don't do anything. -DH 980814, TD 980827
+	 */
+	if ((LYCollapseBRs == FALSE &&
+	     !HText_PreviousLineEmpty(me->text, FALSE)) ||
+	    !HText_LastLineEmpty(me->text, FALSE)) {
+	    HText_setLastChar(me->text, ' ');	/* absorb white space */
+	    HText_appendCharacter(me->text, '\r');
+	}
+	me->in_word = NO;
+	me->inP = FALSE;
+	break;
+
+    case HTML_WBR:
+	UPDATE_STYLE;
+	CHECK_ID(HTML_GEN_ID);
+	HText_setBreakPoint(me->text);
+	break;
+
+    case HTML_HY:
+    case HTML_SHY:
+	UPDATE_STYLE;
+	CHECK_ID(HTML_GEN_ID);
+	HText_appendCharacter(me->text, LY_SOFT_HYPHEN);
+	break;
+
+    case HTML_HR:
+	{
+	    int width;
+
+	    /*
+	     * Start a new line only if we had printable characters following
+	     * the previous newline, or remove the previous line if both it and
+	     * the last line are blank.  - FM
+	     */
+	    UPDATE_STYLE;
+	    if (!HText_LastLineEmpty(me->text, FALSE)) {
+		HText_setLastChar(me->text, ' ');	/* absorb white space */
+		HText_appendCharacter(me->text, '\r');
+	    } else if (HText_PreviousLineEmpty(me->text, FALSE)) {
+		HText_RemovePreviousLine(me->text);
+	    }
+	    me->in_word = NO;
+	    me->inP = FALSE;
+
+	    /*
+	     * Add an ID link if needed.  - FM
+	     */
+	    CHECK_ID(HTML_HR_ID);
+
+	    /*
+	     * Center lines within the current margins, if a right or left
+	     * ALIGNment is not specified.  If WIDTH="#%" is given and not
+	     * garbage, use that to calculate the width, otherwise use the
+	     * default width.  - FM
+	     */
+	    if (present && present[HTML_HR_ALIGN] && value[HTML_HR_ALIGN]) {
+		if (!strcasecomp(value[HTML_HR_ALIGN], "right")) {
+		    me->sp->style->alignment = HT_RIGHT;
+		} else if (!strcasecomp(value[HTML_HR_ALIGN], "left")) {
+		    me->sp->style->alignment = HT_LEFT;
+		} else {
+		    me->sp->style->alignment = HT_CENTER;
+		}
+	    } else {
+		me->sp->style->alignment = HT_CENTER;
+	    }
+	    width = LYcolLimit -
+		me->new_style->leftIndent - me->new_style->rightIndent;
+	    if (present && present[HTML_HR_WIDTH] && value[HTML_HR_WIDTH] &&
+		isdigit(UCH(*value[HTML_HR_WIDTH])) &&
+		value[HTML_HR_WIDTH][strlen(value[HTML_HR_WIDTH]) - 1] == '%') {
+		char *percent = NULL;
+		int Percent, Width;
+
+		StrAllocCopy(percent, value[HTML_HR_WIDTH]);
+		percent[strlen(percent) - 1] = '\0';
+		Percent = atoi(percent);
+		if (Percent > 100 || Percent < 1)
+		    width -= 5;
+		else {
+		    Width = (width * Percent) / 100;
+		    if (Width < 1)
+			width = 1;
+		    else
+			width = Width;
+		}
+		FREE(percent);
+	    } else {
+		width -= 5;
+	    }
+	    for (i = 0; i < width; i++)
+		HTML_put_character(me, '_');
+	    HText_appendCharacter(me->text, '\r');
+	    me->in_word = NO;
+	    me->inP = FALSE;
+
+	    /*
+	     * Reset the alignment appropriately for the division and/or block. 
+	     * - FM
+	     */
+	    if (me->List_Nesting_Level < 0 &&
+		me->Division_Level >= 0) {
+		me->sp->style->alignment =
+		    me->DivisionAlignments[me->Division_Level];
+	    } else if (me->sp->style->id == ST_HeadingCenter ||
+		       me->sp->style->id == ST_Heading1) {
+		me->sp->style->alignment = HT_CENTER;
+	    } else if (me->sp->style->id == ST_HeadingRight) {
+		me->sp->style->alignment = HT_RIGHT;
+	    } else {
+		me->sp->style->alignment = HT_LEFT;
+	    }
+
+	    /*
+	     * Add a blank line and set the second line indentation for lists
+	     * and addresses, or a paragraph separator for other blocks.  - FM
+	     */
+	    if (me->List_Nesting_Level >= 0 ||
+		me->sp[0].tag_number == HTML_ADDRESS) {
+		HText_setLastChar(me->text, ' ');	/* absorb white space */
+		HText_appendCharacter(me->text, '\r');
+	    } else {
+		HText_appendParagraph(me->text);
+	    }
+	}
+	break;
+
+    case HTML_TAB:
+	if (!present) {		/* Bad tag.  Must have at least one attribute. - FM */
+	    CTRACE((tfp, "HTML: TAB tag has no attributes.  Ignored.\n"));
+	    break;
+	}
+	/*
+	 * If page author is using TAB within a TABLE, it's probably formatted
+	 * specifically to work well for Lynx without simple table tracking
+	 * code.  Cancel tracking, it would only make things worse.  - kw
+	 */
+	HText_cancelStbl(me->text);
+	UPDATE_STYLE;
+
+	CANT_JUSTIFY_THIS_LINE;
+	if (present[HTML_TAB_ALIGN] && value[HTML_TAB_ALIGN] &&
+	    (strcasecomp(value[HTML_TAB_ALIGN], "left") ||
+	     !(present[HTML_TAB_TO] || present[HTML_TAB_INDENT]))) {
+	    /*
+	     * Just ensure a collapsible space, until we have the ALIGN and DP
+	     * attributes implemented.  - FM
+	     */
+	    HTML_put_character(me, ' ');
+	    CTRACE((tfp,
+		    "HTML: ALIGN not 'left'.  Using space instead of TAB.\n"));
+
+	} else if (!LYoverride_default_alignment(me) &&
+		   me->current_default_alignment != HT_LEFT) {
+	    /*
+	     * Just ensure a collapsible space, until we can replace
+	     * HText_getCurrentColumn() in GridText.c with code which doesn't
+	     * require that the alignment be HT_LEFT.  - FM
+	     */
+	    HTML_put_character(me, ' ');
+	    CTRACE((tfp, "HTML: Not HT_LEFT.  Using space instead of TAB.\n"));
+
+	} else if ((present[HTML_TAB_TO] &&
+		    non_empty(value[HTML_TAB_TO])) ||
+		   (present[HTML_TAB_INDENT] &&
+		    value[HTML_TAB_INDENT] &&
+		    isdigit(UCH(*value[HTML_TAB_INDENT])))) {
+	    int column, target = -1;
+	    int enval = 2;
+
+	    column = HText_getCurrentColumn(me->text);
+	    if (present[HTML_TAB_TO] &&
+		non_empty(value[HTML_TAB_TO])) {
+		/*
+		 * TO has priority over INDENT if both are present.  - FM
+		 */
+		StrAllocCopy(temp, value[HTML_TAB_TO]);
+		TRANSLATE_AND_UNESCAPE_TO_STD(&temp);
+		if (*temp) {
+		    target = HText_getTabIDColumn(me->text, temp);
+		}
+	    } else if (isEmpty(temp) && present[HTML_TAB_INDENT] &&
+		       value[HTML_TAB_INDENT] &&
+		       isdigit(UCH(*value[HTML_TAB_INDENT]))) {
+		/*
+		 * The INDENT value is in "en" (enval per column) units.
+		 * Divide it by enval, rounding odd values up.  - FM
+		 */
+		target =
+		    (int) (((1.0 * atoi(value[HTML_TAB_INDENT])) / enval) + (0.5));
+	    }
+	    FREE(temp);
+	    /*
+	     * If we are being directed to a column too far to the left or
+	     * right, just add a collapsible space, otherwise, add the
+	     * appropriate number of spaces.  - FM
+	     */
+
+	    if (target < column ||
+		target > HText_getMaximumColumn(me->text)) {
+		HTML_put_character(me, ' ');
+		CTRACE((tfp,
+			"HTML: Column out of bounds.  Using space instead of TAB.\n"));
+	    } else {
+		for (i = column; i < target; i++)
+		    HText_appendCharacter(me->text, ' ');
+		HText_setLastChar(me->text, ' ');	/* absorb white space */
+	    }
+	}
+	me->in_word = NO;
+
+	/*
+	 * If we have an ID attribute, save it together with the value of the
+	 * column we've reached.  - FM
+	 */
+	if (present[HTML_TAB_ID] &&
+	    non_empty(value[HTML_TAB_ID])) {
+	    StrAllocCopy(temp, value[HTML_TAB_ID]);
+	    TRANSLATE_AND_UNESCAPE_TO_STD(&temp);
+	    if (*temp)
+		HText_setTabID(me->text, temp);
+	    FREE(temp);
+	}
+	break;
+
+    case HTML_BASEFONT:
+	break;
+
+    case HTML_FONT:
+
+	/*
+	 * FONT *may* have been declared SGML_EMPTY in HTMLDTD.c, and
+	 * SGML_character() in SGML.c *may* check for a FONT end tag to call
+	 * HTML_end_element() directly (with a check in that to bypass
+	 * decrementing of the HTML parser's stack).  Or this may have been
+	 * really a </FONT> end tag, for which some incarnations of SGML.c
+	 * would fake a <FONT> start tag instead.  - fm & kw
+	 *
+	 * But if we have an open FONT, DON'T close that one now, since FONT
+	 * tags can be legally nested AFAIK, and Lynx currently doesn't do
+	 * anything with them anyway...  - kw
+	 */
+#ifdef NOTUSED_FOTEMODS
+	if (me->inFONT == TRUE)
+	    HTML_end_element(me, HTML_FONT, &include);
+#endif /* NOTUSED_FOTEMODS */
+
+	/*
+	 * Set flag to know we are in a FONT container, and add code to do
+	 * something about it, someday.  - FM
+	 */
+	me->inFONT = TRUE;
+	break;
+
+    case HTML_B:		/* Physical character highlighting */
+    case HTML_BLINK:
+    case HTML_I:
+    case HTML_U:
+
+    case HTML_CITE:		/* Logical character highlighting */
+    case HTML_EM:
+    case HTML_STRONG:
+	UPDATE_STYLE;
+	me->Underline_Level++;
+	CHECK_ID(HTML_GEN_ID);
+	/*
+	 * Ignore this if inside of a bold anchor or header.  Can't display
+	 * both underline and bold at same time.
+	 */
+	if (me->inBoldA == TRUE || me->inBoldH == TRUE) {
+	    CTRACE((tfp, "Underline Level is %d\n", me->Underline_Level));
+	    break;
+	}
+	if (me->inUnderline == FALSE) {
+	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
+	    me->inUnderline = TRUE;
+	    CTRACE((tfp, "Beginning underline\n"));
+	} else {
+	    CTRACE((tfp, "Underline Level is %d\n", me->Underline_Level));
+	}
+	break;
+
+    case HTML_ABBR:		/* Miscellaneous character containers */
+    case HTML_ACRONYM:
+    case HTML_AU:
+    case HTML_AUTHOR:
+    case HTML_BIG:
+    case HTML_CODE:
+    case HTML_DFN:
+    case HTML_KBD:
+    case HTML_SAMP:
+    case HTML_SMALL:
+    case HTML_TT:
+    case HTML_VAR:
+	CHECK_ID(HTML_GEN_ID);
+	break;			/* ignore */
+
+    case HTML_SUP:
+	HText_appendCharacter(me->text, '^');
+	CHECK_ID(HTML_GEN_ID);
+	break;
+
+    case HTML_SUB:
+	HText_appendCharacter(me->text, '[');
+	CHECK_ID(HTML_GEN_ID);
+	break;
+
+    case HTML_DEL:
+    case HTML_S:
+    case HTML_STRIKE:
+	CHECK_ID(HTML_GEN_ID);
+	if (me->inUnderline == FALSE)
+	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
+	HTML_put_string(me, "[DEL:");
+	if (me->inUnderline == FALSE)
+	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
+	HTML_put_character(me, ' ');
+	me->in_word = NO;
+	break;
+
+    case HTML_INS:
+	CHECK_ID(HTML_GEN_ID);
+	if (me->inUnderline == FALSE)
+	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
+	HTML_put_string(me, "[INS:");
+	if (me->inUnderline == FALSE)
+	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
+	HTML_put_character(me, ' ');
+	me->in_word = NO;
+	break;
+
+    case HTML_Q:
+	CHECK_ID(HTML_GEN_ID);
+	/*
+	 * Should check LANG and/or DIR attributes, and the
+	 * me->node_anchor->charset and/or yet to be added structure elements,
+	 * to determine whether we should use chevrons, but for now we'll
+	 * always use double- or single-quotes.  - FM
+	 */
+	if (!(me->Quote_Level & 1))
+	    HTML_put_character(me, '"');
+	else
+	    HTML_put_character(me, '`');
+	me->Quote_Level++;
+	break;
+
+    case HTML_PRE:		/* Formatted text */
+	/*
+	 * Set our inPRE flag to FALSE so that a newline immediately following
+	 * the PRE start tag will be ignored.  HTML_put_character() will set it
+	 * to TRUE when the first character within the PRE block is received. 
+	 * - FM
+	 */
+	me->inPRE = FALSE;
+	/* FALLTHRU */
+    case HTML_LISTING:		/* Literal text */
+	/* FALLTHRU */
+    case HTML_XMP:
+	/* FALLTHRU */
+    case HTML_PLAINTEXT:
+	change_paragraph_style(me, styles[ElementNumber]);
+	UPDATE_STYLE;
+	CHECK_ID(HTML_GEN_ID);
+	if (me->comment_end)
+	    HText_appendText(me->text, me->comment_end);
+	break;
+
+    case HTML_BLOCKQUOTE:
+    case HTML_BQ:
+	change_paragraph_style(me, styles[ElementNumber]);
+	UPDATE_STYLE;
+	if (me->sp->tag_number == (int) ElementNumber)
+	    LYEnsureDoubleSpace(me);
+	CHECK_ID(HTML_BQ_ID);
+	break;
+
+    case HTML_NOTE:
+	change_paragraph_style(me, styles[ElementNumber]);
+	UPDATE_STYLE;
+	if (me->sp->tag_number == (int) ElementNumber)
+	    LYEnsureDoubleSpace(me);
+	CHECK_ID(HTML_NOTE_ID);
+	{
+	    char *note = NULL;
+
+	    /*
+	     * Indicate the type of NOTE.
+	     */
+	    if (present && present[HTML_NOTE_CLASS] &&
+		value[HTML_NOTE_CLASS] &&
+		(!strcasecomp(value[HTML_NOTE_CLASS], "CAUTION") ||
+		 !strcasecomp(value[HTML_NOTE_CLASS], "WARNING"))) {
+		StrAllocCopy(note, value[HTML_NOTE_CLASS]);
+		LYUpperCase(note);
+		StrAllocCat(note, ":");
+	    } else if (present && present[HTML_NOTE_ROLE] &&
+		       value[HTML_NOTE_ROLE] &&
+		       (!strcasecomp(value[HTML_NOTE_ROLE], "CAUTION") ||
+			!strcasecomp(value[HTML_NOTE_ROLE], "WARNING"))) {
+		StrAllocCopy(note, value[HTML_NOTE_ROLE]);
+		LYUpperCase(note);
+		StrAllocCat(note, ":");
+	    } else {
+		StrAllocCopy(note, "NOTE:");
+	    }
+	    if (me->inUnderline == FALSE)
+		HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
+	    HTML_put_string(me, note);
+	    if (me->inUnderline == FALSE)
+		HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
+	    HTML_put_character(me, ' ');
+	    CAN_JUSTIFY_START;
+	    FREE(note);
+	}
+	CAN_JUSTIFY_START;
+	me->inLABEL = TRUE;
+	me->in_word = NO;
+	me->inP = FALSE;
+	break;
+
+    case HTML_ADDRESS:
+	change_paragraph_style(me, styles[ElementNumber]);
+	UPDATE_STYLE;
+	if (me->sp->tag_number == (int) ElementNumber)
+	    LYEnsureDoubleSpace(me);
+	CHECK_ID(HTML_ADDRESS_ID);
+	break;
+
+    case HTML_DL:
+	me->List_Nesting_Level++;	/* increment the List nesting level */
+	if (me->List_Nesting_Level <= 0) {
+	    change_paragraph_style(me, present && present[HTML_DL_COMPACT]
+				   ? styles[HTML_DLC] : styles[HTML_DL]);
+
+	} else if (me->List_Nesting_Level >= 6) {
+	    change_paragraph_style(me, present && present[HTML_DL_COMPACT]
+				   ? styles[HTML_DLC6] : styles[HTML_DL6]);
+
+	} else {
+	    change_paragraph_style(me, present && present[HTML_DL_COMPACT]
+				   ? styles[(HTML_DLC1 - 1) + me->List_Nesting_Level]
+				   : styles[(HTML_DL1 - 1) + me->List_Nesting_Level]);
+	}
+	UPDATE_STYLE;		/* update to the new style */
+	CHECK_ID(HTML_DL_ID);
+
+	break;
+
+    case HTML_DLC:
+	me->List_Nesting_Level++;	/* increment the List nesting level */
+	if (me->List_Nesting_Level <= 0) {
+	    change_paragraph_style(me, styles[HTML_DLC]);
+
+	} else if (me->List_Nesting_Level >= 6) {
+	    change_paragraph_style(me, styles[HTML_DLC6]);
+
+	} else {
+	    change_paragraph_style(me,
+				   styles[(HTML_DLC1 - 1) + me->List_Nesting_Level]);
+	}
+	UPDATE_STYLE;		/* update to the new style */
+	CHECK_ID(HTML_DL_ID);
+	break;
+
+    case HTML_DT:
+	CHECK_ID(HTML_GEN_ID);
+	if (!me->style_change) {
+	    BOOL in_line_1 = HText_inLineOne(me->text);
+	    HTCoord saved_spaceBefore = me->sp->style->spaceBefore;
+	    HTCoord saved_spaceAfter = me->sp->style->spaceAfter;
+
+	    /*
+	     * If there are several DT elements and this is not the first, and
+	     * the preceding DT element's first (and normally only) line has
+	     * not yet been ended, suppress intervening blank line by
+	     * temporarily modifying the paragraph style in place.  Ugly but
+	     * there's ample precedence.  - kw
+	     */
+	    if (in_line_1) {
+		me->sp->style->spaceBefore = 0;		/* temporary change */
+		me->sp->style->spaceAfter = 0;	/* temporary change */
+	    }
+	    HText_appendParagraph(me->text);
+	    me->sp->style->spaceBefore = saved_spaceBefore;	/* undo */
+	    me->sp->style->spaceAfter = saved_spaceAfter;	/* undo */
+	    me->in_word = NO;
+	    me->sp->style->alignment = HT_LEFT;
+	}
+	me->inP = FALSE;
+	break;
+
+    case HTML_DD:
+	CHECK_ID(HTML_GEN_ID);
+	HText_setLastChar(me->text, ' ');	/* absorb white space */
+	if (!me->style_change) {
+	    if (!HText_LastLineEmpty(me->text, FALSE)) {
+		HText_appendCharacter(me->text, '\r');
+	    } else {
+		HText_NegateLineOne(me->text);
+	    }
+	} else {
+	    UPDATE_STYLE;
+	    HText_appendCharacter(me->text, '\t');
+	}
+	me->sp->style->alignment = HT_LEFT;
+	me->in_word = NO;
+	me->inP = FALSE;
+	break;
+
+    case HTML_OL:
+	/*
+	 * Set the default TYPE.
+	 */
+	me->OL_Type[(me->List_Nesting_Level < 11 ?
+		     me->List_Nesting_Level + 1 : 11)] = '1';
+
+	/*
+	 * Check whether we have a starting sequence number, or want to
+	 * continue the numbering from a previous OL in this nest.  - FM
+	 */
+	if (present && (present[HTML_OL_SEQNUM] || present[HTML_OL_START])) {
+	    int seqnum;
+
+	    /*
+	     * Give preference to the valid HTML 3.0 SEQNUM attribute name over
+	     * the Netscape START attribute name (too bad the Netscape
+	     * developers didn't read the HTML 3.0 specs before re-inventing
+	     * the "wheel" as "we'll").  - FM
+	     */
+	    if (present[HTML_OL_SEQNUM] &&
+		non_empty(value[HTML_OL_SEQNUM])) {
+		seqnum = atoi(value[HTML_OL_SEQNUM]);
+	    } else if (present[HTML_OL_START] &&
+		       non_empty(value[HTML_OL_START])) {
+		seqnum = atoi(value[HTML_OL_START]);
+	    } else {
+		seqnum = 1;
+	    }
+
+	    /*
+	     * Don't allow negative numbers less than or equal to our flags, or
+	     * numbers less than 1 if an Alphabetic or Roman TYPE.  - FM
+	     */
+	    if (present[HTML_OL_TYPE] && value[HTML_OL_TYPE]) {
+		if (*value[HTML_OL_TYPE] == 'A') {
+		    me->OL_Type[(me->List_Nesting_Level < 11 ?
+				 me->List_Nesting_Level + 1 : 11)] = 'A';
+		    if (seqnum < 1)
+			seqnum = 1;
+		} else if (*value[HTML_OL_TYPE] == 'a') {
+		    me->OL_Type[(me->List_Nesting_Level < 11 ?
+				 me->List_Nesting_Level + 1 : 11)] = 'a';
+		    if (seqnum < 1)
+			seqnum = 1;
+		} else if (*value[HTML_OL_TYPE] == 'I') {
+		    me->OL_Type[(me->List_Nesting_Level < 11 ?
+				 me->List_Nesting_Level + 1 : 11)] = 'I';
+		    if (seqnum < 1)
+			seqnum = 1;
+		} else if (*value[HTML_OL_TYPE] == 'i') {
+		    me->OL_Type[(me->List_Nesting_Level < 11 ?
+				 me->List_Nesting_Level + 1 : 11)] = 'i';
+		    if (seqnum < 1)
+			seqnum = 1;
+		} else {
+		    if (seqnum <= OL_VOID)
+			seqnum = OL_VOID + 1;
+		}
+	    } else if (seqnum <= OL_VOID) {
+		seqnum = OL_VOID + 1;
+	    }
+
+	    me->OL_Counter[(me->List_Nesting_Level < 11 ?
+			    me->List_Nesting_Level + 1 : 11)] = seqnum;
+
+	} else if (present && present[HTML_OL_CONTINUE]) {
+	    me->OL_Counter[me->List_Nesting_Level < 11 ?
+			   me->List_Nesting_Level + 1 : 11] = OL_CONTINUE;
+
+	} else {
+	    me->OL_Counter[(me->List_Nesting_Level < 11 ?
+			    me->List_Nesting_Level + 1 : 11)] = 1;
+	    if (present && present[HTML_OL_TYPE] && value[HTML_OL_TYPE]) {
+		if (*value[HTML_OL_TYPE] == 'A') {
+		    me->OL_Type[(me->List_Nesting_Level < 11 ?
+				 me->List_Nesting_Level + 1 : 11)] = 'A';
+		} else if (*value[HTML_OL_TYPE] == 'a') {
+		    me->OL_Type[(me->List_Nesting_Level < 11 ?
+				 me->List_Nesting_Level + 1 : 11)] = 'a';
+		} else if (*value[HTML_OL_TYPE] == 'I') {
+		    me->OL_Type[(me->List_Nesting_Level < 11 ?
+				 me->List_Nesting_Level + 1 : 11)] = 'I';
+		} else if (*value[HTML_OL_TYPE] == 'i') {
+		    me->OL_Type[(me->List_Nesting_Level < 11 ?
+				 me->List_Nesting_Level + 1 : 11)] = 'i';
+		}
+	    }
+	}
+	me->List_Nesting_Level++;
+
+	if (me->List_Nesting_Level <= 0) {
+	    change_paragraph_style(me, styles[ElementNumber]);
+
+	} else if (me->List_Nesting_Level >= 6) {
+	    change_paragraph_style(me, styles[HTML_OL6]);
+
+	} else {
+	    change_paragraph_style(me,
+				   styles[HTML_OL1 + me->List_Nesting_Level - 1]);
+	}
+	UPDATE_STYLE;		/* update to the new style */
+	CHECK_ID(HTML_OL_ID);
+	break;
+
+    case HTML_UL:
+	me->List_Nesting_Level++;
+
+	if (me->List_Nesting_Level <= 0) {
+	    if (!(present && present[HTML_UL_PLAIN]) &&
+		!(present && present[HTML_UL_TYPE] &&
+		  value[HTML_UL_TYPE] &&
+		  0 == strcasecomp(value[HTML_UL_TYPE], "PLAIN"))) {
+		change_paragraph_style(me, styles[ElementNumber]);
+	    } else {
+		change_paragraph_style(me, styles[HTML_DIR]);
+		ElementNumber = HTML_DIR;
+	    }
+
+	} else if (me->List_Nesting_Level >= 6) {
+	    if (!(present && present[HTML_UL_PLAIN]) &&
+		!(present && present[HTML_UL_TYPE] &&
+		  value[HTML_UL_TYPE] &&
+		  0 == strcasecomp(value[HTML_UL_TYPE], "PLAIN"))) {
+		change_paragraph_style(me, styles[HTML_OL6]);
+	    } else {
+		change_paragraph_style(me, styles[HTML_MENU6]);
+		ElementNumber = HTML_DIR;
+	    }
+
+	} else {
+	    if (!(present && present[HTML_UL_PLAIN]) &&
+		!(present && present[HTML_UL_TYPE] &&
+		  value[HTML_UL_TYPE] &&
+		  0 == strcasecomp(value[HTML_UL_TYPE], "PLAIN"))) {
+		change_paragraph_style(me,
+				       styles[HTML_OL1 + me->List_Nesting_Level
+					      - 1]);
+	    } else {
+		change_paragraph_style(me,
+				       styles[HTML_MENU1 + me->List_Nesting_Level
+					      - 1]);
+		ElementNumber = HTML_DIR;
+	    }
+	}
+	UPDATE_STYLE;		/* update to the new style */
+	CHECK_ID(HTML_UL_ID);
+	break;
+
+    case HTML_MENU:
+    case HTML_DIR:
+	me->List_Nesting_Level++;
+
+	if (me->List_Nesting_Level <= 0) {
+	    change_paragraph_style(me, styles[ElementNumber]);
+
+	} else if (me->List_Nesting_Level >= 6) {
+	    change_paragraph_style(me, styles[HTML_MENU6]);
+
+	} else {
+	    change_paragraph_style(me,
+				   styles[HTML_MENU1 + me->List_Nesting_Level
+					  - 1]);
+	}
+	UPDATE_STYLE;		/* update to the new style */
+	CHECK_ID(HTML_UL_ID);
+	break;
+
+    case HTML_LH:
+	UPDATE_STYLE;		/* update to the new style */
+	HText_appendParagraph(me->text);
+	CHECK_ID(HTML_GEN_ID);
+	HTML_put_character(me, HT_NON_BREAK_SPACE);
+	HText_setLastChar(me->text, ' ');
+	me->in_word = NO;
+	me->inP = FALSE;
+	break;
+
+    case HTML_LI:
+	UPDATE_STYLE;		/* update to the new style */
+	HText_appendParagraph(me->text);
+	me->sp->style->alignment = HT_LEFT;
+	CHECK_ID(HTML_LI_ID);
+	{
+	    int surrounding_tag_number = me->sp[0].tag_number;
+
+	    /*
+	     * No, a LI should never occur directly within another LI, but this
+	     * may result from incomplete error recovery.  So check one more
+	     * surrounding level in this case.  - kw
+	     */
+	    if (surrounding_tag_number == HTML_LI &&
+		me->sp < (me->stack + MAX_NESTING - 1))
+		surrounding_tag_number = me->sp[1].tag_number;
+	    if (surrounding_tag_number == HTML_OL) {
+		char number_string[20];
+		int counter, seqnum;
+		char seqtype;
+
+		counter = me->List_Nesting_Level < 11 ?
+		    me->List_Nesting_Level : 11;
+		if (present && present[HTML_LI_TYPE] && value[HTML_LI_TYPE]) {
+		    if (*value[HTML_LI_TYPE] == '1') {
+			me->OL_Type[counter] = '1';
+		    } else if (*value[HTML_LI_TYPE] == 'A') {
+			me->OL_Type[counter] = 'A';
+		    } else if (*value[HTML_LI_TYPE] == 'a') {
+			me->OL_Type[counter] = 'a';
+		    } else if (*value[HTML_LI_TYPE] == 'I') {
+			me->OL_Type[counter] = 'I';
+		    } else if (*value[HTML_LI_TYPE] == 'i') {
+			me->OL_Type[counter] = 'i';
+		    }
+		}
+		if (present && present[HTML_LI_VALUE] &&
+		    ((value[HTML_LI_VALUE] != NULL) &&
+		     (*value[HTML_LI_VALUE] != '\0')) &&
+		    ((isdigit(UCH(*value[HTML_LI_VALUE]))) ||
+		     (*value[HTML_LI_VALUE] == '-' &&
+		      isdigit(UCH(*(value[HTML_LI_VALUE] + 1)))))) {
+		    seqnum = atoi(value[HTML_LI_VALUE]);
+		    if (seqnum <= OL_VOID)
+			seqnum = OL_VOID + 1;
+		    seqtype = me->OL_Type[counter];
+		    if (seqtype != '1' && seqnum < 1)
+			seqnum = 1;
+		    me->OL_Counter[counter] = seqnum + 1;
+		} else if (me->OL_Counter[counter] >= OL_VOID) {
+		    seqnum = me->OL_Counter[counter]++;
+		    seqtype = me->OL_Type[counter];
+		    if (seqtype != '1' && seqnum < 1) {
+			seqnum = 1;
+			me->OL_Counter[counter] = seqnum + 1;
+		    }
+		} else {
+		    seqnum = me->Last_OL_Count + 1;
+		    seqtype = me->Last_OL_Type;
+		    for (i = (counter - 1); i >= 0; i--) {
+			if (me->OL_Counter[i] > OL_VOID) {
+			    seqnum = me->OL_Counter[i]++;
+			    seqtype = me->OL_Type[i];
+			    i = 0;
+			}
+		    }
+		}
+		if (seqtype == 'A') {
+		    strcpy(number_string, LYUppercaseA_OL_String(seqnum));
+		} else if (seqtype == 'a') {
+		    strcpy(number_string, LYLowercaseA_OL_String(seqnum));
+		} else if (seqtype == 'I') {
+		    strcpy(number_string, LYUppercaseI_OL_String(seqnum));
+		} else if (seqtype == 'i') {
+		    strcpy(number_string, LYLowercaseI_OL_String(seqnum));
+		} else {
+		    sprintf(number_string, "%2d.", seqnum);
+		}
+		me->Last_OL_Count = seqnum;
+		me->Last_OL_Type = seqtype;
+		/*
+		 * Hack, because there is no append string!
+		 */
+		for (i = 0; number_string[i] != '\0'; i++)
+		    if (number_string[i] == ' ')
+			HTML_put_character(me, HT_NON_BREAK_SPACE);
+		    else
+			HTML_put_character(me, number_string[i]);
+
+		/*
+		 * Use HTML_put_character so that any other spaces coming
+		 * through will be collapsed.  We'll use nbsp, so it won't
+		 * break at the spacing character if there are no spaces in the
+		 * subsequent text up to the right margin, but will declare it
+		 * as a normal space to ensure collapsing if a normal space
+		 * does immediately follow it.  - FM
+		 */
+		HTML_put_character(me, HT_NON_BREAK_SPACE);
+		HText_setLastChar(me->text, ' ');
+	    } else if (surrounding_tag_number == HTML_UL) {
+		/*
+		 * Hack, because there is no append string!
+		 */
+		HTML_put_character(me, HT_NON_BREAK_SPACE);
+		HTML_put_character(me, HT_NON_BREAK_SPACE);
+		switch (me->List_Nesting_Level % 7) {
+		case 0:
+		    HTML_put_character(me, '*');
+		    break;
+		case 1:
+		    HTML_put_character(me, '+');
+		    break;
+		case 2:
+		    HTML_put_character(me, 'o');
+		    break;
+		case 3:
+		    HTML_put_character(me, '#');
+		    break;
+		case 4:
+		    HTML_put_character(me, '@');
+		    break;
+		case 5:
+		    HTML_put_character(me, '-');
+		    break;
+		case 6:
+		    HTML_put_character(me, '=');
+		    break;
+
+		}
+		/*
+		 * Keep using HTML_put_character so that any other spaces
+		 * coming through will be collapsed.  We use nbsp, so we won't
+		 * wrap at the spacing character if there are no spaces in the
+		 * subsequent text up to the right margin, but will declare it
+		 * as a normal space to ensure collapsing if a normal space
+		 * does immediately follow it.  - FM
+		 */
+		HTML_put_character(me, HT_NON_BREAK_SPACE);
+		HText_setLastChar(me->text, ' ');
+	    } else {
+		/*
+		 * Hack, because there is no append string!
+		 */
+		HTML_put_character(me, HT_NON_BREAK_SPACE);
+		HTML_put_character(me, HT_NON_BREAK_SPACE);
+		HText_setLastChar(me->text, ' ');
+	    }
+	}
+	CAN_JUSTIFY_START;
+	me->in_word = NO;
+	me->inP = FALSE;
+	break;
+
+    case HTML_SPAN:
+	CHECK_ID(HTML_GEN_ID);
+	/*
+	 * Should check LANG and/or DIR attributes, and the
+	 * me->node_anchor->charset and/or yet to be added structure elements,
+	 * and do something here.  - FM
+	 */
+	break;
+
+    case HTML_BDO:
+	CHECK_ID(HTML_GEN_ID);
+	/*
+	 * Should check DIR (and LANG) attributes, and the
+	 * me->node_anchor->charset and/or yet to be added structure elements,
+	 * and do something here.  - FM
+	 */
+	break;
+
+    case HTML_SPOT:
+	CHECK_ID(HTML_GEN_ID);
+	break;
+
+    case HTML_FN:
+	change_paragraph_style(me, styles[ElementNumber]);
+	UPDATE_STYLE;
+	if (me->sp->tag_number == (int) ElementNumber)
+	    LYEnsureDoubleSpace(me);
+	CHECK_ID(HTML_GEN_ID);
+	if (me->inUnderline == FALSE)
+	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
+	HTML_put_string(me, "FOOTNOTE:");
+	if (me->inUnderline == FALSE)
+	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
+	HTML_put_character(me, ' ');
+	CAN_JUSTIFY_START
+	    me->inLABEL = TRUE;
+	me->in_word = NO;
+	me->inP = FALSE;
+	break;
+
+    case HTML_A:
+	/*
+	 * If we are looking for client-side image maps, then handle an A
+	 * within a MAP that has a COORDS attribute as an AREA tag. 
+	 * Unfortunately we lose the anchor text this way for the LYNXIMGMAP,
+	 * we would have to do much more parsing to collect it.  After
+	 * potentially handling the A as AREA, always return immediately if
+	 * only looking for image maps, without pushing anything on the style
+	 * stack.  - kw
+	 */
+	if (me->map_address && present && present[HTML_A_COORDS])
+	    LYStartArea(me,
+			present[HTML_A_HREF] ? value[HTML_A_HREF] : NULL,
+			NULL,
+			present[HTML_A_TITLE] ? value[HTML_A_TITLE] : NULL,
+			tag_charset);
+	if (LYMapsOnly) {
+	    return HT_OK;
+	}
+	/*
+	 * A may have been declared SGML_EMPTY in HTMLDTD.c, and
+	 * SGML_character() in SGML.c may check for an A end tag to call
+	 * HTML_end_element() directly (with a check in that to bypass
+	 * decrementing of the HTML parser's stack), so if we have an open A,
+	 * close that one now.  - FM & kw
+	 */
+	if (me->inA) {
+	    SET_SKIP_STACK(HTML_A);
+	    HTML_end_element(me, HTML_A, include);
+	}
+	/*
+	 * Set to know we are in an anchor.
+	 */
+	me->inA = TRUE;
+
+	/*
+	 * Load id_string if we have an ID or NAME.  - FM
+	 */
+	if (present && present[HTML_A_ID] &&
+	    non_empty(value[HTML_A_ID])) {
+	    StrAllocCopy(id_string, value[HTML_A_ID]);
+	} else if (present && present[HTML_A_NAME] &&
+		   non_empty(value[HTML_A_NAME])) {
+	    StrAllocCopy(id_string, value[HTML_A_NAME]);
+	}
+	if (id_string)
+	    TRANSLATE_AND_UNESCAPE_TO_STD(&id_string);
+
+	/*
+	 * Handle the reference.  - FM
+	 */
+	if (present && present[HTML_A_HREF]) {
+	    /*
+	     * Set to know we are making the content bold.
+	     */
+	    me->inBoldA = TRUE;
+
+	    if (isEmpty(value[HTML_A_HREF]))
+		StrAllocCopy(href, "#");
+	    else
+		StrAllocCopy(href, value[HTML_A_HREF]);
+	    CHECK_FOR_INTERN(intern_flag, href);	/* '#' */
+
+	    if (intern_flag) { /*** FAST WAY: ***/
+		TRANSLATE_AND_UNESCAPE_TO_STD(&href);
+
+	    } else {
+		url_type = LYLegitimizeHREF(me, &href, TRUE, TRUE);
+
+		/*
+		 * Deal with our ftp gateway kludge.  - FM
+		 */
+		if (!url_type && !strncmp(href, "/foo/..", 7) &&
+		    (isFTP_URL(me->node_anchor->address) ||
+		     isFILE_URL(me->node_anchor->address))) {
+		    for (i = 0; (href[i] = href[i + 7]) != 0; i++) ;
+		}
+	    }
+
+	    if (present[HTML_A_ISMAP])	/*??? */
+		intern_flag = FALSE;
+	} else {
+	    if (bold_name_anchors == TRUE) {
+		me->inBoldA = TRUE;
+	    }
+	}
+
+	if (present && present[HTML_A_TYPE] && value[HTML_A_TYPE]) {
+	    StrAllocCopy(temp, value[HTML_A_TYPE]);
+	    if (!intern_flag &&
+		!strcasecomp(value[HTML_A_TYPE], HTAtom_name(HTInternalLink)) &&
+		!LYIsUIPage3(me->node_anchor->address, UIP_LIST_PAGE, 0) &&
+		!LYIsUIPage3(me->node_anchor->address, UIP_ADDRLIST_PAGE, 0) &&
+		!isLYNXIMGMAP(me->node_anchor->address)) {
+		/* Some kind of spoof?
+		 * Found TYPE="internal link" but not in a valid context
+		 * where we have written it. - kw
+		 */
+		CTRACE((tfp, "HTML: Found invalid HREF=\"%s\" TYPE=\"%s\"!\n",
+			href, temp));
+		FREE(temp);
+	    }
+	}
+
+	me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+						 id_string,	/* Tag */
+						 href,	/* Address */
+						 (temp
+						  ? (HTLinkType *)
+						  HTAtom_for(temp)
+						  : INTERN_LT));	/* Type */
+	FREE(temp);
+	FREE(id_string);
+
+	if (me->CurrentA && present) {
+	    if (present[HTML_A_TITLE] &&
+		non_empty(value[HTML_A_TITLE])) {
+		StrAllocCopy(title, value[HTML_A_TITLE]);
+		TRANSLATE_AND_UNESCAPE_ENTITIES(&title, TRUE, FALSE);
+		LYTrimHead(title);
+		LYTrimTail(title);
+		if (*title == '\0') {
+		    FREE(title);
+		}
+	    }
+	    if (present[HTML_A_ISMAP])
+		dest_ismap = TRUE;
+	    if (present[HTML_A_CHARSET] &&
+		non_empty(value[HTML_A_CHARSET])) {
+		/*
+		 * Set up to load the anchor's chartrans structures
+		 * appropriately for the current display character set if it
+		 * can handle what's claimed.  - FM
+		 */
+		StrAllocCopy(temp, value[HTML_A_CHARSET]);
+		TRANSLATE_AND_UNESCAPE_TO_STD(&temp);
+		dest_char_set = UCGetLYhndl_byMIME(temp);
+		if (dest_char_set < 0) {
+		    dest_char_set = UCLYhndl_for_unrec;
+		}
+	    }
+	    if (title != NULL || dest_ismap == TRUE || dest_char_set >= 0) {
+		dest = HTAnchor_parent(HTAnchor_followLink(me->CurrentA)
+		    );
+	    }
+	    if (dest && title != NULL && HTAnchor_title(dest) == NULL)
+		HTAnchor_setTitle(dest, title);
+	    if (dest && dest_ismap)
+		dest->isISMAPScript = TRUE;
+	    /* Don't allow CHARSET attribute to change *this* document's
+	       charset assumption. - kw */
+	    if (dest && dest != me->node_anchor && dest_char_set >= 0) {
+		/*
+		 * Load the anchor's chartrans structures.  This should be done
+		 * more intelligently when setting up the structured object,
+		 * but it gets the job done for now.  - FM
+		 */
+		HTAnchor_setUCInfoStage(dest, dest_char_set,
+					UCT_STAGE_MIME,
+					UCT_SETBY_DEFAULT);
+		HTAnchor_setUCInfoStage(dest, dest_char_set,
+					UCT_STAGE_PARSER,
+					UCT_SETBY_LINK);
+	    }
+	    FREE(temp);
+	    dest = NULL;
+	    FREE(title);
+	}
+	me->CurrentANum = HText_beginAnchor(me->text,
+					    me->inUnderline, me->CurrentA);
+	if (me->inBoldA == TRUE && me->inBoldH == FALSE)
+	    HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
+#if defined(NOTUSED_FOTEMODS)
+	/*
+	 * Close an HREF-less NAMED-ed now if we aren't making their content
+	 * bold, and let the check in HTML_end_element() deal with any dangling
+	 * end tag this creates.  - FM
+	 */
+	if (href == NULL && me->inBoldA == FALSE) {
+	    SET_SKIP_STACK(HTML_A);
+	    HTML_end_element(me, HTML_A, include);
+	}
+#else
+	/*Close an HREF-less NAMED-ed now if force_empty_hrefless_a was
+	   requested - VH */
+	if (href == NULL && force_empty_hrefless_a) {
+	    SET_SKIP_STACK(HTML_A);
+	    HTML_end_element(me, HTML_A, include);
+	}
+#endif
+	FREE(href);
+	break;
+
+    case HTML_IMG:		/* Images */
+	/*
+	 * If we're in an anchor, get the destination, and if it's a clickable
+	 * image for the current anchor, set our flags for faking a 0,0
+	 * coordinate pair, which typically returns the image's default.  - FM
+	 */
+	if (me->inA && me->CurrentA) {
+	    if ((dest = HTAnchor_parent(HTAnchor_followLink(me->CurrentA)
+		 )) != NULL) {
+		if (dest->isISMAPScript == TRUE) {
+		    dest_ismap = TRUE;
+		    CTRACE((tfp, "HTML: '%s' is an ISMAP script\n",
+			    dest->address));
+		} else if (present && present[HTML_IMG_ISMAP]) {
+		    dest_ismap = TRUE;
+		    dest->isISMAPScript = TRUE;
+		    CTRACE((tfp, "HTML: Designating '%s' as an ISMAP script\n",
+			    dest->address));
+		}
+	    }
+	}
+
+	intern_flag = FALSE;	/* unless set below - kw */
+	/*
+	 * If there's a USEMAP, resolve it.  - FM
+	 */
+	if (present && present[HTML_IMG_USEMAP] &&
+	    non_empty(value[HTML_IMG_USEMAP])) {
+	    StrAllocCopy(map_href, value[HTML_IMG_USEMAP]);
+	    CHECK_FOR_INTERN(intern_flag, map_href);
+	    (void) LYLegitimizeHREF(me, &map_href, TRUE, TRUE);
+	    /*
+	     * If map_href ended up zero-length or otherwise doesn't have a
+	     * hash, it can't be valid, so ignore it.  - FM
+	     */
+	    if (findPoundSelector(map_href) == NULL) {
+		FREE(map_href);
+	    }
+	}
+
+	/*
+	 * Handle a MAP reference if we have one at this point.  - FM
+	 */
+	if (map_href) {
+	    /*
+	     * If the MAP reference doesn't yet begin with a scheme, check
+	     * whether a base tag is in effect.  - FM
+	     */
+	    /*
+	     * If the USEMAP value is a lone fragment and LYSeekFragMAPinCur is
+	     * set, we'll use the current document's URL for resolving. 
+	     * Otherwise use the BASE.  - kw
+	     */
+	    Base = ((me->inBASE &&
+		     !(*map_href == '#' && LYSeekFragMAPinCur == TRUE))
+		    ? me->base_href
+		    : me->node_anchor->address);
+	    HTParseALL(&map_href, Base);
+
+	    /*
+	     * Prepend our client-side MAP access field.  - FM
+	     */
+	    StrAllocCopy(temp, STR_LYNXIMGMAP);
+	    StrAllocCat(temp, map_href);
+	    StrAllocCopy(map_href, temp);
+	    FREE(temp);
+	}
+
+	/*
+	 * Check whether we want to suppress the server-side ISMAP link if a
+	 * client-side MAP is present.  - FM
+	 */
+	if (LYNoISMAPifUSEMAP && map_href && dest_ismap) {
+	    dest_ismap = FALSE;
+	    dest = NULL;
+	}
+
+	/*
+	 * Check for a TITLE attribute.  - FM
+	 */
+	if (present && present[HTML_IMG_TITLE] &&
+	    non_empty(value[HTML_IMG_TITLE])) {
+	    StrAllocCopy(title, value[HTML_IMG_TITLE]);
+	    TRANSLATE_AND_UNESCAPE_ENTITIES(&title, TRUE, FALSE);
+	    LYTrimHead(title);
+	    LYTrimTail(title);
+	    if (*title == '\0') {
+		FREE(title);
+	    }
+	}
+
+	/*
+	 * If there's an ALT string, use it, unless the ALT string is
+	 * zero-length or just spaces and we are making all SRCs links or have
+	 * a USEMAP link.  - FM
+	 */
+	if (((present) &&
+	     (present[HTML_IMG_ALT] && value[HTML_IMG_ALT])) &&
+	    (!clickable_images ||
+	     ((clickable_images || map_href) &&
+	      *value[HTML_IMG_ALT] != '\0'))) {
+	    StrAllocCopy(alt_string, value[HTML_IMG_ALT]);
+	    TRANSLATE_AND_UNESCAPE_ENTITIES(&alt_string,
+					    me->UsePlainSpace, me->HiddenValue);
+	    /*
+	     * If it's all spaces and we are making SRC or USEMAP links, treat
+	     * it as zero-length.  - FM
+	     */
+	    if (clickable_images || map_href) {
+		LYTrimHead(alt_string);
+		LYTrimTail(alt_string);
+		if (*alt_string == '\0') {
+		    if (map_href) {
+			StrAllocCopy(alt_string, (title ? title :
+						  (temp = MakeNewMapValue(value,
+									  "USEMAP"))));
+			FREE(temp);
+		    } else if (dest_ismap) {
+			StrAllocCopy(alt_string, (title ? title :
+						  (temp = MakeNewMapValue(value,
+									  "ISMAP"))));
+			FREE(temp);
+
+		    } else if (me->inA == TRUE && dest) {
+			StrAllocCopy(alt_string, (title ?
+						  title :
+						  VERBOSE_IMG(value, HTML_IMG_SRC,
+							      "[LINK]")));
+
+		    } else {
+			StrAllocCopy(alt_string,
+				     (title ? title :
+				      ((present &&
+					present[HTML_IMG_ISOBJECT]) ?
+				       "(OBJECT)" :
+				       VERBOSE_IMG(value, HTML_IMG_SRC,
+						   "[INLINE]"))));
+		    }
+		}
+	    }
+
+	} else if (map_href) {
+	    StrAllocCopy(alt_string, (title ? title :
+				      (temp = MakeNewMapValue(value, "USEMAP"))));
+	    FREE(temp);
+
+	} else if ((dest_ismap == TRUE) ||
+		   (me->inA && present && present[HTML_IMG_ISMAP])) {
+	    StrAllocCopy(alt_string, (title ? title :
+				      (temp = MakeNewMapValue(value, "ISMAP"))));
+	    FREE(temp);
+
+	} else if (me->inA == TRUE && dest) {
+	    StrAllocCopy(alt_string, (title ?
+				      title :
+				      VERBOSE_IMG(value, HTML_IMG_SRC,
+						  "[LINK]")));
+
+	} else {
+	    if (pseudo_inline_alts || clickable_images)
+		StrAllocCopy(alt_string, (title ? title :
+					  ((present &&
+					    present[HTML_IMG_ISOBJECT]) ?
+					   "(OBJECT)" :
+					   VERBOSE_IMG(value, HTML_IMG_SRC,
+						       "[INLINE]"))));
+	    else
+		StrAllocCopy(alt_string, NonNull(title));
+	}
+	if (*alt_string == '\0' && map_href) {
+	    StrAllocCopy(alt_string, (temp = MakeNewMapValue(value, "USEMAP")));
+	    FREE(temp);
+	}
+
+	CTRACE((tfp, "HTML IMG: USEMAP=%d ISMAP=%d ANCHOR=%d PARA=%d\n",
+		map_href ? 1 : 0,
+		(dest_ismap == TRUE) ? 1 : 0,
+		me->inA, me->inP));
+
+	/*
+	 * Check for an ID attribute.  - FM
+	 */
+	if (present && present[HTML_IMG_ID] &&
+	    non_empty(value[HTML_IMG_ID])) {
+	    StrAllocCopy(id_string, value[HTML_IMG_ID]);
+	    TRANSLATE_AND_UNESCAPE_TO_STD(&id_string);
+	    if (*id_string == '\0') {
+		FREE(id_string);
+	    }
+	}
+
+	/*
+	 * Create links to the SRC for all images, if desired.  - FM
+	 */
+	if (clickable_images &&
+	    present && present[HTML_IMG_SRC] &&
+	    non_empty(value[HTML_IMG_SRC])) {
+	    StrAllocCopy(href, value[HTML_IMG_SRC]);
+	    LYLegitimizeHREF(me, &href, TRUE, TRUE);
+
+	    /*
+	     * If it's an ISMAP and/or USEMAP, or graphic for an anchor, end
+	     * that anchor and start one for the SRC.  - FM
+	     */
+	    if (me->inA) {
+		/*
+		 * If we have a USEMAP, end this anchor and start a new one for
+		 * the client-side MAP.  - FM
+		 */
+		if (map_href) {
+		    if (dest_ismap) {
+			HTML_put_character(me, ' ');
+			me->in_word = NO;
+			HTML_put_string(me,
+					(temp = MakeNewMapValue(value, "ISMAP")));
+			FREE(temp);
+		    } else if (dest) {
+			HTML_put_character(me, ' ');
+			me->in_word = NO;
+			HTML_put_string(me, "[LINK]");
+		    }
+		    if (me->inBoldA == TRUE && me->inBoldH == FALSE) {
+			HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+		    }
+		    me->inBoldA = FALSE;
+		    HText_endAnchor(me->text, me->CurrentANum);
+		    me->CurrentANum = 0;
+		    if (dest_ismap || dest)
+			HTML_put_character(me, '-');
+		    if (id_string) {
+			if ((ID_A = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+							      id_string,	/* Tag */
+							      NULL,	/* Addresss */
+							      0)) != NULL) {	/* Type */
+			    HText_beginAnchor(me->text, me->inUnderline, ID_A);
+			    HText_endAnchor(me->text, 0);
+			}
+		    }
+		    me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+							     NULL,	/* Tag */
+							     map_href,	/* Addresss */
+							     INTERN_LT);	/* Type */
+		    if (me->CurrentA && title) {
+			if ((dest = HTAnchor_parent(HTAnchor_followLink(me->CurrentA)
+			     )) != NULL) {
+			    if (!HTAnchor_title(dest))
+				HTAnchor_setTitle(dest, title);
+			}
+		    }
+		    me->CurrentANum = HText_beginAnchor(me->text,
+							me->inUnderline,
+							me->CurrentA);
+		    if (me->inBoldA == FALSE && me->inBoldH == FALSE) {
+			HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
+		    }
+		    me->inBoldA = TRUE;
+		} else {
+		    HTML_put_character(me, ' ');	/* space char may be ignored */
+		    me->in_word = NO;
+		}
+		HTML_put_string(me, alt_string);
+		if (me->inBoldA == TRUE && me->inBoldH == FALSE) {
+		    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+		}
+		me->inBoldA = FALSE;
+		HText_endAnchor(me->text, me->CurrentANum);
+		me->CurrentANum = 0;
+		HTML_put_character(me, '-');
+		FREE(newtitle);
+		StrAllocCopy(alt_string,
+			     ((present &&
+			       present[HTML_IMG_ISOBJECT]) ?
+			      ((map_href || dest_ismap) ?
+			       "(IMAGE)" : "(OBJECT)") :
+			      VERBOSE_IMG(value, HTML_IMG_SRC, "[IMAGE]")));
+		if (id_string && !map_href) {
+		    if ((ID_A = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+							  id_string,	/* Tag */
+							  NULL,		/* Addresss */
+							  0)) != NULL) {	/* Type */
+			HText_beginAnchor(me->text, me->inUnderline, ID_A);
+			HText_endAnchor(me->text, 0);
+		    }
+		}
+	    } else if (map_href) {
+		HTML_put_character(me, ' ');	/* space char may be ignored */
+		me->in_word = NO;
+		if (id_string) {
+		    if ((ID_A = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+							  id_string,	/* Tag */
+							  NULL,		/* Addresss */
+							  0)) != NULL) {	/* Type */
+			HText_beginAnchor(me->text, me->inUnderline, ID_A);
+			HText_endAnchor(me->text, 0);
+		    }
+		}
+		me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+							 NULL,	/* Tag */
+							 map_href,	/* Addresss */
+							 INTERN_LT);	/* Type */
+		if (me->CurrentA && title) {
+		    if ((dest = HTAnchor_parent(HTAnchor_followLink(me->CurrentA)
+			 )) != NULL) {
+			if (!HTAnchor_title(dest))
+			    HTAnchor_setTitle(dest, title);
+		    }
+		}
+		me->CurrentANum = HText_beginAnchor(me->text,
+						    me->inUnderline,
+						    me->CurrentA);
+		if (me->inBoldA == FALSE && me->inBoldH == FALSE)
+		    HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
+		me->inBoldA = TRUE;
+		HTML_put_string(me, alt_string);
+		if (me->inBoldA == TRUE && me->inBoldH == FALSE) {
+		    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+		}
+		me->inBoldA = FALSE;
+		HText_endAnchor(me->text, me->CurrentANum);
+		me->CurrentANum = 0;
+		HTML_put_character(me, '-');
+		FREE(newtitle);
+		StrAllocCopy(alt_string,
+			     ((present &&
+			       present[HTML_IMG_ISOBJECT]) ?
+			      "(IMAGE)" :
+			      VERBOSE_IMG(value, HTML_IMG_SRC, "[IMAGE]")));
+	    } else {
+		HTML_put_character(me, ' ');	/* space char may be ignored */
+		me->in_word = NO;
+		if (id_string) {
+		    if ((ID_A = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+							  id_string,	/* Tag */
+							  NULL,		/* Addresss */
+							  0)) != NULL) {	/* Type */
+			HText_beginAnchor(me->text, me->inUnderline, ID_A);
+			HText_endAnchor(me->text, 0);
+		    }
+		}
+	    }
+
+	    /*
+	     * Create the link to the SRC.  - FM
+	     */
+	    me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+						     NULL,	/* Tag */
+						     href,	/* Addresss */
+						     (HTLinkType *) 0);		/* Type */
+	    FREE(href);
+	    me->CurrentANum = HText_beginAnchor(me->text,
+						me->inUnderline,
+						me->CurrentA);
+	    if (me->inBoldH == FALSE)
+		HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
+	    HTML_put_string(me, alt_string);
+	    if (!me->inA) {
+		if (me->inBoldH == FALSE)
+		    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+		HText_endAnchor(me->text, me->CurrentANum);
+		me->CurrentANum = 0;
+		HTML_put_character(me, ' ');	/* space char may be ignored */
+		me->in_word = NO;
+	    } else {
+		HTML_put_character(me, ' ');	/* space char may be ignored */
+		me->in_word = NO;
+		me->inBoldA = TRUE;
+	    }
+	} else if (map_href) {
+	    if (me->inA) {
+		/*
+		 * We're in an anchor and have a USEMAP, so end the anchor and
+		 * start a new one for the client-side MAP.  - FM
+		 */
+		if (dest_ismap) {
+		    HTML_put_character(me, ' ');	/* space char may be ignored */
+		    me->in_word = NO;
+		    HTML_put_string(me, (temp = MakeNewMapValue(value, "ISMAP")));
+		    FREE(temp);
+		} else if (dest) {
+		    HTML_put_character(me, ' ');	/* space char may be ignored */
+		    me->in_word = NO;
+		    HTML_put_string(me, "[LINK]");
+		}
+		if (me->inBoldA == TRUE && me->inBoldH == FALSE) {
+		    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+		}
+		me->inBoldA = FALSE;
+		HText_endAnchor(me->text, me->CurrentANum);
+		me->CurrentANum = 0;
+		if (dest_ismap || dest) {
+		    HTML_put_character(me, '-');
+		}
+	    } else {
+		HTML_put_character(me, ' ');
+		me->in_word = NO;
+	    }
+	    me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+						     NULL,	/* Tag */
+						     map_href,	/* Addresss */
+						     INTERN_LT);	/* Type */
+	    if (me->CurrentA && title) {
+		if ((dest = HTAnchor_parent(HTAnchor_followLink(me->CurrentA)
+		     )) != NULL) {
+		    if (!HTAnchor_title(dest))
+			HTAnchor_setTitle(dest, title);
+		}
+	    }
+	    me->CurrentANum = HText_beginAnchor(me->text,
+						me->inUnderline,
+						me->CurrentA);
+	    if (me->inBoldA == FALSE && me->inBoldH == FALSE) {
+		HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
+	    }
+	    me->inBoldA = TRUE;
+	    HTML_put_string(me, alt_string);
+	    if (!me->inA) {
+		if (me->inBoldA == TRUE && me->inBoldH == FALSE) {
+		    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+		}
+		me->inBoldA = FALSE;
+		HText_endAnchor(me->text, me->CurrentANum);
+		me->CurrentANum = 0;
+	    }
+	} else {
+	    /*
+	     * Just put in the ALT or pseudo-ALT string for the current anchor
+	     * or inline, with an ID link if indicated.  - FM
+	     */
+	    HTML_put_character(me, ' ');	/* space char may be ignored */
+	    me->in_word = NO;
+	    if (id_string) {
+		if ((ID_A = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+						      id_string,	/* Tag */
+						      NULL,	/* Addresss */
+						      (HTLinkType *) 0)) != NULL) {	/* Type */
+		    HText_beginAnchor(me->text, me->inUnderline, ID_A);
+		    HText_endAnchor(me->text, 0);
+		}
+	    }
+	    HTML_put_string(me, alt_string);
+	    HTML_put_character(me, ' ');	/* space char may be ignored */
+	    me->in_word = NO;
+	}
+	FREE(map_href);
+	FREE(alt_string);
+	FREE(id_string);
+	FREE(title);
+	FREE(newtitle);
+	dest = NULL;
+	break;
+
+    case HTML_MAP:
+	/*
+	 * Load id_string if we have a NAME or ID.  - FM
+	 */
+	if (present && present[HTML_MAP_NAME] &&
+	    non_empty(value[HTML_MAP_NAME])) {
+	    StrAllocCopy(id_string, value[HTML_MAP_NAME]);
+	} else if (present && present[HTML_MAP_ID] &&
+		   non_empty(value[HTML_MAP_ID])) {
+	    StrAllocCopy(id_string, value[HTML_MAP_ID]);
+	}
+	if (id_string) {
+	    TRANSLATE_AND_UNESCAPE_TO_STD(&id_string);
+	    if (*id_string == '\0') {
+		FREE(id_string);
+	    }
+	}
+
+	/*
+	 * Generate a target anchor in this place in the containing document. 
+	 * MAP can now contain block markup, if it doesn't contain any AREAs
+	 * (or A anchors with COORDS converted to AREAs) the current location
+	 * can be used as a fallback for following a USEMAP link.  - kw
+	 */
+	if (!LYMapsOnly)
+	    LYHandleID(me, id_string);
+
+	/*
+	 * Load map_address.  - FM
+	 */
+	if (id_string) {
+	    /*
+	     * The MAP must be in the current stream, even if it had a BASE
+	     * tag, so we'll use its address here, but still use the BASE, if
+	     * present, when resolving the AREA elements in it's content,
+	     * unless the AREA's HREF is a lone fragment and
+	     * LYSeekFragAREAinCur is set.  - FM && KW
+	     */
+	    StrAllocCopy(me->map_address, me->node_anchor->address);
+	    if ((cp = strchr(me->map_address, '#')) != NULL)
+		*cp = '\0';
+	    StrAllocCat(me->map_address, "#");
+	    StrAllocCat(me->map_address, id_string);
+	    FREE(id_string);
+	    if (present && present[HTML_MAP_TITLE] &&
+		non_empty(value[HTML_MAP_TITLE])) {
+		StrAllocCopy(title, value[HTML_MAP_TITLE]);
+		TRANSLATE_AND_UNESCAPE_ENTITIES(&title, TRUE, FALSE);
+		LYTrimHead(title);
+		LYTrimTail(title);
+		if (*title == '\0') {
+		    FREE(title);
+		}
+	    }
+	    LYAddImageMap(me->map_address, title, me->node_anchor);
+	    FREE(title);
+	}
+	break;
+
+    case HTML_AREA:
+	if (me->map_address &&
+	    present && present[HTML_AREA_HREF] &&
+	    non_empty(value[HTML_AREA_HREF])) {
+	    /*
+	     * Resolve the HREF.  - FM
+	     */
+	    StrAllocCopy(href, value[HTML_AREA_HREF]);
+	    CHECK_FOR_INTERN(intern_flag, href);
+	    (void) LYLegitimizeHREF(me, &href, TRUE, TRUE);
+
+	    /*
+	     * Check whether a BASE tag is in effect, and use it for resolving,
+	     * even though we used this stream's address for locating the MAP
+	     * itself, unless the HREF is a lone fragment and
+	     * LYSeekFragAREAinCur is set.  - FM
+	     */
+	    Base = (((me->inBASE && *href != '\0') &&
+		     !(*href == '#' && LYSeekFragAREAinCur == TRUE))
+		    ? me->base_href
+		    : me->node_anchor->address);
+	    HTParseALL(&href, Base);
+
+	    /*
+	     * Check for an ALT.  - FM
+	     */
+	    if (present[HTML_AREA_ALT] &&
+		non_empty(value[HTML_AREA_ALT])) {
+		StrAllocCopy(alt_string, value[HTML_AREA_ALT]);
+	    } else if (present[HTML_AREA_TITLE] &&
+		       non_empty(value[HTML_AREA_TITLE])) {
+		/*
+		 * Use the TITLE as an ALT.  - FM
+		 */
+		StrAllocCopy(alt_string, value[HTML_AREA_TITLE]);
+	    }
+	    if (alt_string != NULL) {
+		TRANSLATE_AND_UNESCAPE_ENTITIES(&alt_string,
+						me->UsePlainSpace,
+						me->HiddenValue);
+		/*
+		 * Make sure it's not just space(s).  - FM
+		 */
+		LYTrimHead(alt_string);
+		LYTrimTail(alt_string);
+		if (*alt_string == '\0') {
+		    StrAllocCopy(alt_string, href);
+		}
+	    } else {
+		/*
+		 * Use the HREF as an ALT.  - FM
+		 */
+		StrAllocCopy(alt_string, href);
+	    }
+
+	    LYAddMapElement(me->map_address, href, alt_string,
+			    me->node_anchor, intern_flag);
+	    FREE(href);
+	    FREE(alt_string);
+	}
+	break;
+
+    case HTML_PARAM:
+	/*
+	 * We may need to look at this someday to deal with MAPs, OBJECTs or
+	 * APPLETs optimally, but just ignore it for now.  - FM
+	 */
+	break;
+
+    case HTML_BODYTEXT:
+	CHECK_ID(HTML_BODYTEXT_ID);
+	/*
+	 * We may need to look at this someday to deal with OBJECTs optimally,
+	 * but just ignore it for now.  - FM
+	 */
+	break;
+
+    case HTML_TEXTFLOW:
+	CHECK_ID(HTML_BODYTEXT_ID);
+	/*
+	 * We may need to look at this someday to deal with APPLETs optimally,
+	 * but just ignore it for now.  - FM
+	 */
+	break;
+
+    case HTML_FIG:
+	if (present)
+	    LYHandleFIG(me, present, value,
+			present[HTML_FIG_ISOBJECT],
+			present[HTML_FIG_IMAGEMAP],
+			present[HTML_FIG_ID] ? value[HTML_FIG_ID] : NULL,
+			present[HTML_FIG_SRC] ? value[HTML_FIG_SRC] : NULL,
+			YES, TRUE, &intern_flag);
+	else
+	    LYHandleFIG(me, NULL, NULL,
+			0,
+			0,
+			NULL,
+			NULL, YES, TRUE, &intern_flag);
+	break;
+
+    case HTML_OBJECT:
+	if (!me->object_started) {
+	    /*
+	     * This is an outer OBJECT start tag, i.e., not a nested OBJECT, so
+	     * save its relevant attributes.  - FM
+	     */
+	    if (present) {
+		if (present[HTML_OBJECT_DECLARE])
+		    me->object_declare = TRUE;
+		if (present[HTML_OBJECT_SHAPES])
+		    me->object_shapes = TRUE;
+		if (present[HTML_OBJECT_ISMAP])
+		    me->object_ismap = TRUE;
+		if (present[HTML_OBJECT_USEMAP] &&
+		    non_empty(value[HTML_OBJECT_USEMAP])) {
+		    StrAllocCopy(me->object_usemap, value[HTML_OBJECT_USEMAP]);
+		    TRANSLATE_AND_UNESCAPE_TO_STD(&me->object_usemap);
+		    if (*me->object_usemap == '\0') {
+			FREE(me->object_usemap);
+		    }
+		}
+		if (present[HTML_OBJECT_ID] &&
+		    non_empty(value[HTML_OBJECT_ID])) {
+		    StrAllocCopy(me->object_id, value[HTML_OBJECT_ID]);
+		    TRANSLATE_AND_UNESCAPE_TO_STD(&me->object_id);
+		    if (*me->object_id == '\0') {
+			FREE(me->object_id);
+		    }
+		}
+		if (present[HTML_OBJECT_TITLE] &&
+		    non_empty(value[HTML_OBJECT_TITLE])) {
+		    StrAllocCopy(me->object_title, value[HTML_OBJECT_TITLE]);
+		    TRANSLATE_AND_UNESCAPE_ENTITIES(&me->object_title, TRUE, FALSE);
+		    LYTrimHead(me->object_title);
+		    LYTrimTail(me->object_title);
+		    if (me->object_title == '\0') {
+			FREE(me->object_title);
+		    }
+		}
+		if (present[HTML_OBJECT_DATA] &&
+		    non_empty(value[HTML_OBJECT_DATA])) {
+		    StrAllocCopy(me->object_data, value[HTML_OBJECT_DATA]);
+		    TRANSLATE_AND_UNESCAPE_TO_STD(&me->object_data);
+		    if (*me->object_data == '\0') {
+			FREE(me->object_data);
+		    }
+		}
+		if (present[HTML_OBJECT_TYPE] &&
+		    non_empty(value[HTML_OBJECT_TYPE])) {
+		    StrAllocCopy(me->object_type, value[HTML_OBJECT_TYPE]);
+		    TRANSLATE_AND_UNESCAPE_ENTITIES(&me->object_type, TRUE, FALSE);
+		    LYTrimHead(me->object_type);
+		    LYTrimTail(me->object_type);
+		    if (me->object_type == '\0') {
+			FREE(me->object_type);
+		    }
+		}
+		if (present[HTML_OBJECT_CLASSID] &&
+		    non_empty(value[HTML_OBJECT_CLASSID])) {
+		    StrAllocCopy(me->object_classid,
+				 value[HTML_OBJECT_CLASSID]);
+		    TRANSLATE_AND_UNESCAPE_ENTITIES(&me->object_classid, TRUE, FALSE);
+		    LYTrimHead(me->object_classid);
+		    LYTrimTail(me->object_classid);
+		    if (me->object_classid == '\0') {
+			FREE(me->object_classid);
+		    }
+		}
+		if (present[HTML_OBJECT_CODEBASE] &&
+		    non_empty(value[HTML_OBJECT_CODEBASE])) {
+		    StrAllocCopy(me->object_codebase,
+				 value[HTML_OBJECT_CODEBASE]);
+		    TRANSLATE_AND_UNESCAPE_TO_STD(&me->object_codebase);
+		    if (*me->object_codebase == '\0') {
+			FREE(me->object_codebase);
+		    }
+		}
+		if (present[HTML_OBJECT_CODETYPE] &&
+		    non_empty(value[HTML_OBJECT_CODETYPE])) {
+		    StrAllocCopy(me->object_codetype,
+				 value[HTML_OBJECT_CODETYPE]);
+		    TRANSLATE_AND_UNESCAPE_ENTITIES(&me->object_codetype,
+						    TRUE,
+						    FALSE);
+		    LYTrimHead(me->object_codetype);
+		    LYTrimTail(me->object_codetype);
+		    if (me->object_codetype == '\0') {
+			FREE(me->object_codetype);
+		    }
+		}
+		if (present[HTML_OBJECT_NAME] &&
+		    non_empty(value[HTML_OBJECT_NAME])) {
+		    StrAllocCopy(me->object_name, value[HTML_OBJECT_NAME]);
+		    TRANSLATE_AND_UNESCAPE_ENTITIES(&me->object_name, TRUE, FALSE);
+		    LYTrimHead(me->object_name);
+		    LYTrimTail(me->object_name);
+		    if (me->object_name == '\0') {
+			FREE(me->object_name);
+		    }
+		}
+	    }
+	    /*
+	     * If we can determine now that we are not going to do anything
+	     * special to the OBJECT element's SGML contents, like skipping it
+	     * completely or collecting it up in order to add something after
+	     * it, then generate any output that should be emitted in the place
+	     * of the OBJECT start tag NOW, then don't initialize special
+	     * handling but return, letting our SGML parser know that further
+	     * content is to be parsed normally not literally.  We could defer
+	     * this until we have collected the contents and then recycle the
+	     * contents (as was previously always done), but that has a higher
+	     * chance of completely losing content in case of nesting errors in
+	     * the input, incomplete transmissions, etc.  - kw
+	     */
+	    if ((!present ||
+		 (me->object_declare == FALSE && me->object_name == NULL &&
+		  me->object_shapes == FALSE && me->object_usemap == NULL))) {
+		if (!LYMapsOnly) {
+		    if (!clickable_images || me->object_data == NULL ||
+			!(me->object_data != NULL &&
+			  me->object_classid == NULL &&
+			  me->object_codebase == NULL &&
+			  me->object_codetype == NULL))
+			FREE(me->object_data);
+		    if (me->object_data) {
+			HTStartAnchor5(me,
+				       (me->object_id
+					? value[HTML_OBJECT_ID]
+					: NULL),
+				       value[HTML_OBJECT_DATA],
+				       value[HTML_OBJECT_TYPE],
+				       tag_charset);
+			if ((me->object_type != NULL) &&
+			    !strncasecomp(me->object_type, "image/", 6))
+			    HTML_put_string(me, "(IMAGE)");
+			else
+			    HTML_put_string(me, "(OBJECT)");
+			HTML_end_element(me, HTML_A, NULL);
+		    } else if (me->object_id)
+			LYHandleID(me, me->object_id);
+		}
+		clear_objectdata(me);
+		/*
+		 * We do NOT want the HTML_put_* functions that are going to be
+		 * called for the OBJECT's character content to add to the
+		 * chunk, so we don't push on the stack.  Instead we keep a
+		 * counter for open OBJECT tags that are treated this way, so
+		 * HTML_end_element can skip handling the corresponding end tag
+		 * that is going to arrive unexpectedly as far as our stack is
+		 * concerned.
+		 */
+		status = HT_PARSER_OTHER_CONTENT;
+		if (me->sp[0].tag_number == HTML_FIG &&
+		    me->objects_figged_open > 0) {
+		    ElementNumber = (HTMLElement) HTML_OBJECT_M;
+		} else {
+		    me->objects_mixed_open++;
+		    SET_SKIP_STACK(HTML_OBJECT);
+		}
+	    } else if (me->object_declare == FALSE && me->object_name == NULL &&
+		       me->object_shapes == TRUE) {
+		LYHandleFIG(me, present, value,
+			    1,
+			    1 || me->object_ismap,
+			    me->object_id,
+			    ((me->object_data && !me->object_classid)
+			     ? value[HTML_OBJECT_DATA]
+			     : NULL),
+			    NO, TRUE, &intern_flag);
+		clear_objectdata(me);
+		status = HT_PARSER_OTHER_CONTENT;
+		me->objects_figged_open++;
+		ElementNumber = HTML_FIG;
+
+	    } else {
+		/*
+		 * Set flag that we are accumulating OBJECT content.  - FM
+		 */
+		me->object_started = TRUE;
+	    }
+	}
+	break;
+
+    case HTML_OVERLAY:
+	if (clickable_images && me->inFIG &&
+	    present && present[HTML_OVERLAY_SRC] &&
+	    non_empty(value[HTML_OVERLAY_SRC])) {
+	    StrAllocCopy(href, value[HTML_OVERLAY_SRC]);
+	    LYLegitimizeHREF(me, &href, TRUE, TRUE);
+	    if (*href) {
+
+		if (me->inA) {
+		    SET_SKIP_STACK(HTML_A);
+		    HTML_end_element(me, HTML_A, include);
+		}
+		me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+							 NULL,	/* Tag */
+							 href,	/* Addresss */
+							 (HTLinkType *) 0);	/* Type */
+		HTML_put_character(me, ' ');
+		HText_appendCharacter(me->text, '+');
+		me->CurrentANum = HText_beginAnchor(me->text,
+						    me->inUnderline,
+						    me->CurrentA);
+		if (me->inBoldH == FALSE)
+		    HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
+		HTML_put_string(me, "[OVERLAY]");
+		if (me->inBoldH == FALSE)
+		    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+		HText_endAnchor(me->text, me->CurrentANum);
+		HTML_put_character(me, ' ');
+		me->in_word = NO;
+	    }
+	    FREE(href);
+	}
+	break;
+
+    case HTML_APPLET:
+	me->inAPPLET = TRUE;
+	me->inAPPLETwithP = FALSE;
+	HTML_put_character(me, ' ');	/* space char may be ignored */
+	/*
+	 * Load id_string if we have an ID or NAME.  - FM
+	 */
+	if (present && present[HTML_APPLET_ID] &&
+	    non_empty(value[HTML_APPLET_ID])) {
+	    StrAllocCopy(id_string, value[HTML_APPLET_ID]);
+	} else if (present && present[HTML_APPLET_NAME] &&
+		   non_empty(value[HTML_APPLET_NAME])) {
+	    StrAllocCopy(id_string, value[HTML_APPLET_NAME]);
+	}
+	if (id_string) {
+	    TRANSLATE_AND_UNESCAPE_TO_STD(&id_string);
+	    LYHandleID(me, id_string);
+	    FREE(id_string);
+	}
+	me->in_word = NO;
+
+	/*
+	 * If there's an ALT string, use it, unless the ALT string is
+	 * zero-length and we are making all sources links.  - FM
+	 */
+	if (present && present[HTML_APPLET_ALT] && value[HTML_APPLET_ALT] &&
+	    (!clickable_images ||
+	     (clickable_images && *value[HTML_APPLET_ALT] != '\0'))) {
+	    StrAllocCopy(alt_string, value[HTML_APPLET_ALT]);
+	    TRANSLATE_AND_UNESCAPE_ENTITIES(&alt_string,
+					    me->UsePlainSpace, me->HiddenValue);
+	    /*
+	     * If it's all spaces and we are making sources links, treat it as
+	     * zero-length.  - FM
+	     */
+	    if (clickable_images) {
+		LYTrimHead(alt_string);
+		LYTrimTail(alt_string);
+		if (*alt_string == '\0') {
+		    StrAllocCopy(alt_string, "[APPLET]");
+		}
+	    }
+
+	} else {
+	    if (clickable_images)
+		StrAllocCopy(alt_string, "[APPLET]");
+	    else
+		StrAllocCopy(alt_string, "");
+	}
+
+	/*
+	 * If we're making all sources links, get the source.  - FM
+	 */
+	if (clickable_images && present && present[HTML_APPLET_CODE] &&
+	    non_empty(value[HTML_APPLET_CODE])) {
+	    char *base = NULL;
+
+	    Base = (me->inBASE)
+		? me->base_href
+		: me->node_anchor->address;
+	    /*
+	     * Check for a CODEBASE attribute.  - FM
+	     */
+	    if (present[HTML_APPLET_CODEBASE] &&
+		non_empty(value[HTML_APPLET_CODEBASE])) {
+		StrAllocCopy(base, value[HTML_APPLET_CODEBASE]);
+		LYRemoveBlanks(base);
+		TRANSLATE_AND_UNESCAPE_TO_STD(&base);
+		/*
+		 * Force it to be a directory.  - FM
+		 */
+		if (*base == '\0')
+		    StrAllocCopy(base, "/");
+		LYAddHtmlSep(&base);
+		LYLegitimizeHREF(me, &base, TRUE, FALSE);
+
+		HTParseALL(&base, Base);
+	    }
+
+	    StrAllocCopy(href, value[HTML_APPLET_CODE]);
+	    LYLegitimizeHREF(me, &href, TRUE, FALSE);
+	    HTParseALL(&href, (base ? base : Base));
+	    FREE(base);
+
+	    if (*href) {
+		if (me->inA) {
+		    if (me->inBoldA == TRUE && me->inBoldH == FALSE)
+			HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+		    HText_endAnchor(me->text, me->CurrentANum);
+		    HTML_put_character(me, '-');
+		}
+		me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+							 NULL,	/* Tag */
+							 href,	/* Addresss */
+							 (HTLinkType *) 0);	/* Type */
+		me->CurrentANum = HText_beginAnchor(me->text,
+						    me->inUnderline,
+						    me->CurrentA);
+		if (me->inBoldH == FALSE)
+		    HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
+		HTML_put_string(me, alt_string);
+		if (me->inA == FALSE) {
+		    if (me->inBoldH == FALSE)
+			HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+		    HText_endAnchor(me->text, me->CurrentANum);
+		    me->CurrentANum = 0;
+		}
+		HTML_put_character(me, ' ');	/* space char may be ignored */
+		me->in_word = NO;
+	    }
+	    FREE(href);
+	} else if (*alt_string) {
+	    /*
+	     * Just put up the ALT string, if non-zero.  - FM
+	     */
+	    HTML_put_string(me, alt_string);
+	    HTML_put_character(me, ' ');	/* space char may be ignored */
+	    me->in_word = NO;
+	}
+	FREE(alt_string);
+	FREE(id_string);
+	break;
+
+    case HTML_BGSOUND:
+	/*
+	 * If we're making all sources links, get the source.  - FM
+	 */
+	if (clickable_images && present && present[HTML_BGSOUND_SRC] &&
+	    non_empty(value[HTML_BGSOUND_SRC])) {
+	    StrAllocCopy(href, value[HTML_BGSOUND_SRC]);
+	    LYLegitimizeHREF(me, &href, TRUE, TRUE);
+	    if (*href == '\0') {
+		FREE(href);
+		break;
+	    }
+
+	    if (me->inA) {
+		if (me->inBoldA == TRUE && me->inBoldH == FALSE)
+		    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+		HText_endAnchor(me->text, me->CurrentANum);
+		HTML_put_character(me, '-');
+	    } else {
+		HTML_put_character(me, ' ');	/* space char may be ignored */
+		me->in_word = NO;
+	    }
+	    me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+						     NULL,	/* Tag */
+						     href,	/* Addresss */
+						     (HTLinkType *) 0);		/* Type */
+	    me->CurrentANum = HText_beginAnchor(me->text,
+						me->inUnderline,
+						me->CurrentA);
+	    if (me->inBoldH == FALSE)
+		HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
+	    HTML_put_string(me, "[BGSOUND]");
+	    if (me->inA == FALSE) {
+		if (me->inBoldH == FALSE)
+		    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+		HText_endAnchor(me->text, me->CurrentANum);
+		me->CurrentANum = 0;
+	    }
+	    HTML_put_character(me, ' ');	/* space char may be ignored */
+	    me->in_word = NO;
+	    FREE(href);
+	}
+	break;
+
+    case HTML_EMBED:
+	if (pseudo_inline_alts || clickable_images)
+	    HTML_put_character(me, ' ');	/* space char may be ignored */
+	/*
+	 * Load id_string if we have an ID or NAME.  - FM
+	 */
+	if (present && present[HTML_EMBED_ID] &&
+	    non_empty(value[HTML_EMBED_ID])) {
+	    StrAllocCopy(id_string, value[HTML_EMBED_ID]);
+	} else if (present && present[HTML_EMBED_NAME] &&
+		   non_empty(value[HTML_EMBED_NAME])) {
+	    StrAllocCopy(id_string, value[HTML_EMBED_NAME]);
+	}
+	if (id_string) {
+	    TRANSLATE_AND_UNESCAPE_TO_STD(&id_string);
+	    LYHandleID(me, id_string);
+	    FREE(id_string);
+	}
+	if (pseudo_inline_alts || clickable_images)
+	    me->in_word = NO;
+
+	/*
+	 * If there's an ALT string, use it, unless the ALT string is
+	 * zero-length and we are making all sources links.  - FM
+	 */
+	if (present && present[HTML_EMBED_ALT] && value[HTML_EMBED_ALT] &&
+	    (!clickable_images ||
+	     (clickable_images && *value[HTML_EMBED_ALT] != '\0'))) {
+	    StrAllocCopy(alt_string, value[HTML_EMBED_ALT]);
+	    TRANSLATE_AND_UNESCAPE_ENTITIES(&alt_string,
+					    me->UsePlainSpace, me->HiddenValue);
+	    /*
+	     * If it's all spaces and we are making sources links, treat it as
+	     * zero-length.  - FM
+	     */
+	    if (clickable_images) {
+		LYTrimHead(alt_string);
+		LYTrimTail(alt_string);
+		if (*alt_string == '\0') {
+		    StrAllocCopy(alt_string, "[EMBED]");
+		}
+	    }
+	} else {
+	    if (pseudo_inline_alts || clickable_images)
+		StrAllocCopy(alt_string, "[EMBED]");
+	    else
+		StrAllocCopy(alt_string, "");
+	}
+
+	/*
+	 * If we're making all sources links, get the source.  - FM
+	 */
+	if (clickable_images && present && present[HTML_EMBED_SRC] &&
+	    non_empty(value[HTML_EMBED_SRC])) {
+	    StrAllocCopy(href, value[HTML_EMBED_SRC]);
+	    LYLegitimizeHREF(me, &href, TRUE, TRUE);
+	    if (*href) {
+		if (me->inA) {
+		    if (me->inBoldA == TRUE && me->inBoldH == FALSE)
+			HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+		    HText_endAnchor(me->text, me->CurrentANum);
+		    HTML_put_character(me, '-');
+		}
+		me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+							 NULL,	/* Tag */
+							 href,	/* Addresss */
+							 (HTLinkType *) 0);	/* Type */
+		me->CurrentANum = HText_beginAnchor(me->text,
+						    me->inUnderline,
+						    me->CurrentA);
+		if (me->inBoldH == FALSE)
+		    HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
+		HTML_put_string(me, alt_string);
+		if (me->inBoldH == FALSE)
+		    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+		if (me->inA == FALSE) {
+		    if (me->inBoldH == FALSE)
+			HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+		    HText_endAnchor(me->text, me->CurrentANum);
+		    me->CurrentANum = 0;
+		}
+		HTML_put_character(me, ' ');
+		me->in_word = NO;
+	    }
+	    FREE(href);
+	} else if (*alt_string) {
+	    /*
+	     * Just put up the ALT string, if non-zero.  - FM
+	     */
+	    HTML_put_string(me, alt_string);
+	    HTML_put_character(me, ' ');	/* space char may be ignored */
+	    me->in_word = NO;
+	}
+	FREE(alt_string);
+	FREE(id_string);
+	break;
+
+    case HTML_CREDIT:
+	LYEnsureDoubleSpace(me);
+	LYResetParagraphAlignment(me);
+	me->inCREDIT = TRUE;
+	CHECK_ID(HTML_GEN_ID);
+	if (me->inUnderline == FALSE)
+	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
+	HTML_put_string(me, "CREDIT:");
+	if (me->inUnderline == FALSE)
+	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
+	HTML_put_character(me, ' ');
+	CAN_JUSTIFY_START;
+
+	if (me->inFIG)
+	    /*
+	     * Assume all text in the FIG container is intended to be
+	     * paragraphed.  - FM
+	     */
+	    me->inFIGwithP = TRUE;
+
+	if (me->inAPPLET)
+	    /*
+	     * Assume all text in the APPLET container is intended to be
+	     * paragraphed.  - FM
+	     */
+	    me->inAPPLETwithP = TRUE;
+
+	me->inLABEL = TRUE;
+	me->in_word = NO;
+	me->inP = FALSE;
+	break;
+
+    case HTML_CAPTION:
+	LYEnsureDoubleSpace(me);
+	LYResetParagraphAlignment(me);
+	me->inCAPTION = TRUE;
+	CHECK_ID(HTML_CAPTION_ID);
+	if (me->inUnderline == FALSE)
+	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
+	HTML_put_string(me, "CAPTION:");
+	if (me->inUnderline == FALSE)
+	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
+	HTML_put_character(me, ' ');
+	CAN_JUSTIFY_START;
+
+	if (me->inFIG)
+	    /*
+	     * Assume all text in the FIG container is intended to be
+	     * paragraphed.  - FM
+	     */
+	    me->inFIGwithP = TRUE;
+
+	if (me->inAPPLET)
+	    /*
+	     * Assume all text in the APPLET container is intended to be
+	     * paragraphed.  - FM
+	     */
+	    me->inAPPLETwithP = TRUE;
+
+	me->inLABEL = TRUE;
+	me->in_word = NO;
+	me->inP = FALSE;
+	break;
+
+    case HTML_FORM:
+	{
+	    char *action = NULL;
+	    char *method = NULL;
+	    char *enctype = NULL;
+	    const char *accept_cs = NULL;
+
+	    HTChildAnchor *source;
+	    HTAnchor *link_dest;
+
+	    /*
+	     * FORM may have been declared SGML_EMPTY in HTMLDTD.c, and
+	     * SGML_character() in SGML.c may check for a FORM end tag to call
+	     * HTML_end_element() directly (with a check in that to bypass
+	     * decrementing of the HTML parser's stack), so if we have an open
+	     * FORM, close that one now.  - FM
+	     */
+	    if (me->inFORM) {
+		CTRACE((tfp, "HTML: Missing FORM end tag.  Faking it!\n"));
+		SET_SKIP_STACK(HTML_FORM);
+		HTML_end_element(me, HTML_FORM, include);
+	    }
+
+	    /*
+	     * Set to know we are in a new form.
+	     */
+	    me->inFORM = TRUE;
+	    EMIT_IFDEF_USE_JUSTIFY_ELTS(form_in_htext = TRUE);
+
+	    if (present && present[HTML_FORM_ACCEPT_CHARSET]) {
+		accept_cs = (value[HTML_FORM_ACCEPT_CHARSET]
+			     ? value[HTML_FORM_ACCEPT_CHARSET]
+			     : "UNKNOWN");
+	    }
+
+	    Base = (me->inBASE)
+		? me->base_href
+		: me->node_anchor->address;
+
+	    if (present && present[HTML_FORM_ACTION] &&
+		value[HTML_FORM_ACTION]) {
+
+		StrAllocCopy(action, value[HTML_FORM_ACTION]);
+		LYLegitimizeHREF(me, &action, TRUE, TRUE);
+
+		/*
+		 * Check whether a base tag is in effect.  Note that actions
+		 * always are resolved w.r.t.  to the base, even if the action
+		 * is empty.  - FM
+		 */
+		HTParseALL(&action, Base);
+
+	    } else {
+		StrAllocCopy(action, Base);
+	    }
+
+	    source = HTAnchor_findChildAndLink(me->node_anchor,
+					       NULL,
+					       action,
+					       (HTLinkType *) 0);
+	    if ((link_dest = HTAnchor_followLink(source)) != NULL) {
+		/*
+		 * Memory leak fixed.  05-28-94 Lynx 2-3-1 Garrett Arch Blythe
+		 */
+		char *cp_freeme = HTAnchor_address(link_dest);
+
+		if (cp_freeme != NULL) {
+		    StrAllocCopy(action, cp_freeme);
+		    FREE(cp_freeme);
+		} else {
+		    StrAllocCopy(action, "");
+		}
+	    }
+
+	    if (present && present[HTML_FORM_METHOD])
+		StrAllocCopy(method, (value[HTML_FORM_METHOD]
+				      ? value[HTML_FORM_METHOD]
+				      : "GET"));
+
+	    if (present && present[HTML_FORM_ENCTYPE] &&
+		non_empty(value[HTML_FORM_ENCTYPE])) {
+		StrAllocCopy(enctype, value[HTML_FORM_ENCTYPE]);
+		LYLowerCase(enctype);
+	    }
+
+	    if (present) {
+		/*
+		 * Check for a TITLE attribute, and if none is present, check
+		 * for a SUBJECT attribute as a synonym.  - FM
+		 */
+		if (present[HTML_FORM_TITLE] &&
+		    non_empty(value[HTML_FORM_TITLE])) {
+		    StrAllocCopy(title, value[HTML_FORM_TITLE]);
+		} else if (present[HTML_FORM_SUBJECT] &&
+			   non_empty(value[HTML_FORM_SUBJECT])) {
+		    StrAllocCopy(title, value[HTML_FORM_SUBJECT]);
+		}
+		if (non_empty(title)) {
+		    TRANSLATE_AND_UNESCAPE_ENTITIES(&title, TRUE, FALSE);
+		    LYTrimHead(title);
+		    LYTrimTail(title);
+		    if (*title == '\0') {
+			FREE(title);
+		    }
+		}
+	    }
+
+	    HText_beginForm(action, method, enctype, title, accept_cs);
+
+	    FREE(action);
+	    FREE(method);
+	    FREE(enctype);
+	    FREE(title);
+	}
+	CHECK_ID(HTML_FORM_ID);
+	break;
+
+    case HTML_FIELDSET:
+	LYEnsureDoubleSpace(me);
+	LYResetParagraphAlignment(me);
+	CHECK_ID(HTML_GEN_ID);
+	break;
+
+    case HTML_LEGEND:
+	LYEnsureDoubleSpace(me);
+	LYResetParagraphAlignment(me);
+	CHECK_ID(HTML_CAPTION_ID);
+	break;
+
+    case HTML_LABEL:
+	CHECK_ID(HTML_LABEL_ID);
+	break;
+
+    case HTML_KEYGEN:
+	CHECK_ID(HTML_KEYGEN_ID);
+	break;
+
+    case HTML_BUTTON:
+	{
+	    InputFieldData I;
+	    int chars;
+
+	    /* init */
+	    memset(&I, 0, sizeof(I));
+	    I.name_cs = ATTR_CS_IN;
+	    I.value_cs = ATTR_CS_IN;
+
+	    UPDATE_STYLE;
+	    if (present &&
+		present[HTML_BUTTON_TYPE] &&
+		value[HTML_BUTTON_TYPE]) {
+		if (!strcasecomp(value[HTML_BUTTON_TYPE], "submit") ||
+		    !strcasecomp(value[HTML_BUTTON_TYPE], "reset")) {
+		    /*
+		     * It's a button for submitting or resetting a form.  - FM
+		     */
+		    I.type = value[HTML_BUTTON_TYPE];
+		} else {
+		    /*
+		     * Ugh, it's a button for a script.  - FM
+		     */
+		    I.type = value[HTML_BUTTON_TYPE];
+		}
+	    } else {
+		/* default, if no type given, is a submit button */
+		I.type = "submit";
+	    }
+
+	    /*
+	     * Before any input field, add a collapsible space if we're not in
+	     * a PRE block, to promote a wrap there for any long values that
+	     * would extend past the right margin from our current position in
+	     * the line.  If we are in a PRE block, start a new line if the
+	     * last line already is within 6 characters of the wrap point for
+	     * PRE blocks.  - FM
+	     */
+	    if (me->sp[0].tag_number != HTML_PRE && !me->inPRE &&
+		me->sp->style->freeFormat) {
+		HTML_put_character(me, ' ');
+		me->in_word = NO;
+	    } else if (HText_LastLineSize(me->text, FALSE) > (LYcolLimit - 6)) {
+		HTML_put_character(me, '\n');
+		me->in_word = NO;
+	    }
+	    HTML_put_character(me, '(');
+
+	    if (!(present && present[HTML_BUTTON_NAME] &&
+		  value[HTML_BUTTON_NAME])) {
+		I.name = "";
+	    } else if (strchr(value[HTML_BUTTON_NAME], '&') == NULL) {
+		I.name = value[HTML_BUTTON_NAME];
+	    } else {
+		StrAllocCopy(I_name, value[HTML_BUTTON_NAME]);
+		UNESCAPE_FIELDNAME_TO_STD(&I_name);
+		I.name = I_name;
+	    }
+
+	    if (present && present[HTML_BUTTON_VALUE] &&
+		non_empty(value[HTML_BUTTON_VALUE])) {
+		/*
+		 * Convert any HTML entities or decimal escaping.  - FM
+		 */
+		StrAllocCopy(I_value, value[HTML_BUTTON_VALUE]);
+		me->UsePlainSpace = TRUE;
+		TRANSLATE_AND_UNESCAPE_ENTITIES(&I_value, TRUE, me->HiddenValue);
+		me->UsePlainSpace = FALSE;
+		I.value = I_value;
+		/*
+		 * Convert any newlines or tabs to spaces, and trim any lead or
+		 * trailing spaces.  - FM
+		 */
+		LYReduceBlanks(I.value);
+	    } else if (!strcasecomp(I.type, "button")) {
+		if (non_empty(I.name)) {
+		    StrAllocCopy(I.value, I.name);
+		} else {
+		    StrAllocCopy(I.value, "BUTTON");
+		}
+	    }
+
+	    if (present && present[HTML_BUTTON_DISABLED])
+		I.disabled = YES;
+
+	    if (present && present[HTML_BUTTON_CLASS] &&	/* Not yet used. */
+		non_empty(value[HTML_BUTTON_CLASS]))
+		I.iclass = value[HTML_BUTTON_CLASS];
+
+	    if (present && present[HTML_BUTTON_ID] &&
+		non_empty(value[HTML_BUTTON_ID])) {
+		I.id = value[HTML_BUTTON_ID];
+		CHECK_ID(HTML_BUTTON_ID);
+	    }
+
+	    if (present && present[HTML_BUTTON_LANG] &&		/* Not yet used. */
+		non_empty(value[HTML_BUTTON_LANG]))
+		I.lang = value[HTML_BUTTON_LANG];
+
+	    chars = HText_beginInput(me->text, me->inUnderline, &I);
+	    /*
+	     * Submit and reset buttons have values which don't change, so
+	     * HText_beginInput() sets I.value to the string which should be
+	     * displayed, and we'll enter that instead of underscore
+	     * placeholders into the HText structure to see it instead of
+	     * underscores when dumping or printing.  We also won't worry about
+	     * a wrap in PRE blocks, because the line editor never is invoked
+	     * for submit or reset buttons.  - LE & FM
+	     */
+	    if (me->sp[0].tag_number == HTML_PRE ||
+		!me->sp->style->freeFormat) {
+		/*
+		 * We have a submit or reset button in a PRE block, so output
+		 * the entire value from the markup.  If it extends to the
+		 * right margin, it will wrap there, and only the portion
+		 * before that wrap will be hightlighted on screen display
+		 * (Yuk!) but we may as well show the rest of the full value on
+		 * the next or more lines.  - FM
+		 */
+		while (I.value[i])
+		    HTML_put_character(me, I.value[i++]);
+	    } else {
+		/*
+		 * The submit or reset button is not in a PRE block.  Note that
+		 * if a wrap occurs before outputting the entire value, the
+		 * wrapped portion will not be highlighted or clearly indicated
+		 * as part of the link for submission or reset (Yuk!).  We'll
+		 * replace any spaces in the submit or reset button value with
+		 * nbsp, to promote a wrap at the space we ensured would be
+		 * present before the start of the string, as when we use all
+		 * underscores instead of the INPUT's actual value, but we
+		 * could still get a wrap at the right margin, instead, if the
+		 * value is greater than a line width for the current style. 
+		 * Also, if chars somehow ended up longer than the length of
+		 * the actual value (shouldn't have), we'll continue padding
+		 * with nbsp up to the length of chars.  - FM
+		 */
+		for (i = 0; I.value[i]; i++) {
+		    HTML_put_character(me,
+				       (char) ((I.value[i] == ' ')
+					       ? HT_NON_BREAK_SPACE
+					       : I.value[i]));
+		}
+		while (i++ < chars) {
+		    HTML_put_character(me, HT_NON_BREAK_SPACE);
+		}
+	    }
+	    HTML_put_character(me, ')');
+	    if (me->sp[0].tag_number != HTML_PRE &&
+		me->sp->style->freeFormat) {
+		HTML_put_character(me, ' ');
+		me->in_word = NO;
+	    }
+	    FREE(I_value);
+	    FREE(I_name);
+	}
+	break;
+
+    case HTML_INPUT:
+	{
+	    InputFieldData I;
+	    int chars;
+	    BOOL UseALTasVALUE = FALSE;
+	    BOOL HaveSRClink = FALSE;
+	    char *ImageSrc = NULL;
+	    BOOL IsSubmitOrReset = FALSE;
+	    HTkcode kcode = NOKANJI;
+	    HTkcode specified_kcode = NOKANJI;
+
+	    /* init */
+	    memset(&I, 0, sizeof(I));
+	    I.name_cs = ATTR_CS_IN;
+	    I.value_cs = ATTR_CS_IN;
+
+	    UPDATE_STYLE;
+
+	    /*
+	     * Before any input field, add a collapsible space if we're not in
+	     * a PRE block, to promote a wrap there for any long values that
+	     * would extend past the right margin from our current position in
+	     * the line.  If we are in a PRE block, start a new line if the
+	     * last line already is within 6 characters of the wrap point for
+	     * PRE blocks.  - FM
+	     */
+	    if (me->sp[0].tag_number != HTML_PRE && !me->inPRE &&
+		me->sp->style->freeFormat) {
+		HTML_put_character(me, ' ');
+		me->in_word = NO;
+	    } else if (HText_LastLineSize(me->text, FALSE) > (LYcolLimit - 6)) {
+		HTML_put_character(me, '\n');
+		me->in_word = NO;
+	    }
+
+	    /*
+	     * Get the TYPE and make sure we can handle it.  - FM
+	     */
+	    if (present && present[HTML_INPUT_TYPE] &&
+		non_empty(value[HTML_INPUT_TYPE])) {
+		const char *not_impl = NULL;
+		char *usingval = NULL;
+
+		I.type = value[HTML_INPUT_TYPE];
+
+		if (!strcasecomp(I.type, "range")) {
+		    if (present[HTML_INPUT_MIN] &&
+			non_empty(value[HTML_INPUT_MIN]))
+			I.min = value[HTML_INPUT_MIN];
+		    if (present[HTML_INPUT_MAX] &&
+			non_empty(value[HTML_INPUT_MAX]))
+			I.max = value[HTML_INPUT_MAX];
+		    /*
+		     * Not yet implemented.
+		     */
+#ifdef NOTDEFINED
+		    not_impl = "[RANGE Input]";
+		    if (me->inFORM)
+			HText_DisableCurrentForm();
+#endif /* NOTDEFINED */
+		    CTRACE((tfp, "HTML: Ignoring TYPE=\"range\"\n"));
+		    break;
+
+		} else if (!strcasecomp(I.type, "file")) {
+		    if (present[HTML_INPUT_ACCEPT] &&
+			non_empty(value[HTML_INPUT_ACCEPT]))
+			I.accept = value[HTML_INPUT_ACCEPT];
+#ifndef USE_FILE_UPLOAD
+		    not_impl = "[FILE Input]";
+		    CTRACE((tfp, "Attempting to fake as: %s\n", I.type));
+#ifdef NOTDEFINED
+		    if (me->inFORM)
+			HText_DisableCurrentForm();
+#endif /* NOTDEFINED */
+		    CTRACE((tfp, "HTML: Ignoring TYPE=\"file\"\n"));
+#endif /* USE_FILE_UPLOAD */
+
+		} else if (!strcasecomp(I.type, "button")) {
+		    /*
+		     * Ugh, a button for a script.
+		     */
+		    not_impl = "[BUTTON Input]";
+		}
+		if (not_impl != NULL) {
+		    if (me->inUnderline == FALSE) {
+			HText_appendCharacter(me->text,
+					      LY_UNDERLINE_START_CHAR);
+		    }
+		    HTML_put_string(me, not_impl);
+		    if (usingval != NULL) {
+			HTML_put_string(me, usingval);
+			FREE(usingval);
+		    } else {
+			HTML_put_string(me, " (not implemented)");
+		    }
+		    if (me->inUnderline == FALSE) {
+			HText_appendCharacter(me->text,
+					      LY_UNDERLINE_END_CHAR);
+		    }
+		}
+	    }
+
+	    CTRACE((tfp, "Ok, we're trying type=[%s]\n", NONNULL(I.type)));
+
+	    /*
+	     * Check for an unclosed TEXTAREA.
+	     */
+	    if (me->inTEXTAREA) {
+		if (LYBadHTML(me)) {
+		    LYShowBadHTML("Bad HTML: Missing TEXTAREA end tag.\n");
+		}
+	    }
+
+	    /*
+	     * Check for an unclosed SELECT, try to close it if found.
+	     */
+	    if (me->inSELECT) {
+		CTRACE((tfp, "HTML: Missing SELECT end tag, faking it...\n"));
+		if (me->sp->tag_number != HTML_SELECT) {
+		    SET_SKIP_STACK(HTML_SELECT);
+		}
+		HTML_end_element(me, HTML_SELECT, include);
+	    }
+
+	    /*
+	     * Handle the INPUT as for a FORM.  - FM
+	     */
+	    if (!(present && present[HTML_INPUT_NAME] &&
+		  non_empty(value[HTML_INPUT_NAME]))) {
+		I.name = "";
+	    } else if (strchr(value[HTML_INPUT_NAME], '&') == NULL) {
+		I.name = value[HTML_INPUT_NAME];
+	    } else {
+		StrAllocCopy(I_name, value[HTML_INPUT_NAME]);
+		UNESCAPE_FIELDNAME_TO_STD(&I_name);
+		I.name = I_name;
+	    }
+
+	    if ((present && present[HTML_INPUT_ALT] &&
+		 non_empty(value[HTML_INPUT_ALT]) &&
+		 I.type && !strcasecomp(I.type, "image")) &&
+		!(present && present[HTML_INPUT_VALUE] &&
+		  non_empty(value[HTML_INPUT_VALUE]))) {
+		/*
+		 * This is a TYPE="image" using an ALT rather than VALUE
+		 * attribute to indicate the link string for text clients or
+		 * GUIs with image loading off, so set the flag to use that as
+		 * if it were a VALUE attribute.  - FM
+		 */
+		UseALTasVALUE = TRUE;
+	    }
+	    if (verbose_img && !clickable_images &&
+		present && present[HTML_INPUT_SRC] &&
+		non_empty(value[HTML_INPUT_SRC]) &&
+		I.type && !strcasecomp(I.type, "image")) {
+		ImageSrc = MakeNewImageValue(value);
+	    } else if (clickable_images == TRUE &&
+		       present && present[HTML_INPUT_SRC] &&
+		       non_empty(value[HTML_INPUT_SRC]) &&
+		       I.type && !strcasecomp(I.type, "image")) {
+		StrAllocCopy(href, value[HTML_INPUT_SRC]);
+		/*
+		 * We have a TYPE="image" with a non-zero-length SRC attribute
+		 * and want clickable images.  Make the SRC's value a link if
+		 * it's still not zero-length legitimizing it.  - FM
+		 */
+		LYLegitimizeHREF(me, &href, TRUE, TRUE);
+		if (*href) {
+
+		    if (me->inA) {
+			SET_SKIP_STACK(HTML_A);
+			HTML_end_element(me, HTML_A, include);
+		    }
+		    me->CurrentA = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+							     NULL,	/* Tag */
+							     href,	/* Addresss */
+							     (HTLinkType *) 0);		/* Type */
+		    HText_beginAnchor(me->text, me->inUnderline, me->CurrentA);
+		    if (me->inBoldH == FALSE)
+			HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
+		    HTML_put_string(me, VERBOSE_IMG(value,
+						    HTML_INPUT_SRC,
+						    "[IMAGE]"));
+		    FREE(newtitle);
+		    if (me->inBoldH == FALSE)
+			HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+		    HText_endAnchor(me->text, 0);
+		    HTML_put_character(me, '-');
+		    HaveSRClink = TRUE;
+		}
+		FREE(href);
+	    }
+	    CTRACE((tfp, "2.Ok, we're trying type=[%s] (present=%p)\n",
+		    NONNULL(I.type),
+		    present));
+	    /* text+file don't go in here */
+	    if ((UseALTasVALUE == TRUE) ||
+		(present && present[HTML_INPUT_VALUE] &&
+		 value[HTML_INPUT_VALUE] &&
+		 (*value[HTML_INPUT_VALUE] ||
+		  (I.type && (!strcasecomp(I.type, "checkbox") ||
+			      !strcasecomp(I.type, "radio")))))) {
+
+		/*
+		 * Convert any HTML entities or decimal escaping.  - FM
+		 */
+		int CurrentCharSet = current_char_set;
+		BOOL CurrentEightBitRaw = HTPassEightBitRaw;
+		BOOLEAN CurrentUseDefaultRawMode = LYUseDefaultRawMode;
+		HTCJKlang CurrentHTCJK = HTCJK;
+
+		if (I.type && !strcasecomp(I.type, "hidden")) {
+		    me->HiddenValue = TRUE;
+		    current_char_set = LATIN1;	/* Default ISO-Latin1 */
+		    LYUseDefaultRawMode = TRUE;
+		    HTMLSetCharacterHandling(current_char_set);
+		}
+
+		CTRACE((tfp, "3.Ok, we're trying type=[%s]\n", NONNULL(I.type)));
+		if (!I.type)
+		    me->UsePlainSpace = TRUE;
+		else if (!strcasecomp(I.type, "text") ||
+#ifdef USE_FILE_UPLOAD
+			 !strcasecomp(I.type, "file") ||
+#endif
+			 !strcasecomp(I.type, "submit") ||
+			 !strcasecomp(I.type, "image") ||
+			 !strcasecomp(I.type, "reset")) {
+		    CTRACE((tfp, "normal field type: %s\n", NONNULL(I.type)));
+		    me->UsePlainSpace = TRUE;
+		}
+
+		StrAllocCopy(I_value,
+			     ((UseALTasVALUE == TRUE)
+			      ? value[HTML_INPUT_ALT]
+			      : value[HTML_INPUT_VALUE]));
+		if (me->UsePlainSpace && !me->HiddenValue) {
+		    I.value_cs = current_char_set;
+		}
+		CTRACE((tfp, "4.Ok, we're trying type=[%s]\n", NONNULL(I.type)));
+		TRANSLATE_AND_UNESCAPE_ENTITIES6(&I_value,
+						 ATTR_CS_IN,
+						 I.value_cs,
+						 (BOOL) (me->UsePlainSpace &&
+							 !me->HiddenValue),
+						 me->UsePlainSpace,
+						 me->HiddenValue);
+		I.value = I_value;
+		if (me->UsePlainSpace == TRUE) {
+		    /*
+		     * Convert any newlines or tabs to spaces, and trim any
+		     * lead or trailing spaces.  - FM
+		     */
+		    LYReduceBlanks(I.value);
+		}
+		me->UsePlainSpace = FALSE;
+
+		if (I.type && !strcasecomp(I.type, "hidden")) {
+		    me->HiddenValue = FALSE;
+		    current_char_set = CurrentCharSet;
+		    LYUseDefaultRawMode = CurrentUseDefaultRawMode;
+		    HTMLSetCharacterHandling(current_char_set);
+		    HTPassEightBitRaw = CurrentEightBitRaw;
+		    HTCJK = CurrentHTCJK;
+		}
+	    } else if (HaveSRClink == TRUE) {
+		/*
+		 * We put up an [IMAGE] link and '-' for a TYPE="image" and
+		 * didn't get a VALUE or ALT string, so fake a "Submit" value. 
+		 * If we didn't put up a link, then HText_beginInput() will use
+		 * "[IMAGE]-Submit".  - FM
+		 */
+		StrAllocCopy(I_value, "Submit");
+		I.value = I_value;
+	    } else if (ImageSrc) {
+		/* [IMAGE]-Submit with verbose images and not clickable images.
+		 * Use ImageSrc if no other alt or value is supplied. --LE
+		 */
+		I.value = ImageSrc;
+	    }
+	    if (present && present[HTML_INPUT_READONLY])
+		I.disabled = YES;
+	    if (present && present[HTML_INPUT_CHECKED])
+		I.checked = YES;
+	    if (present && present[HTML_INPUT_SIZE] &&
+		non_empty(value[HTML_INPUT_SIZE]))
+		I.size = atoi(value[HTML_INPUT_SIZE]);
+	    LimitValue(I.size, MAX_LINE);
+	    if (present && present[HTML_INPUT_MAXLENGTH] &&
+		non_empty(value[HTML_INPUT_MAXLENGTH]))
+		I.maxlength = value[HTML_INPUT_MAXLENGTH];
+	    if (present && present[HTML_INPUT_DISABLED])
+		I.disabled = YES;
+
+	    if (present && present[HTML_INPUT_ACCEPT_CHARSET]) {	/* Not yet used. */
+		I.accept_cs = (value[HTML_INPUT_ACCEPT_CHARSET]
+			       ? value[HTML_INPUT_ACCEPT_CHARSET]
+			       : "UNKNOWN");
+	    }
+	    if (present && present[HTML_INPUT_ALIGN] &&		/* Not yet used. */
+		non_empty(value[HTML_INPUT_ALIGN]))
+		I.align = value[HTML_INPUT_ALIGN];
+	    if (present && present[HTML_INPUT_CLASS] &&		/* Not yet used. */
+		non_empty(value[HTML_INPUT_CLASS]))
+		I.iclass = value[HTML_INPUT_CLASS];
+	    if (present && present[HTML_INPUT_ERROR] &&		/* Not yet used. */
+		non_empty(value[HTML_INPUT_ERROR]))
+		I.error = value[HTML_INPUT_ERROR];
+	    if (present && present[HTML_INPUT_HEIGHT] &&	/* Not yet used. */
+		non_empty(value[HTML_INPUT_HEIGHT]))
+		I.height = value[HTML_INPUT_HEIGHT];
+	    if (present && present[HTML_INPUT_WIDTH] &&		/* Not yet used. */
+		non_empty(value[HTML_INPUT_WIDTH]))
+		I.width = value[HTML_INPUT_WIDTH];
+	    if (present && present[HTML_INPUT_ID] &&
+		non_empty(value[HTML_INPUT_ID])) {
+		I.id = value[HTML_INPUT_ID];
+		CHECK_ID(HTML_INPUT_ID);
+	    }
+	    if (present && present[HTML_INPUT_LANG] &&	/* Not yet used. */
+		non_empty(value[HTML_INPUT_LANG]))
+		I.lang = value[HTML_INPUT_LANG];
+	    if (present && present[HTML_INPUT_MD] &&	/* Not yet used. */
+		non_empty(value[HTML_INPUT_MD]))
+		I.md = value[HTML_INPUT_MD];
+
+	    chars = HText_beginInput(me->text, me->inUnderline, &I);
+	    CTRACE((tfp,
+		    "I.%s have %d chars, or something\n",
+		    NONNULL(I.type),
+		    chars));
+	    /*
+	     * Submit and reset buttons have values which don't change, so
+	     * HText_beginInput() sets I.value to the string which should be
+	     * displayed, and we'll enter that instead of underscore
+	     * placeholders into the HText structure to see it instead of
+	     * underscores when dumping or printing.  We also won't worry about
+	     * a wrap in PRE blocks, because the line editor never is invoked
+	     * for submit or reset buttons.  - LE & FM
+	     */
+	    if (I.type &&
+		(!strcasecomp(I.type, "submit") ||
+		 !strcasecomp(I.type, "reset") ||
+		 !strcasecomp(I.type, "image")))
+		IsSubmitOrReset = TRUE;
+
+	    if (I.type && chars == 3 &&
+		!strcasecomp(I.type, "radio")) {
+		/*
+		 * Put a (_) placeholder, and one space (collapsible) before
+		 * the label that is expected to follow.  - FM
+		 */
+		HTML_put_string(me, "(_)");
+		HText_endInput(me->text);
+		chars = 0;
+		me->in_word = YES;
+		if (me->sp[0].tag_number != HTML_PRE &&
+		    me->sp->style->freeFormat) {
+		    HTML_put_character(me, ' ');
+		    me->in_word = NO;
+		}
+	    } else if (I.type && chars == 3 &&
+		       !strcasecomp(I.type, "checkbox")) {
+		/*
+		 * Put a [_] placeholder, and one space (collapsible) before
+		 * the label that is expected to follow.  - FM
+		 */
+		HTML_put_string(me, "[_]");
+		HText_endInput(me->text);
+		chars = 0;
+		me->in_word = YES;
+		if (me->sp[0].tag_number != HTML_PRE &&
+		    me->sp->style->freeFormat) {
+		    HTML_put_character(me, ' ');
+		    me->in_word = NO;
+		}
+	    } else if ((me->sp[0].tag_number == HTML_PRE ||
+			!me->sp->style->freeFormat)
+		       && chars > 6 &&
+		       IsSubmitOrReset == FALSE) {
+		/*
+		 * This is not a submit or reset button, and we are in a PRE
+		 * block with a field intended to exceed 6 character widths. 
+		 * The code inadequately handles INPUT fields in PRE tags if
+		 * wraps occur (at the right margin) for the underscore
+		 * placeholders.  We'll put up a minimum of 6 underscores,
+		 * since we should have wrapped artificially, above, if the
+		 * INPUT begins within 6 columns of the right margin, and if
+		 * any more would exceed the wrap column, we'll ignore them. 
+		 * Note that if we somehow get tripped up and a wrap still does
+		 * occur before all 6 of the underscores are output, the
+		 * wrapped ones won't be treated as part of the editing window,
+		 * nor be highlighted when not editing (Yuk!).  - FM
+		 */
+		for (i = 0; i < 6; i++) {
+		    HTML_put_character(me, '_');
+		    chars--;
+		}
+	    }
+	    CTRACE((tfp, "I.%s, %d\n", NONNULL(I.type), IsSubmitOrReset));
+	    if (IsSubmitOrReset == FALSE) {
+		/*
+		 * This is not a submit or reset button, so output the rest of
+		 * the underscore placeholders, if any more are needed.  - FM
+		 */
+		if (chars > 0) {
+		    for (; chars > 0; chars--)
+			HTML_put_character(me, '_');
+		    HText_endInput(me->text);
+		}
+	    } else {
+		if (HTCJK == JAPANESE) {
+		    kcode = HText_getKcode(me->text);
+		    HText_updateKcode(me->text, kanji_code);
+		    specified_kcode = HText_getSpecifiedKcode(me->text);
+		    HText_updateSpecifiedKcode(me->text, kanji_code);
+		}
+		if (me->sp[0].tag_number == HTML_PRE ||
+		    !me->sp->style->freeFormat) {
+		    /*
+		     * We have a submit or reset button in a PRE block, so
+		     * output the entire value from the markup.  If it extends
+		     * to the right margin, it will wrap there, and only the
+		     * portion before that wrap will be hightlighted on screen
+		     * display (Yuk!) but we may as well show the rest of the
+		     * full value on the next or more lines.  - FM
+		     */
+		    while (I.value[i])
+			HTML_put_character(me, I.value[i++]);
+		} else {
+		    /*
+		     * The submit or reset button is not in a PRE block.  Note
+		     * that if a wrap occurs before outputting the entire
+		     * value, the wrapped portion will not be highlighted or
+		     * clearly indicated as part of the link for submission or
+		     * reset (Yuk!).  We'll replace any spaces in the submit or
+		     * reset button value with nbsp, to promote a wrap at the
+		     * space we ensured would be present before the start of
+		     * the string, as when we use all underscores instead of
+		     * the INPUT's actual value, but we could still get a wrap
+		     * at the right margin, instead, if the value is greater
+		     * than a line width for the current style.  Also, if chars
+		     * somehow ended up longer than the length of the actual
+		     * value (shouldn't have), we'll continue padding with nbsp
+		     * up to the length of chars.  - FM
+		     */
+		    for (i = 0; I.value[i]; i++)
+			HTML_put_character(me,
+					   (char) (I.value[i] == ' '
+						   ? HT_NON_BREAK_SPACE
+						   : I.value[i]));
+		    while (i++ < chars)
+			HTML_put_character(me, HT_NON_BREAK_SPACE);
+		}
+		if (HTCJK == JAPANESE) {
+		    HText_updateKcode(me->text, kcode);
+		    HText_updateSpecifiedKcode(me->text, specified_kcode);
+		}
+	    }
+	    if (chars != 0) {
+		HText_endInput(me->text);
+	    }
+	    FREE(ImageSrc);
+	    FREE(I_value);
+	    FREE(I_name);
+	}
+	break;
+
+    case HTML_TEXTAREA:
+	/*
+	 * Set to know we are in a textarea.
+	 */
+	me->inTEXTAREA = TRUE;
+
+	/*
+	 * Get ready for the value.
+	 */
+	HTChunkClear(&me->textarea);
+	if (present && present[HTML_TEXTAREA_NAME] &&
+	    value[HTML_TEXTAREA_NAME]) {
+	    StrAllocCopy(me->textarea_name, value[HTML_TEXTAREA_NAME]);
+	    me->textarea_name_cs = ATTR_CS_IN;
+	    if (strchr(value[HTML_TEXTAREA_NAME], '&') != NULL) {
+		UNESCAPE_FIELDNAME_TO_STD(&me->textarea_name);
+	    }
+	} else {
+	    StrAllocCopy(me->textarea_name, "");
+	}
+
+	if (present && present[HTML_TEXTAREA_ACCEPT_CHARSET]) {
+	    if (value[HTML_TEXTAREA_ACCEPT_CHARSET]) {
+		StrAllocCopy(me->textarea_accept_cs, value[HTML_TEXTAREA_ACCEPT_CHARSET]);
+		TRANSLATE_AND_UNESCAPE_TO_STD(&me->textarea_accept_cs);
+	    } else {
+		StrAllocCopy(me->textarea_accept_cs, "UNKNOWN");
+	    }
+	} else {
+	    FREE(me->textarea_accept_cs);
+	}
+
+	if (present && present[HTML_TEXTAREA_COLS] &&
+	    value[HTML_TEXTAREA_COLS] &&
+	    isdigit(UCH(*value[HTML_TEXTAREA_COLS]))) {
+	    me->textarea_cols = atoi(value[HTML_TEXTAREA_COLS]);
+	} else {
+	    int width;
+
+	    width = LYcolLimit -
+		me->new_style->leftIndent - me->new_style->rightIndent;
+	    if (dump_output_immediately)	/* don't waste too much for this */
+		width = HTMIN(width, DFT_TEXTAREA_COLS);
+	    if (width > 1 && (width - 1) * 6 < MAX_LINE - 3 -
+		me->new_style->leftIndent - me->new_style->rightIndent)
+		me->textarea_cols = width;
+	    else
+		me->textarea_cols = DFT_TEXTAREA_COLS;
+	}
+	LimitValue(me->textarea_cols, MAX_TEXTAREA_COLS);
+
+	if (present && present[HTML_TEXTAREA_ROWS] &&
+	    value[HTML_TEXTAREA_ROWS] &&
+	    isdigit(UCH(*value[HTML_TEXTAREA_ROWS]))) {
+	    me->textarea_rows = atoi(value[HTML_TEXTAREA_ROWS]);
+	} else {
+	    me->textarea_rows = DFT_TEXTAREA_ROWS;
+	}
+	LimitValue(me->textarea_rows, MAX_TEXTAREA_ROWS);
+
+	/*
+	 * Lynx treats disabled and readonly textarea's the same -
+	 * unmodifiable in either case.
+	 */
+	me->textarea_disabled = NO;
+	if (present && present[HTML_TEXTAREA_READONLY])
+	    me->textarea_disabled = YES;
+
+	if (present && present[HTML_TEXTAREA_DISABLED])
+	    me->textarea_disabled = YES;
+
+	if (present && present[HTML_TEXTAREA_ID]
+	    && non_empty(value[HTML_TEXTAREA_ID])) {
+	    StrAllocCopy(id_string, value[HTML_TEXTAREA_ID]);
+	    TRANSLATE_AND_UNESCAPE_TO_STD(&id_string);
+	    if ((id_string != '\0') &&
+		(ID_A = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+						  id_string,	/* Tag */
+						  NULL,		/* Addresss */
+						  (HTLinkType *) 0))) {		/* Type */
+		HText_beginAnchor(me->text, me->inUnderline, ID_A);
+		HText_endAnchor(me->text, 0);
+		StrAllocCopy(me->textarea_id, id_string);
+	    } else {
+		FREE(me->textarea_id);
+	    }
+	    FREE(id_string);
+	} else {
+	    FREE(me->textarea_id);
+	}
+	break;
+
+    case HTML_SELECT:
+	/*
+	 * Check for an already open SELECT block.  - FM
+	 */
+	if (me->inSELECT) {
+	    if (LYBadHTML(me)) {
+		LYShowBadHTML("Bad HTML: SELECT start tag in SELECT element.  Faking SELECT end tag. *****\n");
+	    }
+	    if (me->sp->tag_number != HTML_SELECT) {
+		SET_SKIP_STACK(HTML_SELECT);
+	    }
+	    HTML_end_element(me, HTML_SELECT, include);
+	}
+
+	/*
+	 * Start a new SELECT block. - FM
+	 */
+	LYHandleSELECT(me,
+		       present, (const char **) value,
+		       include,
+		       TRUE);
+	break;
+
+    case HTML_OPTION:
+	{
+	    /*
+	     * An option is a special case of an input field.
+	     */
+	    InputFieldData I;
+
+	    /*
+	     * Make sure we're in a select tag.
+	     */
+	    if (!me->inSELECT) {
+		if (LYBadHTML(me)) {
+		    LYShowBadHTML("Bad HTML: OPTION tag not within SELECT tag\n");
+		}
+
+		/*
+		 * Too likely to cause a crash, so we'll ignore it.  - FM
+		 */
+		break;
+	    }
+
+	    if (!me->first_option) {
+		/*
+		 * Finish the data off.
+		 */
+		HTChunkTerminate(&me->option);
+
+		/*
+		 * Finish the previous option @@@@@
+		 */
+		HText_setLastOptionValue(me->text,
+					 me->option.data,
+					 me->LastOptionValue,
+					 MIDDLE_ORDER,
+					 me->LastOptionChecked,
+					 me->UCLYhndl,
+					 ATTR_CS_IN);
+	    }
+
+	    /*
+	     * If it's not a multiple option list and select popups are
+	     * enabled, then don't use the checkbox/button method, and don't
+	     * put anything on the screen yet.
+	     */
+	    if (me->first_option ||
+		HTCurSelectGroupType == F_CHECKBOX_TYPE ||
+		LYSelectPopups == FALSE) {
+		if (HTCurSelectGroupType == F_CHECKBOX_TYPE ||
+		    LYSelectPopups == FALSE) {
+		    /*
+		     * Start a newline before each option.
+		     */
+		    LYEnsureSingleSpace(me);
+		} else {
+		    /*
+		     * Add option list designation character.
+		     */
+		    HText_appendCharacter(me->text, '[');
+		    me->in_word = YES;
+		}
+
+		/*
+		 * Inititialize.
+		 */
+		memset(&I, 0, sizeof(I));
+		I.name_cs = -1;
+		I.value_cs = current_char_set;
+
+		I.type = "OPTION";
+
+		if ((present && present[HTML_OPTION_SELECTED]) ||
+		    (me->first_option && LYSelectPopups == FALSE &&
+		     HTCurSelectGroupType == F_RADIO_TYPE))
+		    I.checked = YES;
+
+		if (present && present[HTML_OPTION_VALUE] &&
+		    value[HTML_OPTION_VALUE]) {
+		    /*
+		     * Convert any HTML entities or decimal escaping.  - FM
+		     */
+		    StrAllocCopy(I_value, value[HTML_OPTION_VALUE]);
+		    me->HiddenValue = TRUE;
+		    TRANSLATE_AND_UNESCAPE_ENTITIES6(&I_value,
+						     ATTR_CS_IN,
+						     ATTR_CS_IN,
+						     NO,
+						     me->UsePlainSpace, me->HiddenValue);
+		    I.value_cs = ATTR_CS_IN;
+		    me->HiddenValue = FALSE;
+
+		    I.value = I_value;
+		}
+
+		if (me->select_disabled ||
+		    (0 && present && present[HTML_OPTION_DISABLED])) {
+		    /* 2009/5/25 - suppress check for "disabled" attribute
+		     * for Debian #525934 -TD
+		     */
+		    I.disabled = YES;
+		}
+
+		if (present && present[HTML_OPTION_ID]
+		    && non_empty(value[HTML_OPTION_ID])) {
+		    if ((ID_A = HTAnchor_findChildAndLink(me->node_anchor,	/* Parent */
+							  value[HTML_OPTION_ID],	/* Tag */
+							  NULL,		/* Addresss */
+							  0)) != NULL) {	/* Type */
+			HText_beginAnchor(me->text, me->inUnderline, ID_A);
+			HText_endAnchor(me->text, 0);
+			I.id = value[HTML_OPTION_ID];
+		    }
+		}
+
+		HText_beginInput(me->text, me->inUnderline, &I);
+
+		if (HTCurSelectGroupType == F_CHECKBOX_TYPE) {
+		    /*
+		     * Put a "[_]" placeholder, and one space (collapsible)
+		     * before the label that is expected to follow.  - FM
+		     */
+		    HText_appendCharacter(me->text, '[');
+		    HText_appendCharacter(me->text, '_');
+		    HText_appendCharacter(me->text, ']');
+		    HText_appendCharacter(me->text, ' ');
+		    HText_setLastChar(me->text, ' ');	/* absorb white space */
+		    me->in_word = NO;
+		} else if (LYSelectPopups == FALSE) {
+		    /*
+		     * Put a "(_)" placeholder, and one space (collapsible)
+		     * before the label that is expected to follow.  - FM
+		     */
+		    HText_appendCharacter(me->text, '(');
+		    HText_appendCharacter(me->text, '_');
+		    HText_appendCharacter(me->text, ')');
+		    HText_appendCharacter(me->text, ' ');
+		    HText_setLastChar(me->text, ' ');	/* absorb white space */
+		    me->in_word = NO;
+		}
+	    }
+
+	    /*
+	     * Get ready for the next value.
+	     */
+	    HTChunkClear(&me->option);
+	    if ((present && present[HTML_OPTION_SELECTED]) ||
+		(me->first_option && LYSelectPopups == FALSE &&
+		 HTCurSelectGroupType == F_RADIO_TYPE))
+		me->LastOptionChecked = TRUE;
+	    else
+		me->LastOptionChecked = FALSE;
+	    me->first_option = FALSE;
+
+	    if (present && present[HTML_OPTION_VALUE] &&
+		value[HTML_OPTION_VALUE]) {
+		if (!I_value) {
+		    /*
+		     * Convert any HTML entities or decimal escaping.  - FM
+		     */
+		    StrAllocCopy(I_value, value[HTML_OPTION_VALUE]);
+		    me->HiddenValue = TRUE;
+		    TRANSLATE_AND_UNESCAPE_ENTITIES6(&I_value,
+						     ATTR_CS_IN,
+						     ATTR_CS_IN,
+						     NO,
+						     me->UsePlainSpace, me->HiddenValue);
+		    me->HiddenValue = FALSE;
+		}
+		StrAllocCopy(me->LastOptionValue, I_value);
+	    } else {
+		StrAllocCopy(me->LastOptionValue, me->option.data);
+	    }
+
+	    /*
+	     * If this is a popup option, print its option for use in selecting
+	     * option by number.  - LE
+	     */
+	    if (HTCurSelectGroupType == F_RADIO_TYPE &&
+		LYSelectPopups &&
+		fields_are_numbered()) {
+		char marker[8];
+		int opnum = HText_getOptionNum(me->text);
+
+		if (opnum > 0 && opnum < 100000) {
+		    sprintf(marker, "(%d)", opnum);
+		    HTML_put_string(me, marker);
+		    for (i = (int) strlen(marker); i < 5; ++i) {
+			HTML_put_character(me, '_');
+		    }
+		}
+	    }
+	    FREE(I_value);
+	}
+	break;
+
+    case HTML_TABLE:
+	/*
+	 * Not fully implemented.  Just treat as a division with respect to any
+	 * ALIGN attribute, with a default of HT_LEFT, or leave as a PRE block
+	 * if we are presently in one.  - FM
+	 *
+	 * Also notify simple table tracking code unless in a preformatted
+	 * section, or (currently) non-left alignment.
+	 *
+	 * If page author is using a TABLE within PRE, it's probably formatted
+	 * specifically to work well for Lynx without simple table tracking
+	 * code.  Cancel tracking, it would only make things worse.  - kw
+	 */
+#ifdef EXP_NESTED_TABLES
+	if (!nested_tables)
+#endif
+	    HText_cancelStbl(me->text);
+
+	if (me->inA) {
+	    SET_SKIP_STACK(HTML_A);
+	    HTML_end_element(me, HTML_A, include);
+	}
+	if (me->Underline_Level > 0) {
+	    SET_SKIP_STACK(HTML_U);
+	    HTML_end_element(me, HTML_U, include);
+	}
+	me->inTABLE = TRUE;
+	if (me->sp->style->id == ST_Preformatted) {
+	    UPDATE_STYLE;
+	    CHECK_ID(HTML_TABLE_ID);
+	    break;
+	}
+	if (me->Division_Level < (MAX_NESTING - 1)) {
+	    me->Division_Level++;
+	} else {
+	    CTRACE((tfp,
+		    "HTML: ****** Maximum nesting of %d divisions/tables exceeded!\n",
+		    MAX_NESTING));
+	}
+	if (present && present[HTML_TABLE_ALIGN] &&
+	    non_empty(value[HTML_TABLE_ALIGN])) {
+	    if (!strcasecomp(value[HTML_TABLE_ALIGN], "center")) {
+		if (no_table_center) {
+		    me->DivisionAlignments[me->Division_Level] = HT_LEFT;
+		    change_paragraph_style(me, styles[HTML_DLEFT]);
+		    UPDATE_STYLE;
+		    me->current_default_alignment =
+			styles[HTML_DLEFT]->alignment;
+		} else {
+		    me->DivisionAlignments[me->Division_Level] = HT_CENTER;
+		    change_paragraph_style(me, styles[HTML_DCENTER]);
+		    UPDATE_STYLE;
+		    me->current_default_alignment =
+			styles[HTML_DCENTER]->alignment;
+		}
+
+		stbl_align = HT_CENTER;
+
+	    } else if (!strcasecomp(value[HTML_TABLE_ALIGN], "right")) {
+		me->DivisionAlignments[me->Division_Level] = HT_RIGHT;
+		change_paragraph_style(me, styles[HTML_DRIGHT]);
+		UPDATE_STYLE;
+		me->current_default_alignment = styles[HTML_DRIGHT]->alignment;
+		stbl_align = HT_RIGHT;
+	    } else {
+		me->DivisionAlignments[me->Division_Level] = HT_LEFT;
+		change_paragraph_style(me, styles[HTML_DLEFT]);
+		UPDATE_STYLE;
+		me->current_default_alignment = styles[HTML_DLEFT]->alignment;
+		if (!strcasecomp(value[HTML_TABLE_ALIGN], "left") ||
+		    !strcasecomp(value[HTML_TABLE_ALIGN], "justify"))
+		    stbl_align = HT_LEFT;
+	    }
+	} else {
+	    me->DivisionAlignments[me->Division_Level] = HT_LEFT;
+	    change_paragraph_style(me, styles[HTML_DLEFT]);
+	    UPDATE_STYLE;
+	    me->current_default_alignment = styles[HTML_DLEFT]->alignment;
+	    /* stbl_align remains HT_ALIGN_NONE */
+	}
+	CHECK_ID(HTML_TABLE_ID);
+	HText_startStblTABLE(me->text, stbl_align);
+	break;
+
+    case HTML_TR:
+	/*
+	 * Not fully implemented.  Just start a new row, if needed, act on an
+	 * ALIGN attribute if present, and check for an ID link.  - FM
+	 * Also notify simple table tracking code.  - kw
+	 */
+	if (me->inA) {
+	    SET_SKIP_STACK(HTML_A);
+	    HTML_end_element(me, HTML_A, include);
+	}
+	if (me->Underline_Level > 0) {
+	    SET_SKIP_STACK(HTML_U);
+	    HTML_end_element(me, HTML_U, include);
+	}
+	UPDATE_STYLE;
+	if (!HText_LastLineEmpty(me->text, FALSE)) {
+	    HText_setLastChar(me->text, ' ');	/* absorb white space */
+	    HText_appendCharacter(me->text, '\r');
+	}
+	me->in_word = NO;
+
+	if (me->sp->style->id == ST_Preformatted) {
+	    CHECK_ID(HTML_TR_ID);
+	    me->inP = FALSE;
+/*	    HText_cancelStbl(me->text);  seems unnecessary here - kw */
+	    break;
+	}
+	if (LYoverride_default_alignment(me)) {
+	    me->sp->style->alignment = styles[me->sp[0].tag_number]->alignment;
+	} else if (me->List_Nesting_Level >= 0 ||
+		   ((me->Division_Level < 0) &&
+		    (me->sp->style->id == ST_Normal ||
+		     me->sp->style->id == ST_Preformatted))) {
+	    me->sp->style->alignment = HT_LEFT;
+	} else {
+	    me->sp->style->alignment = (short) me->current_default_alignment;
+	}
+	if (present && present[HTML_TR_ALIGN] && value[HTML_TR_ALIGN]) {
+	    if (!strcasecomp(value[HTML_TR_ALIGN], "center") &&
+		!(me->List_Nesting_Level >= 0 && !me->inP)) {
+		if (no_table_center)
+		    me->sp->style->alignment = HT_LEFT;
+		else
+		    me->sp->style->alignment = HT_CENTER;
+		stbl_align = HT_CENTER;
+	    } else if (!strcasecomp(value[HTML_TR_ALIGN], "right") &&
+		       !(me->List_Nesting_Level >= 0 && !me->inP)) {
+		me->sp->style->alignment = HT_RIGHT;
+		stbl_align = HT_RIGHT;
+	    } else if (!strcasecomp(value[HTML_TR_ALIGN], "left") ||
+		       !strcasecomp(value[HTML_TR_ALIGN], "justify")) {
+		me->sp->style->alignment = HT_LEFT;
+		stbl_align = HT_LEFT;
+	    }
+	}
+
+	CHECK_ID(HTML_TR_ID);
+	me->inP = FALSE;
+	HText_startStblTR(me->text, stbl_align);
+	break;
+
+    case HTML_THEAD:
+    case HTML_TFOOT:
+    case HTML_TBODY:
+	HText_endStblTR(me->text);
+	/*
+	 * Not fully implemented.  Just check for an ID link.  - FM
+	 */
+	if (me->inA) {
+	    SET_SKIP_STACK(HTML_A);
+	    HTML_end_element(me, HTML_A, include);
+	}
+	if (me->Underline_Level > 0) {
+	    SET_SKIP_STACK(HTML_U);
+	    HTML_end_element(me, HTML_U, include);
+	}
+	UPDATE_STYLE;
+	if (me->inTABLE) {
+	    if (present && present[HTML_TR_ALIGN] && value[HTML_TR_ALIGN]) {
+		if (!strcasecomp(value[HTML_TR_ALIGN], "center")) {
+		    stbl_align = HT_CENTER;
+		} else if (!strcasecomp(value[HTML_TR_ALIGN], "right")) {
+		    stbl_align = HT_RIGHT;
+		} else if (!strcasecomp(value[HTML_TR_ALIGN], "left") ||
+			   !strcasecomp(value[HTML_TR_ALIGN], "justify")) {
+		    stbl_align = HT_LEFT;
+		}
+	    }
+	    HText_startStblRowGroup(me->text, stbl_align);
+	}
+	CHECK_ID(HTML_TR_ID);
+	break;
+
+    case HTML_COL:
+    case HTML_COLGROUP:
+	/*
+	 * Not fully implemented.  Just check for an ID link.  - FM
+	 */
+	if (me->inA) {
+	    SET_SKIP_STACK(HTML_A);
+	    HTML_end_element(me, HTML_A, include);
+	}
+	if (me->Underline_Level > 0) {
+	    SET_SKIP_STACK(HTML_U);
+	    HTML_end_element(me, HTML_U, include);
+	}
+	UPDATE_STYLE;
+	if (me->inTABLE) {
+	    int span = 1;
+
+	    if (present && present[HTML_COL_SPAN] &&
+		value[HTML_COL_SPAN] &&
+		isdigit(UCH(*value[HTML_COL_SPAN])))
+		span = atoi(value[HTML_COL_SPAN]);
+	    if (present && present[HTML_COL_ALIGN] && value[HTML_COL_ALIGN]) {
+		if (!strcasecomp(value[HTML_COL_ALIGN], "center")) {
+		    stbl_align = HT_CENTER;
+		} else if (!strcasecomp(value[HTML_COL_ALIGN], "right")) {
+		    stbl_align = HT_RIGHT;
+		} else if (!strcasecomp(value[HTML_COL_ALIGN], "left") ||
+			   !strcasecomp(value[HTML_COL_ALIGN], "justify")) {
+		    stbl_align = HT_LEFT;
+		}
+	    }
+	    HText_startStblCOL(me->text, span, stbl_align,
+			       (BOOL) (ElementNumber == HTML_COLGROUP));
+	}
+	CHECK_ID(HTML_COL_ID);
+	break;
+
+    case HTML_TH:
+    case HTML_TD:
+	if (me->inA) {
+	    SET_SKIP_STACK(HTML_A);
+	    HTML_end_element(me, HTML_A, include);
+	}
+	if (me->Underline_Level > 0) {
+	    SET_SKIP_STACK(HTML_U);
+	    HTML_end_element(me, HTML_U, include);
+	}
+	UPDATE_STYLE;
+	CHECK_ID(HTML_TD_ID);
+	/*
+	 * Not fully implemented.  Just add a collapsible space and break - FM
+	 * Also notify simple table tracking code.  - kw
+	 */
+	HTML_put_character(me, ' ');
+	{
+	    int colspan = 1, rowspan = 1;
+
+	    if (present && present[HTML_TD_COLSPAN] &&
+		value[HTML_TD_COLSPAN] &&
+		isdigit(UCH(*value[HTML_TD_COLSPAN])))
+		colspan = atoi(value[HTML_TD_COLSPAN]);
+	    if (present && present[HTML_TD_ROWSPAN] &&
+		value[HTML_TD_ROWSPAN] &&
+		isdigit(UCH(*value[HTML_TD_ROWSPAN])))
+		rowspan = atoi(value[HTML_TD_ROWSPAN]);
+	    if (present && present[HTML_TD_ALIGN] && value[HTML_TD_ALIGN]) {
+		if (!strcasecomp(value[HTML_TD_ALIGN], "center")) {
+		    stbl_align = HT_CENTER;
+		} else if (!strcasecomp(value[HTML_TD_ALIGN], "right")) {
+		    stbl_align = HT_RIGHT;
+		} else if (!strcasecomp(value[HTML_TD_ALIGN], "left") ||
+			   !strcasecomp(value[HTML_TD_ALIGN], "justify")) {
+		    stbl_align = HT_LEFT;
+		}
+	    }
+	    HText_startStblTD(me->text, colspan, rowspan, stbl_align,
+			      (BOOL) (ElementNumber == HTML_TH));
+	}
+	me->in_word = NO;
+	break;
+
+    case HTML_MATH:
+	/*
+	 * We're getting it as Literal text, which, until we can process it,
+	 * we'll display as is, within brackets to alert the user.  - FM
+	 */
+	HTChunkClear(&me->math);
+	CHECK_ID(HTML_GEN_ID);
+	break;
+
+    default:
+	break;
+
+    }				/* end switch */
+
+    if (ElementNumber >= HTML_ELEMENTS ||
+	HTML_dtd.tags[ElementNumber].contents != SGML_EMPTY) {
+	if (me->skip_stack > 0) {
+	    CTRACE((tfp,
+		    "HTML:begin_element: internal call (level %d), leaving on stack - `%s'\n",
+		    me->skip_stack, NONNULL(GetHTStyleName(me->sp->style))));
+	    me->skip_stack--;
+	    return status;
+	}
+	if (me->sp == me->stack) {
+	    if (me->stack_overrun == FALSE) {
+		HTAlert(HTML_STACK_OVERRUN);
+		CTRACE((tfp,
+			"HTML: ****** Maximum nesting of %d tags exceeded!\n",
+			MAX_NESTING));
+		me->stack_overrun = TRUE;
+	    }
+	    return HT_ERROR;
+	}
+
+	CTRACE((tfp,
+		"HTML:begin_element[%d]: adding style to stack - %s (%s)\n",
+		(int) STACKLEVEL(me),
+		NONNULL(GetHTStyleName(me->new_style)),
+		HTML_dtd.tags[ElementNumber].name));
+	(me->sp)--;
+	me->sp[0].style = me->new_style;	/* Stack new style */
+	me->sp[0].tag_number = ElementNumber;
+#ifdef USE_JUSTIFY_ELTS
+	if (wait_for_this_stacked_elt < 0 &&
+	    HTML_dtd.tags[ElementNumber].can_justify == FALSE)
+	    wait_for_this_stacked_elt = me->stack - me->sp + MAX_NESTING;
+#endif
+    }
+#ifdef USE_JUSTIFY_ELTS
+    if (in_DT && ElementNumber == HTML_DD)
+	in_DT = FALSE;
+    else if (ElementNumber == HTML_DT)
+	in_DT = TRUE;
+#endif
+
+#if defined(USE_COLOR_STYLE)
+/* end really empty tags straight away */
+
+    if (ReallyEmptyTagNum(element_number)) {
+	CTRACE2(TRACE_STYLE,
+		(tfp, "STYLE.begin_element:ending \"EMPTY\" element style\n"));
+	HText_characterStyle(me->text, HCODE_TO_STACK_OFF(hcode), STACK_OFF);
+
+#  if !OMIT_SCN_KEEPING
+	FastTrimColorClass(HTML_dtd.tags[element_number].name,
+			   HTML_dtd.tags[element_number].name_len,
+			   Style_className,
+			   &Style_className_end, &hcode);
+#  endif
+    }
+#endif /* USE_COLOR_STYLE */
+    return status;
+}
+
+/*		End Element
+ *		-----------
+ *
+ *	When we end an element, the style must be returned to that
+ *	in effect before that element.	Note that anchors (etc?)
+ *	don't have an associated style, so that we must scan down the
+ *	stack for an element with a defined style. (In fact, the styles
+ *	should be linked to the whole stack not just the top one.)
+ *	TBL 921119
+ *
+ *	We don't turn on "CAREFUL" check because the parser produces
+ *	(internal code errors apart) good nesting.  The parser checks
+ *	incoming code errors, not this module.
+ */
+static int HTML_end_element(HTStructured * me, int element_number,
+			    char **include)
+{
+    int i = 0;
+    int status = HT_OK;
+    char *temp = NULL, *cp = NULL;
+    BOOL BreakFlag = FALSE;
+    BOOL intern_flag = FALSE;
+
+#ifdef USE_COLOR_STYLE
+    BOOL skip_stack_requested = FALSE;
+#endif
+    EMIT_IFDEF_USE_JUSTIFY_ELTS(BOOL reached_awaited_stacked_elt = FALSE);
+
+#ifdef USE_PRETTYSRC
+    if (psrc_view && !sgml_in_psrc_was_initialized) {
+	if (!psrc_nested_call) {
+	    HTTag *tag = &HTML_dtd.tags[element_number];
+	    char buf[200];
+	    int tag_charset = 0;
+
+	    psrc_nested_call = TRUE;
+	    PSRCSTART(abracket);
+	    PUTS("</");
+	    PSRCSTOP(abracket);
+	    PSRCSTART(tag);
+	    if (tagname_transform != 0)
+		PUTS(tag->name);
+	    else {
+		LYstrncpy(buf, tag->name, sizeof(buf) - 1);
+		LYLowerCase(buf);
+		PUTS(buf);
+	    }
+	    PSRCSTOP(tag);
+	    PSRCSTART(abracket);
+	    PUTC('>');
+	    PSRCSTOP(abracket);
+	    psrc_nested_call = FALSE;
+	    return HT_OK;
+	}
+	/*fall through */
+    }
+#endif
+
+    if ((me->sp >= (me->stack + MAX_NESTING - 1) ||
+	 element_number != me->sp[0].tag_number) &&
+	HTML_dtd.tags[element_number].contents != SGML_EMPTY) {
+	CTRACE((tfp,
+		"HTML: end of element %s when expecting end of %s\n",
+		HTML_dtd.tags[element_number].name,
+		(me->sp == me->stack + MAX_NESTING - 1) ? "none" :
+		(me->sp->tag_number < 0) ? "*invalid tag*" :
+		(me->sp->tag_number >= HTML_ELEMENTS) ? "special tag" :
+		HTML_dtd.tags[me->sp->tag_number].name));
+#ifdef CAREFUL			/* parser assumed to produce good nesting */
+	/* panic */
+#endif /* CAREFUL */
+    }
+
+    /*
+     * If we're seeking MAPs, skip everything that's not a MAP or AREA tag.  -
+     * FM
+     */
+    if (LYMapsOnly) {
+	if (!(element_number == HTML_MAP || element_number == HTML_AREA ||
+	      element_number == HTML_OBJECT)) {
+	    return HT_OK;
+	}
+    }
+
+    /*
+     * Pop state off stack if we didn't declare the element SGML_EMPTY in
+     * HTMLDTD.c.  - FM & KW
+     */
+    if (HTML_dtd.tags[element_number].contents != SGML_EMPTY) {
+#ifdef USE_COLOR_STYLE
+	skip_stack_requested = (BOOL) (me->skip_stack > 0);
+#endif
+	if ((element_number != me->sp[0].tag_number) &&
+	    me->skip_stack <= 0 &&
+	    HTML_dtd.tags[HTML_LH].contents != SGML_EMPTY &&
+	    (me->sp[0].tag_number == HTML_UL ||
+	     me->sp[0].tag_number == HTML_OL ||
+	     me->sp[0].tag_number == HTML_MENU ||
+	     me->sp[0].tag_number == HTML_DIR ||
+	     me->sp[0].tag_number == HTML_LI) &&
+	    (element_number == HTML_H1 ||
+	     element_number == HTML_H2 ||
+	     element_number == HTML_H3 ||
+	     element_number == HTML_H4 ||
+	     element_number == HTML_H5 ||
+	     element_number == HTML_H6)) {
+	    /*
+	     * Set the break flag if we're popping a dummy HTML_LH substituted
+	     * for an HTML_H# encountered in a list.
+	     */
+	    BreakFlag = TRUE;
+	}
+	if (me->skip_stack == 0 && element_number == HTML_OBJECT &&
+	    me->sp[0].tag_number == HTML_OBJECT_M &&
+	    (me->sp < (me->stack + MAX_NESTING - 1)))
+	    me->sp[0].tag_number = HTML_OBJECT;
+	if (me->skip_stack > 0) {
+	    CTRACE2(TRACE_STYLE,
+		    (tfp,
+		     "HTML:end_element: Internal call (level %d), leaving on stack - %s\n",
+		     me->skip_stack, NONNULL(GetHTStyleName(me->sp->style))));
+	    me->skip_stack--;
+	} else if (element_number == HTML_OBJECT &&
+		   me->sp[0].tag_number != HTML_OBJECT &&
+		   me->sp[0].tag_number != HTML_OBJECT_M &&
+		   me->objects_mixed_open > 0 &&
+		   !(me->objects_figged_open > 0 &&
+		     me->sp[0].tag_number == HTML_FIG)) {
+	    /*
+	     * Ignore non-corresponding OBJECT tags that we didn't push because
+	     * the SGML parser was supposed to go on parsing the contents
+	     * non-literally.  - kw
+	     */
+	    CTRACE2(TRACE_STYLE,
+		    (tfp, "HTML:end_element[%d]: %s (level %d), %s - %s\n",
+		     (int) STACKLEVEL(me),
+		     "Special OBJECT handling", me->objects_mixed_open,
+		     "leaving on stack",
+		     NONNULL(GetHTStyleName(me->sp->style))));
+	    me->objects_mixed_open--;
+	} else if (me->stack_overrun == TRUE &&
+		   element_number != me->sp[0].tag_number) {
+	    /*
+	     * Ignore non-corresponding tags if we had a stack overrun.  This
+	     * is not a completely fail-safe strategy for protection against
+	     * any seriously adverse consequences of a stack overrun, and the
+	     * rendering of the document will not be as intended, but we expect
+	     * overruns to be rare, and this should offer reasonable protection
+	     * against crashes if an overrun does occur.  - FM
+	     */
+	    return HT_OK;	/* let's pretend... */
+	} else if (element_number == HTML_SELECT &&
+		   me->sp[0].tag_number != HTML_SELECT) {
+	    /*
+	     * Ignore non-corresponding SELECT tags, since we probably popped
+	     * it and closed the SELECT block to deal with markup which amounts
+	     * to a nested SELECT, or an out of order FORM end tag.  - FM
+	     */
+	    return HT_OK;
+	} else if ((element_number != me->sp[0].tag_number) &&
+		   HTML_dtd.tags[HTML_LH].contents == SGML_EMPTY &&
+		   (me->sp[0].tag_number == HTML_UL ||
+		    me->sp[0].tag_number == HTML_OL ||
+		    me->sp[0].tag_number == HTML_MENU ||
+		    me->sp[0].tag_number == HTML_DIR ||
+		    me->sp[0].tag_number == HTML_LI) &&
+		   (element_number == HTML_H1 ||
+		    element_number == HTML_H2 ||
+		    element_number == HTML_H3 ||
+		    element_number == HTML_H4 ||
+		    element_number == HTML_H5 ||
+		    element_number == HTML_H6)) {
+	    /*
+	     * It's an H# for which we substituted an HTML_LH, which we've
+	     * declared as SGML_EMPTY, so just return.  - FM
+	     */
+	    return HT_OK;
+	} else if (me->sp < (me->stack + MAX_NESTING - 1)) {
+#ifdef USE_JUSTIFY_ELTS
+	    if (wait_for_this_stacked_elt == me->stack - me->sp + MAX_NESTING)
+		reached_awaited_stacked_elt = TRUE;
+#endif
+	    if (element_number == HTML_OBJECT) {
+		if (me->sp[0].tag_number == HTML_FIG &&
+		    me->objects_figged_open > 0) {
+		    /*
+		     * It's an OBJECT for which we substituted a FIG, so pop
+		     * the FIG and pretend that's what we are being called for. 
+		     * - kw
+		     */
+		    CTRACE2(TRACE_STYLE,
+			    (tfp,
+			     "HTML:end_element[%d]: %s (level %d), %s - %s\n",
+			     (int) STACKLEVEL(me),
+			     "Special OBJECT->FIG handling",
+			     me->objects_figged_open,
+			     "treating as end FIG",
+			     NONNULL(GetHTStyleName(me->sp->style))));
+		    me->objects_figged_open--;
+		    element_number = HTML_FIG;
+		}
+	    }
+	    (me->sp)++;
+	    CTRACE2(TRACE_STYLE,
+		    (tfp,
+		     "HTML:end_element[%d]: Popped style off stack - %s\n",
+		     (int) STACKLEVEL(me),
+		     NONNULL(GetHTStyleName(me->sp->style))));
+	} else {
+	    CTRACE2(TRACE_STYLE, (tfp,
+				  "Stack underflow error!  Tried to pop off more styles than exist in stack\n"));
+	}
+    }
+    if (BreakFlag == TRUE) {
+#ifdef USE_JUSTIFY_ELTS
+	if (reached_awaited_stacked_elt)
+	    wait_for_this_stacked_elt = -1;
+#endif
+	return HT_OK;		/* let's pretend... */
+    }
+
+    /*
+     * Check for unclosed TEXTAREA.  - FM
+     */
+    if (me->inTEXTAREA && element_number != HTML_TEXTAREA) {
+	if (LYBadHTML(me)) {
+	    LYShowBadHTML("Bad HTML: Missing TEXTAREA end tag\n");
+	}
+    }
+
+    if (!me->text && !LYMapsOnly) {
+	UPDATE_STYLE;
+    }
+
+    /*
+     * Handle the end tag.  - FM
+     */
+    switch (element_number) {
+
+    case HTML_HTML:
+	if (me->inA || me->inSELECT || me->inTEXTAREA) {
+	    if (LYBadHTML(me)) {
+		char *msg = NULL;
+
+		HTSprintf0(&msg,
+			   "Bad HTML: %s%s%s%s%s not closed before HTML end tag *****\n",
+			   me->inSELECT ? "SELECT" : "",
+			   (me->inSELECT && me->inTEXTAREA) ? ", " : "",
+			   me->inTEXTAREA ? "TEXTAREA" : "",
+			   (((me->inSELECT || me->inTEXTAREA) && me->inA)
+			    ? ", "
+			    : ""),
+			   me->inA ? "A" : "");
+		LYShowBadHTML(msg);
+		FREE(msg);
+	    }
+	}
+	break;
+
+    case HTML_HEAD:
+	if (me->inBASE &&
+	    (LYIsUIPage3(me->node_anchor->address, UIP_LIST_PAGE, 0) ||
+	     LYIsUIPage3(me->node_anchor->address, UIP_ADDRLIST_PAGE, 0))) {
+	    /* If we are parsing the List Page, and have a BASE after we are
+	     * done with the HEAD element, propagate it back to the node_anchor
+	     * object.  The base should have been inserted by showlist() to
+	     * record what document the List Page is about, and other functions
+	     * may later look for it in the anchor.  - kw
+	     */
+	    StrAllocCopy(me->node_anchor->content_base, me->base_href);
+	}
+	if (HText_hasToolbar(me->text))
+	    HText_appendParagraph(me->text);
+	break;
+
+    case HTML_TITLE:
+	HTChunkTerminate(&me->title);
+	HTAnchor_setTitle(me->node_anchor, me->title.data);
+	HTChunkClear(&me->title);
+	/*
+	 * Check if it's a bookmark file, and if so, and multiple bookmark
+	 * support is on, or it's off but this isn't the default bookmark file
+	 * (e.g., because it was on before, and this is another bookmark file
+	 * that has been retrieved as a previous document), insert the current
+	 * description string and filepath for it.  We pass the strings back to
+	 * the SGML parser so that any 8 bit or multibyte/CJK characters will
+	 * be handled by the parser's state and charset routines.  - FM
+	 */
+	if (non_empty(me->node_anchor->bookmark)) {
+	    if ((LYMultiBookmarks != MBM_OFF) ||
+		(non_empty(bookmark_page) &&
+		 strcmp(me->node_anchor->bookmark, bookmark_page))) {
+		if (!include)
+		    include = &me->xinclude;
+		for (i = 0; i <= MBM_V_MAXFILES; i++) {
+		    if (MBM_A_subbookmark[i] &&
+			!strcmp(MBM_A_subbookmark[i],
+				me->node_anchor->bookmark)) {
+			StrAllocCat(*include, "<H2><EM>");
+			StrAllocCat(*include, gettext("Description:"));
+			StrAllocCat(*include, "</EM> ");
+			StrAllocCopy(temp,
+				     ((MBM_A_subdescript[i] &&
+				       *MBM_A_subdescript[i]) ?
+				      MBM_A_subdescript[i] : gettext("(none)")));
+			LYEntify(&temp, TRUE);
+			StrAllocCat(*include, temp);
+			StrAllocCat(*include, "<BR><EM>&nbsp;&nbsp;&nbsp;");
+			StrAllocCat(*include, gettext("Filepath:"));
+			StrAllocCat(*include, "</EM> ");
+			StrAllocCopy(temp,
+				     ((MBM_A_subbookmark[i] &&
+				       *MBM_A_subbookmark[i])
+				      ? MBM_A_subbookmark[i]
+				      : gettext("(unknown)")));
+			LYEntify(&temp, TRUE);
+			StrAllocCat(*include, temp);
+			FREE(temp);
+			StrAllocCat(*include, "</H2>");
+			break;
+		    }
+		}
+	    }
+	}
+	break;
+
+    case HTML_STYLE:
+	/*
+	 * We're getting it as Literal text, which, for now, we'll just ignore. 
+	 * - FM
+	 */
+	HTChunkTerminate(&me->style_block);
+	CTRACE2(TRACE_STYLE,
+		(tfp, "HTML: STYLE content =\n%s\n",
+		 me->style_block.data));
+	HTChunkClear(&me->style_block);
+	break;
+
+    case HTML_SCRIPT:
+	/*
+	 * We're getting it as Literal text, which, for now, we'll just ignore. 
+	 * - FM
+	 */
+	HTChunkTerminate(&me->script);
+	CTRACE((tfp, "HTML: SCRIPT content =\n%s\n",
+		me->script.data));
+	HTChunkClear(&me->script);
+	break;
+
+    case HTML_BODY:
+	if (me->inA || me->inSELECT || me->inTEXTAREA) {
+	    if (LYBadHTML(me)) {
+		char *msg = NULL;
+
+		HTSprintf0(&msg,
+			   "Bad HTML: %s%s%s%s%s not closed before BODY end tag *****\n",
+			   me->inSELECT ? "SELECT" : "",
+			   (me->inSELECT && me->inTEXTAREA) ? ", " : "",
+			   me->inTEXTAREA ? "TEXTAREA" : "",
+			   (((me->inSELECT || me->inTEXTAREA) && me->inA)
+			    ? ", "
+			    : ""),
+			   me->inA ? "A" : "");
+		LYShowBadHTML(msg);
+		FREE(msg);
+	    }
+	}
+	break;
+
+    case HTML_FRAMESET:
+	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
+	break;
+
+    case HTML_NOFRAMES:
+    case HTML_IFRAME:
+	LYEnsureDoubleSpace(me);
+	LYResetParagraphAlignment(me);
+	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
+	break;
+
+    case HTML_BANNER:
+    case HTML_MARQUEE:
+    case HTML_BLOCKQUOTE:
+    case HTML_BQ:
+    case HTML_ADDRESS:
+	/*
+	 * Set flag to know that style has ended.  Fall through.
+	 i_prior_style = -1;
+	 */
+	change_paragraph_style(me, me->sp->style);
+	UPDATE_STYLE;
+	if (me->sp->tag_number == element_number)
+	    LYEnsureDoubleSpace(me);
+	if (me->List_Nesting_Level >= 0)
+	    HText_NegateLineOne(me->text);
+	break;
+
+    case HTML_CENTER:
+    case HTML_DIV:
+	if (me->Division_Level >= 0)
+	    me->Division_Level--;
+	if (me->Division_Level >= 0) {
+	    if (me->sp->style->alignment !=
+		me->DivisionAlignments[me->Division_Level]) {
+		if (me->inP)
+		    LYEnsureSingleSpace(me);
+		me->sp->style->alignment =
+		    me->DivisionAlignments[me->Division_Level];
+	    }
+	}
+	change_paragraph_style(me, me->sp->style);
+	if (me->style_change) {
+	    actually_set_style(me);
+	    if (me->List_Nesting_Level >= 0)
+		HText_NegateLineOne(me->text);
+	} else if (me->inP)
+	    LYEnsureSingleSpace(me);
+	me->current_default_alignment = me->sp->style->alignment;
+	break;
+
+    case HTML_H1:		/* header styles */
+    case HTML_H2:
+    case HTML_H3:
+    case HTML_H4:
+    case HTML_H5:
+    case HTML_H6:
+	if (me->Division_Level >= 0) {
+	    me->sp->style->alignment =
+		me->DivisionAlignments[me->Division_Level];
+	} else if (me->sp->style->id == ST_HeadingCenter ||
+		   me->sp->style->id == ST_Heading1) {
+	    me->sp->style->alignment = HT_CENTER;
+	} else if (me->sp->style->id == ST_HeadingRight) {
+	    me->sp->style->alignment = HT_RIGHT;
+	} else {
+	    me->sp->style->alignment = HT_LEFT;
+	}
+	change_paragraph_style(me, me->sp->style);
+	UPDATE_STYLE;
+	if (styles[element_number]->font & HT_BOLD) {
+	    if (me->inBoldA == FALSE && me->inBoldH == TRUE) {
+		HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+	    }
+	    me->inBoldH = FALSE;
+	}
+	if (me->List_Nesting_Level >= 0)
+	    HText_NegateLineOne(me->text);
+	if (me->Underline_Level > 0 && me->inUnderline == FALSE) {
+	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
+	    me->inUnderline = TRUE;
+	}
+	break;
+
+    case HTML_P:
+	LYHandlePlike(me,
+		      (const BOOL *) 0, (const char **) 0,
+		      include, 0,
+		      FALSE);
+	break;
+
+    case HTML_FONT:
+	me->inFONT = FALSE;
+	break;
+
+    case HTML_B:		/* Physical character highlighting */
+    case HTML_BLINK:
+    case HTML_I:
+    case HTML_U:
+
+    case HTML_CITE:		/* Logical character highlighting */
+    case HTML_EM:
+    case HTML_STRONG:
+	/*
+	 * Ignore any emphasis end tags if the Underline_Level is not set.  -
+	 * FM
+	 */
+	if (me->Underline_Level <= 0)
+	    break;
+
+	/*
+	 * Adjust the Underline level counter, and turn off underlining if
+	 * appropriate.  - FM
+	 */
+	me->Underline_Level--;
+	if (me->inUnderline && me->Underline_Level < 1) {
+	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
+	    me->inUnderline = FALSE;
+	    CTRACE((tfp, "Ending underline\n"));
+	} else {
+	    CTRACE((tfp, "Underline Level is %d\n", me->Underline_Level));
+	}
+	break;
+
+    case HTML_ABBR:		/* Miscellaneous character containers */
+    case HTML_ACRONYM:
+    case HTML_AU:
+    case HTML_AUTHOR:
+    case HTML_BIG:
+    case HTML_CODE:
+    case HTML_DFN:
+    case HTML_KBD:
+    case HTML_SAMP:
+    case HTML_SMALL:
+    case HTML_SUP:
+    case HTML_TT:
+    case HTML_VAR:
+	break;
+
+    case HTML_SUB:
+	HText_appendCharacter(me->text, ']');
+	break;
+
+    case HTML_DEL:
+    case HTML_S:
+    case HTML_STRIKE:
+	HTML_put_character(me, ' ');
+	if (me->inUnderline == FALSE)
+	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
+	HTML_put_string(me, ":DEL]");
+	if (me->inUnderline == FALSE)
+	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
+	HTML_put_character(me, ' ');
+	me->in_word = NO;
+	break;
+
+    case HTML_INS:
+	HTML_put_character(me, ' ');
+	if (me->inUnderline == FALSE)
+	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
+	HTML_put_string(me, ":INS]");
+	if (me->inUnderline == FALSE)
+	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
+	HTML_put_character(me, ' ');
+	me->in_word = NO;
+	break;
+
+    case HTML_Q:
+	if (me->Quote_Level > 0)
+	    me->Quote_Level--;
+	/*
+	 * Should check LANG and/or DIR attributes, and the
+	 * me->node_anchor->charset and/or yet to be added structure elements,
+	 * to determine whether we should use chevrons, but for now we'll
+	 * always use double- or single-quotes.  - FM
+	 */
+	if (!(me->Quote_Level & 1))
+	    HTML_put_character(me, '"');
+	else
+	    HTML_put_character(me, '\'');
+	break;
+
+    case HTML_PRE:		/* Formatted text */
+	/*
+	 * Set to know that we are no longer in a PRE block.
+	 */
+	HText_appendCharacter(me->text, '\n');
+	me->inPRE = FALSE;
+	/* FALLTHRU */
+    case HTML_LISTING:		/* Literal text */
+	/* FALLTHRU */
+    case HTML_XMP:
+	/* FALLTHRU */
+    case HTML_PLAINTEXT:
+	if (me->comment_start)
+	    HText_appendText(me->text, me->comment_start);
+	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
+	if (me->List_Nesting_Level >= 0) {
+	    UPDATE_STYLE;
+	    HText_NegateLineOne(me->text);
+	}
+	break;
+
+    case HTML_NOTE:
+    case HTML_FN:
+	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
+	UPDATE_STYLE;
+	if (me->sp->tag_number == element_number)
+	    LYEnsureDoubleSpace(me);
+	if (me->List_Nesting_Level >= 0)
+	    HText_NegateLineOne(me->text);
+	me->inLABEL = FALSE;
+	break;
+
+    case HTML_OL:
+	me->OL_Counter[me->List_Nesting_Level < 11 ?
+		       me->List_Nesting_Level : 11] = OL_VOID;
+	/* FALLTHRU */
+    case HTML_DL:
+	/* FALLTHRU */
+    case HTML_UL:
+	/* FALLTHRU */
+    case HTML_MENU:
+	/* FALLTHRU */
+    case HTML_DIR:
+	me->List_Nesting_Level--;
+	CTRACE((tfp, "HTML_end_element: Reducing List Nesting Level to %d\n",
+		me->List_Nesting_Level));
+#ifdef USE_JUSTIFY_ELTS
+	if (element_number == HTML_DL)
+	    in_DT = FALSE;	/*close the term that was without definition. */
+#endif
+	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
+	UPDATE_STYLE;
+	if (me->List_Nesting_Level >= 0)
+	    LYEnsureSingleSpace(me);
+	break;
+
+    case HTML_SPAN:
+	/*
+	 * Should undo anything we did based on LANG and/or DIR attributes, and
+	 * the me->node_anchor->charset and/or yet to be added structure
+	 * elements.  - FM
+	 */
+	break;
+
+    case HTML_BDO:
+	/*
+	 * Should undo anything we did based on DIR (and/or LANG) attributes,
+	 * and the me->node_anchor->charset and/or yet to be added structure
+	 * elements.  - FM
+	 */
+	break;
+
+    case HTML_A:
+	/*
+	 * Ignore any spurious A end tags.  - FM
+	 */
+	if (me->inA == FALSE)
+	    break;
+	/*
+	 * Set to know that we are no longer in an anchor.
+	 */
+	me->inA = FALSE;
+#ifdef MARK_HIDDEN_LINKS
+	if (non_empty(hidden_link_marker) &&
+	    HText_isAnchorBlank(me->text, me->CurrentANum)) {
+	    HText_appendText(me->text, hidden_link_marker);
+	}
+#endif
+	UPDATE_STYLE;
+	if (me->inBoldA == TRUE && me->inBoldH == FALSE)
+	    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+	HText_endAnchor(me->text, me->CurrentANum);
+	me->CurrentANum = 0;
+	me->inBoldA = FALSE;
+	if (me->Underline_Level > 0 && me->inUnderline == FALSE) {
+	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
+	    me->inUnderline = TRUE;
+	}
+	break;
+
+    case HTML_MAP:
+	FREE(me->map_address);
+	break;
+
+    case HTML_BODYTEXT:
+	/*
+	 * We may need to look at this someday to deal with OBJECTs optimally,
+	 * but just ignore it for now.  - FM
+	 */
+	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
+	break;
+
+    case HTML_TEXTFLOW:
+	/*
+	 * We may need to look at this someday to deal with APPLETs optimally,
+	 * but just ignore it for now.  - FM
+	 */
+	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
+	break;
+
+    case HTML_FIG:
+	LYHandleFIG(me, NULL, NULL,
+		    0,
+		    0,
+		    NULL,
+		    NULL, NO, FALSE, &intern_flag);
+	break;
+
+    case HTML_OBJECT:
+	/*
+	 * Finish the data off.
+	 */
+	{
+	    int s = 0, e = 0;
+	    char *start = NULL, *first_end = NULL, *last_end = NULL;
+	    char *first_map = NULL, *last_map = NULL;
+	    BOOL have_param = FALSE;
+	    char *data = NULL;
+
+	    HTChunkTerminate(&me->object);
+	    data = me->object.data;
+	    while ((cp = strchr(data, '<')) != NULL) {
+		/*
+		 * Look for nested OBJECTs.  This procedure could get tripped
+		 * up if invalid comments are present in the content, or if an
+		 * OBJECT end tag is present in a quoted attribute.  - FM
+		 */
+		if (!strncmp(cp, "<!--", 4)) {
+		    data = LYFindEndOfComment(cp);
+		    cp = data;
+		} else if (s == 0 && !strncasecomp(cp, "<PARAM", 6) &&
+			   !IsNmChar(cp[6])) {
+		    have_param = TRUE;
+		} else if (!strncasecomp(cp, "<OBJECT", 7) &&
+			   !IsNmChar(cp[7])) {
+		    if (s == 0)
+			start = cp;
+		    s++;
+		} else if (!strncasecomp(cp, "</OBJECT", 8) &&
+			   !IsNmChar(cp[8])) {
+		    if (e == 0)
+			first_end = cp;
+		    last_end = cp;
+		    e++;
+		} else if (!strncasecomp(cp, "<MAP", 4) &&
+			   !IsNmChar(cp[4])) {
+		    if (!first_map)
+			first_map = cp;
+		    last_map = cp;
+		} else if (!strncasecomp(cp, "</MAP", 5) &&
+			   !IsNmChar(cp[5])) {
+		    last_map = cp;
+		}
+		data = ++cp;
+	    }
+	    if (s < e) {
+		/*
+		 * We had more end tags than start tags, so we have bad HTML or
+		 * otherwise misparsed.  - FM
+		 */
+		if (LYBadHTML(me)) {
+		    char *msg = NULL;
+
+		    HTSprintf0(&msg,
+			       "Bad HTML: Unmatched OBJECT start and end tags.  Discarding content:\n%s\n",
+			       me->object.data);
+		    LYShowBadHTML(msg);
+		    FREE(msg);
+		}
+		goto End_Object;
+	    }
+	    if (s > e) {
+		if (!me->object_declare && !me->object_name &&
+		    !(me->object_shapes && !LYMapsOnly) &&
+		    !(me->object_usemap != NULL && !LYMapsOnly) &&
+		    !(clickable_images && !LYMapsOnly &&
+		      me->object_data != NULL &&
+		      !have_param &&
+		      me->object_classid == NULL &&
+		      me->object_codebase == NULL &&
+		      me->object_codetype == NULL)) {
+		    /*
+		     * We have nested OBJECT tags, and not yet all of the end
+		     * tags, but have a case where the content needs to be
+		     * parsed again (not dropped) and where we don't want to
+		     * output anything special at the point when we
+		     * *do* have accumulated all the end tags.  So recycle
+		     * the incomplete contents now, and signal the SGML parser
+		     * that it should not regard the current OBJECT ended but
+		     * should treat its contents as mixed.  Normally these
+		     * cases would have already handled in the real
+		     * start_element call, so this block may not be necessary. 
+		     * - kw
+		     */
+		    CTRACE((tfp, "%s:\n%s\n",
+			    "HTML: Nested OBJECT tags.  Recycling incomplete contents",
+			    me->object.data));
+		    status = HT_PARSER_OTHER_CONTENT;
+		    me->object.size--;
+		    HTChunkPuts(&me->object, "</OBJECT>");
+		    if (!include)	/* error, should not happen */
+			include = &me->xinclude;
+		    StrnAllocCat(*include, me->object.data, me->object.size);
+		    clear_objectdata(me);
+		    /* an internal fake call to keep our stack happy: */
+		    HTML_start_element(me, HTML_OBJECT, NULL, NULL,
+				       me->tag_charset, include);
+		    break;
+		}
+		/*
+		 * We have nested OBJECT tags, and not yet all of the end tags,
+		 * and we want the end tags.  So restore an end tag to the
+		 * content, and signal to the SGML parser that it should resume
+		 * the accumulation of OBJECT content (after calling back to
+		 * start_element) in a way that is equivalent to passing it a
+		 * dummy start tag.  - FM, kw
+		 */
+		CTRACE((tfp, "HTML: Nested OBJECT tags.  Recycling.\n"));
+		status = HT_PARSER_REOPEN_ELT;
+		me->object.size--;
+		HTChunkPuts(&me->object, "</OBJECT>");
+		if (!LYMapsOnly)
+		    change_paragraph_style(me, me->sp->style);
+		break;
+	    }
+
+	    /*
+	     * OBJECT start and end tags are fully matched, assuming we weren't
+	     * tripped up by comments or quoted attributes.  - FM
+	     */
+	    CTRACE((tfp, "HTML:OBJECT content:\n%s\n", me->object.data));
+
+	    /*
+	     * OBJECTs with DECLARE should be saved but not instantiated, and
+	     * if nested, can have only other DECLAREd OBJECTs.  Until we have
+	     * code to handle these, we'll just create an anchor for the ID, if
+	     * present, and discard the content (sigh 8-).  - FM
+	     */
+	    if (me->object_declare == TRUE) {
+		if (non_empty(me->object_id) && !LYMapsOnly)
+		    LYHandleID(me, me->object_id);
+		CTRACE((tfp, "HTML: DECLAREd OBJECT.  Ignoring!\n"));
+		goto End_Object;
+	    }
+
+	    /*
+	     * OBJECTs with NAME are for FORM submissions.  We'll just create
+	     * an anchor for the ID, if present, and discard the content until
+	     * we have code to handle these.  (sigh 8-).  - FM
+	     */
+	    if (me->object_name != NULL && !LYMapsOnly) {
+		if (non_empty(me->object_id))
+		    LYHandleID(me, me->object_id);
+		CTRACE((tfp, "HTML: NAMEd OBJECT.  Ignoring!\n"));
+		goto End_Object;
+	    }
+
+	    /*
+	     * Deal with any nested OBJECTs by descending to the inner-most
+	     * OBJECT.  - FM
+	     */
+	    if (s > 0) {
+		if (start != NULL &&
+		    first_end != NULL && first_end > start) {
+		    /*
+		     * Minumum requirements for the ad hoc parsing to have
+		     * succeeded are met.  We'll hope that it did succeed.  -
+		     * FM
+		     */
+		    if (LYMapsOnly) {
+			/*
+			 * Well we don't need to do this any more, nested
+			 * objects should either not get here any more at all
+			 * or can be handled fine by other code below.  Leave
+			 * in place for now as a special case for LYMapsOnly. 
+			 * - kw
+			 */
+			if (LYMapsOnly && (!last_map || last_map < first_end))
+			    *first_end = '\0';
+			else
+			    e = 0;
+			data = NULL;
+			if (LYMapsOnly && (!first_map || first_map > start))
+			    StrAllocCopy(data, start);
+			else
+			    StrAllocCopy(data, me->object.data);
+			if (e > 0) {
+			    for (i = e; i > 0; i--) {
+				StrAllocCat(data, "</OBJECT>");
+			    }
+			}
+			if (!include)	/* error, should not happen */
+			    include = &me->xinclude;
+			StrAllocCat(*include, data);
+			CTRACE((tfp, "HTML: Recycling nested OBJECT%s.\n",
+				(s > 1) ? "s" : ""));
+			FREE(data);
+			goto End_Object;
+		    }
+		} else {
+		    if (LYBadHTML(me)) {
+			LYShowBadHTML("Bad HTML: Unmatched OBJECT start and end tags.  Discarding content.\n");
+		    }
+		    goto End_Object;
+		}
+	    }
+
+	    /*
+	     * If its content has SHAPES, convert it to FIG.  - FM
+	     *
+	     * This is now handled in our start_element without using include
+	     * if the SGML parser cooperates, so this block may be unnecessary. 
+	     * - kw
+	     */
+	    if (me->object_shapes == TRUE && !LYMapsOnly) {
+		CTRACE((tfp, "HTML: OBJECT has SHAPES.  Converting to FIG.\n"));
+		if (!include)	/* error, should not happen */
+		    include = &me->xinclude;
+		StrAllocCat(*include, "<FIG ISOBJECT IMAGEMAP");
+		if (me->object_ismap == TRUE)
+		    StrAllocCat(*include, " IMAGEMAP");
+		if (me->object_id != NULL) {
+		    StrAllocCat(*include, " ID=\"");
+		    StrAllocCat(*include, me->object_id);
+		    StrAllocCat(*include, "\"");
+		}
+		if (me->object_data != NULL &&
+		    me->object_classid == NULL) {
+		    StrAllocCat(*include, " SRC=\"");
+		    StrAllocCat(*include, me->object_data);
+		    StrAllocCat(*include, "\"");
+		}
+		StrAllocCat(*include, ">");
+		me->object.size--;
+		HTChunkPuts(&me->object, "</FIG>");
+		HTChunkTerminate(&me->object);
+		StrAllocCat(*include, me->object.data);
+		goto End_Object;
+	    }
+
+	    /*
+	     * If it has a USEMAP attribute and didn't have SHAPES, convert it
+	     * to IMG.  - FM
+	     */
+	    if (me->object_usemap != NULL && !LYMapsOnly) {
+		CTRACE((tfp, "HTML: OBJECT has USEMAP.  Converting to IMG.\n"));
+
+		if (!include)	/* error, should not happen */
+		    include = &me->xinclude;
+		StrAllocCat(*include, "<IMG ISOBJECT");
+		if (me->object_id != NULL) {
+		    /*
+		     * Pass the ID.  - FM
+		     */
+		    StrAllocCat(*include, " ID=\"");
+		    StrAllocCat(*include, me->object_id);
+		    StrAllocCat(*include, "\"");
+		}
+		if (me->object_data != NULL &&
+		    me->object_classid == NULL) {
+		    /*
+		     * We have DATA with no CLASSID, so let's hope it'
+		     * equivalent to an SRC.  - FM
+		     */
+		    StrAllocCat(*include, " SRC=\"");
+		    StrAllocCat(*include, me->object_data);
+		    StrAllocCat(*include, "\"");
+		}
+		if (me->object_title != NULL) {
+		    /*
+		     * Use the TITLE for both the MAP and the IMGs ALT.  - FM
+		     */
+		    StrAllocCat(*include, " TITLE=\"");
+		    StrAllocCat(*include, me->object_title);
+		    StrAllocCat(*include, "\" ALT=\"");
+		    StrAllocCat(*include, me->object_title);
+		    StrAllocCat(*include, "\"");
+		}
+		/*
+		 * Add the USEMAP, and an ISMAP if present.  - FM
+		 */
+		if (me->object_usemap != NULL) {
+		    StrAllocCat(*include, " USEMAP=\"");
+		    StrAllocCat(*include, me->object_usemap);
+		    if (me->object_ismap == TRUE)
+			StrAllocCat(*include, "\" ISMAP>");
+		    else
+			StrAllocCat(*include, "\">");
+		} else {
+		    StrAllocCat(*include, ">");
+		}
+		/*
+		 * Add the content if it has <MAP, since that may be the MAP
+		 * this usemap points to.  But if we have nested objects, try
+		 * to eliminate portions that cannot contribute to the quest
+		 * for MAP.  This is not perfect, we may get too much content;
+		 * this seems preferable over losing too much.  - kw
+		 */
+		if (first_map) {
+		    if (s == 0) {
+			StrAllocCat(*include, me->object.data);
+			CTRACE((tfp,
+				"HTML: MAP found, recycling object contents.\n"));
+			goto End_Object;
+		    }
+		    /* s > 0 and s == e */
+		    data = NULL;
+		    if (last_map < start) {
+			*start = '\0';
+			i = 0;
+		    } else if (last_map < first_end) {
+			*first_end = '\0';
+			i = e;
+		    } else if (last_map < last_end) {
+			*last_end = '\0';
+			i = 1;
+		    } else {
+			i = 0;
+		    }
+		    if (first_map > last_end) {
+			/* fake empty object to keep stacks stack happy */
+			StrAllocCopy(data, "<OBJECT><");
+			StrAllocCat(data, last_end + 1);
+			i = 0;
+		    } else if (first_map > start) {
+			StrAllocCopy(data, start);
+		    } else {
+			StrAllocCopy(data, me->object.data);
+		    }
+		    for (; i > 0; i--) {
+			StrAllocCat(data, "</OBJECT>");
+		    }
+		    CTRACE((tfp, "%s:\n%s\n",
+			    "HTML: MAP and nested OBJECT tags.  Recycling parts",
+			    data));
+		    StrAllocCat(*include, data);
+		    FREE(data);
+		}
+		goto End_Object;
+	    }
+
+	    /*
+	     * Add an ID link if needed.  - FM
+	     */
+	    if (non_empty(me->object_id) && !LYMapsOnly)
+		LYHandleID(me, me->object_id);
+
+	    /*
+	     * Add the OBJECTs content if not empty.  - FM
+	     */
+	    if (me->object.size > 1) {
+		if (!include)	/* error, should not happen */
+		    include = &me->xinclude;
+		StrAllocCat(*include, me->object.data);
+	    }
+
+	    /*
+	     * Create a link to the DATA, if desired, and we can rule out that
+	     * it involves scripting code.  This a risky thing to do, but we
+	     * can toggle clickable_images mode off if it really screws things
+	     * up, and so we may as well give it a try.  - FM
+	     */
+	    if (clickable_images) {
+		if (!LYMapsOnly &&
+		    me->object_data != NULL &&
+		    !have_param &&
+		    me->object_classid == NULL &&
+		    me->object_codebase == NULL &&
+		    me->object_codetype == NULL) {
+		    /*
+		     * We have a DATA value and no need for scripting code, so
+		     * close the current Anchor, if one is open, and add an
+		     * Anchor for this source.  If we also have a TYPE value,
+		     * check whether it's an image or not, and set the link
+		     * name accordingly.  - FM
+		     */
+		    if (!include)	/* error, should not happen */
+			include = &me->xinclude;
+		    if (me->inA)
+			StrAllocCat(*include, "</A>");
+		    StrAllocCat(*include, " -<A HREF=\"");
+		    StrAllocCat(*include, me->object_data);
+		    StrAllocCat(*include, "\">");
+		    if ((me->object_type != NULL) &&
+			!strncasecomp(me->object_type, "image/", 6)) {
+			StrAllocCat(*include, "(IMAGE)");
+		    } else {
+			StrAllocCat(*include, "(OBJECT)");
+		    }
+		    StrAllocCat(*include, "</A> ");
+		}
+	    }
+	}
+
+	/*
+	 * Re-intialize all of the OBJECT elements.  - FM
+	 */
+      End_Object:
+	clear_objectdata(me);
+
+	if (!LYMapsOnly)
+	    change_paragraph_style(me, me->sp->style);	/* Often won't really change */
+	break;
+
+    case HTML_APPLET:
+	if (me->inAPPLETwithP) {
+	    LYEnsureDoubleSpace(me);
+	} else {
+	    HTML_put_character(me, ' ');	/* space char may be ignored */
+	}
+	LYResetParagraphAlignment(me);
+	me->inAPPLETwithP = FALSE;
+	me->inAPPLET = FALSE;
+	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
+	break;
+
+    case HTML_CAPTION:
+	LYEnsureDoubleSpace(me);
+	LYResetParagraphAlignment(me);
+	me->inCAPTION = FALSE;
+	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
+	me->inLABEL = FALSE;
+	break;
+
+    case HTML_CREDIT:
+	LYEnsureDoubleSpace(me);
+	LYResetParagraphAlignment(me);
+	me->inCREDIT = FALSE;
+	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
+	me->inLABEL = FALSE;
+	break;
+
+    case HTML_FORM:
+	/*
+	 * Check if we had a FORM start tag, and issue a message if not, but
+	 * fall through to check for an open SELECT and ensure that the
+	 * FORM-related globals in GridText.c are initialized.  - FM
+	 */
+	if (!me->inFORM) {
+	    if (LYBadHTML(me)) {
+		LYShowBadHTML("Bad HTML: Unmatched FORM end tag\n");
+	    }
+	}
+	EMIT_IFDEF_USE_JUSTIFY_ELTS(form_in_htext = FALSE);
+
+	/*
+	 * Check if we still have a SELECT element open.  FORM may have been
+	 * declared SGML_EMPTY in HTMLDTD.c, and in that case SGML_character()
+	 * in SGML.c is not able to ensure correct nesting; or it may have
+	 * failed to enforce valid nesting.  If a SELECT is open, issue a
+	 * message, then call HTML_end_element() directly (with a check in that
+	 * to bypass decrementing of the HTML parser's stack) to close the
+	 * SELECT.  - kw
+	 */
+	if (me->inSELECT) {
+	    if (LYBadHTML(me)) {
+		LYShowBadHTML("Bad HTML: Open SELECT at FORM end. Faking SELECT end tag. *****\n");
+	    }
+	    if (me->sp->tag_number != HTML_SELECT) {
+		SET_SKIP_STACK(HTML_SELECT);
+	    }
+	    HTML_end_element(me, HTML_SELECT, include);
+	}
+
+	/*
+	 * Set to know that we are no longer in an form.
+	 */
+	me->inFORM = FALSE;
+
+	HText_endForm(me->text);
+	/*
+	 * If we are in a list and are on the first line with no text following
+	 * a bullet or number, don't force a newline.  This could happen if we
+	 * were called from HTML_start_element() due to a missing FORM end tag. 
+	 * - FM
+	 */
+	if (!(me->List_Nesting_Level >= 0 && !me->inP))
+	    LYEnsureSingleSpace(me);
+	break;
+
+    case HTML_FIELDSET:
+	LYEnsureDoubleSpace(me);
+	LYResetParagraphAlignment(me);
+	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
+	break;
+
+    case HTML_LEGEND:
+	LYEnsureDoubleSpace(me);
+	LYResetParagraphAlignment(me);
+	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
+	break;
+
+    case HTML_LABEL:
+	break;
+
+    case HTML_BUTTON:
+	break;
+
+    case HTML_TEXTAREA:
+	{
+	    InputFieldData I;
+	    int chars;
+	    char *data;
+
+	    /*
+	     * Make sure we had a textarea start tag.
+	     */
+	    if (!me->inTEXTAREA) {
+		if (LYBadHTML(me)) {
+		    LYShowBadHTML("Bad HTML: Unmatched TEXTAREA end tag\n");
+		}
+		break;
+	    }
+
+	    /*
+	     * Set to know that we are no longer in a textarea tag.
+	     */
+	    me->inTEXTAREA = FALSE;
+
+	    /*
+	     * Initialize.
+	     */
+	    memset(&I, 0, sizeof(I));
+	    I.value_cs = current_char_set;
+
+	    UPDATE_STYLE;
+	    /*
+	     * Before any input field add a space if necessary.
+	     */
+	    HTML_put_character(me, ' ');
+	    me->in_word = NO;
+	    /*
+	     * Add a return.
+	     */
+	    HText_appendCharacter(me->text, '\r');
+
+	    /*
+	     * Finish the data off.
+	     */
+	    HTChunkTerminate(&me->textarea);
+	    FREE(temp);
+
+	    I.type = "textarea";
+	    I.size = me->textarea_cols;
+	    I.name = me->textarea_name;
+	    I.name_cs = me->textarea_name_cs;
+	    I.accept_cs = me->textarea_accept_cs;
+	    me->textarea_accept_cs = NULL;
+	    I.disabled = me->textarea_disabled;
+	    I.id = me->textarea_id;
+
+	    /*
+	     * Transform the TEXTAREA content as needed, then parse it into
+	     * individual lines to be handled as a series series of INPUT
+	     * fields (ugh!).  Any raw 8-bit or multibyte characters already
+	     * have been handled in relation to the display character set in
+	     * SGML_character().
+	     *
+	     * If TEXTAREA is handled as SGML_LITTERAL (the old way), we need
+	     * to SGML-unescape any character references and NCRs here. 
+	     * Otherwise this will already have happened in the SGML.c parsing. 
+	     * - kw
+	     */
+	    me->UsePlainSpace = TRUE;
+
+	    if (HTML_dtd.tags[element_number].contents == SGML_LITTERAL) {
+		TRANSLATE_AND_UNESCAPE_ENTITIES6(&me->textarea.data,
+						 me->UCLYhndl,
+						 current_char_set,
+						 NO,
+						 me->UsePlainSpace, me->HiddenValue);
+	    } else {
+		/*
+		 * This shouldn't have anything to do, normally, but just in
+		 * case...  There shouldn't be lynx special character codes in
+		 * the chunk ("DTD" flag Tgf_nolyspcl tells SGML.c not to
+		 * generate them).  If there were, we could set the last
+		 * parameter ('Back') below to YES, which would take them out
+		 * of the data.  The data may however contain non break space,
+		 * soft hyphen, or en space etc., in the me->UCLYhndl character
+		 * encoding.  If that's a problem, perhaps for the (line or
+		 * other) editor, setting 'Back' to YES should also help to
+		 * always convert them to plain spaces (or drop them).  - kw
+		 */
+		TRANSLATE_HTML7(&me->textarea.data,
+				me->UCLYhndl,
+				current_char_set,
+				NO,
+				me->UsePlainSpace, me->HiddenValue,
+				NO);
+	    }
+	    data = me->textarea.data;
+
+	    /*
+	     * Trim any trailing newlines and skip any lead newlines.  - FM
+	     */
+	    if (*data != '\0') {
+		cp = (data + strlen(data)) - 1;
+		while (cp >= data && *cp == '\n') {
+		    *cp-- = '\0';
+		}
+		while (*data == '\n') {
+		    data++;
+		}
+	    }
+	    /*
+	     * Load the first text line, or set up for all blank rows.  - FM
+	     */
+	    if ((cp = strchr(data, '\n')) != NULL) {
+		*cp = '\0';
+		StrAllocCopy(temp, data);
+		*cp = '\n';
+		data = (cp + 1);
+	    } else {
+		if (*data != '\0') {
+		    StrAllocCopy(temp, data);
+		} else {
+		    FREE(temp);
+		}
+		data = "";
+	    }
+	    /*
+	     * Display at least the requested number of text lines and/or blank
+	     * rows.  - FM
+	     */
+	    for (i = 0; i < me->textarea_rows; i++) {
+		int j;
+
+		for (j = 0; temp && temp[j]; j++) {
+		    if (temp[j] == '\r')
+			temp[j] = (char) (temp[j + 1] ? ' ' : '\0');
+		}
+		I.value = temp;
+		chars = HText_beginInput(me->text, me->inUnderline, &I);
+		for (; chars > 0; chars--)
+		    HTML_put_character(me, '_');
+		HText_appendCharacter(me->text, '\r');
+		if (*data != '\0') {
+		    if (*data == '\n') {
+			FREE(temp);
+			data++;
+		    } else if ((cp = strchr(data, '\n')) != NULL) {
+			*cp = '\0';
+			StrAllocCopy(temp, data);
+			*cp = '\n';
+			data = (cp + 1);
+		    } else {
+			StrAllocCopy(temp, data);
+			data = "";
+		    }
+		} else {
+		    FREE(temp);
+		}
+	    }
+	    /*
+	     * Check for more data lines than the rows attribute.  We add them
+	     * to the display, because we support only horizontal and not also
+	     * vertical scrolling.  - FM
+	     */
+	    while (*data != '\0' || temp != NULL) {
+		int j;
+
+		for (j = 0; temp && temp[j]; j++) {
+		    if (temp[j] == '\r')
+			temp[j] = (char) (temp[j + 1] ? ' ' : '\0');
+		}
+		I.value = temp;
+		(void) HText_beginInput(me->text, me->inUnderline, &I);
+		for (chars = me->textarea_cols; chars > 0; chars--)
+		    HTML_put_character(me, '_');
+		HText_appendCharacter(me->text, '\r');
+		if (*data == '\n') {
+		    FREE(temp);
+		    data++;
+		} else if ((cp = strchr(data, '\n')) != NULL) {
+		    *cp = '\0';
+		    StrAllocCopy(temp, data);
+		    *cp = '\n';
+		    data = (cp + 1);
+		} else if (*data != '\0') {
+		    StrAllocCopy(temp, data);
+		    data = "";
+		} else {
+		    FREE(temp);
+		}
+	    }
+	    FREE(temp);
+	    cp = NULL;
+	    me->UsePlainSpace = FALSE;
+
+	    HTChunkClear(&me->textarea);
+	    FREE(me->textarea_name);
+	    me->textarea_name_cs = -1;
+	    FREE(me->textarea_id);
+	    break;
+	}
+
+    case HTML_SELECT:
+	{
+	    char *ptr = NULL;
+
+	    /*
+	     * Make sure we had a select start tag.
+	     */
+	    if (!me->inSELECT) {
+		if (LYBadHTML(me)) {
+		    LYShowBadHTML("Bad HTML: Unmatched SELECT end tag *****\n");
+		}
+		break;
+	    }
+
+	    /*
+	     * Set to know that we are no longer in a select tag.
+	     */
+	    me->inSELECT = FALSE;
+
+	    /*
+	     * Clear the disable attribute.
+	     */
+	    me->select_disabled = FALSE;
+
+	    /*
+	     * Make sure we're in a form.
+	     */
+	    if (!me->inFORM) {
+		if (LYBadHTML(me)) {
+		    LYShowBadHTML("Bad HTML: SELECT end tag not within FORM element *****\n");
+		}
+		/*
+		 * Hopefully won't crash, so we'll ignore it.  - kw
+		 */
+	    }
+
+	    /*
+	     * Finish the data off.
+	     */
+	    HTChunkTerminate(&me->option);
+	    /*
+	     * Finish the previous option.
+	     */
+	    if (!me->first_option)
+		ptr = HText_setLastOptionValue(me->text,
+					       me->option.data,
+					       me->LastOptionValue,
+					       LAST_ORDER,
+					       me->LastOptionChecked,
+					       me->UCLYhndl,
+					       ATTR_CS_IN);
+	    FREE(me->LastOptionValue);
+
+	    me->LastOptionChecked = FALSE;
+
+	    if (HTCurSelectGroupType == F_CHECKBOX_TYPE ||
+		LYSelectPopups == FALSE) {
+		/*
+		 * Start a newline after the last checkbox/button option.
+		 */
+		LYEnsureSingleSpace(me);
+	    } else {
+		/*
+		 * Output popup box with the default option to screen, but use
+		 * non-breaking spaces for output.
+		 */
+		if (ptr &&
+		    (me->sp[0].tag_number == HTML_PRE || me->inPRE == TRUE ||
+		     !me->sp->style->freeFormat) &&
+		    strlen(ptr) > 6) {
+		    /*
+		     * The code inadequately handles OPTION fields in PRE tags. 
+		     * We'll put up a minimum of 6 characters, and if any more
+		     * would exceed the wrap column, we'll ignore them.
+		     */
+		    for (i = 0; i < 6; i++) {
+			if (*ptr == ' ')
+			    HText_appendCharacter(me->text, HT_NON_BREAK_SPACE);
+			else
+			    HText_appendCharacter(me->text, *ptr);
+			ptr++;
+		    }
+		}
+		for (; non_empty(ptr); ptr++) {
+		    if (*ptr == ' ')
+			HText_appendCharacter(me->text, HT_NON_BREAK_SPACE);
+		    else {
+			HTkcode kcode = NOKANJI;
+			HTkcode specified_kcode = NOKANJI;
+
+			if (HTCJK == JAPANESE) {
+			    kcode = HText_getKcode(me->text);
+			    HText_updateKcode(me->text, kanji_code);
+			    specified_kcode = HText_getSpecifiedKcode(me->text);
+			    HText_updateSpecifiedKcode(me->text, kanji_code);
+			}
+			HText_appendCharacter(me->text, *ptr);
+			if (HTCJK == JAPANESE) {
+			    HText_updateKcode(me->text, kcode);
+			    HText_updateSpecifiedKcode(me->text, specified_kcode);
+			}
+		    }
+		}
+		/*
+		 * Add end option character.
+		 */
+		if (!me->first_option) {
+		    HText_appendCharacter(me->text, ']');
+		    HText_endInput(me->text);
+		    HText_setLastChar(me->text, ']');
+		    me->in_word = YES;
+		}
+	    }
+	    HTChunkClear(&me->option);
+
+	    if (me->Underline_Level > 0 && me->inUnderline == FALSE) {
+		HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
+		me->inUnderline = TRUE;
+	    }
+	    if (me->needBoldH == TRUE && me->inBoldH == FALSE) {
+		HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
+		me->inBoldH = TRUE;
+		me->needBoldH = FALSE;
+	    }
+	}
+	break;
+
+    case HTML_TABLE:
+#ifdef EXP_NESTED_TABLES
+	if (!nested_tables)
+#endif
+	    me->inTABLE = FALSE;
+
+	if (me->sp->style->id == ST_Preformatted) {
+	    break;
+	}
+	if (me->Division_Level >= 0)
+	    me->Division_Level--;
+	if (me->Division_Level >= 0)
+	    me->sp->style->alignment =
+		me->DivisionAlignments[me->Division_Level];
+	change_paragraph_style(me, me->sp->style);
+	UPDATE_STYLE;
+
+#ifdef EXP_NESTED_TABLES
+	if (nested_tables) {
+	    me->inTABLE = HText_endStblTABLE(me->text);
+	} else {
+	    HText_endStblTABLE(me->text);
+	}
+#else
+	HText_endStblTABLE(me->text);
+#endif
+
+	me->current_default_alignment = me->sp->style->alignment;
+	if (me->List_Nesting_Level >= 0)
+	    HText_NegateLineOne(me->text);
+	break;
+
+/* These TABLE related elements may now not be SGML_EMPTY. - kw */
+    case HTML_TR:
+	HText_endStblTR(me->text);
+	if (!HText_LastLineEmpty(me->text, FALSE)) {
+	    HText_setLastChar(me->text, ' ');	/* absorb next white space */
+	    HText_appendCharacter(me->text, '\r');
+	}
+	me->in_word = NO;
+	break;
+
+    case HTML_THEAD:
+    case HTML_TFOOT:
+    case HTML_TBODY:
+	break;
+
+    case HTML_COLGROUP:
+	if (me->inTABLE)
+	    HText_endStblCOLGROUP(me->text);
+	break;
+
+    case HTML_TH:
+    case HTML_TD:
+	HText_endStblTD(me->text);
+	break;
+
+/* More stuff that may now not be SGML_EMPTY any more: */
+    case HTML_DT:
+    case HTML_DD:
+    case HTML_LH:
+    case HTML_LI:
+    case HTML_OVERLAY:
+	break;
+
+    case HTML_MATH:
+	/*
+	 * We're getting it as Literal text, which, until we can process it,
+	 * we'll display as is, within brackets to alert the user.  - FM
+	 */
+	HTChunkPutc(&me->math, ' ');
+	HTChunkTerminate(&me->math);
+	if (me->math.size > 2) {
+	    LYEnsureSingleSpace(me);
+	    if (me->inUnderline == FALSE)
+		HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
+	    HTML_put_string(me, "[MATH:");
+	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
+	    HTML_put_character(me, ' ');
+	    HTML_put_string(me, me->math.data);
+	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
+	    HTML_put_string(me, ":MATH]");
+	    if (me->inUnderline == FALSE)
+		HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
+	    LYEnsureSingleSpace(me);
+	}
+	HTChunkClear(&me->math);
+	break;
+
+    default:
+	change_paragraph_style(me, me->sp->style);	/* Often won't really change */
+	break;
+
+    }				/* switch */
+
+#ifdef USE_JUSTIFY_ELTS
+    if (reached_awaited_stacked_elt)
+	wait_for_this_stacked_elt = -1;
+#endif
+
+    if (me->xinclude) {
+	HText_appendText(me->text, " *** LYNX ERROR ***\rUnparsed data:\r");
+	HText_appendText(me->text, me->xinclude);
+	FREE(me->xinclude);
+    }
+#ifdef USE_COLOR_STYLE
+    if (!skip_stack_requested) {	/*don't emit stylechanges if skipped stack element - VH */
+# if !OMIT_SCN_KEEPING
+	FastTrimColorClass(HTML_dtd.tags[element_number].name,
+			   HTML_dtd.tags[element_number].name_len,
+			   Style_className,
+			   &Style_className_end, &hcode);
+#  endif
+
+	if (!ReallyEmptyTagNum(element_number)) {
+	    CTRACE2(TRACE_STYLE,
+		    (tfp,
+		     "STYLE.end_element: ending non-\"EMPTY\" style <%s...>\n",
+		     HTML_dtd.tags[element_number].name));
+	    HText_characterStyle(me->text, HCODE_TO_STACK_OFF(hcode), STACK_OFF);
+	}
+    }
+#endif /* USE_COLOR_STYLE */
+    return status;
+}
+
+/*		Expanding entities
+ *		------------------
+ */
+/*	(In fact, they all shrink!)
+*/
+int HTML_put_entity(HTStructured * me, int entity_number)
+{
+    int nent = (int) HTML_dtd.number_of_entities;
+
+    if (entity_number < nent) {
+	HTML_put_string(me, p_entity_values[entity_number]);
+	return HT_OK;
+    }
+    return HT_CANNOT_TRANSLATE;
+}
+
+/*	Free an HTML object
+ *	-------------------
+ *
+ *	If the document is empty, the text object will not yet exist.
+ *	So we could in fact abandon creating the document and return
+ *	an error code.	In fact an empty document is an important type
+ *	of document, so we don't.
+ *
+ *	If non-interactive, everything is freed off.   No: crashes -listrefs
+ *	Otherwise, the interactive object is left.
+ */
+static void HTML_free(HTStructured * me)
+{
+    char *include = NULL;
+
+    if (LYMapsOnly && !me->text) {
+	/*
+	 * We only handled MAP, AREA and BASE tags, and didn't create an HText
+	 * structure for the document nor want one now, so just make sure we
+	 * free anything that might have been allocated.  - FM
+	 */
+	FREE(me->base_href);
+	FREE(me->map_address);
+	clear_objectdata(me);
+	FREE(me->xinclude);
+	FREE(me);
+	return;
+    }
+
+    UPDATE_STYLE;		/* Creates empty document here! */
+    if (me->comment_end)
+	HTML_put_string(me, me->comment_end);
+    if (me->text) {
+	/*
+	 * Emphasis containers, A, FONT, and FORM may be declared SGML_EMPTY in
+	 * HTMLDTD.c, and SGML_character() in SGML.c may check for their end
+	 * tags to call HTML_end_element() directly (with a check in that to
+	 * bypass decrementing of the HTML parser's stack).  So if we still
+	 * have the emphasis (Underline) on, or any open A, FONT, or FORM
+	 * containers, turn it off or close them now.  - FM & kw
+	 *
+	 * IF those tags are not declared SGML_EMPTY, but we let the SGML.c
+	 * parser take care of correctly stacked ordering, and of correct
+	 * wind-down on end-of-stream (in SGML_free SGML_abort), THEN these and
+	 * other checks here in HTML.c should not be necessary.  Still it can't
+	 * hurt to include them.  - kw
+	 */
+	if (me->inUnderline) {
+	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
+	    me->inUnderline = FALSE;
+	    me->Underline_Level = 0;
+	    CTRACE((tfp, "HTML_free: Ending underline\n"));
+	}
+	if (me->inA) {
+	    HTML_end_element(me, HTML_A, &include);
+	    me->inA = FALSE;
+	    CTRACE((tfp, "HTML_free: Ending HTML_A\n"));
+	}
+	if (me->inFONT) {
+	    HTML_end_element(me, HTML_FONT, &include);
+	    me->inFONT = FALSE;
+	}
+	if (me->inFORM) {
+	    HTML_end_element(me, HTML_FORM, &include);
+	    me->inFORM = FALSE;
+	}
+	if (me->option.size > 0) {
+	    /*
+	     * If we still have data in the me->option chunk after forcing a
+	     * close of a still-open form, something must have gone very wrong. 
+	     * - kw
+	     */
+	    if (LYBadHTML(me)) {
+		LYShowBadHTML("Bad HTML: SELECT or OPTION not ended properly *****\n");
+	    }
+	    HTChunkTerminate(&me->option);
+	    /*
+	     * Output the left-over data as text, maybe it was invalid markup
+	     * meant to be shown somewhere.  - kw
+	     */
+	    CTRACE((tfp, "HTML_free: ***** leftover option data: %s\n",
+		    me->option.data));
+	    HTML_put_string(me, me->option.data);
+	    HTChunkClear(&me->option);
+	}
+	if (me->textarea.size > 0) {
+	    /*
+	     * If we still have data in the me->textarea chunk after forcing a
+	     * close of a still-open form, something must have gone very wrong. 
+	     * - kw
+	     */
+	    if (LYBadHTML(me)) {
+		LYShowBadHTML("Bad HTML: TEXTAREA not used properly *****\n");
+	    }
+	    HTChunkTerminate(&me->textarea);
+	    /*
+	     * Output the left-over data as text, maybe it was invalid markup
+	     * meant to be shown somewhere.  - kw
+	     */
+	    CTRACE((tfp, "HTML_free: ***** leftover textarea data: %s\n",
+		    me->textarea.data));
+	    HTML_put_string(me, me->textarea.data);
+	    HTChunkClear(&me->textarea);
+	}
+	/*
+	 * If we're interactive and have hidden links but no visible links, add
+	 * a message informing the user about this and suggesting use of the
+	 * 'l'ist command.  - FM
+	 */
+	if (!dump_output_immediately &&
+	    HText_sourceAnchors(me->text) < 1 &&
+	    HText_HiddenLinkCount(me->text) > 0) {
+	    HTML_start_element(me, HTML_P, 0, 0, -1, &include);
+	    HTML_put_character(me, '[');
+	    HTML_start_element(me, HTML_EM, 0, 0, -1, &include);
+	    HTML_put_string(me,
+			    gettext("Document has only hidden links.  Use the 'l'ist command."));
+	    HTML_end_element(me, HTML_EM, &include);
+	    HTML_put_character(me, ']');
+	    HTML_end_element(me, HTML_P, &include);
+	}
+	if (me->xinclude) {
+	    HText_appendText(me->text, " *** LYNX ERROR ***\rUnparsed data:\r");
+	    HText_appendText(me->text, me->xinclude);
+	    FREE(me->xinclude);
+	}
+
+	/*
+	 * Now call the cleanup function.  - FM
+	 */
+	HText_endAppend(me->text);
+    }
+    if (me->option.size > 0) {
+	/*
+	 * If we still have data in the me->option chunk after forcing a close
+	 * of a still-open form, something must have gone very wrong.  - kw
+	 */
+	if (LYBadHTML(me)) {
+	    LYShowBadHTML("Bad HTML: SELECT or OPTION not ended properly *****\n");
+	}
+	if (TRACE) {
+	    HTChunkTerminate(&me->option);
+	    CTRACE((tfp, "HTML_free: ***** leftover option data: %s\n",
+		    me->option.data));
+	}
+	HTChunkClear(&me->option);
+    }
+    if (me->textarea.size > 0) {
+	/*
+	 * If we still have data in the me->textarea chunk after forcing a
+	 * close of a still-open form, something must have gone very wrong.  -
+	 * kw
+	 */
+	if (LYBadHTML(me)) {
+	    LYShowBadHTML("Bad HTML: TEXTAREA not used properly *****\n");
+	}
+	if (TRACE) {
+	    HTChunkTerminate(&me->textarea);
+	    CTRACE((tfp, "HTML_free: ***** leftover textarea data: %s\n",
+		    me->textarea.data));
+	}
+	HTChunkClear(&me->textarea);
+    }
+
+    if (me->target) {
+	(*me->targetClass._free) (me->target);
+    }
+    if (me->sp && me->sp->style && GetHTStyleName(me->sp->style)) {
+	if (me->sp->style->id == ST_DivCenter ||
+	    me->sp->style->id == ST_HeadingCenter ||
+	    me->sp->style->id == ST_Heading1) {
+	    me->sp->style->alignment = HT_CENTER;
+	} else if (me->sp->style->id == ST_DivRight ||
+		   me->sp->style->id == ST_HeadingRight) {
+	    me->sp->style->alignment = HT_RIGHT;
+	} else {
+	    me->sp->style->alignment = HT_LEFT;
+	}
+	styles[HTML_PRE]->alignment = HT_LEFT;
+    }
+    FREE(me->base_href);
+    FREE(me->map_address);
+    FREE(me->LastOptionValue);
+    clear_objectdata(me);
+    FREE(me);
+}
+
+static void HTML_abort(HTStructured * me, HTError e)
+{
+    char *include = NULL;
+
+    if (me->text) {
+	/*
+	 * If we have emphasis on, or open A, FONT, or FORM containers, turn it
+	 * off or close them now.  - FM
+	 */
+	if (me->inUnderline) {
+	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
+	    me->inUnderline = FALSE;
+	    me->Underline_Level = 0;
+	}
+	if (me->inA) {
+	    HTML_end_element(me, HTML_A, &include);
+	    me->inA = FALSE;
+	}
+	if (me->inFONT) {
+	    HTML_end_element(me, HTML_FONT, &include);
+	    me->inFONT = FALSE;
+	}
+	if (me->inFORM) {
+	    HTML_end_element(me, HTML_FORM, &include);
+	    me->inFORM = FALSE;
+	}
+
+	/*
+	 * Now call the cleanup function.  - FM
+	 */
+	HText_endAppend(me->text);
+    }
+
+    if (me->option.size > 0) {
+	/*
+	 * If we still have data in the me->option chunk after forcing a close
+	 * of a still-open form, something must have gone very wrong.  - kw
+	 */
+	if (TRACE) {
+	    CTRACE((tfp,
+		    "HTML_abort: SELECT or OPTION not ended properly *****\n"));
+	    HTChunkTerminate(&me->option);
+	    CTRACE((tfp, "HTML_abort: ***** leftover option data: %s\n",
+		    me->option.data));
+	}
+	HTChunkClear(&me->option);
+    }
+    if (me->textarea.size > 0) {
+	/*
+	 * If we still have data in the me->textarea chunk after forcing a
+	 * close of a still-open form, something must have gone very wrong.  -
+	 * kw
+	 */
+	if (TRACE) {
+	    CTRACE((tfp, "HTML_abort: TEXTAREA not used properly *****\n"));
+	    HTChunkTerminate(&me->textarea);
+	    CTRACE((tfp, "HTML_abort: ***** leftover textarea data: %s\n",
+		    me->textarea.data));
+	}
+	HTChunkClear(&me->textarea);
+    }
+
+    if (me->target) {
+	(*me->targetClass._abort) (me->target, e);
+    }
+    if (me->sp && me->sp->style && GetHTStyleName(me->sp->style)) {
+	if (me->sp->style->id == ST_DivCenter ||
+	    me->sp->style->id == ST_HeadingCenter ||
+	    me->sp->style->id == ST_Heading1) {
+	    me->sp->style->alignment = HT_CENTER;
+	} else if (me->sp->style->id == ST_DivRight ||
+		   me->sp->style->id == ST_HeadingRight) {
+	    me->sp->style->alignment = HT_RIGHT;
+	} else {
+	    me->sp->style->alignment = HT_LEFT;
+	}
+	styles[HTML_PRE]->alignment = HT_LEFT;
+    }
+    FREE(me->base_href);
+    FREE(me->map_address);
+    FREE(me->textarea_name);
+    FREE(me->textarea_accept_cs);
+    FREE(me->textarea_id);
+    FREE(me->LastOptionValue);
+    FREE(me->xinclude);
+    clear_objectdata(me);
+    FREE(me);
+}
+
+/*	Get Styles from style sheet
+ *	---------------------------
+ */
+static void get_styles(void)
+{
+    HTStyle **st = NULL;
+
+    styleSheet = DefaultStyle(&st);	/* sets st[] array */
+
+    default_style = st[ST_Normal];
+
+    styles[HTML_H1] = st[ST_Heading1];
+    styles[HTML_H2] = st[ST_Heading2];
+    styles[HTML_H3] = st[ST_Heading3];
+    styles[HTML_H4] = st[ST_Heading4];
+    styles[HTML_H5] = st[ST_Heading5];
+    styles[HTML_H6] = st[ST_Heading6];
+    styles[HTML_HCENTER] = st[ST_HeadingCenter];
+    styles[HTML_HLEFT] = st[ST_HeadingLeft];
+    styles[HTML_HRIGHT] = st[ST_HeadingRight];
+
+    styles[HTML_DCENTER] = st[ST_DivCenter];
+    styles[HTML_DLEFT] = st[ST_DivLeft];
+    styles[HTML_DRIGHT] = st[ST_DivRight];
+
+    styles[HTML_DL] = st[ST_Glossary];
+    /* nested list styles */
+    styles[HTML_DL1] = st[ST_Glossary1];
+    styles[HTML_DL2] = st[ST_Glossary2];
+    styles[HTML_DL3] = st[ST_Glossary3];
+    styles[HTML_DL4] = st[ST_Glossary4];
+    styles[HTML_DL5] = st[ST_Glossary5];
+    styles[HTML_DL6] = st[ST_Glossary6];
+
+    styles[HTML_UL] =
+	styles[HTML_OL] = st[ST_List];
+    /* nested list styles */
+    styles[HTML_OL1] = st[ST_List1];
+    styles[HTML_OL2] = st[ST_List2];
+    styles[HTML_OL3] = st[ST_List3];
+    styles[HTML_OL4] = st[ST_List4];
+    styles[HTML_OL5] = st[ST_List5];
+    styles[HTML_OL6] = st[ST_List6];
+
+    styles[HTML_MENU] =
+	styles[HTML_DIR] = st[ST_Menu];
+    /* nested list styles */
+    styles[HTML_MENU1] = st[ST_Menu1];
+    styles[HTML_MENU2] = st[ST_Menu2];
+    styles[HTML_MENU3] = st[ST_Menu3];
+    styles[HTML_MENU4] = st[ST_Menu4];
+    styles[HTML_MENU5] = st[ST_Menu5];
+    styles[HTML_MENU6] = st[ST_Menu6];
+
+    styles[HTML_DLC] = st[ST_GlossaryCompact];
+    /* nested list styles */
+    styles[HTML_DLC1] = st[ST_GlossaryCompact1];
+    styles[HTML_DLC2] = st[ST_GlossaryCompact2];
+    styles[HTML_DLC3] = st[ST_GlossaryCompact3];
+    styles[HTML_DLC4] = st[ST_GlossaryCompact4];
+    styles[HTML_DLC5] = st[ST_GlossaryCompact5];
+    styles[HTML_DLC6] = st[ST_GlossaryCompact6];
+
+    styles[HTML_ADDRESS] = st[ST_Address];
+    styles[HTML_BANNER] = st[ST_Banner];
+    styles[HTML_BLOCKQUOTE] = st[ST_Blockquote];
+    styles[HTML_BQ] = st[ST_Bq];
+    styles[HTML_FN] = st[ST_Footnote];
+    styles[HTML_NOTE] = st[ST_Note];
+    styles[HTML_PLAINTEXT] =
+	styles[HTML_XMP] = st[ST_Example];
+    styles[HTML_PRE] = st[ST_Preformatted];
+    styles[HTML_LISTING] = st[ST_Listing];
+}
+
+/*
+ * If we're called from another module, make sure we've initialized styles
+ * array first.
+ */
+HTStyle *LYstyles(int style_number)
+{
+    if (styles[style_number] == 0)
+	get_styles();
+    return styles[style_number];
+}
+
+/*				P U B L I C
+*/
+
+/*	Structured Object Class
+ *	-----------------------
+ */
+const HTStructuredClass HTMLPresentation =	/* As opposed to print etc */
+{
+    "Lynx_HTML_Handler",
+    HTML_free,
+    HTML_abort,
+    HTML_put_character, HTML_put_string, HTML_write,
+    HTML_start_element, HTML_end_element,
+    HTML_put_entity
+};
+
+/*		New Structured Text object
+ *		--------------------------
+ *
+ *	The structured stream can generate either presentation,
+ *	or plain text, or HTML.
+ */
+HTStructured *HTML_new(HTParentAnchor *anchor,
+		       HTFormat format_out,
+		       HTStream *stream)
+{
+
+    HTStructured *me;
+
+    CTRACE((tfp, "start HTML_new\n"));
+
+    if (format_out != WWW_PLAINTEXT && format_out != WWW_PRESENT) {
+	HTStream *intermediate = HTStreamStack(WWW_HTML, format_out,
+					       stream, anchor);
+
+	if (intermediate)
+	    return HTMLGenerator(intermediate);
+	fprintf(stderr, "\n** Internal error: can't parse HTML to %s\n",
+		HTAtom_name(format_out));
+	exit_immediately(EXIT_FAILURE);
+    }
+
+    me = typecalloc(HTStructured);
+    if (me == NULL)
+	outofmem(__FILE__, "HTML_new");
+
+    /*
+     * This used to call 'get_styles()' only on the first time through this
+     * function.  However, if the user reloads a page with ^R, the styles[]
+     * array is not necessarily the same as it was from 'get_styles()'.  So
+     * we reinitialize the whole thing.
+     */
+    get_styles();
+
+    me->isa = &HTMLPresentation;
+    me->node_anchor = anchor;
+
+    me->CurrentA = NULL;
+    me->CurrentANum = 0;
+    me->base_href = NULL;
+    me->map_address = NULL;
+
+    HTChunkInit(&me->title, 128);
+
+    HTChunkInit(&me->object, 128);
+    me->object_started = FALSE;
+    me->object_declare = FALSE;
+    me->object_shapes = FALSE;
+    me->object_ismap = FALSE;
+    me->object_id = NULL;
+    me->object_title = NULL;
+    me->object_data = NULL;
+    me->object_type = NULL;
+    me->object_classid = NULL;
+    me->object_codebase = NULL;
+    me->object_codetype = NULL;
+    me->object_usemap = NULL;
+    me->object_name = NULL;
+
+    HTChunkInit(&me->option, 128);
+    me->first_option = TRUE;
+    me->LastOptionValue = NULL;
+    me->LastOptionChecked = FALSE;
+    me->select_disabled = FALSE;
+
+    HTChunkInit(&me->textarea, 128);
+    me->textarea_name = NULL;
+    me->textarea_name_cs = -1;
+    me->textarea_accept_cs = NULL;
+    me->textarea_cols = 0;
+    me->textarea_rows = 4;
+    me->textarea_disabled = NO;
+    me->textarea_id = NULL;
+
+    HTChunkInit(&me->math, 128);
+
+    HTChunkInit(&me->style_block, 128);
+
+    HTChunkInit(&me->script, 128);
+
+    me->text = 0;
+    me->style_change = YES;	/* Force check leading to text creation */
+    me->new_style = default_style;
+    me->old_style = 0;
+    me->current_default_alignment = HT_LEFT;
+    me->sp = (me->stack + MAX_NESTING - 1);
+    me->skip_stack = 0;
+    me->sp->tag_number = -1;	/* INVALID */
+    me->sp->style = default_style;	/* INVALID */
+    me->sp->style->alignment = HT_LEFT;
+    me->stack_overrun = FALSE;
+
+    me->Division_Level = -1;
+    me->Underline_Level = 0;
+    me->Quote_Level = 0;
+
+    me->UsePlainSpace = FALSE;
+    me->HiddenValue = FALSE;
+    me->lastraw = -1;
+
+    /*
+     * Used for nested lists.  - FM
+     */
+    me->List_Nesting_Level = -1;	/* counter for list nesting level */
+    LYZero_OL_Counter(me);	/* Initializes OL_Counter[] and OL_Type[] */
+    me->Last_OL_Count = 0;	/* last count in ordered lists */
+    me->Last_OL_Type = '1';	/* last type in ordered lists */
+
+    me->inA = FALSE;
+    me->inAPPLET = FALSE;
+    me->inAPPLETwithP = FALSE;
+    me->inBadBASE = FALSE;
+    me->inBadHREF = FALSE;
+    me->inBadHTML = FALSE;
+    me->inBASE = FALSE;
+    me->node_anchor->inBASE = FALSE;
+    me->inBoldA = FALSE;
+    me->inBoldH = FALSE;
+    me->inCAPTION = FALSE;
+    me->inCREDIT = FALSE;
+    me->inFIG = FALSE;
+    me->inFIGwithP = FALSE;
+    me->inFONT = FALSE;
+    me->inFORM = FALSE;
+    me->inLABEL = FALSE;
+    me->inP = FALSE;
+    me->inPRE = FALSE;
+    me->inSELECT = FALSE;
+    me->inTABLE = FALSE;
+    me->inUnderline = FALSE;
+
+    me->needBoldH = FALSE;
+
+    me->comment_start = NULL;
+    me->comment_end = NULL;
+
+#ifdef USE_COLOR_STYLE
+#ifdef LY_FIND_LEAKS
+    if (Style_className == 0) {
+	atexit(free_Style_className);
+    }
+#endif
+    addClassName("", "", 0);
+    class_string[0] = '\0';
+#endif
+
+    /*
+     * Create a chartrans stage info structure for the anchor, if it does not
+     * exist already (in which case the default MIME stage info will be loaded
+     * as well), and load the HTML stage info into me->UCI and me->UCLYhndl.  -
+     * FM
+     */
+    LYGetChartransInfo(me);
+    UCTransParams_clear(&me->T);
+
+    /*
+     * Load the existing or default input charset info into the holding
+     * elements.  We'll believe what is indicated for UCT_STAGE_PARSER.  - FM
+     */
+    me->inUCLYhndl = HTAnchor_getUCLYhndl(me->node_anchor,
+					  UCT_STAGE_PARSER);
+    if (me->inUCLYhndl < 0) {
+	me->inUCLYhndl = HTAnchor_getUCLYhndl(me->node_anchor,
+					      UCT_STAGE_MIME);
+	me->inUCI = HTAnchor_getUCInfoStage(me->node_anchor,
+					    UCT_STAGE_MIME);
+    } else {
+	me->inUCI = HTAnchor_getUCInfoStage(me->node_anchor,
+					    UCT_STAGE_PARSER);
+    }
+
+    /*
+     * Load the existing or default output charset info into the holding
+     * elements, UCT_STAGE_STRUCTURED should be the same as UCT_STAGE_TEXT at
+     * this point, but we could check, perhaps.  - FM
+     */
+    me->outUCI = HTAnchor_getUCInfoStage(me->node_anchor,
+					 UCT_STAGE_STRUCTURED);
+    me->outUCLYhndl = HTAnchor_getUCLYhndl(me->node_anchor,
+					   UCT_STAGE_STRUCTURED);
+
+    me->target = stream;
+    if (stream)
+	me->targetClass = *stream->isa;		/* Copy pointers */
+
+    return (HTStructured *) me;
+}
+
+#ifdef USE_SOURCE_CACHE
+
+/*
+ * A flag set by a file write error.  Used for only generating an alert the
+ * first time such an error happens, since Lynx should still be usable if the
+ * temp space becomes full, and an alert each time a cache file cannot be
+ * written would be annoying.  Reset when lynx.cfg is being reloaded (user may
+ * change SOURCE_CACHE setting).  - kw
+ */
+BOOLEAN source_cache_file_error = FALSE;
+
+/*
+ * Pass-thru cache HTStream
+ */
+
+static void CacheThru_do_free(HTStream *me)
+{
+    if (me->anchor->source_cache_file) {
+	CTRACE((tfp, "SourceCacheWriter: Removing previous file %s\n",
+		me->anchor->source_cache_file));
+	LYRemoveTemp(me->anchor->source_cache_file);
+	FREE(me->anchor->source_cache_file);
+    }
+    if (me->anchor->source_cache_chunk) {
+	CTRACE((tfp, "SourceCacheWriter: Removing previous memory chunk %p\n",
+		(void *) me->anchor->source_cache_chunk));
+	HTChunkFree(me->anchor->source_cache_chunk);
+	me->anchor->source_cache_chunk = NULL;
+    }
+    if (me->fp) {
+	fflush(me->fp);
+	if (ferror(me->fp))
+	    me->status = HT_ERROR;
+	LYCloseTempFP(me->fp);
+	if (me->status == HT_OK) {
+	    char *cp_freeme = 0;
+
+	    me->anchor->source_cache_file = me->filename;
+	    CTRACE((tfp,
+		    "SourceCacheWriter: Committing file %s for URL %s to anchor\n",
+		    me->filename,
+		    cp_freeme = HTAnchor_address((HTAnchor *) me->anchor)));
+	    FREE(cp_freeme);
+	} else {
+	    if (source_cache_file_error == FALSE) {
+		HTAlert(gettext("Source cache error - disk full?"));
+		source_cache_file_error = TRUE;
+	    }
+	    LYRemoveTemp(me->filename);
+	    me->anchor->source_cache_file = NULL;
+	}
+    } else if (me->status != HT_OK) {
+	if (me->chunk) {
+	    CTRACE((tfp, "SourceCacheWriter: memory chunk %p had errors.\n",
+		    (void *) me->chunk));
+	    HTChunkFree(me->chunk);
+	    me->chunk = me->last_chunk = NULL;
+	}
+	HTAlert(gettext("Source cache error - not enough memory!"));
+    }
+    if (me->chunk) {
+	char *cp_freeme = NULL;
+
+	me->anchor->source_cache_chunk = me->chunk;
+	CTRACE((tfp,
+		"SourceCacheWriter: Committing memory chunk %p for URL %s to anchor\n",
+		(void *) me->chunk,
+		cp_freeme = HTAnchor_address((HTAnchor *) me->anchor)));
+	FREE(cp_freeme);
+    }
+}
+
+static void CacheThru_free(HTStream *me)
+{
+    CacheThru_do_free(me);
+    (*me->actions->_free) (me->target);
+    FREE(me);
+}
+
+static void CacheThru_abort(HTStream *me, HTError e)
+{
+    if (me->fp)
+	LYCloseTempFP(me->fp);
+    if (LYCacheSourceForAborted == SOURCE_CACHE_FOR_ABORTED_DROP) {
+	if (me->filename) {
+	    CTRACE((tfp, "SourceCacheWriter: Removing active file %s\n",
+		    me->filename));
+	    LYRemoveTemp(me->filename);
+	    FREE(me->filename);
+	}
+	if (me->chunk) {
+	    CTRACE((tfp,
+		    "SourceCacheWriter: Removing active memory chunk %p\n",
+		    (void *) me->chunk));
+	    HTChunkFree(me->chunk);
+	}
+    } else {
+	me->status = HT_OK;	/*fake it */
+	CacheThru_do_free(me);
+    }
+    (*me->actions->_abort) (me->target, e);
+    FREE(me);
+}
+
+/*
+ * FIXME: never used!
+ */
+static void CacheThru_put_character(HTStream *me, char c_in)
+{
+    if (me->status == HT_OK) {
+	if (me->fp) {
+	    fputc(c_in, me->fp);
+	} else if (me->chunk) {
+	    me->last_chunk = HTChunkPutc2(me->last_chunk, c_in);
+	    if (me->last_chunk == NULL || me->last_chunk->allocated == 0)
+		me->status = HT_ERROR;
+	}
+    }
+    (*me->actions->put_character) (me->target, c_in);
+}
+
+/*
+ * FIXME: never used!
+ */
+static void CacheThru_put_string(HTStream *me, const char *str)
+{
+    if (me->status == HT_OK) {
+	if (me->fp) {
+	    fputs(str, me->fp);
+	} else if (me->chunk) {
+	    me->last_chunk = HTChunkPuts2(me->last_chunk, str);
+	    if (me->last_chunk == NULL || me->last_chunk->allocated == 0)
+		me->status = HT_ERROR;
+	}
+    }
+    (*me->actions->put_string) (me->target, str);
+}
+
+static void CacheThru_write(HTStream *me, const char *str, int l)
+{
+    if (me->status == HT_OK && l != 0) {
+	if (me->fp) {
+	    fwrite(str, 1, (unsigned) l, me->fp);
+	    if (ferror(me->fp))
+		me->status = HT_ERROR;
+	} else if (me->chunk) {
+	    me->last_chunk = HTChunkPutb2(me->last_chunk, str, l);
+	    if (me->last_chunk == NULL || me->last_chunk->allocated == 0)
+		me->status = HT_ERROR;
+	}
+    }
+    (*me->actions->put_block) (me->target, str, l);
+}
+
+static const HTStreamClass PassThruCache =
+{
+    "PassThruCache",
+    CacheThru_free,
+    CacheThru_abort,
+    CacheThru_put_character,
+    CacheThru_put_string,
+    CacheThru_write
+};
+
+static HTStream *CacheThru_new(HTParentAnchor *anchor,
+			       HTStream *target)
+{
+    char *cp_freeme = NULL;
+    char filename[LY_MAXPATH];
+    HTStream *stream = NULL;
+    HTProtocol *p = (HTProtocol *) anchor->protocol;
+
+    /*
+     * Neatly and transparently vanish if source caching is disabled.
+     */
+    if (LYCacheSource == SOURCE_CACHE_NONE)
+	return target;
+
+#ifndef DEBUG_SOURCE_CACHE
+    /*  Only remote HTML documents may benefit from HTreparse_document(),  */
+    /*  oh, assume http protocol:                                          */
+    if (strcmp(p->name, "http") != 0
+	&& strcmp(p->name, "https") != 0) {
+	CTRACE((tfp, "SourceCacheWriter: Protocol is \"%s\"; not cached\n", p->name));
+	return target;
+    }
+#else
+    /* all HTStreams will be cached */
+#endif
+
+    CTRACE((tfp, "start CacheThru_new\n"));
+
+    stream = (HTStream *) malloc(sizeof(*stream));
+    if (!stream)
+	outofmem(__FILE__, "CacheThru_new");
+
+    assert(stream != NULL);
+
+    stream->isa = &PassThruCache;
+    stream->anchor = anchor;
+    stream->fp = NULL;
+    stream->filename = NULL;
+    stream->chunk = NULL;
+    stream->target = target;
+    stream->actions = target->isa;
+    stream->status = HT_OK;
+
+    if (LYCacheSource == SOURCE_CACHE_FILE) {
+
+	if (anchor->source_cache_file) {
+	    CTRACE((tfp,
+		    "SourceCacheWriter: If successful, will replace source cache file %s\n",
+		    anchor->source_cache_file));
+	}
+
+	/*
+	 * We open the temp file in binary mode to make sure that
+	 * end-of-line stuff and high-bit Latin-1 (or other) characters
+	 * don't get munged; this way, the file should (knock on wood)
+	 * contain exactly what came in from the network.
+	 */
+	if (!(stream->fp = LYOpenTemp(filename, HTML_SUFFIX, BIN_W))) {
+	    CTRACE((tfp,
+		    "SourceCacheWriter: Cannot open source cache file for URL %s\n",
+		    cp_freeme = HTAnchor_address((HTAnchor *) anchor)));
+	    FREE(stream);
+	    FREE(cp_freeme);
+	    return target;
+	}
+
+	StrAllocCopy(stream->filename, filename);
+
+	CTRACE((tfp,
+		"SourceCacheWriter: Caching source for URL %s in file %s\n",
+		cp_freeme = HTAnchor_address((HTAnchor *) anchor),
+		filename));
+	FREE(cp_freeme);
+    }
+
+    if (LYCacheSource == SOURCE_CACHE_MEMORY) {
+	if (anchor->source_cache_chunk) {
+	    CTRACE((tfp,
+		    "SourceCacheWriter: If successful, will replace memory chunk %p\n",
+		    (void *) anchor->source_cache_chunk));
+	}
+	stream->chunk = stream->last_chunk = HTChunkCreateMayFail(4096, 1);
+	if (!stream->chunk)	/* failed already? pretty bad... - kw */
+	    stream->status = HT_ERROR;
+
+	CTRACE((tfp,
+		"SourceCacheWriter: Caching source for URL %s in memory chunk %p\n",
+		cp_freeme = HTAnchor_address((HTAnchor *) anchor),
+		(void *) stream->chunk));
+	FREE(cp_freeme);
+    }
+
+    return stream;
+}
+#else
+#define CacheThru_new(anchor, target) target
+#endif
+
+/*	HTConverter for HTML to plain text
+ *	----------------------------------
+ *
+ *	This will convert from HTML to presentation or plain text.
+ *
+ *	It is registered in HTInit.c, but never actually used by lynx.
+ *	- kw 1999-03-15
+ */
+HTStream *HTMLToPlain(HTPresentation *pres,
+		      HTParentAnchor *anchor,
+		      HTStream *sink)
+{
+    CTRACE((tfp, "HTMLToPlain calling CacheThru_new\n"));
+    return CacheThru_new(anchor,
+			 SGML_new(&HTML_dtd, anchor,
+				  HTML_new(anchor, pres->rep_out, sink)));
+}
+
+/*	HTConverter for HTML source to plain text
+ *	-----------------------------------------
+ *
+ *	This will preparse HTML and convert back to presentation or plain text.
+ *
+ *	It is registered in HTInit.c and used by lynx if invoked with
+ *	-preparsed.  The stream generated here will be fed with HTML text,
+ *	It feeds that to the SGML.c parser, which in turn feeds an HTMLGen.c
+ *	structured stream for regenerating flat text; the latter should
+ *	end up being handled as text/plain. - kw
+ */
+HTStream *HTMLParsedPresent(HTPresentation *pres,
+			    HTParentAnchor *anchor,
+			    HTStream *sink)
+{
+    HTStream *intermediate = sink;
+
+    if (!intermediate) {
+	/*
+	 * Trick to prevent HTPlainPresent from translating again.  Temporarily
+	 * change UCT_STAGE_PARSER setting in anchor while the HTPlain stream
+	 * is initialized, so that HTPlain sees its input and output charsets
+	 * as the same.  - kw
+	 */
+	int old_parser_cset = HTAnchor_getUCLYhndl(anchor, UCT_STAGE_PARSER);
+	int structured_cset = HTAnchor_getUCLYhndl(anchor, UCT_STAGE_STRUCTURED);
+
+	if (structured_cset < 0)
+	    structured_cset = HTAnchor_getUCLYhndl(anchor, UCT_STAGE_HTEXT);
+	if (structured_cset < 0)
+	    structured_cset = current_char_set;
+	HTAnchor_setUCInfoStage(anchor, structured_cset,
+				UCT_STAGE_PARSER, UCT_SETBY_MIME);
+	if (pres->rep_out == WWW_SOURCE) {
+	    /*  same effect as
+	       intermediate = HTPlainPresent(pres, anchor, NULL);
+	       just written in a more general way:
+	     */
+	    intermediate = HTStreamStack(WWW_PLAINTEXT, WWW_PRESENT,
+					 NULL, anchor);
+	} else {
+	    /*  this too should amount to calling HTPlainPresent: */
+	    intermediate = HTStreamStack(WWW_PLAINTEXT, pres->rep_out,
+					 NULL, anchor);
+	}
+	if (old_parser_cset != structured_cset) {
+	    HTAnchor_resetUCInfoStage(anchor, old_parser_cset,
+				      UCT_STAGE_PARSER, UCT_SETBY_NONE);
+	    if (old_parser_cset >= 0) {
+		HTAnchor_setUCInfoStage(anchor, old_parser_cset,
+					UCT_STAGE_PARSER,
+					UCT_SETBY_DEFAULT + 1);
+	    }
+	}
+    }
+    if (!intermediate)
+	return NULL;
+    CTRACE((tfp, "HTMLParsedPresent calling CacheThru_new\n"));
+    return CacheThru_new(anchor,
+			 SGML_new(&HTML_dtd, anchor,
+				  HTMLGenerator(intermediate)));
+}
+
+/*	HTConverter for HTML to C code
+ *	------------------------------
+ *
+ *	C code is like plain text but all non-preformatted code
+ *	is commented out.
+ *	This will convert from HTML to presentation or plain text.
+ *
+ *	It is registered in HTInit.c, but normally not used by lynx.
+ *	- kw 1999-03-15
+ */
+HTStream *HTMLToC(HTPresentation *pres GCC_UNUSED,
+		  HTParentAnchor *anchor,
+		  HTStream *sink)
+{
+    HTStructured *html;
+
+    if (sink)
+	(*sink->isa->put_string) (sink, "/* ");		/* Before even title */
+    html = HTML_new(anchor, WWW_PLAINTEXT, sink);
+    html->comment_start = "/* ";
+    html->comment_end = " */\n";	/* Must start in col 1 for cpp */
+    if (!sink)
+	HTML_put_string(html, html->comment_start);
+    CTRACE((tfp, "HTMLToC calling CacheThru_new\n"));
+    return CacheThru_new(anchor,
+			 SGML_new(&HTML_dtd, anchor, html));
+}
+
+/*	Presenter for HTML
+ *	------------------
+ *
+ *	This will convert from HTML to presentation or plain text.
+ *
+ * (Comment from original libwww:)
+ *	Override this if you have a windows version
+ */
+#ifndef GUI
+HTStream *HTMLPresent(HTPresentation *pres GCC_UNUSED,
+		      HTParentAnchor *anchor,
+		      HTStream *sink GCC_UNUSED)
+{
+    CTRACE((tfp, "HTMLPresent calling CacheThru_new\n"));
+    return CacheThru_new(anchor,
+			 SGML_new(&HTML_dtd, anchor,
+				  HTML_new(anchor, WWW_PRESENT, NULL)));
+}
+#endif /* !GUI */
+
+/* (Comments from original libwww:) */
+/*	Record error message as a hypertext object
+ *	------------------------------------------
+ *
+ *	The error message should be marked as an error so that
+ *	it can be reloaded later.
+ *	This implementation just throws up an error message
+ *	and leaves the document unloaded.
+ *	A smarter implementation would load an error document,
+ *	marking at such so that it is retried on reload.
+ *
+ * On entry,
+ *	sink	is a stream to the output device if any
+ *	number	is the HTTP error number
+ *	message is the human readable message.
+ *
+ * On exit,
+ *	returns a negative number to indicate lack of success in the load.
+ */
+/* (We don't actually do any of that hypertext stuff for errors,
+   the trivial implementation for lynx just generates a message
+   and returns. - kw 1999-03-15)
+*/
+int HTLoadError(HTStream *sink GCC_UNUSED, int number,
+		const char *message)
+{
+    HTAlert(message);		/* @@@@@@@@@@@@@@@@@@@ */
+    return -number;
+}
+
+static char *MakeNewTitle(const char **value, int src_type)
+{
+    char *ptr;
+    char *newtitle = NULL;
+
+    StrAllocCopy(newtitle, "[");
+    if (value != 0 && value[src_type] != 0) {
+	ptr = strrchr(value[src_type], '/');
+	if (!ptr) {
+	    StrAllocCat(newtitle, value[src_type]);
+	} else {
+	    StrAllocCat(newtitle, ptr + 1);
+	}
+    } else {
+	ptr = 0;
+    }
+#ifdef SH_EX			/* 1998/04/02 (Thu) 16:02:00 */
+
+    /* for proxy server 1998/12/19 (Sat) 11:53:30 */
+    if (AS_casecomp(newtitle + 1, "internal-gopher-menu") == 0) {
+	StrAllocCopy(newtitle, "+");
+    } else if (AS_casecomp(newtitle + 1, "internal-gopher-unknown") == 0) {
+	StrAllocCopy(newtitle, " ");
+    } else {
+	/* normal title */
+	ptr = strrchr(newtitle, '.');
+	if (ptr) {
+	    if (AS_casecomp(ptr, ".gif") == 0)
+		*ptr = '\0';
+	    else if (AS_casecomp(ptr, ".jpg") == 0)
+		*ptr = '\0';
+	    else if (AS_casecomp(ptr, ".jpeg") == 0)
+		*ptr = '\0';
+	}
+	StrAllocCat(newtitle, "]");
+    }
+#else
+    StrAllocCat(newtitle, "]");
+#endif
+    return newtitle;
+}
+
+static char *MakeNewImageValue(const char **value)
+{
+    char *ptr;
+    char *newtitle = NULL;
+
+    StrAllocCopy(newtitle, "[");
+    ptr = (value[HTML_INPUT_SRC]
+	   ? strrchr(value[HTML_INPUT_SRC], '/')
+	   : 0);
+    if (!ptr) {
+	StrAllocCat(newtitle, value[HTML_INPUT_SRC]);
+    } else {
+	StrAllocCat(newtitle, ptr + 1);
+    }
+    StrAllocCat(newtitle, "]-Submit");
+    return newtitle;
+}
+
+static char *MakeNewMapValue(const char **value, const char *mapstr)
+{
+    char *ptr;
+    char *newtitle = NULL;
+
+    StrAllocCopy(newtitle, "[");
+    StrAllocCat(newtitle, mapstr);	/* ISMAP or USEMAP */
+    if (verbose_img && non_empty(value[HTML_IMG_SRC])) {
+	StrAllocCat(newtitle, ":");
+	ptr = strrchr(value[HTML_IMG_SRC], '/');
+	if (!ptr) {
+	    StrAllocCat(newtitle, value[HTML_IMG_SRC]);
+	} else {
+	    StrAllocCat(newtitle, ptr + 1);
+	}
+    }
+    StrAllocCat(newtitle, "]");
+    return newtitle;
+}
diff --git a/src/HTML.h b/src/HTML.h
new file mode 100644
index 00000000..695ed9f2
--- /dev/null
+++ b/src/HTML.h
@@ -0,0 +1,276 @@
+/*					HTML to rich text converter for libwww
+ *
+ *			THE HTML TO RTF OBJECT CONVERTER
+ *
+ *  This interprets the HTML semantics.
+ */
+#ifndef HTML_H
+#define HTML_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif /* HTUTILS_H */
+
+#include <UCDefs.h>
+#include <UCAux.h>
+#include <HTAnchor.h>
+#include <HTMLDTD.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* #define ATTR_CS_IN (me->T.output_utf8 ? me->UCLYhndl : 0) */
+#define ATTR_CS_IN me->tag_charset
+#define TRANSLATE_AND_UNESCAPE_ENTITIES(s, p, h) \
+	LYUCTranslateHTMLString(s, ATTR_CS_IN, current_char_set, YES, p, h, st_HTML)
+#define TRANSLATE_AND_UNESCAPE_ENTITIES5(s,cs_from,cs_to,p,h) \
+	LYUCTranslateHTMLString(s, cs_from, cs_to, YES, p, h, st_HTML)
+#define TRANSLATE_AND_UNESCAPE_ENTITIES6(s,cs_from,cs_to,spcls,p,h) \
+	LYUCTranslateHTMLString(s, cs_from, cs_to, spcls, p, h, st_HTML)
+#define TRANSLATE_HTML(s,p,h) \
+	LYUCFullyTranslateString(s, me->UCLYhndl, current_char_set, NO, YES, p, h, NO, st_HTML)
+#define TRANSLATE_HTML5(s,cs_from,cs_to,p,h) \
+	LYUCFullyTranslateString(s, cs_from, cs_to, NO, YES, p, h, NO, st_HTML)
+#define TRANSLATE_HTML7(s,cs_from,cs_to,spcls,p,h,Back) \
+	LYUCFullyTranslateString(s, cs_from, cs_to, NO, spcls, p, h, Back, st_HTML)
+/*
+ * Strings from attributes which should be converted to some kind of "standard"
+ * representation (character encoding), was Latin-1, esp.  URLs (incl. 
+ * #fragments) and HTML NAME and ID stuff.
+ */
+#define TRANSLATE_AND_UNESCAPE_TO_STD(s) \
+	LYUCTranslateHTMLString(s, ATTR_CS_IN, ATTR_CS_IN, NO, NO, YES, st_URL)
+#define UNESCAPE_FIELDNAME_TO_STD(s) \
+	LYUCTranslateHTMLString(s, ATTR_CS_IN, ATTR_CS_IN, NO, NO, YES, st_HTML)
+    extern const HTStructuredClass HTMLPresentation;
+
+#ifdef Lynx_HTML_Handler
+/*
+ *	This section is semi-private to HTML.c and it's helper modules. - FM
+ *	--------------------------------------------------------------------
+ */
+
+    typedef struct _stack_element {
+	HTStyle *style;
+	int tag_number;
+    } stack_element;
+
+/*		HTML Object
+ *		-----------
+ */
+#define MAX_NESTING 800		/* Should be checked by parser */
+
+    struct _HTStructured {
+	const HTStructuredClass *isa;
+	HTParentAnchor *node_anchor;
+	HText *text;
+
+	HTStream *target;	/* Output stream */
+	HTStreamClass targetClass;	/* Output routines */
+
+	HTChildAnchor *CurrentA;	/* current HTML_A anchor */
+	int CurrentANum;	/* current HTML_A number */
+	char *base_href;	/* current HTML_BASE href */
+	char *map_address;	/* current HTML_MAP address */
+
+	HTChunk title;		/* Grow by 128 */
+	HTChunk object;		/* Grow by 128 */
+	BOOL object_started;
+	BOOL object_declare;
+	BOOL object_shapes;
+	BOOL object_ismap;
+	char *object_usemap;
+	char *object_id;
+	char *object_title;
+	char *object_data;
+	char *object_type;
+	char *object_classid;
+	char *object_codebase;
+	char *object_codetype;
+	char *object_name;
+	int objects_mixed_open, objects_figged_open;
+	HTChunk option;		/* Grow by 128 */
+	BOOL first_option;	/* First OPTION in SELECT? */
+	char *LastOptionValue;
+	BOOL LastOptionChecked;
+	BOOL select_disabled;
+	HTChunk textarea;	/* Grow by 128 */
+	char *textarea_name;
+	int textarea_name_cs;
+	char *textarea_accept_cs;
+	int textarea_cols;
+	int textarea_rows;
+	int textarea_disabled;
+	char *textarea_id;
+	HTChunk math;		/* Grow by 128 */
+	HTChunk style_block;	/* Grow by 128 */
+	HTChunk script;		/* Grow by 128 */
+
+	/*
+	 *  Used for nested lists. - FM
+	 */
+	int List_Nesting_Level;	/* counter for list nesting level */
+	int OL_Counter[12];	/* counter for ordered lists */
+	char OL_Type[12];	/* types for ordered lists */
+	int Last_OL_Count;	/* last count in ordered lists */
+	char Last_OL_Type;	/* last type in ordered lists */
+
+	int Division_Level;
+	short DivisionAlignments[MAX_NESTING];
+	int Underline_Level;
+	int Quote_Level;
+
+	BOOL UsePlainSpace;
+	BOOL HiddenValue;
+	int lastraw;
+
+	char *comment_start;	/* for literate programming */
+	char *comment_end;
+
+	HTTag *current_tag;
+	BOOL style_change;
+	HTStyle *new_style;
+	HTStyle *old_style;
+	int current_default_alignment;
+	BOOL in_word;		/* Have just had a non-white char */
+	stack_element stack[MAX_NESTING];
+	stack_element *sp;	/* Style stack pointer */
+	BOOL stack_overrun;	/* Was MAX_NESTING exceeded? */
+	int skip_stack;		/* flag to skip next style stack operation */
+
+	/*
+	 *  Track if we are in an anchor, paragraph, address, base, etc.
+	 */
+	BOOL inA;
+	BOOL inAPPLET;
+	BOOL inAPPLETwithP;
+	BOOL inBadBASE;
+	BOOL inBadHREF;
+	BOOL inBadHTML;
+	BOOL inBASE;
+	BOOL inBoldA;
+	BOOL inBoldH;
+	BOOL inCAPTION;
+	BOOL inCREDIT;
+	BOOL inFIG;
+	BOOL inFIGwithP;
+	BOOL inFONT;
+	BOOL inFORM;
+	BOOL inLABEL;
+	BOOL inP;
+	BOOL inPRE;
+	BOOL inSELECT;
+	BOOL inTABLE;
+	BOOL inTEXTAREA;
+	BOOL inUnderline;
+
+	BOOL needBoldH;
+
+	char *xinclude;		/* if no include strin address passed */
+	/*
+	 * UCI and UCLYhndl give the UCInfo and charset registered for the HTML
+	 * parser in the node_anchor's UCStages structure.  It indicates what is
+	 * fed to the HTML parser as the stream of character data (not necessarily
+	 * tags and attributes).  It should currently always be set to be the same
+	 * as UCI and UCLhndl for the HTEXT stage in the node_anchor's UCStages
+	 * structure, since the HTML parser sends its input character data to the
+	 * output without further charset translation.
+	 */
+	LYUCcharset *UCI;
+	int UCLYhndl;
+	/*
+	 * inUCI and inUCLYhndl indicate the UCInfo and charset which the HTML
+	 * parser treats at the input charset.  It is normally set to the UCI and
+	 * UCLhndl for the SGML parser in the node_anchor's UCStages structure
+	 * (which may be a dummy, based on the MIME parser's UCI and UCLhndl in
+	 * that structure, when we are handling a local file or non-http(s)
+	 * gateway).  It could be changed temporarily by the HTML parser, for
+	 * conversions of attribute strings, but should be reset once done.  - FM
+	 */
+	LYUCcharset *inUCI;
+	int inUCLYhndl;
+	/*
+	 * outUCI and outUCLYhndl indicate the UCInfo and charset which the HTML
+	 * parser treats as the output charset.  It is normally set to its own UCI
+	 * and UCLhndl.  It could be changed for conversions of attribute strings,
+	 * but should be reset once done.  - FM
+	 */
+	LYUCcharset *outUCI;
+	int outUCLYhndl;
+	/*
+	 * T holds the transformation rules for conversions of strings between the
+	 * input and output charsets by the HTML parser.  - FM
+	 */
+	UCTransParams T;
+
+	int tag_charset;	/* charset for attribute values etc. */
+    };
+
+    extern HTStyle *LYstyles(int style_number);
+    extern BOOL LYBadHTML(HTStructured * me);
+    extern void LYShowBadHTML(const char *s);
+
+/*
+ *	Semi-Private functions. - FM
+ */
+    extern void HTML_put_character(HTStructured * me, char c);
+    extern void HTML_put_string(HTStructured * me, const char *s);
+    extern void HTML_write(HTStructured * me, const char *s, int l);
+    extern int HTML_put_entity(HTStructured * me, int entity_number);
+    extern void actually_set_style(HTStructured * me);
+
+/*	Style buffering avoids dummy paragraph begin/ends.
+*/
+#define UPDATE_STYLE if (me->style_change) { actually_set_style(me); }
+#endif				/* Lynx_HTML_Handler */
+
+    extern void strtolower(char *i);
+
+/*				P U B L I C
+*/
+
+/*
+ *  HTConverter to present HTML
+ */
+    extern HTStream *HTMLToPlain(HTPresentation *pres,
+				 HTParentAnchor *anchor,
+				 HTStream *sink);
+
+    extern HTStream *HTMLParsedPresent(HTPresentation *pres,
+				       HTParentAnchor *anchor,
+				       HTStream *sink);
+
+    extern HTStream *HTMLToC(HTPresentation *pres,
+			     HTParentAnchor *anchor,
+			     HTStream *sink);
+
+    extern HTStream *HTMLPresent(HTPresentation *pres,
+				 HTParentAnchor *anchor,
+				 HTStream *sink);
+
+    extern HTStructured *HTML_new(HTParentAnchor *anchor,
+				  HTFormat format_out,
+				  HTStream *target);
+
+/*
+ * Record error message as a hypertext object.
+ *
+ * The error message should be marked as an error so that it can be reloaded
+ * later.  This implementation just throws up an error message and leaves the
+ * document unloaded.
+ *
+ * On entry,
+ *      sink    is a stream to the output device if any
+ *      number  is the HTTP error number
+ *      message is the human readable message.
+ * On exit,
+ *      a return code like HT_LOADED if object exists else 60; 0
+ */
+    extern int HTLoadError(HTStream *sink,
+			   int number,
+			   const char *message);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* HTML_H */
diff --git a/src/HTNestedList.h b/src/HTNestedList.h
new file mode 100644
index 00000000..5a5f1039
--- /dev/null
+++ b/src/HTNestedList.h
@@ -0,0 +1,44 @@
+#ifndef HTNESTEDLIST_H
+#define HTNESTEDLIST_H
+
+#define HTML_OL1        (HTML_ELEMENTS+1)
+#define HTML_OL2        (HTML_ELEMENTS+2)
+#define HTML_OL3        (HTML_ELEMENTS+3)
+#define HTML_OL4        (HTML_ELEMENTS+4)
+#define HTML_OL5        (HTML_ELEMENTS+5)
+#define HTML_OL6        (HTML_ELEMENTS+6)
+
+#define HTML_MENU1      (HTML_ELEMENTS+7)
+#define HTML_MENU2      (HTML_ELEMENTS+8)
+#define HTML_MENU3      (HTML_ELEMENTS+9)
+#define HTML_MENU4      (HTML_ELEMENTS+10)
+#define HTML_MENU5      (HTML_ELEMENTS+11)
+#define HTML_MENU6      (HTML_ELEMENTS+12)
+
+#define HTML_DL1        (HTML_ELEMENTS+13)
+#define HTML_DL2        (HTML_ELEMENTS+14)
+#define HTML_DL3        (HTML_ELEMENTS+15)
+#define HTML_DL4        (HTML_ELEMENTS+16)
+#define HTML_DL5        (HTML_ELEMENTS+17)
+#define HTML_DL6        (HTML_ELEMENTS+18)
+
+#define HTML_DLC1       (HTML_ELEMENTS+19)
+#define HTML_DLC2       (HTML_ELEMENTS+20)
+#define HTML_DLC3       (HTML_ELEMENTS+21)
+#define HTML_DLC4       (HTML_ELEMENTS+22)
+#define HTML_DLC5       (HTML_ELEMENTS+23)
+#define HTML_DLC6       (HTML_ELEMENTS+24)
+
+#define HTML_HCENTER  	(HTML_ELEMENTS+25)
+#define HTML_HLEFT      (HTML_ELEMENTS+26)
+#define HTML_HRIGHT     (HTML_ELEMENTS+27)
+
+#define HTML_DCENTER    (HTML_ELEMENTS+28)
+#define HTML_DLEFT      (HTML_ELEMENTS+29)
+#define HTML_DRIGHT     (HTML_ELEMENTS+30)
+
+#define HTML_OBJECT_M   (HTML_ELEMENTS+31)
+
+#define LYNX_HTML_EXTRA_ELEMENTS 31
+
+#endif /* HTNESTEDLIST_H */
diff --git a/src/HTSaveToFile.h b/src/HTSaveToFile.h
new file mode 100644
index 00000000..35ce3904
--- /dev/null
+++ b/src/HTSaveToFile.h
@@ -0,0 +1,29 @@
+#ifndef HTSAVETOFILE_H
+#define HTSAVETOFILE_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#include <HTStream.h>
+#include <HTFormat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    extern HTStream *HTSaveToFile(HTPresentation *pres,
+				  HTParentAnchor *anchor,
+				  HTStream *sink);
+
+    extern HTStream *HTDumpToStdout(HTPresentation *pres,
+				    HTParentAnchor *anchor,
+				    HTStream *sink);
+
+    extern HTStream *HTCompressed(HTPresentation *pres,
+				  HTParentAnchor *anchor,
+				  HTStream *sink);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* HTSAVETOFILE_H */
diff --git a/src/LYBookmark.c b/src/LYBookmark.c
new file mode 100644
index 00000000..4f0e93ce
--- /dev/null
+++ b/src/LYBookmark.c
@@ -0,0 +1,1123 @@
+/*
+ * $LynxId: LYBookmark.c,v 1.65 2010/04/30 09:45:08 tom Exp $
+ */
+#include <HTUtils.h>
+#include <HTAlert.h>
+#include <HTFile.h>
+#include <LYUtils.h>
+#include <LYStrings.h>
+#include <LYBookmark.h>
+#include <LYGlobalDefs.h>
+#include <LYClean.h>
+#include <LYKeymap.h>
+#include <LYCharUtils.h>	/* need for META charset */
+#include <UCAux.h>
+#include <LYCharSets.h>		/* need for LYHaveCJKCharacterSet */
+#include <LYCurses.h>
+#include <GridText.h>
+#include <HTCJK.h>
+
+#ifdef VMS
+#include <nam.h>
+#endif /* VMS */
+
+#include <LYLeaks.h>
+
+char *MBM_A_subbookmark[MBM_V_MAXFILES + 1];
+char *MBM_A_subdescript[MBM_V_MAXFILES + 1];
+
+static BOOLEAN is_mosaic_hotlist = FALSE;
+static const char *convert_mosaic_bookmark_file(const char *filename_buffer);
+
+int LYindex2MBM(int n)
+{
+    static char MBMcodes[MBM_V_MAXFILES + 2] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+    return n >= 0 && n <= MBM_V_MAXFILES ? MBMcodes[n] : '?';
+}
+
+int LYMBM2index(int ch)
+{
+    if ((ch = TOUPPER(ch)) > 0) {
+	const char *letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+	const char *result = strchr(letters, ch);
+
+	if (result != 0
+	    && (result - letters) <= MBM_V_MAXFILES)
+	    return (result - letters);
+    }
+    return -1;
+}
+
+static void show_bookmark_not_defined(void)
+{
+    char *string_buffer = 0;
+
+    HTSprintf0(&string_buffer,
+	       BOOKMARK_FILE_NOT_DEFINED,
+	       key_for_func(LYK_OPTIONS));
+    LYMBM_statusline(string_buffer);
+    FREE(string_buffer);
+}
+
+/*
+ * Tries to open a bookmark file for reading, which may be the default, or
+ * based on offering the user a choice from the MBM_A_subbookmark[] array.  If
+ * successful the file is closed, and the filename in system path specs is
+ * returned, the URL is allocated into *URL, and the MBM_A_subbookmark[]
+ * filepath is allocated into the BookmarkPage global.  Returns a zero-length
+ * pointer to flag a cancel, or a space to flag an undefined selection, without
+ * allocating into *URL or BookmarkPage.  Returns NULL with allocating into
+ * BookmarkPage but not *URL is the selection is valid but the file doesn't yet
+ * exist.  - FM
+ */
+const char *get_bookmark_filename(char **URL)
+{
+    static char filename_buffer[LY_MAXPATH];
+    char *string_buffer = 0;
+    FILE *fp;
+    int MBM_tmp;
+
+    /*
+     * Multi_Bookmarks support.  - FMG & FM
+     * Let user select a bookmark file.
+     */
+    MBM_tmp = select_multi_bookmarks();
+    if (MBM_tmp == -2)
+	/*
+	 * Zero-length pointer flags a cancel.  - FM
+	 */
+	return ("");
+    if (MBM_tmp == -1) {
+	show_bookmark_not_defined();
+	/*
+	 * Space flags an undefined selection.  - FMG
+	 */
+	return (" ");
+    } else {
+	/*
+	 * Save the filepath as a global.  The system path will be loaded into
+	 * to the (static) filename_buffer as the return value, the URL will be
+	 * allocated into *URL, and we also need the filepath available to
+	 * calling functions.  This is all pitifully non-reentrant, a la the
+	 * original Lynx, and should be redesigned someday.  - FM
+	 */
+	StrAllocCopy(BookmarkPage, MBM_A_subbookmark[MBM_tmp]);
+    }
+
+    /*
+     * Seek it in the home path.  - FM
+     */
+    LYAddPathToHome(filename_buffer,
+		    sizeof(filename_buffer),
+		    BookmarkPage);
+    CTRACE((tfp, "\nget_bookmark_filename: SEEKING %s\n   AS %s\n\n",
+	    BookmarkPage, filename_buffer));
+    if ((fp = fopen(filename_buffer, TXT_R)) != NULL) {
+	/*
+	 * We now have the file open.
+	 * Check if it is a mosaic hotlist.
+	 */
+	if (LYSafeGets(&string_buffer, fp) != 0
+	    && *LYTrimNewline(string_buffer) != '\0'
+	    && !strncmp(string_buffer, "ncsa-xmosaic-hotlist-format-1", 29)) {
+	    const char *newname;
+
+	    /*
+	     * It is a mosaic hotlist file.
+	     */
+	    is_mosaic_hotlist = TRUE;
+	    newname = convert_mosaic_bookmark_file(filename_buffer);
+	    LYLocalFileToURL(URL, newname);
+	} else {
+	    is_mosaic_hotlist = FALSE;
+	    LYLocalFileToURL(URL, filename_buffer);
+	}
+	FREE(string_buffer);
+	LYCloseInput(fp);
+
+	return (filename_buffer);	/* bookmark file exists */
+    }
+    return (NULL);
+
+}				/* big end */
+
+/*
+ * Converts a Mosaic hotlist file into an HTML file for handling as a Lynx
+ * bookmark file.  - FM
+ */
+static const char *convert_mosaic_bookmark_file(const char *filename_buffer)
+{
+    static char newfile[LY_MAXPATH];
+    FILE *fp, *nfp;
+    char *buf = NULL;
+    int line = -2;
+
+    LYRemoveTemp(newfile);
+    if ((nfp = LYOpenTemp(newfile, HTML_SUFFIX, "w")) == NULL) {
+	LYMBM_statusline(NO_TEMP_FOR_HOTLIST);
+	LYSleepAlert();
+	return ("");
+    }
+
+    if ((fp = fopen(filename_buffer, TXT_R)) == NULL)
+	return ("");		/* should always open */
+
+    fprintf(nfp, "<head>\n<title>%s</title>\n</head>\n", MOSAIC_BOOKMARK_TITLE);
+    fprintf(nfp, "%s\n\n<p>\n<ol>\n", gettext("\
+     This file is an HTML representation of the X Mosaic hotlist file.\n\
+     Outdated or invalid links may be removed by using the\n\
+     remove bookmark command, it is usually the 'R' key but may have\n\
+     been remapped by you or your system administrator."));
+
+    while ((LYSafeGets(&buf, fp)) != NULL) {
+	if (line >= 0) {
+	    LYTrimNewline(buf);
+	    if ((line % 2) == 0) {	/* even lines */
+		if (*buf != '\0') {
+		    strtok(buf, " ");	/* kill everything after the space */
+		    fprintf(nfp, "<LI><a href=\"%s\">", buf);	/* the URL */
+		}
+	    } else {		/* odd lines */
+		fprintf(nfp, "%s</a>\n", buf);	/* the title */
+	    }
+	}
+	/* else - ignore the line (this gets rid of first two lines) */
+	line++;
+    }
+    LYCloseTempFP(nfp);
+    LYCloseInput(fp);
+    return (newfile);
+}
+
+static BOOLEAN havevisible(const char *Title);
+static BOOLEAN have8bit(const char *Title);
+static char *title_convert8bit(const char *Title);
+
+/*
+ * Adds a link to a bookmark file, creating the file if it doesn't already
+ * exist, and making sure that no_cache is set for a pre-existing, cached file,
+ * so that the change will be evident on return to to that file.  - FM
+ */
+void save_bookmark_link(const char *address,
+			const char *title)
+{
+    FILE *fp;
+    BOOLEAN first_time = FALSE;
+    const char *filename;
+    char *bookmark_URL = NULL;
+    char filename_buffer[LY_MAXPATH];
+    char string_buffer[BUFSIZ];
+    char tmp_buffer[BUFSIZ];
+    char *Address = NULL;
+    char *Title = NULL;
+    int i, c;
+    DocAddress WWWDoc;
+    HTParentAnchor *tmpanchor;
+    HText *text;
+
+    /*
+     * Make sure we were passed something to save.  - FM
+     */
+    if (isEmpty(address)) {
+	HTAlert(MALFORMED_ADDRESS);
+	return;
+    }
+
+    /*
+     * Offer a choice of bookmark files, or get the default.  - FMG
+     */
+    filename = get_bookmark_filename(&bookmark_URL);
+
+    /*
+     * If filename is NULL, must create a new file.  If filename is a space, an
+     * invalid bookmark file was selected, or if zero-length, the user
+     * cancelled.  Ignore request in both cases.  Otherwise, make a copy before
+     * anything might change the static get_bookmark_filename() buffer.  - FM
+     */
+    if (filename == NULL) {
+	first_time = TRUE;
+	filename_buffer[0] = '\0';
+    } else {
+	if (*filename == '\0' || !strcmp(filename, " ")) {
+	    FREE(bookmark_URL);
+	    return;
+	}
+	LYstrncpy(filename_buffer, filename, sizeof(filename_buffer) - 1);
+    }
+
+    /*
+     * If BookmarkPage is NULL, something went wrong, so ignore the request.  -
+     * FM
+     */
+    if (BookmarkPage == NULL) {
+	FREE(bookmark_URL);
+	return;
+    }
+
+    /*
+     * If the link will be added to the same bookmark file, get confirmation. 
+     * - FM
+     */
+    if (LYMultiBookmarks != MBM_OFF) {
+	const char *url = HTLoadedDocumentURL();
+	const char *page = (*BookmarkPage == '.')
+	? (BookmarkPage + 1)
+	: BookmarkPage;
+
+	if (strstr(url, page) != NULL) {
+	    LYMBM_statusline(MULTIBOOKMARKS_SELF);
+	    c = LYgetch_single();
+	    if (c != 'L') {
+		FREE(bookmark_URL);
+		return;
+	    }
+	}
+    }
+
+    /*
+     * Allow user to change the title.  - FM
+     */
+    do {
+	if (HTCJK == JAPANESE) {
+	    switch (kanji_code) {
+	    case EUC:
+		TO_EUC((const unsigned char *) title, (unsigned char *) tmp_buffer);
+		break;
+	    case SJIS:
+		TO_SJIS((const unsigned char *) title, (unsigned char *) tmp_buffer);
+		break;
+	    default:
+		break;
+	    }
+	    LYstrncpy(string_buffer, tmp_buffer, sizeof(string_buffer) - 1);
+	} else {
+	    LYstrncpy(string_buffer, title, sizeof(string_buffer) - 1);
+	}
+	LYReduceBlanks(string_buffer);
+	LYMBM_statusline(TITLE_PROMPT);
+	LYgetstr(string_buffer, VISIBLE, sizeof(string_buffer), NORECALL);
+	if (*string_buffer == '\0') {
+	    LYMBM_statusline(CANCELLED);
+	    LYSleepMsg();
+	    FREE(bookmark_URL);
+	    return;
+	}
+    } while (!havevisible(string_buffer));
+
+    /*
+     * Create the Title with any left-angle-brackets converted to &lt; entities
+     * and any ampersands converted to &amp; entities.  - FM
+     *
+     * Convert 8-bit letters to &#xUUUU to avoid dependencies from display
+     * character set which may need changing.  Do NOT convert any 8-bit chars
+     * if we have CJK display.  - LP
+     */
+    LYformTitle(&Title, string_buffer);
+    LYEntify(&Title, TRUE);
+    if (UCSaveBookmarksInUnicode &&
+	have8bit(Title) && (!LYHaveCJKCharacterSet)) {
+	char *p = title_convert8bit(Title);
+
+	FREE(Title);
+	Title = p;
+    }
+
+    /*
+     * Create the bookmark file, if it doesn't exist already, Otherwise, open
+     * the pre-existing bookmark file.  - FM
+     */
+    SetDefaultMode(O_TEXT);
+    if (first_time) {
+	/*
+	 * Seek it in the home path.  - FM
+	 */
+	LYAddPathToHome(filename_buffer,
+			sizeof(filename_buffer),
+			BookmarkPage);
+    }
+    CTRACE((tfp, "\nsave_bookmark_link: SEEKING %s\n   AS %s\n\n",
+	    BookmarkPage, filename_buffer));
+    if ((fp = fopen(filename_buffer, (first_time ? TXT_W : TXT_A))) == NULL) {
+	LYMBM_statusline(BOOKMARK_OPEN_FAILED);
+	LYSleepAlert();
+	FREE(Title);
+	FREE(bookmark_URL);
+	return;
+    }
+
+    /*
+     * Convert all ampersands in the address to &amp; entities.  - FM
+     */
+    StrAllocCopy(Address, address);
+    LYEntify(&Address, FALSE);
+
+    /*
+     * If we created a new bookmark file, write the headers.  - FM
+     * Once and forever...
+     */
+    if (first_time) {
+	fprintf(fp, "<head>\n");
+#if defined(SH_EX) && !defined(_WINDOWS)	/* 1997/12/11 (Thu) 19:13:40 */
+	if (HTCJK != JAPANESE)
+	    LYAddMETAcharsetToFD(fp, -1);
+	else
+	    fprintf(fp, "<META %s %s>\n",
+		    "http-equiv=\"content-type\"",
+		    "content=\"text/html;charset=iso-2022-jp\"");
+#else
+	LYAddMETAcharsetToFD(fp, -1);
+#endif /* !_WINDOWS */
+	fprintf(fp, "<title>%s</title>\n</head>\n", BOOKMARK_TITLE);
+#ifdef _WINDOWS
+	fprintf(fp,
+		gettext("     You can delete links by the 'R' key<br>\n<ol>\n"));
+#else
+	fprintf(fp, "%s<br>\n%s\n\n<!--\n%s\n-->\n\n<p>\n<ol>\n",
+		gettext("\
+     You can delete links using the remove bookmark command.  It is usually\n\
+     the 'R' key but may have been remapped by you or your system\n\
+     administrator."),
+		gettext("\
+     This file also may be edited with a standard text editor to delete\n\
+     outdated or invalid links, or to change their order."),
+		gettext("\
+Note: if you edit this file manually\n\
+      you should not change the format within the lines\n\
+      or add other HTML markup.\n\
+      Make sure any bookmark link is saved as a single line."));
+#endif /* _WINDOWS */
+    }
+
+    /*
+     * Add the bookmark link, in Mosaic hotlist or Lynx format.  - FM
+     */
+    if (is_mosaic_hotlist) {
+	time_t NowTime = time(NULL);
+	char *TimeString = (char *) ctime(&NowTime);
+
+	/*
+	 * TimeString has a \n at the end.
+	 */
+	fprintf(fp, "%s %s%s\n", Address, TimeString, Title);
+    } else {
+	fprintf(fp, "<LI><a href=\"%s\">%s</a>\n", Address, Title);
+    }
+    LYCloseOutput(fp);
+
+    SetDefaultMode(O_BINARY);
+    /*
+     * If this is a cached bookmark file, set nocache for it so we'll see the
+     * new bookmark link when that cache is retrieved.  - FM
+     */
+    if (!first_time && nhist > 0 && bookmark_URL) {
+	for (i = 0; i < nhist; i++) {
+	    if (HDOC(i).bookmark &&
+		!strcmp(HDOC(i).address, bookmark_URL)) {
+		WWWDoc.address = HDOC(i).address;
+		WWWDoc.post_data = NULL;
+		WWWDoc.post_content_type = NULL;
+		WWWDoc.bookmark = HDOC(i).bookmark;
+		WWWDoc.isHEAD = FALSE;
+		WWWDoc.safe = FALSE;
+		tmpanchor = HTAnchor_findAddress(&WWWDoc);
+		if ((text = (HText *) HTAnchor_document(tmpanchor)) != NULL) {
+		    HText_setNoCache(text);
+		}
+		break;
+	    }
+	}
+    }
+
+    /*
+     * Clean up and report success.
+     */
+    FREE(Title);
+    FREE(Address);
+    FREE(bookmark_URL);
+    LYMBM_statusline(OPERATION_DONE);
+    LYSleepMsg();
+}
+
+/*
+ * Remove a link from a bookmark file.  The calling function is expected to
+ * have used get_filename_link(), pass us the link number as cur, the
+ * MBM_A_subbookmark[] string as cur_bookmark_page, and to have set up no_cache
+ * itself.  - FM
+ */
+void remove_bookmark_link(int cur,
+			  char *cur_bookmark_page)
+{
+    FILE *fp, *nfp;
+    char *buf = NULL;
+    int n;
+
+#ifdef VMS
+    char filename_buffer[NAM$C_MAXRSS + 12];
+    char newfile[NAM$C_MAXRSS + 12];
+
+#define keep_tempfile FALSE
+#else
+    char filename_buffer[LY_MAXPATH];
+    char newfile[LY_MAXPATH];
+    BOOLEAN keep_tempfile = FALSE;
+
+#ifdef UNIX
+    struct stat stat_buf;
+    mode_t mode;
+    BOOLEAN regular = FALSE;
+#endif /* UNIX */
+#endif /* VMS */
+    char homepath[LY_MAXPATH];
+
+    CTRACE((tfp, "remove_bookmark_link: deleting link number: %d\n", cur));
+
+    if (!cur_bookmark_page)
+	return;
+    LYAddPathToHome(filename_buffer,
+		    sizeof(filename_buffer),
+		    cur_bookmark_page);
+    CTRACE((tfp, "\nremove_bookmark_link: SEEKING %s\n   AS %s\n\n",
+	    cur_bookmark_page, filename_buffer));
+    if ((fp = fopen(filename_buffer, TXT_R)) == NULL) {
+	HTAlert(BOOKMARK_OPEN_FAILED_FOR_DEL);
+	return;
+    }
+
+    LYAddPathToHome(homepath, sizeof(homepath), "");
+    if ((nfp = LYOpenScratch(newfile, homepath)) == 0) {
+	LYCloseInput(fp);
+	HTAlert(BOOKSCRA_OPEN_FAILED_FOR_DEL);
+	return;
+    }
+#ifdef UNIX
+    /*
+     * Explicitly preserve bookmark file mode on Unix.  - DSL
+     */
+    if (stat(filename_buffer, &stat_buf) == 0) {
+	regular = (BOOLEAN) (S_ISREG(stat_buf.st_mode) && stat_buf.st_nlink == 1);
+	mode = ((stat_buf.st_mode & HIDE_CHMOD) | 0600);	/* make it writable */
+	(void) chmod(newfile, mode);
+	if ((nfp = LYReopenTemp(newfile)) == NULL) {
+	    (void) LYCloseInput(fp);
+	    HTAlert(BOOKTEMP_REOPEN_FAIL_FOR_DEL);
+	    return;
+	}
+    }
+#endif /* UNIX */
+
+    if (is_mosaic_hotlist) {
+	int del_line = cur * 2;	/* two lines per entry */
+
+	n = -3;			/* skip past cookie and name lines */
+	while (LYSafeGets(&buf, fp) != NULL) {
+	    n++;
+	    if (n == del_line || n == del_line + 1)
+		continue;	/* remove two lines */
+	    if (fputs(buf, nfp) == EOF)
+		goto failure;
+	}
+
+    } else {
+	char *cp;
+	BOOLEAN retain;
+	int seen;
+
+	n = -1;
+	while (LYSafeGets(&buf, fp) != NULL) {
+	    int keep_ol = FALSE;
+
+	    retain = TRUE;
+	    seen = 0;
+	    cp = buf;
+	    if ((cur == 0) && LYstrstr(cp, "<ol><LI>"))
+		keep_ol = TRUE;	/* Do not erase, this corrects a bug in an
+				   older version */
+	    while (n < cur && (cp = LYstrstr(cp, "<a href="))) {
+		seen++;
+		if (++n == cur) {
+		    if (seen != 1 || !LYstrstr(buf, "</a>") ||
+			LYstrstr((cp + 1), "<a href=")) {
+			HTAlert(BOOKMARK_LINK_NOT_ONE_LINE);
+			goto failure;
+		    }
+		    CTRACE((tfp, "remove_bookmark_link: skipping link %d\n", n));
+		    if (keep_ol)
+			fprintf(nfp, "<ol>\n");
+		    retain = FALSE;
+		}
+		cp += 8;
+	    }
+	    if (retain && fputs(buf, nfp) == EOF)
+		goto failure;
+	}
+    }
+
+    FREE(buf);
+    CTRACE((tfp, "remove_bookmark_link: files: %s %s\n",
+	    newfile, filename_buffer));
+
+    LYCloseInput(fp);
+    fp = NULL;
+    if (fflush(nfp) == EOF) {
+	CTRACE((tfp, "fflush(nfp): %s", LYStrerror(errno)));
+	goto failure;
+    }
+    LYCloseTempFP(nfp);
+    nfp = NULL;
+#if defined(DOSPATH) || defined(__EMX__)
+    remove(filename_buffer);
+#endif /* DOSPATH */
+
+#ifdef UNIX
+    /*
+     * By copying onto the bookmark file, rather than renaming it, we can
+     * preserve the original ownership of the file, provided that it is
+     * writable by the current process.
+     *
+     * Changed to copy 1998-04-26 -- gil
+     *
+     * But if the copy fails, for example because the filesystem is full, we
+     * are left with a corrupt bookmark file.  Changed back to use the previous
+     * mechanism [try rename(), then mv for EXDEV], except in usual cases (not
+     * a regular file e.g., symbolic link, or has hard links).  This will let
+     * bookmarks survive a filesystem full condition in the "normal" case
+     * (bookmark is on same filesystem as home directory, is a regular file,
+     * has no additional hard links).
+     *
+     * If we first tried LYCopyFile, and that fails, also fall back to trying
+     * the other stuff.  That gives a chance to recover in case the LYCopyFile
+     * left a corrupt target file.
+     *
+     * If there is an error, and that error may mean that the bookmark file has
+     * been corrupted, don't remove the temporary newfile (which should always
+     * be uncorrupted) in place, it may still be used to recover manually.  If
+     * this applies, produce an additional message to that effect.  The temp
+     * file will still be removed by normal program exit cleanup.  - kw
+     * 1999-11-12
+     */
+    if (!regular) {
+	if (LYCopyFile(newfile, filename_buffer) == 0) {
+	    LYRemoveTemp(newfile);
+	    return;
+	}
+	LYSleepAlert();		/* give a chance to see error from cp - kw */
+	HTUserMsg(BOOKTEMP_COPY_FAIL);
+	keep_tempfile = TRUE;
+    }
+#endif /* UNIX */
+
+    if (rename(newfile, filename_buffer) != -1) {
+#ifdef MULTI_USER_UNIX
+	if (regular)
+	    chmod(filename_buffer, stat_buf.st_mode & 07777);
+#endif
+	HTSYS_purge(filename_buffer);
+	return;
+    } else {
+#ifndef VMS
+	/*
+	 * Rename won't work across file systems.  Check if this is the case
+	 * and do something appropriate.  Used to be ODD_RENAME
+	 */
+#if defined(_WINDOWS) || defined(WIN_EX)
+#if defined(WIN_EX)
+	if (GetLastError() == ERROR_NOT_SAME_DEVICE)
+#else /* !_WIN_EX */
+	if (errno == ENOTSAM)
+#endif /* _WIN_EX */
+	{
+	    if (rename(newfile, filename_buffer) != 0) {
+		if (LYCopyFile(newfile, filename_buffer) == 0)
+		    remove(newfile);
+	    }
+	}
+#else
+	if (errno == EXDEV) {
+	    static const char MV_FMT[] = "%s %s %s";
+	    char *buffer = 0;
+	    const char *program;
+
+	    if ((program = HTGetProgramPath(ppMV)) != NULL) {
+		HTAddParam(&buffer, MV_FMT, 1, program);
+		HTAddParam(&buffer, MV_FMT, 2, newfile);
+		HTAddParam(&buffer, MV_FMT, 3, filename_buffer);
+		HTEndParam(&buffer, MV_FMT, 3);
+		if (LYSystem(buffer) == 0) {
+#ifdef MULTI_USER_UNIX
+		    if (regular)
+			chmod(filename_buffer, stat_buf.st_mode & 07777);
+#endif
+		    FREE(buffer);
+		    return;
+		}
+	    }
+	    FREE(buffer);
+	    keep_tempfile = TRUE;
+	    goto failure;
+	}
+	CTRACE((tfp, "rename(): %s", LYStrerror(errno)));
+#endif /* _WINDOWS */
+#endif /* !VMS */
+
+#ifdef VMS
+	HTAlert(ERROR_RENAMING_SCRA);
+#else
+	HTAlert(ERROR_RENAMING_TEMP);
+#endif /* VMS */
+	if (TRACE)
+	    perror("renaming the file");
+    }
+
+  failure:
+    FREE(buf);
+    HTAlert(BOOKMARK_DEL_FAILED);
+    if (nfp)
+	LYCloseTempFP(nfp);
+    if (fp != NULL)
+	LYCloseInput(fp);
+    if (keep_tempfile) {
+	HTUserMsg2(gettext("File may be recoverable from %s during this session"),
+		   newfile);
+    } else {
+	LYRemoveTemp(newfile);
+    }
+}
+
+/*
+ * Allows user to select sub-bookmarks files.  - FMG & FM
+ */
+int select_multi_bookmarks(void)
+{
+    int c;
+
+    /*
+     * If not enabled, pick the "default" (0).
+     */
+    if (LYMultiBookmarks == MBM_OFF || LYHaveSubBookmarks() == FALSE) {
+	if (MBM_A_subbookmark[0])	/* If it exists! */
+	    return (0);
+	else
+	    return (-1);
+    }
+
+    /*
+     * For ADVANCED users, we can just mess with the status line to save the 2
+     * redraws of the screen, if LYMBMAdvnced is TRUE.  '=' will still show the
+     * screen and let them do it the "long" way.
+     */
+    if (LYMultiBookmarks == MBM_ADVANCED && user_mode == ADVANCED_MODE) {
+	LYMBM_statusline(MULTIBOOKMARKS_SELECT);
+      get_advanced_choice:
+	c = LYgetch();
+#ifdef VMS
+	if (HadVMSInterrupt) {
+	    HadVMSInterrupt = FALSE;
+	    c = LYCharINTERRUPT2;
+	}
+#endif /* VMS */
+	if (LYisNonAlnumKeyname(c, LYK_PREV_DOC) || LYCharIsINTERRUPT_HARD(c)) {
+	    /*
+	     * Treat left-arrow, ^G, or ^C as cancel.
+	     */
+	    return (-2);
+	}
+	if (LYisNonAlnumKeyname(c, LYK_REFRESH)) {
+	    /*
+	     * Refresh the screen.
+	     */
+	    lynx_force_repaint();
+	    LYrefresh();
+	    goto get_advanced_choice;
+	}
+	if (LYisNonAlnumKeyname(c, LYK_ACTIVATE)) {
+	    /*
+	     * Assume default bookmark file on ENTER or right-arrow.
+	     */
+	    return (MBM_A_subbookmark[0] ? 0 : -1);
+	}
+	switch (c) {
+	case '=':
+	    /*
+	     * Get the choice via the menu.
+	     */
+	    return (select_menu_multi_bookmarks());
+
+	default:
+	    /*
+	     * Convert to an array index, act on it if valid.
+	     * Otherwise, get another keystroke.
+	     */
+	    if ((c = LYMBM2index(c)) < 0) {
+		goto get_advanced_choice;
+	    }
+	}
+	/*
+	 * See if we have a bookmark like that.
+	 */
+	return (MBM_A_subbookmark[c] ? c : -1);
+    } else {
+	/*
+	 * Get the choice via the menu.
+	 */
+	return (select_menu_multi_bookmarks());
+    }
+}
+
+/*
+ * Allows user to select sub-bookmarks files.  - FMG & FM
+ */
+int select_menu_multi_bookmarks(void)
+{
+    int c, d, MBM_tmp_count, MBM_allow;
+    int MBM_screens, MBM_from, MBM_to, MBM_current;
+
+    /*
+     * If not enabled, pick the "default" (0).
+     */
+    if (LYMultiBookmarks == MBM_OFF)
+	return (0);
+
+    /*
+     *        Filip M. Gieszczykiewicz (filipg@paranoia.com) & FM
+     *  ---------------------------------------------------
+     * MBM_A_subbookmark[n] - Hold values of the respective "multi_bookmarkn"
+     * in the lynxrc file.
+     *
+     * MBM_A_subdescript[n] - Hold description entries in the lynxrc file.
+     *
+     * Note: MBM_A_subbookmark[0] is defined to be same value as
+     *       "bookmark_file" in the lynxrc file and/or the startup
+     *       "bookmark_page".
+     *
+     * We make the display of bookmarks depend on rows we have available.
+     *
+     * We load BookmarkPage with the valid MBM_A_subbookmark[n] via
+     * get_bookmark_filename().  Otherwise, that function returns a zero-length
+     * string to indicate a cancel, a single space to indicate an invalid
+     * choice, or NULL to indicate an inaccessible file.
+     */
+    MBM_allow = (LYlines - 7);	/* We need 7 for header and footer */
+    /*
+     * Screen big enough?
+     */
+    if (MBM_allow <= 0) {
+	/*
+	 * Too small.
+	 */
+	HTAlert(MULTIBOOKMARKS_SMALL);
+	return (-2);
+    }
+
+    MBM_screens = (MBM_V_MAXFILES / MBM_allow) + 1;	/* int rounds off low. */
+
+    MBM_current = 1;		/* Gotta start somewhere :-) */
+
+    for (;;) {
+	MBM_from = MBM_allow * MBM_current - MBM_allow;
+	if (MBM_from < 0)
+	    MBM_from = 0;	/* 0 is default bookmark... */
+	if (MBM_current != 1)
+	    MBM_from++;
+
+	MBM_to = (MBM_allow * MBM_current);
+	if (MBM_to > MBM_V_MAXFILES)
+	    MBM_to = MBM_V_MAXFILES;
+
+	/*
+	 * Display menu of bookmarks.  NOTE that we avoid printw()'s to
+	 * increase the chances that any non-ASCII or multibyte/CJK characters
+	 * will be handled properly.  - FM
+	 */
+	LYclear();
+	LYmove(1, 5);
+	lynx_start_h1_color();
+	if (MBM_screens > 1) {
+	    char *shead_buffer = 0;
+
+	    HTSprintf0(&shead_buffer,
+		       MULTIBOOKMARKS_SHEAD_MASK, MBM_current, MBM_screens);
+	    LYaddstr(shead_buffer);
+	    FREE(shead_buffer);
+	} else {
+	    LYaddstr(MULTIBOOKMARKS_SHEAD);
+	}
+
+	lynx_stop_h1_color();
+
+	MBM_tmp_count = 0;
+	for (c = MBM_from; c <= MBM_to; c++) {
+	    LYmove(3 + MBM_tmp_count, 5);
+	    LYaddch((chtype) LYindex2MBM(c));
+	    LYaddstr(" : ");
+	    if (MBM_A_subdescript[c])
+		LYaddstr(MBM_A_subdescript[c]);
+	    LYmove(3 + MBM_tmp_count, 36);
+	    LYaddch('(');
+	    if (MBM_A_subbookmark[c])
+		LYaddstr(MBM_A_subbookmark[c]);
+	    LYaddch(')');
+	    MBM_tmp_count++;
+	}
+
+	/*
+	 * Don't need to show it if it all fits on one screen!
+	 */
+	if (MBM_screens > 1) {
+	    LYmove(LYlines - 2, 0);
+	    LYaddstr("'");
+	    lynx_start_bold();
+	    LYaddstr("[");
+	    lynx_stop_bold();
+	    LYaddstr("' ");
+	    LYaddstr(PREVIOUS);
+	    LYaddstr(", '");
+	    lynx_start_bold();
+	    LYaddstr("]");
+	    lynx_stop_bold();
+	    LYaddstr("' ");
+	    LYaddstr(NEXT_SCREEN);
+	}
+
+	LYMBM_statusline(MULTIBOOKMARKS_SAVE);
+
+	for (;;) {
+	    c = LYgetch();
+#ifdef VMS
+	    if (HadVMSInterrupt) {
+		HadVMSInterrupt = FALSE;
+		c = 7;
+	    }
+#endif /* VMS */
+
+	    if ((d = LYMBM2index(c)) >= 0) {
+		/*
+		 * See if we have a bookmark like that.
+		 */
+		if (MBM_A_subbookmark[d] != NULL)
+		    return (d);
+
+		show_bookmark_not_defined();
+		LYMBM_statusline(MULTIBOOKMARKS_SAVE);
+	    } else if (LYisNonAlnumKeyname(c, LYK_PREV_DOC) ||
+		       c == 7 || c == 3) {
+		/*
+		 * Treat left-arrow, ^G, or ^C as cancel.
+		 */
+		return (-2);
+	    } else if (LYisNonAlnumKeyname(c, LYK_REFRESH)) {
+		/*
+		 * Refresh the screen.
+		 */
+		lynx_force_repaint();
+		LYrefresh();
+	    } else if (LYisNonAlnumKeyname(c, LYK_ACTIVATE)) {
+		/*
+		 * Assume default bookmark file on ENTER or right-arrow.
+		 */
+		return (MBM_A_subbookmark[0] ? 0 : -1);
+	    } else if ((c == ']' || LYisNonAlnumKeyname(c, LYK_NEXT_PAGE)) &&
+		       MBM_screens > 1) {
+		/*
+		 * Next range, if available.
+		 */
+		if (++MBM_current > MBM_screens)
+		    MBM_current = 1;
+		break;
+	    }
+
+	    else if ((c == '[' || LYisNonAlnumKeyname(c, LYK_PREV_PAGE)) &&
+		     MBM_screens > 1) {
+		/*
+		 * Previous range, if available.
+		 */
+		if (--MBM_current <= 0)
+		    MBM_current = MBM_screens;
+		break;
+	    }
+	}
+    }
+}
+
+/*
+ * This function returns TRUE if we have sub-bookmarks defined.  Otherwise
+ * (i.e., only the default bookmark file is defined), it returns FALSE.  - FM
+ */
+BOOLEAN LYHaveSubBookmarks(void)
+{
+    int i;
+
+    for (i = 1; i < MBM_V_MAXFILES; i++) {
+	if (non_empty(MBM_A_subbookmark[i]))
+	    return (TRUE);
+    }
+
+    return (FALSE);
+}
+
+/*
+ * This function passes a string to _statusline(), making sure it is at the
+ * bottom of the screen if LYMultiBookmarks is not MBM_OFF, otherwise, letting
+ * it go to the normal statusline position based on the current user mode.  We
+ * want to use _statusline() so that any multibyte/CJK characters in the string
+ * will be handled properly.  - FM
+ */
+void LYMBM_statusline(const char *text)
+{
+    if (LYMultiBookmarks != MBM_OFF && user_mode == NOVICE_MODE) {
+	LYStatusLine = (LYlines - 1);
+	_statusline(text);
+	LYStatusLine = -1;
+    } else {
+	_statusline(text);
+    }
+}
+
+/*
+ * Check whether we have any visible (non-blank) chars.
+ */
+static BOOLEAN havevisible(const char *Title)
+{
+    BOOLEAN result = FALSE;
+    const char *p = Title;
+    unsigned char c;
+    long unicode;
+
+    for (; *p; p++) {
+	c = UCH(TOASCII(*p));
+	if (c > 32 && c < 127) {
+	    result = TRUE;
+	    break;
+	}
+	if (c <= 32 || c == 127)
+	    continue;
+	if (LYHaveCJKCharacterSet || !UCCanUniTranslateFrom(current_char_set)) {
+	    result = TRUE;
+	    break;
+	}
+	unicode = UCTransToUni(*p, current_char_set);
+	if (unicode == ucNeedMore)
+	    continue;
+	if (unicode > 32 && unicode < 127) {
+	    result = TRUE;
+	    break;
+	}
+	if (unicode <= 32 || unicode == 0xa0 || unicode == 0xad)
+	    continue;
+	if (unicode < 0x2000 || unicode >= 0x200f) {
+	    result = TRUE;
+	    break;
+	}
+    }
+    return (result);
+}
+
+/*
+ * Check whether string have 8 bit chars.
+ */
+static BOOLEAN have8bit(const char *Title)
+{
+    const char *p = Title;
+
+    for (; *p; p++) {
+	if (UCH(*p) > 127)
+	    return (TRUE);
+    }
+    return (FALSE);		/* if we came here */
+}
+
+/*
+ * Ok, title have 8-bit characters and they are in display charset.  Bookmarks
+ * is a permanent file.  To avoid dependencies from display character set which
+ * may be changed with time we store 8-bit characters as numeric character
+ * reference (NCR), so where the character encoded as unicode number in form of
+ * &#xUUUU;
+ *
+ * To make bookmarks more readable for human (&#xUUUU certainly not) we add a
+ * comment with '7-bit approximation' from the converted string.  This is a
+ * valid HTML and bookmarks code.
+ *
+ * We do not want use META charset tag in bookmarks file:  it will never be
+ * changed later :-(
+ *
+ * NCR's translation is part of I18N and HTML4.0 supported starting with Lynx
+ * 2.7.2, Netscape 4.0 and MSIE 4.0.  Older versions fail.
+ */
+static char *title_convert8bit(const char *Title)
+{
+    const char *p = Title;
+    char *p0;
+    char *q;
+    char *comment = NULL;
+    char *ncr = NULL;
+    char *buf = NULL;
+    int charset_in = current_char_set;
+    int charset_out = UCGetLYhndl_byMIME("us-ascii");
+
+    for (; *p; p++) {
+	char temp[2];
+
+	LYstrncpy(temp, p, sizeof(temp) - 1);
+	if (UCH(*temp) <= 127) {
+	    StrAllocCat(comment, temp);
+	    StrAllocCat(ncr, temp);
+	} else {
+	    long unicode;
+	    char replace_buf[32];
+
+	    if (UCTransCharStr(replace_buf, sizeof(replace_buf), *temp,
+			       charset_in, charset_out, YES) > 0)
+		StrAllocCat(comment, replace_buf);
+
+	    unicode = UCTransToUni(*temp, charset_in);
+
+	    StrAllocCat(ncr, "&#");
+	    sprintf(replace_buf, "%ld", unicode);
+	    StrAllocCat(ncr, replace_buf);
+	    StrAllocCat(ncr, ";");
+	}
+    }
+
+    if (comment != NULL) {
+	/*
+	 * Cleanup comment, collapse multiple dashes into one dash, skip '>'.
+	 */
+	for (q = p0 = comment; *p0; p0++) {
+	    if (UCH(TOASCII(*p0)) >= 32 &&
+		*p0 != '>' &&
+		(q == comment || *p0 != '-' || *(q - 1) != '-')) {
+		*q++ = *p0;
+	    }
+	}
+	*q = '\0';
+
+	/*
+	 * valid bookmark should be a single line (no linebreaks!).
+	 */
+	StrAllocCat(buf, "<!-- ");
+	StrAllocCat(buf, comment);
+	StrAllocCat(buf, " -->");
+	StrAllocCat(buf, ncr);
+
+	FREE(comment);
+    }
+    FREE(ncr);
+    return (buf);
+}
+
+/*
+ * Since this is the "Default Bookmark File", we save it as a global, and as
+ * the first MBM_A_subbookmark entry.
+ */
+void set_default_bookmark_page(char *value)
+{
+    if (value != 0) {
+	if (bookmark_page == 0
+	    || strcmp(bookmark_page, value)) {
+	    StrAllocCopy(bookmark_page, value);
+	}
+	StrAllocCopy(BookmarkPage, bookmark_page);
+	StrAllocCopy(MBM_A_subbookmark[0], bookmark_page);
+	StrAllocCopy(MBM_A_subdescript[0], MULTIBOOKMARKS_DEFAULT);
+    }
+}
diff --git a/src/LYBookmark.h b/src/LYBookmark.h
new file mode 100644
index 00000000..ae8257bd
--- /dev/null
+++ b/src/LYBookmark.h
@@ -0,0 +1,25 @@
+#ifndef LYBOOKMARK_H
+#define LYBOOKMARK_H
+
+#ifndef LYSTRUCTS_H
+#include <LYStructs.h>
+#endif /* LYSTRUCTS_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    extern BOOLEAN LYHaveSubBookmarks(void);
+    extern const char *get_bookmark_filename(char **name);
+    extern int LYMBM2index(int ch);
+    extern int LYindex2MBM(int n);
+    extern int select_menu_multi_bookmarks(void);
+    extern int select_multi_bookmarks(void);
+    extern void LYMBM_statusline(const char *text);
+    extern void remove_bookmark_link(int cur, char *cur_bookmark_page);
+    extern void save_bookmark_link(const char *address, const char *title);
+    extern void set_default_bookmark_page(char *value);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYBOOKMARK_H */
diff --git a/src/LYCgi.c b/src/LYCgi.c
new file mode 100644
index 00000000..b9faca54
--- /dev/null
+++ b/src/LYCgi.c
@@ -0,0 +1,751 @@
+/*
+ * $LynxId: LYCgi.c,v 1.57 2010/04/29 23:42:23 tom Exp $
+ *                   Lynx CGI support                              LYCgi.c
+ *                   ================
+ *
+ * Authors
+ *          GL      George Lindholm <George.Lindholm@ubc.ca>
+ *
+ * History
+ *      15 Jun 95   Created as way to provide a lynx based service with
+ *                  dynamic pages without the need for a http daemon.  GL
+ *      27 Jun 95   Added <index> (command line) support.  Various cleanup
+ *                  and bug fixes. GL
+ *	04 Sep 97   Added support for PATH_INFO scripts.  JKT
+ *
+ * Bugs
+ *      If the called scripts aborts before sending the mime headers then
+ *      lynx hangs.
+ *
+ *      Should do something about SIGPIPE, (but then it should never happen)
+ *
+ *      No support for redirection.  Or mime-types.
+ *
+ *      Should try and parse for a HTTP 1.1 header in case we are "calling" a
+ *      nph- script.
+ */
+
+#include <HTUtils.h>
+#include <HTTP.h>
+#include <HTParse.h>
+#include <HTTCP.h>
+#include <HTFormat.h>
+#include <HTFile.h>
+#include <HTAlert.h>
+#include <HTMIME.h>
+#include <HTAABrow.h>
+
+#include <LYGlobalDefs.h>
+#include <LYUtils.h>
+#include <HTML.h>
+#include <HTInit.h>
+#include <LYGetFile.h>
+#include <LYBookmark.h>
+#include <GridText.h>
+#include <LYCgi.h>
+#include <LYStrings.h>
+#include <LYLocal.h>
+
+#include <LYLeaks.h>
+#include <www_wait.h>
+
+struct _HTStream {
+    HTStreamClass *isa;
+};
+
+static char **env = NULL;	/* Environment variables */
+static unsigned envc_size = 0;	/* Slots in environment array */
+static unsigned envc = 0;	/* Slots used so far */
+static HTList *alloced = NULL;
+
+#if defined(LYNXCGI_LINKS) && !defined(__MINGW32__)
+static char *user_agent = NULL;
+static char *server_software = NULL;
+static char *accept_language = NULL;
+static char *post_len = NULL;
+#endif /* LYNXCGI_LINKS */
+
+static void add_environment_value(const char *env_value);
+
+#define PERROR(msg) CTRACE((tfp, "LYNXCGI: %s: %s\n", msg, LYStrerror(errno)))
+
+#define PUTS(buf)    (*target->isa->put_block)(target, buf, strlen(buf))
+
+#ifdef LY_FIND_LEAKS
+static void free_alloced_lynxcgi(void)
+{
+    void *ptr;
+
+    while ((ptr = HTList_removeLastObject(alloced)) != NULL) {
+	FREE(ptr);
+    }
+    FREE(alloced);
+#ifdef LYNXCGI_LINKS
+    FREE(user_agent);
+    FREE(server_software);
+#endif
+}
+#endif /* LY_FIND_LEAKS */
+
+static void remember_alloced(void *ptr)
+{
+    if (!alloced) {
+	alloced = HTList_new();
+#ifdef LY_FIND_LEAKS
+	atexit(free_alloced_lynxcgi);
+#endif
+    }
+    HTList_addObject(alloced, ptr);
+}
+
+/*
+ * Simple routine for expanding the environment array and adding a value to
+ * it
+ */
+static void add_environment_value(const char *env_value)
+{
+    if (envc == envc_size) {	/* Need some more slots */
+	envc_size += 10;
+	if (env) {
+	    env = (char **) realloc(env,
+				    sizeof(env[0]) * (envc_size + 2));
+	    /* + terminator and base 0 */
+	} else {
+	    env = (char **) malloc(sizeof(env[0]) * (envc_size + 2));
+	    /* + terminator and base 0 */
+	    remember_alloced(env);
+	}
+	if (env == NULL) {
+	    outofmem(__FILE__, "LYCgi");
+	}
+	assert(env != NULL);
+    }
+
+    env[envc++] = (char *) env_value;
+    env[envc] = NULL;		/* Make sure it is always properly terminated */
+}
+
+/*
+ * Add the value of an existing environment variable to those passed on to the
+ * lynxcgi script.
+ */
+void add_lynxcgi_environment(const char *variable_name)
+{
+    char *env_value;
+
+    env_value = LYGetEnv(variable_name);
+    if (env_value != NULL) {
+	char *add_value = NULL;
+
+	HTSprintf0(&add_value, "%s=%s", variable_name, env_value);
+	add_environment_value(add_value);
+	remember_alloced(add_value);
+    }
+}
+
+#ifdef __MINGW32__
+static int LYLoadCGI(const char *arg,
+		     HTParentAnchor *anAnchor,
+		     HTFormat format_out,
+		     HTStream *sink)
+{
+    return -1;
+}
+#else
+#ifdef LYNXCGI_LINKS
+/*
+ * Wrapper for exec_ok(), confirming with user if the link text is not visible
+ * in the status line.
+ */
+static BOOL can_exec_cgi(const char *linktext, const char *linkargs)
+{
+    const char *format = gettext("Do you want to execute \"%s\"?");
+    char *message = NULL;
+    char *command = NULL;
+    char *p;
+    BOOL result = TRUE;
+
+    if (!exec_ok(HTLoadedDocumentURL(), linktext, CGI_PATH)) {
+	/* exec_ok gives out msg. */
+	result = FALSE;
+    } else {
+	StrAllocCopy(command, linktext);
+	if (non_empty(linkargs)) {
+	    HTSprintf(&command, " %s", linkargs);
+	}
+	HTUnEscape(command);
+	for (p = command; *p; ++p)
+	    if (*p == '+')
+		*p = ' ';
+	HTSprintf0(&message, format, command);
+	result = HTConfirm(message);
+	FREE(message);
+	FREE(command);
+    }
+    return result;
+}
+#endif /* LYNXCGI_LINKS */
+
+static int LYLoadCGI(const char *arg,
+		     HTParentAnchor *anAnchor,
+		     HTFormat format_out,
+		     HTStream *sink)
+{
+    int status = 0;
+
+#ifdef LYNXCGI_LINKS
+#ifndef VMS
+    char *cp;
+    struct stat stat_buf;
+    char *pgm = NULL;		/* executable */
+    char *pgm_args = NULL;	/* and its argument(s) */
+    int statrv;
+    char *orig_pgm = NULL;	/* Path up to ? as given, URL-escaped */
+    char *document_root = NULL;	/* Corrected value of DOCUMENT_ROOT  */
+    char *path_info = NULL;	/* PATH_INFO extracted from pgm      */
+    char *pgm_buff = NULL;	/* PATH_INFO extraction buffer       */
+    char *path_translated;	/* From document_root/path_info      */
+
+    if (isEmpty(arg) || strlen(arg) <= 8) {
+	HTAlert(BAD_REQUEST);
+	status = -2;
+	return (status);
+
+    } else {
+	if (strncmp(arg, "lynxcgi://localhost", 19) == 0) {
+	    StrAllocCopy(pgm, arg + 19);
+	} else {
+	    StrAllocCopy(pgm, arg + 8);
+	}
+	if ((cp = strchr(pgm, '?')) != NULL) {	/* Need to terminate executable */
+	    *cp++ = '\0';
+	    pgm_args = cp;
+	}
+    }
+
+    StrAllocCopy(orig_pgm, pgm);
+    if ((cp = trimPoundSelector(pgm)) != NULL) {
+	/*
+	 * Strip a #fragment from path.  In this case any pgm_args found above
+	 * will also be bogus, since the '?' came after the '#' and is part of
+	 * the fragment.  Note that we don't handle the case where a '#'
+	 * appears after a '?' properly according to URL rules.  - kw
+	 */
+	pgm_args = NULL;
+    }
+    HTUnEscape(pgm);
+
+    /* BEGIN WebSter Mods */
+    /* If pgm is not stat-able, see if PATH_INFO data is at the end of pgm */
+    if ((statrv = stat(pgm, &stat_buf)) < 0) {
+	StrAllocCopy(pgm_buff, pgm);
+	while (statrv < 0 || (statrv = stat(pgm_buff, &stat_buf)) < 0) {
+	    if ((cp = strrchr(pgm_buff, '/')) != NULL) {
+		*cp = '\0';
+		statrv = 1;	/* force new stat()  - kw */
+	    } else {
+		PERROR("strrchr(pgm_buff, '/') returned NULL");
+		break;
+	    }
+	}
+
+	if (statrv < 0) {
+	    /* Did not find PATH_INFO data */
+	    PERROR("stat() of pgm_buff failed");
+	} else {
+	    /* Found PATH_INFO data.  Strip it off of pgm and into path_info. */
+	    StrAllocCopy(path_info, pgm + strlen(pgm_buff));
+	    /* The following is safe since pgm_buff was derived from pgm
+	       by stripping stuff off its end and by HTUnEscaping, so we
+	       know we have enough memory allocated for pgm.  Note that
+	       pgm_args may still point into that memory, so we cannot
+	       reallocate pgm here. - kw */
+	    strcpy(pgm, pgm_buff);
+	    CTRACE((tfp,
+		    "LYNXCGI: stat() of %s succeeded, path_info=\"%s\".\n",
+		    pgm_buff, path_info));
+	}
+	FREE(pgm_buff);
+    }
+    /* END WebSter Mods */
+
+    if (statrv != 0) {
+	/*
+	 * Neither the path as given nor any components examined by backing up
+	 * were stat()able.  - kw
+	 */
+	HTAlert(gettext("Unable to access cgi script"));
+	PERROR("stat() failed");
+	status = -4;
+
+    } else
+#ifdef _WINDOWS			/* 1998/01/14 (Wed) 09:16:04 */
+#define isExecutable(mode) (mode & (S_IXUSR))
+#else
+#define isExecutable(mode) (mode & (S_IXUSR|S_IXGRP|S_IXOTH))
+#endif
+    if (!(S_ISREG(stat_buf.st_mode) && isExecutable(stat_buf.st_mode))) {
+	/*
+	 * Not a runnable file, See if we can load it using "file:" code.
+	 */
+	char *new_arg = NULL;
+
+	/*
+	 * But try "file:" only if the file we are looking at is the path as
+	 * given (no path_info was extracted), otherwise it will be to
+	 * confusing to know just what file is loaded.  - kw
+	 */
+	if (path_info) {
+	    CTRACE((tfp,
+		    "%s is not a file and %s not an executable, giving up.\n",
+		    orig_pgm, pgm));
+	    FREE(path_info);
+	    FREE(pgm);
+	    FREE(orig_pgm);
+	    status = -4;
+	    return (status);
+	}
+
+	LYLocalFileToURL(&new_arg, orig_pgm);
+
+	CTRACE((tfp, "%s is not an executable file, passing the buck.\n", arg));
+	status = HTLoadFile(new_arg, anAnchor, format_out, sink);
+	FREE(new_arg);
+
+    } else if (path_info &&
+	       anAnchor != HTMainAnchor &&
+	       !(reloading && anAnchor->document) &&
+	       strcmp(arg, HTLoadedDocumentURL()) &&
+	       HText_AreDifferent(anAnchor, arg) &&
+	       HTUnEscape(orig_pgm) &&
+	       !can_exec_cgi(orig_pgm, "")) {
+	/*
+	 * If we have extra path info and are not just reloading the current,
+	 * check the full file path (after unescaping) now to catch forbidden
+	 * segments.  - kw
+	 */
+	status = HT_NOT_LOADED;
+
+    } else if (no_lynxcgi) {
+	HTUserMsg(CGI_DISABLED);
+	status = HT_NOT_LOADED;
+
+    } else if (no_bookmark_exec &&
+	       anAnchor != HTMainAnchor &&
+	       !(reloading && anAnchor->document) &&
+	       strcmp(arg, HTLoadedDocumentURL()) &&
+	       HText_AreDifferent(anAnchor, arg) &&
+	       HTLoadedDocumentBookmark()) {
+	/*
+	 * If we are reloading a lynxcgi document that had already been loaded,
+	 * the various checks above should allow it even if no_bookmark_exec is
+	 * TRUE an we are not now coming from a bookmark page.  - kw
+	 */
+	HTUserMsg(BOOKMARK_EXEC_DISABLED);
+	status = HT_NOT_LOADED;
+
+    } else if (anAnchor != HTMainAnchor &&
+	       !(reloading && anAnchor->document) &&
+	       strcmp(arg, HTLoadedDocumentURL()) &&
+	       HText_AreDifferent(anAnchor, arg) &&
+	       !can_exec_cgi(pgm, pgm_args)) {
+	/*
+	 * If we are reloading a lynxcgi document that had already been loaded,
+	 * the various checks above should allow it even if exec_ok() would
+	 * reject it because we are not now coming from a document with a URL
+	 * allowed by TRUSTED_LYNXCGI rules.  - kw
+	 */
+	status = HT_NOT_LOADED;
+
+    } else {
+	HTFormat format_in;
+	HTStream *target = NULL;	/* Unconverted data */
+	int fd1[2], fd2[2];
+	char buf[MAX_LINE];
+	int pid;
+
+#ifdef HAVE_TYPE_UNIONWAIT
+	union wait wstatus;
+
+#else
+	int wstatus;
+#endif
+
+	if (anAnchor->isHEAD || keep_mime_headers) {
+
+	    /* Show output as plain text */
+	    format_in = WWW_PLAINTEXT;
+	} else {
+
+	    /* Decode full HTTP response */
+	    format_in = HTAtom_for("www/mime");
+	}
+
+	target = HTStreamStack(format_in,
+			       format_out,
+			       sink, anAnchor);
+
+	if (!target || target == NULL) {
+	    char *tmp = 0;
+
+	    HTSprintf0(&tmp, CANNOT_CONVERT_I_TO_O,
+		       HTAtom_name(format_in),
+		       HTAtom_name(format_out));
+	    HTAlert(tmp);
+	    FREE(tmp);
+	    status = HT_NOT_LOADED;
+
+	} else if (anAnchor->post_data && pipe(fd1) < 0) {
+	    HTAlert(CONNECT_SET_FAILED);
+	    PERROR("pipe() failed");
+	    status = -3;
+
+	} else if (pipe(fd2) < 0) {
+	    HTAlert(CONNECT_SET_FAILED);
+	    PERROR("pipe() failed");
+	    close(fd1[0]);
+	    close(fd1[1]);
+	    status = -3;
+
+	} else {
+	    static BOOL first_time = TRUE;	/* One time setup flag */
+
+	    if (first_time) {	/* Set up static environment variables */
+		first_time = FALSE;	/* Only once */
+
+		add_environment_value("REMOTE_HOST=localhost");
+		add_environment_value("REMOTE_ADDR=127.0.0.1");
+
+		HTSprintf0(&user_agent, "HTTP_USER_AGENT=%s/%s libwww/%s",
+			   LYNX_NAME, LYNX_VERSION, HTLibraryVersion);
+		add_environment_value(user_agent);
+
+		HTSprintf0(&server_software, "SERVER_SOFTWARE=%s/%s",
+			   LYNX_NAME, LYNX_VERSION);
+		add_environment_value(server_software);
+	    }
+	    fflush(stdout);
+	    fflush(stderr);
+	    CTRACE_FLUSH(tfp);
+
+	    if ((pid = fork()) > 0) {	/* The good, */
+		int chars, total_chars;
+
+		close(fd2[1]);
+
+		if (anAnchor->post_data) {
+		    int written, remaining, total_written = 0;
+
+		    close(fd1[0]);
+
+		    /* We have form data to push across the pipe */
+		    if (TRACE) {
+			CTRACE((tfp,
+				"LYNXCGI: Doing post, content-type '%s'\n",
+				anAnchor->post_content_type));
+			CTRACE((tfp, "LYNXCGI: Writing:\n"));
+			trace_bstring(anAnchor->post_data);
+			CTRACE((tfp, "----------------------------------\n"));
+		    }
+		    remaining = BStrLen(anAnchor->post_data);
+		    while ((written = write(fd1[1],
+					    BStrData(anAnchor->post_data) + total_written,
+					    (unsigned) remaining)) != 0) {
+			if (written < 0) {
+#ifdef EINTR
+			    if (errno == EINTR)
+				continue;
+#endif /* EINTR */
+#ifdef ERESTARTSYS
+			    if (errno == ERESTARTSYS)
+				continue;
+#endif /* ERESTARTSYS */
+			    PERROR("write() of POST data failed");
+			    break;
+			}
+			CTRACE((tfp, "LYNXCGI: Wrote %d bytes of POST data.\n",
+				written));
+			total_written += written;
+			remaining -= written;
+			if (remaining == 0)
+			    break;
+		    }
+		    if (remaining != 0) {
+			CTRACE((tfp, "LYNXCGI: %d bytes remain unwritten!\n",
+				remaining));
+		    }
+		    close(fd1[1]);
+		}
+
+		HTReadProgress(total_chars = 0, 0);
+		while ((chars = read(fd2[0], buf, sizeof(buf))) != 0) {
+		    if (chars < 0) {
+#ifdef EINTR
+			if (errno == EINTR)
+			    continue;
+#endif /* EINTR */
+#ifdef ERESTARTSYS
+			if (errno == ERESTARTSYS)
+			    continue;
+#endif /* ERESTARTSYS */
+			PERROR("read() of CGI output failed");
+			break;
+		    }
+		    HTReadProgress(total_chars += chars, 0);
+		    CTRACE((tfp, "LYNXCGI: Rx: %.*s\n", chars, buf));
+		    (*target->isa->put_block) (target, buf, chars);
+		}
+
+		if (chars < 0 && total_chars == 0) {
+		    status = HT_NOT_LOADED;
+		    (*target->isa->_abort) (target, NULL);
+		    target = NULL;
+		} else if (chars != 0) {
+		    status = HT_PARTIAL_CONTENT;
+		} else {
+		    status = HT_LOADED;
+		}
+
+#if !HAVE_WAITPID
+		while (wait(&wstatus) != pid) ;		/* do nothing */
+#else
+		while (-1 == waitpid(pid, &wstatus, 0)) {	/* wait for child */
+#ifdef EINTR
+		    if (errno == EINTR)
+			continue;
+#endif /* EINTR */
+#ifdef ERESTARTSYS
+		    if (errno == ERESTARTSYS)
+			continue;
+#endif /* ERESTARTSYS */
+		    break;
+		}
+#endif /* !HAVE_WAITPID */
+		close(fd2[0]);
+
+	    } else if (pid == 0) {	/* The Bad, */
+		char **argv = NULL;
+		int argv_cnt = 3;	/* name, one arg and terminator */
+		char **cur_argv = NULL;
+		int exec_errno;
+
+		/* Set up output pipe */
+		close(fd2[0]);
+		dup2(fd2[1], fileno(stdout));	/* Should check success code */
+		dup2(fd2[1], fileno(stderr));
+		close(fd2[1]);
+
+		if (non_empty(language)) {
+		    HTSprintf0(&accept_language, "HTTP_ACCEPT_LANGUAGE=%s", language);
+		    add_environment_value(accept_language);
+		}
+
+		if (non_empty(pref_charset)) {
+		    cp = NULL;
+		    StrAllocCopy(cp, "HTTP_ACCEPT_CHARSET=");
+		    StrAllocCat(cp, pref_charset);
+		    add_environment_value(cp);
+		}
+
+		if (anAnchor->post_data &&
+		    anAnchor->post_content_type) {
+		    cp = NULL;
+		    StrAllocCopy(cp, "CONTENT_TYPE=");
+		    StrAllocCat(cp, anAnchor->post_content_type);
+		    add_environment_value(cp);
+		}
+
+		if (anAnchor->post_data) {	/* post script, read stdin */
+		    close(fd1[1]);
+		    dup2(fd1[0], fileno(stdin));
+		    close(fd1[0]);
+
+		    /* Build environment variables */
+
+		    add_environment_value("REQUEST_METHOD=POST");
+
+		    HTSprintf0(&post_len, "CONTENT_LENGTH=%d",
+			       BStrLen(anAnchor->post_data));
+		    add_environment_value(post_len);
+		} else {
+		    close(fileno(stdin));
+
+		    if (anAnchor->isHEAD) {
+			add_environment_value("REQUEST_METHOD=HEAD");
+		    }
+		}
+
+		/*
+		 * Set up argument line, mainly for <index> scripts
+		 */
+		if (pgm_args != NULL) {
+		    for (cp = pgm_args; *cp != '\0'; cp++) {
+			if (*cp == '+') {
+			    argv_cnt++;
+			}
+		    }
+		}
+
+		argv = (char **) malloc((unsigned) argv_cnt * sizeof(char *));
+
+		if (argv == NULL) {
+		    outofmem(__FILE__, "LYCgi");
+		}
+		assert(argv != NULL);
+
+		cur_argv = argv + 1;	/* For argv[0] */
+		if (pgm_args != NULL) {
+		    char *cr;
+
+		    /* Data for a get/search form */
+		    if (is_www_index) {
+			add_environment_value("REQUEST_METHOD=SEARCH");
+		    } else if (!anAnchor->isHEAD && !anAnchor->post_data) {
+			add_environment_value("REQUEST_METHOD=GET");
+		    }
+
+		    cp = NULL;
+		    StrAllocCopy(cp, "QUERY_STRING=");
+		    StrAllocCat(cp, pgm_args);
+		    add_environment_value(cp);
+
+		    /*
+		     * Split up arguments into argv array
+		     */
+		    cp = pgm_args;
+		    cr = cp;
+		    while (1) {
+			if (*cp == '\0') {
+			    *(cur_argv++) = HTUnEscape(cr);
+			    break;
+
+			} else if (*cp == '+') {
+			    *cp++ = '\0';
+			    *(cur_argv++) = HTUnEscape(cr);
+			    cr = cp;
+			}
+			cp++;
+		    }
+		} else if (!anAnchor->isHEAD && !anAnchor->post_data) {
+		    add_environment_value("REQUEST_METHOD=GET");
+		}
+		*cur_argv = NULL;	/* Terminate argv */
+		argv[0] = pgm;
+
+		/* Begin WebSter Mods  -jkt */
+		if (LYCgiDocumentRoot != NULL) {
+		    /* Add DOCUMENT_ROOT to env */
+		    cp = NULL;
+		    StrAllocCopy(cp, "DOCUMENT_ROOT=");
+		    StrAllocCat(cp, LYCgiDocumentRoot);
+		    add_environment_value(cp);
+		}
+		if (path_info != NULL) {
+		    /* Add PATH_INFO to env */
+		    cp = NULL;
+		    StrAllocCopy(cp, "PATH_INFO=");
+		    StrAllocCat(cp, path_info);
+		    add_environment_value(cp);
+		}
+		if (LYCgiDocumentRoot != NULL && path_info != NULL) {
+		    /* Construct and add PATH_TRANSLATED to env */
+		    StrAllocCopy(document_root, LYCgiDocumentRoot);
+		    LYTrimHtmlSep(document_root);
+		    path_translated = document_root;
+		    StrAllocCat(path_translated, path_info);
+		    cp = NULL;
+		    StrAllocCopy(cp, "PATH_TRANSLATED=");
+		    StrAllocCat(cp, path_translated);
+		    add_environment_value(cp);
+		    FREE(path_translated);
+		}
+		/* End WebSter Mods  -jkt */
+
+		execve(argv[0], argv, env);
+		exec_errno = errno;
+		PERROR("execve failed");
+		printf("Content-Type: text/plain\r\n\r\n");
+		if (!anAnchor->isHEAD) {
+		    printf("exec of %s failed", pgm);
+		    printf(": %s.\r\n", LYStrerror(exec_errno));
+		}
+		fflush(stdout);
+		fflush(stderr);
+		_exit(1);
+
+	    } else {		/* and the Ugly */
+		HTAlert(CONNECT_FAILED);
+		PERROR("fork() failed");
+		close(fd1[0]);
+		close(fd1[1]);
+		close(fd2[0]);
+		close(fd2[1]);
+		status = -1;
+	    }
+
+	}
+	if (target != NULL) {
+	    (*target->isa->_free) (target);
+	}
+    }
+    FREE(path_info);
+    FREE(pgm);
+    FREE(orig_pgm);
+#else /* VMS */
+    HTStream *target;
+    char *buf = 0;
+
+    target = HTStreamStack(WWW_HTML,
+			   format_out,
+			   sink, anAnchor);
+
+    HTSprintf0(&buf, "<html>\n<head>\n<title>%s</title>\n</head>\n<body>\n",
+	       gettext("Good Advice"));
+    PUTS(buf);
+
+    HTSprintf0(&buf, "<h1>%s</h1>\n", gettext("Good Advice"));
+    PUTS(buf);
+
+    HTSprintf0(&buf, "%s <a\n",
+	       gettext("An excellent http server for VMS is available via"));
+    PUTS(buf);
+
+    HTSprintf0(&buf,
+	       "href=\"http://www.ecr6.ohio-state.edu/www/doc/serverinfo.html\"\n");
+    PUTS(buf);
+
+    HTSprintf0(&buf, ">%s</a>.\n", gettext("this link"));
+    PUTS(buf);
+
+    HTSprintf0(&buf, "<p>%s\n",
+	       gettext("It provides state of the art CGI script support.\n"));
+    PUTS(buf);
+
+    HTSprintf0(&buf, "</body>\n</html>\n");
+    PUTS(buf);
+
+    (*target->isa->_free) (target);
+    FREE(buf);
+    status = HT_LOADED;
+#endif /* VMS */
+#else /* LYNXCGI_LINKS */
+    HTUserMsg(CGI_NOT_COMPILED);
+    status = HT_NOT_LOADED;
+#endif /* LYNXCGI_LINKS */
+
+    (void) arg;
+    (void) anAnchor;
+    (void) format_out;
+    (void) sink;
+
+    return (status);
+}
+#endif /* __MINGW32__ */
+
+#ifdef GLOBALDEF_IS_MACRO
+#define _LYCGI_C_GLOBALDEF_1_INIT { "lynxcgi", LYLoadCGI, 0 }
+GLOBALDEF(HTProtocol, LYLynxCGI, _LYCGI_C_GLOBALDEF_1_INIT);
+#else
+GLOBALDEF HTProtocol LYLynxCGI =
+{"lynxcgi", LYLoadCGI, 0};
+#endif /* GLOBALDEF_IS_MACRO */
diff --git a/src/LYCgi.h b/src/LYCgi.h
new file mode 100644
index 00000000..6b90f2de
--- /dev/null
+++ b/src/LYCgi.h
@@ -0,0 +1,16 @@
+#ifndef LYCGI_H
+#define LYCGI_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    extern void add_lynxcgi_environment(const char *variable_name);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYGETFILE_H */
diff --git a/src/LYCharSets.c b/src/LYCharSets.c
new file mode 100644
index 00000000..900478e0
--- /dev/null
+++ b/src/LYCharSets.c
@@ -0,0 +1,1157 @@
+/*
+ * $LynxId: LYCharSets.c,v 1.64 2009/11/21 15:52:05 tom Exp $
+ */
+#include <HTUtils.h>
+#include <HTCJK.h>
+#include <HTMLDTD.h>
+
+#include <LYGlobalDefs.h>
+#include <UCMap.h>
+#include <UCdomap.h>
+#include <UCDefs.h>
+#include <LYCharSets.h>
+#include <GridText.h>
+#include <LYCurses.h>
+#include <LYStrings.h>
+
+#include <LYLeaks.h>
+
+HTkcode kanji_code = NOKANJI;
+BOOLEAN LYHaveCJKCharacterSet = FALSE;
+BOOLEAN DisplayCharsetMatchLocale = TRUE;
+BOOL force_old_UCLYhndl_on_reload = FALSE;
+int forced_UCLYhdnl;
+int LYNumCharsets = 0;		/* Will be initialized later by UC_Register. */
+int current_char_set = -1;	/* will be intitialized later in LYMain.c */
+int linedrawing_char_set = -1;
+const char **p_entity_values = NULL;	/* Pointer, for HTML_put_entity() */
+
+			      /* obsolete and probably not used(???)        */
+			      /* will be initialized in HTMLUseCharacterSet */
+#ifdef USE_CHARSET_CHOICE
+charset_subset_t charset_subsets[MAXCHARSETS];
+BOOL custom_display_charset = FALSE;
+BOOL custom_assumed_doc_charset = FALSE;
+
+#ifndef ALL_CHARSETS_IN_O_MENU_SCREEN
+int display_charset_map[MAXCHARSETS];
+int assumed_doc_charset_map[MAXCHARSETS];
+
+const char *display_charset_choices[MAXCHARSETS + 1];
+const char *assumed_charset_choices[MAXCHARSETS + 1];
+int displayed_display_charset_idx;
+#endif
+#endif /* USE_CHARSET_CHOICE */
+
+/*
+ * New character sets now declared with UCInit() in UCdomap.c
+ *
+ * INSTRUCTIONS for adding new character sets which do not have
+ *		Unicode tables now in UCdomap.h
+ *
+ *
+ * [We hope you need not correct/add old-style mapping below as in ISO_LATIN1[]
+ * or SevenBitApproximations[] any more - it works now via new chartrans
+ * mechanism, but kept for compatibility only:  we should cleanup the stuff,
+ * but this is not so easy...]
+ *
+ * Currently we only declare some charset's properties here (such as MIME
+ * names, etc.), it does not include real mapping.
+ *
+ * There is a place marked "Add your new character sets HERE" in this file. 
+ * Make up a character set and add it in the same style as the ISO_LATIN1 set
+ * below, giving it a unique name.
+ *
+ * Add the name of the set to LYCharSets.  Similarly add the appropriate
+ * information to the tables below:  LYchar_set_names, LYCharSet_UC,
+ * LYlowest_eightbit.  These 4 tables all MUST have the same order.  (And this
+ * is the order you will see in Lynx Options Menu, which is why few
+ * unicode-based charsets are listed here).
+ *
+ */
+
+/*	Entity values -- for ISO Latin 1 local representation
+ *
+ *	This MUST match exactly the table referred to in the DTD!
+ */
+static const char *ISO_Latin1[] =
+{
+    "\306",			/* capital AE diphthong (ligature) (&#198;) - AElig */
+    "\301",			/* capital A, acute accent (&#193;) - Aacute */
+    "\302",			/* capital A, circumflex accent (&#194;) - Acirc */
+    "\300",			/* capital A, grave accent (&#192;) - Agrave */
+    "\305",			/* capital A, ring - Aring (&#197;) */
+    "\303",			/* capital A, tilde - Atilde (&#195;) */
+    "\304",			/* capital A, dieresis or umlaut mark (&#196;) - Auml */
+    "\307",			/* capital C, cedilla - Ccedil (&#199;) */
+    "\320",			/* capital Eth or D with stroke (&#208;) - Dstrok */
+    "\320",			/* capital Eth, Icelandic (&#208;) - ETH */
+    "\311",			/* capital E, acute accent (&#201;) - Eacute */
+    "\312",			/* capital E, circumflex accent (&#202;) - Ecirc */
+    "\310",			/* capital E, grave accent (&#200;) - Egrave */
+    "\313",			/* capital E, dieresis or umlaut mark (&#203;) - Euml */
+    "\315",			/* capital I, acute accent (&#205;) - Iacute */
+    "\316",			/* capital I, circumflex accent (&#206;) - Icirc */
+    "\314",			/* capital I, grave accent (&#204;) - Igrave */
+    "\317",			/* capital I, dieresis or umlaut mark (&#207;) - Iuml */
+    "\321",			/* capital N, tilde (&#209;) - Ntilde */
+    "\323",			/* capital O, acute accent (&#211;) - Oacute */
+    "\324",			/* capital O, circumflex accent (&#212;) - Ocirc */
+    "\322",			/* capital O, grave accent (&#210;) - Ograve */
+    "\330",			/* capital O, slash (&#216;) - Oslash */
+    "\325",			/* capital O, tilde (&#213;) - Otilde */
+    "\326",			/* capital O, dieresis or umlaut mark (&#214;) - Ouml */
+    "\336",			/* capital THORN, Icelandic (&#222;) - THORN */
+    "\332",			/* capital U, acute accent (&#218;) - Uacute */
+    "\333",			/* capital U, circumflex accent (&#219;) - Ucirc */
+    "\331",			/* capital U, grave accent (&#217;) - Ugrave */
+    "\334",			/* capital U, dieresis or umlaut mark (&#220;) - Uuml */
+    "\335",			/* capital Y, acute accent (&#221;) - Yacute */
+    "\341",			/* small a, acute accent (&#225;) - aacute */
+    "\342",			/* small a, circumflex accent (&#226;) - acirc */
+    "\264",			/* spacing acute (&#180;) - acute */
+    "\346",			/* small ae diphthong (ligature) (&#230;) - aelig */
+    "\340",			/* small a, grave accent (&#224;) - agrave */
+    "\046",			/* ampersand (&#38;) - amp */
+    "\345",			/* small a, ring (&#229;) - aring */
+    "\343",			/* small a, tilde (&#227;) - atilde */
+    "\344",			/* small a, dieresis or umlaut mark (&#228;) - auml */
+    "\246",			/* broken vertical bar (&#166;) - brkbar */
+    "\246",			/* broken vertical bar (&#166;) - brvbar */
+    "\347",			/* small c, cedilla (&#231;) - ccedil */
+    "\270",			/* spacing cedilla (&#184;) - cedil */
+    "\242",			/* cent sign (&#162;) - cent */
+    "\251",			/* copyright sign (&#169;) - copy */
+    "\244",			/* currency sign (&#164;) - curren */
+    "\260",			/* degree sign (&#176;) - deg */
+    "\250",			/* spacing dieresis (&#168;) - die */
+    "\367",			/* division sign (&#247;) - divide */
+    "\351",			/* small e, acute accent (&#233;) - eacute */
+    "\352",			/* small e, circumflex accent (&#234;) - ecirc */
+    "\350",			/* small e, grave accent (&#232;) - egrave */
+    "-",			/* dash the width of emsp - emdash */
+    "\002",			/* emsp, em space - not collapsed NEVER CHANGE THIS - emsp */
+    "-",			/* dash the width of ensp - endash */
+    "\002",			/* ensp, en space - not collapsed NEVER CHANGE THIS - ensp */
+    "\360",			/* small eth, Icelandic (&#240;) - eth */
+    "\353",			/* small e, dieresis or umlaut mark (&#235;) - euml */
+    "\275",			/* fraction 1/2 (&#189;) - frac12 */
+    "\274",			/* fraction 1/4 (&#188;) - frac14 */
+    "\276",			/* fraction 3/4 (&#190;) - frac34 */
+    "\076",			/* greater than (&#62;) - gt */
+    "\257",			/* spacing macron (&#175;) - hibar */
+    "\355",			/* small i, acute accent (&#237;) - iacute */
+    "\356",			/* small i, circumflex accent (&#238;) - icirc */
+    "\241",			/* inverted exclamation mark (&#161;) - iexcl */
+    "\354",			/* small i, grave accent (&#236;) - igrave */
+    "\277",			/* inverted question mark (&#191;) - iquest */
+    "\357",			/* small i, dieresis or umlaut mark (&#239;) - iuml */
+    "\253",			/* angle quotation mark, left (&#171;) - laquo */
+    "\074",			/* less than (&#60;) - lt */
+    "\257",			/* spacing macron (&#175;) - macr */
+    "-",			/* dash the width of emsp - mdash */
+    "\265",			/* micro sign (&#181;) - micro */
+    "\267",			/* middle dot (&#183;) - middot */
+    "\001",			/* nbsp non-breaking space NEVER CHANGE THIS - nbsp */
+    "-",			/* dash the width of ensp - ndash */
+    "\254",			/* negation sign (&#172;) - not */
+    "\361",			/* small n, tilde (&#241;) - ntilde */
+    "\363",			/* small o, acute accent (&#243;) - oacute */
+    "\364",			/* small o, circumflex accent (&#244;) - ocirc */
+    "\362",			/* small o, grave accent (&#242;) - ograve */
+    "\252",			/* feminine ordinal indicator (&#170;) - ordf */
+    "\272",			/* masculine ordinal indicator (&#186;) - ordm */
+    "\370",			/* small o, slash (&#248;) - oslash */
+    "\365",			/* small o, tilde (&#245;) - otilde */
+    "\366",			/* small o, dieresis or umlaut mark (&#246;) - ouml */
+    "\266",			/* paragraph sign (&#182;) - para */
+    "\261",			/* plus-or-minus sign (&#177;) - plusmn */
+    "\243",			/* pound sign (&#163;) - pound */
+    "\042",			/* quote '"' (&#34;) - quot */
+    "\273",			/* angle quotation mark, right (&#187;) - raquo */
+    "\256",			/* circled R registered sign (&#174;) - reg */
+    "\247",			/* section sign (&#167;) - sect */
+    "\007",			/* soft hyphen (&#173;) NEVER CHANGE THIS - shy */
+    "\271",			/* superscript 1 (&#185;) - sup1 */
+    "\262",			/* superscript 2 (&#178;) - sup2 */
+    "\263",			/* superscript 3 (&#179;) - sup3 */
+    "\337",			/* small sharp s, German (sz ligature) (&#223;) - szlig */
+    "\002",			/* thin space - not collapsed NEVER CHANGE THIS - thinsp */
+    "\376",			/* small thorn, Icelandic (&#254;) - thorn */
+    "\327",			/* multiplication sign (&#215;) - times */
+    "(TM)",			/* circled TM trade mark sign (&#8482;) - trade */
+    "\372",			/* small u, acute accent (&#250;) - uacute */
+    "\373",			/* small u, circumflex accent (&#251;) - ucirc */
+    "\371",			/* small u, grave accent (&#249;) - ugrave */
+    "\250",			/* spacing dieresis (&#168;) - uml */
+    "\374",			/* small u, dieresis or umlaut mark (&#252;) - uuml */
+    "\375",			/* small y, acute accent (&#253;) - yacute */
+    "\245",			/* yen sign (&#165;) - yen */
+    "\377",			/* small y, dieresis or umlaut mark (&#255;) - yuml */
+};
+
+/*	Entity values -- 7 bit character approximations
+ *
+ *	This MUST match exactly the table referred to in the DTD!
+ */
+const char *SevenBitApproximations[] =
+{
+    "AE",			/* capital AE diphthong (ligature) (&#198;) - AElig */
+    "A",			/* capital A, acute accent (&#193;) - Aacute */
+    "A",			/* capital A, circumflex accent (&#194;) - Acirc */
+    "A",			/* capital A, grave accent (&#192;) - Agrave */
+    "A",			/* capital A, ring - Aring (&#197;) */
+    "A",			/* capital A, tilde - Atilde (&#195;) */
+#ifdef LY_UMLAUT
+    "Ae",			/* capital A, dieresis or umlaut mark (&#196;) - Auml */
+#else
+    "A",			/* capital A, dieresis or umlaut mark (&#196;) - Auml */
+#endif				/* LY_UMLAUT */
+    "C",			/* capital C, cedilla (&#199;) - Ccedil */
+    "Dj",			/* capital D with stroke (&#208;) - Dstrok */
+    "DH",			/* capital Eth, Icelandic (&#208;) - ETH */
+    "E",			/* capital E, acute accent (&#201;) - Eacute */
+    "E",			/* capital E, circumflex accent (&#202;) - Ecirc */
+    "E",			/* capital E, grave accent (&#200;) - Egrave */
+    "E",			/* capital E, dieresis or umlaut mark (&#203;) - Euml */
+    "I",			/* capital I, acute accent (&#205;) - Iacute */
+    "I",			/* capital I, circumflex accent (&#206;) - Icirc */
+    "I",			/* capital I, grave accent (&#204;) - Igrave */
+    "I",			/* capital I, dieresis or umlaut mark (&#207;) - Iuml */
+    "N",			/* capital N, tilde - Ntilde (&#209;) */
+    "O",			/* capital O, acute accent (&#211;) - Oacute */
+    "O",			/* capital O, circumflex accent (&#212;) - Ocirc */
+    "O",			/* capital O, grave accent (&#210;) - Ograve */
+    "O",			/* capital O, slash (&#216;) - Oslash */
+    "O",			/* capital O, tilde (&#213;) - Otilde */
+#ifdef LY_UMLAUT
+    "Oe",			/* capital O, dieresis or umlaut mark (&#214;) - Ouml */
+#else
+    "O",			/* capital O, dieresis or umlaut mark (&#214;) - Ouml */
+#endif				/* LY_UMLAUT */
+    "P",			/* capital THORN, Icelandic (&#222;) - THORN */
+    "U",			/* capital U, acute accent (&#218;) - Uacute */
+    "U",			/* capital U, circumflex accent (&#219;) - Ucirc */
+    "U",			/* capital U, grave accent (&#217;) - Ugrave */
+#ifdef LY_UMLAUT
+    "Ue",			/* capital U, dieresis or umlaut mark (&#220;) - Uuml */
+#else
+    "U",			/* capital U, dieresis or umlaut mark (&#220;) - Uuml */
+#endif				/* LY_UMLAUT */
+    "Y",			/* capital Y, acute accent (&#221;) - Yacute */
+    "a",			/* small a, acute accent (&#225;) - aacute */
+    "a",			/* small a, circumflex accent (&#226;) - acirc */
+    "'",			/* spacing acute (&#180;) - acute */
+    "ae",			/* small ae diphthong (ligature) (&#230;) - aelig */
+    "`a",			/* small a, grave accent (&#232;) - agrave */
+    "&",			/* ampersand (&#38;) - amp */
+    "a",			/* small a, ring (&#229;) - aring */
+    "a",			/* small a, tilde (&#227;) - atilde */
+#ifdef LY_UMLAUT
+    "ae",			/* small a, dieresis or umlaut mark (&#228;) - auml */
+#else
+    "a",			/* small a, dieresis or umlaut mark (&#228;) - auml */
+#endif				/* LY_UMLAUT */
+    "|",			/* broken vertical bar (&#166;) - brkbar */
+    "|",			/* broken vertical bar (&#166;) - brvbar */
+    "c",			/* small c, cedilla (&#231;) - ccedil */
+    ",",			/* spacing cedilla (&#184;) - cedil */
+    "-c-",			/* cent sign (&#162;) - cent */
+    "(c)",			/* copyright sign (&#169;) - copy */
+    "CUR",			/* currency sign (&#164;) - curren */
+    "DEG",			/* degree sign (&#176;) - deg */
+    "\042",			/* spacing dieresis (&#168;) - die */
+    "/",			/* division sign (&#247;) - divide */
+    "e",			/* small e, acute accent (&#233;) - eacute */
+    "e",			/* small e, circumflex accent (&#234;) - ecirc */
+    "e",			/* small e, grave accent (&#232;) - egrave */
+    "-",			/* dash the width of emsp - emdash */
+    "\002",			/* emsp NEVER CHANGE THIS - emsp */
+    "-",			/* dash the width of ensp - endash */
+    "\002",			/* ensp NEVER CHANGE THIS - ensp */
+    "dh",			/* small eth, Icelandic eth (&#240;) */
+    "e",			/* small e, dieresis or umlaut mark (&#235;) - euml */
+    " 1/2",			/* fraction 1/2 (&#189;) - frac12 */
+    " 1/4",			/* fraction 1/4 (&#188;) - frac14 */
+    " 3/4",			/* fraction 3/4 (&#190;) - frac34 */
+    ">",			/* greater than (&#62;) - gt */
+    "-",			/* spacing macron (&#175;) - hibar */
+    "i",			/* small i, acute accent (&#237;) - iacute */
+    "i",			/* small i, circumflex accent (&#238;) - icirc */
+    "!",			/* inverted exclamation mark (&#161;) - iexcl */
+    "`i",			/* small i, grave accent (&#236;) - igrave */
+    "?",			/* inverted question mark (&#191;) - iquest */
+    "i",			/* small i, dieresis or umlaut mark (&#239;) - iuml */
+    "<<",			/* angle quotation mark, left (&#171;) - laquo */
+    "<",			/* less than - lt (&#60;) */
+    "-",			/* spacing macron (&#175;) - macr */
+    "-",			/* dash the width of emsp - mdash */
+    "u",			/* micro sign (&#181;) - micro */
+    ".",			/* middle dot (&#183;) - middot */
+    "\001",			/* nbsp non-breaking space NEVER CHANGE THIS - nbsp */
+    "-",			/* dash the width of ensp - ndash */
+    "NOT",			/* negation sign (&#172;) - not */
+    "n",			/* small n, tilde (&#241;) - ntilde */
+    "o",			/* small o, acute accent (&#243;) - oacute */
+    "o",			/* small o, circumflex accent (&#244;) - ocirc */
+    "o",			/* small o, grave accent (&#242;) - ograve */
+    "-a",			/* feminine ordinal indicator (&#170;) - ordf */
+    "-o",			/* masculine ordinal indicator (&#186;) - ordm */
+    "o",			/* small o, slash (&#248;) - oslash */
+    "o",			/* small o, tilde (&#245;) - otilde */
+#ifdef LY_UMLAUT
+    "oe",			/* small o, dieresis or umlaut mark (&#246;) - ouml */
+#else
+    "o",			/* small o, dieresis or umlaut mark (&#246;) - ouml */
+#endif				/* LY_UMLAUT */
+    "P:",			/* paragraph sign (&#182;) - para */
+    "+-",			/* plus-or-minus sign (&#177;) - plusmn */
+    "-L-",			/* pound sign (&#163;) - pound */
+    "\"",			/* quote '"' (&#34;) - quot */
+    ">>",			/* angle quotation mark, right (&#187;) - raquo */
+    "(R)",			/* circled R registered sign (&#174;) - reg */
+    "S:",			/* section sign (&#167;) - sect */
+    "\007",			/* soft hyphen (&#173;) NEVER CHANGE THIS - shy */
+    "^1",			/* superscript 1 (&#185;) - sup1 */
+    "^2",			/* superscript 2 (&#178;) - sup2 */
+    "^3",			/* superscript 3 (&#179;) - sup3 */
+    "ss",			/* small sharp s, German (sz ligature) (&#223;) - szlig */
+    "\002",			/* thin space - not collapsed NEVER CHANGE THIS - thinsp */
+    "p",			/* small thorn, Icelandic (&#254;) - thorn */
+    "*",			/* multiplication sign (&#215;) - times */
+    "(TM)",			/* circled TM trade mark sign (&#8482;) - trade */
+    "u",			/* small u, acute accent (&#250;) - uacute */
+    "u",			/* small u, circumflex accent (&#251;) - ucirc */
+    "u",			/* small u, grave accent (&#249;) - ugrave */
+    "\042",			/* spacing dieresis (&#168;) - uml */
+#ifdef LY_UMLAUT
+    "ue",			/* small u, dieresis or umlaut mark (&#252;) - uuml */
+#else
+    "u",			/* small u, dieresis or umlaut mark (&#252;) - uuml */
+#endif				/* LY_UMLAUT */
+    "y",			/* small y, acute accent (&#253;) - yacute */
+    "YEN",			/* yen sign (&#165;) - yen */
+    "y",			/* small y, dieresis or umlaut mark (&#255;) - yuml */
+};
+
+/*
+ * Add your new character sets HERE (but only if you can't construct Unicode
+ * tables for them).  - FM
+ */
+
+/*
+ * Add the array name to LYCharSets
+ */
+const char **LYCharSets[MAXCHARSETS] =
+{
+    ISO_Latin1,			/* ISO Latin 1          */
+    SevenBitApproximations,	/* 7 Bit Approximations */
+};
+
+/*
+ * Add the name that the user will see below.  The order of LYCharSets and
+ * LYchar_set_names MUST be the same
+ */
+const char *LYchar_set_names[MAXCHARSETS + 1] =
+{
+    "Western (ISO-8859-1)",
+    "7 bit approximations (US-ASCII)",
+    (char *) 0
+};
+
+/*
+ * Associate additional pieces of info with each of the charsets listed above. 
+ * Will be automatically modified (and extended) by charset translations which
+ * are loaded using the chartrans mechanism.  Most important piece of info to
+ * put here is a MIME charset name.  Used for chartrans (see UCDefs.h).  The
+ * order of LYCharSets and LYCharSet_UC MUST be the same.
+ *
+ * Note that most of the charsets added by the new mechanism in src/chrtrans
+ * don't show up here at all.  They don't have to.
+ */
+LYUCcharset LYCharSet_UC[MAXCHARSETS] =
+{
+  /*
+   * Zero position placeholder and HTMLGetEntityUCValue() reference.  - FM
+   */
+    {-1, "iso-8859-1", UCT_ENC_8BIT, 0,
+     UCT_REP_IS_LAT1,
+     UCT_CP_IS_LAT1, UCT_R_LAT1, UCT_R_LAT1},
+
+  /*
+   * Placeholders for Unicode tables.  - FM
+   */
+    {-1, "us-ascii", UCT_ENC_7BIT, 0,
+     UCT_REP_SUBSETOF_LAT1,
+     UCT_CP_SUBSETOF_LAT1, UCT_R_ASCII, UCT_R_ASCII},
+
+};
+
+/*
+ * Add the code of the the lowest character with the high bit set that can be
+ * directly displayed.  The order of LYCharSets and LYlowest_eightbit MUST be
+ * the same.
+ *
+ * (If charset have chartrans unicode table, LYlowest_eightbit will be
+ * verified/modified anyway.)
+ */
+int LYlowest_eightbit[MAXCHARSETS] =
+{
+    160,			/* ISO Latin 1          */
+    999,			/* 7 bit approximations */
+};
+
+/*
+ * Function to set the handling of selected character sets based on the current
+ * LYUseDefaultRawMode value.  - FM
+ */
+void HTMLSetCharacterHandling(int i)
+{
+    int chndl = safeUCGetLYhndl_byMIME(UCAssume_MIMEcharset);
+    BOOLEAN LYRawMode_flag = LYRawMode;
+    int UCLYhndl_for_unspec_flag = UCLYhndl_for_unspec;
+
+    if (LYCharSet_UC[i].enc != UCT_ENC_CJK) {
+	HTCJK = NOCJK;
+	kanji_code = NOKANJI;
+	if (i == chndl)
+	    LYRawMode = LYUseDefaultRawMode;
+	else
+	    LYRawMode = (BOOL) (!LYUseDefaultRawMode);
+
+	HTPassEightBitNum = (BOOL) ((LYCharSet_UC[i].codepoints & UCT_CP_SUPERSETOF_LAT1)
+				    || (LYCharSet_UC[i].like8859 & UCT_R_HIGH8BIT));
+
+	if (LYRawMode) {
+	    HTPassEightBitRaw = (BOOL) (LYlowest_eightbit[i] <= 160);
+	} else {
+	    HTPassEightBitRaw = FALSE;
+	}
+	if (LYRawMode || i == chndl) {
+	    HTPassHighCtrlRaw = (BOOL) (LYlowest_eightbit[i] <= 130);
+	} else {
+	    HTPassHighCtrlRaw = FALSE;
+	}
+
+	HTPassHighCtrlNum = FALSE;
+
+    } else {			/* CJK encoding: */
+	const char *mime = LYCharSet_UC[i].MIMEname;
+
+	if (!strcmp(mime, "euc-cn")) {
+	    HTCJK = CHINESE;
+	    kanji_code = EUC;
+	} else if (!strcmp(mime, "euc-jp")) {
+	    HTCJK = JAPANESE;
+	    kanji_code = EUC;
+	} else if (!strcmp(mime, "shift_jis")) {
+	    HTCJK = JAPANESE;
+	    kanji_code = SJIS;
+	} else if (!strcmp(mime, "euc-kr")) {
+	    HTCJK = KOREAN;
+	    kanji_code = EUC;
+	} else if (!strcmp(mime, "big5")) {
+	    HTCJK = TAIPEI;
+	    kanji_code = EUC;
+	}
+
+	/* for any CJK: */
+	if (!LYUseDefaultRawMode)
+	    HTCJK = NOCJK;
+	LYRawMode = (BOOL) (IS_CJK_TTY ? TRUE : FALSE);
+	HTPassEightBitRaw = FALSE;
+	HTPassEightBitNum = FALSE;
+	HTPassHighCtrlRaw = (BOOL) (IS_CJK_TTY ? TRUE : FALSE);
+	HTPassHighCtrlNum = FALSE;
+    }
+
+    /*
+     * Comment for coding below:
+     * UCLYhndl_for_unspec is "current" state with LYRawMode, but
+     * UCAssume_MIMEcharset is independent from LYRawMode:  holds the history
+     * and may be changed from 'O'ptions menu only.  - LP
+     */
+    if (LYRawMode) {
+	UCLYhndl_for_unspec = i;	/* UCAssume_MIMEcharset not changed! */
+    } else {
+	if (chndl != i &&
+	    (LYCharSet_UC[i].enc != UCT_ENC_CJK ||
+	     LYCharSet_UC[chndl].enc != UCT_ENC_CJK)) {
+	    UCLYhndl_for_unspec = chndl;	/* fall to UCAssume_MIMEcharset */
+	} else {
+	    UCLYhndl_for_unspec = LATIN1;	/* UCAssume_MIMEcharset not changed! */
+	}
+    }
+
+#ifdef USE_SLANG
+    if (LYlowest_eightbit[i] > 191) {
+	/*
+	 * Higher than this may output cntrl chars to screen.  - KW
+	 */
+	SLsmg_Display_Eight_Bit = 191;
+    } else {
+	SLsmg_Display_Eight_Bit = LYlowest_eightbit[i];
+    }
+#endif /* USE_SLANG */
+
+    ena_csi((BOOLEAN) (LYlowest_eightbit[current_char_set] > 155));
+
+    /* some diagnostics */
+    if (TRACE) {
+	if (LYRawMode_flag != LYRawMode)
+	    CTRACE((tfp,
+		    "HTMLSetCharacterHandling: LYRawMode changed %s -> %s\n",
+		    (LYRawMode_flag ? "ON" : "OFF"),
+		    (LYRawMode ? "ON" : "OFF")));
+	if (UCLYhndl_for_unspec_flag != UCLYhndl_for_unspec)
+	    CTRACE((tfp,
+		    "HTMLSetCharacterHandling: UCLYhndl_for_unspec changed %d -> %d\n",
+		    UCLYhndl_for_unspec_flag,
+		    UCLYhndl_for_unspec));
+    }
+
+    return;
+}
+
+/*
+ * Function to set HTCJK based on "in" and "out" charsets.
+ */
+void Set_HTCJK(const char *inMIMEname,
+	       const char *outMIMEname)
+{
+    /* need not check for synonyms: MIMEname's got from LYCharSet_UC */
+
+    if (LYRawMode) {
+	if ((!strcmp(inMIMEname, "euc-jp") ||
+#ifdef EXP_JAPANESEUTF8_SUPPORT
+	     !strcmp(inMIMEname, "utf-8") ||
+#endif
+	     !strcmp(inMIMEname, "shift_jis")) &&
+	    (!strcmp(outMIMEname, "euc-jp") ||
+	     !strcmp(outMIMEname, "shift_jis"))) {
+	    HTCJK = JAPANESE;
+	} else if (!strcmp(inMIMEname, "euc-cn") &&
+		   !strcmp(outMIMEname, "euc-cn")) {
+	    HTCJK = CHINESE;
+	} else if (!strcmp(inMIMEname, "big5") &&
+		   !strcmp(outMIMEname, "big5")) {
+	    HTCJK = TAIPEI;
+	} else if (!strcmp(inMIMEname, "euc-kr") &&
+		   !strcmp(outMIMEname, "euc-kr")) {
+	    HTCJK = KOREAN;
+	} else {
+	    HTCJK = NOCJK;
+	}
+    } else {
+	HTCJK = NOCJK;
+    }
+}
+
+/*
+ * Function to set the LYDefaultRawMode value based on the selected character
+ * set.  - FM
+ *
+ * Currently unused:  the default value so obvious that LYUseDefaultRawMode
+ * utilized directly by someone's mistake.  - LP
+ */
+static void HTMLSetRawModeDefault(int i)
+{
+    LYDefaultRawMode = (BOOL) (LYCharSet_UC[i].enc == UCT_ENC_CJK);
+    return;
+}
+
+/*
+ * Function to set the LYUseDefaultRawMode value based on the selected
+ * character set and the current LYRawMode value.  - FM
+ */
+void HTMLSetUseDefaultRawMode(int i,
+			      BOOLEAN modeflag)
+{
+    if (LYCharSet_UC[i].enc != UCT_ENC_CJK) {
+
+	int chndl = safeUCGetLYhndl_byMIME(UCAssume_MIMEcharset);
+
+	if (i == chndl)
+	    LYUseDefaultRawMode = modeflag;
+	else
+	    LYUseDefaultRawMode = (BOOL) (!modeflag);
+    } else			/* CJK encoding: */
+	LYUseDefaultRawMode = modeflag;
+
+    return;
+}
+
+/*
+ * Function to set the LYHaveCJKCharacterSet value based on the selected
+ * character set.  - FM
+ */
+static void HTMLSetHaveCJKCharacterSet(int i)
+{
+    LYHaveCJKCharacterSet = (BOOL) (LYCharSet_UC[i].enc == UCT_ENC_CJK);
+    return;
+}
+
+/*
+ * Function to set the DisplayCharsetMatchLocale value based on the selected
+ * character set.  It is used in UPPER8 for 8bit case-insensitive search by
+ * matching def7_uni.tbl images.  - LP
+ */
+static void HTMLSetDisplayCharsetMatchLocale(int i)
+{
+    BOOLEAN match;
+
+    if (LYHaveCJKCharacterSet) {
+	/*
+	 * We have no intention to pass CJK via UCTransChar if that happened.
+	 * Let someone from CJK correct this if necessary.
+	 */
+	DisplayCharsetMatchLocale = TRUE;	/* old-style */
+	return;
+
+    } else if (strncasecomp(LYCharSet_UC[i].MIMEname, "cp", 2) ||
+	       strncasecomp(LYCharSet_UC[i].MIMEname, "windows", 7)) {
+	/*
+	 * Assume dos/windows displays usually on remote terminal, hence it
+	 * rarely matches locale.  (In fact, MS Windows codepoints locale are
+	 * never seen on UNIX).
+	 */
+	match = FALSE;
+    } else {
+	match = TRUE;		/* guess, but see below */
+
+#if !defined(LOCALE)
+	if (LYCharSet_UC[i].enc != UCT_ENC_UTF8)
+	    /*
+	     * Leave true for utf-8 display - the code doesn't deal very well
+	     * with this case.  - kw
+	     */
+	    match = FALSE;
+#else
+	if (UCForce8bitTOUPPER) {
+	    /*
+	     * Force disable locale (from lynx.cfg)
+	     */
+	    match = FALSE;
+	}
+#endif
+    }
+
+    DisplayCharsetMatchLocale = match;
+    return;
+}
+
+/*
+ * lynx 2.8/2.7.2(and more early) compatibility code:  "human-readable" charset
+ * names changes with time so we map that history names to MIME here to get old
+ * lynx.cfg and (especially) .lynxrc always recognized.  Please update this
+ * table when you change "fullname" of any present charset.
+ */
+typedef struct _names_pairs {
+    const char *fullname;
+    const char *MIMEname;
+} names_pairs;
+/* *INDENT-OFF* */
+static const names_pairs OLD_charset_names[] =
+{
+    {"ISO Latin 1",		"iso-8859-1"},
+    {"ISO Latin 2",             "iso-8859-2"},
+    {"WinLatin1 (cp1252)",      "windows-1252"},
+    {"DEC Multinational",       "dec-mcs"},
+    {"Macintosh (8 bit)",       "macintosh"},
+    {"NeXT character set",      "next"},
+    {"KOI8-R Cyrillic",         "koi8-r"},
+    {"Chinese",                 "euc-cn"},
+    {"Japanese (EUC)",          "euc-jp"},
+    {"Japanese (SJIS)",         "shift_jis"},
+    {"Korean",                  "euc-kr"},
+    {"Taipei (Big5)",           "big5"},
+    {"Vietnamese (VISCII)",     "viscii"},
+    {"7 bit approximations",    "us-ascii"},
+    {"Transparent",             "x-transparent"},
+    {"DosLatinUS (cp437)",      "cp437"},
+    {"IBM PC character set",    "cp437"},
+    {"DosLatin1 (cp850)",       "cp850"},
+    {"IBM PC codepage 850",     "cp850"},
+    {"DosLatin2 (cp852)",       "cp852"},
+    {"PC Latin2 CP 852",        "cp852"},
+    {"DosCyrillic (cp866)",     "cp866"},
+    {"DosArabic (cp864)",       "cp864"},
+    {"DosGreek (cp737)",        "cp737"},
+    {"DosBaltRim (cp775)",      "cp775"},
+    {"DosGreek2 (cp869)",       "cp869"},
+    {"DosHebrew (cp862)",       "cp862"},
+    {"WinLatin2 (cp1250)",      "windows-1250"},
+    {"WinCyrillic (cp1251)",    "windows-1251"},
+    {"WinGreek (cp1253)",       "windows-1253"},
+    {"WinHebrew (cp1255)",      "windows-1255"},
+    {"WinArabic (cp1256)",      "windows-1256"},
+    {"WinBaltRim (cp1257)",     "windows-1257"},
+    {"ISO Latin 3",             "iso-8859-3"},
+    {"ISO Latin 4",             "iso-8859-4"},
+    {"ISO 8859-5 Cyrillic",     "iso-8859-5"},
+    {"ISO 8859-6 Arabic",       "iso-8859-6"},
+    {"ISO 8859-7 Greek",        "iso-8859-7"},
+    {"ISO 8859-8 Hebrew",       "iso-8859-8"},
+    {"ISO-8859-8-I",            "iso-8859-8"},
+    {"ISO-8859-8-E",            "iso-8859-8"},
+    {"ISO 8859-9 (Latin 5)",    "iso-8859-9"},
+    {"ISO 8859-10",             "iso-8859-10"},
+    {"UNICODE UTF 8",           "utf-8"},
+    {"RFC 1345 w/o Intro",      "mnemonic+ascii+0"},
+    {"RFC 1345 Mnemonic",       "mnemonic"},
+    {NULL, NULL},		/* terminated with NULL */
+};
+/* *INDENT-ON* */
+
+/*
+ * lynx 2.8/2.7.2 compatibility code:  read "character_set" parameter from
+ * lynx.cfg and .lynxrc in both MIME name and "human-readable" name (old and
+ * new style).  Returns -1 if not recognized.
+ */
+int UCGetLYhndl_byAnyName(char *value)
+{
+    int i;
+
+    LYTrimTrailing(value);
+    if (value == NULL)
+	return -1;
+
+    CTRACE((tfp, "UCGetLYhndl_byAnyName(%s)\n", value));
+
+    /* search by name */
+    for (i = 0; (i < MAXCHARSETS && LYchar_set_names[i]); i++) {
+	if (!strcmp(value, LYchar_set_names[i])) {
+	    return i;		/* OK */
+	}
+    }
+
+    /* search by old name from 2.8/2.7.2 version */
+    for (i = 0; (OLD_charset_names[i].fullname); i++) {
+	if (!strcmp(value, OLD_charset_names[i].fullname)) {
+	    return UCGetLYhndl_byMIME(OLD_charset_names[i].MIMEname);	/* OK */
+	}
+    }
+
+    return UCGetLYhndl_byMIME(value);	/* by MIME */
+}
+
+/*
+ * Entity names -- Ordered by ISO Latin 1 value.
+ * ---------------------------------------------
+ * For conversions of DECIMAL escaped entities.
+ * Must be in order of ascending value.
+ */
+static const char *LYEntityNames[] =
+{
+/*	 NAME		   DECIMAL VALUE */
+    "nbsp",			/* 160, non breaking space */
+    "iexcl",			/* 161, inverted exclamation mark */
+    "cent",			/* 162, cent sign */
+    "pound",			/* 163, pound sign */
+    "curren",			/* 164, currency sign */
+    "yen",			/* 165, yen sign */
+    "brvbar",			/* 166, broken vertical bar, (brkbar) */
+    "sect",			/* 167, section sign */
+    "uml",			/* 168, spacing dieresis */
+    "copy",			/* 169, copyright sign */
+    "ordf",			/* 170, feminine ordinal indicator */
+    "laquo",			/* 171, angle quotation mark, left */
+    "not",			/* 172, negation sign */
+    "shy",			/* 173, soft hyphen */
+    "reg",			/* 174, circled R registered sign */
+    "hibar",			/* 175, spacing macron */
+    "deg",			/* 176, degree sign */
+    "plusmn",			/* 177, plus-or-minus sign */
+    "sup2",			/* 178, superscript 2 */
+    "sup3",			/* 179, superscript 3 */
+    "acute",			/* 180, spacing acute (96) */
+    "micro",			/* 181, micro sign */
+    "para",			/* 182, paragraph sign */
+    "middot",			/* 183, middle dot */
+    "cedil",			/* 184, spacing cedilla */
+    "sup1",			/* 185, superscript 1 */
+    "ordm",			/* 186, masculine ordinal indicator */
+    "raquo",			/* 187, angle quotation mark, right */
+    "frac14",			/* 188, fraction 1/4 */
+    "frac12",			/* 189, fraction 1/2 */
+    "frac34",			/* 190, fraction 3/4 */
+    "iquest",			/* 191, inverted question mark */
+    "Agrave",			/* 192, capital A, grave accent */
+    "Aacute",			/* 193, capital A, acute accent */
+    "Acirc",			/* 194, capital A, circumflex accent */
+    "Atilde",			/* 195, capital A, tilde */
+    "Auml",			/* 196, capital A, dieresis or umlaut mark */
+    "Aring",			/* 197, capital A, ring */
+    "AElig",			/* 198, capital AE diphthong (ligature) */
+    "Ccedil",			/* 199, capital C, cedilla */
+    "Egrave",			/* 200, capital E, grave accent */
+    "Eacute",			/* 201, capital E, acute accent */
+    "Ecirc",			/* 202, capital E, circumflex accent */
+    "Euml",			/* 203, capital E, dieresis or umlaut mark */
+    "Igrave",			/* 204, capital I, grave accent */
+    "Iacute",			/* 205, capital I, acute accent */
+    "Icirc",			/* 206, capital I, circumflex accent */
+    "Iuml",			/* 207, capital I, dieresis or umlaut mark */
+    "ETH",			/* 208, capital Eth, Icelandic (or Latin2 Dstrok) */
+    "Ntilde",			/* 209, capital N, tilde */
+    "Ograve",			/* 210, capital O, grave accent */
+    "Oacute",			/* 211, capital O, acute accent */
+    "Ocirc",			/* 212, capital O, circumflex accent */
+    "Otilde",			/* 213, capital O, tilde */
+    "Ouml",			/* 214, capital O, dieresis or umlaut mark */
+    "times",			/* 215, multiplication sign */
+    "Oslash",			/* 216, capital O, slash */
+    "Ugrave",			/* 217, capital U, grave accent */
+    "Uacute",			/* 218, capital U, acute accent */
+    "Ucirc",			/* 219, capital U, circumflex accent */
+    "Uuml",			/* 220, capital U, dieresis or umlaut mark */
+    "Yacute",			/* 221, capital Y, acute accent */
+    "THORN",			/* 222, capital THORN, Icelandic */
+    "szlig",			/* 223, small sharp s, German (sz ligature) */
+    "agrave",			/* 224, small a, grave accent */
+    "aacute",			/* 225, small a, acute accent */
+    "acirc",			/* 226, small a, circumflex accent */
+    "atilde",			/* 227, small a, tilde */
+    "auml",			/* 228, small a, dieresis or umlaut mark */
+    "aring",			/* 229, small a, ring */
+    "aelig",			/* 230, small ae diphthong (ligature) */
+    "ccedil",			/* 231, small c, cedilla */
+    "egrave",			/* 232, small e, grave accent */
+    "eacute",			/* 233, small e, acute accent */
+    "ecirc",			/* 234, small e, circumflex accent */
+    "euml",			/* 235, small e, dieresis or umlaut mark */
+    "igrave",			/* 236, small i, grave accent */
+    "iacute",			/* 237, small i, acute accent */
+    "icirc",			/* 238, small i, circumflex accent */
+    "iuml",			/* 239, small i, dieresis or umlaut mark */
+    "eth",			/* 240, small eth, Icelandic */
+    "ntilde",			/* 241, small n, tilde */
+    "ograve",			/* 242, small o, grave accent */
+    "oacute",			/* 243, small o, acute accent */
+    "ocirc",			/* 244, small o, circumflex accent */
+    "otilde",			/* 245, small o, tilde */
+    "ouml",			/* 246, small o, dieresis or umlaut mark */
+    "divide",			/* 247, division sign */
+    "oslash",			/* 248, small o, slash */
+    "ugrave",			/* 249, small u, grave accent */
+    "uacute",			/* 250, small u, acute accent */
+    "ucirc",			/* 251, small u, circumflex accent */
+    "uuml",			/* 252, small u, dieresis or umlaut mark */
+    "yacute",			/* 253, small y, acute accent */
+    "thorn",			/* 254, small thorn, Icelandic */
+    "yuml",			/* 255, small y, dieresis or umlaut mark */
+};
+
+/*
+ * Function to return the entity names of ISO-8859-1 8-bit characters.  - FM
+ */
+const char *HTMLGetEntityName(UCode_t code)
+{
+#define IntValue code
+    int MaxValue = (TABLESIZE(LYEntityNames) - 1);
+
+    if (IntValue < 0 || IntValue > MaxValue) {
+	return "";
+    }
+
+    return LYEntityNames[IntValue];
+}
+
+/*
+ * Function to return the UCode_t (long int) value for entity names.  It
+ * returns 0 if not found.
+ *
+ * unicode_entities[] handles all the names from old style entities[] too. 
+ * Lynx now calls unicode_entities[] only through this function: 
+ * HTMLGetEntityUCValue().  Note, we need not check for special characters here
+ * in function or even before it, we should check them *after* invoking this
+ * function, see put_special_unicodes() in SGML.c.
+ *
+ * In the future we will try to isolate all calls to entities[] in favor of new
+ * unicode-based chartrans scheme.  - LP
+ */
+UCode_t HTMLGetEntityUCValue(const char *name)
+{
+#include <entities.h>
+
+    UCode_t value = 0;
+    size_t i, high, low;
+    int diff = 0;
+    size_t number_of_unicode_entities = TABLESIZE(unicode_entities);
+
+    /*
+     * Make sure we have a non-zero length name.  - FM
+     */
+    if (isEmpty(name))
+	return (value);
+
+    /*
+     * Try UC_entity_info unicode_entities[].
+     */
+    for (low = 0, high = number_of_unicode_entities;
+	 high > low;
+	 diff < 0 ? (low = i + 1) : (high = i)) {
+	/*
+	 * Binary search.
+	 */
+	i = (low + (high - low) / 2);
+	diff = AS_cmp(unicode_entities[i].name, name);	/* Case sensitive! */
+	if (diff == 0) {
+	    value = unicode_entities[i].code;
+	    break;
+	}
+    }
+    return (value);
+}
+
+/*
+ * Original comment -
+ * Assume these are Microsoft code points, inflicted on us by FrontPage.  - FM
+ *
+ * MS FrontPage uses syntax like &#153; in 128-159 range and doesn't follow
+ * Unicode standards for this area.  Windows-1252 codepoints are assumed here.
+ *
+ * However see -
+ * http://www.whatwg.org/specs/web-apps/current-work/multipage/infrastructure.html#character-encodings-0
+ */
+UCode_t LYcp1252ToUnicode(UCode_t code)
+{
+    if ((code == 1) ||
+	(code > 127 && code < 160)) {
+	switch (code) {
+	case 1:
+	    /*
+	     * WHITE SMILING FACE
+	     */
+	    code = 0x263a;
+	    break;
+	case 128:
+	    /*
+	     * EURO currency sign
+	     */
+	    code = 0x20ac;
+	    break;
+	case 130:
+	    /*
+	     * SINGLE LOW-9 QUOTATION MARK (sbquo)
+	     */
+	    code = 0x201a;
+	    break;
+	case 131:
+	    /*
+	     * LATIN SMALL LETTER F WITH HOOK
+	     */
+	    code = 0x192;
+	    break;
+	case 132:
+	    /*
+	     * DOUBLE LOW-9 QUOTATION MARK (bdquo)
+	     */
+	    code = 0x201e;
+	    break;
+	case 133:
+	    /*
+	     * HORIZONTAL ELLIPSIS (hellip)
+	     */
+	    code = 0x2026;
+	    break;
+	case 134:
+	    /*
+	     * DAGGER (dagger)
+	     */
+	    code = 0x2020;
+	    break;
+	case 135:
+	    /*
+	     * DOUBLE DAGGER (Dagger)
+	     */
+	    code = 0x2021;
+	    break;
+	case 136:
+	    /*
+	     * MODIFIER LETTER CIRCUMFLEX ACCENT
+	     */
+	    code = 0x2c6;
+	    break;
+	case 137:
+	    /*
+	     * PER MILLE SIGN (permil)
+	     */
+	    code = 0x2030;
+	    break;
+	case 138:
+	    /*
+	     * LATIN CAPITAL LETTER S WITH CARON
+	     */
+	    code = 0x160;
+	    break;
+	case 139:
+	    /*
+	     * SINGLE LEFT-POINTING ANGLE QUOTATION MARK (lsaquo)
+	     */
+	    code = 0x2039;
+	    break;
+	case 140:
+	    /*
+	     * LATIN CAPITAL LIGATURE OE
+	     */
+	    code = 0x152;
+	    break;
+	case 142:
+	    /*
+	     * LATIN CAPITAL LETTER Z WITH CARON
+	     */
+	    code = 0x17d;
+	    break;
+	case 145:
+	    /*
+	     * LEFT SINGLE QUOTATION MARK (lsquo)
+	     */
+	    code = 0x2018;
+	    break;
+	case 146:
+	    /*
+	     * RIGHT SINGLE QUOTATION MARK (rsquo)
+	     */
+	    code = 0x2019;
+	    break;
+	case 147:
+	    /*
+	     * LEFT DOUBLE QUOTATION MARK (ldquo)
+	     */
+	    code = 0x201c;
+	    break;
+	case 148:
+	    /*
+	     * RIGHT DOUBLE QUOTATION MARK (rdquo)
+	     */
+	    code = 0x201d;
+	    break;
+	case 149:
+	    /*
+	     * BULLET (bull)
+	     */
+	    code = 0x2022;
+	    break;
+	case 150:
+	    /*
+	     * EN DASH (ndash)
+	     */
+	    code = 0x2013;
+	    break;
+	case 151:
+	    /*
+	     * EM DASH (mdash)
+	     */
+	    code = 0x2014;
+	    break;
+	case 152:
+	    /*
+	     * SMALL TILDE (tilde)
+	     */
+	    code = 0x02dc;
+	    break;
+	case 153:
+	    /*
+	     * TRADE MARK SIGN (trade)
+	     */
+	    code = 0x2122;
+	    break;
+	case 154:
+	    /*
+	     * LATIN SMALL LETTER S WITH CARON
+	     */
+	    code = 0x161;
+	    break;
+	case 155:
+	    /*
+	     * SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (rsaquo)
+	     */
+	    code = 0x203a;
+	    break;
+	case 156:
+	    /*
+	     * LATIN SMALL LIGATURE OE
+	     */
+	    code = 0x153;
+	    break;
+	case 158:
+	    /*
+	     * LATIN SMALL LETTER Z WITH CARON
+	     */
+	    code = 0x17e;
+	    break;
+	case 159:
+	    /*
+	     * LATIN CAPITAL LETTER Y WITH DIAERESIS
+	     */
+	    code = 0x178;
+	    break;
+	default:
+	    /*
+	     * Undefined (by convention, use the replacement character).
+	     */
+	    code = 0xfffd;
+	    break;
+	}
+    }
+    return code;
+}
+
+/*
+ * Function to select a character set and then set the character handling and
+ * LYHaveCJKCharacterSet flag.  - FM
+ */
+void HTMLUseCharacterSet(int i)
+{
+    HTMLSetRawModeDefault(i);
+    p_entity_values = LYCharSets[i];
+    HTMLSetCharacterHandling(i);	/* set LYRawMode and CJK attributes */
+    HTMLSetHaveCJKCharacterSet(i);
+    HTMLSetDisplayCharsetMatchLocale(i);
+    return;
+}
+
+/*
+ * Initializer, calls initialization function for the CHARTRANS handling.  - KW
+ */
+int LYCharSetsDeclared(void)
+{
+    UCInit();
+
+    return UCInitialized;
+}
+
+#ifdef USE_CHARSET_CHOICE
+void init_charset_subsets(void)
+{
+    int i, n;
+    int cur_display = 0;
+    int cur_assumed = 0;
+
+    /* add them to displayed values */
+    charset_subsets[UCLYhndl_for_unspec].hide_assumed = FALSE;
+    charset_subsets[current_char_set].hide_display = FALSE;
+
+#ifndef ALL_CHARSETS_IN_O_MENU_SCREEN
+    /*all this stuff is for supporting old menu screen... */
+    for (i = 0; i < LYNumCharsets; ++i) {
+	if (charset_subsets[i].hide_display == FALSE) {
+	    n = cur_display++;
+	    if (i == current_char_set)
+		displayed_display_charset_idx = n;
+	    display_charset_map[n] = i;
+	    display_charset_choices[n] = LYchar_set_names[i];
+	}
+	if (charset_subsets[i].hide_assumed == FALSE) {
+	    n = cur_assumed++;
+	    assumed_doc_charset_map[n] = i;
+	    assumed_charset_choices[n] = LYCharSet_UC[i].MIMEname;
+	    charset_subsets[i].assumed_idx = n;
+	}
+	display_charset_choices[cur_display] = NULL;
+	assumed_charset_choices[cur_assumed] = NULL;
+    }
+#endif
+}
+#endif /* USE_CHARSET_CHOICE */
diff --git a/src/LYCharSets.h b/src/LYCharSets.h
new file mode 100644
index 00000000..0b698be1
--- /dev/null
+++ b/src/LYCharSets.h
@@ -0,0 +1,154 @@
+/*
+ * $LynxId: LYCharSets.h,v 1.32 2009/11/21 15:52:05 tom Exp $
+ */
+#ifndef LYCHARSETS_H
+#define LYCHARSETS_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#include <UCDefs.h>
+
+#ifndef UCMAP_H
+#include <UCMap.h>
+#endif /* !UCMAP_H */
+
+#include <HTCJK.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    extern BOOL HTPassEightBitRaw;
+    extern BOOL HTPassEightBitNum;
+    extern BOOL HTPassHighCtrlRaw;
+    extern BOOL HTPassHighCtrlNum;
+    extern BOOLEAN LYHaveCJKCharacterSet;
+    extern BOOLEAN DisplayCharsetMatchLocale;
+
+    extern HTkcode kanji_code;
+
+/*
+ *  currently active character set (internal handler)
+ */
+    extern int current_char_set;
+/*
+ *  character set where it is safe to draw lines on boxes.
+ */
+    extern int linedrawing_char_set;
+
+/*
+ *  Initializer, calls initialization function for the
+ *  CHARTRANS handling. - KW
+ */
+    extern int LYCharSetsDeclared(void);
+
+    extern const char **LYCharSets[];
+    extern const char *SevenBitApproximations[];
+    extern const char **p_entity_values;
+    extern const char *LYchar_set_names[];	/* Full name, not MIME */
+    extern int LYlowest_eightbit[];
+    extern int LYNumCharsets;
+    extern LYUCcharset LYCharSet_UC[];
+    extern int UCGetLYhndl_byAnyName(char *value);
+    extern void HTMLSetCharacterHandling(int i);
+    extern void HTMLSetUseDefaultRawMode(int i, BOOLEAN modeflag);
+    extern void HTMLUseCharacterSet(int i);
+    extern UCode_t HTMLGetEntityUCValue(const char *name);
+    extern void Set_HTCJK(const char *inMIMEname, const char *outMIMEname);
+
+    extern const char *HTMLGetEntityName(UCode_t code);
+
+    UCode_t LYcp1252ToUnicode(UCode_t code);
+
+/*
+ * HTMLGetEntityName calls LYEntityNames for iso-8859-1 entity names only. 
+ * This is an obsolete technique but widely used in the code.  Note that
+ * unicode number in general may have several equivalent entity names because
+ * of synonyms.
+ */
+    extern BOOL force_old_UCLYhndl_on_reload;
+    extern int forced_UCLYhdnl;
+
+#ifndef  USE_CHARSET_CHOICE
+# define ALL_CHARSETS_IN_O_MENU_SCREEN 1
+#endif
+
+#ifdef USE_CHARSET_CHOICE
+    typedef struct {
+	BOOL hide_display;	/* if FALSE, show in "display-charset" menu */
+	BOOL hide_assumed;	/* if FALSE, show in "assumed-charset" menu */
+#ifndef ALL_CHARSETS_IN_O_MENU_SCREEN
+	int assumed_idx;	/* only this field is needed */
+#endif
+    } charset_subset_t;
+
+/* each element corresponds to charset in LYCharSets */
+    extern charset_subset_t charset_subsets[];
+
+/* all zeros by default - i.e., all charsets allowed */
+
+/*
+ * true if the charset choices for display charset were requested by user via
+ * lynx.cfg.  It will remain FALSE if no "display_charset_choice" settings were
+ * encountered in lynx.cfg
+ */
+    extern BOOL custom_display_charset;
+
+/* similar to custom_display_charset */
+    extern BOOL custom_assumed_doc_charset;
+
+#ifndef ALL_CHARSETS_IN_O_MENU_SCREEN
+
+/* this stuff is initialized after reading lynx.cfg and .lynxrc */
+
+/*
+ * These arrays map index of charset shown in menu to the index in LYCharsets[]
+ */
+    extern int display_charset_map[];
+    extern int assumed_doc_charset_map[];
+
+/* these arrays are NULL terminated */
+    extern const char *display_charset_choices[];
+    extern const char *assumed_charset_choices[];
+
+    extern int displayed_display_charset_idx;
+
+#endif
+/* this will be called after lynx.cfg and .lynxrc are read */
+    extern void init_charset_subsets(void);
+#endif				/* USE_CHARSET_CHOICE */
+
+#if !defined(NO_AUTODETECT_DISPLAY_CHARSET)
+#  ifdef __EMX__
+#    define CAN_AUTODETECT_DISPLAY_CHARSET
+#    ifdef EXP_CHARTRANS_AUTOSWITCH
+#      define CAN_SWITCH_DISPLAY_CHARSET
+#    endif
+#  endif
+#endif
+
+#ifdef CAN_AUTODETECT_DISPLAY_CHARSET
+    extern int auto_display_charset;
+#endif
+
+#ifdef CAN_SWITCH_DISPLAY_CHARSET
+    enum switch_display_charset_t {
+	SWITCH_DISPLAY_CHARSET_MAYBE,
+	SWITCH_DISPLAY_CHARSET_REALLY,
+	SWITCH_DISPLAY_CHARSET_RESIZE
+    };
+    extern int Switch_Display_Charset(int ord, enum switch_display_charset_t really);
+    extern int Find_Best_Display_Charset(int ord);
+    extern char *charsets_directory;
+    extern char *charset_switch_rules;
+    extern int switch_display_charsets;
+    extern int auto_other_display_charset;
+    extern int codepages[2];
+    extern int real_charsets[2];	/* Non "auto-" charsets for the codepages */
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYCHARSETS_H */
diff --git a/src/LYCharUtils.c b/src/LYCharUtils.c
new file mode 100644
index 00000000..c6be3aee
--- /dev/null
+++ b/src/LYCharUtils.c
@@ -0,0 +1,3415 @@
+/*
+ * $LynxId: LYCharUtils.c,v 1.105 2010/04/29 20:53:31 tom Exp $
+ *
+ *  Functions associated with LYCharSets.c and the Lynx version of HTML.c - FM
+ *  ==========================================================================
+ */
+#include <HTUtils.h>
+#include <SGML.h>
+
+#define Lynx_HTML_Handler
+#include <HTChunk.h>
+#include <HText.h>
+#include <HTStyle.h>
+#include <HTMIME.h>
+#include <HTML.h>
+
+#include <HTCJK.h>
+#include <HTAtom.h>
+#include <HTMLGen.h>
+#include <HTParse.h>
+#include <UCMap.h>
+#include <UCDefs.h>
+#include <UCAux.h>
+
+#include <LYGlobalDefs.h>
+#include <LYCharUtils.h>
+#include <LYCharSets.h>
+
+#include <HTAlert.h>
+#include <HTForms.h>
+#include <HTNestedList.h>
+#include <GridText.h>
+#include <LYStrings.h>
+#include <LYUtils.h>
+#include <LYMap.h>
+#include <LYBookmark.h>
+#include <LYCurses.h>
+#include <LYCookie.h>
+
+#include <LYexit.h>
+#include <LYLeaks.h>
+
+/*
+ * Used for nested lists.  - FM
+ */
+int OL_CONTINUE = -29999;	/* flag for whether CONTINUE is set */
+int OL_VOID = -29998;		/* flag for whether a count is set */
+
+/*
+ *  This function converts any ampersands in allocated
+ *  strings to "&amp;".  If isTITLE is TRUE, it also
+ *  converts any angle-brackets to "&lt;" or "&gt;". - FM
+ */
+void LYEntify(char **str,
+	      BOOLEAN isTITLE)
+{
+    char *p = *str;
+    char *q = NULL, *cp = NULL;
+    int amps = 0, lts = 0, gts = 0;
+
+#ifdef CJK_EX
+    enum _state {
+	S_text,
+	S_esc,
+	S_dollar,
+	S_paren,
+	S_nonascii_text,
+	S_dollar_paren
+    } state = S_text;
+    int in_sjis = 0;
+#endif
+
+    if (isEmpty(p))
+	return;
+
+    /*
+     * Count the ampersands.  - FM
+     */
+    while ((*p != '\0') && (q = strchr(p, '&')) != NULL) {
+	amps++;
+	p = (q + 1);
+    }
+
+    /*
+     * Count the left-angle-brackets, if needed.  - FM
+     */
+    if (isTITLE == TRUE) {
+	p = *str;
+	while ((*p != '\0') && (q = strchr(p, '<')) != NULL) {
+	    lts++;
+	    p = (q + 1);
+	}
+    }
+
+    /*
+     * Count the right-angle-brackets, if needed.  - FM
+     */
+    if (isTITLE == TRUE) {
+	p = *str;
+	while ((*p != '\0') && (q = strchr(p, '>')) != NULL) {
+	    gts++;
+	    p = (q + 1);
+	}
+    }
+
+    /*
+     * Check whether we need to convert anything.  - FM
+     */
+    if (amps == 0 && lts == 0 && gts == 0)
+	return;
+
+    /*
+     * Allocate space and convert.  - FM
+     */
+    q = typecallocn(char,
+		    (strlen(*str)
+		     + (unsigned)(4 * amps)
+		     + (unsigned)(3 * lts)
+		     + (unsigned)(3 * gts) + 1));
+    if ((cp = q) == NULL)
+	outofmem(__FILE__, "LYEntify");
+
+    assert(cp != NULL);
+    assert(q != NULL);
+
+    for (p = *str; *p; p++) {
+#ifdef CJK_EX
+	if (IS_CJK_TTY) {
+	    switch (state) {
+	    case S_text:
+		if (*p == '\033') {
+		    state = S_esc;
+		    *q++ = *p;
+		    continue;
+		}
+		break;
+
+	    case S_esc:
+		if (*p == '$') {
+		    state = S_dollar;
+		    *q++ = *p;
+		    continue;
+		} else if (*p == '(') {
+		    state = S_paren;
+		    *q++ = *p;
+		    continue;
+		} else {
+		    state = S_text;
+		    *q++ = *p;
+		    continue;
+		}
+
+	    case S_dollar:
+		if (*p == '@' || *p == 'B' || *p == 'A') {
+		    state = S_nonascii_text;
+		    *q++ = *p;
+		    continue;
+		} else if (*p == '(') {
+		    state = S_dollar_paren;
+		    *q++ = *p;
+		    continue;
+		} else {
+		    state = S_text;
+		    *q++ = *p;
+		    continue;
+		}
+
+	    case S_dollar_paren:
+		if (*p == 'C') {
+		    state = S_nonascii_text;
+		    *q++ = *p;
+		    continue;
+		} else {
+		    state = S_text;
+		    *q++ = *p;
+		    continue;
+		}
+
+	    case S_paren:
+		if (*p == 'B' || *p == 'J' || *p == 'T') {
+		    state = S_text;
+		    *q++ = *p;
+		    continue;
+		} else if (*p == 'I') {
+		    state = S_nonascii_text;
+		    *q++ = *p;
+		    continue;
+		}
+		/* FALLTHRU */
+
+	    case S_nonascii_text:
+		if (*p == '\033')
+		    state = S_esc;
+		*q++ = *p;
+		continue;
+
+	    default:
+		break;
+	    }
+	    if (*(p + 1) != '\0' &&
+		(IS_EUC(UCH(*p), UCH(*(p + 1))) ||
+		 IS_SJIS(UCH(*p), UCH(*(p + 1)), in_sjis) ||
+		 IS_BIG5(UCH(*p), UCH(*(p + 1))))) {
+		*q++ = *p++;
+		*q++ = *p;
+		continue;
+	    }
+	}
+#endif
+	if (*p == '&') {
+	    *q++ = '&';
+	    *q++ = 'a';
+	    *q++ = 'm';
+	    *q++ = 'p';
+	    *q++ = ';';
+	} else if (isTITLE && *p == '<') {
+	    *q++ = '&';
+	    *q++ = 'l';
+	    *q++ = 't';
+	    *q++ = ';';
+	} else if (isTITLE && *p == '>') {
+	    *q++ = '&';
+	    *q++ = 'g';
+	    *q++ = 't';
+	    *q++ = ';';
+	} else {
+	    *q++ = *p;
+	}
+    }
+    *q = '\0';
+    FREE(*str);
+    *str = cp;
+}
+
+/*
+ * Callers to LYEntifyTitle/LYEntifyValue do not look at the 'target' param.
+ * Optimize things a little by avoiding the memory allocation if not needed,
+ * as is usually the case.
+ */
+static BOOL MustEntify(const char *source)
+{
+    BOOL result;
+
+#ifdef CJK_EX
+    if (IS_CJK_TTY && strchr(source, '\033') != 0) {
+	result = TRUE;
+    } else
+#endif
+    {
+	size_t length = strlen(source);
+	size_t reject = strcspn(source, "<&>");
+
+	result = (BOOL) (length != reject);
+    }
+
+    return result;
+}
+
+/*
+ * Wrappers for LYEntify() which do not assume that the source was allocated,
+ * e.g., output from gettext().
+ */
+const char *LYEntifyTitle(char **target, const char *source)
+{
+    const char *result = 0;
+
+    if (MustEntify(source)) {
+	StrAllocCopy(*target, source);
+	LYEntify(target, TRUE);
+	result = *target;
+    } else {
+	result = source;
+    }
+    return result;
+}
+
+const char *LYEntifyValue(char **target, const char *source)
+{
+    const char *result = 0;
+
+    if (MustEntify(source)) {
+	StrAllocCopy(*target, source);
+	LYEntify(target, FALSE);
+	result = *target;
+    } else {
+	result = source;
+    }
+    return result;
+}
+
+/*
+ *  This function trims characters <= that of a space (32),
+ *  including HT_NON_BREAK_SPACE (1) and HT_EN_SPACE (2),
+ *  but not ESC, from the heads of strings. - FM
+ */
+void LYTrimHead(char *str)
+{
+    const char *s = str;
+
+    if (isEmpty(s))
+	return;
+
+    while (*s && WHITE(*s) && UCH(*s) != UCH(CH_ESC))	/* S/390 -- gil -- 1669 */
+	s++;
+    if (s > str) {
+	char *ns = str;
+
+	while (*s) {
+	    *ns++ = *s++;
+	}
+	*ns = '\0';
+    }
+}
+
+/*
+ *  This function trims characters <= that of a space (32),
+ *  including HT_NON_BREAK_SPACE (1), HT_EN_SPACE (2), and
+ *  ESC from the tails of strings. - FM
+ */
+void LYTrimTail(char *str)
+{
+    int i;
+
+    if (isEmpty(str))
+	return;
+
+    i = (int) strlen(str) - 1;
+    while (i >= 0) {
+	if (WHITE(str[i]))
+	    str[i] = '\0';
+	else
+	    break;
+	i--;
+    }
+}
+
+/*
+ * This function should receive a pointer to the start
+ * of a comment.  It returns a pointer to the end ('>')
+ * character of comment, or it's best guess if the comment
+ * is invalid. - FM
+ */
+char *LYFindEndOfComment(char *str)
+{
+    char *cp, *cp1;
+    enum comment_state {
+	start1,
+	start2,
+	end1,
+	end2
+    } state;
+
+    if (str == NULL)
+	/*
+	 * We got NULL, so return NULL.  - FM
+	 */
+	return NULL;
+
+    if (strncmp(str, "<!--", 4))
+	/*
+	 * We don't have the start of a comment, so return the beginning of the
+	 * string.  - FM
+	 */
+	return str;
+
+    cp = (str + 4);
+    if (*cp == '>')
+	/*
+	 * It's an invalid comment, so
+	 * return this end character. - FM
+	 */
+	return cp;
+
+    if ((cp1 = strchr(cp, '>')) == NULL)
+	/*
+	 * We don't have an end character, so return the beginning of the
+	 * string.  - FM
+	 */
+	return str;
+
+    if (*cp == '-')
+	/*
+	 * Ugh, it's a "decorative" series of dashes, so return the next end
+	 * character.  - FM
+	 */
+	return cp1;
+
+    /*
+     * OK, we're ready to start parsing.  - FM
+     */
+    state = start2;
+    while (*cp != '\0') {
+	switch (state) {
+	case start1:
+	    if (*cp == '-')
+		state = start2;
+	    else
+		/*
+		 * Invalid comment, so return the first '>' from the start of
+		 * the string.  - FM
+		 */
+		return cp1;
+	    break;
+
+	case start2:
+	    if (*cp == '-')
+		state = end1;
+	    break;
+
+	case end1:
+	    if (*cp == '-')
+		state = end2;
+	    else
+		/*
+		 * Invalid comment, so return the first '>' from the start of
+		 * the string.  - FM
+		 */
+		return cp1;
+	    break;
+
+	case end2:
+	    if (*cp == '>')
+		/*
+		 * Valid comment, so return the end character.  - FM
+		 */
+		return cp;
+	    if (*cp == '-') {
+		state = start1;
+	    } else if (!(WHITE(*cp) && UCH(*cp) != UCH(CH_ESC))) {	/* S/390 -- gil -- 1686 */
+		/*
+		 * Invalid comment, so return the first '>' from the start of
+		 * the string.  - FM
+		 */
+		return cp1;
+	    }
+	    break;
+
+	default:
+	    break;
+	}
+	cp++;
+    }
+
+    /*
+     * Invalid comment, so return the first '>' from the start of the string. 
+     * - FM
+     */
+    return cp1;
+}
+
+/*
+ *  If an HREF, itself or if resolved against a base,
+ *  represents a file URL, and the host is defaulted,
+ *  force in "//localhost".  We need this until
+ *  all the other Lynx code which performs security
+ *  checks based on the "localhost" string is changed
+ *  to assume "//localhost" when a host field is not
+ *  present in file URLs - FM
+ */
+void LYFillLocalFileURL(char **href,
+			const char *base)
+{
+    char *temp = NULL;
+
+    if (isEmpty(*href))
+	return;
+
+    if (!strcmp(*href, "//") || !strncmp(*href, "///", 3)) {
+	if (base != NULL && isFILE_URL(base)) {
+	    StrAllocCopy(temp, STR_FILE_URL);
+	    StrAllocCat(temp, *href);
+	    StrAllocCopy(*href, temp);
+	}
+    }
+    if (isFILE_URL(*href)) {
+	if (*(*href + 5) == '\0') {
+	    StrAllocCat(*href, "//localhost");
+	} else if (!strcmp(*href, "file://")) {
+	    StrAllocCat(*href, "localhost");
+	} else if (!strncmp(*href, "file:///", 8)) {
+	    StrAllocCopy(temp, (*href + 7));
+	    LYLocalFileToURL(href, temp);
+	} else if (!strncmp(*href, "file:/", 6) && !LYIsHtmlSep(*(*href + 6))) {
+	    StrAllocCopy(temp, (*href + 5));
+	    LYLocalFileToURL(href, temp);
+	}
+    }
+#if defined(USE_DOS_DRIVES)
+    if (LYIsDosDrive(*href)) {
+	/*
+	 * If it's a local DOS path beginning with drive letter,
+	 * add file://localhost/ prefix and go ahead.
+	 */
+	StrAllocCopy(temp, *href);
+	LYLocalFileToURL(href, temp);
+    }
+
+    /* use below: strlen("file://localhost/") = 17 */
+    if (!strncmp(*href, "file://localhost/", 17)
+	&& (strlen(*href) == 19)
+	&& LYIsDosDrive(*href + 17)) {
+	/*
+	 * Terminate DOS drive letter with a slash to surf root successfully.
+	 * Here seems a proper place to do so.
+	 */
+	LYAddPathSep(href);
+    }
+#endif /* USE_DOS_DRIVES */
+
+    /*
+     * No path in a file://localhost URL means a
+     * directory listing for the current default. - FM
+     */
+    if (!strcmp(*href, "file://localhost")) {
+	const char *temp2;
+
+#ifdef VMS
+	temp2 = HTVMS_wwwName(LYGetEnv("PATH"));
+#else
+	char curdir[LY_MAXPATH];
+
+	temp2 = wwwName(Current_Dir(curdir));
+#endif /* VMS */
+	if (!LYIsHtmlSep(*temp2))
+	    LYAddHtmlSep(href);
+	/*
+	 * Check for pathological cases - current dir has chars which MUST BE
+	 * URL-escaped - kw
+	 */
+	if (strchr(temp2, '%') != NULL || strchr(temp2, '#') != NULL) {
+	    FREE(temp);
+	    temp = HTEscape(temp2, URL_PATH);
+	    StrAllocCat(*href, temp);
+	} else {
+	    StrAllocCat(*href, temp2);
+	}
+    }
+#ifdef VMS
+    /*
+     * On VMS, a file://localhost/ URL means
+     * a listing for the login directory. - FM
+     */
+    if (!strcmp(*href, "file://localhost/"))
+	StrAllocCat(*href, (HTVMS_wwwName(Home_Dir()) + 1));
+#endif /* VMS */
+
+    FREE(temp);
+    return;
+}
+
+/*
+ *  This function writes a line with a META tag to an open file,
+ *  which will specify a charset parameter to use when the file is
+ *  read back in.  It is meant for temporary HTML files used by the
+ *  various special pages which may show titles of documents.  When those
+ *  files are created, the title strings normally have been translated and
+ *  expanded to the display character set, so we have to make sure they
+ *  don't get translated again.
+ *  If the user has changed the display character set during the lifetime
+ *  of the Lynx session (or, more exactly, during the time the title
+ *  strings to be written were generated), they may now have different
+ *  character encodings and there is currently no way to get it all right.
+ *  To change this, we would have to add a variable for each string which
+ *  keeps track of its character encoding.
+ *  But at least we can try to ensure that reading the file after future
+ *  display character set changes will give reasonable output.
+ *
+ *  The META tag is not written if the display character set (passed as
+ *  disp_chndl) already corresponds to the charset assumption that
+ *  would be made when the file is read. - KW
+ *
+ *  Currently this function is used for temporary files like "Lynx Info Page"
+ *  and for one permanent - bookmarks (so it may be a problem if you change
+ *  the display charset later: new bookmark entries may be mistranslated).
+ *								 - LP
+ */
+void LYAddMETAcharsetToFD(FILE *fd, int disp_chndl)
+{
+    if (disp_chndl == -1)
+	/*
+	 * -1 means use current_char_set.
+	 */
+	disp_chndl = current_char_set;
+
+    if (fd == NULL || disp_chndl < 0)
+	/*
+	 * Should not happen.
+	 */
+	return;
+
+    if (UCLYhndl_HTFile_for_unspec == disp_chndl)
+	/*
+	 * Not need to do, so we don't.
+	 */
+	return;
+
+    if (LYCharSet_UC[disp_chndl].enc == UCT_ENC_7BIT)
+	/*
+	 * There shouldn't be any 8-bit characters in this case.
+	 */
+	return;
+
+    /*
+     * In other cases we don't know because UCLYhndl_for_unspec may change
+     * during the lifetime of the file (by toggling raw mode or changing the
+     * display character set), so proceed.
+     */
+    fprintf(fd, "<META %s content=\"text/html;charset=%s\">\n",
+	    "http-equiv=\"content-type\"",
+	    LYCharSet_UC[disp_chndl].MIMEname);
+}
+
+/*
+ * This function returns OL TYPE="A" strings in
+ * the range of " A." (1) to "ZZZ." (18278). - FM
+ */
+char *LYUppercaseA_OL_String(int seqnum)
+{
+    static char OLstring[8];
+
+    if (seqnum <= 1) {
+	strcpy(OLstring, " A.");
+	return OLstring;
+    }
+    if (seqnum < 27) {
+	sprintf(OLstring, " %c.", (seqnum + 64));
+	return OLstring;
+    }
+    if (seqnum < 703) {
+	sprintf(OLstring, "%c%c.", ((seqnum - 1) / 26 + 64),
+		(seqnum - ((seqnum - 1) / 26) * 26 + 64));
+	return OLstring;
+    }
+    if (seqnum < 18279) {
+	sprintf(OLstring, "%c%c%c.", ((seqnum - 27) / 676 + 64),
+		(((seqnum - ((seqnum - 27) / 676) * 676) - 1) / 26 + 64),
+		(seqnum - ((seqnum - 1) / 26) * 26 + 64));
+	return OLstring;
+    }
+    strcpy(OLstring, "ZZZ.");
+    return OLstring;
+}
+
+/*
+ * This function returns OL TYPE="a" strings in
+ * the range of " a." (1) to "zzz." (18278). - FM
+ */
+char *LYLowercaseA_OL_String(int seqnum)
+{
+    static char OLstring[8];
+
+    if (seqnum <= 1) {
+	strcpy(OLstring, " a.");
+	return OLstring;
+    }
+    if (seqnum < 27) {
+	sprintf(OLstring, " %c.", (seqnum + 96));
+	return OLstring;
+    }
+    if (seqnum < 703) {
+	sprintf(OLstring, "%c%c.", ((seqnum - 1) / 26 + 96),
+		(seqnum - ((seqnum - 1) / 26) * 26 + 96));
+	return OLstring;
+    }
+    if (seqnum < 18279) {
+	sprintf(OLstring, "%c%c%c.", ((seqnum - 27) / 676 + 96),
+		(((seqnum - ((seqnum - 27) / 676) * 676) - 1) / 26 + 96),
+		(seqnum - ((seqnum - 1) / 26) * 26 + 96));
+	return OLstring;
+    }
+    strcpy(OLstring, "zzz.");
+    return OLstring;
+}
+
+/*
+ * This function returns OL TYPE="I" strings in the
+ * range of " I." (1) to "MMM." (3000).- FM
+ * Maximum length: 16 -TD
+ */
+char *LYUppercaseI_OL_String(int seqnum)
+{
+    static char OLstring[20];
+    int Arabic = seqnum;
+
+    if (Arabic >= 3000) {
+	strcpy(OLstring, "MMM.");
+	return OLstring;
+    }
+
+    switch (Arabic) {
+    case 1:
+	strcpy(OLstring, " I.");
+	return OLstring;
+    case 5:
+	strcpy(OLstring, " V.");
+	return OLstring;
+    case 10:
+	strcpy(OLstring, " X.");
+	return OLstring;
+    case 50:
+	strcpy(OLstring, " L.");
+	return OLstring;
+    case 100:
+	strcpy(OLstring, " C.");
+	return OLstring;
+    case 500:
+	strcpy(OLstring, " D.");
+	return OLstring;
+    case 1000:
+	strcpy(OLstring, " M.");
+	return OLstring;
+    default:
+	OLstring[0] = '\0';
+	break;
+    }
+
+    while (Arabic >= 1000) {
+	strcat(OLstring, "M");
+	Arabic -= 1000;
+    }
+
+    if (Arabic >= 900) {
+	strcat(OLstring, "CM");
+	Arabic -= 900;
+    }
+
+    if (Arabic >= 500) {
+	strcat(OLstring, "D");
+	Arabic -= 500;
+	while (Arabic >= 500) {
+	    strcat(OLstring, "C");
+	    Arabic -= 10;
+	}
+    }
+
+    if (Arabic >= 400) {
+	strcat(OLstring, "CD");
+	Arabic -= 400;
+    }
+
+    while (Arabic >= 100) {
+	strcat(OLstring, "C");
+	Arabic -= 100;
+    }
+
+    if (Arabic >= 90) {
+	strcat(OLstring, "XC");
+	Arabic -= 90;
+    }
+
+    if (Arabic >= 50) {
+	strcat(OLstring, "L");
+	Arabic -= 50;
+	while (Arabic >= 50) {
+	    strcat(OLstring, "X");
+	    Arabic -= 10;
+	}
+    }
+
+    if (Arabic >= 40) {
+	strcat(OLstring, "XL");
+	Arabic -= 40;
+    }
+
+    while (Arabic > 10) {
+	strcat(OLstring, "X");
+	Arabic -= 10;
+    }
+
+    switch (Arabic) {
+    case 1:
+	strcat(OLstring, "I.");
+	break;
+    case 2:
+	strcat(OLstring, "II.");
+	break;
+    case 3:
+	strcat(OLstring, "III.");
+	break;
+    case 4:
+	strcat(OLstring, "IV.");
+	break;
+    case 5:
+	strcat(OLstring, "V.");
+	break;
+    case 6:
+	strcat(OLstring, "VI.");
+	break;
+    case 7:
+	strcat(OLstring, "VII.");
+	break;
+    case 8:
+	strcat(OLstring, "VIII.");
+	break;
+    case 9:
+	strcat(OLstring, "IX.");
+	break;
+    case 10:
+	strcat(OLstring, "X.");
+	break;
+    default:
+	strcat(OLstring, ".");
+	break;
+    }
+
+    return OLstring;
+}
+
+/*
+ * This function returns OL TYPE="i" strings in
+ * range of " i." (1) to "mmm." (3000).- FM
+ * Maximum length: 16 -TD
+ */
+char *LYLowercaseI_OL_String(int seqnum)
+{
+    static char OLstring[20];
+    int Arabic = seqnum;
+
+    if (Arabic >= 3000) {
+	strcpy(OLstring, "mmm.");
+	return OLstring;
+    }
+
+    switch (Arabic) {
+    case 1:
+	strcpy(OLstring, " i.");
+	return OLstring;
+    case 5:
+	strcpy(OLstring, " v.");
+	return OLstring;
+    case 10:
+	strcpy(OLstring, " x.");
+	return OLstring;
+    case 50:
+	strcpy(OLstring, " l.");
+	return OLstring;
+    case 100:
+	strcpy(OLstring, " c.");
+	return OLstring;
+    case 500:
+	strcpy(OLstring, " d.");
+	return OLstring;
+    case 1000:
+	strcpy(OLstring, " m.");
+	return OLstring;
+    default:
+	OLstring[0] = '\0';
+	break;
+    }
+
+    while (Arabic >= 1000) {
+	strcat(OLstring, "m");
+	Arabic -= 1000;
+    }
+
+    if (Arabic >= 900) {
+	strcat(OLstring, "cm");
+	Arabic -= 900;
+    }
+
+    if (Arabic >= 500) {
+	strcat(OLstring, "d");
+	Arabic -= 500;
+	while (Arabic >= 500) {
+	    strcat(OLstring, "c");
+	    Arabic -= 10;
+	}
+    }
+
+    if (Arabic >= 400) {
+	strcat(OLstring, "cd");
+	Arabic -= 400;
+    }
+
+    while (Arabic >= 100) {
+	strcat(OLstring, "c");
+	Arabic -= 100;
+    }
+
+    if (Arabic >= 90) {
+	strcat(OLstring, "xc");
+	Arabic -= 90;
+    }
+
+    if (Arabic >= 50) {
+	strcat(OLstring, "l");
+	Arabic -= 50;
+	while (Arabic >= 50) {
+	    strcat(OLstring, "x");
+	    Arabic -= 10;
+	}
+    }
+
+    if (Arabic >= 40) {
+	strcat(OLstring, "xl");
+	Arabic -= 40;
+    }
+
+    while (Arabic > 10) {
+	strcat(OLstring, "x");
+	Arabic -= 10;
+    }
+
+    switch (Arabic) {
+    case 1:
+	strcat(OLstring, "i.");
+	break;
+    case 2:
+	strcat(OLstring, "ii.");
+	break;
+    case 3:
+	strcat(OLstring, "iii.");
+	break;
+    case 4:
+	strcat(OLstring, "iv.");
+	break;
+    case 5:
+	strcat(OLstring, "v.");
+	break;
+    case 6:
+	strcat(OLstring, "vi.");
+	break;
+    case 7:
+	strcat(OLstring, "vii.");
+	break;
+    case 8:
+	strcat(OLstring, "viii.");
+	break;
+    case 9:
+	strcat(OLstring, "ix.");
+	break;
+    case 10:
+	strcat(OLstring, "x.");
+	break;
+    default:
+	strcat(OLstring, ".");
+	break;
+    }
+
+    return OLstring;
+}
+
+/*
+ *  This function initializes the Ordered List counter. - FM
+ */
+void LYZero_OL_Counter(HTStructured * me)
+{
+    int i;
+
+    if (!me)
+	return;
+
+    for (i = 0; i < 12; i++) {
+	me->OL_Counter[i] = OL_VOID;
+	me->OL_Type[i] = '1';
+    }
+
+    me->Last_OL_Count = 0;
+    me->Last_OL_Type = '1';
+
+    return;
+}
+
+/*
+ *  This function is used by the HTML Structured object. - KW
+ */
+void LYGetChartransInfo(HTStructured * me)
+{
+    me->UCLYhndl = HTAnchor_getUCLYhndl(me->node_anchor,
+					UCT_STAGE_STRUCTURED);
+    if (me->UCLYhndl < 0) {
+	int chndl = HTAnchor_getUCLYhndl(me->node_anchor, UCT_STAGE_HTEXT);
+
+	if (chndl < 0) {
+	    chndl = current_char_set;
+	    HTAnchor_setUCInfoStage(me->node_anchor, chndl,
+				    UCT_STAGE_HTEXT,
+				    UCT_SETBY_STRUCTURED);
+	}
+	HTAnchor_setUCInfoStage(me->node_anchor, chndl,
+				UCT_STAGE_STRUCTURED,
+				UCT_SETBY_STRUCTURED);
+	me->UCLYhndl = HTAnchor_getUCLYhndl(me->node_anchor,
+					    UCT_STAGE_STRUCTURED);
+    }
+    me->UCI = HTAnchor_getUCInfoStage(me->node_anchor,
+				      UCT_STAGE_STRUCTURED);
+}
+
+	/* as in HTParse.c, saves some calls - kw */
+static const char *hex = "0123456789ABCDEF";
+
+/*
+ *	  Any raw 8-bit or multibyte characters already have been
+ *	  handled in relation to the display character set
+ *	  in SGML_character(), including named and numeric entities.
+ *
+ *  This function used for translations HTML special fields inside tags
+ *  (ALT=, VALUE=, etc.) from charset `cs_from' to charset `cs_to'.
+ *  It also unescapes non-ASCII characters from URL (#fragments !)
+ *  if st_URL is active.
+ *
+ *  If `do_ent' is YES, it converts named entities
+ *  and numeric character references (NCRs) to their `cs_to' replacements.
+ *
+ *  Named entities converted to unicodes.  NCRs (unicodes) converted
+ *  by UCdomap.c chartrans functions.
+ *  ???NCRs with values in the ISO-8859-1 range 160-255 may be converted
+ *  to their HTML entity names (via old-style entities) and then translated
+ *  according to the LYCharSets.c array for `cs_out'???.
+ *
+ *  Some characters (see descriptions in `put_special_unicodes' from SGML.c)
+ *  translated in relation with the state of boolean variables
+ *  `use_lynx_specials', `plain_space' and `hidden'.  It is not clear yet:
+ *
+ *  If plain_space is TRUE, nbsp (160) will be treated as an ASCII
+ *  space (32).  If hidden is TRUE, entities will be translated
+ *  (if `do_ent' is YES) but escape sequences will be passed unaltered.
+ *  If `hidden' is FALSE, some characters are converted to Lynx special
+ *  codes (see `put_special_unicodes') or ASCII space if `plain_space'
+ *  applies).  @@ is `use_lynx_specials' needed, does it have any effect? @@
+ *  If `use_lynx_specials' is YES, translate byte values 160 and 173
+ *  meaning U+00A0 and U+00AD given as or converted from raw char input
+ *  are converted to HT_NON_BREAK_SPACE and LY_SOFT_HYPHEN, respectively
+ *  (unless input and output charset are both iso-8859-1, for compatibility
+ *  with previous usage in HTML.c) even if `hidden' or `plain_space' is set.
+ *
+ *  If `Back' is YES, the reverse is done instead i.e., Lynx special codes
+ *  in the input are translated back to character values.
+ *
+ *  If `Back' is YES, an attempt is made to use UCReverseTransChar() for
+ *  back translation which may be more efficient. (?)
+ *
+ *  If `stype' is st_URL, non-ASCII characters are URL-encoded instead.
+ *  The sequence of bytes being URL-encoded is the raw input character if
+ *  we couldn't translate it from `cs_in' (CJK etc.); otherwise it is the
+ *  UTF-8 representation if either `cs_to' requires this or if the
+ *  character's Unicode value is > 255, otherwise it should be the iso-8859-1
+ *  representation.
+ *  No general URL-encoding occurs for displayable ASCII characters and
+ *  spaces and some C0 controls valid in HTML (LF, TAB), it is expected
+ *  that other functions will take care of that as appropriate.
+ *
+ *  Escape characters (0x1B, '\033') are
+ *  - URL-encoded	if `stype'  is st_URL,	 otherwise
+ *  - dropped		if `stype'  is st_other, otherwise (i.e., st_HTML)
+ *  - passed		if `hidden' is TRUE or HTCJK is set, otherwise
+ *  - dropped.
+ *
+ *  (If `stype' is st_URL or st_other most of the parameters really predefined:
+ *  cs_from=cs_to, use_lynx_specials=plain_space=NO, and hidden=YES)
+ *
+ *
+ *  Returns pointer to the char** passed in
+ *		 if string translated or translation unnecessary,
+ *	    NULL otherwise
+ *		 (in which case something probably went wrong.)
+ *
+ *
+ *  In general, this somehow ugly function (KW)
+ *  cover three functions from v.2.7.2 (FM):
+ *		    extern void LYExpandString (
+ *		       HTStructured *	       me,
+ *		       char **		       str);
+ *		    extern void LYUnEscapeEntities (
+ *		       HTStructured *	       me,
+ *		       char **		       str);
+ *		    extern void LYUnEscapeToLatinOne (
+ *		       HTStructured *	       me,
+ *		       char **		       str,
+ *		       BOOLEAN		       isURL);
+ */
+
+char **LYUCFullyTranslateString(char **str,
+				int cs_from,
+				int cs_to,
+				BOOLEAN do_ent,
+				BOOL use_lynx_specials,
+				BOOLEAN plain_space,
+				BOOLEAN hidden,
+				BOOL Back,
+				CharUtil_st stype)
+{
+    char *p;
+    char *q, *qs;
+    HTChunk *chunk = NULL;
+    char *cp = 0;
+    char cpe = 0;
+    char *esc = NULL;
+    char replace_buf[64];
+    int uck;
+    int lowest_8;
+    UCode_t code = 0;
+    unsigned long lcode;
+    BOOL output_utf8 = 0, repl_translated_C0 = 0;
+    size_t len;
+    const char *name = NULL;
+    BOOLEAN no_bytetrans;
+    UCTransParams T;
+    BOOL from_is_utf8 = FALSE;
+    char *puni;
+    enum _state {
+	S_text,
+	S_esc,
+	S_dollar,
+	S_paren,
+	S_nonascii_text,
+	S_dollar_paren,
+	S_trans_byte,
+	S_check_ent,
+	S_ncr,
+	S_check_uni,
+	S_named,
+	S_check_name,
+	S_recover,
+	S_got_oututf8,
+	S_got_outstring,
+	S_put_urlstring,
+	S_got_outchar,
+	S_put_urlchar,
+	S_next_char,
+	S_done
+    } state = S_text;
+    enum _parsing_what {
+	P_text,
+	P_utf8,
+	P_hex,
+	P_decimal,
+	P_named
+    } what = P_text;
+
+#ifdef KANJI_CODE_OVERRIDE
+    static unsigned char sjis_1st = '\0';
+
+#ifdef CONV_JISX0201KANA_JISX0208KANA
+    unsigned char sjis_str[3];
+#endif
+#endif
+
+    /*
+     * Make sure we have a non-empty string.  - FM
+     */
+    if (isEmpty(*str))
+	return str;
+
+    /*
+     * FIXME: something's wrong with the limit checks here (clearing the
+     * buffer helps).
+     */
+    memset(replace_buf, 0, sizeof(replace_buf));
+
+    /*
+     * Don't do byte translation if original AND target character sets are both
+     * iso-8859-1 (and we are not called to back-translate), or if we are in
+     * CJK mode.
+     */
+    if (IS_CJK_TTY
+#ifdef EXP_JAPANESEUTF8_SUPPORT
+	&& (strcmp(LYCharSet_UC[cs_from].MIMEname, "utf-8") != 0)
+	&& (strcmp(LYCharSet_UC[cs_to].MIMEname, "utf-8") != 0)
+#endif
+	) {
+	no_bytetrans = TRUE;
+    } else if (cs_to <= 0 && cs_from == cs_to && (!Back || cs_to < 0)) {
+	no_bytetrans = TRUE;
+    } else {
+	/* No need to translate or examine the string any further */
+	no_bytetrans = (BOOL) (!use_lynx_specials && !Back &&
+			       UCNeedNotTranslate(cs_from, cs_to));
+    }
+    /*
+     * Save malloc/calloc overhead in simple case - kw
+     */
+    if (do_ent && hidden && (stype != st_URL) && (strchr(*str, '&') == NULL))
+	do_ent = FALSE;
+
+    /* Can't do, caller should figure out what to do... */
+    if (!UCCanTranslateFromTo(cs_from, cs_to)) {
+	if (cs_to < 0)
+	    return NULL;
+	if (!do_ent && no_bytetrans)
+	    return NULL;
+	no_bytetrans = TRUE;
+    } else if (cs_to < 0) {
+	do_ent = FALSE;
+    }
+
+    if (!do_ent && no_bytetrans)
+	return str;
+    p = *str;
+
+    if (!no_bytetrans) {
+	UCTransParams_clear(&T);
+	UCSetTransParams(&T, cs_from, &LYCharSet_UC[cs_from],
+			 cs_to, &LYCharSet_UC[cs_to]);
+	from_is_utf8 = (BOOL) (LYCharSet_UC[cs_from].enc == UCT_ENC_UTF8);
+	output_utf8 = T.output_utf8;
+	repl_translated_C0 = T.repl_translated_C0;
+	puni = p;
+    } else if (do_ent) {
+	output_utf8 = (BOOL) (LYCharSet_UC[cs_to].enc == UCT_ENC_UTF8 ||
+			      HText_hasUTF8OutputSet(HTMainText));
+	repl_translated_C0 = (BOOL) (LYCharSet_UC[cs_to].enc == UCT_ENC_8BIT_C0);
+    }
+
+    lowest_8 = LYlowest_eightbit[cs_to];
+
+    /*
+     * Create a buffer string seven times the length of the original, so we
+     * have plenty of room for expansions.  - FM
+     */
+    len = strlen(p) + 16;
+    q = p;
+
+    qs = q;
+
+/*  Create the HTChunk only if we need it */
+#define CHUNK (chunk ? chunk : (chunk = HTChunkCreate2(128, len+1)))
+
+#define REPLACE_STRING(s) \
+		if (q != qs) HTChunkPutb(CHUNK, qs, q-qs); \
+		HTChunkPuts(CHUNK, s); \
+		qs = q = *str
+
+#define REPLACE_CHAR(c) if (q > p) { \
+		HTChunkPutb(CHUNK, qs, q-qs); \
+		qs = q = *str; \
+		*q++ = c; \
+	    } else \
+		*q++ = c
+
+    /*
+     * Loop through string, making conversions as needed.
+     *
+     * The while() checks for a non-'\0' char only for the normal text states
+     * since other states may temporarily modify p or *p (which should be
+     * restored before S_done!) - kw
+     */
+    while (*p || (state != S_text && state != S_nonascii_text)) {
+	switch (state) {
+	case S_text:
+	    code = UCH(*p);
+#ifdef KANJI_CODE_OVERRIDE
+	    if (HTCJK == JAPANESE && last_kcode == SJIS) {
+		if (sjis_1st == '\0' && (IS_SJIS_HI1(code) || IS_SJIS_HI2(code))) {
+		    sjis_1st = UCH(code);
+		} else if (sjis_1st && IS_SJIS_LO(code)) {
+		    sjis_1st = '\0';
+		} else {
+#ifdef CONV_JISX0201KANA_JISX0208KANA
+		    if (0xA1 <= code && code <= 0xDF) {
+			sjis_str[2] = '\0';
+			JISx0201TO0208_SJIS(UCH(code),
+					    sjis_str, sjis_str + 1);
+			REPLACE_STRING(sjis_str);
+			p++;
+			continue;
+		    }
+#endif
+		}
+	    }
+#endif
+	    if (*p == '\033') {
+		if ((IS_CJK_TTY && !hidden) || stype != st_HTML) {
+		    state = S_esc;
+		    if (stype == st_URL) {
+			REPLACE_STRING("%1B");
+			p++;
+			continue;
+		    } else if (stype != st_HTML) {
+			p++;
+			continue;
+		    } else {
+			*q++ = *p++;
+			continue;
+		    }
+		} else if (!hidden) {
+		    /*
+		     * CJK handling not on, and not a hidden INPUT, so block
+		     * escape.  - FM
+		     */
+		    state = S_next_char;
+		} else {
+		    state = S_trans_byte;
+		}
+	    } else {
+		state = (do_ent ? S_check_ent : S_trans_byte);
+	    }
+	    break;
+
+	case S_esc:
+	    if (*p == '$') {
+		state = S_dollar;
+		*q++ = *p++;
+		continue;
+	    } else if (*p == '(') {
+		state = S_paren;
+		*q++ = *p++;
+		continue;
+	    } else {
+		state = S_text;
+	    }
+	    break;
+
+	case S_dollar:
+	    if (*p == '@' || *p == 'B' || *p == 'A') {
+		state = S_nonascii_text;
+		*q++ = *p++;
+		continue;
+	    } else if (*p == '(') {
+		state = S_dollar_paren;
+		*q++ = *p++;
+		continue;
+	    } else {
+		state = S_text;
+	    }
+	    break;
+
+	case S_dollar_paren:
+	    if (*p == 'C') {
+		state = S_nonascii_text;
+		*q++ = *p++;
+		continue;
+	    } else {
+		state = S_text;
+	    }
+	    break;
+
+	case S_paren:
+	    if (*p == 'B' || *p == 'J' || *p == 'T') {
+		state = S_text;
+		*q++ = *p++;
+		continue;
+	    } else if (*p == 'I') {
+		state = S_nonascii_text;
+		*q++ = *p++;
+		continue;
+	    } else {
+		state = S_text;
+	    }
+	    break;
+
+	case S_nonascii_text:
+	    if (*p == '\033') {
+		if ((IS_CJK_TTY && !hidden) || stype != st_HTML) {
+		    state = S_esc;
+		    if (stype == st_URL) {
+			REPLACE_STRING("%1B");
+			p++;
+			continue;
+		    } else if (stype != st_HTML) {
+			p++;
+			continue;
+		    }
+		}
+	    }
+	    *q++ = *p++;
+	    continue;
+
+	case S_trans_byte:
+	    /* character translation goes here */
+	    /*
+	     * Don't do anything if we have no string, or if original AND
+	     * target character sets are both iso-8859-1, or if we are in CJK
+	     * mode.
+	     */
+	    if (*p == '\0' || no_bytetrans) {
+		state = S_got_outchar;
+		break;
+	    }
+
+	    if (Back) {
+		int rev_c;
+
+		if ((*p) == HT_NON_BREAK_SPACE ||
+		    (*p) == HT_EN_SPACE) {
+		    if (plain_space) {
+			code = *p = ' ';
+			state = S_got_outchar;
+			break;
+		    } else {
+			code = 160;
+			if (LYCharSet_UC[cs_to].enc == UCT_ENC_8859 ||
+			    (LYCharSet_UC[cs_to].like8859 & UCT_R_8859SPECL)) {
+			    state = S_got_outchar;
+			    break;
+			} else if (!(LYCharSet_UC[cs_from].enc == UCT_ENC_8859
+				     || (LYCharSet_UC[cs_from].like8859 & UCT_R_8859SPECL))) {
+			    state = S_check_uni;
+			    break;
+			} else {
+			    *(unsigned char *) p = UCH(160);
+			}
+		    }
+		} else if ((*p) == LY_SOFT_HYPHEN) {
+		    code = 173;
+		    if (LYCharSet_UC[cs_to].enc == UCT_ENC_8859 ||
+			(LYCharSet_UC[cs_to].like8859 & UCT_R_8859SPECL)) {
+			state = S_got_outchar;
+			break;
+		    } else if (!(LYCharSet_UC[cs_from].enc == UCT_ENC_8859
+				 || (LYCharSet_UC[cs_from].like8859 & UCT_R_8859SPECL))) {
+			state = S_check_uni;
+			break;
+		    } else {
+			*(unsigned char *) p = UCH(173);
+		    }
+#ifdef EXP_JAPANESEUTF8_SUPPORT
+		} else if (output_utf8) {
+		    if ((!strcmp(LYCharSet_UC[cs_from].MIMEname, "euc-jp") &&
+			 (IS_EUC((unsigned char) (*p),
+				 (unsigned char) (*(p + 1))))) ||
+			(!strcmp(LYCharSet_UC[cs_from].MIMEname, "shift_jis") &&
+			 (IS_SJIS_2BYTE((unsigned char) (*p),
+					(unsigned char) (*(p + 1)))))) {
+			code = UCTransJPToUni(p, 2, cs_from);
+			p++;
+			state = S_check_uni;
+			break;
+		    }
+#endif
+		} else if (code < 127 || T.transp) {
+		    state = S_got_outchar;
+		    break;
+		}
+		rev_c = UCReverseTransChar(*p, cs_to, cs_from);
+		if (rev_c > 127) {
+		    *p = (char) rev_c;
+		    code = rev_c;
+		    state = S_got_outchar;
+		    break;
+		}
+	    } else if (code < 127) {
+		state = S_got_outchar;
+		break;
+	    }
+
+	    if (from_is_utf8) {
+		if (((*p) & 0xc0) == 0xc0) {
+		    puni = p;
+		    code = UCGetUniFromUtf8String(&puni);
+		    if (code <= 0) {
+			code = UCH(*p);
+		    } else {
+			what = P_utf8;
+		    }
+		}
+	    } else if (use_lynx_specials && !Back &&
+		       (code == 160 || code == 173) &&
+		       (LYCharSet_UC[cs_from].enc == UCT_ENC_8859 ||
+			(LYCharSet_UC[cs_from].like8859 & UCT_R_8859SPECL))) {
+		if (code == 160)
+		    code = *p = HT_NON_BREAK_SPACE;
+		else if (code == 173)
+		    code = *p = LY_SOFT_HYPHEN;
+		state = S_got_outchar;
+		break;
+	    } else if (T.trans_to_uni) {
+		code = UCTransToUni(*p, cs_from);
+		if (code <= 0) {
+		    /* What else can we do? */
+		    code = UCH(*p);
+		}
+	    } else if (!T.trans_from_uni) {
+		state = S_got_outchar;
+		break;
+	    }
+	    /*
+	     * Substitute Lynx special character for 160 (nbsp) if
+	     * use_lynx_specials is set.
+	     */
+	    if (use_lynx_specials && !Back &&
+		(code == 160 || code == 173)) {
+		code = ((code == 160 ? HT_NON_BREAK_SPACE : LY_SOFT_HYPHEN));
+		state = S_got_outchar;
+		break;
+	    }
+
+	    state = S_check_uni;
+	    break;
+
+	case S_check_ent:
+	    if (*p == '&') {
+		char *pp = p + 1;
+
+		len = strlen(pp);
+		/*
+		 * Check for a numeric entity.  - FM
+		 */
+		if (*pp == '#' && len > 2 &&
+		    (*(pp + 1) == 'x' || *(pp + 1) == 'X') &&
+		    UCH(*(pp + 2)) < 127 &&
+		    isxdigit(UCH(*(pp + 2)))) {
+		    what = P_hex;
+		    state = S_ncr;
+		} else if (*pp == '#' && len > 2 &&
+			   UCH(*(pp + 1)) < 127 &&
+			   isdigit(UCH(*(pp + 1)))) {
+		    what = P_decimal;
+		    state = S_ncr;
+		} else if (UCH(*pp) < 127 &&
+			   isalpha(UCH(*pp))) {
+		    what = P_named;
+		    state = S_named;
+		} else {
+		    state = S_trans_byte;
+		}
+	    } else {
+		state = S_trans_byte;
+	    }
+	    break;
+
+	case S_ncr:
+	    if (what == P_hex) {
+		p += 3;
+	    } else {		/* P_decimal */
+		p += 2;
+	    }
+	    cp = p;
+	    while (*p && UCH(*p) < 127 &&
+		   (what == P_hex ? isxdigit(UCH(*p)) :
+		    isdigit(UCH(*p)))) {
+		p++;
+	    }
+	    /*
+	     * Save the terminator and isolate the digit(s).  - FM
+	     */
+	    cpe = *p;
+	    if (*p)
+		*p++ = '\0';
+	    /*
+	     * Show the numeric entity if the value:
+	     * (1) Is greater than 255 and unhandled Unicode.
+	     * (2) Is less than 32, and not valid and we don't have HTCJK set.
+	     * (3) Is 127 and we don't have HTPassHighCtrlRaw or HTCJK set.
+	     * (4) Is 128 - 159 and we don't have HTPassHighCtrlNum set.
+	     */
+	    if ((((what == P_hex)
+		  ? sscanf(cp, "%lx", &lcode)
+		  : sscanf(cp, "%lu", &lcode)) != 1) ||
+		lcode > 0x7fffffffL) {
+		state = S_recover;
+		break;
+	    } else {
+		code = LYcp1252ToUnicode(lcode);
+		state = S_check_uni;
+	    }
+	    break;
+
+	case S_check_uni:
+	    /*
+	     * Show the numeric entity if the value:
+	     * (2) Is less than 32, and not valid and we don't have HTCJK set.
+	     * (3) Is 127 and we don't have HTPassHighCtrlRaw or HTCJK set.
+	     * (4) Is 128 - 159 and we don't have HTPassHighCtrlNum set.
+	     */
+	    if ((code < 32 &&
+		 code != 9 && code != 10 && code != 13 &&
+		 !IS_CJK_TTY) ||
+		(code == 127 &&
+		 !(HTPassHighCtrlRaw || IS_CJK_TTY)) ||
+		(code > 127 && code < 160 &&
+		 !HTPassHighCtrlNum)) {
+		state = S_recover;
+		break;
+	    }
+	    /*
+	     * Convert the value as an unsigned char, hex escaped if isURL is
+	     * set and it's 8-bit, and then recycle the terminator if it is not
+	     * a semicolon.  - FM
+	     */
+	    if (code > 159 && stype == st_URL) {
+		state = S_got_oututf8;
+		break;
+	    }
+	    /*
+	     * For 160 (nbsp), use that value if it's a hidden INPUT, otherwise
+	     * use an ASCII space (32) if plain_space is TRUE, otherwise use
+	     * the Lynx special character.  - FM
+	     */
+	    if (code == 160) {
+		if (plain_space) {
+		    code = ' ';
+		    state = S_got_outchar;
+		    break;
+		} else if (use_lynx_specials) {
+		    code = HT_NON_BREAK_SPACE;
+		    state = S_got_outchar;
+		    break;
+		} else if ((hidden && !Back)
+			   || (LYCharSet_UC[cs_to].codepoints & UCT_CP_SUPERSETOF_LAT1)
+			   || LYCharSet_UC[cs_to].enc == UCT_ENC_8859
+			   || (LYCharSet_UC[cs_to].like8859 &
+			       UCT_R_8859SPECL)) {
+		    state = S_got_outchar;
+		    break;
+		} else if (
+			      (LYCharSet_UC[cs_to].repertoire & UCT_REP_SUPERSETOF_LAT1)) {
+		    ;		/* nothing, may be translated later */
+		} else {
+		    code = ' ';
+		    state = S_got_outchar;
+		    break;
+		}
+	    }
+	    /*
+	     * For 173 (shy), use that value if it's a hidden INPUT, otherwise
+	     * ignore it if plain_space is TRUE, otherwise use the Lynx special
+	     * character.  - FM
+	     */
+	    if (code == 173) {
+		if (plain_space) {
+		    replace_buf[0] = '\0';
+		    state = S_got_outstring;
+		    break;
+		} else if (Back &&
+			   !(LYCharSet_UC[cs_to].enc == UCT_ENC_8859 ||
+			     (LYCharSet_UC[cs_to].like8859 &
+			      UCT_R_8859SPECL))) {
+		    ;		/* nothing, may be translated later */
+		} else if (hidden || Back) {
+		    state = S_got_outchar;
+		    break;
+		} else if (use_lynx_specials) {
+		    code = LY_SOFT_HYPHEN;
+		    state = S_got_outchar;
+		    break;
+		}
+	    }
+	    /*
+	     * Seek a translation from the chartrans tables.
+	     */
+	    if ((uck = UCTransUniChar(code,
+				      cs_to)) >= 32 &&
+		uck < 256 &&
+		(uck < 127 || uck >= lowest_8)) {
+		code = uck;
+		state = S_got_outchar;
+		break;
+	    } else if ((uck == -4 ||
+			(repl_translated_C0 &&
+			 uck > 0 && uck < 32)) &&
+		/*
+		 * Not found; look for replacement string.
+		 */
+		       UCTransUniCharStr(replace_buf,
+					 60, code,
+					 cs_to,
+					 0) >= 0) {
+		state = S_got_outstring;
+		break;
+	    }
+	    if (output_utf8 &&
+		code > 127 && code < 0x7fffffffL) {
+		state = S_got_oututf8;
+		break;
+	    }
+	    /*
+	     * For 8194 (ensp), 8195 (emsp), or 8201 (thinsp), use the
+	     * character reference if it's a hidden INPUT, otherwise use an
+	     * ASCII space (32) if plain_space is TRUE, otherwise use the Lynx
+	     * special character.  - FM
+	     */
+	    if (code == 8194 || code == 8195 || code == 8201) {
+		if (hidden) {
+		    state = S_recover;
+		} else if (plain_space) {
+		    code = ' ';
+		    state = S_got_outchar;
+		} else {
+		    code = HT_EN_SPACE;
+		    state = S_got_outchar;
+		}
+		break;
+		/*
+		 * Ignore 8204 (zwnj), 8205 (zwj) 8206 (lrm), and 8207 (rlm),
+		 * for now, if we got this far without finding a representation
+		 * for them.
+		 */
+	    } else if (code == 8204 || code == 8205 ||
+		       code == 8206 || code == 8207) {
+		CTRACE((tfp, "LYUCFullyTranslateString: Ignoring '%ld'.\n", code));
+		replace_buf[0] = '\0';
+		state = S_got_outstring;
+		break;
+		/*
+		 * Show the numeric entity if the value:  (1) Is greater than
+		 * 255 and unhandled Unicode.
+		 */
+	    } else if (code > 255) {
+		/*
+		 * Illegal or not yet handled value.  Return "&#" verbatim and
+		 * continue from there.  - FM
+		 */
+		state = S_recover;
+		break;
+		/*
+		 * If it's ASCII, or is 8-bit but HTPassEightBitNum is set or
+		 * the character set is "ISO Latin 1", use it's value.  - FM
+		 */
+	    } else if (code < 161 ||
+		       (code < 256 &&
+			(HTPassEightBitNum || cs_to == LATIN1))) {
+		/*
+		 * No conversion needed.
+		 */
+		state = S_got_outchar;
+		break;
+
+		/* The following disabled section doesn't make sense any more. 
+		 * It used to make sense in the past, when S_check_named would
+		 * look in "old style" tables in addition to what it does now. 
+		 * Disabling of going to S_check_name here prevents endless
+		 * looping between S_check_uni and S_check_names states, which
+		 * could occur here for Latin 1 codes for some cs_to if they
+		 * had no translation in that cs_to.  Normally all cs_to
+		 * *should* now have valid translations via UCTransUniChar or
+		 * UCTransUniCharStr for all Latin 1 codes, so that we would
+		 * not get here anyway, and no loop could occur.  Still, if we
+		 * *do* get here, FALL THROUGH to case S_recover now.  - kw
+		 */
+#if 0
+		/*
+		 * If we get to here, convert and handle the character as a
+		 * named entity.  - FM
+		 */
+	    } else {
+		name = HTMLGetEntityName(code - 160);
+		state = S_check_name;
+		break;
+#endif
+	    }
+
+	case S_recover:
+	    if (what == P_decimal || what == P_hex) {
+		/*
+		 * Illegal or not yet handled value.  Return "&#" verbatim and
+		 * continue from there.  - FM
+		 */
+		*q++ = '&';
+		*q++ = '#';
+		if (what == P_hex)
+		    *q++ = 'x';
+		if (cpe != '\0')
+		    *(p - 1) = cpe;
+		p = cp;
+		state = S_done;
+	    } else if (what == P_named) {
+		*cp = cpe;
+		*q++ = '&';
+		state = S_done;
+	    } else if (!T.output_utf8 && stype == st_HTML && !hidden &&
+		       !(HTPassEightBitRaw &&
+			 UCH(*p) >= lowest_8)) {
+		sprintf(replace_buf, "U%.2lX", code);
+		state = S_got_outstring;
+	    } else {
+		puni = p;
+		code = UCH(*p);
+		state = S_got_outchar;
+	    }
+	    break;
+
+	case S_named:
+	    cp = ++p;
+	    while (*cp && UCH(*cp) < 127 &&
+		   isalnum(UCH(*cp)))
+		cp++;
+	    cpe = *cp;
+	    *cp = '\0';
+	    name = p;
+	    state = S_check_name;
+	    break;
+
+	case S_check_name:
+	    /*
+	     * Seek the Unicode value for the named entity.
+	     *
+	     * !!!!  We manually recover the case of '=' terminator which is
+	     * commonly found on query to CGI-scripts enclosed as href= URLs
+	     * like "somepath/?x=1&yz=2" Without this dirty fix, submission of
+	     * such URLs was broken if &yz string happened to be a recognized
+	     * entity name.  - LP
+	     */
+	    if (((code = HTMLGetEntityUCValue(name)) > 0) &&
+		!((cpe == '=') && (stype == st_URL))) {
+		state = S_check_uni;
+		break;
+	    }
+	    /*
+	     * Didn't find the entity.  Return verbatim.
+	     */
+	    state = S_recover;
+	    break;
+
+	    /* * * O U T P U T   S T A T E S * * */
+
+	case S_got_oututf8:
+	    if (code > 255 ||
+		(code >= 128 && LYCharSet_UC[cs_to].enc == UCT_ENC_UTF8)) {
+		UCConvertUniToUtf8(code, replace_buf);
+		state = S_got_outstring;
+	    } else {
+		state = S_got_outchar;
+	    }
+	    break;
+	case S_got_outstring:
+	    if (what == P_decimal || what == P_hex) {
+		if (cpe != ';' && cpe != '\0')
+		    *(--p) = cpe;
+		p--;
+	    } else if (what == P_named) {
+		*cp = cpe;
+		p = (*cp != ';') ? (cp - 1) : cp;
+	    } else if (what == P_utf8) {
+		p = puni;
+	    }
+	    if (replace_buf[0] == '\0') {
+		state = S_next_char;
+		break;
+	    }
+	    if (stype == st_URL) {
+		code = replace_buf[0];	/* assume string OK if first char is */
+		if (code >= 127 ||
+		    (code < 32 && (code != 9 && code != 10 && code != 0))) {
+		    state = S_put_urlstring;
+		    break;
+		}
+	    }
+	    REPLACE_STRING(replace_buf);
+	    state = S_next_char;
+	    break;
+	case S_put_urlstring:
+	    esc = HTEscape(replace_buf, URL_XALPHAS);
+	    REPLACE_STRING(esc);
+	    FREE(esc);
+	    state = S_next_char;
+	    break;
+	case S_got_outchar:
+	    if (what == P_decimal || what == P_hex) {
+		if (cpe != ';' && cpe != '\0')
+		    *(--p) = cpe;
+		p--;
+	    } else if (what == P_named) {
+		*cp = cpe;
+		p = (*cp != ';') ? (cp - 1) : cp;
+	    } else if (what == P_utf8) {
+		p = puni;
+	    }
+	    if (stype == st_URL &&
+	    /*  Not a full HTEscape, only for 8bit and ctrl chars */
+		(TOASCII(code) >= 127 ||	/* S/390 -- gil -- 1925 */
+		 (code < ' ' && (code != '\t' && code != '\n')))) {
+		state = S_put_urlchar;
+		break;
+	    } else if (!hidden && code == 10 && *p == 10
+		       && q != qs && *(q - 1) == 13) {
+		/*
+		 * If this is not a hidden string, and the current char is the
+		 * LF ('\n') of a CRLF pair, drop the CR ('\r').  - KW
+		 */
+		*(q - 1) = *p++;
+		state = S_done;
+		break;
+	    }
+	    *q++ = (char) code;
+	    state = S_next_char;
+	    break;
+	case S_put_urlchar:
+	    *q++ = '%';
+	    REPLACE_CHAR(hex[(TOASCII(code) >> 4) & 15]);	/* S/390 -- gil -- 1944 */
+	    REPLACE_CHAR(hex[(TOASCII(code) & 15)]);
+	    /* fall through */
+	case S_next_char:
+	    p++;		/* fall through */
+	case S_done:
+	    state = S_text;
+	    what = P_text;
+	    /* for next round */
+	}
+    }
+
+    *q = '\0';
+    if (chunk) {
+	HTChunkPutb(CHUNK, qs, q - qs + 1);	/* also terminates */
+	if (stype == st_URL || stype == st_other) {
+	    LYTrimHead(chunk->data);
+	    LYTrimTail(chunk->data);
+	}
+	StrAllocCopy(*str, chunk->data);
+	HTChunkFree(chunk);
+    } else {
+	if (stype == st_URL || stype == st_other) {
+	    LYTrimHead(qs);
+	    LYTrimTail(qs);
+	}
+    }
+    return str;
+}
+
+#undef REPLACE_CHAR
+#undef REPLACE_STRING
+
+BOOL LYUCTranslateHTMLString(char **str,
+			     int cs_from,
+			     int cs_to,
+			     BOOL use_lynx_specials,
+			     BOOLEAN plain_space,
+			     BOOLEAN hidden,
+			     CharUtil_st stype)
+{
+    BOOL ret = YES;
+
+    /* May reallocate *str even if cs_to == 0 */
+    if (!LYUCFullyTranslateString(str, cs_from, cs_to, TRUE,
+				  use_lynx_specials, plain_space, hidden,
+				  NO, stype)) {
+	ret = NO;
+    }
+    return ret;
+}
+
+BOOL LYUCTranslateBackFormData(char **str,
+			       int cs_from,
+			       int cs_to,
+			       BOOLEAN plain_space)
+{
+    char **ret;
+
+    /* May reallocate *str */
+    ret = (LYUCFullyTranslateString(str, cs_from, cs_to, FALSE,
+				    NO, plain_space, YES,
+				    YES, st_HTML));
+    return (BOOL) (ret != NULL);
+}
+
+/*
+ * Parse a parameter from an HTML META tag, i.e., the CONTENT.
+ */
+char *LYParseTagParam(char *from,
+		      const char *name)
+{
+    size_t len = strlen(name);
+    char *result = NULL;
+    char *string = from;
+
+    do {
+	if ((string = strchr(string, ';')) == NULL)
+	    return NULL;
+	while (*string != '\0' && (*string == ';' || isspace(UCH(*string)))) {
+	    string++;
+	}
+	if (strlen(string) < len)
+	    return NULL;
+    } while (strncasecomp(string, name, (int) len) != 0);
+    string += len;
+    while (*string != '\0' && (isspace(UCH(*string)) || *string == '=')) {
+	string++;
+    }
+
+    StrAllocCopy(result, string);
+    len = 0;
+    while (isprint(UCH(string[len])) && !isspace(UCH(string[len]))) {
+	len++;
+    }
+    result[len] = '\0';
+
+    /*
+     * Strip single quotes, just in case.
+     */
+    if (len > 2 && result[0] == '\'' && result[len - 1] == result[0]) {
+	result[len - 1] = '\0';
+	for (string = result; (string[0] = string[1]) != '\0'; ++string) ;
+    }
+    return result;
+}
+
+/*
+ * Given a refresh-URL content string, parses the delay time and the URL
+ * string.  Ignore the remainder of the content.
+ */
+void LYParseRefreshURL(char *content,
+		       char **p_seconds,
+		       char **p_address)
+{
+    char *cp;
+    char *cp1 = NULL;
+    char *Seconds = NULL;
+
+    /*
+     * Look for the Seconds field.  - FM
+     */
+    cp = LYSkipBlanks(content);
+    if (*cp && isdigit(UCH(*cp))) {
+	cp1 = cp;
+	while (*cp1 && isdigit(UCH(*cp1)))
+	    cp1++;
+	StrnAllocCopy(Seconds, cp, cp1 - cp);
+    }
+    *p_seconds = Seconds;
+    *p_address = LYParseTagParam(content, "URL");
+
+    CTRACE((tfp,
+	    "LYParseRefreshURL\n\tcontent: %s\n\tseconds: %s\n\taddress: %s\n",
+	    content, NonNull(*p_seconds), NonNull(*p_address)));
+}
+
+/*
+ *  This function processes META tags in HTML streams. - FM
+ */
+void LYHandleMETA(HTStructured * me, const BOOL *present,
+		  const char **value,
+		  char **include GCC_UNUSED)
+{
+    char *http_equiv = NULL, *name = NULL, *content = NULL;
+    char *href = NULL, *id_string = NULL, *temp = NULL;
+    char *cp, *cp0, *cp1 = NULL;
+    int url_type = 0;
+
+    if (!me || !present)
+	return;
+
+    /*
+     * Load the attributes for possible use by Lynx.  - FM
+     */
+    if (present[HTML_META_HTTP_EQUIV] &&
+	non_empty(value[HTML_META_HTTP_EQUIV])) {
+	StrAllocCopy(http_equiv, value[HTML_META_HTTP_EQUIV]);
+	convert_to_spaces(http_equiv, TRUE);
+	LYUCTranslateHTMLString(&http_equiv, me->tag_charset, me->tag_charset,
+				NO, NO, YES, st_other);
+	if (*http_equiv == '\0') {
+	    FREE(http_equiv);
+	}
+    }
+    if (present[HTML_META_NAME] &&
+	non_empty(value[HTML_META_NAME])) {
+	StrAllocCopy(name, value[HTML_META_NAME]);
+	convert_to_spaces(name, TRUE);
+	LYUCTranslateHTMLString(&name, me->tag_charset, me->tag_charset,
+				NO, NO, YES, st_other);
+	if (*name == '\0') {
+	    FREE(name);
+	}
+    }
+    if (present[HTML_META_CONTENT] &&
+	non_empty(value[HTML_META_CONTENT])) {
+	/*
+	 * Technically, we should be creating a comma-separated list, but META
+	 * tags come one at a time, and we'll handle (or ignore) them as each
+	 * is received.  Also, at this point, we only trim leading and trailing
+	 * blanks from the CONTENT value, without translating any named
+	 * entities or numeric character references, because how we should do
+	 * that depends on what type of information it contains, and whether or
+	 * not any of it might be sent to the screen.  - FM
+	 */
+	StrAllocCopy(content, value[HTML_META_CONTENT]);
+	convert_to_spaces(content, FALSE);
+	LYTrimHead(content);
+	LYTrimTail(content);
+	if (*content == '\0') {
+	    FREE(content);
+	}
+    }
+    CTRACE((tfp,
+	    "LYHandleMETA: HTTP-EQUIV=\"%s\" NAME=\"%s\" CONTENT=\"%s\"\n",
+	    NONNULL(http_equiv),
+	    NONNULL(name),
+	    NONNULL(content)));
+
+    /*
+     * Make sure we have META name/value pairs to handle.  - FM
+     */
+    if (!(http_equiv || name) || !content)
+	goto free_META_copies;
+
+    /*
+     * Check for a no-cache Pragma
+     * or Cache-Control directive. - FM
+     */
+    if (!strcasecomp(NonNull(http_equiv), "Pragma") ||
+	!strcasecomp(NonNull(http_equiv), "Cache-Control")) {
+	LYUCTranslateHTMLString(&content, me->tag_charset, me->tag_charset,
+				NO, NO, YES, st_other);
+	if (!strcasecomp(content, "no-cache")) {
+	    me->node_anchor->no_cache = TRUE;
+	    HText_setNoCache(me->text);
+	}
+
+	/*
+	 * If we didn't get a Cache-Control MIME header, and the META has one,
+	 * convert to lowercase, store it in the anchor element, and if we
+	 * haven't yet set no_cache, check whether we should.  - FM
+	 */
+	if ((!me->node_anchor->cache_control) &&
+	    !strcasecomp(NonNull(http_equiv), "Cache-Control")) {
+	    LYLowerCase(content);
+	    StrAllocCopy(me->node_anchor->cache_control, content);
+	    if (me->node_anchor->no_cache == FALSE) {
+		cp0 = content;
+		while ((cp = strstr(cp0, "no-cache")) != NULL) {
+		    cp += 8;
+		    while (*cp != '\0' && WHITE(*cp))
+			cp++;
+		    if (*cp == '\0' || *cp == ';') {
+			me->node_anchor->no_cache = TRUE;
+			HText_setNoCache(me->text);
+			break;
+		    }
+		    cp0 = cp;
+		}
+		if (me->node_anchor->no_cache == TRUE)
+		    goto free_META_copies;
+		cp0 = content;
+		while ((cp = strstr(cp0, "max-age")) != NULL) {
+		    cp += 7;
+		    while (*cp != '\0' && WHITE(*cp))
+			cp++;
+		    if (*cp == '=') {
+			cp++;
+			while (*cp != '\0' && WHITE(*cp))
+			    cp++;
+			if (isdigit(UCH(*cp))) {
+			    cp0 = cp;
+			    while (isdigit(UCH(*cp)))
+				cp++;
+			    if (*cp0 == '0' && cp == (cp0 + 1)) {
+				me->node_anchor->no_cache = TRUE;
+				HText_setNoCache(me->text);
+				break;
+			    }
+			}
+		    }
+		    cp0 = cp;
+		}
+	    }
+	}
+
+	/*
+	 * Check for an Expires directive. - FM
+	 */
+    } else if (!strcasecomp(NonNull(http_equiv), "Expires")) {
+	/*
+	 * If we didn't get an Expires MIME header, store it in the anchor
+	 * element, and if we haven't yet set no_cache, check whether we
+	 * should.  Note that we don't accept a Date header via META tags,
+	 * because it's likely to be untrustworthy, but do check for a Date
+	 * header from a server when making the comparison.  - FM
+	 */
+	LYUCTranslateHTMLString(&content, me->tag_charset, me->tag_charset,
+				NO, NO, YES, st_other);
+	StrAllocCopy(me->node_anchor->expires, content);
+	if (me->node_anchor->no_cache == FALSE) {
+	    if (!strcmp(content, "0")) {
+		/*
+		 * The value is zero, which we treat as an absolute no-cache
+		 * directive.  - FM
+		 */
+		me->node_anchor->no_cache = TRUE;
+		HText_setNoCache(me->text);
+	    } else if (me->node_anchor->date != NULL) {
+		/*
+		 * We have a Date header, so check if the value is less than or
+		 * equal to that.  - FM
+		 */
+		if (LYmktime(content, TRUE) <=
+		    LYmktime(me->node_anchor->date, TRUE)) {
+		    me->node_anchor->no_cache = TRUE;
+		    HText_setNoCache(me->text);
+		}
+	    } else if (LYmktime(content, FALSE) == 0) {
+		/*
+		 * We don't have a Date header, and the value is in past for
+		 * us.  - FM
+		 */
+		me->node_anchor->no_cache = TRUE;
+		HText_setNoCache(me->text);
+	    }
+	}
+
+	/*
+	 * Check for a text/html Content-Type with a charset directive, if we
+	 * didn't already set the charset via a server's header.  - AAC & FM
+	 */
+    } else if (isEmpty(me->node_anchor->charset) &&
+	       !strcasecomp(NonNull(http_equiv), "Content-Type")) {
+	LYUCcharset *p_in = NULL;
+	LYUCcharset *p_out = NULL;
+
+	LYUCTranslateHTMLString(&content, me->tag_charset, me->tag_charset,
+				NO, NO, YES, st_other);
+	LYLowerCase(content);
+
+	if ((cp1 = strstr(content, "charset")) != NULL) {
+	    BOOL chartrans_ok = NO;
+	    char *cp3 = NULL, *cp4;
+	    int chndl;
+
+	    cp1 += 7;
+	    while (*cp1 == ' ' || *cp1 == '=' || *cp1 == '"')
+		cp1++;
+
+	    StrAllocCopy(cp3, cp1);	/* copy to mutilate more */
+	    for (cp4 = cp3; (*cp4 != '\0' && *cp4 != '"' &&
+			     *cp4 != ';' && *cp4 != ':' &&
+			     !WHITE(*cp4)); cp4++) {
+		;		/* do nothing */
+	    }
+	    *cp4 = '\0';
+	    cp4 = cp3;
+	    chndl = UCGetLYhndl_byMIME(cp3);
+
+#ifdef CAN_SWITCH_DISPLAY_CHARSET
+	    /* Allow a switch to a more suitable display charset */
+	    if (Switch_Display_Charset(chndl, SWITCH_DISPLAY_CHARSET_MAYBE)) {
+		/* UCT_STAGE_STRUCTURED and UCT_STAGE_HTEXT
+		   should have the same setting for UCInfoStage. */
+		HTAnchor_getUCInfoStage(me->node_anchor, UCT_STAGE_STRUCTURED);
+
+		me->outUCLYhndl = current_char_set;
+		HTAnchor_setUCInfoStage(me->node_anchor,
+					current_char_set,
+					UCT_STAGE_HTEXT,
+					UCT_SETBY_MIME);	/* highest priorty! */
+		HTAnchor_setUCInfoStage(me->node_anchor,
+					current_char_set,
+					UCT_STAGE_STRUCTURED,
+					UCT_SETBY_MIME);	/* highest priorty! */
+		me->outUCI = HTAnchor_getUCInfoStage(me->node_anchor,
+						     UCT_STAGE_HTEXT);
+		/* The SGML stage will be reset in change_chartrans_handling */
+	    }
+#endif
+
+	    if (UCCanTranslateFromTo(chndl, current_char_set)) {
+		chartrans_ok = YES;
+		StrAllocCopy(me->node_anchor->charset, cp4);
+		HTAnchor_setUCInfoStage(me->node_anchor, chndl,
+					UCT_STAGE_PARSER,
+					UCT_SETBY_STRUCTURED);
+	    } else if (chndl < 0) {
+		/*
+		 * Got something but we don't recognize it.
+		 */
+		chndl = UCLYhndl_for_unrec;
+		if (chndl < 0)	/* UCLYhndl_for_unrec not defined :-( */
+		    chndl = UCLYhndl_for_unspec;	/* always >= 0 */
+		if (UCCanTranslateFromTo(chndl, current_char_set)) {
+		    chartrans_ok = YES;
+		    HTAnchor_setUCInfoStage(me->node_anchor, chndl,
+					    UCT_STAGE_PARSER,
+					    UCT_SETBY_STRUCTURED);
+		}
+	    }
+	    if (chartrans_ok) {
+		p_in = HTAnchor_getUCInfoStage(me->node_anchor,
+					       UCT_STAGE_PARSER);
+		p_out = HTAnchor_setUCInfoStage(me->node_anchor,
+						current_char_set,
+						UCT_STAGE_HTEXT,
+						UCT_SETBY_DEFAULT);
+		if (!p_out) {
+		    /*
+		     * Try again.
+		     */
+		    p_out = HTAnchor_getUCInfoStage(me->node_anchor,
+						    UCT_STAGE_HTEXT);
+		}
+		if (!strcmp(p_in->MIMEname, "x-transparent")) {
+		    HTPassEightBitRaw = TRUE;
+		    HTAnchor_setUCInfoStage(me->node_anchor,
+					    HTAnchor_getUCLYhndl(me->node_anchor,
+								 UCT_STAGE_HTEXT),
+					    UCT_STAGE_PARSER,
+					    UCT_SETBY_DEFAULT);
+		}
+		if (!strcmp(p_out->MIMEname, "x-transparent")) {
+		    HTPassEightBitRaw = TRUE;
+		    HTAnchor_setUCInfoStage(me->node_anchor,
+					    HTAnchor_getUCLYhndl(me->node_anchor,
+								 UCT_STAGE_PARSER),
+					    UCT_STAGE_HTEXT,
+					    UCT_SETBY_DEFAULT);
+		}
+		if ((p_in->enc != UCT_ENC_CJK)
+#ifdef EXP_JAPANESEUTF8_SUPPORT
+		    && (p_in->enc != UCT_ENC_UTF8)
+#endif
+		    ) {
+		    HTCJK = NOCJK;
+		    if (!(p_in->codepoints &
+			  UCT_CP_SUBSETOF_LAT1) &&
+			chndl == current_char_set) {
+			HTPassEightBitRaw = TRUE;
+		    }
+		} else if (p_out->enc == UCT_ENC_CJK) {
+		    Set_HTCJK(p_in->MIMEname, p_out->MIMEname);
+		}
+		LYGetChartransInfo(me);
+		/*
+		 * Update the chartrans info homologously to a Content-Type
+		 * MIME header with a charset parameter.  - FM
+		 */
+		if (me->UCLYhndl != chndl) {
+		    HTAnchor_setUCInfoStage(me->node_anchor, chndl,
+					    UCT_STAGE_MIME,
+					    UCT_SETBY_STRUCTURED);
+		    HTAnchor_setUCInfoStage(me->node_anchor, chndl,
+					    UCT_STAGE_PARSER,
+					    UCT_SETBY_STRUCTURED);
+		    me->inUCLYhndl = HTAnchor_getUCLYhndl(me->node_anchor,
+							  UCT_STAGE_PARSER);
+		    me->inUCI = HTAnchor_getUCInfoStage(me->node_anchor,
+							UCT_STAGE_PARSER);
+		}
+		UCSetTransParams(&me->T,
+				 me->inUCLYhndl, me->inUCI,
+				 me->outUCLYhndl, me->outUCI);
+	    } else {
+		/*
+		 * Cannot translate.  If according to some heuristic the given
+		 * charset and the current display character both are likely to
+		 * be like ISO-8859 in structure, pretend we have some kind of
+		 * match.
+		 */
+		BOOL given_is_8859 = (BOOL) (!strncmp(cp4, "iso-8859-", 9) &&
+					     isdigit(UCH(cp4[9])));
+		BOOL given_is_8859like = (BOOL) (given_is_8859
+						 || !strncmp(cp4, "windows-", 8)
+						 || !strncmp(cp4, "cp12", 4)
+						 || !strncmp(cp4, "cp-12", 5));
+		BOOL given_and_display_8859like = (BOOL) (given_is_8859like &&
+							  (strstr(LYchar_set_names[current_char_set],
+								  "ISO-8859") ||
+							   strstr(LYchar_set_names[current_char_set],
+								  "windows-")));
+
+		if (given_is_8859) {
+		    cp1 = &cp4[10];
+		    while (*cp1 &&
+			   isdigit(UCH((*cp1))))
+			cp1++;
+		    *cp1 = '\0';
+		}
+		if (given_and_display_8859like) {
+		    StrAllocCopy(me->node_anchor->charset, cp4);
+		    HTPassEightBitRaw = TRUE;
+		}
+		HTAlert(*cp4 ? cp4 : me->node_anchor->charset);
+
+	    }
+	    FREE(cp3);
+
+	    if (me->node_anchor->charset) {
+		CTRACE((tfp,
+			"LYHandleMETA: New charset: %s\n",
+			me->node_anchor->charset));
+	    }
+	}
+	/*
+	 * Set the kcode element based on the charset.  - FM
+	 */
+	HText_setKcode(me->text, me->node_anchor->charset, p_in);
+
+	/*
+	 * Check for a Refresh directive.  - FM
+	 */
+    } else if (!strcasecomp(NonNull(http_equiv), "Refresh")) {
+	char *Seconds = NULL;
+
+	LYUCTranslateHTMLString(&content, me->tag_charset, me->tag_charset,
+				NO, NO, YES, st_other);
+	LYParseRefreshURL(content, &Seconds, &href);
+
+	if (Seconds) {
+	    if (href) {
+		/*
+		 * We found a URL field, so check it out.  - FM
+		 */
+		if (!LYLegitimizeHREF(me, &href, TRUE, FALSE)) {
+		    /*
+		     * The specs require a complete URL, but this is a
+		     * Netscapism, so don't expect the author to know that.  -
+		     * FM
+		     */
+		    HTUserMsg(REFRESH_URL_NOT_ABSOLUTE);
+		    /*
+		     * Use the document's address as the base.  - FM
+		     */
+		    if (*href != '\0') {
+			temp = HTParse(href,
+				       me->node_anchor->address, PARSE_ALL);
+			StrAllocCopy(href, temp);
+			FREE(temp);
+		    } else {
+			StrAllocCopy(href, me->node_anchor->address);
+			HText_setNoCache(me->text);
+		    }
+
+		} else {
+		    /*
+		     * Check whether to fill in localhost.  - FM
+		     */
+		    LYFillLocalFileURL(&href,
+				       (me->inBASE ?
+					me->base_href : me->node_anchor->address));
+		}
+
+		/*
+		 * Set the no_cache flag if the Refresh URL is the same as the
+		 * document's address.  - FM
+		 */
+		if (!strcmp(href, me->node_anchor->address)) {
+		    HText_setNoCache(me->text);
+		}
+	    } else {
+		/*
+		 * We didn't find a URL field, so use the document's own
+		 * address and set the no_cache flag.  - FM
+		 */
+		StrAllocCopy(href, me->node_anchor->address);
+		HText_setNoCache(me->text);
+	    }
+	    /*
+	     * Check for an anchor in http or https URLs.  - FM
+	     */
+	    cp = NULL;
+#ifndef DONT_TRACK_INTERNAL_LINKS
+	    /* id_string seems to be used wrong below if given.
+	       not that it matters much.  avoid setting it here. - kw */
+	    if ((strncmp(href, "http", 4) == 0) &&
+		(cp = strchr(href, '#')) != NULL) {
+		StrAllocCopy(id_string, cp);
+		*cp = '\0';
+	    }
+#endif
+	    if (me->inA) {
+		/*
+		 * Ugh!  The META tag, which is a HEAD element, is in an
+		 * Anchor, which is BODY element.  All we can do is close the
+		 * Anchor and cross our fingers.  - FM
+		 */
+		if (me->inBoldA == TRUE && me->inBoldH == FALSE)
+		    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+		me->inBoldA = FALSE;
+		HText_endAnchor(me->text, me->CurrentANum);
+		me->inA = FALSE;
+		me->CurrentANum = 0;
+	    }
+	    me->CurrentA = HTAnchor_findChildAndLink
+		(
+		    me->node_anchor,	/* Parent */
+		    id_string,	/* Tag */
+		    href,	/* Addresss */
+		    (HTLinkType *) 0);	/* Type */
+	    if (id_string)
+		*cp = '#';
+	    FREE(id_string);
+	    LYEnsureSingleSpace(me);
+	    if (me->inUnderline == FALSE)
+		HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
+	    HTML_put_string(me, "REFRESH(");
+	    HTML_put_string(me, Seconds);
+	    HTML_put_string(me, " sec):");
+	    FREE(Seconds);
+	    if (me->inUnderline == FALSE)
+		HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
+	    HTML_put_character(me, ' ');
+	    me->in_word = NO;
+	    HText_beginAnchor(me->text, me->inUnderline, me->CurrentA);
+	    if (me->inBoldH == FALSE)
+		HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
+	    HTML_put_string(me, href);
+	    FREE(href);
+	    if (me->inBoldH == FALSE)
+		HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+	    HText_endAnchor(me->text, 0);
+	    LYEnsureSingleSpace(me);
+	}
+
+	/*
+	 * Check for a suggested filename via a Content-Disposition with a
+	 * filename=name.suffix in it, if we don't already have it via a server
+	 * header.  - FM
+	 */
+    } else if (isEmpty(me->node_anchor->SugFname) &&
+	       !strcasecomp((http_equiv ?
+			     http_equiv : ""), "Content-Disposition")) {
+	cp = content;
+	while (*cp != '\0' && strncasecomp(cp, "filename", 8))
+	    cp++;
+	if (*cp != '\0') {
+	    cp = LYSkipBlanks(cp + 8);
+	    if (*cp == '=')
+		cp++;
+	    cp = LYSkipBlanks(cp);
+	    if (*cp != '\0') {
+		StrAllocCopy(me->node_anchor->SugFname, cp);
+		if (*me->node_anchor->SugFname == '"') {
+		    if ((cp = strchr((me->node_anchor->SugFname + 1),
+				     '"')) != NULL) {
+			*(cp + 1) = '\0';
+			HTMIME_TrimDoubleQuotes(me->node_anchor->SugFname);
+			if (isEmpty(me->node_anchor->SugFname)) {
+			    FREE(me->node_anchor->SugFname);
+			}
+		    } else {
+			FREE(me->node_anchor->SugFname);
+		    }
+		}
+#if defined(UNIX) && !defined(DOSPATH)
+		/*
+		 * If blanks are not legal for local filenames, replace them
+		 * with underscores.
+		 */
+		if ((cp = me->node_anchor->SugFname) != NULL) {
+		    while (*cp != '\0') {
+			if (isspace(UCH(*cp)))
+			    *cp = '_';
+			++cp;
+		    }
+		}
+#endif
+	    }
+	}
+	/*
+	 * Check for a Set-Cookie directive.  - AK
+	 */
+    } else if (!strcasecomp(NonNull(http_equiv), "Set-Cookie")) {
+	/*
+	 * This will need to be updated when Set-Cookie/Set-Cookie2 handling is
+	 * finalized.  For now, we'll still assume "historical" cookies in META
+	 * directives.  - FM
+	 */
+	url_type = is_url(me->inBASE ?
+			  me->base_href : me->node_anchor->address);
+	if (url_type == HTTP_URL_TYPE || url_type == HTTPS_URL_TYPE) {
+	    LYSetCookie(content,
+			NULL,
+			(me->inBASE ?
+			 me->base_href : me->node_anchor->address));
+	}
+    }
+
+    /*
+     * Free the copies.  - FM
+     */
+  free_META_copies:
+    FREE(http_equiv);
+    FREE(name);
+    FREE(content);
+}
+
+/*
+ *  This function handles P elements in HTML streams.
+ *  If start is TRUE it handles a start tag, and if
+ *  FALSE, an end tag.	We presently handle start
+ *  and end tags identically, but this can lead to
+ *  a different number of blank lines between the
+ *  current paragraph and subsequent text when a P
+ *  end tag is present or not in the markup. - FM
+ */
+void LYHandlePlike(HTStructured * me, const BOOL *present,
+		   const char **value,
+		   char **include GCC_UNUSED,
+		   int align_idx,
+		   BOOL start)
+{
+    if (TRUE) {
+	/*
+	 * FIG content should be a true block, which like P inherits the
+	 * current style.  APPLET is like character elements or an ALT
+	 * attribute, unless it content contains a block element.  If we
+	 * encounter a P in either's content, we set flags to treat the content
+	 * as a block.  - FM
+	 */
+	if (start) {
+	    if (me->inFIG)
+		me->inFIGwithP = TRUE;
+
+	    if (me->inAPPLET)
+		me->inAPPLETwithP = TRUE;
+	}
+
+	UPDATE_STYLE;
+	if (me->List_Nesting_Level >= 0) {
+	    /*
+	     * We're in a list.  Treat P as an instruction to create one blank
+	     * line, if not already present, then fall through to handle
+	     * attributes, with the "second line" margins.  - FM
+	     */
+	    if (me->inP) {
+		if (me->inFIG || me->inAPPLET ||
+		    me->inCAPTION || me->inCREDIT ||
+		    me->sp->style->spaceAfter > 0 ||
+		    (start && me->sp->style->spaceBefore > 0)) {
+		    LYEnsureDoubleSpace(me);
+		} else {
+		    LYEnsureSingleSpace(me);
+		}
+	    }
+	} else if (me->sp[0].tag_number == HTML_ADDRESS) {
+	    /*
+	     * We're in an ADDRESS.  Treat P as an instruction to start a
+	     * newline, if needed, then fall through to handle attributes.  -
+	     * FM
+	     */
+	    if (!HText_LastLineEmpty(me->text, FALSE)) {
+		HText_setLastChar(me->text, ' ');	/* absorb white space */
+		HText_appendCharacter(me->text, '\r');
+	    }
+	} else {
+	    if (start) {
+		if (!(me->inLABEL && !me->inP)) {
+		    HText_appendParagraph(me->text);
+		}
+	    } else if (me->sp->style->spaceAfter > 0) {
+		LYEnsureDoubleSpace(me);
+	    } else {
+		LYEnsureSingleSpace(me);
+	    }
+	    me->inLABEL = FALSE;
+	}
+	me->in_word = NO;
+
+	if (LYoverride_default_alignment(me)) {
+	    me->sp->style->alignment = LYstyles(me->sp[0].tag_number)->alignment;
+	} else if ((me->List_Nesting_Level >= 0 &&
+		    (me->sp->style->id == ST_DivCenter ||
+		     me->sp->style->id == ST_DivLeft ||
+		     me->sp->style->id == ST_DivRight)) ||
+		   ((me->Division_Level < 0) &&
+		    (me->sp->style->id == ST_Normal ||
+		     me->sp->style->id == ST_Preformatted))) {
+	    me->sp->style->alignment = HT_LEFT;
+	} else {
+	    me->sp->style->alignment = (short) me->current_default_alignment;
+	}
+
+	if (start) {
+	    if (present && present[align_idx] && value[align_idx]) {
+		if (!strcasecomp(value[align_idx], "center") &&
+		    !(me->List_Nesting_Level >= 0 && !me->inP))
+		    me->sp->style->alignment = HT_CENTER;
+		else if (!strcasecomp(value[align_idx], "right") &&
+			 !(me->List_Nesting_Level >= 0 && !me->inP))
+		    me->sp->style->alignment = HT_RIGHT;
+		else if (!strcasecomp(value[align_idx], "left") ||
+			 !strcasecomp(value[align_idx], "justify"))
+		    me->sp->style->alignment = HT_LEFT;
+	    }
+
+	}
+
+	/*
+	 * Mark that we are starting a new paragraph and don't have any of it's
+	 * text yet.  - FM
+	 */
+	me->inP = FALSE;
+    }
+
+    return;
+}
+
+/*
+ *  This function handles SELECT elements in HTML streams.
+ *  If start is TRUE it handles a start tag, and if FALSE,
+ *  an end tag. - FM
+ */
+void LYHandleSELECT(HTStructured * me, const BOOL *present,
+		    const char **value,
+		    char **include GCC_UNUSED,
+		    BOOL start)
+{
+    int i;
+
+    if (start == TRUE) {
+	char *name = NULL;
+	BOOLEAN multiple = NO;
+	char *size = NULL;
+
+	/*
+	 * Initialize the disable attribute.
+	 */
+	me->select_disabled = FALSE;
+
+	/*
+	 * Check for unclosed TEXTAREA.
+	 */
+	if (me->inTEXTAREA) {
+	    if (LYBadHTML(me)) {
+		LYShowBadHTML("Bad HTML: Missing TEXTAREA end tag\n");
+	    }
+	}
+
+	/*
+	 * Set to know we are in a select tag.
+	 */
+	me->inSELECT = TRUE;
+
+	if (!(present && present[HTML_SELECT_NAME] &&
+	      non_empty(value[HTML_SELECT_NAME]))) {
+	    StrAllocCopy(name, "");
+	} else if (strchr(value[HTML_SELECT_NAME], '&') == NULL) {
+	    StrAllocCopy(name, value[HTML_SELECT_NAME]);
+	} else {
+	    StrAllocCopy(name, value[HTML_SELECT_NAME]);
+	    UNESCAPE_FIELDNAME_TO_STD(&name);
+	}
+	if (present && present[HTML_SELECT_MULTIPLE])
+	    multiple = YES;
+	if (present && present[HTML_SELECT_DISABLED])
+	    me->select_disabled = TRUE;
+	if (present && present[HTML_SELECT_SIZE] &&
+	    non_empty(value[HTML_SELECT_SIZE])) {
+	    /*
+	     * Let the size be determined by the number of OPTIONs.  - FM
+	     */
+	    CTRACE((tfp, "LYHandleSELECT: Ignoring SIZE=\"%s\" for SELECT.\n",
+		    value[HTML_SELECT_SIZE]));
+	}
+
+	if (me->inBoldH == TRUE &&
+	    (multiple == NO || LYSelectPopups == FALSE)) {
+	    HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
+	    me->inBoldH = FALSE;
+	    me->needBoldH = TRUE;
+	}
+	if (me->inUnderline == TRUE &&
+	    (multiple == NO || LYSelectPopups == FALSE)) {
+	    HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
+	    me->inUnderline = FALSE;
+	}
+
+	if ((multiple == NO && LYSelectPopups == TRUE) &&
+	    (me->sp[0].tag_number == HTML_PRE || me->inPRE == TRUE ||
+	     !me->sp->style->freeFormat) &&
+	    HText_LastLineSize(me->text, FALSE) > (LYcolLimit - 7)) {
+	    /*
+	     * Force a newline when we're using a popup in a PRE block and are
+	     * within 7 columns from the right margin.  This will allow for the
+	     * '[' popup designator and help avoid a wrap in the underscore
+	     * placeholder for the retracted popup entry in the HText
+	     * structure.  - FM
+	     */
+	    HTML_put_character(me, '\n');
+	    me->in_word = NO;
+	}
+
+	LYCheckForID(me, present, value, (int) HTML_SELECT_ID);
+
+	HText_beginSelect(name, ATTR_CS_IN, multiple, size);
+	FREE(name);
+	FREE(size);
+
+	me->first_option = TRUE;
+    } else {
+	/*
+	 * Handle end tag.
+	 */
+	char *ptr;
+
+	/*
+	 * Make sure we had a select start tag.
+	 */
+	if (!me->inSELECT) {
+	    if (LYBadHTML(me)) {
+		LYShowBadHTML("Bad HTML: Unmatched SELECT end tag\n");
+	    }
+	    return;
+	}
+
+	/*
+	 * Set to know that we are no longer in a select tag.
+	 */
+	me->inSELECT = FALSE;
+
+	/*
+	 * Clear the disable attribute.
+	 */
+	me->select_disabled = FALSE;
+
+	/*
+	 * Finish the data off.
+	 */
+	HTChunkTerminate(&me->option);
+	/*
+	 * Finish the previous option.
+	 */
+	ptr = HText_setLastOptionValue(me->text,
+				       me->option.data,
+				       me->LastOptionValue,
+				       LAST_ORDER,
+				       me->LastOptionChecked,
+				       me->UCLYhndl,
+				       ATTR_CS_IN);
+	FREE(me->LastOptionValue);
+
+	me->LastOptionChecked = FALSE;
+
+	if (HTCurSelectGroupType == F_CHECKBOX_TYPE ||
+	    LYSelectPopups == FALSE) {
+	    /*
+	     * Start a newline after the last checkbox/button option.
+	     */
+	    LYEnsureSingleSpace(me);
+	} else {
+	    /*
+	     * Output popup box with the default option to screen, but use
+	     * non-breaking spaces for output.
+	     */
+	    if (ptr &&
+		me->sp[0].tag_number == HTML_PRE && strlen(ptr) > 6) {
+		/*
+		 * The code inadequately handles OPTION fields in PRE tags. 
+		 * We'll put up a minimum of 6 characters, and if any more
+		 * would exceed the wrap column, we'll ignore them.
+		 */
+		for (i = 0; i < 6; i++) {
+		    if (*ptr == ' ')
+			HText_appendCharacter(me->text, HT_NON_BREAK_SPACE);
+		    else
+			HText_appendCharacter(me->text, *ptr);
+		    ptr++;
+		}
+	    }
+	    for (; non_empty(ptr); ptr++) {
+		if (*ptr == ' ')
+		    HText_appendCharacter(me->text, HT_NON_BREAK_SPACE);
+		else
+		    HText_appendCharacter(me->text, *ptr);
+	    }
+	    /*
+	     * Add end option character.
+	     */
+	    if (!me->first_option) {
+		HText_appendCharacter(me->text, ']');
+		HText_setLastChar(me->text, ']');
+		me->in_word = YES;
+	    }
+	}
+	HTChunkClear(&me->option);
+
+	if (me->Underline_Level > 0 && me->inUnderline == FALSE) {
+	    HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
+	    me->inUnderline = TRUE;
+	}
+	if (me->needBoldH == TRUE && me->inBoldH == FALSE) {
+	    HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
+	    me->inBoldH = TRUE;
+	    me->needBoldH = FALSE;
+	}
+    }
+}
+
+/*
+ *  This function strips white characters and
+ *  generally fixes up attribute values that
+ *  were received from the SGML parser and
+ *  are to be treated as partial or absolute
+ *  URLs. - FM
+ */
+int LYLegitimizeHREF(HTStructured * me, char **href,
+		     BOOL force_slash,
+		     BOOL strip_dots)
+{
+    int url_type = 0;
+    char *p = NULL;
+    char *pound = NULL;
+    const char *Base = NULL;
+
+    if (!me || !href || isEmpty(*href))
+	return (url_type);
+
+    if (!LYTrimStartfile(*href)) {
+	/*
+	 * Collapse spaces in the actual URL, but just protect against tabs or
+	 * newlines in the fragment, if present.  This seeks to cope with
+	 * atrocities inflicted on the Web by authoring tools such as
+	 * Frontpage.  - FM
+	 */
+
+	/*  Before working on spaces check if we have any, usually none. */
+	p = LYSkipNonBlanks(*href);
+
+	if (*p) {		/* p == first space character */
+	    /* no reallocs below, all converted in place */
+
+	    pound = findPoundSelector(*href);
+
+	    if (pound != NULL && pound < p) {
+		convert_to_spaces(p, FALSE);	/* done */
+
+	    } else {
+		if (pound != NULL)
+		    *pound = '\0';	/* mark */
+
+		/*
+		 * No blanks really belong in the HREF,
+		 * but if it refers to an actual file,
+		 * it may actually have blanks in the name.
+		 * Try to accommodate. See also HTParse().
+		 */
+		if (LYRemoveNewlines(p) || strchr(p, '\t') != 0) {
+		    LYRemoveBlanks(p);	/* a compromise... */
+		}
+
+		if (pound != NULL) {
+		    p = strchr(p, '\0');
+		    *pound = '#';	/* restore */
+		    convert_to_spaces(pound, FALSE);
+		    if (p < pound)
+			strcpy(p, pound);
+		}
+	    }
+	}
+    }
+    if (**href == '\0')
+	return (url_type);
+
+    TRANSLATE_AND_UNESCAPE_TO_STD(href);
+
+    Base = me->inBASE ?
+	me->base_href : me->node_anchor->address;
+
+    url_type = is_url(*href);
+    if (!url_type && force_slash && **href == '.' &&
+	(!strcmp(*href, ".") || !strcmp(*href, "..")) &&
+	!isFILE_URL(Base)) {
+	/*
+	 * The Fielding RFC/ID for resolving partial HREFs says that a slash
+	 * should be on the end of the preceding symbolic element for "." and
+	 * "..", but all tested browsers only do that for an explicit "./" or
+	 * "../", so we'll respect the RFC/ID only if force_slash was TRUE and
+	 * it's not a file URL.  - FM
+	 */
+	StrAllocCat(*href, "/");
+    }
+    if ((!url_type && LYStripDotDotURLs && strip_dots && **href == '.') &&
+	!strncasecomp(Base, "http", 4)) {
+	/*
+	 * We will be resolving a partial reference versus an http or https
+	 * URL, and it has lead dots, which may be retained when resolving via
+	 * HTParse(), but the request would fail if the first element of the
+	 * resultant path is two dots, because no http or https server accepts
+	 * such paths, and the current URL draft, likely to become an RFC, says
+	 * that it's optional for the UA to strip them as a form of error
+	 * recovery.  So we will, recursively, for http/https URLs, like the
+	 * "major market browsers" which made this problem so common on the
+	 * Web, but we'll also issue a message about it, such that the bad
+	 * partial reference might get corrected by the document provider.  -
+	 * FM
+	 */
+	char *temp = NULL, *path = NULL, *cp;
+	const char *str = "";
+
+	temp = HTParse(*href, Base, PARSE_ALL);
+	path = HTParse(temp, "", PARSE_PATH + PARSE_PUNCTUATION);
+	if (!strncmp(path, "/..", 3)) {
+	    cp = (path + 3);
+	    if (LYIsHtmlSep(*cp) || *cp == '\0') {
+		if (Base[4] == 's') {
+		    str = "s";
+		}
+		CTRACE((tfp,
+			"LYLegitimizeHREF: Bad value '%s' for http%s URL.\n",
+			*href, str));
+		CTRACE((tfp, "                  Stripping lead dots.\n"));
+		if (!me->inBadHREF) {
+		    HTUserMsg(BAD_PARTIAL_REFERENCE);
+		    me->inBadHREF = TRUE;
+		}
+	    }
+	    if (*cp == '\0') {
+		StrAllocCopy(*href, "/");
+	    } else if (LYIsHtmlSep(*cp)) {
+		while (!strncmp(cp, "/..", 3)) {
+		    if (*(cp + 3) == '/') {
+			cp += 3;
+			continue;
+		    } else if (*(cp + 3) == '\0') {
+			*(cp + 1) = '\0';
+			*(cp + 2) = '\0';
+		    }
+		    break;
+		}
+		StrAllocCopy(*href, cp);
+	    }
+	}
+	FREE(temp);
+	FREE(path);
+    }
+    return (url_type);
+}
+
+/*
+ *  This function checks for a Content-Base header,
+ *  and if not present, a Content-Location header
+ *  which is an absolute URL, and sets the BASE
+ *  accordingly.  If set, it will be replaced by
+ *  any BASE tag in the HTML stream, itself. - FM
+ */
+void LYCheckForContentBase(HTStructured * me)
+{
+    char *cp = NULL;
+    BOOL present[HTML_BASE_ATTRIBUTES];
+    const char *value[HTML_BASE_ATTRIBUTES];
+    int i;
+
+    if (!(me && me->node_anchor))
+	return;
+
+    if (me->node_anchor->content_base != NULL) {
+	/*
+	 * We have a Content-Base value.  Use it if it's non-zero length.  - FM
+	 */
+	if (*me->node_anchor->content_base == '\0')
+	    return;
+	StrAllocCopy(cp, me->node_anchor->content_base);
+	LYRemoveBlanks(cp);
+    } else if (me->node_anchor->content_location != NULL) {
+	/*
+	 * We didn't have a Content-Base value, but do have a Content-Location
+	 * value.  Use it if it's an absolute URL.  - FM
+	 */
+	if (*me->node_anchor->content_location == '\0')
+	    return;
+	StrAllocCopy(cp, me->node_anchor->content_location);
+	LYRemoveBlanks(cp);
+	if (!is_url(cp)) {
+	    FREE(cp);
+	    return;
+	}
+    } else {
+	/*
+	 * We had neither a Content-Base nor Content-Location value.  - FM
+	 */
+	return;
+    }
+
+    /*
+     * If we collapsed to a zero-length value, ignore it.  - FM
+     */
+    if (*cp == '\0') {
+	FREE(cp);
+	return;
+    }
+
+    /*
+     * Pass the value to HTML_start_element as the HREF of a BASE tag.  - FM
+     */
+    for (i = 0; i < HTML_BASE_ATTRIBUTES; i++)
+	present[i] = NO;
+    present[HTML_BASE_HREF] = YES;
+    value[HTML_BASE_HREF] = (const char *) cp;
+    (*me->isa->start_element) (me, HTML_BASE, present, value,
+			       0, 0);
+    FREE(cp);
+}
+
+/*
+ *  This function creates NAMEd Anchors if a non-zero-length NAME
+ *  or ID attribute was present in the tag. - FM
+ */
+void LYCheckForID(HTStructured * me, const BOOL *present,
+		  const char **value,
+		  int attribute)
+{
+    HTChildAnchor *ID_A = NULL;
+    char *temp = NULL;
+
+    if (!(me && me->text))
+	return;
+
+    if (present && present[attribute]
+	&& non_empty(value[attribute])) {
+	/*
+	 * Translate any named or numeric character references.  - FM
+	 */
+	StrAllocCopy(temp, value[attribute]);
+	LYUCTranslateHTMLString(&temp, me->tag_charset, me->tag_charset,
+				NO, NO, YES, st_URL);
+
+	/*
+	 * Create the link if we still have a non-zero-length string.  - FM
+	 */
+	if ((temp[0] != '\0') &&
+	    (ID_A = HTAnchor_findChildAndLink
+	     (
+		 me->node_anchor,	/* Parent */
+		 temp,		/* Tag */
+		 NULL,		/* Addresss */
+		 (HTLinkType *) 0))) {	/* Type */
+	    HText_beginAnchor(me->text, me->inUnderline, ID_A);
+	    HText_endAnchor(me->text, 0);
+	}
+	FREE(temp);
+    }
+}
+
+/*
+ *  This function creates a NAMEd Anchor for the ID string
+ *  passed to it directly as an argument.  It assumes the
+ *  does not need checking for character references. - FM
+ */
+void LYHandleID(HTStructured * me, const char *id)
+{
+    HTChildAnchor *ID_A = NULL;
+
+    if (!(me && me->text) ||
+	isEmpty(id))
+	return;
+
+    /*
+     * Create the link if we still have a non-zero-length string.  - FM
+     */
+    if ((ID_A = HTAnchor_findChildAndLink
+	 (
+	     me->node_anchor,	/* Parent */
+	     id,		/* Tag */
+	     NULL,		/* Addresss */
+	     (HTLinkType *) 0)) != NULL) {	/* Type */
+	HText_beginAnchor(me->text, me->inUnderline, ID_A);
+	HText_endAnchor(me->text, 0);
+    }
+}
+
+/*
+ *  This function checks whether we want to override
+ *  the current default alignment for paragraphs and
+ *  instead use that specified in the element's style
+ *  sheet. - FM
+ */
+BOOLEAN LYoverride_default_alignment(HTStructured * me)
+{
+    if (!me)
+	return NO;
+
+    switch (me->sp[0].tag_number) {
+    case HTML_BLOCKQUOTE:
+    case HTML_BQ:
+    case HTML_NOTE:
+    case HTML_FN:
+    case HTML_ADDRESS:
+	me->sp->style->alignment = HT_LEFT;
+	return YES;
+
+    default:
+	break;
+    }
+    return NO;
+}
+
+/*
+ *  This function inserts newlines if needed to create double spacing,
+ *  and sets the left margin for subsequent text to the second line
+ *  indentation of the current style. - FM
+ */
+void LYEnsureDoubleSpace(HTStructured * me)
+{
+    if (!me || !me->text)
+	return;
+
+    if (!HText_LastLineEmpty(me->text, FALSE)) {
+	HText_setLastChar(me->text, ' ');	/* absorb white space */
+	HText_appendCharacter(me->text, '\r');
+	HText_appendCharacter(me->text, '\r');
+    } else if (!HText_PreviousLineEmpty(me->text, FALSE)) {
+	HText_setLastChar(me->text, ' ');	/* absorb white space */
+	HText_appendCharacter(me->text, '\r');
+    } else if (me->List_Nesting_Level >= 0) {
+	HText_NegateLineOne(me->text);
+    }
+    me->in_word = NO;
+    return;
+}
+
+/*
+ *  This function inserts a newline if needed to create single spacing,
+ *  and sets the left margin for subsequent text to the second line
+ *  indentation of the current style. - FM
+ */
+void LYEnsureSingleSpace(HTStructured * me)
+{
+    if (!me || !me->text)
+	return;
+
+    if (!HText_LastLineEmpty(me->text, FALSE)) {
+	HText_setLastChar(me->text, ' ');	/* absorb white space */
+	HText_appendCharacter(me->text, '\r');
+    } else if (me->List_Nesting_Level >= 0) {
+	HText_NegateLineOne(me->text);
+    }
+    me->in_word = NO;
+    return;
+}
+
+/*
+ *  This function resets paragraph alignments for block
+ *  elements which do not have a defined style sheet. - FM
+ */
+void LYResetParagraphAlignment(HTStructured * me)
+{
+    if (!me)
+	return;
+
+    if (me->List_Nesting_Level >= 0 ||
+	((me->Division_Level < 0) &&
+	 (me->sp->style->id == ST_Normal ||
+	  me->sp->style->id == ST_Preformatted))) {
+	me->sp->style->alignment = HT_LEFT;
+    } else {
+	me->sp->style->alignment = (short) me->current_default_alignment;
+    }
+    return;
+}
+
+/*
+ *  This example function checks whether the given anchor has
+ *  an address with a file scheme, and if so, loads it into the
+ *  the SGML parser's context->url element, which was passed as
+ *  the second argument.  The handle_comment() calling function in
+ *  SGML.c then calls LYDoCSI() in LYUtils.c to insert HTML markup
+ *  into the corresponding stream, homologously to an SSI by an
+ *  HTTP server. - FM
+ *
+ *  For functions similar to this but which depend on details of
+ *  the HTML handler's internal data, the calling interface should
+ *  be changed, and functions in SGML.c would have to make sure not
+ *  to call such functions inappropriately (e.g., calling a function
+ *  specific to the Lynx_HTML_Handler when SGML.c output goes to
+ *  some other HTStructured object like in HTMLGen.c), or the new
+ *  functions could be added to the SGML.h interface.
+ */
+BOOLEAN LYCheckForCSI(HTParentAnchor *anchor,
+		      char **url)
+{
+    if (!(anchor && anchor->address))
+	return FALSE;
+
+    if (!isFILE_URL(anchor->address))
+	return FALSE;
+
+    if (!LYisLocalHost(anchor->address))
+	return FALSE;
+
+    StrAllocCopy(*url, anchor->address);
+    return TRUE;
+}
+
+/*
+ *  This function is called from the SGML parser to look at comments
+ *  and see whether we should collect some info from them.  Currently
+ *  it only looks for comments with Message-Id and Subject info, in the
+ *  exact form generated by MHonArc for archived mailing list.  If found,
+ *  the info is stored in the document's HTParentAnchor.  It can later be
+ *  used for generating a mail response.
+ *
+ *  We are extra picky here because there isn't any official definition
+ *  for these kinds of comments - we might (and still can) misinterpret
+ *  arbitrary comments as something they aren't.
+ *
+ *  If something doesn't look right, for example invalid characters, the
+ *  strings are not stored.  Mail responses will use something else as
+ *  the subject, probably the document URL, and will not have an
+ *  In-Reply-To header.
+ *
+ *  All this is a hack - to do this the right way, mailing list archivers
+ *  would have to agree on some better mechanism to make this kind of info
+ *  from original mail headers available, for example using LINK.  - kw
+ */
+BOOLEAN LYCommentHacks(HTParentAnchor *anchor,
+		       const char *comment)
+{
+    const char *cp;
+    size_t len;
+
+    if (comment == NULL)
+	return FALSE;
+
+    if (!(anchor && anchor->address))
+	return FALSE;
+
+    if (strncmp(comment, "!--X-Message-Id: ", 17) == 0) {
+	char *messageid = NULL;
+	char *p;
+
+	for (cp = comment + 17; *cp; cp++) {
+	    if (UCH(*cp) >= 127 || !isgraph(UCH(*cp))) {
+		break;
+	    }
+	}
+	if (strcmp(cp, " --")) {
+	    return FALSE;
+	}
+	cp = comment + 17;
+	StrAllocCopy(messageid, cp);
+	/* This should be ok - message-id should only contain 7-bit ASCII */
+	if (!LYUCTranslateHTMLString(&messageid, 0, 0, NO, NO, YES, st_URL))
+	    return FALSE;
+	for (p = messageid; *p; p++) {
+	    if (UCH(*p) >= 127 || !isgraph(UCH(*p))) {
+		break;
+	    }
+	}
+	if (strcmp(p, " --")) {
+	    FREE(messageid);
+	    return FALSE;
+	}
+	if ((p = strchr(messageid, '@')) == NULL || p[1] == '\0') {
+	    FREE(messageid);
+	    return FALSE;
+	}
+	p = messageid;
+	if ((len = strlen(p)) >= 8 && !strcmp(&p[len - 3], " --")) {
+	    p[len - 3] = '\0';
+	} else {
+	    FREE(messageid);
+	    return FALSE;
+	}
+	if (HTAnchor_setMessageID(anchor, messageid)) {
+	    FREE(messageid);
+	    return TRUE;
+	} else {
+	    FREE(messageid);
+	    return FALSE;
+	}
+    }
+    if (strncmp(comment, "!--X-Subject: ", 14) == 0) {
+	char *subject = NULL;
+	char *p;
+
+	for (cp = comment + 14; *cp; cp++) {
+	    if (UCH(*cp) >= 127 || !isprint(UCH(*cp))) {
+		return FALSE;
+	    }
+	}
+	cp = comment + 14;
+	StrAllocCopy(subject, cp);
+	/* @@@
+	 * This may not be the right thing for the subject - but mail
+	 * subjects shouldn't contain 8-bit characters in raw form anyway.
+	 * We have to unescape character entities, since that's what MHonArc
+	 * seems to generate.  But if after that there are 8-bit characters
+	 * the string is rejected.  We would probably not know correctly
+	 * what charset to assume anyway - the mail sender's can differ from
+	 * the archive's.  And the code for sending mail cannot deal well
+	 * with 8-bit characters - we should not put them in the Subject
+	 * header in raw form, but don't have MIME encoding implemented.
+	 * Someone may want to do more about this...  - kw
+	 */
+	if (!LYUCTranslateHTMLString(&subject, 0, 0, NO, YES, NO, st_HTML))
+	    return FALSE;
+	for (p = subject; *p; p++) {
+	    if (UCH(*p) >= 127 || !isprint(UCH(*p))) {
+		FREE(subject);
+		return FALSE;
+	    }
+	}
+	p = subject;
+	if ((len = strlen(p)) >= 4 && !strcmp(&p[len - 3], " --")) {
+	    p[len - 3] = '\0';
+	} else {
+	    FREE(subject);
+	    return FALSE;
+	}
+	if (HTAnchor_setSubject(anchor, subject)) {
+	    FREE(subject);
+	    return TRUE;
+	} else {
+	    FREE(subject);
+	    return FALSE;
+	}
+    }
+
+    return FALSE;
+}
+
+    /*
+     * Create the Title with any left-angle-brackets converted to &lt; entities
+     * and any ampersands converted to &amp; entities.  - FM
+     *
+     * Convert 8-bit letters to &#xUUUU to avoid dependencies from display
+     * character set which may need changing.  Do NOT convert any 8-bit chars
+     * if we have CJK display.  - LP
+     */
+void LYformTitle(char **dst,
+		 const char *src)
+{
+    if (HTCJK == JAPANESE) {
+	char *tmp_buffer = NULL;
+
+	if ((tmp_buffer = (char *) malloc(strlen(src) + 1)) == 0)
+	    outofmem(__FILE__, "LYformTitle");
+
+	assert(tmp_buffer != NULL);
+
+	switch (kanji_code) {	/* 1997/11/22 (Sat) 09:28:00 */
+	case EUC:
+	    TO_EUC((const unsigned char *) src, (unsigned char *) tmp_buffer);
+	    break;
+	case SJIS:
+	    TO_SJIS((const unsigned char *) src, (unsigned char *) tmp_buffer);
+	    break;
+	default:
+	    CTRACE((tfp, "\nLYformTitle: kanji_code is an unexpected value."));
+	    strcpy(tmp_buffer, src);
+	    break;
+	}
+	StrAllocCopy(*dst, tmp_buffer);
+	FREE(tmp_buffer);
+    } else {
+	StrAllocCopy(*dst, src);
+    }
+}
diff --git a/src/LYCharUtils.h b/src/LYCharUtils.h
new file mode 100644
index 00000000..6209778e
--- /dev/null
+++ b/src/LYCharUtils.h
@@ -0,0 +1,103 @@
+/*
+ * $LynxId: LYCharUtils.h,v 1.24 2009/01/19 23:53:27 tom Exp $
+ */
+#ifndef LYCHARUTILS_H
+#define LYCHARUTILS_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif /* HTUTILS_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#define CHECK_ID(code) LYCheckForID(me, present, value, (int)code)
+    typedef enum {
+	st_HTML = 0,		/* attributes and content found in HTML, probably meant for display */
+	st_URL,			/* URLs, fragments, NAME and ID */
+	st_other
+    } CharUtil_st;
+
+    extern char **LYUCFullyTranslateString(char **str,
+					   int cs_from,
+					   int cs_to,
+					   BOOLEAN do_ent,
+					   BOOL use_lynx_specials,
+					   BOOLEAN plain_space,
+					   BOOLEAN hidden,
+					   BOOL Back,
+					   CharUtil_st stype);
+    extern BOOL LYUCTranslateHTMLString(char **str,
+					int cs_from,
+					int cs_to,
+					BOOL use_lynx_specials,
+					BOOLEAN plain_space,
+					BOOLEAN hidden,
+					CharUtil_st stype);
+    extern BOOL LYUCTranslateBackFormData(char **str,
+					  int cs_from,
+					  int cs_to,
+					  BOOLEAN plain_space);
+    extern void LYEntify(char **str,
+			 BOOLEAN isTITLE);
+    extern const char *LYEntifyTitle(char **target, const char *source);
+    extern const char *LYEntifyValue(char **target, const char *source);
+    extern void LYTrimHead(char *str);
+    extern void LYTrimTail(char *str);
+    extern char *LYFindEndOfComment(char *str);
+    extern void LYFillLocalFileURL(char **href,
+				   const char *base);
+    extern void LYAddMETAcharsetToFD(FILE *fd,
+				     int disp_chndl);
+    extern void LYformTitle(char **dst,
+			    const char *src);
+    extern char *LYParseTagParam(char *from,
+				 const char *name);
+    extern void LYParseRefreshURL(char *content,
+				  char **p_seconds,
+				  char **p_address);
+
+#ifdef Lynx_HTML_Handler
+    extern int OL_CONTINUE;	/* flag for whether CONTINUE is set */
+    extern int OL_VOID;		/* flag for whether a count is set */
+    extern void LYZero_OL_Counter(HTStructured * me);
+    extern char *LYUppercaseA_OL_String(int seqnum);
+    extern char *LYLowercaseA_OL_String(int seqnum);
+    extern char *LYUppercaseI_OL_String(int seqnum);
+    extern char *LYLowercaseI_OL_String(int seqnum);
+    extern void LYGetChartransInfo(HTStructured * me);
+    extern void LYHandleMETA(HTStructured * me, const BOOL *present,
+			     const char **value,
+			     char **include);
+    extern void LYHandlePlike(HTStructured * me, const BOOL *present,
+			      const char **value,
+			      char **include,
+			      int align_idx,
+			      BOOL start);
+    extern void LYHandleSELECT(HTStructured * me, const BOOL *present,
+			       const char **value,
+			       char **include,
+			       BOOL start);
+    extern int LYLegitimizeHREF(HTStructured * me, char **href,
+				BOOL force_slash,
+				BOOL strip_dots);
+    extern void LYCheckForContentBase(HTStructured * me);
+    extern void LYCheckForID(HTStructured * me, const BOOL *present,
+			     const char **value,
+			     int attribute);
+    extern void LYHandleID(HTStructured * me, const char *id);
+    extern BOOLEAN LYoverride_default_alignment(HTStructured * me);
+    extern void LYEnsureDoubleSpace(HTStructured * me);
+    extern void LYEnsureSingleSpace(HTStructured * me);
+    extern void LYResetParagraphAlignment(HTStructured * me);
+    extern BOOLEAN LYCheckForCSI(HTParentAnchor *anchor,
+				 char **url);
+
+#endif				/* Lynx_HTML_Handler */
+
+#define LYUCTranslateBackHeaderText LYUCTranslateBackFormData
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYCHARUTILS_H */
diff --git a/src/LYCharVals.h b/src/LYCharVals.h
new file mode 100644
index 00000000..43fac156
--- /dev/null
+++ b/src/LYCharVals.h
@@ -0,0 +1,34 @@
+#ifndef LYCHARVALS_H
+#define LYCHARVALS_H 1
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+/*
+ * Use integer values for character constants rather than '\octal' form, since
+ * not all compilers agree that those will fit in a character, even when cast.
+ */
+#ifndef   CH_ESC
+#ifdef    EBCDIC
+#define CH_DEL           0x07
+#define CH_ESC           0x27
+#define CH_ESC_PAR       0x27
+#define CH_HICTL         0x3f
+#define CH_NBSP          0x41
+#define CH_SHY           0xca
+#define LYCharINTERRUPT1 0x03	/* Control-C */
+#define LYCharINTERRUPT2 0x2f	/* Control-G */
+#else /* EBCDIC */
+#define CH_ESC           0033
+#define CH_DEL           0177
+#define CH_ESC_PAR       0233
+#define CH_HICTL         0237
+#define CH_NBSP          0240
+#define CH_SHY           0255
+#define LYCharINTERRUPT1 0003	/* Control-C */
+#define LYCharINTERRUPT2 0007	/* Control-G */
+#endif /* EBCDIC */
+#endif /* CH_ESC */
+
+#endif /* LYCHARVALS_H */
diff --git a/src/LYClean.c b/src/LYClean.c
new file mode 100644
index 00000000..efeb3841
--- /dev/null
+++ b/src/LYClean.c
@@ -0,0 +1,224 @@
+/* $LynxId: LYClean.c,v 1.38 2008/02/11 00:50:19 Paul.B.Mahol Exp $ */
+#include <HTUtils.h>
+#include <LYCurses.h>
+#include <LYUtils.h>
+#include <LYSignal.h>
+#include <LYClean.h>
+#include <LYMainLoop.h>
+#include <LYGlobalDefs.h>
+#include <LYTraversal.h>
+#include <LYHistory.h>
+#include <LYCookie.h>
+#include <LYSession.h>
+#include <UCAuto.h>
+#include <HTAlert.h>
+
+#include <LYexit.h>
+#include <LYLeaks.h>
+
+#ifdef DJGPP
+extern void sig_handler_watt(int);
+#endif /* DJGPP */
+
+#ifdef VMS
+BOOLEAN HadVMSInterrupt = FALSE;
+#endif /* VMS */
+
+/*
+ * Interrupt handler.  Stop curses and exit gracefully.
+ */
+void cleanup_sig(int sig)
+{
+#ifdef IGNORE_CTRL_C
+    if (sig == SIGINT) {
+	/*
+	 * Need to rearm the signal.
+	 */
+#ifdef DJGPP
+	if (wathndlcbrk) {
+	    sig_handler_watt(sig);	/* Use WATT-32 signal handler */
+	}
+#endif /* DJGPP */
+	signal(SIGINT, cleanup_sig);
+	sigint = TRUE;
+#ifdef DJGPP
+	_eth_release();
+	_eth_init();
+#endif /* DJGPP */
+	return;
+    }
+#endif /* IGNORE_CTRL_C */
+
+#ifdef VMS
+    if (!dump_output_immediately) {
+	int c;
+
+	/*
+	 * Reassert the AST.
+	 */
+	(void) signal(SIGINT, cleanup_sig);
+	if (!LYCursesON)
+	    return;
+
+	/*
+	 * Refresh screen to get rid of "cancel" message, then query.
+	 */
+	lynx_force_repaint();
+	LYrefresh();
+
+	/*
+	 * Ask if exit is intended.
+	 */
+	if (LYQuitDefaultYes == TRUE) {
+	    c = HTConfirmDefault(REALLY_EXIT, YES);
+	} else {
+	    c = HTConfirmDefault(REALLY_EXIT, NO);
+	}
+	HadVMSInterrupt = TRUE;
+	if (LYQuitDefaultYes == TRUE) {
+	    if (c == NO) {
+		return;
+	    }
+	} else if (c != YES) {
+	    return;
+	}
+    }
+#endif /* VMS */
+
+    /*
+     * Ignore further interrupts.  - mhc:  11/2/91
+     */
+#ifndef NOSIGHUP
+    (void) signal(SIGHUP, SIG_IGN);
+#endif /* NOSIGHUP */
+
+#ifdef VMS
+    /*
+     * Use ttclose() from cleanup() for VMS if not dumping.
+     */
+    if (dump_output_immediately)
+#else /* Unix: */
+    (void) signal(SIGINT, SIG_IGN);
+#endif /* VMS */
+
+    (void) signal(SIGTERM, SIG_IGN);
+
+    if (traversal)
+	dump_traversal_history();
+
+#ifndef NOSIGHUP
+    if (sig != SIGHUP) {
+#endif /* NOSIGHUP */
+
+	if (!dump_output_immediately) {
+	    /*
+	     * cleanup() also calls cleanup_files().
+	     */
+	    cleanup();
+	}
+	if (sig != 0) {
+	    SetOutputMode(O_TEXT);
+	    printf("\n\n%s %d\n\n",
+		   gettext("Exiting via interrupt:"),
+		   sig);
+	    fflush(stdout);
+	}
+#ifndef NOSIGHUP
+    } else {
+#ifdef USE_SESSIONS
+	/*
+	 * Wondering is this right place and time to do it.
+	 * We need this, for example it is usefull to save session
+	 * if user closed lynx in non standard way, like closing
+	 * xterm window or in worst one like crash.
+	 */
+	SaveSession();
+#endif /* USE_SESSIONS */
+	cleanup_files();
+    }
+#endif /* NOSIGHUP */
+    if (sig != 0) {
+	exit_immediately(EXIT_SUCCESS);
+    } else {
+	reset_signals();
+    }
+}
+
+/*
+ * Called by Interrupt handler or at quit time.  Erases the temporary files
+ * that lynx created.
+ */
+void cleanup_files(void)
+{
+    LYCleanupTemp();
+    FREE(lynx_temp_space);
+}
+
+void cleanup(void)
+{
+#ifdef VMS
+    extern BOOLEAN DidCleanup;
+#endif /* VMS */
+
+    /*
+     * Cleanup signals - just in case.  Ignore further interrupts.  - mhc: 
+     * 11/2/91
+     */
+#ifndef NOSIGHUP
+    (void) signal(SIGHUP, SIG_IGN);
+#endif /* NOSIGHUP */
+    (void) signal(SIGTERM, SIG_IGN);
+
+#ifndef VMS			/* use ttclose() from cleanup() for VMS */
+    (void) signal(SIGINT, SIG_IGN);
+#endif /* !VMS */
+
+    if (LYCursesON) {
+	LYmove(LYlines - 1, 0);
+	LYclrtoeol();
+
+	lynx_stop_all_colors();
+	LYrefresh();
+
+	stop_curses();
+    }
+#ifdef EXP_CHARTRANS_AUTOSWITCH
+    /*
+     * Currently implemented only for LINUX:  Restore original font.
+     */
+    UCChangeTerminalCodepage(-1, (LYUCcharset *) 0);
+#endif /* EXP_CHARTRANS_AUTOSWITCH */
+
+#ifdef USE_PERSISTENT_COOKIES
+    /*
+     * This can go right here for now.  We need to work up a better place
+     * to save cookies for the next release, preferably whenever a new
+     * persistent cookie is received or used.  Some sort of protocol to
+     * handle two processes writing to the cookie file needs to be worked
+     * out as well.
+     */
+    if (persistent_cookies)
+	LYStoreCookies(LYCookieSaveFile);
+#endif
+#ifdef USE_SESSIONS
+    /*
+     * Wondering is this right place and time to do it.
+     */
+    SaveSession();
+#endif /* USE_SESSIONS */
+
+    cleanup_files();
+#ifdef VMS
+    ttclose();
+    DidCleanup = TRUE;
+#endif /* VMS */
+
+    /*
+     * If we're looking at memory leaks, hang onto the trace file, since there
+     * is no memory freed in this function, and it is a nuisance to not be able
+     * to trace the cleanup activity -TD
+     */
+#ifndef LY_FIND_LEAKS
+    LYCloseTracelog();
+#endif
+}
diff --git a/src/LYClean.h b/src/LYClean.h
new file mode 100644
index 00000000..6126c2c6
--- /dev/null
+++ b/src/LYClean.h
@@ -0,0 +1,24 @@
+#ifndef LYCLEAN_H
+#define LYCLEAN_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef VMS
+    extern BOOLEAN HadVMSInterrupt;
+#endif
+
+    extern void cleanup_sig(int sig);
+    extern void cleanup(void);
+    extern void cleanup_files(void);
+    extern void set_alarm(int sig);
+    extern void reset_alarm(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYCLEAN_H */
diff --git a/src/LYCookie.c b/src/LYCookie.c
new file mode 100644
index 00000000..c549bdaa
--- /dev/null
+++ b/src/LYCookie.c
@@ -0,0 +1,2743 @@
+/*
+ * $LynxId: LYCookie.c,v 1.99 2010/04/29 23:42:23 tom Exp $
+ *
+ *			       Lynx Cookie Support		   LYCookie.c
+ *			       ===================
+ *
+ *	Author: AMK	A.M. Kuchling (amk@magnet.com)	12/25/96
+ *
+ *	Incorporated with mods by FM			01/16/97
+ *
+ *  Based on:
+ *	http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-state-mgmt-05.txt
+ *
+ *	Updated for:
+ *   http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-state-man-mec-02.txt
+ *		- FM					1997-07-09
+ *
+ *	Updated for:
+ *   ftp://ds.internic.net/internet-drafts/draft-ietf-http-state-man-mec-03.txt
+ *		- FM					1997-08-02
+ *
+ *	Partially checked against:
+ *   http://www.ietf.org/internet-drafts/draft-ietf-http-state-man-mec-10.txt
+ *		- kw					1998-12-11
+ *
+ *  TO DO: (roughly in order of decreasing priority)
+      * Persistent cookies are still experimental.  Presently cookies
+	lose many pieces of information that distinguish
+	version 1 from version 0 cookies.  There is no easy way around
+	that with the current cookie file format.  Ports are currently
+	not stored persistently at all which is clearly wrong.
+      * We currently don't do anything special for unverifiable
+	transactions to third-party hosts.
+      * We currently don't use effective host names or check for
+	Domain=.local.
+      * Hex escaping isn't considered at all.  Any semi-colons, commas,
+	or spaces actually in cookie names or values (i.e., not serving
+	as punctuation for the overall Set-Cookie value) should be hex
+	escaped if not quoted, but presumably the server is expecting
+	them to be hex escaped in our Cookie request header as well, so
+	in theory we need not unescape them.  We'll see how this works
+	out in practice.
+      * The prompt should show more information about the cookie being
+	set in Novice mode.
+      * The truncation heuristic in HTConfirmCookie should probably be
+	smarter, smart enough to leave a really short name/value string
+	alone.
+      * We protect against denial-of-service attacks (see section 6.3.1
+	of the draft) by limiting a domain to 50 cookies, limiting the
+	total number of cookies to 500, and limiting a processed cookie
+	to a maximum of 4096 bytes, but we count on the normal garbage
+	collections to bring us back down under the limits, rather than
+	actively removing cookies and/or domains based on age or frequency
+	of use.
+      * If a cookie has the secure flag set, we presently treat only SSL
+	connections as secure.  This may need to be expanded for other
+	secure communication protocols that become standardized.
+*/
+
+#include <HTUtils.h>
+#include <HTAccess.h>
+#include <HTParse.h>
+#include <HTAlert.h>
+#include <LYCurses.h>
+#include <LYUtils.h>
+#include <LYCharUtils.h>
+#include <LYClean.h>
+#include <LYGlobalDefs.h>
+#include <LYEdit.h>
+#include <LYStrings.h>
+#include <GridText.h>
+#include <LYCookie.h>
+
+#include <LYLeaks.h>
+
+/* default for new domains, one of the invcheck_behaviour_t values: */
+#define DEFAULT_INVCHECK_BV INVCHECK_QUERY
+
+#define CTrace(p) CTRACE2(TRACE_COOKIES, p)
+
+/*
+ *  The first level of the cookie list is a list indexed by the domain
+ *  string; cookies with the same domain will be placed in the same
+ *  list.  Thus, finding the cookies that apply to a given URL is a
+ *  two-level scan; first we check each domain to see if it applies,
+ *  and if so, then we check the paths of all the cookies on that
+ *  list.  We keep a running total of cookies as we add or delete
+ *  them
+ */
+static HTList *domain_list = NULL;
+static HTList *cookie_list = NULL;
+static int total_cookies = 0;
+
+struct _cookie {
+    char *lynxID;		/* Lynx cookie identifier */
+    char *name;			/* Name of this cookie */
+    char *value;		/* Value of this cookie */
+    int version;		/* Cookie protocol version (=1) */
+    char *comment;		/* Comment to show to user */
+    char *commentURL;		/* URL for comment to show to user */
+    char *domain;		/* Domain for which this cookie is valid */
+    int port;			/* Server port from which this cookie was given (usu. 80) */
+    char *PortList;		/* List of ports for which cookie can be sent */
+    char *path;			/* Path prefix for which this cookie is valid */
+    int pathlen;		/* Length of the path */
+    int flags;			/* Various flags */
+    time_t expires;		/* The time when this cookie expires */
+    BOOL quoted;		/* Was a value quoted in the Set-Cookie header? */
+};
+typedef struct _cookie cookie;
+
+#define COOKIE_FLAG_SECURE 1	/* If set, cookie requires secure links */
+#define COOKIE_FLAG_DISCARD 2	/* If set, expire at end of session */
+#define COOKIE_FLAG_EXPIRES_SET 4	/* If set, an expiry date was set */
+#define COOKIE_FLAG_DOMAIN_SET 8	/* If set, an non-default domain was set */
+#define COOKIE_FLAG_PATH_SET 16	/* If set, an non-default path was set */
+#define COOKIE_FLAG_FROM_FILE 32	/* If set, this cookie was persistent */
+
+struct _HTStream {
+    HTStreamClass *isa;
+};
+
+static void MemAllocCopy(char **dest,
+			 const char *start,
+			 const char *end)
+{
+    char *temp;
+
+    if (!(start && end) || (end <= start)) {
+	HTSACopy(dest, "");
+	return;
+    }
+
+    temp = typecallocn(char, (unsigned)(end - start) + 1);
+    if (temp == NULL)
+	outofmem(__FILE__, "MemAllocCopy");
+    LYstrncpy(temp, start, (end - start));
+    HTSACopy(dest, temp);
+    FREE(temp);
+}
+
+static cookie *newCookie(void)
+{
+    cookie *p = typecalloc(cookie);
+
+    if (p == NULL)
+	outofmem(__FILE__, "newCookie");
+
+    assert(p != NULL);
+
+    HTSprintf0(&(p->lynxID), "%p", (void *) p);
+    p->port = 80;
+    return p;
+}
+
+static void freeCookie(cookie * co)
+{
+    if (co) {
+	FREE(co->lynxID);
+	FREE(co->name);
+	FREE(co->value);
+	FREE(co->comment);
+	FREE(co->commentURL);
+	FREE(co->domain);
+	FREE(co->path);
+	FREE(co->PortList);
+	FREE(co);
+    }
+}
+
+#ifdef LY_FIND_LEAKS
+static void LYCookieJar_free(void)
+{
+    HTList *dl = domain_list;
+    domain_entry *de = NULL;
+    HTList *cl = NULL, *next = NULL;
+    cookie *co = NULL;
+
+    CTrace((tfp, "LYCookieJar_free\n"));
+    while (dl) {
+	if ((de = dl->object) != NULL) {
+	    CTrace((tfp, "...LYCookieJar_free domain %s\n", de->domain));
+	    cl = de->cookie_list;
+	    while (cl) {
+		next = cl->next;
+		co = cl->object;
+		if (co) {
+		    HTList_removeObject(de->cookie_list, co);
+		    freeCookie(co);
+		}
+		cl = next;
+	    }
+	    FREE(de->domain);
+	    HTList_delete(de->cookie_list);
+	    de->cookie_list = NULL;
+	    FREE(dl->object);
+	}
+	dl = dl->next;
+    }
+    cookie_list = NULL;
+    HTList_delete(domain_list);
+    domain_list = NULL;
+}
+#endif /* LY_FIND_LEAKS */
+
+/*
+ *  Compare two hostnames as specified in Section 2 of:
+ *   http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-state-man-mec-02.txt
+ *	- AK & FM
+ */
+static BOOLEAN host_matches(const char *A,
+			    const char *B)
+{
+    /*
+     * The following line will handle both numeric IP addresses and FQDNs.  Do
+     * numeric addresses require special handling?
+     */
+    if (*B != '.' && !strcasecomp(A, B))
+	return YES;
+
+    /*
+     * The following will pass a "dotted tail" match to "a.b.c.e" as described
+     * in Section 2 of draft-ietf-http-state-man-mec-10.txt.
+     */
+    if (*B == '.' && B[1] != '\0' && B[1] != '.' && *A != '.') {
+	int diff = (int) (strlen(A) - strlen(B));
+
+	if (diff > 0) {
+	    if (!strcasecomp((A + diff), B))
+		return YES;
+	}
+    }
+    return NO;
+}
+
+/*
+ *  Compare the current port with a port list as specified in Section 4.3 of:
+ *   http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-state-man-mec-02.txt
+ *	- FM
+ */
+static BOOLEAN port_matches(int port,
+			    const char *list)
+{
+    const char *number = list;
+
+    if (!(number && isdigit(UCH(*number))))
+	return (FALSE);
+
+    while (*number != '\0') {
+	if (atoi(number) == port) {
+	    return (TRUE);
+	}
+	while (isdigit(UCH(*number))) {
+	    number++;
+	}
+	while (*number != '\0' && !isdigit(UCH(*number))) {
+	    number++;
+	}
+    }
+
+    return (FALSE);
+}
+
+/*
+ * Returns the length of the given path ignoring trailing slashes.
+ */
+static int ignore_trailing_slash(const char *a)
+{
+    int len = (int) strlen(a);
+
+    while (len > 1 && a[len - 1] == '/')
+	--len;
+    return len;
+}
+
+/*
+ * Check if the path 'a' is a prefix of path 'b', ignoring trailing slashes
+ * in either, since they denote an empty component.
+ */
+static BOOL is_prefix(const char *a, const char *b)
+{
+    int len_a = ignore_trailing_slash(a);
+    int len_b = ignore_trailing_slash(b);
+
+    if (len_a > len_b) {
+	return FALSE;
+    } else {
+	if (strncmp(a, b, (unsigned) len_a) != 0) {
+	    return FALSE;
+	}
+	if (len_a < len_b && (len_a > 1 || a[0] != '/')) {
+	    if (b[len_a] != '\0'
+		&& b[len_a] != '/') {
+		return FALSE;
+	    }
+	}
+    }
+    return TRUE;
+}
+
+/*
+ * Find the domain-entry for the given name.
+ */
+static domain_entry *find_domain_entry(const char *name)
+{
+    HTList *hl;
+    domain_entry *de = NULL;
+
+    if (name != 0
+	&& *name != '\0') {
+	for (hl = domain_list; hl != NULL; hl = hl->next) {
+	    de = (domain_entry *) hl->object;
+	    if (de != NULL && de->domain != NULL) {
+		CTrace((tfp,
+			"...test_domain_entry(%s) bv:%u, invcheck_bv:%u\n",
+			de->domain,
+			de->bv,
+			de->invcheck_bv));
+		if (!strcasecomp(name, de->domain)) {
+		    break;
+		}
+	    }
+	    de = NULL;
+	}
+    }
+    CTrace((tfp, "find_domain_entry(%s) bv:%d, invcheck_bv:%d\n",
+	    name,
+	    de ? (int) de->bv : -1,
+	    de ? (int) de->invcheck_bv : -1));
+    return de;
+}
+
+/*
+ *  Store a cookie somewhere in the domain list. - AK & FM
+ */
+static void store_cookie(cookie * co, const char *hostname,
+			 const char *path)
+{
+    HTList *hl, *next;
+    cookie *c2;
+    time_t now = time(NULL);
+    int pos;
+    const char *ptr;
+    domain_entry *de = NULL;
+    BOOL Replacement = FALSE;
+    int invprompt_reasons = 0;	/* what is wrong with this cookie - kw */
+
+#define FAILS_COND1 0x01
+#define FAILS_COND4 0x02
+
+    if (co == NULL)
+	return;
+
+    /*
+     * Ensure that the domain list exists.
+     */
+    if (domain_list == NULL) {
+#ifdef LY_FIND_LEAKS
+	atexit(LYCookieJar_free);
+#endif
+	domain_list = HTList_new();
+	total_cookies = 0;
+    }
+
+    /*
+     * Look through domain_list to see if the cookie's domain is already
+     * listed.
+     */
+    cookie_list = NULL;
+    if ((de = find_domain_entry(co->domain)) != NULL)
+	cookie_list = de->cookie_list;
+
+    /*
+     * Apply sanity checks.
+     *
+     * Section 4.3.2, condition 1:  The value for the Path attribute is
+     * not a prefix of the request-URI.
+     *
+     * If cookie checking for this domain is set to INVCHECK_LOOSE,
+     * then we want to bypass this check.  The user should be queried
+     * if set to INVCHECK_QUERY.
+     */
+    if (!is_prefix(co->path, path)) {
+	invcheck_behaviour_t invcheck_bv = (de ? de->invcheck_bv
+					    : DEFAULT_INVCHECK_BV);
+
+	switch (invcheck_bv) {
+	case INVCHECK_LOOSE:
+	    break;		/* continue as if nothing were wrong */
+
+	case INVCHECK_QUERY:
+	    invprompt_reasons |= FAILS_COND1;
+	    break;		/* will prompt later if we get that far */
+
+	case INVCHECK_STRICT:
+	    CTrace((tfp,
+		    "store_cookie: Rejecting because '%s' is not a prefix of '%s'.\n",
+		    co->path, path));
+	    freeCookie(co);
+	    return;
+	}
+    }
+
+    /*
+     * The next 4 conditions do NOT apply if the domain is still
+     * the default of request-host. (domains - case insensitive).
+     */
+    if (strcasecomp(co->domain, hostname) != 0) {
+	/*
+	 * The hostname does not contain a dot.
+	 */
+	if (strchr(hostname, '.') == NULL) {
+	    CTrace((tfp, "store_cookie: Rejecting because '%s' has no dot.\n",
+		    hostname));
+	    freeCookie(co);
+	    return;
+	}
+
+	/*
+	 * Section 4.3.2, condition 2:  The value for the Domain attribute
+	 * contains no embedded dots or does not start with a dot.  (A dot is
+	 * embedded if it's neither the first nor last character.) Note that we
+	 * added a lead dot ourselves if a domain attribute value otherwise
+	 * qualified.  - FM
+	 */
+	if (co->domain[0] != '.' || co->domain[1] == '\0') {
+	    CTrace((tfp, "store_cookie: Rejecting domain '%s'.\n", co->domain));
+	    freeCookie(co);
+	    return;
+	}
+	ptr = strchr((co->domain + 1), '.');
+	if (ptr == NULL || ptr[1] == '\0') {
+	    CTrace((tfp, "store_cookie: Rejecting domain '%s'.\n", co->domain));
+	    freeCookie(co);
+	    return;
+	}
+
+	/*
+	 * Section 4.3.2, condition 3:  The value for the request-host does not
+	 * domain-match the Domain attribute.
+	 */
+	if (!host_matches(hostname, co->domain)) {
+	    CTrace((tfp,
+		    "store_cookie: Rejecting domain '%s' for host '%s'.\n",
+		    co->domain, hostname));
+	    freeCookie(co);
+	    return;
+	}
+
+	/*
+	 * Section 4.3.2, condition 4:  The request-host is an HDN (not IP
+	 * address) and has the form HD, where D is the value of the Domain
+	 * attribute, and H is a string that contains one or more dots.
+	 *
+	 * If cookie checking for this domain is set to INVCHECK_LOOSE, then we
+	 * want to bypass this check.  The user should be queried if set to
+	 * INVCHECK_QUERY.
+	 */
+	ptr = ((hostname + strlen(hostname)) - strlen(co->domain));
+	if (strchr(hostname, '.') < ptr) {
+	    invcheck_behaviour_t invcheck_bv = (de ? de->invcheck_bv
+						: DEFAULT_INVCHECK_BV);
+
+	    switch (invcheck_bv) {
+	    case INVCHECK_LOOSE:
+		break;		/* continue as if nothing were wrong */
+
+	    case INVCHECK_QUERY:
+		invprompt_reasons |= FAILS_COND4;
+		break;		/* will prompt later if we get that far */
+
+	    case INVCHECK_STRICT:
+		CTrace((tfp,
+			"store_cookie: Rejecting because '%s' is not a prefix of '%s'.\n",
+			co->path, path));
+		freeCookie(co);
+		return;
+	    }
+	}
+    }
+
+    /*
+     * If we found reasons for issuing an invalid cookie confirmation prompt,
+     * do that now.  Rejection by the user here is the last chance to
+     * completely ignore this cookie; after it passes this hurdle, it may at
+     * least supersede a previous cookie (even if it finally gets rejected).  -
+     * kw
+     */
+    if (invprompt_reasons) {
+	char *msg = 0;
+
+	if (invprompt_reasons & FAILS_COND4) {
+	    HTSprintf0(&msg,
+		       INVALID_COOKIE_DOMAIN_CONFIRMATION,
+		       co->domain,
+		       hostname);
+	    if (!HTForcedPrompt(cookie_noprompt, msg, NO)) {
+		CTrace((tfp,
+			"store_cookie: Rejecting domain '%s' for host '%s'.\n",
+			co->domain,
+			hostname));
+		freeCookie(co);
+		FREE(msg);
+		return;
+	    }
+	}
+	if (invprompt_reasons & FAILS_COND1) {
+	    HTSprintf0(&msg,
+		       INVALID_COOKIE_PATH_CONFIRMATION,
+		       co->path, path);
+	    if (!HTForcedPrompt(cookie_noprompt, msg, NO)) {
+		CTrace((tfp,
+			"store_cookie: Rejecting because '%s' is not a prefix of '%s'.\n",
+			co->path, path));
+		freeCookie(co);
+		FREE(msg);
+		return;
+	    }
+	}
+	FREE(msg);
+    }
+
+    if (de == NULL) {
+	/*
+	 * Domain not found; add a new entry for this domain.
+	 */
+	de = typecalloc(domain_entry);
+	if (de == NULL)
+	    outofmem(__FILE__, "store_cookie");
+
+	assert(de != NULL);
+
+	de->bv = QUERY_USER;
+	de->invcheck_bv = DEFAULT_INVCHECK_BV;	/* should this go here? */
+	cookie_list = de->cookie_list = HTList_new();
+	StrAllocCopy(de->domain, co->domain);
+	HTList_appendObject(domain_list, de);
+    }
+
+    /*
+     * Loop over the cookie list, deleting expired and matching cookies.
+     */
+    hl = cookie_list;
+    pos = 0;
+    while (hl) {
+	c2 = (cookie *) hl->object;
+	next = hl->next;
+	/*
+	 * Check if this cookie has expired.
+	 */
+	if ((c2 != NULL) &&
+	    (c2->flags & COOKIE_FLAG_EXPIRES_SET) &&
+	    c2->expires <= now) {
+	    HTList_removeObject(cookie_list, c2);
+	    freeCookie(c2);
+	    c2 = NULL;
+	    total_cookies--;
+
+	    /*
+	     * Check if this cookie matches the one we're inserting.
+	     */
+	} else if ((c2) &&
+		   !strcasecomp(co->domain, c2->domain) &&
+		   !strcmp(co->path, c2->path) &&
+		   !strcmp(co->name, c2->name)) {
+	    HTList_removeObject(cookie_list, c2);
+	    freeCookie(c2);
+	    c2 = NULL;
+	    total_cookies--;
+	    Replacement = TRUE;
+
+	} else if ((c2) && (c2->pathlen) >= (co->pathlen)) {
+	    /*
+	     * This comparison determines the (tentative) position of the new
+	     * cookie in the list such that it comes before existing cookies
+	     * with a less specific path, but after existing cookies of equal
+	     * (or greater) path length.  Thus it should normally preserve the
+	     * order of new cookies with the same path as they are received,
+	     * although this is not required.
+	     *
+	     * From RFC 2109 4.3.4:
+	     *
+	     * If multiple cookies satisfy the criteria above, they are ordered
+	     * in the Cookie header such that those with more specific Path
+	     * attributes precede those with less specific.  Ordering with
+	     * respect to other attributes (e.g., Domain) is unspecified.
+	     */
+	    pos++;
+	}
+	hl = next;
+    }
+
+    /*
+     * Don't bother to add the cookie if it's already expired.
+     */
+    if ((co->flags & COOKIE_FLAG_EXPIRES_SET) && co->expires <= now) {
+	freeCookie(co);
+	co = NULL;
+
+	/*
+	 * Don't add the cookie if we're over the domain's limit.  - FM
+	 */
+    } else if (HTList_count(cookie_list) > max_cookies_domain) {
+	CTrace((tfp,
+		"store_cookie: Domain's cookie limit exceeded!  Rejecting cookie.\n"));
+	freeCookie(co);
+	co = NULL;
+
+	/*
+	 * Don't add the cookie if we're over the total cookie limit.  - FM
+	 */
+    } else if (total_cookies > max_cookies_global) {
+	CTrace((tfp,
+		"store_cookie: Total cookie limit exceeded!  Rejecting cookie.\n"));
+	freeCookie(co);
+	co = NULL;
+
+	/*
+	 * Don't add the cookie if the value is NULL. - BJP
+	 */
+	/*
+	 * Presence of value is now needed (indicated normally by '='),
+	 * but it can now be an empty string.
+	 * - kw 1999-06-24
+	 */
+    } else if (co->value == NULL) {	/* should not happen - kw */
+	CTrace((tfp, "store_cookie: Value is NULL! Not storing cookie.\n"));
+	freeCookie(co);
+	co = NULL;
+
+	/*
+	 * If it's a replacement for a cookie that had not expired, and never
+	 * allow has not been set, add it again without confirmation.  - FM
+	 */
+    } else if ((Replacement == TRUE && de) && de->bv != REJECT_ALWAYS) {
+	HTList_insertObjectAt(cookie_list, co, pos);
+	total_cookies++;
+
+	/*
+	 * Get confirmation if we need it, and add cookie if confirmed or
+	 * 'allow' is set to always.  - FM
+	 *
+	 * Cookies read from file are accepted without confirmation prompting. 
+	 * (Prompting may actually not be possible if LYLoadCookies is called
+	 * before curses is setup.) Maybe this should instead depend on
+	 * LYSetCookies and/or LYCookieAcceptDomains and/or
+	 * LYCookieRejectDomains and/or LYAcceptAllCookies and/or some other
+	 * settings.  -kw
+	 */
+    } else if ((co->flags & COOKIE_FLAG_FROM_FILE)
+	       || HTConfirmCookie(de, hostname, co->name, co->value)) {
+	/*
+	 * Insert the new cookie so that more specific paths (longer
+	 * pathlen) come first in the list. - kw
+	 */
+	HTList_insertObjectAt(cookie_list, co, pos);
+	total_cookies++;
+    } else {
+	freeCookie(co);
+	co = NULL;
+    }
+}
+
+/*
+ *  Scan a domain's cookie_list for any cookies we should
+ *  include in a Cookie: request header. - AK & FM
+ */
+static char *scan_cookie_sublist(char *hostname,
+				 char *path,
+				 int port,
+				 HTList *sublist,
+				 char *header,
+				 BOOL secure)
+{
+    HTList *hl = sublist, *next = NULL;
+    cookie *co;
+    time_t now = time(NULL);
+    int len = 0;
+    char crlftab[8];
+
+    sprintf(crlftab, "%c%c%c", CR, LF, '\t');
+    while (hl) {
+	co = (cookie *) hl->object;
+	next = hl->next;
+
+	if ((co) &&		/* speed-up host_matches() and limit trace output */
+	    (LYstrstr(hostname, co->domain) != NULL)) {
+	    CTrace((tfp, "Checking cookie %p %s=%s\n",
+		    (void *) hl,
+		    (co->name ? co->name : "(no name)"),
+		    (co->value ? co->value : "(no value)")));
+	    CTrace((tfp, "\t%s %s %d %s %s %d%s\n",
+		    hostname,
+		    (co->domain ? co->domain : "(no domain)"),
+		    host_matches(hostname, co->domain),
+		    path, co->path,
+		    (co->pathlen > 0)
+		    ? !is_prefix(co->path, path)
+		    : 0,
+		    (co->flags & COOKIE_FLAG_SECURE)
+		    ? " secure"
+		    : ""));
+	}
+	/*
+	 * Check if this cookie has expired, and if so, delete it.
+	 */
+	if (((co) && (co->flags & COOKIE_FLAG_EXPIRES_SET)) &&
+	    co->expires <= now) {
+	    HTList_removeObject(sublist, co);
+	    freeCookie(co);
+	    co = NULL;
+	    total_cookies--;
+	}
+
+	/*
+	 * Check if we have a unexpired match, and handle if we do.
+	 */
+	if (((co != NULL) &&
+	     host_matches(hostname, co->domain)) &&
+	    (co->pathlen == 0 || is_prefix(co->path, path))) {
+	    /*
+	     * Skip if the secure flag is set and we don't have a secure
+	     * connection.  HTTP.c presently treats only SSL connections as
+	     * secure.  - FM
+	     */
+	    if ((co->flags & COOKIE_FLAG_SECURE) && secure == FALSE) {
+		hl = next;
+		continue;
+	    }
+
+	    /*
+	     * Skip if we have a port list and the current port is not listed. 
+	     * - FM
+	     */
+	    if (co->PortList && !port_matches(port, co->PortList)) {
+		hl = next;
+		continue;
+	    }
+
+	    /*
+	     * Start or append to the request header.
+	     */
+	    if (header == NULL) {
+		if (co->version > 0) {
+		    /*
+		     * For Version 1 (or greater) cookies, the version number
+		     * goes before the first cookie.
+		     */
+		    HTSprintf0(&header, "$Version=\"%d\"; ", co->version);
+		    len += (int) strlen(header);
+		}
+	    } else {
+		/*
+		 * There's already cookie data there, so add a separator
+		 * (always use a semi-colon for "backward compatibility").  -
+		 * FM
+		 */
+		StrAllocCat(header, "; ");
+		/*
+		 * Check if we should fold the header.  - FM
+		 */
+
+		/*
+		 * Section 2.2 of RFC1945 says:
+		 *
+		 * HTTP/1.0 headers may be folded onto multiple lines if each
+		 * continuation line begins with a space or horizontal tab. 
+		 * All linear whitespace, including folding, has the same
+		 * semantics as SP.  [...] However, folding of header lines is
+		 * not expected by some applications, and should not be
+		 * generated by HTTP/1.0 applications.
+		 *
+		 * This code was causing problems.  Let's not use it.  -BJP
+		 */
+
+		/* if (len > 800) { */
+		/*    StrAllocCat(header, crlftab); */
+		/*    len = 0; */
+		/* } */
+
+	    }
+	    /*
+	     * Include the cookie name=value pair.
+	     */
+	    StrAllocCat(header, co->name);
+	    StrAllocCat(header, "=");
+	    if (co->quoted) {
+		StrAllocCat(header, "\"");
+		len++;
+	    }
+	    StrAllocCat(header, co->value);
+	    if (co->quoted) {
+		StrAllocCat(header, "\"");
+		len++;
+	    }
+	    len += (int) (strlen(co->name) + strlen(co->value) + 1);
+	    /*
+	     * For Version 1 (or greater) cookies, add $PATH, $PORT and/or
+	     * $DOMAIN attributes for the cookie if they were specified via a
+	     * server reply header.  - FM
+	     */
+	    if (co->version > 0) {
+		if (co->path && (co->flags & COOKIE_FLAG_PATH_SET)) {
+		    /*
+		     * Append the path attribute.  - FM
+		     */
+		    StrAllocCat(header, "; $Path=\"");
+		    StrAllocCat(header, co->path);
+		    StrAllocCat(header, "\"");
+		    len += (int) (strlen(co->path) + 10);
+		}
+		if (co->PortList && isdigit(UCH(*co->PortList))) {
+		    /*
+		     * Append the port attribute.  - FM
+		     */
+		    StrAllocCat(header, "; $Port=\"");
+		    StrAllocCat(header, co->PortList);
+		    StrAllocCat(header, "\"");
+		    len += (int) (strlen(co->PortList) + 10);
+		}
+		if (co->domain && (co->flags & COOKIE_FLAG_DOMAIN_SET)) {
+		    /*
+		     * Append the domain attribute.  - FM
+		     */
+		    StrAllocCat(header, "; $Domain=\"");
+		    StrAllocCat(header, co->domain);
+		    StrAllocCat(header, "\"");
+		    len += (int) (strlen(co->domain) + 12);
+		}
+	    }
+	}
+	hl = next;
+    }
+
+    return (header);
+}
+
+/*
+ * Presence of value is needed (indicated normally by '=') to start a cookie,
+ * but it can be an empty string.  - kw 1999-06-24
+ */
+static char *alloc_attr_value(const char *value_start,
+			      const char *value_end)
+{
+    char *value = NULL;
+
+    if (value_start && value_end >= value_start) {
+	int value_len = (value_end - value_start);
+
+	if (value_len > max_cookies_buffer) {
+	    value_len = max_cookies_buffer;
+	}
+	value = typecallocn(char, (unsigned) value_len + 1);
+
+	if (value == NULL)
+	    outofmem(__FILE__, "LYProcessSetCookies");
+	LYstrncpy(value, value_start, value_len);
+    }
+    return value;
+}
+
+#define FLAGS_INVALID_PORT 1
+#define FLAGS_KNOWN_ATTR   2
+#define FLAGS_MAXAGE_ATTR  4
+
+#define is_attr(s, len) attr_len == len && !strncasecomp(attr_start, s, len)
+
+static unsigned parse_attribute(unsigned flags,
+				cookie * cur_cookie,
+				int *cookie_len,
+				const char *attr_start,
+				int attr_len,
+				char *value,
+				const char *address,
+				char *hostname,
+				int port)
+{
+    BOOLEAN known_attr = NO;
+    int url_type;
+
+    flags &= ~FLAGS_KNOWN_ATTR;
+    if (is_attr("secure", 6)) {
+	if (value == NULL) {
+	    known_attr = YES;
+	    if (cur_cookie != NULL) {
+		cur_cookie->flags |= COOKIE_FLAG_SECURE;
+	    }
+	} else {
+	    /*
+	     * If secure has a value, assume someone misused it as cookie name. 
+	     * - FM
+	     */
+	    known_attr = NO;
+	}
+    } else if (is_attr("discard", 7)) {
+	if (value == NULL) {
+	    known_attr = YES;
+	    if (cur_cookie != NULL) {
+		cur_cookie->flags |= COOKIE_FLAG_DISCARD;
+	    }
+	} else {
+	    /*
+	     * If discard has a value, assume someone used it as a cookie name. 
+	     * - FM
+	     */
+	    known_attr = NO;
+	}
+    } else if (is_attr("comment", 7)) {
+	known_attr = YES;
+	if (cur_cookie != NULL && value &&
+	/*
+	 * Don't process a repeat comment.  - FM
+	 */
+	    cur_cookie->comment == NULL) {
+	    StrAllocCopy(cur_cookie->comment, value);
+	    *cookie_len += (int) strlen(cur_cookie->comment);
+	}
+    } else if (is_attr("commentURL", 10)) {
+	known_attr = YES;
+	if (cur_cookie != NULL && value &&
+	/*
+	 * Don't process a repeat commentURL.  - FM
+	 */
+	    cur_cookie->commentURL == NULL) {
+	    /*
+	     * We should get only absolute URLs as values, but will resolve
+	     * versus the request's URL just in case.  - FM
+	     */
+	    cur_cookie->commentURL = HTParse(value,
+					     address,
+					     PARSE_ALL);
+	    /*
+	     * Accept only URLs for http or https servers.  - FM
+	     */
+	    if ((url_type = is_url(cur_cookie->commentURL)) &&
+		(url_type == HTTP_URL_TYPE ||
+		 url_type == HTTPS_URL_TYPE)) {
+		*cookie_len += (int) strlen(cur_cookie->commentURL);
+	    } else {
+		CTrace((tfp,
+			"LYProcessSetCookies: Rejecting commentURL value '%s'\n",
+			cur_cookie->commentURL));
+		FREE(cur_cookie->commentURL);
+	    }
+	}
+    } else if (is_attr("domain", 6)) {
+	known_attr = YES;
+	if (cur_cookie != NULL && value &&
+	/*
+	 * Don't process a repeat domain.  - FM
+	 */
+	    !(cur_cookie->flags & COOKIE_FLAG_DOMAIN_SET)) {
+	    *cookie_len -= (int) strlen(cur_cookie->domain);
+	    /*
+	     * If the value does not have a lead dot, but does have an embedded
+	     * dot, and is not an exact match to the hostname, nor is a numeric
+	     * IP address, add a lead dot.  Otherwise, use the value as is.  -
+	     * FM (domains - case insensitive).
+	     */
+	    if (value[0] != '.' && value[0] != '\0' &&
+		value[1] != '\0' && strcasecomp(value, hostname)) {
+		char *ptr = strchr(value, '.');
+
+		if (ptr != NULL && ptr[1] != '\0') {
+		    ptr = value;
+		    while (*ptr == '.' ||
+			   isdigit(UCH(*ptr)))
+			ptr++;
+		    if (*ptr != '\0') {
+			CTrace((tfp,
+				"LYProcessSetCookies: Adding lead dot for domain value '%s'\n",
+				value));
+			StrAllocCopy(cur_cookie->domain, ".");
+			StrAllocCat(cur_cookie->domain, value);
+		    } else {
+			StrAllocCopy(cur_cookie->domain, value);
+		    }
+		} else {
+		    StrAllocCopy(cur_cookie->domain, value);
+		}
+	    } else {
+		StrAllocCopy(cur_cookie->domain, value);
+	    }
+	    *cookie_len += (int) strlen(cur_cookie->domain);
+	    cur_cookie->flags |= COOKIE_FLAG_DOMAIN_SET;
+	}
+    } else if (is_attr("path", 4)) {
+	known_attr = YES;
+	if (cur_cookie != NULL && value &&
+	/*
+	 * Don't process a repeat path.  - FM
+	 */
+	    !(cur_cookie->flags & COOKIE_FLAG_PATH_SET)) {
+	    *cookie_len -= (int) strlen(cur_cookie->path);
+	    StrAllocCopy(cur_cookie->path, value);
+	    *cookie_len += (cur_cookie->pathlen = (int) strlen(cur_cookie->path));
+	    cur_cookie->flags |= COOKIE_FLAG_PATH_SET;
+	}
+    } else if (is_attr("port", 4)) {
+	if (cur_cookie != NULL && value &&
+	/*
+	 * Don't process a repeat port.  - FM
+	 */
+	    cur_cookie->PortList == NULL) {
+	    char *cp = value;
+
+	    while ((*cp != '\0') &&
+		   (isdigit(UCH(*cp)) ||
+		    *cp == ',' || *cp == ' ')) {
+		cp++;
+	    }
+	    if (*cp == '\0') {
+		if (!port_matches(port, value)) {
+		    flags |= FLAGS_INVALID_PORT;
+		} else {
+		    StrAllocCopy(cur_cookie->PortList, value);
+		    *cookie_len += (int) strlen(cur_cookie->PortList);
+		}
+		known_attr = YES;
+	    } else {
+		known_attr = NO;
+	    }
+	} else if (cur_cookie != NULL) {
+	    /*
+	     * Don't process a repeat port.  - FM
+	     */
+	    if (cur_cookie->PortList == NULL) {
+		HTSprintf0(&(cur_cookie->PortList), "%d", port);
+		*cookie_len += (int) strlen(cur_cookie->PortList);
+	    }
+	    known_attr = YES;
+	}
+    } else if (is_attr("version", 7)) {
+	known_attr = YES;
+	if (cur_cookie != NULL && value &&
+	/*
+	 * Don't process a repeat version.  - FM
+	 */
+	    cur_cookie->version < 1) {
+	    int temp = strtol(value, NULL, 10);
+
+	    if (errno != -ERANGE) {
+		cur_cookie->version = temp;
+	    }
+	}
+    } else if (is_attr("max-age", 7)) {
+	known_attr = YES;
+	if (cur_cookie != NULL && value &&
+	/*
+	 * Don't process a repeat max-age.  - FM
+	 */
+	    !(flags & FLAGS_MAXAGE_ATTR)) {
+	    int temp = strtol(value, NULL, 10);
+
+	    cur_cookie->flags |= COOKIE_FLAG_EXPIRES_SET;
+	    if (errno == -ERANGE) {
+		cur_cookie->expires = (time_t) 0;
+	    } else {
+		cur_cookie->expires = (time(NULL) + temp);
+		CTrace((tfp, "LYSetCookie: expires %" PRI_time_t ", %s",
+			CAST_time_t(cur_cookie->expires),
+			ctime(&cur_cookie->expires)));
+	    }
+	    flags |= FLAGS_MAXAGE_ATTR;
+	}
+    } else if (is_attr("expires", 7)) {
+	/*
+	 * Convert an 'expires' attribute value if we haven't received a
+	 * 'max-age'.  Note that 'expires' should not be used in Version 1
+	 * cookies, but it might be used for "backward compatibility", and, in
+	 * turn, ill-informed people surely would start using it instead of,
+	 * rather than in addition to, 'max-age'.  - FM
+	 */
+	known_attr = YES;
+	if ((cur_cookie != NULL && !(flags & FLAGS_MAXAGE_ATTR)) &&
+	    !(cur_cookie->flags & COOKIE_FLAG_EXPIRES_SET)) {
+	    if (value) {
+		cur_cookie->flags |= COOKIE_FLAG_EXPIRES_SET;
+		cur_cookie->expires = LYmktime(value, FALSE);
+		if (cur_cookie->expires > 0) {
+		    CTrace((tfp, "LYSetCookie: expires %" PRI_time_t ", %s",
+			    CAST_time_t(cur_cookie->expires),
+			    ctime(&cur_cookie->expires)));
+		}
+	    }
+	}
+    }
+    if (known_attr)
+	flags |= FLAGS_KNOWN_ATTR;
+    return flags;
+}
+
+/*
+ *  Process potentially concatenated Set-Cookie2 and/or Set-Cookie
+ *  headers. - FM
+ */
+static void LYProcessSetCookies(const char *SetCookie,
+				const char *SetCookie2,
+				const char *address,
+				char *hostname,
+				char *path,
+				int port)
+{
+    const char *p, *attr_start, *attr_end, *value_start, *value_end;
+    HTList *CombinedCookies = NULL, *cl = NULL;
+    cookie *cur_cookie = NULL, *co = NULL;
+    int cookie_len = 0;
+    int NumCookies = 0;
+    BOOL Quoted = FALSE;
+    unsigned parse_flags = 0;
+
+    if (isEmpty(SetCookie) &&
+	isEmpty(SetCookie2)) {
+	/*
+	 * Yuk!  Garbage in, so nothing out.  - FM
+	 */
+	return;
+    }
+
+    /*
+     * If we have both Set-Cookie and Set-Cookie2 headers.  process the
+     * Set-Cookie2 header.  Otherwise, process whichever of the two headers we
+     * do have.  Note that if more than one instance of a valued attribute for
+     * the same cookie is encountered, the value for the first instance is
+     * retained.  We only accept up to 50 cookies from the header, and only if
+     * a cookie's values do not exceed the 4096 byte limit on overall size.  -
+     * FM
+     */
+    CombinedCookies = HTList_new();
+
+    /*
+     * Process the Set-Cookie2 header, if present and not zero-length, adding
+     * each cookie to the CombinedCookies list.  - FM
+     */
+    p = NonNull(SetCookie2);
+    if (SetCookie && *p) {
+	CTrace((tfp, "LYProcessSetCookies: Using Set-Cookie2 header.\n"));
+    }
+    while (NumCookies <= max_cookies_domain && *p) {
+	value_start = value_end = NULL;
+	p = LYSkipCBlanks(p);
+	/*
+	 * Get the attribute name.
+	 */
+	attr_start = p;
+	while (*p != '\0' && !isspace(UCH(*p)) &&
+	       *p != '=' && *p != ';' && *p != ',')
+	    p++;
+	attr_end = p;
+	p = LYSkipCBlanks(p);
+
+	/*
+	 * Check for an '=' delimiter, or an 'expires' name followed by white,
+	 * since Netscape's bogus parser doesn't require an '=' delimiter, and
+	 * 'expires' attributes are being encountered without them.  These
+	 * shouldn't be in a Set-Cookie2 header, but we'll assume it's an
+	 * expires attribute rather a cookie with that name, since the
+	 * attribute mistake rather than name mistake seems more likely to be
+	 * made by providers.  - FM
+	 */
+	if (*p == '=' ||
+	    !strncasecomp(attr_start, "Expires", 7)) {
+	    /*
+	     * Get the value string.
+	     */
+	    if (*p == '=') {
+		p++;
+	    }
+	    p = LYSkipCBlanks(p);
+	    /*
+	     * Hack alert!  We must handle Netscape-style cookies with
+	     *          "Expires=Mon, 01-Jan-96 13:45:35 GMT" or
+	     *          "Expires=Mon,  1 Jan 1996 13:45:35 GMT".
+	     * No quotes, but there are spaces.  Argh...  Anyway, we know it
+	     * will have at least 3 space separators within it, and two dashes
+	     * or two more spaces, so this code looks for a space after the 5th
+	     * space separator or dash to mark the end of the value.  - FM
+	     */
+	    if ((attr_end - attr_start) == 7 &&
+		!strncasecomp(attr_start, "Expires", 7)) {
+		int spaces = 6;
+
+		value_start = p;
+		if (isdigit(UCH(*p))) {
+		    /*
+		     * No alphabetic day field.  - FM
+		     */
+		    spaces--;
+		} else {
+		    /*
+		     * Skip the alphabetic day field.  - FM
+		     */
+		    while (*p != '\0' && isalpha(UCH(*p))) {
+			p++;
+		    }
+		    while (*p == ',' || isspace(UCH(*p))) {
+			p++;
+		    }
+		    spaces--;
+		}
+		while (*p != '\0' && *p != ';' && *p != ',' && spaces) {
+		    p++;
+		    if (isspace(UCH(*p))) {
+			while (isspace(UCH(*(p + 1))))
+			    p++;
+			spaces--;
+		    } else if (*p == '-') {
+			spaces--;
+		    }
+		}
+		value_end = p;
+		/*
+		 * Hack Alert!  The port attribute can take a comma separated
+		 * list of numbers as a value, and such values should be
+		 * quoted, but if not, make sure we don't treat a number in the
+		 * list as the start of a new cookie.  - FM
+		 */
+	    } else if ((attr_end - attr_start) == 4 &&
+		       !strncasecomp(attr_start, "port", 4) &&
+		       isdigit(UCH(*p))) {
+		/*
+		 * The value starts as an unquoted number.
+		 */
+		const char *cp, *cp1;
+
+		value_start = p;
+		while (1) {
+		    while (isdigit(UCH(*p)))
+			p++;
+		    value_end = p;
+		    p = LYSkipCBlanks(p);
+		    if (*p == '\0' || *p == ';')
+			break;
+		    if (*p == ',') {
+			cp = LYSkipCBlanks(p + 1);
+			if (*cp != '\0' && isdigit(UCH(*cp))) {
+			    cp1 = cp;
+			    while (isdigit(UCH(*cp1)))
+				cp1++;
+			    cp1 = LYSkipCBlanks(cp1);
+			    if (*cp1 == '\0' || *cp1 == ',' || *cp1 == ';') {
+				p = cp;
+				continue;
+			    }
+			}
+		    }
+		    while (*p != '\0' && *p != ';' && *p != ',')
+			p++;
+		    value_end = p;
+		    /*
+		     * Trim trailing spaces.
+		     */
+		    if ((value_end > value_start) &&
+			isspace(UCH(*(value_end - 1)))) {
+			value_end--;
+			while ((value_end > (value_start + 1)) &&
+			       isspace(UCH(*value_end)) &&
+			       isspace(UCH(*(value_end - 1)))) {
+			    value_end--;
+			}
+		    }
+		    break;
+		}
+	    } else if (*p == '"') {
+		BOOLEAN escaped = FALSE;
+
+		/*
+		 * It looks like quoted string.
+		 */
+		p++;
+		value_start = p;
+		while (*p != '\0' && (*p != '"' || escaped)) {
+		    escaped = (BOOL) (!escaped && *p == '\\');
+		    p++;
+		}
+		if (p != value_start && *p == '"' && !escaped) {
+		    value_end = p;
+		    p++;
+		    Quoted = TRUE;
+		} else {
+		    value_start--;
+		    value_end = p;
+		    if (*p)
+			p++;
+		    Quoted = FALSE;
+		}
+	    } else {
+		/*
+		 * Otherwise, it's an unquoted string.
+		 */
+		value_start = p;
+		while (*p != '\0' && *p != ';' && *p != ',')
+		    p++;
+		value_end = p;
+		/*
+		 * Trim trailing spaces.
+		 */
+		if ((value_end > value_start) &&
+		    isspace(UCH(*(value_end - 1)))) {
+		    value_end--;
+		    while ((value_end > (value_start + 1)) &&
+			   isspace(UCH(*value_end)) &&
+			   isspace(UCH(*(value_end - 1)))) {
+			value_end--;
+		    }
+		}
+	    }
+	}
+
+	/*
+	 * Check for a separator character, and skip it.
+	 */
+	if (*p == ';' || *p == ',')
+	    p++;
+
+	/*
+	 * Now, we can handle this attribute/value pair.
+	 */
+	if (attr_end > attr_start) {
+	    char *value = alloc_attr_value(value_start, value_end);
+
+	    parse_flags = parse_attribute(parse_flags,
+					  cur_cookie,
+					  &cookie_len,
+					  attr_start,
+					  (attr_end - attr_start),
+					  value,
+					  address,
+					  hostname,
+					  port);
+
+	    /*
+	     * Presence of value is needed (indicated normally by '='),
+	     * but it can be an empty string. - kw 1999-06-24
+	     */
+	    if (!(parse_flags & FLAGS_KNOWN_ATTR)
+		&& value
+		&& value_end >= value_start) {
+		/*
+		 * If we've started a cookie, and it's not too big, save it in
+		 * the CombinedCookies list.  - FM
+		 */
+		if (cookie_len <= max_cookies_buffer
+		    && cur_cookie != NULL
+		    && !(parse_flags & FLAGS_INVALID_PORT)) {
+		    /*
+		     * Assume version 1 if not set to that or higher.  - FM
+		     */
+		    if (cur_cookie->version < 1) {
+			cur_cookie->version = 1;
+		    }
+		    HTList_appendObject(CombinedCookies, cur_cookie);
+		} else if (cur_cookie != NULL) {
+		    CTrace((tfp,
+			    "LYProcessSetCookies: Rejecting Set-Cookie2: %s=%s\n",
+			    (cur_cookie->name ?
+			     cur_cookie->name : "[no name]"),
+			    (cur_cookie->value ?
+			     cur_cookie->value : "[no value]")));
+		    CTrace((tfp,
+			    (parse_flags & FLAGS_INVALID_PORT) ?
+			    "                     due to excessive length!\n"
+			    : "                     due to invalid port!\n"));
+		    if (parse_flags & FLAGS_INVALID_PORT) {
+			NumCookies--;
+		    }
+		    freeCookie(cur_cookie);
+		    cur_cookie = NULL;
+		}
+		/*
+		 * Start a new cookie.  - FM
+		 */
+		cur_cookie = newCookie();
+		cookie_len = 0;
+		NumCookies++;
+		MemAllocCopy(&(cur_cookie->name), attr_start, attr_end);
+		cookie_len += (int) strlen(cur_cookie->name);
+		MemAllocCopy(&(cur_cookie->value), value_start, value_end);
+		cookie_len += (int) strlen(cur_cookie->value);
+		StrAllocCopy(cur_cookie->domain, hostname);
+		cookie_len += (int) strlen(cur_cookie->domain);
+		StrAllocCopy(cur_cookie->path, path);
+		cookie_len += (cur_cookie->pathlen = (int) strlen(cur_cookie->path));
+		cur_cookie->port = port;
+		parse_flags = 0;
+		cur_cookie->quoted = TRUE;
+	    }
+	    FREE(value);
+	}
+    }
+
+    /*
+     * Add any final SetCookie2 cookie to the CombinedCookie list if we are
+     * within the length limit.  - FM
+     */
+    if (NumCookies <= max_cookies_domain
+	&& cookie_len <= max_cookies_buffer
+	&& cur_cookie != NULL && !(parse_flags & FLAGS_INVALID_PORT)) {
+	if (cur_cookie->version < 1) {
+	    cur_cookie->version = 1;
+	}
+	HTList_appendObject(CombinedCookies, cur_cookie);
+    } else if (cur_cookie != NULL && !(parse_flags & FLAGS_INVALID_PORT)) {
+	CTrace((tfp, "LYProcessSetCookies: Rejecting Set-Cookie2: %s=%s\n",
+		(cur_cookie->name ? cur_cookie->name : "[no name]"),
+		(cur_cookie->value ? cur_cookie->value : "[no value]")));
+	CTrace((tfp, "                     due to excessive %s%s%s\n",
+		(cookie_len > max_cookies_buffer ? "length" : ""),
+		(cookie_len > max_cookies_buffer &&
+		 NumCookies > max_cookies_domain
+		 ? " and "
+		 : ""),
+		(NumCookies > max_cookies_domain ? "number!\n" : "!\n")));
+	freeCookie(cur_cookie);
+	cur_cookie = NULL;
+    } else if (cur_cookie != NULL) {	/* invalidport */
+	CTrace((tfp, "LYProcessSetCookies: Rejecting Set-Cookie2: %s=%s\n",
+		(cur_cookie->name ? cur_cookie->name : "[no name]"),
+		(cur_cookie->value ? cur_cookie->value : "[no value]")));
+	CTrace((tfp, "                     due to invalid port!\n"));
+	NumCookies--;
+	freeCookie(cur_cookie);
+	cur_cookie = NULL;
+    }
+
+    /*
+     * Process the Set-Cookie header, if no non-zero-length Set-Cookie2 header
+     * was present.  - FM
+     */
+    cookie_len = 0;
+    NumCookies = 0;
+    cur_cookie = NULL;
+    p = ((SetCookie && isEmpty(SetCookie2)) ? SetCookie : "");
+    if (SetCookie2 && *p) {
+	CTrace((tfp, "LYProcessSetCookies: Using Set-Cookie header.\n"));
+    }
+    while (NumCookies <= max_cookies_domain && *p) {
+	value_start = value_end = NULL;
+	p = LYSkipCBlanks(p);
+	/*
+	 * Get the attribute name.
+	 */
+	attr_start = p;
+	while (*p != '\0' && !isspace(UCH(*p)) &&
+	       *p != '=' && *p != ';' && *p != ',')
+	    p++;
+	attr_end = p;
+	p = LYSkipCBlanks(p);
+
+	/*
+	 * Check for an '=' delimiter, or an 'expires' name followed by white,
+	 * since Netscape's bogus parser doesn't require an '=' delimiter, and
+	 * 'expires' attributes are being encountered without them.  - FM
+	 */
+	if (*p == '=' ||
+	    !strncasecomp(attr_start, "Expires", 7)) {
+	    /*
+	     * Get the value string.
+	     */
+	    if (*p == '=') {
+		p++;
+	    }
+	    p = LYSkipCBlanks(p);
+	    /*
+	     * Hack alert!  We must handle Netscape-style cookies with
+	     *          "Expires=Mon, 01-Jan-96 13:45:35 GMT" or
+	     *          "Expires=Mon,  1 Jan 1996 13:45:35 GMT".
+	     * No quotes, but there are spaces.  Argh...  Anyway, we know it
+	     * will have at least 3 space separators within it, and two dashes
+	     * or two more spaces, so this code looks for a space after the 5th
+	     * space separator or dash to mark the end of the value.  - FM
+	     */
+	    if ((attr_end - attr_start) == 7 &&
+		!strncasecomp(attr_start, "Expires", 7)) {
+		int spaces = 6;
+
+		value_start = p;
+		if (isdigit(UCH(*p))) {
+		    /*
+		     * No alphabetic day field.  - FM
+		     */
+		    spaces--;
+		} else {
+		    /*
+		     * Skip the alphabetic day field.  - FM
+		     */
+		    while (*p != '\0' && isalpha(UCH(*p))) {
+			p++;
+		    }
+		    while (*p == ',' || isspace(UCH(*p))) {
+			p++;
+		    }
+		    spaces--;
+		}
+		while (*p != '\0' && *p != ';' && *p != ',' && spaces) {
+		    p++;
+		    if (isspace(UCH(*p))) {
+			while (isspace(UCH(*(p + 1))))
+			    p++;
+			spaces--;
+		    } else if (*p == '-') {
+			spaces--;
+		    }
+		}
+		value_end = p;
+		/*
+		 * Hack Alert!  The port attribute can take a comma separated
+		 * list of numbers as a value, and such values should be
+		 * quoted, but if not, make sure we don't treat a number in the
+		 * list as the start of a new cookie.  - FM
+		 */
+	    } else if ((attr_end - attr_start) == 4 &&
+		       !strncasecomp(attr_start, "port", 4) &&
+		       isdigit(UCH(*p))) {
+		/*
+		 * The value starts as an unquoted number.
+		 */
+		const char *cp, *cp1;
+
+		value_start = p;
+		while (1) {
+		    while (isdigit(UCH(*p)))
+			p++;
+		    value_end = p;
+		    p = LYSkipCBlanks(p);
+		    if (*p == '\0' || *p == ';')
+			break;
+		    if (*p == ',') {
+			cp = LYSkipCBlanks(p + 1);
+			if (*cp != '\0' && isdigit(UCH(*cp))) {
+			    cp1 = cp;
+			    while (isdigit(UCH(*cp1)))
+				cp1++;
+			    cp1 = LYSkipCBlanks(cp1);
+			    if (*cp1 == '\0' || *cp1 == ',' || *cp1 == ';') {
+				p = cp;
+				continue;
+			    }
+			}
+		    }
+		    while (*p != '\0' && *p != ';' && *p != ',')
+			p++;
+		    value_end = p;
+		    /*
+		     * Trim trailing spaces.
+		     */
+		    if ((value_end > value_start) &&
+			isspace(UCH(*(value_end - 1)))) {
+			value_end--;
+			while ((value_end > (value_start + 1)) &&
+			       isspace(UCH(*value_end)) &&
+			       isspace(UCH(*(value_end - 1)))) {
+			    value_end--;
+			}
+		    }
+		    break;
+		}
+	    } else if (*p == '"') {
+		BOOLEAN escaped = FALSE;
+
+		/*
+		 * It looks like quoted string.
+		 */
+		p++;
+		value_start = p;
+		while (*p != '\0' && (*p != '"' || escaped)) {
+		    escaped = (BOOL) (!escaped && *p == '\\');
+		    p++;
+		}
+		if (p != value_start && *p == '"' && !escaped) {
+		    value_end = p;
+		    p++;
+		    Quoted = TRUE;
+		} else {
+		    value_start--;
+		    value_end = p;
+		    if (*p)
+			p++;
+		    Quoted = FALSE;
+		}
+	    } else {
+		/*
+		 * Otherwise, it's an unquoted string.
+		 */
+		value_start = p;
+		while (*p != '\0' && *p != ';' && *p != ',')
+		    p++;
+		value_end = p;
+		/*
+		 * Trim trailing spaces.
+		 */
+		if ((value_end > value_start) &&
+		    isspace(UCH(*(value_end - 1)))) {
+		    value_end--;
+		    while ((value_end > (value_start + 1)) &&
+			   isspace(UCH(*value_end)) &&
+			   isspace(UCH(*(value_end - 1)))) {
+			value_end--;
+		    }
+		}
+	    }
+	}
+
+	/*
+	 * Check for a separator character, and skip it.
+	 */
+	if (*p == ';' || *p == ',')
+	    p++;
+
+	/*
+	 * Now, we can handle this attribute/value pair.
+	 */
+	if (attr_end > attr_start) {
+	    char *value = alloc_attr_value(value_start, value_end);
+
+	    parse_flags = parse_attribute(parse_flags,
+					  cur_cookie,
+					  &cookie_len,
+					  attr_start,
+					  (attr_end - attr_start),
+					  value,
+					  address,
+					  hostname,
+					  port);
+
+	    /*
+	     * Presence of value is needed (indicated normally by '='),
+	     * but it can be an empty string. - kw 1999-06-24
+	     */
+	    if (!(parse_flags & FLAGS_KNOWN_ATTR)
+		&& value
+		&& value_end >= value_start) {
+		/*
+		 * If we've started a cookie, and it's not too big, save it in
+		 * the CombinedCookies list.  - FM
+		 */
+		if (cookie_len <= max_cookies_buffer
+		    && cur_cookie != NULL) {
+		    /*
+		     * If we had a Set-Cookie2 header, make sure the version is
+		     * at least 1, and mark it for quoting.  - FM
+		     */
+		    if (SetCookie2 != NULL) {
+			if (cur_cookie->version < 1) {
+			    cur_cookie->version = 1;
+			}
+			cur_cookie->quoted = TRUE;
+		    }
+		    HTList_appendObject(CombinedCookies, cur_cookie);
+		} else if (cur_cookie != NULL) {
+		    CTrace((tfp,
+			    "LYProcessSetCookies: Rejecting Set-Cookie: %s=%s\n",
+			    (cur_cookie->name ?
+			     cur_cookie->name : "[no name]"),
+			    (cur_cookie->value ?
+			     cur_cookie->value : "[no value]")));
+		    CTrace((tfp,
+			    "                     due to excessive length!\n"));
+		    freeCookie(cur_cookie);
+		    cur_cookie = NULL;
+		}
+		/*
+		 * Start a new cookie.  - FM
+		 */
+		cur_cookie = newCookie();
+		NumCookies++;
+		cookie_len = 0;
+		MemAllocCopy(&(cur_cookie->name), attr_start, attr_end);
+		cookie_len += (int) strlen(cur_cookie->name);
+		MemAllocCopy(&(cur_cookie->value), value_start, value_end);
+		cookie_len += (int) strlen(cur_cookie->value);
+		StrAllocCopy(cur_cookie->domain, hostname);
+		cookie_len += (int) strlen(cur_cookie->domain);
+		StrAllocCopy(cur_cookie->path, path);
+		cookie_len += (cur_cookie->pathlen = (int) strlen(cur_cookie->path));
+		cur_cookie->port = port;
+		parse_flags = 0;
+		cur_cookie->quoted = Quoted;
+		Quoted = FALSE;
+	    }
+	    FREE(value);
+	}
+    }
+
+    /*
+     * Handle the final Set-Cookie cookie if within length limit.  - FM
+     */
+    if (NumCookies <= max_cookies_domain
+	&& cookie_len <= max_cookies_buffer
+	&& cur_cookie != NULL) {
+	if (SetCookie2 != NULL) {
+	    if (cur_cookie->version < 1) {
+		cur_cookie->version = 1;
+	    }
+	    cur_cookie->quoted = TRUE;
+	}
+	HTList_appendObject(CombinedCookies, cur_cookie);
+    } else if (cur_cookie != NULL) {
+	CTrace((tfp, "LYProcessSetCookies: Rejecting Set-Cookie: %s=%s\n",
+		(cur_cookie->name ? cur_cookie->name : "[no name]"),
+		(cur_cookie->value ? cur_cookie->value : "[no value]")));
+	CTrace((tfp, "                     due to excessive %s%s%s\n",
+		(cookie_len > max_cookies_buffer ? "length" : ""),
+		(cookie_len > max_cookies_buffer && NumCookies > max_cookies_domain
+		 ? " and "
+		 : ""),
+		(NumCookies > max_cookies_domain ? "number!\n" : "!\n")));
+	freeCookie(cur_cookie);
+	cur_cookie = NULL;
+    }
+
+    /*
+     * OK, now we can actually store any cookies in the CombinedCookies list. 
+     * - FM
+     */
+    cl = CombinedCookies;
+    while (NULL != (co = (cookie *) HTList_nextObject(cl))) {
+	CTrace((tfp, "LYProcessSetCookie: attr=value pair: '%s=%s'\n",
+		(co->name ? co->name : "[no name]"),
+		(co->value ? co->value : "[no value]")));
+	if (co->expires > 0) {
+	    CTrace((tfp, "                    expires: %" PRI_time_t ", %s\n",
+		    CAST_time_t(co->expires),
+		    ctime(&co->expires)));
+	}
+	if (isHTTPS_URL(address) &&
+	    LYForceSSLCookiesSecure == TRUE &&
+	    !(co->flags & COOKIE_FLAG_SECURE)) {
+	    co->flags |= COOKIE_FLAG_SECURE;
+	    CTrace((tfp, "                    Forced the 'secure' flag on.\n"));
+	}
+	store_cookie(co, hostname, path);
+    }
+    HTList_delete(CombinedCookies);
+    CombinedCookies = NULL;
+
+    return;
+}
+
+/*
+ *  Entry function for handling Set-Cookie: and/or Set-Cookie2:
+ *  reply headers.   They may have been concatenated as comma
+ *  separated lists in HTTP.c or HTMIME.c. - FM
+ */
+void LYSetCookie(const char *SetCookie,
+		 const char *SetCookie2,
+		 const char *address)
+{
+    BOOL BadHeaders = FALSE;
+    char *hostname = NULL, *path = NULL, *ptr;
+    int port = 80;
+
+    /*
+     * Get the hostname, port and path of the address, and report the
+     * Set-Cookie and/or Set-Cookie2 header(s) if trace mode is on, but set the
+     * cookie(s) only if LYSetCookies is TRUE.  - FM
+     */
+    if (((hostname = HTParse(address, "", PARSE_HOST)) != NULL) &&
+	(ptr = strchr(hostname, ':')) != NULL) {
+	/*
+	 * Replace default port number.
+	 */
+	*ptr = '\0';
+	ptr++;
+	port = atoi(ptr);
+    } else if (isHTTPS_URL(address)) {
+	port = 443;
+    }
+
+    /*
+     * Get the path from the request URI.
+     */
+    if ((path = HTParse(address, "", PARSE_PATH | PARSE_PUNCTUATION)) != NULL) {
+	/*
+	 * Trim off any parameters to provide something that we can compare
+	 * against the cookie's path for verifying if it has the proper prefix.
+	 */
+	if ((ptr = strchr(path, '?')) != NULL) {
+	    CTrace((tfp, "discarding params \"%s\" in request URI\n", ptr));
+	    *ptr = '\0';
+	}
+	/* trim a trailing slash, unless we have only a "/" */
+	if ((ptr = strrchr(path, '/')) != NULL &&
+	    (ptr != path) &&
+	    ptr[1] == '\0') {
+	    CTrace((tfp, "discarding trailing \"/\" in request URI\n"));
+	    *ptr = '\0';
+	}
+    }
+
+    if (isEmpty(SetCookie) &&
+	isEmpty(SetCookie2)) {
+	/*
+	 * Yuk, something must have gone wrong in HTMIME.c or HTTP.c because
+	 * both SetCookie and SetCookie2 are NULL or zero-length.  - FM
+	 */
+	BadHeaders = TRUE;
+    }
+    CTrace((tfp, "LYSetCookie called with host '%s', path '%s',\n",
+	    NonNull(hostname),
+	    NonNull(path)));
+    if (SetCookie) {
+	CTrace((tfp, "    and Set-Cookie: '%s'\n", SetCookie));
+    }
+    if (SetCookie2) {
+	CTrace((tfp, "    and Set-Cookie2: '%s'\n", SetCookie2));
+    }
+    if (LYSetCookies == FALSE || BadHeaders == TRUE) {
+	CTrace((tfp, "    Ignoring this Set-Cookie/Set-Cookie2 request.\n"));
+    }
+
+    /*
+     * We're done if LYSetCookies is off or we have bad headers.  - FM
+     */
+    if (LYSetCookies == FALSE || BadHeaders == TRUE) {
+	FREE(hostname);
+	FREE(path);
+	return;
+    }
+
+    /*
+     * Process the header(s).
+     */
+    LYProcessSetCookies(SetCookie, SetCookie2, address, hostname, path, port);
+    FREE(hostname);
+    FREE(path);
+    return;
+}
+
+/*
+ *  Entry function from creating a Cookie: request header
+ *  if needed. - AK & FM
+ */
+char *LYAddCookieHeader(char *hostname,
+			char *path,
+			int port,
+			BOOL secure)
+{
+    char *header = NULL;
+    HTList *hl = domain_list, *next = NULL;
+    domain_entry *de;
+
+    CTrace((tfp, "LYCookie: Searching for '%s:%d', '%s'.\n",
+	    NONNULL(hostname),
+	    port,
+	    NONNULL(path)));
+
+    /*
+     * Search the cookie_list elements in the domain_list for any cookies
+     * associated with the //hostname:port/path
+     */
+    while (hl) {
+	de = (domain_entry *) hl->object;
+	next = hl->next;
+
+	if (de != NULL) {
+	    if (!HTList_isEmpty(de->cookie_list)) {
+		/*
+		 * Scan the domain's cookie_list for any cookies we should
+		 * include in our request header.
+		 */
+		header = scan_cookie_sublist(hostname, path, port,
+					     de->cookie_list, header, secure);
+	    } else if (de->bv == QUERY_USER && de->invcheck_bv == DEFAULT_INVCHECK_BV) {
+		/*
+		 * No cookies in this domain, and no default accept/reject
+		 * choice was set by the user, so delete the domain.  - FM
+		 */
+		FREE(de->domain);
+		HTList_delete(de->cookie_list);
+		de->cookie_list = NULL;
+		HTList_removeObject(domain_list, de);
+		FREE(de);
+	    }
+	}
+	hl = next;
+    }
+    if (header)
+	return (header);
+
+    return (NULL);
+}
+
+#ifdef USE_PERSISTENT_COOKIES
+static int number_of_file_cookies = 0;
+
+/* rjp - cookie loading */
+void LYLoadCookies(char *cookie_file)
+{
+    FILE *cookie_handle;
+    char *buf = NULL;
+    static char domain[256], path[LY_MAXPATH], name[256], value[4100];
+    static char what[8], secure[8], expires_a[16];
+    /* *INDENT-OFF* */
+    static struct {
+	char *s;
+	size_t n;
+    } tok_values[] = {
+	{ domain,	sizeof(domain) },
+	{ what,		sizeof(what) },
+	{ path,		sizeof(path) },
+	{ secure,	sizeof(secure) },
+	{ expires_a,	sizeof(expires_a) },
+	{ name,		sizeof(name) },
+	{ value,	sizeof(value) },
+	{ NULL, 0 }
+	};
+    /* *INDENT-ON* */
+
+    time_t expires;
+
+    cookie_handle = fopen(cookie_file, TXT_R);
+    if (!cookie_handle)
+	return;
+
+    CTrace((tfp, "LYLoadCookies: reading cookies from %s\n", cookie_file));
+
+    number_of_file_cookies = 0;
+    while (LYSafeGets(&buf, cookie_handle) != 0) {
+	cookie *moo;
+	int tok_loop;
+	char *tok_out, *tok_ptr;
+
+	LYTrimNewline(buf);
+	if (buf[0] == '\0' || buf[0] == '#') {
+	    continue;
+	}
+
+	number_of_file_cookies++;
+
+	strcat(buf, "\t");	/* add sep after line if enough space - kw */
+
+	/*
+	 * Tokenise the cookie line into its component parts -
+	 * this only works for Netscape style cookie files at the
+	 * moment.  It may be worth investigating an alternative
+	 * format for Lynx because the Netscape format isn't all
+	 * that useful, or future-proof. - RP
+	 *
+	 * 'fixed' by using strsep instead of strtok.  No idea
+	 * what kind of platform problems this might introduce. - RP
+	 */
+	/*
+	 * This fails when the path is blank
+	 *
+	 * sscanf(buf, "%s\t%s\t%s\t%s\t%d\t%s\t%[ -~]",
+	 *  domain, what, path, secure, &expires, name, value);
+	 */
+	CTrace((tfp, "LYLoadCookies: tokenising %s\n", buf));
+	tok_ptr = buf;
+	tok_out = LYstrsep(&tok_ptr, "\t");
+	for (tok_loop = 0; tok_out && tok_values[tok_loop].s; tok_loop++) {
+	    CTrace((tfp, "\t%d:[%03d]:[%s]\n",
+		    tok_loop, (int) (tok_out - buf), tok_out));
+	    LYstrncpy(tok_values[tok_loop].s,
+		      tok_out,
+		      (int) tok_values[tok_loop].n);
+	    /*
+	     * It looks like strtok ignores a leading delimiter,
+	     * which makes things a bit more interesting.  Something
+	     * like "FALSE\t\tFALSE\t" translates to FALSE,FALSE
+	     * instead of FALSE,,FALSE. - RP
+	     */
+	    tok_out = LYstrsep(&tok_ptr, "\t");
+	}
+
+	if (tok_values[tok_loop].s) {
+	    /* tok_out in above loop must have been NULL prematurely - kw */
+	    CTrace((tfp,
+		    "*** wrong format: not enough tokens, ignoring line!\n"));
+	    continue;
+	}
+
+	expires = atol(expires_a);
+	CTrace((tfp, "expires:\t%s\n", ctime(&expires)));
+	moo = newCookie();
+	StrAllocCopy(moo->domain, domain);
+	StrAllocCopy(moo->path, path);
+	StrAllocCopy(moo->name, name);
+	if (value[0] == '"' &&
+	    value[1] && value[strlen(value) - 1] == '"' &&
+	    value[strlen(value) - 2] != '\\') {
+	    value[strlen(value) - 1] = '\0';
+	    StrAllocCopy(moo->value, value + 1);
+	    moo->quoted = TRUE;
+	} else {
+	    StrAllocCopy(moo->value, value);
+	}
+	moo->pathlen = (int) strlen(moo->path);
+	/*
+	 *  Justification for following flags:
+	 *  COOKIE_FLAG_FROM_FILE    So we know were it comes from.
+	 *  COOKIE_FLAG_EXPIRES_SET  It must have had an explicit
+	 *                           expiration originally, otherwise
+	 *                           it would not be in the file.
+	 *  COOKIE_FLAG_DOMAIN_SET,  We don't know whether these were
+	 *   COOKIE_FLAG_PATH_SET    explicit or implicit, but this
+	 *                           only matters for sending version 1
+	 *                           cookies; the cookies read from the
+	 *                           file are currently treated all like
+	 *                           version 0 (we don't set moo->version)
+	 *                           so $Domain= and $Path= will normally
+	 *                           not be sent to the server.  But if
+	 *                           these cookies somehow get mixed with
+	 *                           new version 1 cookies we may end up
+	 *                           sending version 1 to the server, and
+	 *                           in that case we should send $Domain
+	 *                           and $Path.  The state-man-mec drafts
+	 *                           and RFC 2109 say that $Domain and
+	 *                           $Path SHOULD be omitted if they were
+	 *                           not given explicitly, but not that
+	 *                           they MUST be omitted.
+	 *                           See 8.2 Cookie Spoofing in draft -10
+	 *                           for a good reason to send them.
+	 *                           However, an explicit domain should be
+	 *                           now prefixed with a dot (unless it is
+	 *                           for a single host), so we check for
+	 *                           that.
+	 *  COOKIE_FLAG_SECURE       Should have "FALSE" for normal,
+	 *                           otherwise set it.
+	 */
+	moo->flags |= COOKIE_FLAG_FROM_FILE | COOKIE_FLAG_EXPIRES_SET |
+	    COOKIE_FLAG_PATH_SET;
+	if (domain[0] == '.')
+	    moo->flags |= COOKIE_FLAG_DOMAIN_SET;
+	if (secure[0] != 'F')
+	    moo->flags |= COOKIE_FLAG_SECURE;
+	/* @@@ Should we set port to 443 if secure is set? @@@ */
+	moo->expires = expires;
+	/*
+	 * I don't like using this to store the cookies because it's
+	 * designed to store cookies that have been received from an
+	 * HTTP request, not from a persistent cookie jar.  Hence the
+	 * mucking about with the COOKIE_FLAG_FROM_FILE above. - RP
+	 */
+	store_cookie(moo, domain, path);
+    }
+    LYCloseInput(cookie_handle);
+}
+
+static FILE *NewCookieFile(char *cookie_file)
+{
+    CTrace((tfp, "LYStoreCookies: save cookies to %s on exit\n", cookie_file));
+    return LYNewTxtFile(cookie_file);
+}
+
+/* rjp - persistent cookie support */
+void LYStoreCookies(char *cookie_file)
+{
+    HTList *dl, *cl;
+    domain_entry *de;
+    cookie *co;
+    FILE *cookie_handle = NULL;
+    time_t now = time(NULL);	/* system specific? - RP */
+
+    if (isEmpty(cookie_file) || !strcmp(cookie_file, "/dev/null")) {
+	/* We give /dev/null the Unix meaning, regardless of OS */
+	return;
+    }
+
+    /*
+     * Check whether we have something to do.  - FM
+     */
+    if (HTList_isEmpty(domain_list) &&
+	number_of_file_cookies == 0) {
+	/* No cookies now, and haven't read any,
+	 * so don't bother updating the file.
+	 */
+	return;
+    }
+
+    /* if we read cookies from the file, we'll update it even if now empty */
+    if (number_of_file_cookies != 0) {
+	cookie_handle = NewCookieFile(cookie_file);
+	if (cookie_handle == NULL)
+	    return;
+    }
+
+    for (dl = domain_list; dl != NULL; dl = dl->next) {
+	de = (domain_entry *) (dl->object);
+	if (de == NULL)
+	    /*
+	     * Fote says the first object is NULL.  Go with that.
+	     */
+	    continue;
+
+	/*
+	 * Show the domain's cookies.  - FM
+	 */
+	for (cl = de->cookie_list; cl != NULL; cl = cl->next) {
+	    /*
+	     * First object is always NULL.  - FM
+	     */
+	    if ((co = (cookie *) cl->object) == NULL)
+		continue;
+
+	    CTrace((tfp, "LYStoreCookies: %" PRI_time_t " cf %" PRI_time_t " ",
+		    CAST_time_t(now), CAST_time_t(co->expires)));
+
+	    if ((co->flags & COOKIE_FLAG_DISCARD)) {
+		CTrace((tfp, "not stored - DISCARD\n"));
+		continue;
+	    } else if (!(co->flags & COOKIE_FLAG_EXPIRES_SET)) {
+		CTrace((tfp, "not stored - no expiration time\n"));
+		continue;
+	    } else if (co->expires <= now) {
+		CTrace((tfp, "not stored - EXPIRED\n"));
+		continue;
+	    }
+
+	    /* when we're sure we'll write to the file - open it */
+	    if (cookie_handle == NULL) {
+		cookie_handle = NewCookieFile(cookie_file);
+		if (cookie_handle == NULL)
+		    return;
+	    }
+
+	    fprintf(cookie_handle, "%s\t%s\t%s\t%s\t%" PRI_time_t
+		    "\t%s\t%s%s%s\n",
+		    de->domain,
+		    (de->domain[0] == '.') ? "TRUE" : "FALSE",
+		    co->path,
+		    co->flags & COOKIE_FLAG_SECURE ? "TRUE" : "FALSE",
+		    CAST_time_t(co->expires), co->name,
+		    (co->quoted ? "\"" : ""),
+		    NonNull(co->value),
+		    (co->quoted ? "\"" : ""));
+
+	    CTrace((tfp, "STORED\n"));
+	}
+    }
+    if (cookie_handle != NULL) {
+	LYCloseOutput(cookie_handle);
+	HTSYS_purge(cookie_file);
+    }
+}
+#endif
+
+/*	LYHandleCookies - F.Macrides (macrides@sci.wfeb.edu)
+ *	---------------
+ *
+ *  Lists all cookies by domain, and allows deletions of
+ *  individual cookies or entire domains, and changes of
+ *  'allow' settings.  The list is invoked via the COOKIE_JAR
+ *  command (Ctrl-K), and deletions or changes of 'allow'
+ *  settings are done by activating links in that list.
+ *  The procedure uses a LYNXCOOKIE: internal URL scheme.
+ *
+ *  Semantics:
+ *	LYNXCOOKIE:/			Create and load the Cookie Jar Page.
+ *	LYNXCOOKIE://domain		Manipulate the domain.
+ *	LYNXCOOKIE://domain/lynxID	Delete cookie with lynxID in domain.
+ *
+ *	New functions can be added as extensions to the path, and/or by
+ *	assigning meanings to ;parameters, a ?searchpart, and/or #fragments.
+ */
+static int LYHandleCookies(const char *arg,
+			   HTParentAnchor *anAnchor,
+			   HTFormat format_out,
+			   HTStream *sink)
+{
+    HTFormat format_in = WWW_HTML;
+    HTStream *target = NULL;
+    char *buf = NULL;
+    char *domain = NULL;
+    char *lynxID = NULL;
+    HTList *dl, *cl, *next;
+    domain_entry *de;
+    cookie *co;
+    char *name = NULL, *value = NULL, *path = NULL;
+    char *comment = NULL, *Address = NULL, *Title = NULL;
+    int ch;
+
+    /*
+     * Check whether we have something to do.  - FM
+     */
+    if (HTList_isEmpty(domain_list)) {
+	HTProgress(COOKIE_JAR_IS_EMPTY);
+	LYSleepMsg();
+	HTNoDataOK = 1;
+	return (HT_NO_DATA);
+    }
+
+    /*
+     * If there's a domain string in the "host" field of the LYNXCOOKIE:  URL,
+     * this is a request to delete something or change and 'allow' setting.  -
+     * FM
+     */
+    if ((domain = HTParse(arg, "", PARSE_HOST)) != NULL) {
+	if (*domain == '\0') {
+	    FREE(domain);
+	} else {
+	    /*
+	     * If there is a path string (not just a slash) in the LYNXCOOKIE: 
+	     * URL, that's a cookie's lynxID and this is a request to delete it
+	     * from the Cookie Jar.  - FM
+	     */
+	    if ((lynxID = HTParse(arg, "", PARSE_PATH)) != NULL) {
+		if (*lynxID == '\0') {
+		    FREE(lynxID);
+		}
+	    }
+	}
+    }
+    if (domain) {
+	/*
+	 * Seek the domain in the domain_list structure.  - FM
+	 */
+	if ((de = find_domain_entry(domain)) != NULL) {
+	    FREE(domain);
+	    /*
+	     * We found the domain.  Check whether a lynxID is present.  - FM
+	     */
+	    if (lynxID) {
+		/*
+		 * Seek and delete the cookie with this lynxID in the domain's
+		 * cookie list.  - FM
+		 */
+		for (cl = de->cookie_list; cl != NULL; cl = cl->next) {
+		    if ((co = (cookie *) cl->object) == NULL)
+			/*
+			 * First object is always empty.  - FM
+			 */
+			continue;
+		    if (!strcmp(lynxID, co->lynxID)) {
+			/*
+			 * We found the cookie.  Delete it if confirmed.  - FM
+			 */
+			if (HTConfirm(DELETE_COOKIE_CONFIRMATION) == FALSE) {
+			    FREE(lynxID);
+			    HTNoDataOK = 1;
+			    return (HT_NO_DATA);
+			}
+			HTList_removeObject(de->cookie_list, co);
+			freeCookie(co);
+			co = NULL;
+			total_cookies--;
+			if ((de->bv == QUERY_USER &&
+			     HTList_isEmpty(de->cookie_list)) &&
+			    HTConfirm(DELETE_EMPTY_DOMAIN_CONFIRMATION)) {
+			    /*
+			     * No more cookies in this domain, no default
+			     * accept/reject choice was set by the user, and
+			     * got confirmation on deleting the domain, so do
+			     * it.  - FM
+			     */
+			    FREE(de->domain);
+			    HTList_delete(de->cookie_list);
+			    de->cookie_list = NULL;
+			    HTList_removeObject(domain_list, de);
+			    FREE(de);
+			    HTProgress(DOMAIN_EATEN);
+			} else {
+			    HTProgress(COOKIE_EATEN);
+			}
+			LYSleepMsg();
+			HTNoDataOK = 1;
+			break;
+		    }
+		}
+	    } else {
+		/*
+		 * Prompt whether to delete all of the cookies in this domain,
+		 * or the domain if no cookies in it, or to change its 'allow'
+		 * setting, or to cancel, and then act on the user's response. 
+		 * - FM
+		 */
+		if (HTList_isEmpty(de->cookie_list)) {
+		    _statusline(DELETE_DOMAIN_SET_ALLOW_OR_CANCEL);
+		} else {
+		    _statusline(DELETE_COOKIES_SET_ALLOW_OR_CANCEL);
+		}
+		HTNoDataOK = 1;
+		while (1) {
+		    ch = LYgetch_single();
+#ifdef VMS
+		    if (HadVMSInterrupt) {
+			HadVMSInterrupt = FALSE;
+			ch = 'C';
+		    }
+#endif /* VMS */
+		    switch (ch) {
+		    case 'A':
+			/*
+			 * Set to accept all cookies from this domain.  - FM
+			 */
+			de->bv = ACCEPT_ALWAYS;
+			HTUserMsg2(ALWAYS_ALLOWING_COOKIES, de->domain);
+			return (HT_NO_DATA);
+
+		    case 'C':
+			/*
+			 * Cancelled.  - FM
+			 */
+		      reject:
+			HTUserMsg(CANCELLED);
+			return (HT_NO_DATA);
+
+		    case 'D':
+			if (HTList_isEmpty(de->cookie_list)) {
+			    /*
+			     * We had an empty domain, so we were asked to
+			     * delete it.  - FM
+			     */
+			    FREE(de->domain);
+			    HTList_delete(de->cookie_list);
+			    de->cookie_list = NULL;
+			    HTList_removeObject(domain_list, de);
+			    FREE(de);
+			    HTProgress(DOMAIN_EATEN);
+			    LYSleepMsg();
+			    break;
+			}
+		      Delete_all_cookies_in_domain:
+			/*
+			 * Delete all cookies in this domain.  - FM
+			 */
+			cl = de->cookie_list;
+			while (cl) {
+			    next = cl->next;
+			    co = (cookie *) (cl->object);
+			    if (co) {
+				HTList_removeObject(de->cookie_list, co);
+				freeCookie(co);
+				co = NULL;
+				total_cookies--;
+			    }
+			    cl = next;
+			}
+			HTProgress(DOMAIN_COOKIES_EATEN);
+			LYSleepMsg();
+			/*
+			 * If a default accept/reject choice is set, we're
+			 * done.  - FM
+			 */
+			if (de->bv != QUERY_USER)
+			    return (HT_NO_DATA);
+			/*
+			 * Check whether to delete the empty domain.  - FM
+			 */
+			if (HTConfirm(DELETE_EMPTY_DOMAIN_CONFIRMATION)) {
+			    FREE(de->domain);
+			    HTList_delete(de->cookie_list);
+			    de->cookie_list = NULL;
+			    HTList_removeObject(domain_list, de);
+			    FREE(de);
+			    HTProgress(DOMAIN_EATEN);
+			    LYSleepMsg();
+			}
+			break;
+
+		    case 'P':
+			/*
+			 * Set to prompt for cookie acceptance from this
+			 * domain.  - FM
+			 */
+			de->bv = QUERY_USER;
+			HTUserMsg2(PROMPTING_TO_ALLOW_COOKIES, de->domain);
+			return (HT_NO_DATA);
+
+		    case 'V':
+			/*
+			 * Set to reject all cookies from this domain.  - FM
+			 */
+			de->bv = REJECT_ALWAYS;
+			HTUserMsg2(NEVER_ALLOWING_COOKIES, de->domain);
+			if ((!HTList_isEmpty(de->cookie_list)) &&
+			    HTConfirm(DELETE_ALL_COOKIES_IN_DOMAIN))
+			    goto Delete_all_cookies_in_domain;
+			return (HT_NO_DATA);
+
+		    default:
+			if (LYCharIsINTERRUPT(ch))
+			    goto reject;
+			continue;
+		    }
+		    break;
+		}
+	    }
+	}
+	if (HTList_isEmpty(domain_list)) {
+	    /*
+	     * There are no more domains left.  Don't delete the domain_list,
+	     * otherwise atexit may be called multiple times.  - kw
+	     */
+	    HTProgress(ALL_COOKIES_EATEN);
+	    LYSleepMsg();
+	}
+	FREE(domain);
+	FREE(lynxID);
+	return (HT_NO_DATA);
+    }
+
+    /*
+     * If we get to here, it was a LYNXCOOKIE:/ URL for creating and displaying
+     * the Cookie Jar Page, or we didn't find the domain or cookie in a
+     * deletion request.  Set up an HTML stream and return an updated Cookie
+     * Jar Page.  - FM
+     */
+    target = HTStreamStack(format_in,
+			   format_out,
+			   sink, anAnchor);
+    if (!target || target == NULL) {
+	HTSprintf0(&buf, CANNOT_CONVERT_I_TO_O,
+		   HTAtom_name(format_in), HTAtom_name(format_out));
+	HTAlert(buf);
+	FREE(buf);
+	return (HT_NOT_LOADED);
+    }
+
+    /*
+     * Load HTML strings into buf and pass buf to the target for parsing and
+     * rendering.  - FM
+     */
+#define PUTS(buf)    (*target->isa->put_block)(target, buf, (int) strlen(buf))
+
+    HTSprintf0(&buf,
+	       "<html>\n<head>\n<title>%s</title>\n</head>\n<body>\n",
+	       COOKIE_JAR_TITLE);
+    PUTS(buf);
+    HTSprintf0(&buf, "<h1>%s (%s)%s<a href=\"%s%s\">%s</a></h1>\n",
+	       LYNX_NAME, LYNX_VERSION,
+	       HELP_ON_SEGMENT,
+	       helpfilepath, COOKIE_JAR_HELP, COOKIE_JAR_TITLE);
+    PUTS(buf);
+
+    HTSprintf0(&buf, "<note>%s\n", ACTIVATE_TO_GOBBLE);
+    PUTS(buf);
+    HTSprintf0(&buf, "%s</note>\n", OR_CHANGE_ALLOW);
+    PUTS(buf);
+
+    HTSprintf0(&buf, "<dl compact>\n");
+    PUTS(buf);
+    for (dl = domain_list; dl != NULL; dl = dl->next) {
+	de = (domain_entry *) (dl->object);
+	if (de == NULL)
+	    /*
+	     * First object always is NULL.  - FM
+	     */
+	    continue;
+
+	/*
+	 * Show the domain link and 'allow' setting.  - FM
+	 */
+	HTSprintf0(&buf, "<dt>%s<dd><a href=\"%s//%s/\">Domain=%s</a>\n",
+		   de->domain, STR_LYNXCOOKIE, de->domain, de->domain);
+	PUTS(buf);
+	switch (de->bv) {
+	case (ACCEPT_ALWAYS):
+	    HTSprintf0(&buf, COOKIES_ALWAYS_ALLOWED);
+	    break;
+	case (REJECT_ALWAYS):
+	    HTSprintf0(&buf, COOKIES_NEVER_ALLOWED);
+	    break;
+	case (QUERY_USER):
+	    HTSprintf0(&buf, COOKIES_ALLOWED_VIA_PROMPT);
+	    break;
+	}
+	PUTS(buf);
+	HTSprintf0(&buf, "\n");
+	PUTS(buf);
+
+	/*
+	 * Show the domain's cookies.  - FM
+	 */
+	for (cl = de->cookie_list; cl != NULL; cl = cl->next) {
+	    if ((co = (cookie *) cl->object) == NULL)
+		/*
+		 * First object is always NULL.  - FM
+		 */
+		continue;
+
+	    /*
+	     * Show the name=value pair.  - FM
+	     */
+	    if (co->name) {
+		StrAllocCopy(name, co->name);
+		LYEntify(&name, TRUE);
+	    } else {
+		StrAllocCopy(name, NO_NAME);
+	    }
+	    if (co->value) {
+		StrAllocCopy(value, co->value);
+		LYEntify(&value, TRUE);
+	    } else {
+		StrAllocCopy(value, NO_VALUE);
+	    }
+	    HTSprintf0(&buf, "<dd><a href=\"%s//%s/%s\">%s=%s</a>\n",
+		       STR_LYNXCOOKIE, de->domain, co->lynxID, name, value);
+	    FREE(name);
+	    FREE(value);
+	    PUTS(buf);
+
+	    if (co->flags & COOKIE_FLAG_FROM_FILE) {
+		HTSprintf0(&buf, "%s\n",
+			   gettext("(from a previous session)"));
+		PUTS(buf);
+	    }
+
+	    /*
+	     * Show the path, port, secure and discard setting.  - FM
+	     */
+	    if (co->path) {
+		StrAllocCopy(path, co->path);
+		LYEntify(&path, TRUE);
+	    } else {
+		StrAllocCopy(path, "/");
+	    }
+	    HTSprintf0(&buf,
+		       "<dd>Path=%s\n<dd>Port: %d Secure: %s Discard: %s\n",
+		       path, co->port,
+		       ((co->flags & COOKIE_FLAG_SECURE) ? "YES" : "NO"),
+		       ((co->flags & COOKIE_FLAG_DISCARD) ? "YES" : "NO"));
+	    FREE(path);
+	    PUTS(buf);
+
+	    /*
+	     * Show the list of acceptable ports, if present.  - FM
+	     */
+	    if (co->PortList) {
+		HTSprintf0(&buf, "<dD>PortList=\"%s\"\n", co->PortList);
+		PUTS(buf);
+	    }
+
+	    /*
+	     * Show the commentURL, if we have one.  - FM
+	     */
+	    if (co->commentURL) {
+		StrAllocCopy(Address, co->commentURL);
+		LYEntify(&Address, FALSE);
+		StrAllocCopy(Title, co->commentURL);
+		LYEntify(&Title, TRUE);
+		HTSprintf0(&buf,
+			   "<dd>CommentURL: <a href=\"%s\">%s</a>\n",
+			   Address,
+			   Title);
+		FREE(Address);
+		FREE(Title);
+		PUTS(buf);
+	    }
+
+	    /*
+	     * Show the comment, if we have one.  - FM
+	     */
+	    if (co->comment) {
+		StrAllocCopy(comment, co->comment);
+		LYEntify(&comment, TRUE);
+		HTSprintf0(&buf, "<dd>Comment: %s\n", comment);
+		FREE(comment);
+		PUTS(buf);
+	    }
+
+	    /*
+	     * Show the Maximum Gobble Date.  - FM
+	     */
+	    HTSprintf0(&buf, "<dd><em>%s</em> %s%s",
+		       gettext("Maximum Gobble Date:"),
+		       ((co->flags & COOKIE_FLAG_EXPIRES_SET)
+			?
+			ctime(&co->expires) : END_OF_SESSION),
+		       ((co->flags & COOKIE_FLAG_EXPIRES_SET)
+			?
+			"" : "\n"));
+	    PUTS(buf);
+	}
+	HTSprintf0(&buf, "</dt>\n");
+	PUTS(buf);
+    }
+    HTSprintf0(&buf, "</dl>\n</body>\n</html>\n");
+    PUTS(buf);
+
+    /*
+     * Free the target to complete loading of the Cookie Jar Page, and report a
+     * successful load.  - FM
+     */
+    (*target->isa->_free) (target);
+    FREE(buf);
+    return (HT_LOADED);
+}
+
+/*      cookie_domain_flag_set
+ *      ----------------------
+ *      All purpose function to handle setting domain flags for a
+ *      comma-delimited list of domains.  cookie_domain_flags handles
+ *      invcheck behavior, as well as accept/reject behavior. - BJP
+ */
+static void cookie_domain_flag_set(char *domainstr,
+				   int flag)
+{
+    domain_entry *de = NULL;
+    char **str = typecalloc(char *);
+    char *dstr = NULL;
+    char *strsmall = NULL;
+
+    if (str == NULL) {
+	HTAlwaysAlert(gettext("Internal"),
+		      gettext("cookie_domain_flag_set error, aborting program"));
+	exit_immediately(EXIT_FAILURE);
+    }
+
+    /*
+     * Is this the first domain we're handling?  If so, initialize domain_list.
+     */
+    if (domain_list == NULL) {
+#ifdef LY_FIND_LEAKS
+	atexit(LYCookieJar_free);
+#endif
+	domain_list = HTList_new();
+	total_cookies = 0;
+    }
+
+    StrAllocCopy(dstr, domainstr);
+
+    *str = dstr;
+
+    while ((strsmall = LYstrsep(str, ",")) != 0) {
+
+	if (*strsmall == '\0')
+	    /* Never add a domain for empty string.  It would actually
+	     * make more sense to use strtok here. - kw */
+	    continue;
+
+	/*
+	 * Check the list of existing domains to see if this is a
+	 * re-setting of an already existing domain -- if so, just
+	 * change the behavior, if not, create a new domain entry.
+	 */
+
+	if ((de = find_domain_entry(strsmall)) == NULL) {
+	    de = typecalloc(domain_entry);
+	    if (de == NULL)
+		outofmem(__FILE__, "cookie_domain_flag_set");
+
+	    assert(de != NULL);
+
+	    de->bv = ACCEPT_ALWAYS;
+	    de->invcheck_bv = INVCHECK_QUERY;
+
+	    switch (flag) {
+	    case (FLAG_ACCEPT_ALWAYS):
+		de->invcheck_bv = DEFAULT_INVCHECK_BV;
+		break;
+	    case (FLAG_REJECT_ALWAYS):
+		de->invcheck_bv = DEFAULT_INVCHECK_BV;
+		break;
+	    case (FLAG_QUERY_USER):
+		de->invcheck_bv = DEFAULT_INVCHECK_BV;
+		break;
+	    case (FLAG_INVCHECK_QUERY):
+		de->bv = QUERY_USER;
+		break;
+	    case (FLAG_INVCHECK_STRICT):
+		de->bv = QUERY_USER;
+		break;
+	    case (FLAG_INVCHECK_LOOSE):
+		de->bv = QUERY_USER;
+		break;
+	    }
+
+	    StrAllocCopy(de->domain, strsmall);
+	    de->cookie_list = HTList_new();
+	    HTList_appendObject(domain_list, de);
+	}
+	switch (flag) {
+	case (FLAG_ACCEPT_ALWAYS):
+	    de->bv = ACCEPT_ALWAYS;
+	    break;
+	case (FLAG_REJECT_ALWAYS):
+	    de->bv = REJECT_ALWAYS;
+	    break;
+	case (FLAG_QUERY_USER):
+	    de->bv = QUERY_USER;
+	    break;
+	case (FLAG_INVCHECK_QUERY):
+	    de->invcheck_bv = INVCHECK_QUERY;
+	    break;
+	case (FLAG_INVCHECK_STRICT):
+	    de->invcheck_bv = INVCHECK_STRICT;
+	    break;
+	case (FLAG_INVCHECK_LOOSE):
+	    de->invcheck_bv = INVCHECK_LOOSE;
+	    break;
+	}
+	CTrace((tfp,
+		"cookie_domain_flag_set (%s, bv=%u, invcheck_bv=%u)\n",
+		strsmall, de->bv, de->invcheck_bv));
+    }
+
+    FREE(strsmall);
+    FREE(str);
+    FREE(dstr);
+}
+
+/*
+ * If any COOKIE_{ACCEPT,REJECT}_DOMAINS have been defined, process them.
+ * These are comma delimited lists of domains.  - BJP
+ *
+ * And for query/strict/loose invalid cookie checking.  - BJP
+ */
+void LYConfigCookies(void)
+{
+    static const struct {
+	char **domain;
+	int flag;
+	int once;
+    } table[] = {
+	/* *INDENT-OFF* */
+	{ &LYCookieSAcceptDomains,	FLAG_ACCEPT_ALWAYS,   TRUE },
+	{ &LYCookieSRejectDomains,	FLAG_REJECT_ALWAYS,   TRUE },
+	{ &LYCookieSStrictCheckDomains, FLAG_INVCHECK_STRICT, TRUE },
+	{ &LYCookieSLooseCheckDomains,	FLAG_INVCHECK_LOOSE,  TRUE },
+	{ &LYCookieSQueryCheckDomains,	FLAG_INVCHECK_QUERY,  TRUE },
+	{ &LYCookieAcceptDomains,	FLAG_ACCEPT_ALWAYS,   FALSE },
+	{ &LYCookieRejectDomains,	FLAG_REJECT_ALWAYS,   FALSE },
+	{ &LYCookieStrictCheckDomains,	FLAG_INVCHECK_STRICT, FALSE },
+	{ &LYCookieLooseCheckDomains,	FLAG_INVCHECK_LOOSE,  FALSE },
+	{ &LYCookieQueryCheckDomains,	FLAG_INVCHECK_QUERY,  FALSE },
+	/* *INDENT-ON* */
+
+    };
+    unsigned n;
+
+    for (n = 0; n < TABLESIZE(table); n++) {
+	if (*(table[n].domain) != NULL) {
+	    cookie_domain_flag_set(*(table[n].domain), table[n].flag);
+	    /*
+	     * Discard the value for system settings after we've used them.
+	     * The local settings will be merged with the contents of .lynxrc
+	     */
+	    if (table[n].once) {
+		FREE(*(table[n].domain));
+	    }
+	}
+    }
+}
+
+#ifdef GLOBALDEF_IS_MACRO
+#define _LYCOOKIE_C_GLOBALDEF_1_INIT { "LYNXCOOKIE",LYHandleCookies,0}
+GLOBALDEF(HTProtocol, LYLynxCookies, _LYCOOKIE_C_GLOBALDEF_1_INIT);
+#else
+GLOBALDEF HTProtocol LYLynxCookies =
+{"LYNXCOOKIE", LYHandleCookies, 0};
+#endif /* GLOBALDEF_IS_MACRO */
diff --git a/src/LYCookie.h b/src/LYCookie.h
new file mode 100644
index 00000000..3b0e109b
--- /dev/null
+++ b/src/LYCookie.h
@@ -0,0 +1,57 @@
+#ifndef LYCOOKIES_H
+#define LYCOOKIES_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#include <HTList.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    typedef enum {
+	ACCEPT_ALWAYS = 0
+	,REJECT_ALWAYS
+	,QUERY_USER
+    } behaviour_t;
+
+    typedef enum {
+	INVCHECK_QUERY = 0
+	,INVCHECK_STRICT
+	,INVCHECK_LOOSE
+    } invcheck_behaviour_t;
+
+    typedef enum {
+	FLAG_ACCEPT_ALWAYS = 0
+	,FLAG_REJECT_ALWAYS
+	,FLAG_QUERY_USER
+	,FLAG_FROM_FILE
+	,FLAG_INVCHECK_QUERY
+	,FLAG_INVCHECK_STRICT
+	,FLAG_INVCHECK_LOOSE
+    } cookie_domain_flags;
+
+    struct _domain_entry {
+	char *domain;		/* Domain for which these cookies are valid */
+	behaviour_t bv;
+	invcheck_behaviour_t invcheck_bv;
+	HTList *cookie_list;
+    };
+    typedef struct _domain_entry domain_entry;
+
+    extern void LYSetCookie(const char *SetCookie,
+			    const char *SetCookie2,
+			    const char *address);
+    extern char *LYAddCookieHeader(char *hostname,
+				   char *partialpath,
+				   int port,
+				   BOOL secure);
+    extern void LYStoreCookies(char *cookie_file);
+    extern void LYLoadCookies(char *cookie_file);
+    extern void LYConfigCookies(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYCOOKIES_H */
diff --git a/src/LYCurses.c b/src/LYCurses.c
new file mode 100644
index 00000000..611534ee
--- /dev/null
+++ b/src/LYCurses.c
@@ -0,0 +1,2970 @@
+/* $LynxId: LYCurses.c,v 1.150 2010/05/03 00:09:05 tom Exp $ */
+#include <HTUtils.h>
+#include <HTAlert.h>
+
+#ifdef __MINGW32__
+#ifdef UNIX
+#undef UNIX
+#endif /* UNIX */
+#endif /* __MINGW32__ */
+
+#ifdef __DJGPP__
+#include <pc.h>
+#endif /* __DJGPP__ */
+
+#include <LYCurses.h>
+#include <LYStyle.h>
+#include <LYUtils.h>
+#include <LYGlobalDefs.h>
+#include <LYSignal.h>
+#include <LYClean.h>
+#include <LYReadCFG.h>
+#include <LYStrings.h>
+#include <LYCharSets.h>
+#include <UCAux.h>
+#include <HTFont.h>
+
+#include <LYexit.h>
+#include <LYLeaks.h>
+
+#ifdef VMS
+#include <LYMainLoop.h>
+#endif
+
+#if defined(VMS) && defined(__GNUC__)
+#include <gnu_hacks.h>
+#undef LINES
+#undef COLS
+#define LINES lines
+#define COLS cols
+extern int _NOSHARE(LINES);
+extern int _NOSHARE(COLS);
+#endif /* VMS && __GNUC__ */
+
+#ifdef USE_COLOR_STYLE
+#include <AttrList.h>
+#include <LYHash.h>
+#endif
+
+#ifdef NEED_WCHAR_H
+#include <wchar.h>
+#endif
+
+#if defined(COLOR_CURSES)
+int lynx_has_color = FALSE;
+#endif
+
+#ifdef HAVE_XCURSES
+char *XCursesProgramName = "Lynx";
+#endif
+
+#if defined(USE_COLOR_STYLE) && !defined(USE_COLOR_TABLE)
+#define COLOR_BKGD ((s_normal != NOSTYLE) ? hashStyles[s_normal].color : A_NORMAL)
+#else
+#define COLOR_BKGD ((COLOR_PAIRS >= 9) ? (chtype) get_color_pair(9) : A_NORMAL)
+#endif
+
+#ifdef USE_CURSES_PADS
+WINDOW *LYwin = 0;
+int LYshiftWin = 0;
+int LYwideLines = FALSE;
+int LYtableCols = 0;		/* in 1/12 of screen width */
+BOOLEAN LYuseCursesPads = TRUE;	/* use pads for left/right shifting */
+#endif
+
+/*
+ * These are routines to start and stop curses and to cleanup the screen at the
+ * end.
+ */
+
+static int dumbterm(char *terminal);
+BOOLEAN LYCursesON = FALSE;
+
+#if defined(USE_BLINK) && defined(__EMX__)
+static void make_blink_boldbg(void);
+#endif
+
+#if defined(USE_COLOR_TABLE) || defined(USE_SLANG)
+int Current_Attr;
+static int Masked_Attr;
+#endif
+
+#ifdef USE_SLANG
+unsigned Lynx_Color_Flags = 0;
+BOOLEAN FullRefresh = FALSE;
+int curscr = 0;
+
+#ifdef SLANG_MBCS_HACK
+/*
+ * Will be set by size_change.  - KW
+ */
+int PHYSICAL_SLtt_Screen_Cols = 10;
+#endif /* SLANG_MBCS_HACK */
+
+void LY_SLrefresh(void)
+{
+    if (FullRefresh) {
+	SLsmg_suspend_smg();
+	SLsmg_resume_smg();
+	FullRefresh = FALSE;
+    } else {
+	SLsmg_refresh();
+    }
+
+    return;
+}
+
+/* the following renamed from LY_SLclear since it is more like erase()
+   described in curses man pages than like clear(); but for USE_SLANG
+   clear() is still a macro calling this, and will do the same thing as
+   erase(). - kw */
+void LY_SLerase(void)
+{
+    SLsmg_gotorc(0, 0);
+    SLsmg_erase_eos();
+}
+
+#ifdef VMS
+void VTHome(void)
+{
+    printf("\033[;H");
+
+    return;
+}
+#endif /* VMS */
+
+void LYaddAttr(int a)
+{
+    Current_Attr |= a;
+    SLsmg_set_color(Current_Attr & ~Masked_Attr);
+}
+
+void LYsubAttr(int a)
+{
+    Current_Attr &= ~a;
+    SLsmg_set_color(Current_Attr & ~Masked_Attr);
+}
+
+static void lynx_setup_attrs(void)
+{
+    static int monoattr[] =
+    {
+	0,
+	SLTT_BOLD_MASK,
+	SLTT_REV_MASK,
+	SLTT_REV_MASK | SLTT_BOLD_MASK,
+	SLTT_ULINE_MASK,
+	SLTT_ULINE_MASK | SLTT_BOLD_MASK,
+	SLTT_ULINE_MASK | SLTT_REV_MASK,
+	SLTT_ULINE_MASK | SLTT_BOLD_MASK | SLTT_REV_MASK
+    };
+    int n;
+
+    for (n = 1; n <= 7; n++)
+	SLtt_set_mono(n, NULL, (monoattr[n] & ~Masked_Attr));
+}
+
+void lynx_setup_colors(void)
+{
+    CTRACE((tfp, "lynx_setup_colors\n"));
+    SLtt_set_color(0, NULL, DEFAULT_FG, DEFAULT_BG);
+    SLtt_set_color(1, NULL, "blue", DEFAULT_BG);	/* bold */
+    SLtt_set_color(2, NULL, "yellow", "blue");	/* reverse */
+    SLtt_set_color(4, NULL, "magenta", DEFAULT_BG);	/* underline */
+    /*
+     * The other objects are '|'ed together to get rest.
+     */
+    SLtt_set_color(3, NULL, "green", DEFAULT_BG);	/* bold-reverse */
+    SLtt_set_color(5, NULL, "blue", DEFAULT_BG);	/* bold-underline */
+    SLtt_set_color(6, NULL, "red", DEFAULT_BG);		/* reverse-underline */
+    SLtt_set_color(7, NULL, "magenta", "cyan");		/* reverse-underline-bold */
+    /*
+     * Now set monochrome attributes.
+     */
+    lynx_setup_attrs();
+}
+
+static void sl_suspend(int sig)
+{
+#ifdef SIGSTOP
+#ifndef VMS
+    int r, c;
+
+    lynx_enable_mouse(0);
+    if (sig == SIGTSTP)
+	SLsmg_suspend_smg();
+    SLang_reset_tty();
+    kill(getpid(), SIGSTOP);
+#if SLANG_VERSION > 9929
+    SLang_init_tty(-1, 0, 1);
+#else
+    SLang_init_tty(3, 0, 1);
+#endif /* SLANG_VERSION > 9929 */
+    signal(SIGTSTP, sl_suspend);
+#if defined(REAL_UNIX_SYSTEM) && !defined(__CYGWIN__)
+    SLtty_set_suspend_state(1);
+#endif
+    if (sig == SIGTSTP)
+	SLsmg_resume_smg();
+    /*
+     * Get new window size in case it changed.
+     */
+    r = SLtt_Screen_Rows;
+    c = SLtt_Screen_Cols;
+    size_change(0);
+    if ((r != SLtt_Screen_Rows) || (c != SLtt_Screen_Cols)) {
+	recent_sizechange = TRUE;
+    }
+    lynx_enable_mouse(1);
+#endif /* !VMS */
+#endif /* SIGSTOP */
+    return;
+}
+#else
+
+#ifdef FANCY_CURSES
+
+#ifndef VMS
+/* *INDENT-OFF* */
+/* definitions for the mono attributes we can use */
+static struct {
+    const char *name;
+    int code;
+} Mono_Attrs[7] =
+{
+    { "normal",		A_NORMAL },
+    { "bold",		A_BOLD },
+    { "reverse",	A_REVERSE },
+    { "underline",	A_UNDERLINE },
+    { "standout",	A_STANDOUT },
+    { "blink",		A_BLINK },
+    { "dim",		A_DIM },
+};
+/* *INDENT-ON* */
+
+int string_to_attr(const char *name)
+{
+    unsigned i;
+
+    for (i = 0; i < TABLESIZE(Mono_Attrs); i++) {
+	if (!strcasecomp(Mono_Attrs[i].name, name)) {
+	    return Mono_Attrs[i].code;
+	}
+    }
+    return 0;
+}
+#endif /* VMS */
+
+#ifdef USE_COLOR_STYLE
+static char *attr_to_string(int code)
+{
+    static char result[sizeof(Mono_Attrs) + 80];
+    unsigned i;
+    int pair = PAIR_NUMBER(code);
+    int bold = (pair != 0 && (code & A_BOLD) != 0);
+
+    if (bold)
+	code &= (int) ~A_BOLD;
+
+    *result = 0;
+    for (i = 0; i < TABLESIZE(Mono_Attrs); i++) {
+	if (Mono_Attrs[i].code & code) {
+	    if (*result)
+		strcat(result, "+");
+	    strcat(result, Mono_Attrs[i].name);
+	}
+    }
+    if (pair != 0) {
+	short f, b;
+
+	if (pair_content((short) pair, &f, &b) != ERR) {
+	    const char *fg = lookup_color(bold ? f + COLORS : f);
+	    const char *bg = lookup_color(b);
+
+	    if (*result)
+		strcat(result, "+");
+	    sprintf(result + strlen(result), "%s/%s", fg, bg);
+	}
+    }
+    return result;
+}
+#endif /* USE_COLOR_STYLE */
+#endif /* FANCY_CURSES */
+#endif /* USE_SLANG */
+
+/*
+ *  This function boxes windows for (n)curses.
+ */
+void LYbox(WINDOW * win, BOOLEAN formfield GCC_UNUSED)
+{
+#ifdef USE_SLANG
+    SLsmg_draw_box(win->top_y, win->left_x, win->height, win->width + 4);
+#else
+#ifdef VMS
+    /*
+     * This should work for VAX-C and DEC-C, since they both have the same
+     * win._max_y and win._max_x members -TD
+     *
+     * (originally VMSbox by FM)
+     */
+    int i;
+
+    wmove(win, 0, 0);
+    waddstr(win, "\033)0\016l");
+    for (i = 1; i < win->_max_x; i++)
+	waddch(win, 'q');
+    waddch(win, 'k');
+    for (i = 1; i < win->_max_y - 1; i++) {
+	wmove(win, i, 0);
+	waddch(win, 'x');
+	wmove(win, i, win->_max_x - 1);
+	waddch(win, 'x');
+    }
+    wmove(win, i, 0);
+    waddch(win, 'm');
+    for (i = 1; i < win->_max_x; i++)
+	waddch(win, 'q');
+    waddstr(win, "j\017");
+#else /* !VMS */
+    int boxvert, boxhori;
+
+    UCSetBoxChars(current_char_set, &boxvert, &boxhori, BOXVERT, BOXHORI);
+#ifdef CSS
+    if (formfield)
+	wcurses_css(win, "frame", ABS_ON);
+#endif
+    /*
+     * If we don't have explicitly specified characters for either vertical or
+     * horizontal lines, the characters that box() would use for the corners
+     * probably also won't work well.  So we specify our own ASCII characters
+     * for the corners and call wborder() instead of box().  - kw
+     */
+    LynxWChangeStyle(win, s_menu_frame, STACK_ON);
+#ifdef HAVE_WBORDER
+    if (!boxvert || !boxhori) {
+	box(win,
+	    (chtype) boxvert,
+	    (chtype) boxhori);
+    } else if (boxvert == '*' || boxhori == '*') {
+	wborder(win,
+		(chtype) boxvert,
+		(chtype) boxvert,
+		(chtype) boxhori,
+		(chtype) boxhori,
+		'*', '*', '*', '*');
+    } else {
+	wborder(win,
+		(chtype) boxvert,
+		(chtype) boxvert,
+		(chtype) boxhori,
+		(chtype) boxhori,
+		'/', '\\', '\\', '/');
+    }
+#else
+    box(win, boxvert, boxhori);
+#endif
+    LynxWChangeStyle(win, s_menu_frame, STACK_OFF);
+#ifdef CSS
+    if (formfield)
+	wcurses_css(win, "frame", ABS_OFF);
+#endif
+#endif /* VMS */
+    wrefresh(win);
+#endif /* USE_SLANG */
+}
+
+#if defined(USE_COLOR_STYLE)
+/* Ok, explanation of the USE_COLOR_STYLE styles.  The basic styles (ie non
+ * HTML) are set the same as the SLANG version for ease of programming.  The
+ * other styles are simply the HTML enum from HTMLDTD.h + 16.
+ */
+HTCharStyle displayStyles[DSTYLE_ELEMENTS];
+
+/*
+ * set a style's attributes - RP
+ */
+void setStyle(int style,
+	      int color,
+	      int cattr,
+	      int mono)
+{
+    displayStyles[style].color = color;
+    displayStyles[style].cattr = cattr;
+    displayStyles[style].mono = mono;
+}
+
+void setHashStyle(int style,
+		  int color,
+		  int cattr,
+		  int mono,
+		  const char *element)
+{
+    bucket *ds = &hashStyles[style];
+
+    CTRACE2(TRACE_STYLE,
+	    (tfp, "CSS(SET): <%s> hash=%d, ca=%#x, ma=%#x\n",
+	     element, style, color, mono));
+
+    ds->color = color;
+    ds->cattr = cattr;
+    ds->mono = mono;
+    ds->code = style;
+    FREE(ds->name);
+    StrAllocCopy(ds->name, element);
+}
+
+/*
+ * set the curses attributes to be color or mono - RP
+ */
+static void LYAttrset(WINDOW * win, int color,
+		      int mono)
+{
+    if (lynx_has_color
+	&& LYShowColor >= SHOW_COLOR_ON
+	&& color >= 0) {
+	CTRACE2(TRACE_STYLE, (tfp, "CSS:LYAttrset color %#x -> (%s)\n",
+			      color, attr_to_string(color)));
+	(void) wattrset(win, color);
+    } else if (mono >= 0) {
+	CTRACE2(TRACE_STYLE, (tfp, "CSS:LYAttrset mono %#x -> (%s)\n",
+			      mono, attr_to_string(mono)));
+	(void) wattrset(win, mono);
+    } else {
+	CTRACE2(TRACE_STYLE, (tfp, "CSS:LYAttrset (A_NORMAL)\n"));
+	(void) wattrset(win, A_NORMAL);
+    }
+}
+
+void curses_w_style(WINDOW * win, int style,
+		    int dir)
+{
+#if OMIT_SCN_KEEPING
+# define SPECIAL_STYLE /*(CSHASHSIZE+1) */ 88888
+/* if TRACEs are not compiled in, this macro is redundant - we needn't valid
+'ds' to stack off. */
+#endif
+
+    int YP, XP;
+
+#if !OMIT_SCN_KEEPING
+    bucket *ds = (style == NOSTYLE ? &nostyle_bucket : &hashStyles[style]);
+
+#else
+    bucket *ds = (style == NOSTYLE ? &nostyle_bucket :
+		  (style == SPECIAL_STYLE ? &special_bucket : &hashStyles[style]));
+#endif
+
+    if (!ds->name) {
+	CTRACE2(TRACE_STYLE, (tfp, "CSS.CS:Style %d not configured\n", style));
+#if !OMIT_SCN_KEEPING
+	return;
+#endif
+    }
+
+    CTRACE2(TRACE_STYLE, (tfp, "CSS.CS:<%s%s> style %d code %#x, color %#x\n",
+			  (dir ? "" : "/"),
+			  ds->name, style, ds->code, ds->color));
+
+    getyx(win, YP, XP);
+
+    if (style == s_normal && dir) {
+	LYAttrset(win, ds->color, ds->mono);
+	if (win == LYwin)
+	    SetCachedStyle(YP, XP, s_normal);
+	return;
+    }
+
+    switch (dir) {
+	/* ABS_OFF is the same as STACK_OFF for the moment */
+    case STACK_OFF:
+	if (last_colorattr_ptr) {
+	    int last_attr = last_styles[--last_colorattr_ptr];
+
+	    LYAttrset(win, last_attr, last_attr);
+	} else
+	    LYAttrset(win, A_NORMAL, -1);
+	break;
+
+    case STACK_ON:		/* remember the current attributes */
+	if (last_colorattr_ptr >= MAX_LAST_STYLES) {
+	    CTRACE2(TRACE_STYLE, (tfp, "........... %s (0x%x) %s\r\n",
+				  "attribute cache FULL, dropping last",
+				  last_styles[last_colorattr_ptr],
+				  "in LynxChangeStyle(curses_w_style)"));
+	    last_colorattr_ptr = MAX_LAST_STYLES - 1;
+	}
+	last_styles[last_colorattr_ptr++] = LYgetattrs(win);
+	/* don't cache style changes for active links */
+#if OMIT_SCN_KEEPING
+	/* since we don't compute the hcode to stack off in HTML.c, we
+	 * don't know whether this style is configured.  So, we
+	 * shouldn't simply return on stacking on unconfigured
+	 * styles, we should push curr attrs on stack.  -HV
+	 */
+	if (!ds->name)
+	    break;
+#endif
+	/* FALL THROUGH */
+    case ABS_ON:		/* change without remembering the previous style */
+	/* don't cache style changes for active links and edits */
+	if (style != s_alink
+	    && style != s_curedit
+	    && style != s_aedit
+	    && style != s_aedit_sel
+	    && style != s_aedit_pad
+	    && style != s_aedit_arr) {
+	    CTRACE2(TRACE_STYLE, (tfp, "CACHED: <%s> @(%d,%d)\n",
+				  ds->name, YP, XP));
+	    if (win == LYwin)
+		SetCachedStyle(YP, XP, style);
+	}
+	LYAttrset(win, ds->color, ds->mono);
+	break;
+    }
+}
+
+/*
+ * wrapper function to set on-screen styles - RP
+ */
+void wcurses_css(WINDOW * win, char *name,
+		 int dir)
+{
+    int try_again = 1;
+
+    while (try_again) {
+	int tmpHash = hash_code(name);
+
+	CTRACE2(TRACE_STYLE, (tfp, "CSSTRIM:trying to set [%s] style - ", name));
+	if (tmpHash == NOSTYLE) {
+	    char *pclass = strrchr(name, '.');
+
+	    CTRACE2(TRACE_STYLE, (tfp, "undefined, trimming at %p\n", pclass));
+	    if (pclass)
+		*pclass = '\0';
+	    else
+		try_again = 0;
+	} else {
+	    CTRACE2(TRACE_STYLE, (tfp, "ok (%d)\n", hash_code(name)));
+	    curses_w_style(win, hash_code(name), dir);
+	    try_again = 0;
+	}
+    }
+}
+
+void curses_css(char *name,
+		int dir)
+{
+    wcurses_css(LYwin, name, dir);
+}
+
+void curses_style(int style,
+		  int dir)
+{
+    curses_w_style(LYwin, style, dir);
+}
+#endif /* USE_COLOR_STYLE */
+
+static BOOL lynx_called_initscr = FALSE;
+
+#if defined(USE_COLOR_TABLE) && defined(COLOR_CURSES)
+#define COLOR_CFG_MAX 8
+
+/*
+ * This block of code is designed to produce the same color effects using SVr4
+ * curses as the slang library's implementation in this module.  That maps the
+ * SGR codes into a 0-7 index into the color table, with special treatment for
+ * backgrounds.  There's a bit of convoluted (but necessary) code handling the
+ * special case of initialization before 'initscr()' is called.
+ * 1997/1/19 - T.E.Dickey <dickey@clark.net>
+ */
+/* *INDENT-OFF* */
+#define COLOR_CFG(c) c, (c) == DEFAULT_COLOR
+static struct {
+    int fg, dft_fg, bg, dft_bg;
+} lynx_color_cfg[] = {
+    /*0*/ { COLOR_CFG(DEFAULT_FG),       COLOR_CFG(DEFAULT_BG)},
+    /*1*/ { COLOR_CFG(COLOR_BLUE),       COLOR_CFG(DEFAULT_BG)},
+    /*2*/ { COLOR_CFG((COLOR_YELLOW)+8), COLOR_CFG(COLOR_BLUE)},
+    /*3*/ { COLOR_CFG(COLOR_GREEN),      COLOR_CFG(DEFAULT_BG)},
+    /*4*/ { COLOR_CFG(COLOR_MAGENTA),    COLOR_CFG(DEFAULT_BG)},
+    /*5*/ { COLOR_CFG(COLOR_BLUE),       COLOR_CFG(DEFAULT_BG)},
+    /*6*/ { COLOR_CFG(COLOR_RED),        COLOR_CFG(DEFAULT_BG)},
+    /*7*/ { COLOR_CFG(COLOR_MAGENTA),    COLOR_CFG(COLOR_CYAN)}
+};
+/* *INDENT-ON* */
+
+#define COLOR_PAIRS_MAX (COLOR_CFG_MAX * 3 + 1)
+
+/*
+ * Hold the codes for color-pairs here until 'initscr()' is called.
+ */
+static struct {
+    int fg;
+    int bg;
+} lynx_color_pairs[COLOR_PAIRS_MAX];
+
+/*
+ * If we find an exact match for the given default colors, force curses to use
+ * color pair 0, which corresponds to the terminal's default colors.  Normally
+ * curses assumes white-on-black, but we can override the assumption with this
+ * function.
+ */
+static int get_color_pair(int n)
+{
+#ifdef USE_CURSES_PAIR_0
+    if ((n < (int) TABLESIZE(lynx_color_pairs))
+	&& lynx_color_pairs[n].fg == default_fg
+	&& lynx_color_pairs[n].bg == default_bg)
+	return 0;
+#endif
+    return COLOR_PAIR(n);
+}
+
+/*
+ * Lynx "knows" about 16 colors.  ANSI colors (and most color terminal
+ * emulators) only go to 8, though some curses implementations (ncurses and
+ * PDCurses) handle 16.  If lynx's configuration calls for a color past the
+ * number of colors that the terminal handles (COLORS), map the extra value
+ * to bold.
+ */
+#define is_boldc(c) ((c) > (COLORS-1))
+#define map2bold(c) (is_boldc(c) ? ((c) & (COLORS-1)) : (c))
+
+/*
+ * Return the extra color as A_BOLD.
+ * If there is no extra color, return A_NORMAL.
+ */
+static int lynx_color_cfg_attr(int code)
+{
+    int result = A_NORMAL;
+
+    if (code >= 0 && code < COLOR_CFG_MAX) {
+	int fg = lynx_color_cfg[code].fg;
+
+	if (is_boldc(fg) && (fg & COLORS))
+	    result = A_BOLD;
+    }
+    return result;
+}
+
+static int encode_color_attr(int color_attr)
+{
+    int result;
+    int code = 0;
+    int offs = 1;
+
+    if (color_attr & A_BOLD)
+	code |= 1;
+    if (color_attr & (A_REVERSE | A_DIM))
+	code |= 2;
+    if (color_attr & A_UNDERLINE)
+	code |= 4;
+    result = lynx_color_cfg_attr(code);
+
+    if (code + offs < COLOR_PAIRS) {
+	result |= get_color_pair(code + offs);
+    }
+    return result;
+}
+
+static int decode_mono_code(int mono_code)
+{
+    unsigned result = 0;
+
+    if (mono_code & 1)
+	result |= A_BOLD;
+    if (mono_code & 2)
+	result |= A_REVERSE;
+    if (mono_code & 4)
+	result |= A_UNDERLINE;
+
+    return (int) result;
+}
+
+/*
+ * Map the SGR attributes (0-7) into ANSI colors, modified with the actual BOLD
+ * attribute to get 16 colors.
+ */
+int LYgetTableAttr(void)
+{
+    int result;
+
+    if (lynx_has_color && LYShowColor >= SHOW_COLOR_ON) {
+	result = encode_color_attr(Current_Attr);
+    } else {
+	result = Current_Attr;
+    }
+    return result & ~Masked_Attr;
+}
+
+#ifdef USE_COLOR_STYLE
+/*
+ * Return a string that corresponds to the attributes that would be returned by
+ * LYgetTableAttr().
+ */
+char *LYgetTableString(int code)
+{
+    int mask = decode_mono_code(code);
+    int second = encode_color_attr(mask);
+    int pair = PAIR_NUMBER(second);
+    int mono = (int) (mask & A_ATTRIBUTES);
+    int fg = lynx_color_pairs[pair].fg;
+    int bg = lynx_color_pairs[pair].bg;
+    unsigned n;
+    char *result = 0;
+
+    CTRACE((tfp, "LYgetTableString(%d)\n", code));
+
+    if (fg == 0 && bg == 0) {
+	fg = COLOR_WHITE;
+    }
+    CTRACE((tfp, "%#x -> %#x (mono %#x pair %d) fg=%d, bg=%d\n",
+	    mask, second, mono, pair, fg, bg));
+    for (n = 0; n < TABLESIZE(Mono_Attrs); ++n) {
+	if ((Mono_Attrs[n].code & mono) != 0) {
+	    if (result != 0)
+		StrAllocCat(result, "+");
+	    StrAllocCat(result, Mono_Attrs[n].name);
+	}
+    }
+    if (result == 0)
+	StrAllocCopy(result, "normal");
+    StrAllocCat(result, ":");
+    StrAllocCat(result, lookup_color(fg));
+    if (bg >= 0) {
+	StrAllocCat(result, ":");
+	StrAllocCat(result, lookup_color(bg));
+    }
+    CTRACE((tfp, "->%s\n", result));
+    return result;
+}
+#endif
+
+/*
+ * Initialize a curses color-pair based on our configured color values.
+ */
+static void lynx_init_color_pair(int n)
+{
+#ifdef USE_COLOR_STYLE
+    (void) n;			/* we only use lynx_color_pairs[] data */
+#else
+    int m;
+
+    if (lynx_called_initscr) {
+	for (m = 0; m <= 16; m += 8) {
+	    int pair = n + m + 1;
+
+	    if (pair < COLOR_PAIRS)
+		init_pair((short) pair,
+			  (short) map2bold(lynx_color_pairs[pair].fg),
+			  (short) map2bold(lynx_color_pairs[pair].bg));
+	}
+	if (n == 0 && LYShowColor >= SHOW_COLOR_ON) {
+	    wbkgd(LYwin, COLOR_BKGD | ' ');
+	}
+    }
+#endif
+}
+
+static void lynx_map_color(int n)
+{
+    int j;
+
+    CTRACE((tfp, "lynx_map_color(%d)\n", n));
+
+    if (n + 1 < (int) TABLESIZE(lynx_color_pairs)) {
+	for (j = n + 1; j < COLOR_PAIRS_MAX; j += COLOR_CFG_MAX) {
+	    lynx_color_pairs[j].fg = lynx_color_cfg[n].fg;
+	    lynx_color_pairs[j].bg = lynx_color_cfg[n].bg;
+	}
+
+	/* special case (does not apply to 3rd set) */
+	lynx_color_pairs[n + 1 + COLOR_CFG_MAX].bg = lynx_color_cfg[0].bg;
+    }
+
+    lynx_init_color_pair(n);
+}
+
+/*
+ * Change a configured color value.  This may be called before initscr(), so
+ * we may not be able to call init_pair() to finish the change.
+ */
+int lynx_chg_color(int color,
+		   int fg,
+		   int bg)
+{
+    CTRACE((tfp, "lynx_chg_color(color=%d, fg=%d, bg=%d)\n", color, fg, bg));
+
+    if (fg == ERR_COLOR || bg == ERR_COLOR)
+	return -1;
+    if (color >= 0 && color < COLOR_CFG_MAX) {
+	lynx_color_cfg[color].fg = fg;
+	lynx_color_cfg[color].bg = bg;
+	lynx_map_color(color);
+    } else {
+	return -1;
+    }
+    return 0;
+}
+
+void lynx_set_color(int a)
+{
+    if (lynx_has_color && LYShowColor >= SHOW_COLOR_ON) {
+	(void) wattrset(LYwin, lynx_color_cfg_attr(a)
+			| (((a + 1) < COLOR_PAIRS)
+			   ? (chtype) get_color_pair(a + 1)
+			   : A_NORMAL));
+    }
+}
+
+void lynx_standout(int flag)
+{
+    if (flag)
+	LYaddAttr(A_REVERSE);
+    else
+	LYsubAttr(A_REVERSE);
+}
+
+static void lynx_init_colors(void)
+{
+    if (lynx_has_color) {
+	size_t n;
+
+	CTRACE((tfp, "lynx_init_colors (default %d/%d)\n",
+		default_fg, default_bg));
+
+	lynx_color_cfg[0].fg = default_fg;
+	lynx_color_cfg[0].bg = default_bg;
+
+	for (n = 0; n < TABLESIZE(lynx_color_cfg); n++) {
+	    lynx_init_color_pair((int) n);
+	}
+    } else if (LYShowColor != SHOW_COLOR_NEVER) {
+	LYShowColor = SHOW_COLOR_OFF;
+    }
+}
+
+void lynx_setup_colors(void)
+{
+    int n;
+
+    CTRACE((tfp, "lynx_setup_colors\n"));
+#ifdef USE_DEFAULT_COLORS
+    if (!LYuse_default_colors) {
+	for (n = 0; n < COLOR_CFG_MAX; n++) {
+	    if (lynx_color_cfg[n].dft_fg)
+		lynx_color_cfg[n].fg = COLOR_BLACK;
+	    if (lynx_color_cfg[n].dft_bg)
+		lynx_color_cfg[n].bg = COLOR_WHITE;
+	}
+    }
+#endif
+    for (n = 0; n < COLOR_CFG_MAX; n++)
+	lynx_map_color(n);
+}
+#endif /* USE_COLOR_TABLE */
+
+void LYnoVideo(int a)
+{
+    CTRACE((tfp, "LYnoVideo(%d)\n", a));
+#ifdef USE_SLANG
+    if (a & 1)
+	Masked_Attr |= SLTT_BOLD_MASK;
+    if (a & 2)
+	Masked_Attr |= SLTT_REV_MASK;
+    if (a & 4)
+	Masked_Attr |= SLTT_ULINE_MASK;
+    lynx_setup_attrs();
+#else
+#ifdef USE_COLOR_TABLE
+    Masked_Attr = decode_mono_code(a);
+#endif
+#endif
+}
+
+#define NEWTERM_NAME "newterm"
+#if       !defined(VMS) && !defined(USE_SLANG)
+/*
+ * If newterm is not defined, assume a curses subset which
+ * supports only initscr.  --gil
+ */
+#if defined(HAVE_NEWTERM) && defined(HAVE_DELSCREEN) && !defined(PDCURSES) && !(defined(NCURSES) && defined(HAVE_RESIZETERM))
+static SCREEN *LYscreen = NULL;
+
+#define LYDELSCR() { \
+if (recent_sizechange) { \
+    CTRACE((tfp, "Screen size: delscreen()\n")); \
+    delscreen(LYscreen); \
+    LYscreen = NULL; } }
+/*
+ * Surrogates for newterm and delscreen
+ */
+#else /* HAVE_NEWTERM   */
+static WINDOW *LYscreen = NULL;
+
+#undef  NEWTERM_NAME
+#define NEWTERM_NAME "initscr"
+#undef  newterm
+#define newterm(type, out, in) (initscr())
+#define LYDELSCR()		/* nothing */
+#endif /* HAVE_NEWTERM   */
+#else /* !defined(VMS) && !defined(USE_SLANG) */
+/*
+ * Provide last recourse definitions of LYscreen and LYDELSCR for
+ * stop_curses, which only tests LYscreen for zero/nonzero but
+ * never uses it as a pointer or L-value.
+ */
+#define LYscreen TRUE
+#define LYDELSCR()		/* nothing */
+#endif /* !defined(VMS) && !defined(USE_SLANG) */
+
+#if defined(PDCURSES) && defined(PDC_BUILD) && PDC_BUILD >= 2401
+int saved_scrsize_x = 0;
+int saved_scrsize_y = 0;
+#endif
+
+void start_curses(void)
+{
+#ifdef USE_SLANG
+    static int slinit;
+
+    if (LYCursesON) {
+	CTRACE((tfp, "start_curses: Hmm, already ON.\n"));
+	return;
+    }
+
+    if (slinit == 0) {
+#if defined(HAVE_TTYNAME)
+	if (isatty(fileno(stdout)) && LYReopenInput() < 0) {
+	    fprintf(stderr, "Cannot open tty input\n");
+	    exit_immediately(EXIT_FAILURE);
+	}
+#endif
+#if defined(USE_KEYMAPS)
+	if (-1 == lynx_initialize_keymaps())
+	    exit_immediately(EXIT_FAILURE);
+#else
+	SLtt_get_terminfo();
+#endif
+#if (defined(__DJGPP__) && !defined(DJGPP_KEYHANDLER)) || defined(__CYGWIN__)
+	SLkp_init();
+#endif /* __DJGPP__ && !DJGPP_KEYHANDLER */
+
+#if defined(REAL_UNIX_SYSTEM) && !defined(__CYGWIN__)
+#if SLANG_VERSION >= 9935
+	SLang_TT_Read_FD = fileno(stdin);
+#endif /* SLANG_VERSION >= 9935 */
+#endif /* REAL_UNIX_SYSTEM && !__CYGWIN__ */
+
+#if !defined(USE_KEYMAPS) && defined(ENHANCED_LINEEDIT) && defined(ESCDELAY)
+	/* way to get ESC that's not part of a recognized sequence through */
+	ESCDELAY = 2000;
+#endif
+	/*
+	 * Check whether a saved show_color:off override is in effect.  - kw
+	 */
+	if (LYrcShowColor == SHOW_COLOR_NEVER) {
+	    SLtt_Use_Ansi_Colors = 0;
+	}
+	/*
+	 * Check whether we're forcing color on.  - FM
+	 */
+	if ((LYShowColor > 1) && (Lynx_Color_Flags & SL_LYNX_USE_COLOR))
+	    SLtt_Use_Ansi_Colors = 1;
+	/*
+	 * Check whether a -nocolor override is in effect.  - kw
+	 */
+	if (Lynx_Color_Flags & SL_LYNX_OVERRIDE_COLOR)
+	    SLtt_Use_Ansi_Colors = 0;
+	/*
+	 * Make sure our flags are in register.  - FM
+	 */
+	if (SLtt_Use_Ansi_Colors == 1) {
+	    if (LYShowColor != SHOW_COLOR_ALWAYS) {
+		LYShowColor = SHOW_COLOR_ON;
+	    }
+	} else {
+	    if (LYShowColor != SHOW_COLOR_NEVER) {
+		LYShowColor = SHOW_COLOR_OFF;
+	    }
+	}
+	size_change(0);
+
+#if (defined(VMS) || defined(REAL_UNIX_SYSTEM)) && !defined(__CYGWIN__)
+	if ((Masked_Attr & SLTT_ULINE_MASK) == 0) {
+	    SLtt_add_color_attribute(4, SLTT_ULINE_MASK);
+	    SLtt_add_color_attribute(5, SLTT_ULINE_MASK);
+	}
+	/*
+	 * If set, the blink escape sequence will turn on high intensity
+	 * background (rxvt and maybe Linux console).
+	 */
+	SLtt_Blink_Mode = term_blink_is_boldbg;
+#endif /* (VMS || REAL_UNIX_SYSTEM) && !__CYGWIN__  */
+    }
+#ifdef __DJGPP__
+    _eth_init();
+#endif /* __DJGPP__ */
+
+    slinit = 1;
+    Current_Attr = 0;
+#ifndef VMS
+#if SLANG_VERSION > 9929
+    SLang_init_tty(-1, 0, 1);
+#else
+    SLang_init_tty(3, 0, 1);
+#endif /* SLANG_VERSION > 9929 */
+#endif /* !VMS */
+    SLsmg_init_smg();
+    SLsmg_Display_Eight_Bit = LYlowest_eightbit[current_char_set];
+    if (SLsmg_Display_Eight_Bit > 191)
+	SLsmg_Display_Eight_Bit = 191;	/* may print ctrl chars otherwise - kw */
+    scrollok(0, 0);
+    SLsmg_Backspace_Moves = 1;
+#if SLANG_VERSION > 10306
+    SLsmg_touch_screen();
+#endif
+#ifndef VMS
+#if defined(REAL_UNIX_SYSTEM) && !defined(__CYGWIN__)
+    SLtty_set_suspend_state(1);
+#endif /* REAL_UNIX_SYSTEM && !__CYGWIN__ */
+#ifdef SIGTSTP
+    if (!no_suspend)
+	signal(SIGTSTP, sl_suspend);
+#endif /* SIGTSTP */
+    signal(SIGINT, cleanup_sig);
+#endif /* !VMS */
+
+    lynx_enable_mouse(1);
+
+#else /* USE_SLANG; Now using curses: */
+    int keypad_on = 0;
+
+#ifdef VMS
+    /*
+     * If we are VMS then do initscr() everytime start_curses() is called!
+     */
+    CTRACE((tfp, "Screen size: initscr()\n"));
+    initscr();			/* start curses */
+#else /* Unix: */
+
+#if defined(HAVE_TTYNAME)
+    if (isatty(fileno(stdout)) && LYReopenInput() < 0) {
+	fprintf(stderr, "Cannot open tty input\n");
+	exit_immediately(EXIT_FAILURE);
+    }
+#endif
+
+#ifdef __CYGWIN__
+    /*
+     * Workaround for buggy Cygwin, which breaks subprocesses of a
+     * full-screen application (tested with cygwin dll, dated
+     * 2002/6/23 -TD)
+     */
+    if (!lynx_called_initscr) {
+	FILE *fp = fopen("/dev/tty", "w");
+
+	if (fp != 0)
+	    stdout = fp;
+    }
+#endif
+
+    if (!LYscreen) {
+	/*
+	 * If we're not VMS then only do initscr() one time, and one time only!
+	 */
+#if defined(HAVE_NEWTERM)
+#if !(defined(NCURSES) && !defined(HAVE_RESIZETERM))
+	BOOLEAN savesize;
+
+	savesize = recent_sizechange;
+	size_change(0);
+	recent_sizechange = savesize;	/* avoid extra redraw */
+#if defined(__MVS__)
+	{
+	    /*
+	     * The requirement to do this may be a bug in OS/390.
+	     *
+	     * Put screen geometry in environment variables used by
+	     * XOpen curses before calling newterm().  I believe this
+	     * completes work left unfinished by AJL & FM -- gil
+	     */
+	    static char lines_putenv[] = "LINES=abcde", cols_putenv[] = "COLUMNS=abcde";
+
+	    sprintf(lines_putenv + 6, "%d", LYlines & 0xfff);
+	    sprintf(cols_putenv + 8, "%d", LYcols & 0xfff);
+	    putenv(lines_putenv);
+	    putenv(cols_putenv);
+	    CTRACE((tfp, "start_curses putenv %s, %s\n", lines_putenv, cols_putenv));
+	}
+#endif /* defined(__MVS__) */
+#endif /* !(defined(NCURSES) && defined(HAVE_RESIZETERM)) */
+	CTRACE((tfp, "Screen size: %s()\n", NEWTERM_NAME));
+	if (!(LYscreen = newterm(NULL, stdout, stdin))) {	/* start curses */
+	    fprintf(tfp, "%s\n",
+		    gettext("Terminal initialisation failed - unknown terminal type?"));
+	    exit_immediately(EXIT_FAILURE);
+	}
+#else
+	CTRACE((tfp, "Screen size: initscr()\n"));
+	initscr();
+#endif /* HAVE_NEWTERM */
+	lynx_called_initscr = TRUE;
+	LYlines = LYscreenHeight();
+	LYcols = LYscreenWidth();
+
+#if defined(SIGWINCH) && defined(NCURSES_VERSION)
+	size_change(0);
+	recent_sizechange = FALSE;	/* prevent mainloop drawing 1st doc twice */
+#endif /* SIGWINCH */
+	CTRACE((tfp, "Screen size is now %d x %d\n", LYlines, LYcols));
+
+#ifdef USE_CURSES_PADS
+	if (LYuseCursesPads) {
+	    LYwin = newpad(LYlines, MAX_COLS);
+	    LYshiftWin = 0;
+	    LYwideLines = FALSE;
+	} else {
+	    LYwin = stdscr;
+	}
+#endif
+
+#if defined(USE_KEYMAPS) && defined(NCURSES_VERSION)
+#  if HAVE_KEYPAD
+	/* Need to switch keypad on before initializing keymaps, otherwise
+	   when the keypad is switched on, some keybindings may be overriden. */
+	keypad(LYwin, TRUE);
+	keypad_on = 1;
+#  endif			/* HAVE_KEYPAD */
+
+	if (-1 == lynx_initialize_keymaps()) {
+	    endwin();
+	    exit_immediately(EXIT_FAILURE);
+	}
+#endif
+
+	/*
+	 * This is a workaround for a bug in SVr4 curses, observed on Solaris
+	 * 2.4:  if your terminal's alternate-character set contains codes in
+	 * the range 128-255, they'll be sign-extended in the acs_map[] table,
+	 * which in turn causes their values to be emitted as 255 (0xff).
+	 * "Fix" this by forcing the table to 8-bit codes (it has to be
+	 * anyway).
+	 */
+#if defined(ALT_CHAR_SET) && !defined(NCURSES_VERSION)
+	{
+	    int n;
+
+	    for (n = 0; n < 128; n++)
+		if (ALT_CHAR_SET[n] & 0x80) {
+		    ALT_CHAR_SET[n] &= 0xff;
+		    ALT_CHAR_SET[n] |= A_ALTCHARSET;
+		}
+	}
+#endif
+
+#if defined(USE_COLOR_STYLE) || defined(USE_COLOR_TABLE)
+	if (has_colors()) {
+	    lynx_has_color = TRUE;
+	    start_color();
+
+#ifndef COLORS
+	    /* map2boldc() relies on COLORS being a power of 2 */
+	    if (COLORS > 16)
+		COLORS = 16;
+	    if (COLORS < 8)
+		COLORS = 2;
+	    if (COLORS > 8 && COLORS != 16)
+		COLORS = 8;
+#endif
+
+#ifdef USE_DEFAULT_COLORS
+	    if (LYuse_default_colors) {
+#if defined(EXP_ASSUMED_COLOR) && defined(USE_COLOR_TABLE)
+		/*
+		 * Adjust the color mapping table to match the ASSUMED_COLOR
+		 * setting in lynx.cfg
+		 */
+		if (assume_default_colors(default_fg, default_bg) != OK) {
+		    default_fg = COLOR_WHITE;
+		    default_bg = COLOR_BLACK;
+		}
+		CTRACE((tfp, "initializing default colors %d/%d\n",
+			default_fg, default_bg));
+		if (default_fg >= 0 || default_bg >= 0) {
+		    unsigned n;
+
+		    for (n = 0; n < TABLESIZE(lynx_color_cfg); n++) {
+			if (default_fg >= 0 && lynx_color_cfg[n].fg < 0)
+			    lynx_color_cfg[n].fg = default_fg;
+			if (default_bg >= 0 && lynx_color_cfg[n].bg < 0)
+			    lynx_color_cfg[n].bg = default_bg;
+			CTRACE((tfp, "color_cfg[%u] = %d/%d\n", n,
+				lynx_color_cfg[n].fg,
+				lynx_color_cfg[n].bg));
+		    }
+		    lynx_setup_colors();
+		}
+#else
+#if defined(HAVE_USE_DEFAULT_COLORS)
+		if (!default_color_reset) {
+		    if (lynx_called_initscr) {
+			if (LYuse_default_colors && (use_default_colors() == OK)) {
+			    default_fg = DEFAULT_COLOR;
+			    default_bg = DEFAULT_COLOR;
+			} else {
+			    default_fg = COLOR_WHITE;
+			    default_bg = COLOR_BLACK;
+			    default_color_reset = TRUE;
+			}
+		    }
+		}
+#endif /* HAVE_USE_DEFAULT_COLORS */
+#endif /* EXP_ASSUMED_COLOR */
+	    }
+#endif /* USE_DEFAULT_COLORS */
+	}
+#endif /* USE_COLOR_STYLE || USE_COLOR_TABLE */
+
+#ifdef USE_COLOR_STYLE
+	/* Curses forgets color settings when we call delscreen() */
+	if (non_empty(lynx_lss_file) && LYCanReadFile(lynx_lss_file)) {
+	    style_readFromFile(lynx_lss_file);
+	}
+	parse_userstyles();
+#endif
+#ifdef USE_COLOR_TABLE
+	lynx_init_colors();
+#endif
+    }
+#ifdef __DJGPP__
+    _eth_init();
+#endif /* __DJGPP__ */
+#endif /* not VMS */
+
+/* nonl();   *//* seems to slow things down */
+
+#ifdef VMS
+    crmode();
+    raw();
+#else
+#ifdef HAVE_CBREAK
+    cbreak();
+#else
+    crmode();
+#endif /* HAVE_CBREAK */
+    signal(SIGINT, cleanup_sig);
+#endif /* VMS */
+
+    noecho();
+
+#ifdef HAVE_KEYPAD
+    if (!keypad_on)
+	keypad(LYwin, TRUE);
+#endif /* HAVE_KEYPAD */
+
+    lynx_enable_mouse(1);
+
+    fflush(stdin);
+    fflush(stdout);
+    fflush(stderr);
+#endif /* USE_SLANG */
+
+#if defined(WIN_EX)
+    LYclear();
+#endif
+
+#if defined(USE_BLINK) && defined(__EMX__)
+    if (term_blink_is_boldbg)	/* Now actually make it so! */
+	make_blink_boldbg();
+#endif
+
+    LYCursesON = TRUE;
+#if defined(PDCURSES) && defined(PDC_BUILD) && PDC_BUILD >= 2401
+    if ((scrsize_x != 0) && (scrsize_y != 0)) {
+	if (saved_scrsize_x == 0) {
+	    saved_scrsize_x = COLS;
+	    saved_scrsize_y = LINES;
+	}
+	CTRACE((tfp, "resize_term: x=%d, y=%d\n", scrsize_x, scrsize_y));
+	CTRACE((tfp, "saved terminal size: x=%d, y=%d\n", saved_scrsize_x, saved_scrsize_y));
+	resize_term(scrsize_y, scrsize_x);
+	LYclear();
+    }
+#endif
+    CTRACE((tfp, "start_curses: done.\n"));
+}				/* end of start_curses() */
+
+void lynx_enable_mouse(int state)
+{
+#ifdef USE_MOUSE
+/***********************************************************************/
+
+#if defined(WIN_EX)
+/* modify lynx_enable_mouse() for pdcurses configuration so that mouse support
+   is disabled unless -use_mouse is specified
+*/
+    HANDLE hConIn = INVALID_HANDLE_VALUE;
+
+    hConIn = GetStdHandle(STD_INPUT_HANDLE);
+    if (LYUseMouse == 0) {
+	SetConsoleMode(hConIn, ENABLE_WINDOW_INPUT);
+	FlushConsoleInputBuffer(hConIn);
+	return;
+    }
+#endif
+
+    (void) state;
+
+    if (LYUseMouse == 0)
+	return;
+
+#if defined(USE_SLANG)
+    SLtt_set_mouse_mode(state, 0);
+    SLtt_flush_output();
+#else
+
+#if defined(WIN_EX) && defined(PDCURSES)
+    if (state) {
+	SetConsoleMode(hConIn, ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT);
+	FlushConsoleInputBuffer(hConIn);
+    }
+#else
+#if defined(NCURSES)
+    if (state) {
+	/* Compensate for small value of maxclick in ncurses.  */
+	static int was = 0;
+
+	if (!was) {
+	    int old = mouseinterval(-1);
+
+	    was++;
+	    if (old < 200)	/* Default 166 */
+		mouseinterval(300);
+	}
+	/* Inform ncurses which mouse events we're interested in.
+	 * We shouldn't need to include BUTTONn_PRESSED and BUTTONn_RELEASED
+	 * events, since ncurses should translate them to click events. - kw
+	 * However, if we do not include them, then ncurses effectively
+	 * ignores mouseinterval(), thus translates *any* sequence of
+	 * press/release to a click, which leads to inconveniences.
+	 * We special-case these events in LYStrings.c.
+	 */
+	mousemask(BUTTON_CTRL | BUTTON_ALT
+		  | BUTTON1_PRESSED | BUTTON1_RELEASED
+		  | BUTTON1_CLICKED
+		  | BUTTON1_DOUBLE_CLICKED | BUTTON1_TRIPLE_CLICKED
+		  | BUTTON2_PRESSED | BUTTON2_RELEASED
+		  | BUTTON2_CLICKED
+		  | BUTTON3_PRESSED | BUTTON3_RELEASED
+		  | BUTTON3_CLICKED
+		  | BUTTON3_DOUBLE_CLICKED | BUTTON3_TRIPLE_CLICKED
+#if NCURSES_MOUSE_VERSION >= 2
+		  | BUTTON4_PRESSED | BUTTON4_RELEASED
+		  | BUTTON4_CLICKED
+		  | BUTTON4_DOUBLE_CLICKED | BUTTON4_TRIPLE_CLICKED
+		  | BUTTON5_PRESSED | BUTTON5_RELEASED
+		  | BUTTON5_CLICKED
+		  | BUTTON5_DOUBLE_CLICKED | BUTTON5_TRIPLE_CLICKED
+#endif
+		  ,NULL);
+    } else
+	mousemask(0, NULL);
+#endif /* NCURSES */
+#endif /* WIN_EX and PDCURSES */
+
+#if defined(PDCURSES)
+    if (state)
+	mouse_set(
+		     BUTTON1_CLICKED | BUTTON1_PRESSED | BUTTON1_RELEASED |
+		     BUTTON2_CLICKED | BUTTON2_PRESSED | BUTTON2_RELEASED |
+		     BUTTON3_CLICKED | BUTTON3_PRESSED | BUTTON3_RELEASED);
+#endif
+#endif /* NOT USE_SLANG */
+
+/***********************************************************************/
+#endif /* USE_MOUSE */
+}
+
+/*
+ * SVr4 curses (and ncurses) initialize the terminal I/O to raw mode, and
+ * simulate other modes in the library.  This means that when running, it
+ * simulates the OCRNL setting.  Normally that is not a problem.  However, when
+ * spawning a subprocess (e.g., xli), the subprocess may write to the screen.
+ * Fine so far - curses resets the terminal I/O to the normal state on exit.
+ * But the subprocess's messages can still be coming to the screen when lynx
+ * returns to the screen mode.  This function delays restoring OCRNL until
+ * after the first getch() call.
+ *
+ * The OCRNL setting is controlled by nl()/nonl() of course - but we do not
+ * want to give up that optimization since it would be a bit slower.  (Note -
+ * slang does not use this optimization; if it did, the same screen glitch
+ * would occur).
+ *
+ * FIXME:  for simplicity, only ncurses is implemented here - the TTY and
+ * SET_TTY definitions are ncurses-specific.  The same effect could be done for
+ * other curses implementations, since the "cur_term->Nttyb" part is common to
+ * SVr4 curses.
+ */
+void lynx_nl2crlf(int normal GCC_UNUSED)
+{
+#if defined(NCURSES_VERSION_PATCH) && defined(SET_TTY) && defined(TERMIOS) && defined(ONLCR)
+    static TTY saved_tty;
+    static int did_save = FALSE;
+    static int waiting = FALSE;
+    static int can_fix = TRUE;
+
+    if (!did_save) {
+	if (cur_term == 0) {
+	    can_fix = FALSE;
+	} else {
+	    saved_tty = cur_term->Nttyb;
+	    did_save = TRUE;
+#if NCURSES_VERSION_PATCH < 20010529
+	    /* workaround for optimizer bug with nonl() */
+	    if ((tigetstr("cud1") != 0 && *tigetstr("cud1") == '\n')
+		|| (tigetstr("ind") != 0 && *tigetstr("ind") == '\n'))
+		can_fix = FALSE;
+#endif
+	}
+    }
+    if (can_fix) {
+	if (normal) {
+	    if (!waiting) {
+		cur_term->Nttyb.c_oflag |= ONLCR;
+		waiting = TRUE;
+		nonl();
+	    }
+	} else {
+	    if (waiting) {
+		cur_term->Nttyb = saved_tty;
+		SET_TTY(fileno(stdout), &saved_tty);
+		waiting = FALSE;
+		nl();
+		LYrefresh();
+	    }
+	}
+    }
+#endif
+}
+
+void stop_curses(void)
+{
+    if (LYCursesON) {
+#ifdef USE_COLOR_STYLE
+	FreeCachedStyles();
+#endif
+	echo();
+    }
+#if defined(PDCURSES) && defined(PDC_BUILD) && PDC_BUILD >= 2401
+    resetty();
+#endif
+
+#ifdef __DJGPP__
+    _eth_release();
+#endif /* __DJGPP__ */
+
+/* ifdef's for non-Unix curses or slang */
+#if defined(__MINGW32__)
+    chtype bb;
+
+    bb = getbkgd(stdscr);
+    bkgdset(0);
+    clear();
+    refresh();
+    bkgdset(bb);
+#if defined(PDCURSES)
+    endwin();
+#endif /* PDCURSES */
+
+#elif defined(DOSPATH) && !(defined(USE_SLANG) || defined(_WIN_CC))
+
+#if defined(PDCURSES)
+    endwin();
+#endif /* PDCURSES */
+
+#ifdef __DJGPP__
+    ScreenClear();
+#elif !defined(PDCURSES)	/* some flavor of win32?  */
+    clrscr();
+#endif /* win32 */
+
+#else /* Unix, etc */
+
+    if (LYCursesON == TRUE) {
+	lynx_nl2crlf(TRUE);
+	lynx_enable_mouse(0);
+	if (LYscreen || lynx_called_initscr) {
+	    endwin();		/* stop curses */
+	    LYDELSCR();
+	}
+    } else {
+#ifdef SH_EX
+	int i;
+
+	for (i = 0; i <= 3; i++) {
+	    printf("\r\n");
+	}
+#endif
+    }
+
+    fflush(stdout);
+#endif /* ifdef's for non-Unix curses or slang */
+    fflush(stderr);
+
+    LYCursesON = FALSE;
+    CTRACE((tfp, "stop_curses: done.\n"));
+
+#if defined(SIGTSTP) && defined(USE_SLANG)
+#ifndef VMS
+    if (!no_suspend)
+	signal(SIGTSTP, SIG_DFL);
+#endif /* !VMS */
+#endif /* SIGTSTP && USE_SLANG */
+
+#ifndef VMS
+    signal(SIGINT, SIG_DFL);
+#endif /* !VMS */
+}
+
+#ifdef VMS
+
+#ifdef USE_SLANG
+extern void longname(char *, char *);
+#endif /* USE_SLANG */
+
+/*
+ * Check terminal type, start curses & setup terminal.
+ */
+BOOLEAN setup(char *terminal)
+{
+    int c;
+    int status;
+    char *dummy = 0, *cp, term[81];
+
+    /*
+     * If the display was not set by a command line option then see if it is
+     * available from the environment.
+     */
+    if ((cp = LYgetXDisplay()) != 0) {
+	StrAllocCopy(x_display, cp);
+    } else {
+	FREE(x_display);
+    }
+
+    /*
+     * Get terminal type, and convert to lower case.
+     */
+    term[0] = '\0';
+    longname(dummy, term);
+    if (term[0] == '\0' && (form_get_data || form_post_data)) {
+	/*
+	 * Some yoyo used these under conditions which require -dump, so force
+	 * that mode here.  - FM
+	 */
+	dump_output_immediately = TRUE;
+	LYcols = DFT_COLS;
+	if (keypad_mode == NUMBERS_AS_ARROWS)
+	    keypad_mode = LINKS_ARE_NUMBERED;
+	status = mainloop();
+	exit_immediately(status);
+    }
+    LYLowerCase(term);
+
+    printf("%s%s\n", gettext("Terminal ="), term);
+    if ((strlen(term) < 5) ||
+	strncmp(term, "vt", 2) || !isdigit(term[2])) {
+	printf("%s\n",
+	       gettext("You must use a vt100, 200, etc. terminal with this program."));
+	printf(CONFIRM_PROCEED, "n/y");
+	c = getchar();
+	if (c != 'y' && c != 'Y') {
+	    printf("\n");
+	    return (FALSE);
+	}
+	strcpy(term, "vt100");
+    }
+
+    ttopen();
+    start_curses();
+
+    LYlines = LYscreenHeight();
+    LYcols = LYscreenWidth();
+
+    return (TRUE);
+}
+
+#else /* Not VMS: */
+
+/*
+ * Check terminal type, start curses & setup terminal.
+ */
+BOOLEAN setup(char *terminal)
+{
+    char *term_putenv = NULL;
+    char *buffer = NULL;
+    char *cp;
+
+    /*
+     * If the display was not set by a command line option then see if it is
+     * available from the environment .
+     */
+    if ((cp = LYgetXDisplay()) != NULL) {
+	StrAllocCopy(x_display, cp);
+    } else {
+	FREE(x_display);
+    }
+
+    if (terminal != NULL) {
+	HTSprintf0(&term_putenv, "TERM=%.106s", terminal);
+	(void) putenv(term_putenv);
+    }
+
+    /*
+     * Query the terminal type.
+     */
+    if (dumbterm(LYGetEnv("TERM"))) {
+	printf("\n\n  %s\n\n", gettext("Your Terminal type is unknown!"));
+	printf("  %s [vt100] ", gettext("Enter a terminal type:"));
+
+	if (LYSafeGets(&buffer, stdin) != 0) {
+	    LYTrimLeading(buffer);
+	    LYTrimTrailing(buffer);
+	}
+
+	if (isEmpty(buffer))
+	    StrAllocCopy(buffer, "vt100");
+
+	HTSprintf0(&term_putenv, "TERM=%.106s", buffer);
+	FREE(buffer);
+
+	(void) putenv(term_putenv);
+	printf("\n%s %s\n", gettext("TERMINAL TYPE IS SET TO"),
+	       LYGetEnv("TERM"));
+	LYSleepMsg();
+    }
+
+    start_curses();
+
+#ifdef HAVE_TTYTYPE
+    /*
+     * Account for lossage on the 'sun' terminal type (80x24) Sun text console
+     * driver.  It only supports reverse video, but all SGR sequences produce
+     * that same reverse video, and the terminfo entry lists different SGRs for
+     * 'bold' and 'rev'.  As a result, the current link is indistinguishable
+     * from all other links.  The workaround here is to disable the 'rev'
+     * capability.
+     */
+    if ((strncmp((const char *) ttytype, "sun", 3) == 0)) {
+	LYnoVideo(2);
+    }
+#endif /* HAVE_TTYTYPE */
+
+    LYlines = LYscreenHeight();
+    LYcols = LYscreenWidth();
+
+    return (1);
+}
+
+static int dumbterm(char *terminal)
+{
+    int dumb = FALSE;
+
+    /*
+     * Began checking for terminal == NULL in case that TERM environment
+     * variable is not set.  Thanks to Dick Wesseling (ftu@fi.ruu.nl).
+     */
+    if (terminal == NULL ||
+	!strcasecomp(terminal, "network") ||
+	!strcasecomp(terminal, "unknown") ||
+	!strcasecomp(terminal, "dialup") ||
+	!strcasecomp(terminal, "dumb") ||
+	!strcasecomp(terminal, "switch") ||
+	!strcasecomp(terminal, "ethernet"))
+	dumb = TRUE;
+    return (dumb);
+}
+
+#ifdef FANCY_CURSES
+#ifndef USE_COLOR_STYLE
+#ifdef USE_COLOR_TABLE
+static void LYsetWAttr(WINDOW * win)
+{
+    (void) wattrset(win, LYgetTableAttr());
+}
+
+void LYaddWAttr(WINDOW * win, int a)
+{
+    Current_Attr |= a;
+    LYsetWAttr(win);
+}
+
+void LYaddAttr(int a)
+{
+    LYaddWAttr(LYwin, a);
+}
+
+void LYsubWAttr(WINDOW * win, int a)
+{
+    Current_Attr &= ~a;
+    LYsetWAttr(win);
+}
+
+void LYsubAttr(int a)
+{
+    LYsubWAttr(LYwin, a);
+}
+#endif /* USE_COLOR_TABLE */
+#endif /* !USE_COLOR_STYLE */
+#endif /* FANCY_CURSES */
+#endif /* VMS */
+
+/* Use this rather than the 'wprintw()' function to write a blank-padded
+ * string to the given window, since someone's asserted that printw doesn't
+ * handle 8-bit characters unlike addstr (though more info would be useful).
+ *
+ * We're blank-filling so that with SVr4 curses, it'll show the background
+ * color to a uniform width in the popup-menu.
+ */
+#ifndef USE_SLANG
+void LYpaddstr(WINDOW * the_window, int width, const char *the_string)
+{
+    int y, x1, x2;
+    int length = (int) strlen(the_string);
+
+#ifdef WIDEC_CURSES
+    int actual = (int) LYstrCells(the_string);
+#endif
+
+    getyx(the_window, y, x1);
+    if (width + x1 > LYcolLimit)
+	width = LYcolLimit - x1;
+#ifdef WIDEC_CURSES
+    if (actual > width) {
+	actual = width;
+	/* FIXME: a binary search might be faster */
+	while (LYstrExtent(the_string, length, length) > actual) {
+	    --length;
+	}
+    }
+#endif
+    LYwaddnstr(the_window, the_string, (size_t) length);
+    getyx(the_window, y, x2);
+    width -= (x2 - x1);
+    while (width-- > 0)
+	waddstr(the_window, " ");
+}
+
+/*
+ * Work around limitation of curses's order-of-refresh by setting a pointer to
+ * the topmost window that should be displayed.
+ *
+ * FIXME: the associated call on 'keypad()' is not needed for Unix, but
+ * something in the OS/2 EMX port requires it.
+ */
+static WINDOW *my_subwindow;
+
+void LYsubwindow(WINDOW * param)
+{
+    if (param != 0) {
+	my_subwindow = param;
+#if defined(NCURSES) || defined(PDCURSES)
+	keypad(my_subwindow, TRUE);
+#if defined(USE_COLOR_STYLE)
+	LynxWChangeStyle(my_subwindow, s_menu_bg, STACK_ON);
+	{
+	    long b = LYgetattrs(my_subwindow);
+
+	    wbkgd(my_subwindow, (chtype) (b | ' '));
+	}
+	LynxWChangeStyle(my_subwindow, s_menu_bg, STACK_OFF);
+#elif defined(HAVE_GETBKGD)	/* not defined in ncurses 1.8.7 */
+	wbkgd(my_subwindow, getbkgd(LYwin));
+#endif
+#endif
+	scrollok(my_subwindow, TRUE);
+    } else {
+	touchwin(LYwin);
+	delwin(my_subwindow);
+	my_subwindow = 0;
+    }
+}
+
+WINDOW *LYtopwindow(void)
+{
+    return (my_subwindow ? my_subwindow : LYwin);
+}
+#endif
+
+WINDOW *LYstartPopup(int *top_y,
+		     int *left_x,
+		     int *height,
+		     int *width)
+{
+    WINDOW *form_window = 0;
+
+#ifdef USE_SLANG
+    static WINDOW fake_window;
+
+    if (*left_x < 1 || (*left_x + *width + 4) >= LYcolLimit) {
+	*left_x = 1;
+	*width = LYcolLimit - 5;
+    }
+
+    SLsmg_fill_region(*top_y, *left_x - 1, *height, *width + 4, ' ');
+    form_window = &fake_window;
+    form_window->top_y = *top_y;
+    form_window->left_x = *left_x;
+    form_window->height = *height;
+    form_window->width = *width;
+#else
+    if (*left_x > 0 && (*left_x + *width + 4) < LYcolLimit)
+	form_window = newwin(*height, *width + 4, *top_y, *left_x - 1);
+    if (form_window == 0) {
+	if (*width > LYcolLimit - 4) {
+	    *width = LYcolLimit - 4;
+	    *left_x = 1;
+	} else {
+	    *left_x = LYcolLimit - 4 - *width;
+	    if (*left_x <= 0)
+		*left_x = 1;
+	}
+	form_window = newwin(*height, *width + 4, *top_y, *left_x - 1);
+    }
+    if (form_window == 0) {
+	HTAlert(POPUP_FAILED);
+    } else {
+	LYsubwindow(form_window);
+    }
+#endif /* USE_SLANG */
+    return form_window;
+}
+
+void LYstartTargetEmphasis(void)
+{
+#ifdef USE_COLOR_STYLE
+    if (s_whereis != NOSTYLE) {
+	curses_style(s_whereis, STACK_ON);
+	return;
+    }
+#endif
+#if defined(FANCY_CURSES) || defined(USE_SLANG)
+    lynx_start_bold();
+    lynx_start_reverse();
+#endif /* FANCY_CURSES || USE_SLANG */
+    lynx_start_underline();
+}
+
+void LYstopTargetEmphasis(void)
+{
+#ifdef USE_COLOR_STYLE
+    if (s_whereis != NOSTYLE) {
+	curses_style(s_whereis, STACK_OFF);
+	return;
+    }
+#endif
+    lynx_stop_underline();
+#if defined(FANCY_CURSES) || defined(USE_SLANG)
+    lynx_stop_reverse();
+    lynx_stop_bold();
+#endif /* FANCY_CURSES || USE_SLANG */
+}
+
+/*
+ * Accommodate the different flavors of touchline
+ */
+void LYtouchline(int row)
+{
+#if defined(HAVE_WREDRAWLN) && !defined(NCURSES_VERSION)
+    wredrawln(LYwin, row, 1);
+#else
+#if defined(HAVE_TOUCHLINE)
+    /* touchline() is not available on VMS before version 7.0, and then only on
+     * Alpha, since prior ports of curses were broken.  BSD touchline() has a
+     * 4th parameter since it is used internally by touchwin().
+     */
+#if defined(HAVE_BSD_TOUCHLINE)
+    touchline(LYwin, row, 0, COLS);
+#else
+    touchline(LYwin, row, 1);
+#endif
+#else
+#if !defined(USE_SLANG)
+    touchwin(LYwin);
+#else
+    SLsmg_touch_lines(row, 1);
+#endif
+#endif
+#endif
+}
+
+/*
+ * Wrapper for waddnstr().
+ */
+void LYwaddnstr(WINDOW * w GCC_UNUSED,
+		const char *src,
+		size_t len)
+{
+    int y0, x0;
+    int y, x;
+    size_t inx;
+
+#ifdef USE_CURSES_PADS
+    /*
+     * If we've configured to use pads for left/right scrolling, that can
+     * interfere with calls to this function that assume they're wrapping. 
+     * Writing to a pad which is wider than the screen will simply not wrap.
+     *
+     * Link-highlighting uses wrapping.  You can see this by viewing the
+     * options screen in a terminal which is narrower than 80 columns.
+     *
+     * Check for that case, and use curses's wrapping in a derived window to
+     * simplify things, e.g., in case the string contains multibyte or
+     * multicolumn characters.
+     */
+    getyx(LYwin, y0, x0);
+
+    if (LYuseCursesPads
+	&& (LYwin == w)
+	&& (LYshiftWin == 0)
+	&& LYwideLines == FALSE
+	&& ((int) len > (LYcolLimit - x0))
+	&& (x0 < LYcolLimit)) {
+	WINDOW *sub = derwin(LYwin, LYlines, LYcolLimit, 0, 0);
+
+	if (sub != 0) {
+	    wmove(sub, y0, x0);
+	    LYwideLines = TRUE;
+	    LYwaddnstr(sub, src, len);
+	    getyx(sub, y0, x0);
+	    delwin(sub);
+	    wmove(LYwin, y0, x0);
+	}
+	LYwideLines = FALSE;
+
+	return;
+    }
+#endif
+    /*
+     * We only want to trace this function for the color-style code.  It would
+     * be too much logging if not needed.
+     */
+#ifdef USE_COLOR_STYLE
+    if (TRACE) {
+	LYGetYX(y, x);
+	CTRACE2(TRACE_STYLE, (tfp, "[%2d,%2d] LYwaddnstr(%.*s, %u)\n",
+			      y, x, (int) len, src, (unsigned) len));
+    }
+#endif
+    LYGetYX(y0, x0);
+
+    for (inx = 0; inx < len; ++inx) {
+	/*
+	 * Do tab-expansion relative to the base of the string (rather than
+	 * the screen) so that tabs in a TEXTAREA will look right.
+	 */
+	if (src[inx] == '\t') {
+	    LYGetYX(y, x);
+	    while ((++x - x0) % 8)
+		waddch(w, ' ');
+	    waddch(w, ' ');
+	} else {
+	    waddch(w, UCH(src[inx]));
+	}
+    }
+}
+
+/*
+ * Determine the number of cells the given string would take up on the screen,
+ * limited (in the case of wide characters) by the maxCells parameter.
+ *
+ * If the returnCellNum parameter is TRUE, return the number of cells;
+ * otherwise, return the length (limited by the len parameter) of the prefix of
+ * the string that fits in maxCells cells.
+ */
+static
+int LYstrExtent0(const char *string,
+		 int len,
+		 int maxCells GCC_UNUSED,
+		 BOOL retCellNum GCC_UNUSED)
+{
+    int used = (len < 0 ? (int) strlen(string) : len);
+    int result = used;
+
+#ifdef WIDEC_CURSES
+    if (used > 0 && lynx_called_initscr) {
+	static WINDOW *fake_win;
+	static int fake_max;
+
+	if (fake_max < maxCells) {
+	    fake_max = (maxCells + 1) * 2;
+	    if (fake_win != 0) {
+		delwin(fake_win);
+		fake_win = 0;
+	    }
+	}
+	if (fake_win == 0) {
+	    fake_win = newwin(2, fake_max, 0, 0);
+	}
+	if (fake_win != 0) {
+	    int new_x = 0;
+	    int new_y = 0;
+	    int x = 0;
+	    int n;
+
+	    wmove(fake_win, 0, 0);
+	    for (n = 0; n < used; ++n) {
+		if (IsNormalChar(string[n])) {
+		    waddch(fake_win, UCH(string[n]));
+		    getyx(fake_win, new_y, new_x);
+		    if (new_y > 0 || new_x > maxCells)
+			break;
+		    x = new_x;
+		}
+	    }
+	    result = (retCellNum ? x : n);
+	}
+    }
+#endif
+    return result;
+}
+
+/*
+ * Determine the number of cells the given string would take up on the screen,
+ * limited by the maxCells parameter.  This is used for constructing aligned
+ * text in the options and similar forms.
+ *
+ * FIXME: make this account for wrapping, too.
+ * FIXME: make this useful for "lynx -dump", which hasn't initialized curses.
+ */
+int LYstrExtent(const char *string, int len, int maxCells)
+{
+    int result = LYstrExtent0(string, len, maxCells, TRUE);
+
+    return (result > maxCells ? maxCells : result);
+}
+
+/*
+ * Return the number of cells in the first 'len' bytes of the string.
+ *
+ * This relies upon the coincidence that multicell characters use at least as
+ * many bytes as cells.  But we have to account for tab, which can use 8, and
+ * control characters which use 2.
+ */
+int LYstrExtent2(const char *string, int len)
+{
+    return LYstrExtent(string, len, 8 * len);
+}
+
+/*
+ * Determine the longest prefix of a string that fits in a given number of
+ * cells and return its length.
+ */
+int LYstrFittable(const char *string, int maxCells)
+{
+    return LYstrExtent0(string, -1, maxCells, FALSE);
+}
+
+/*
+ * Returns the total number of cells that the string would use.
+ */
+int LYstrCells(const char *string)
+{
+    return LYstrExtent2(string, (int) strlen(string));
+}
+
+#ifdef VMS
+/*
+ *	Cut-down termio --
+ *		Do character-oriented stream input for Jeff.
+ *		Code ripped off from Micro-Emacs 3.7 by Daniel Lawrence.
+ *
+ *		Ever-so-slightly modified by Kathryn Huxtable.	29-Jan-1991.
+ *		Cut down for Lou.  8 Sep 1992.
+ *		Cut down farther for Lou.  19 Apr 1993.
+ *			We don't set PASSALL or PASTHRU since we don't
+ *			want to block CTRL/C, CTRL/Y, CTRL/S or CTRL/Q.
+ *			Simply setting NOECHO and doing timed reads
+ *			is sufficient.
+ *		Further mods by Fote.  29-June-1993
+ *			ttopen() and ttclose() are now terminal initialization
+ *			 and restoration procedures, called once at startup
+ *			 and at exit, respectively, of the LYNX image.
+ *			ttclose() should be called before an exit from LYNX
+ *			 no matter how the exit is invoked.
+ *			setup(terminal) does the ttopen().
+ *			cleanup() calls cleanup_files() and ttclose().
+ *			ttgetc() now handles NOECHO and NOFLITR (instead of
+ *			 setting the terminal itself to NOECHO in ttopen()).
+ *			VMSsignal() added for handling both Ctrl-C *and* Ctrl-Y
+ *			 interrupts, and disabling system response to Ctrl-T.
+ *		Further mods by Fote.  15-Dec-1993
+ *			Added edit handler in ttopen() which will invoke
+ *			 VMSexit() and behave intelligently on ACCVIO's.
+ *		Further mods by Fote.  29-Dec-1993
+ *			Simplified ttgetc().
+ *		Further mods by Fote.  16-Jan-1994
+ *			Added code in ttopen() which will invoke VMSVersion()
+ *			 to get the version of VMS as VersionVMS for use by
+ *			 by new or modified interrupt or spawning routines.
+ *		Further mods by Fote.  27-Jan-1994
+ *			Added back a typeahead() which supports 'z' or 'Z' as
+ *			an "Zap transfer" command via HTCheckForInterrupt()
+ *			in LYUtils.c.
+ */
+
+#include <descrip.h>
+#include <iodef.h>
+#include <ssdef.h>
+#include <msgdef.h>
+#include <ttdef.h>
+#include <tt2def.h>
+#include <libclidef.h>
+#include <lib$routines.h>
+#include <starlet.h>
+#include <clidef.h>
+#include <syidef.h>
+#ifdef signal
+#undef signal
+#endif /* signal */
+#include <signal.h>
+#ifdef system
+#undef system
+#endif /* system */
+#include <processes.h>
+#include <LYVMSdef.h>
+
+#define EFN	0		/* Event flag                   */
+
+static unsigned char buffer[20];	/* Input buffer                 */
+static int in_pos, in_len;	/* For escape sequences         */
+static int oldmode[3];		/* Old TTY mode bits            */
+static int newmode[3];		/* New TTY mode bits            */
+static short iochan;		/* TTY I/O channel              */
+static $DESCRIPTOR(term_nam_dsc, "TT");		/* Descriptor for iochan        */
+static unsigned long mask = LIB$M_CLI_CTRLY | LIB$M_CLI_CTRLT;	/* ^Y and ^T */
+static unsigned long old_msk;	/* Saved control mask           */
+static short trap_flag = FALSE;	/* TRUE if AST is set           */
+BOOLEAN DidCleanup = FALSE;	/* Exit handler flag            */
+static char VersionVMS[20];	/* Version of VMS               */
+
+int VMSVersion(char *VerString,
+	       int VerLen)
+{
+    unsigned long status, itm_cod = SYI$_VERSION;
+    int i, verlen = 0;
+    struct dsc$descriptor version;
+    char *m;
+
+    version.dsc$a_pointer = VerString;
+    version.dsc$w_length = VerLen - 1;
+    version.dsc$b_dtype = DSC$K_DTYPE_B;
+    version.dsc$b_class = DSC$K_CLASS_S;
+
+    status = lib$getsyi(&itm_cod, 0, &version, &verlen, 0, 0);
+    if (!(status & 1) || verlen == 0)
+	return 0;
+
+    /*
+     * Cut out trailing spaces
+     */
+    for (m = VerString + verlen, i = verlen - 1; i > 0 && VerString[i] == ' '; --i)
+	*(--m) = '\0';
+
+    return strlen(VerString) + 1;	/* Transmit ending 0 too */
+}
+
+void VMSexit(void)
+{
+    /*
+     * If we get here and DidCleanup is not set, it was via an ACCVIO, or
+     * outofmemory forced exit, so make *sure* we attempt a cleanup and reset
+     * the terminal.
+     */
+    if (!DidCleanup) {
+	if (LYOutOfMemory == FALSE) {
+	    fprintf(stderr,
+		    gettext("\nA Fatal error has occurred in %s Ver. %s\n"),
+		    LYNX_NAME, LYNX_VERSION);
+	    fprintf(stderr,
+		    gettext("\nPlease notify your system administrator to confirm a bug, and if\n\
+confirmed, to notify the lynx-dev list.  Bug reports should have concise\n\
+descriptions of the command and/or URL which causes the problem, the\n\
+operating system name with version number, the TCPIP implementation, the\n\
+TRACEBACK if it can be captured, and any other relevant information.\n"));
+
+	    if (LYTraceLogFP == NULL) {
+		fprintf(stderr, RETURN_TO_CLEANUP);
+		(void) getchar();
+	    }
+	} else if (LYCursesON) {
+	    HTAlert(MEMORY_EXHAUSTED_ABORT);
+	}
+	cleanup();
+    }
+    if (LYOutOfMemory == TRUE) {
+	printf("\r\n%s\r\n\r\n", MEMORY_EXHAUSTED_ABORT);
+	fflush(stdout);
+	fflush(stderr);
+    }
+}
+
+/*
+ *	TTOPEN --
+ *		This function is called once to set up the terminal
+ *		device streams.  It translates TT until it finds
+ *		the terminal, then assigns a channel to it, sets it
+ *		to EDIT, and sets up the Ctrl-C and Ctrl-Y interrupt
+ *		handling.
+ */
+int ttopen(void)
+{
+    int iosb[2];
+    int status;
+    static unsigned long condition;
+    static struct _exit_block {
+	unsigned long forward;
+	unsigned long address;
+	unsigned long zero;
+	unsigned long condition;
+    } exit_handler_block;
+
+    status = sys$assign(&term_nam_dsc, &iochan, 0, 0);
+    if (status != SS$_NORMAL)
+	exit_immediately(status);
+
+    status = sys$qiow(EFN, iochan, IO$_SENSEMODE, &iosb, 0, 0,
+		      &oldmode, sizeof(oldmode), 0, 0, 0, 0);
+    if (status != SS$_NORMAL)
+	exit_immediately(status);
+
+    status = iosb[0] & 0xFFFF;
+    if (status != SS$_NORMAL)
+	exit_immediately(status);
+
+    newmode[0] = oldmode[0];
+    newmode[1] = oldmode[1];
+    newmode[2] = oldmode[2] | TT2$M_EDIT;
+
+    status = sys$qiow(EFN, iochan, IO$_SETMODE, &iosb, 0, 0,
+		      &newmode, sizeof(newmode), 0, 0, 0, 0);
+    if (status != SS$_NORMAL)
+	exit_immediately(status);
+
+    status = iosb[0] & 0xFFFF;
+    if (status != SS$_NORMAL)
+	exit_immediately(status);
+
+    /*
+     * Declare the exit handler block.
+     */
+    exit_handler_block.forward = 0;
+    exit_handler_block.address = (unsigned long) &VMSexit;
+    exit_handler_block.zero = 0;
+    exit_handler_block.condition = (unsigned long) &condition;
+    status = sys$dclexh(&exit_handler_block);
+    if (status != SS$_NORMAL)
+	exit_immediately(status);
+
+    /*
+     * Set the AST.
+     */
+    lib$disable_ctrl(&mask, &old_msk);
+    trap_flag = TRUE;
+    status = sys$qiow(EFN, iochan,
+		      IO$_SETMODE | IO$M_CTRLCAST | IO$M_CTRLYAST,
+		      &iosb, 0, 0,
+		      &cleanup_sig, SIGINT, 0, 0, 0, 0);
+    if (status != SS$_NORMAL) {
+	lib$enable_ctrl(&old_msk);
+	exit_immediately(status);
+    }
+
+    /*
+     * Get the version of VMS.
+     */
+    if (VMSVersion(VersionVMS, 20) < 3)
+	/*
+	 * Load zeros on error.
+	 */
+	strcpy(VersionVMS, "V0.0-0");
+
+    return (0);
+}				/*  ttopen  */
+
+/*
+ *	TTCLOSE --
+ *		This function gets called just before we go back home
+ *		to the command interpreter.  It puts the terminal back
+ *		in a reasonable state.
+ */
+int ttclose(void)
+{
+    int status;
+    int iosb[1];
+
+    status = sys$qiow(EFN, iochan, IO$_SETMODE, &iosb, 0, 0,
+		      &oldmode, sizeof(oldmode), 0, 0, 0, 0);
+
+    if (status != SS$_NORMAL || (iosb[0] & 0xFFFF) != SS$_NORMAL)
+	exit_immediately(status);
+
+    if (trap_flag) {
+	status = sys$dassgn(iochan);
+	status = lib$enable_ctrl(&old_msk);
+	trap_flag = FALSE;
+    }
+    return (0);
+}				/*  ttclose  */
+
+/*
+ *	TTGETC --
+ *		Read a character from the terminal, with NOECHO and NOFILTR.
+ */
+int ttgetc(void)
+{
+    int status;
+    unsigned short iosb[4];
+
+    if (in_pos < in_len)
+	return (buffer[in_pos++]);
+
+    status = sys$qiow(EFN, iochan,
+		      IO$_READVBLK | IO$M_NOECHO | IO$M_NOFILTR,
+		      &iosb, 0, 0,
+		      &buffer, 1, 0, 0, 0, 0);
+    if ((status & 1) == 1)
+	status = iosb[0];
+    if (status == SS$_PARTESCAPE) {
+	/*
+	 * Escape sequence in progress.  Fake a successful read.
+	 */
+	status = 1;
+    }
+    if ((status & 1) != 1 && status != SS$_DATAOVERUN)
+	exit_immediately(status);
+    in_pos = 1;
+    in_len = iosb[1] + iosb[3];
+    return (buffer[0]);
+}
+
+/*
+ *	TYPEAHEAD -- Fote Macrides 27-Jan-1994
+ *		Check whether a keystroke has been entered, and return
+ *		 it, or -1 if none was entered.
+ */
+int typeahead(void)
+{
+    int status;
+    unsigned short iosb[4];
+
+    if (dump_output_immediately)
+	return -1;
+
+    if (in_pos < in_len)
+	return (buffer[in_pos++]);
+
+  again:
+    status = sys$qiow(EFN, iochan,
+		      IO$_READVBLK | IO$M_TIMED | IO$M_NOECHO | IO$M_NOFILTR,
+		      &iosb, 0, 0,
+		      &buffer, 1, 0, 0, 0, 0);
+    if ((status & 1) == 1)
+	status = iosb[0];
+    if (status == SS$_PARTESCAPE) {
+	/*
+	 * Escape sequence in progress, finish reading it.
+	 */
+	goto again;
+    }
+
+    in_pos = 1;
+    in_len = iosb[1] + iosb[3];
+    if (status == SS$_TIMEOUT || status == SS$_DATAOVERUN)
+	return (-1);
+    return (buffer[0]);
+}
+
+/*
+ *	VMSSIGNAL -- Fote Macrides 29-Jun-1993
+ *		Sets up AST for both Ctrl-C and Ctrl-Y, with system response
+ *		 to Ctrl-T disabled.  If called with a sig other than SIGINT,
+ *		 it will use the C library's system(sig, func).
+ *		The equivalent of VMSsignal(SIGINT, cleanup_sig) is done on
+ *		 intialization by ttopen(), so don't do it again.
+ *		VMSsignal(SIGINT, SIG_DFL) is treated as a call to ttclose().
+ *		Call VMSsignal(SIGINT, SIG_IGN) before system() calls to
+ *		 enable Ctrl-C and Ctrl-Y in the subprocess, and then call
+ *		 VMSsignal(SIG_INT, cleanup_sig) on return from the subprocess.
+ *		For func's which set flags and do not invoke an exit from
+ *		 LYNX, the func should reassert itself.
+ *		The VMS signal() calls do not fully emulate the Unix calls,
+ *		 and VMSsignal() is just a "helper", also not a full emulation.
+ */
+
+void VMSsignal(int sig,
+	       void (*func) ())
+{
+    int status;
+    short iosb[4];
+    static int SIG_IGN_flag;
+
+    /*
+     * Pass all signals other than SIGINT to signal().
+     * Also pass SIGINT to signal() if we're dumping.
+     */
+    if (sig != SIGINT || dump_output_immediately) {
+	signal(sig, func);
+	return;
+    }
+
+    /*
+     * If func is SIG_DFL, treat it as ttclose().
+     */
+    if (func == SIG_DFL) {
+	ttclose();
+	return;
+    }
+
+    /*
+     * Clear any previous AST.
+     */
+    if (trap_flag) {
+	status = sys$dassgn(iochan);
+	status = lib$enable_ctrl(&old_msk);
+	trap_flag = FALSE;
+    }
+
+    /*
+     * If func is SIG_IGN, leave the TT channel closed and the system response
+     * to interrupts enabled for system() calls.
+     */
+    if (func == SIG_IGN)
+	return;
+
+    /*
+     * If we get to here, we have a LYNX func, so set the AST.
+     */
+    lib$disable_ctrl(&mask, &old_msk);
+    trap_flag = TRUE;
+    status = sys$assign(&term_nam_dsc, &iochan, 0, 0);
+    status = sys$qiow(EFN, iochan,
+		      IO$_SETMODE | IO$M_CTRLCAST | IO$M_CTRLYAST,
+		      &iosb, 0, 0,
+		      func, SIGINT, 0, 0, 0, 0);
+
+}				/* VMSsignal */
+
+/*
+ * DCLspawn_exception, spawn_DCLprocess, DCLsystem -- F.Macrides 16-Jan-1994
+ *	Exception-handler routines for regulating interrupts and enabling
+ *	Control-T during spawns.  Includes TRUSTED flag for versions of VMS
+ *	which require it in captive accounts.  This code should be used
+ *	instead of the VAXC or DECC system(), by including LYUtils.h in
+ *	modules which have system() calls.  It helps ensure that we return
+ *	to Lynx instead of breaking out to DCL if a user issues interrupts
+ *	or generates an ACCVIO during spawns.
+ */
+#ifdef __DECC
+static unsigned int DCLspawn_exception(void *sigarr,
+				       void *mecharr)
+#else
+static int DCLspawn_exception(void *sigarr,
+			      void *mecharr)
+#endif				/* __DECC */
+{
+    int status;
+
+    status = lib$sig_to_ret(sigarr, mecharr);
+    return (SS$_UNWIND);
+}
+
+static int spawn_DCLprocess(char *command)
+{
+    int status;
+    unsigned long Status = 0;
+
+    /*
+     * Keep DECC from complaining.
+     */
+    struct dsc$descriptor_s command_desc;
+
+    command_desc.dsc$w_length = strlen(command);
+    command_desc.dsc$b_class = DSC$K_CLASS_S;
+    command_desc.dsc$b_dtype = DSC$K_DTYPE_T;
+    command_desc.dsc$a_pointer = command;
+
+    VAXC$ESTABLISH(DCLspawn_exception);
+
+#ifdef __ALPHA /** OpenVMS/AXP lacked the TRUSTED flag before v6.1 **/
+    if (VersionVMS[1] > '6' ||
+	(VersionVMS[1] == '6' && VersionVMS[2] == '.' &&
+	 VersionVMS[3] >= '1'))
+#else
+    if (VersionVMS[1] >= '6')
+#endif /* __ALPHA */
+    {
+	/*
+	 * Include TRUSTED flag.
+	 */
+	unsigned long trusted = CLI$M_TRUSTED;
+
+	status = lib$spawn(&command_desc, 0, 0, &trusted,
+			   0, 0, &Status);
+	/*
+	 * If it was invalid, try again without the flag.
+	 */
+	if (status == LIB$_INVARG)
+	    status = lib$spawn(&command_desc, 0, 0, 0,
+			       0, 0, &Status);
+    } else
+	status = lib$spawn(&command_desc, 0, 0, 0,
+			   0, 0, &Status);
+    /*
+     * Return -1 on error.
+     */
+    if ((status & 1) != 1 || (Status & 1) != 1)
+	return (-1);
+    /*
+     * Return 0 on success.
+     */
+    return (0);
+}
+
+int DCLsystem(char *command)
+{
+    int status;
+
+    VMSsignal(SIGINT, SIG_IGN);
+    status = spawn_DCLprocess(command);
+    VMSsignal(SIGINT, cleanup_sig);
+    /*
+     * Returns 0 on success, -1 any error.
+     */
+    return (status);
+}
+#endif /* VMS */
+
+/*
+ * Return the physical screen dimensions that we're allowed to use.
+ */
+int LYscreenHeight(void)
+{
+    int result = LINES;
+
+    if (result <= 0)
+	result = DFT_ROWS;
+    return result;
+}
+
+int LYscreenWidth(void)
+{
+    int result = COLS;
+
+#if defined(PDCURSES_EXP) && defined(WIN_EX) && defined(CJK_EX)		/* 1999/08/26 (Thu) 17:53:38 */
+    {
+	extern int current_codepage;	/* PDCurses lib. */
+
+	if (current_codepage == 932)
+	    result--;
+    }
+#endif
+    if (result <= 0)
+	result = DFT_COLS;
+    return result;
+}
+
+/*
+ * Set the window's background color (make the pad's color agree), e.g., when
+ * we have just parsed it from the config file, or after clearing the screen.
+ */
+void LYnormalColor(void)
+{
+#if defined(USE_COLOR_STYLE) && defined(USE_CURSES_PADS)
+    if (LYwin != stdscr) {
+	int color = displayStyles[DSTYLE_NORMAL].color;
+
+	if (color >= 0) {
+	    wbkgd(LYwin, (chtype) (color | ' '));
+	    LYrefresh();
+	}
+    }
+#endif
+}
+
+/*
+ * The functions ifdef'd with USE_CURSES_PADS are implemented that way so we
+ * don't break the slang configuration.
+ */
+void LYclear(void)
+{
+#ifdef USE_CURSES_PADS
+    wclear(LYwin);
+#else
+    clear();
+#endif
+    LYnormalColor();
+}
+
+void LYclrtoeol(void)
+{
+#ifdef USE_CURSES_PADS
+    wclrtoeol(LYwin);
+#else
+    clrtoeol();
+#endif
+}
+
+void LYerase(void)
+{
+#ifdef USE_CURSES_PADS
+    werase(LYwin);
+#else
+    erase();
+#endif
+    LYnormalColor();
+}
+
+void LYmove(int y, int x)
+{
+#ifdef USE_CURSES_PADS
+    wmove(LYwin, y, x);
+#else
+    move(y, x);
+#endif
+}
+
+void LYrefresh(void)
+{
+#ifdef USE_CURSES_PADS
+    if (LYwin != stdscr) {
+	/*
+	 * Workaround for special case where lynx is prompting for a mailto,
+	 * and has a subject line that is wider than the screen.  The
+	 * wnoutrefresh() call resets newscr's position to match stdscr's,
+	 * which happens to be the window's origin because we were not updating
+	 * that, and other stray wmove's in lynx fail because the coordinate
+	 * is on/after the right margin.  Force things to look ok here.
+	 */
+	int y, x;
+
+	getyx(LYwin, y, x);
+	if (x > LYcolLimit)
+	    x = LYcolLimit;
+	wmove(stdscr, y, x);
+
+	wnoutrefresh(stdscr);
+	pnoutrefresh(LYwin, 0, LYshiftWin, 0, 0, LYlines, LYscreenWidth() - 1);
+
+	/*
+	 * Keep a popup window visible.  This can happen if the user presses
+	 * '/' to do a search within a popup.
+	 */
+	if (my_subwindow != 0) {
+	    touchwin(my_subwindow);
+	    wnoutrefresh(my_subwindow);
+	}
+	doupdate();
+    } else {
+	refresh();
+    }
+#else
+    refresh();
+#endif
+}
+
+void lynx_force_repaint(void)
+{
+    clearok(curscr, TRUE);
+}
+
+void lynx_start_title_color(void)
+{
+#ifdef SH_EX
+    lynx_start_reverse();
+#endif
+}
+
+void lynx_stop_title_color(void)
+{
+#ifdef SH_EX
+    lynx_stop_reverse();
+#endif
+}
+
+void lynx_start_link_color(int flag,
+			   int pending)
+{
+    if (flag) {
+	/* makes some terminals work wrong because
+	 * they can't handle two attributes at the
+	 * same time
+	 */
+	/* lynx_start_bold();  */
+	lynx_start_reverse();
+#if defined(USE_SLANG)
+#ifndef __DJGPP__
+	if (SLtt_Use_Ansi_Colors)
+#endif /* !__DJGPP__ */
+	    lynx_start_underline();
+#endif /* USE_SLANG */
+#if defined(FANCY_CURSES) && defined(COLOR_CURSES)
+	if (lynx_has_color && LYShowColor >= SHOW_COLOR_ON)
+	    lynx_start_underline();
+#endif /* USE_SLANG */
+    } else {
+	lynx_start_bold();
+	/*
+	 * Make sure when flag is OFF that "unhighlighted" links will be
+	 * underlined if appropriate.  - LE & FM
+	 */
+	if (pending)
+	    lynx_start_underline();
+    }
+}
+
+void lynx_stop_link_color(int flag,
+			  int pending GCC_UNUSED)
+{
+#ifdef USE_COLOR_STYLE
+    LynxChangeStyle(flag == ON ? s_alink : s_a, ABS_OFF);
+#else
+    if (flag) {
+	lynx_stop_reverse();
+#if defined(USE_SLANG)
+#ifndef __DJGPP__
+	if (SLtt_Use_Ansi_Colors)
+#endif /* !__DJGPP__ */
+	    lynx_stop_underline();
+#endif /* USE_SLANG */
+#if defined(FANCY_CURSES) && defined(COLOR_CURSES)
+	if (lynx_has_color && LYShowColor >= SHOW_COLOR_ON)
+	    lynx_stop_underline();
+#endif /* FANCY_CURSES && COLOR_CURSES */
+    } else {
+	lynx_stop_bold();
+	/*
+	 * If underlining was turned on above, turn it off.  - LE & FM
+	 */
+	if (pending)
+	    lynx_stop_underline();
+    }
+#endif
+}
+
+/* FIXME: consider inlining these */
+
+void lynx_stop_target_color(void)
+{
+    lynx_stop_underline();
+    lynx_stop_reverse();
+    lynx_stop_bold();
+}
+
+void lynx_start_target_color(void)
+{
+    lynx_start_bold();
+    lynx_start_reverse();
+    lynx_start_underline();
+}
+
+void lynx_start_status_color(void)
+{
+#if defined(USE_COLOR_TABLE) && defined(COLOR_CURSES)
+    if (lynx_has_color && LYShowColor >= SHOW_COLOR_ON)
+	lynx_set_color(2);
+    else
+#endif
+	lynx_start_reverse();
+}
+
+void lynx_stop_status_color(void)
+{
+#if defined(USE_COLOR_TABLE) && defined(COLOR_CURSES)
+    if (lynx_has_color && LYShowColor >= SHOW_COLOR_ON)
+	lynx_set_color(0);
+    else
+#endif
+	lynx_stop_reverse();
+}
+
+void lynx_start_h1_color(void)
+{
+    if (bold_H1 || bold_headers)
+	lynx_start_bold();
+}
+
+void lynx_stop_h1_color(void)
+{
+    if (bold_H1 || bold_headers)
+	lynx_stop_bold();
+}
+
+void lynx_start_prompt_color(void)
+{
+    lynx_start_reverse();
+}
+
+void lynx_stop_prompt_color(void)
+{
+    lynx_stop_reverse();
+}
+
+void lynx_start_radio_color(void)
+{
+    lynx_start_bold();
+}
+
+void lynx_stop_radio_color(void)
+{
+    lynx_stop_bold();
+}
+
+void lynx_stop_all_colors(void)
+{
+    lynx_stop_underline();
+    lynx_stop_reverse();
+    lynx_stop_bold();
+}
+
+/*
+ * Wrappers for LYUnderlineLinks flag.
+ */
+void lynx_start_bold(void)
+{
+    start_bold();
+}
+
+void lynx_start_reverse(void)
+{
+    start_reverse();
+}
+
+void lynx_start_underline(void)
+{
+    start_underline();
+}
+
+void lynx_stop_bold(void)
+{
+    stop_bold();
+}
+
+void lynx_stop_reverse(void)
+{
+    stop_reverse();
+}
+
+void lynx_stop_underline(void)
+{
+    stop_underline();
+}
+
+void LYSetDisplayLines(void)
+{
+    if (!no_title) {
+	if (user_mode == NOVICE_MODE)
+	    display_lines = LYlines - 4;
+	else
+	    display_lines = LYlines - 2;
+    } else if (user_mode == NOVICE_MODE) {
+	display_lines = LYlines - 3;
+    } else {
+	display_lines = LYlines - 1;
+    }
+}
+
+/*
+ * If LYShowCursor is ON, move the cursor to the left of the current option, so
+ * that blind users, who are most likely to have LYShowCursor ON, will have
+ * it's string spoken or passed to the braille interface as each option is made
+ * current.  Otherwise, move it to the bottom, right column of the screen, to
+ * "hide" the cursor as for the main document, and let sighted users rely on
+ * the current option's highlighting or color without the distraction of a
+ * blinking cursor in the window.  - FM
+ */
+void LYstowCursor(WINDOW * win, int row, int col)
+{
+    if (LYShowCursor) {
+	wmove(win, row, col);
+    } else {
+	LYHideCursor();
+    }
+#ifdef USE_SLANG
+    SLsmg_refresh();
+#else
+    wrefresh(win);
+#endif /* USE_SLANG  */
+}
+
+#if defined(USE_BLINK) && defined(__EMX__)	/* Can't put it earler due to BOOLEAN conflict */
+#  define BOOLEAN os2BOOLEAN
+#  define INCL_VIO
+#  include "os2.h"
+static void make_blink_boldbg(void)
+{
+    VIOINTENSITY buf;		/* VIO windows have it anyway, */
+
+    /* but FS session need a switch */
+    buf.cb = sizeof(buf);
+    buf.type = 2;		/* VIOINTENSITY request */
+    buf.fs = 1;			/* Intensity == boldbg */
+    VioSetState(&buf, 0);
+}
+#endif
+
+#if defined(HAVE_WATTR_GET)
+/*
+ * getattrs() is not in X/Open curses, but it is more convenient than this.
+ */
+long LYgetattrs(WINDOW * win)
+{
+    long result;
+
+#if ( defined(HAVE_GETATTRS) && ( !defined(NCURSES_VERSION_MAJOR) || NCURSES_VERSION_MAJOR < 5 ) )
+
+    result = getattrs(win);
+#else
+    attr_t attrs = 0;
+    short pair = 0;
+
+    /*
+     * FIXME: this ignores the color-pair, which for most implementations is
+     * not stored in the attribute value.
+     */
+    (void) wattr_get(win, &attrs, &pair, NULL);
+    result = (long) attrs;
+#endif
+    return result;
+}
+#endif /* HAVE_WATTR_GET */
+
+#if defined(NCURSES_VERSION_PATCH) && NCURSES_VERSION_PATCH > 20021012
+#ifndef HAVE_USE_LEGACY_CODING
+/*
+ * Between ncurses 5.3 and 5.4 as part of fixes for wide-character mode, the
+ * locale support no longer allows characters in the range 128-159 to be
+ * treated as printable characters.  Here is a workaround to fool
+ * waddch_nosync() into treating "all" 8-bit characters as printable.
+ */
+NCURSES_CONST char *unctrl(chtype ch)
+{
+    static char result[3];
+    unsigned data = (unsigned char) ch;
+
+    if (data < 32) {
+	result[0] = '^';
+	result[1] = ch | '@';
+	result[2] = 0;
+    } else if (data == 127) {
+	result[0] = '^';
+	result[1] = '?';
+	result[2] = 0;
+    } else {
+	result[0] = data;
+	result[1] = 0;
+    }
+    return result;
+}
+#endif /* HAVE_USE_LEGACY_CODING */
+#endif
diff --git a/src/LYCurses.h b/src/LYCurses.h
new file mode 100644
index 00000000..178b0d4d
--- /dev/null
+++ b/src/LYCurses.h
@@ -0,0 +1,831 @@
+/* $LynxId: LYCurses.h,v 1.83 2009/11/27 12:58:31 tom Exp $ */
+#ifndef LYCURSES_H
+#define LYCURSES_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+/*
+ * Because we have to configure PDCURSES last, we may get bogus definitions
+ * from the system curses library - cancel these now.
+ */
+#ifdef HAVE_XCURSES
+
+#undef ASSUME_DEFAULT_COLORS
+#undef COLOR_CURSES
+#undef FANCY_CURSES
+#undef HAVE_CBREAK
+#undef HAVE_RESIZETERM
+#undef HAVE_USE_DEFAULT_COLORS
+#undef NCURSES
+#undef USE_DEFAULT_COLORS
+
+#define HAVE_CBREAK 1
+#define COLOR_CURSES 1
+#define FANCY_CURSES 1
+
+#endif
+
+/*
+ * The simple color scheme maps the 8 combinations of bold/underline/reverse
+ * to the standard 8 ANSI colors (with some variations based on context).
+ */
+#undef USE_COLOR_TABLE
+
+#ifdef USE_COLOR_STYLE
+#define USE_COLOR_TABLE 1	/* default color logic is used */
+#else
+#if defined(USE_SLANG) || defined(COLOR_CURSES)
+#define USE_COLOR_TABLE 1
+#endif
+#endif
+
+#ifdef TRUE
+#undef TRUE			/* to prevent parse error :( */
+#endif /* TRUE */
+#ifdef FALSE
+#undef FALSE			/* to prevent parse error :( */
+#endif /* FALSE */
+
+#ifdef USE_SLANG
+#include <slang.h>
+typedef unsigned long chtype;
+
+#undef WINDOW
+typedef struct {
+    int top_y;
+    int left_x;
+    int height;
+    int width;
+} WINDOW;
+
+/* slang doesn't really do windows... */
+#define waddch(w,c)  LYaddch(c)
+#define waddstr(w,s) addstr(s)
+#define wmove(win, row, col) SLsmg_gotorc(((win)?(win)->top_y:0) + (row), ((win)?(win)->left_x:0) + (col))
+
+#ifndef SLSMG_UARROW_CHAR
+#define SLSMG_UARROW_CHAR '^'
+#endif
+
+#ifndef SLSMG_DARROW_CHAR
+#define SLSMG_DARROW_CHAR 'v'
+#endif
+
+#ifndef SLSMG_LARROW_CHAR
+#define SLSMG_LARROW_CHAR '<'
+#endif
+
+#ifndef SLSMG_RARROW_CHAR
+#define SLSMG_RARROW_CHAR '>'
+#endif
+
+#ifndef SLSMG_CKBRD_CHAR
+#define SLSMG_CKBRD_CHAR '#'
+#endif
+
+#ifndef SLSMG_BLOCK_CHAR
+#define SLSMG_BLOCK_CHAR '#'
+#endif
+
+#ifndef ACS_UARROW
+#define ACS_UARROW  SLSMG_UARROW_CHAR
+#endif
+
+#ifndef ACS_DARROW
+#define ACS_DARROW  SLSMG_DARROW_CHAR
+#endif
+
+#ifndef ACS_LARROW
+#define ACS_LARROW  SLSMG_LARROW_CHAR
+#endif
+
+#ifndef ACS_RARROW
+#define ACS_RARROW  SLSMG_RARROW_CHAR
+#endif
+
+#ifndef ACS_CKBOARD
+#define ACS_CKBOARD SLSMG_CKBRD_CHAR
+#endif
+
+#ifndef ACS_BLOCK
+#define ACS_BLOCK   SLSMG_BLOCK_CHAR
+#endif
+
+#else /* Using curses: */
+
+#ifdef VMS
+#define FANCY_CURSES
+
+#endif /* VMS */
+
+#ifndef HAVE_TYPE_CHTYPE
+
+#ifdef __PDCURSES__
+#define HAVE_TYPE_CHTYPE 1
+#endif
+
+#if defined(_VMS_CURSES) || defined(VMS)
+typedef char chtype;
+
+#define HAVE_TYPE_CHTYPE 1
+#endif
+
+#endif /* ! HAVE_TYPE_CHTYPE */
+
+/*
+ *	CR may be defined before the curses.h include occurs.
+ *	There is a conflict between the termcap char *CR and the define.
+ *	Assuming that the definition of CR will always be carriage return.
+ *	06-09-94 Lynx 2-3-1 Garrett Arch Blythe
+ */
+#ifdef CR
+#undef CR			/* to prevent parse error :( */
+#define REDEFINE_CR
+#endif /* CR */
+
+#ifdef HZ
+#undef HZ			/* to prevent parse error :( */
+#endif /* HZ */
+
+/* SunOS 4.x has a redefinition between ioctl.h and termios.h */
+#if defined(sun) && !defined(__SVR4)
+#undef NL0
+#undef NL1
+#undef CR0
+#undef CR1
+#undef CR2
+#undef CR3
+#undef TAB0
+#undef TAB1
+#undef TAB2
+#undef XTABS
+#undef BS0
+#undef BS1
+#undef FF0
+#undef FF1
+#undef ECHO
+#undef NOFLSH
+#undef TOSTOP
+#undef FLUSHO
+#undef PENDIN
+#endif
+
+#if defined(_MSC_VER)
+#undef MOUSE_MOVED		/* conflict between PDCURSES and _WIN32 */
+#endif /* _MSC_VER */
+
+/*
+ * Do this to build with glibc 2.1.3 (apparently it was not used to build a
+ * system before release).
+ */
+#include <signal.h>
+
+#undef CS			/* some BSD versions of curses use this */
+#define CS curses_CS		/* ...but we don't */
+
+#ifdef ERR
+#undef ERR			/* all versions of curses define this */
+#endif
+
+#ifdef MOUSE_MOVED
+#undef MOUSE_MOVED		/* wincon.h or MINGW32's copy of it */
+#endif
+
+#ifdef HAVE_CONFIG_H
+# ifdef HAVE_NCURSESW_NCURSES_H
+#  undef GCC_PRINTFLIKE		/* <libutf8.h> may define 'printf' */
+#  include <ncursesw/ncurses.h>
+#  undef printf			/* but we don't want that... */
+# else
+#  ifdef HAVE_NCURSES_NCURSES_H
+#   include <ncurses/ncurses.h>
+#  else
+#   ifdef HAVE_NCURSES_H
+#    include <ncurses.h>
+#   else
+#    ifdef HAVE_CURSESX_H
+#     include <cursesX.h>	/* Ultrix */
+#    else
+#     ifdef HAVE_JCURSES_H
+#      include <jcurses.h>	/* sony_news */
+#     else
+#      ifdef HAVE_XCURSES
+#       include <xcurses.h>	/* PDCurses' UNIX port */
+#      else
+#       include <curses.h>	/* default */
+#      endif
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# if defined(wgetbkgd) && !defined(getbkgd)
+#  define getbkgd(w) wgetbkgd(w)	/* workaround pre-1.9.9g bug */
+# endif
+
+# ifdef FANCY_CURSES
+#  if defined(NCURSES) && defined(HAVE_NCURSESW_TERM_H)
+#    include <ncursesw/term.h>
+#  else
+#    if defined(NCURSES) && defined(HAVE_NCURSES_TERM_H)
+#      include <ncurses/term.h>
+#    else
+#     if defined(HAVE_NCURSESW_NCURSES_H) || defined(HAVE_NCURSES_NCURSES_H) || defined(HAVE_XCURSES)
+#       undef HAVE_TERM_H	/* only use one in comparable path! */
+#     endif
+#     if defined(HAVE_TERM_H)
+#      include <term.h>
+#     endif
+#   endif
+#  endif
+# endif
+
+# if defined(NCURSES_VERSION) && defined(HAVE_DEFINE_KEY)
+#  define USE_KEYMAPS		1
+# endif
+
+#else
+# if defined(VMS) && defined(__GNUC__)
+#  include <LYGCurses.h>
+#  else
+#   include <curses.h>		/* everything else */
+# endif				/* VMS && __GNUC__ */
+#endif /* HAVE_CONFIG_H */
+
+/*
+ * PDCurses' mouse code does nothing in the DJGPP configuration.
+ */
+#if defined(PDCURSES) && !defined(__DJGPP__) && !defined(HAVE_XCURSES)
+#define USE_MOUSE 1
+#endif
+
+/*
+ * Pick up the native ncurses name:
+ */
+#if defined(NCURSES_MOUSE_VERSION)
+#define USE_MOUSE 1
+#endif
+
+/*
+ * For systems where select() does not work for TTY's, we can poll using
+ * curses.
+ */
+#if defined(_WINDOWS) || defined(__MINGW32__)
+#if defined(PDCURSES) && defined(PDC_BUILD) && PDC_BUILD >= 2401
+#define USE_CURSES_NODELAY 1
+#endif
+
+#if defined(NCURSES_VERSION)
+#define USE_CURSES_NODELAY 1
+#endif
+#endif /* _WINDOWS || __MINGW32__ */
+
+#if defined(NCURSES_VERSION) && defined(__BEOS__)
+#define USE_CURSES_NODELAY 1
+#endif
+
+/*
+ * If we have pads, use them to implement left/right scrolling.
+ */
+#if defined(HAVE_NEWPAD) && defined(HAVE_PNOUTREFRESH) && !defined(PDCURSES)
+#define USE_CURSES_PADS 1
+#endif
+
+/*
+ * ncurses 1.9.9e won't work for pads, but 4.2 does (1.9.9g doesn't have a
+ * convenient ifdef, though it would work).
+ */
+#if defined(NCURSES_VERSION) && !defined(NCURSES_VERSION_MAJOR)
+#undef USE_CURSES_PADS
+#endif
+
+/*
+ * Most implementations of curses treat pair 0 specially, as the default
+ * foreground and background color.  Also, the COLORS variable corresponds to
+ * the total number of colors.
+ *
+ * PDCurses does not follow these rules.  Its COLORS variable claims it has
+ * 8 colors, but it actually implements 16.  That makes it hard to optimize
+ * color settings against color pair 0 in a portable fashion.
+ */
+#if defined(COLOR_CURSES)
+#if defined(PDCURSES) || defined(HAVE_XCURSES)
+#define COLORS 16		/* should be a variable... */
+#else
+#define USE_CURSES_PAIR_0
+#endif
+#endif
+
+#endif /* USE_SLANG */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef USE_SLANG
+#define LYstopPopup()		/* nothing */
+#define LYtopwindow() LYwin
+#else
+    extern void LYsubwindow(WINDOW * param);
+    extern WINDOW *LYtopwindow(void);
+
+#define LYstopPopup() LYsubwindow(0)
+#endif				/* NCURSES */
+
+    extern void LYbox(WINDOW * win, BOOLEAN formfield);
+    extern WINDOW *LYstartPopup(int *top_y, int *left_x, int *height, int *width);
+
+/*
+ * Useful macros not in PDCurses or very old ncurses headers.
+ */
+#if !defined(HAVE_GETBEGX) && !defined(getbegx)
+#define getbegx(win) ((win)->_begx)
+#endif
+#if !defined(HAVE_GETBEGY) && !defined(getbegy)
+#define getbegy(win) ((win)->_begy)
+#endif
+#if !defined(HAVE_GETBKGD) && !defined(getbkgd)
+#define getbkgd(win) ((win)->_bkgd)
+#endif
+
+#if defined(HAVE_WATTR_GET)
+    extern long LYgetattrs(WINDOW * win);
+
+#else
+#if defined(HAVE_GETATTRS) || defined(getattrs)
+#define LYgetattrs(win) getattrs(win)
+#else
+#define LYgetattrs(win) ((win)->_attrs)
+#endif
+#endif				/* HAVE_WATTR_GET */
+
+#if defined(PDCURSES)
+#define HAVE_GETBKGD 1		/* can use fallback definition */
+#define HAVE_NAPMS 1		/* can use millisecond-delays */
+#  if defined(PDC_BUILD) && PDC_BUILD >= 2401
+    extern int saved_scrsize_x;
+    extern int saved_scrsize_y;
+#  endif
+#endif
+
+#ifdef HAVE_NAPMS
+#define SECS2Secs(n) (1000 * (n))
+#define Secs2SECS(n) ((n) / 1000.0)
+#define SECS_FMT "%.3f"
+#else
+#define SECS2Secs(n) (n)
+#define Secs2SECS(n) (n)
+#define SECS_FMT "%.0f"
+#endif
+
+#ifdef NCURSES_VERSION
+    extern void _nc_freeall(void);	/* HAVE__NC_FREEALL */
+    extern void _nc_free_and_exit(int);		/* HAVE__NC_FREE_AND_EXIT */
+#endif
+
+/* Both slang and curses: */
+#ifndef TRUE
+#define TRUE  1
+#endif				/* !TRUE */
+#ifndef FALSE
+#define FALSE 0
+#endif				/* !FALSE */
+
+#ifdef REDEFINE_CR
+#define CR FROMASCII('\015')
+#endif				/* REDEFINE_CR */
+
+#ifdef ALT_CHAR_SET
+#define BOXVERT 0		/* use alt char set for popup window vertical borders */
+#define BOXHORI 0		/* use alt char set for popup window vertical borders */
+#endif
+
+#ifndef BOXVERT
+#define BOXVERT '*'		/* character for popup window vertical borders */
+#endif
+#ifndef BOXHORI
+#define BOXHORI '*'		/* character for popup window horizontal borders */
+#endif
+
+#ifndef KEY_DOWN
+#undef HAVE_KEYPAD		/* avoid confusion with bogus 'keypad()' */
+#endif
+
+    extern int LYlines;		/* replaces LINES */
+    extern int LYcols;		/* replaces COLS */
+
+/*
+ * The scrollbar, if used, occupies the rightmost column.
+ */
+#ifdef USE_SCROLLBAR
+#define LYbarWidth (LYShowScrollbar ? 1 : 0)
+#else
+#define LYbarWidth 0
+#endif
+
+/*
+ * Usable limits for display:
+ */
+#if defined(FANCY_CURSES) || defined(USE_SLANG)
+#if defined(PDCURSES)
+#define LYcolLimit (LYcols - LYbarWidth - 1)	/* PDCurses wrapping is buggy */
+#else
+#define LYcolLimit (LYcols - LYbarWidth)
+#endif
+#else
+#define LYcolLimit (LYcols - 1)
+#endif
+
+#ifdef USE_CURSES_PADS
+    extern WINDOW *LYwin;
+    extern int LYshiftWin;
+    extern int LYwideLines;
+    extern int LYtableCols;
+    extern BOOLEAN LYuseCursesPads;
+
+#else
+#define LYwin stdscr
+#define LYshiftWin	0
+#define LYwideLines	0
+#define LYtableCols	0
+#endif
+
+    extern BOOLEAN setup(char *terminal);
+    extern int LYscreenHeight(void);
+    extern int LYscreenWidth(void);
+    extern int LYstrExtent(const char *string, int len, int maxCells);
+    extern int LYstrExtent2(const char *string, int len);
+    extern int LYstrFittable(const char *string, int maxCells);
+    extern int LYstrCells(const char *string);
+    extern void LYclear(void);
+    extern void LYclrtoeol(void);
+    extern void LYerase(void);
+    extern void LYmove(int y, int x);
+    extern void LYnoVideo(int mask);
+    extern void LYnormalColor(void);
+    extern void LYpaddstr(WINDOW * w, int width, const char *s);
+    extern void LYrefresh(void);
+    extern void LYstartTargetEmphasis(void);
+    extern void LYstopTargetEmphasis(void);
+    extern void LYtouchline(int row);
+    extern void LYwaddnstr(WINDOW * w, const char *s, size_t len);
+    extern void start_curses(void);
+    extern void stop_curses(void);
+
+#define LYaddstr(s)      LYwaddnstr(LYwin, s, strlen(s))
+
+#ifdef VMS
+    extern int DCLsystem(char *command);
+    extern void VMSexit();
+    extern int ttopen();
+    extern int ttclose();
+    extern int ttgetc();
+    extern void VMSsignal(int sig, void (*func) ());
+#endif				/* VMS */
+
+#if defined(USE_COLOR_STYLE)
+    extern void curses_css(char *name, int dir);
+    extern void curses_style(int style, int dir);
+    extern void setHashStyle(int style, int color, int cattr, int mono, const char *element);
+    extern void setStyle(int style, int color, int cattr, int mono);
+    extern void wcurses_css(WINDOW * win, char *name, int dir);
+    extern void curses_w_style(WINDOW * win, int style, int dir);
+
+#  define LynxChangeStyle(style,dir) curses_style(style,dir)
+#  define LynxWChangeStyle(win,style,dir) curses_w_style(win,style,dir)
+#else
+#  define LynxWChangeStyle(win,style,dir)	(void)1
+#endif				/* USE_COLOR_STYLE */
+
+#ifdef USE_COLOR_TABLE
+    extern void LYaddAttr(int a);
+    extern void LYsubAttr(int a);
+    extern void lynx_setup_colors(void);
+    extern unsigned Lynx_Color_Flags;
+#endif
+
+#if defined(USE_COLOR_TABLE) || defined(USE_SLANG)
+    extern int Current_Attr;
+#endif
+
+#ifdef USE_SLANG
+#define SHOW_WHEREIS_TARGETS 1
+
+#if !defined(VMS) && !defined(DJGPP)
+#define USE_MOUSE              1
+#endif
+
+#if !defined(__DJGPP__) && !defined(__CYGWIN__)
+#define USE_KEYMAPS		1
+#endif
+
+#define SL_LYNX_USE_COLOR	1
+#define SL_LYNX_OVERRIDE_COLOR	2
+
+#define start_bold()      	LYaddAttr(LYUnderlineLinks ? 4 : 1)
+#define start_reverse()   	LYaddAttr(2)
+#define start_underline() 	LYaddAttr(LYUnderlineLinks ? 1 : 4)
+#define stop_bold()       	LYsubAttr(LYUnderlineLinks ? 4 : 1)
+#define stop_reverse()    	LYsubAttr(2)
+#define stop_underline()  	LYsubAttr(LYUnderlineLinks ? 1 : 4)
+
+#ifdef FANCY_CURSES
+#undef FANCY_CURSES
+#endif				/* FANCY_CURSES */
+
+/*
+ *  Map some curses functions to slang functions.
+ */
+#define stdscr ((WINDOW *)0)
+#ifdef SLANG_MBCS_HACK
+    extern int PHYSICAL_SLtt_Screen_Cols;
+
+#define COLS PHYSICAL_SLtt_Screen_Cols
+#else
+#define COLS SLtt_Screen_Cols
+#endif				/* SLANG_MBCS_HACK */
+#define LINES SLtt_Screen_Rows
+#define move SLsmg_gotorc
+#define addstr SLsmg_write_string
+    extern void LY_SLerase(void);
+
+#define erase LY_SLerase
+#define clear LY_SLerase
+#define standout SLsmg_reverse_video
+#define standend  SLsmg_normal_video
+#define clrtoeol SLsmg_erase_eol
+
+#ifdef SLSMG_NEWLINE_SCROLLS
+#define scrollok(a,b) SLsmg_Newline_Behavior \
+   = ((b) ? SLSMG_NEWLINE_SCROLLS : SLSMG_NEWLINE_MOVES)
+#else
+#define scrollok(a,b) SLsmg_Newline_Moves = ((b) ? 1 : -1)
+#endif
+
+#define LYaddch(ch)   SLsmg_write_char(ch)
+
+#if SLANG_VERSION >= 20000
+#define addch_raw(ch) do {                                \
+                        SLsmg_Char_Type buf;              \
+                        buf.nchars = 1;                   \
+                        buf.wchars[0] = ch;               \
+                        buf.color = Current_Attr;         \
+                        SLsmg_write_raw (&buf, 1);        \
+                      } while (0)
+#else
+#define addch_raw(ch) do {                                \
+                        SLsmg_Char_Type buf;              \
+                        buf = (ch) | (Current_Attr << 4); \
+                        SLsmg_write_raw (&buf, 1);        \
+                      } while (0)
+#endif				/* SLANG_VERSION >= 20000 */
+
+#define echo()
+#define printw        SLsmg_printf
+
+    extern int curscr;
+    extern BOOLEAN FullRefresh;
+
+#ifdef clearok
+#undef clearok
+#endif				/* clearok */
+#define clearok(a,b) { FullRefresh = (BOOLEAN)b; }
+    extern void LY_SLrefresh(void);
+
+#ifdef refresh
+#undef refresh
+#endif				/* refresh */
+#define refresh LY_SLrefresh
+
+#ifdef VMS
+    extern void VTHome(void);
+
+#define endwin() LYclear(),refresh(),SLsmg_reset_smg(),VTHome()
+#else
+#define endwin SLsmg_reset_smg(),SLang_reset_tty
+#endif				/* VMS */
+
+#else				/* Define curses functions: */
+
+#ifdef FANCY_CURSES
+#define SHOW_WHEREIS_TARGETS 1
+
+#ifdef VMS
+/*
+ *  For VMS curses, [w]setattr() and [w]clrattr()
+ *  add and subtract, respectively, the attributes
+ *  _UNDERLINE, _BOLD, _REVERSE, and _BLINK. - FM
+ */
+#define start_bold()		setattr(LYUnderlineLinks ? _UNDERLINE : _BOLD)
+#define stop_bold()		clrattr(LYUnderlineLinks ? _UNDERLINE : _BOLD)
+#define start_underline()	setattr(LYUnderlineLinks ? _BOLD : _UNDERLINE)
+#define stop_underline()	clrattr(LYUnderlineLinks ? _BOLD : _UNDERLINE)
+#define start_reverse()		setattr(_REVERSE)
+#define wstart_reverse(w)	wsetattr(w, _REVERSE)
+#define stop_reverse()		clrattr(_REVERSE)
+#define wstop_reverse(w)	wclrattr(w, _REVERSE)
+
+#else				/* Not VMS: */
+
+    extern int string_to_attr(const char *name);
+
+/*
+ *  For Unix FANCY_FANCY curses we interpose
+ *  our own functions to add or subtract the
+ *  A_foo attributes. - FM
+ */
+#if defined(USE_COLOR_TABLE) && !defined(USE_COLOR_STYLE)
+    extern void LYaddWAttr(WINDOW * win, int a);
+    extern void LYsubWAttr(WINDOW * win, int a);
+    extern void LYaddWAttr(WINDOW * win, int a);
+    extern void LYsubWAttr(WINDOW * win, int a);
+
+#undef  standout
+#define standout() 		lynx_standout(TRUE)
+#undef  standend
+#define standend() 		lynx_standout(FALSE)
+#else
+#define LYaddAttr(attr)		LYaddWAttr(LYwin,attr)
+#define LYaddWAttr(win,attr)	wattron(win,attr)
+#define LYsubAttr(attr)		LYsubWAttr(LYwin,attr)
+#define LYsubWAttr(win,attr)	wattroff(win,attr)
+#endif
+
+#if defined(USE_COLOR_TABLE)
+    extern void lynx_set_color(int a);
+    extern void lynx_standout(int a);
+    extern char *LYgetTableString(int code);
+    extern int LYgetTableAttr(void);
+    extern int lynx_chg_color(int, int, int);
+#endif
+
+#define start_bold()		LYaddAttr(LYUnderlineLinks ? A_UNDERLINE : A_BOLD)
+#define stop_bold()		LYsubAttr(LYUnderlineLinks ? A_UNDERLINE : A_BOLD)
+#define start_underline()	LYaddAttr(LYUnderlineLinks ? A_BOLD : A_UNDERLINE)
+#define stop_underline()	LYsubAttr(LYUnderlineLinks ? A_BOLD : A_UNDERLINE)
+
+#if defined(SNAKE) && defined(HP_TERMINAL)
+#define start_reverse()		LYaddWAttr(LYwin, A_DIM)
+#define wstart_reverse(w)	LYaddWAttr(w, A_DIM)
+#define stop_reverse()		LYsubWAttr(LYwin, A_DIM)
+#define wstop_reverse(w)	LYsubWAttr(w, A_DIM)
+#else
+#define start_reverse()		LYaddAttr(A_REVERSE)
+#define wstart_reverse(w)	LYaddWAttr(w, A_REVERSE)
+#define stop_reverse()		LYsubAttr(A_REVERSE)
+#define wstop_reverse(w)	LYsubWAttr(w, A_REVERSE)
+#endif				/* SNAKE && HP_TERMINAL */
+
+#endif				/* VMS */
+
+#else				/* Not FANCY_CURSES: */
+/* *INDENT-OFF* */
+#ifdef COLOR_CURSES
+#undef COLOR_CURSES
+Error FANCY_CURSES
+There is a problem with the configuration.  We expect to have FANCY_CURSES
+defined when COLOR_CURSES is defined, since we build on the attributes used in
+FANCY_CURSES.  Check your config.log to see why the FANCY_CURSES test failed.
+#endif
+/* *INDENT-ON* */
+
+/*
+ *  We only have [w]standout() and [w]standin(),
+ *  so we'll use them synonymously for bold and
+ *  reverse, and ignore underline. - FM
+ */
+#define start_bold()		standout()
+#define start_underline()	/* nothing */
+#define start_reverse()		standout()
+#define wstart_reverse(a)	wstandout(a)
+#define stop_bold()		standend()
+#define stop_underline()	/* nothing */
+#define stop_reverse()		standend()
+#define wstop_reverse(a)	wstandend(a)
+
+#endif				/* FANCY_CURSES */
+
+#ifdef __hpux			/* FIXME: configure check */
+#undef ACS_UARROW
+#undef ACS_DARROW
+#undef ACS_LARROW
+#undef ACS_RARROW
+#undef ACS_BLOCK
+#undef ACS_CKBOARD
+#endif
+
+#ifndef ACS_UARROW
+#define ACS_UARROW  '^'
+#endif
+
+#ifndef ACS_DARROW
+#define ACS_DARROW  'V'
+#endif
+
+#ifndef ACS_LARROW
+#define ACS_LARROW '{'
+#endif
+
+#ifndef ACS_RARROW
+#define ACS_RARROW '}'
+#endif
+
+#ifndef ACS_BLOCK
+#define ACS_BLOCK  '}'
+#endif
+
+#ifndef ACS_CKBOARD
+#define ACS_CKBOARD '}'
+#endif
+
+#define LYaddch(ch)		waddch(LYwin, ch)
+
+#define addch_raw(ch)           LYaddch(ch)
+
+#endif				/* USE_SLANG */
+
+#ifdef USE_SLANG
+#define LYGetYX(y, x)   y = SLsmg_get_row(), x = SLsmg_get_column()
+#else
+#ifdef getyx
+#define LYGetYX(y, x)   getyx(LYwin, y, x)
+#else
+#define LYGetYX(y, x)   y = LYwin->_cury, x = LYwin->_curx
+#endif				/* getyx */
+#endif				/* USE_SLANG */
+
+/*
+ * If the screen library allows us to specify "default" color, allow user to
+ * control it.
+ */
+#ifdef USE_DEFAULT_COLORS
+#if defined(USE_SLANG) || defined(HAVE_ASSUME_DEFAULT_COLORS)
+#define EXP_ASSUMED_COLOR 1
+#endif
+#endif
+
+    extern void lynx_enable_mouse(int);
+    extern void lynx_force_repaint(void);
+    extern void lynx_nl2crlf(int normal);
+    extern void lynx_start_title_color(void);
+    extern void lynx_stop_title_color(void);
+    extern void lynx_start_link_color(int flag, int pending);
+    extern void lynx_stop_link_color(int flag, int pending);
+    extern void lynx_stop_target_color(void);
+    extern void lynx_start_target_color(void);
+    extern void lynx_start_status_color(void);
+    extern void lynx_stop_status_color(void);
+    extern void lynx_start_h1_color(void);
+    extern void lynx_stop_h1_color(void);
+    extern void lynx_start_prompt_color(void);
+    extern void lynx_stop_prompt_color(void);
+    extern void lynx_start_radio_color(void);
+    extern void lynx_stop_radio_color(void);
+    extern void lynx_stop_all_colors(void);
+
+    extern void lynx_start_bold(void);
+    extern void lynx_start_reverse(void);
+    extern void lynx_start_underline(void);
+    extern void lynx_stop_bold(void);
+    extern void lynx_stop_reverse(void);
+    extern void lynx_stop_underline(void);
+
+/*
+ * To prevent corrupting binary data on DOS, MS-WINDOWS or OS/2 we open files
+ * and stdout in BINARY mode by default.  Where necessary we should open and
+ * (close!) TEXT mode.
+ *
+ * Note:  EMX has no corresponding variable like _fmode on DOS, but it does
+ * have setmode.
+ */
+#if defined(_WINDOWS) || defined(DJGPP) || defined(__EMX__) || defined(WIN_EX)
+#define SetOutputMode(mode) fflush(stdout), setmode(fileno(stdout), mode)
+#else
+#define SetOutputMode(mode)	/* nothing */
+#endif
+
+#if defined(_WINDOWS) || defined(DJGPP)
+#define SetDefaultMode(mode) _fmode = mode
+#else
+#define SetDefaultMode(mode)	/* nothing */
+#endif
+
+/*
+ * Very old versions of curses cannot put the cursor on the lower right corner.
+ * Adjust our "hidden" cursor position accordingly.
+ */
+#if defined(FANCY_CURSES) || defined(USE_SLANG)
+#define LYHideCursor() LYmove((LYlines - 1), (LYcolLimit - 1))
+#else
+#define LYHideCursor() LYmove((LYlines - 1), (LYcolLimit - 2))
+#endif
+
+    extern void LYstowCursor(WINDOW * win, int row, int col);
+    extern void LYSetDisplayLines(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYCURSES_H */
diff --git a/src/LYDownload.c b/src/LYDownload.c
new file mode 100644
index 00000000..18dc5f5f
--- /dev/null
+++ b/src/LYDownload.c
@@ -0,0 +1,586 @@
+/* $LynxId: LYDownload.c,v 1.59 2008/12/14 18:26:03 tom Exp $ */
+#include <HTUtils.h>
+#include <HTParse.h>
+#include <HTList.h>
+#include <HTAlert.h>
+#include <LYCurses.h>
+#include <LYUtils.h>
+#include <LYGlobalDefs.h>
+#include <LYStrings.h>
+#include <LYDownload.h>
+
+#include <LYLeaks.h>
+
+/*
+ * LYDownload takes a URL and downloads it using a user selected download
+ * program
+ *
+ * It parses an incoming link that looks like
+ *
+ * LYNXDOWNLOAD://Method=<#>/File=<STRING>/SugFile=<STRING>
+ */
+#ifdef VMS
+BOOLEAN LYDidRename = FALSE;
+#endif /* VMS */
+
+static char LYValidDownloadFile[LY_MAXPATH] = "\0";
+
+void LYDownload(char *line)
+{
+    char *Line = NULL, *method, *file, *sug_file = NULL;
+    int method_number;
+    int count;
+    char *the_command = 0;
+    char buffer[LY_MAXPATH];
+    char command[LY_MAXPATH];
+    char *cp;
+    lynx_list_item_type *download_command = 0;
+    int ch;
+    RecallType recall;
+    int FnameTotal;
+    int FnameNum;
+    BOOLEAN FirstRecall = TRUE;
+    BOOLEAN SecondS = FALSE;
+
+#ifdef VMS
+    LYDidRename = FALSE;
+#endif /* VMS */
+
+    /*
+     * Make sure we have a valid download file comparison string loaded via the
+     * download options menu.  - FM
+     */
+    if (LYValidDownloadFile[0] == '\0') {
+	goto failed;
+    }
+
+    /*
+     * Make a copy of the LYNXDOWNLOAD internal URL for parsing.  - FM
+     */
+    StrAllocCopy(Line, line);
+
+    /*
+     * Parse out the File, sug_file, and the Method.
+     */
+    if ((file = strstr(Line, "/File=")) == NULL)
+	goto failed;
+    *file = '\0';
+    /*
+     * Go past "File=".
+     */
+    file += 6;
+
+    if ((sug_file = strstr(file + 1, "/SugFile=")) != NULL) {
+	*sug_file = '\0';
+	/*
+	 * Go past "SugFile=".
+	 */
+	sug_file += 9;
+	HTUnEscape(sug_file);
+    }
+
+    /*
+     * Make sure that the file string is the one from the last displayed
+     * download options menu.  - FM
+     */
+    if (strcmp(file, LYValidDownloadFile)) {
+	goto failed;
+    }
+#if defined(DIRED_SUPPORT)
+    /* FIXME: use HTLocalName */
+    if (!strncmp(file, "file://localhost", 16)) {
+#ifdef __DJGPP__
+	if (!strncmp(file + 16, "/dev/", 5))
+	    file += 16;
+	else {
+	    file += 17;
+	    file = HTDOS_name(file);
+	}
+#else
+	file += 16;
+#endif /* __DJGPP__ */
+    } else if (isFILE_URL(file))
+	file += LEN_FILE_URL;
+    HTUnEscape(file);
+#else
+#if defined(_WINDOWS)		/* 1997/10/15 (Wed) 16:27:38 */
+    if (!strncmp(file, "file://localhost/", 17))
+	file += 17;
+    else if (!strncmp(file, "file:/", 6))
+	file += 6;
+    HTUnEscape(file);
+#endif /* _WINDOWS */
+#endif /* DIRED_SUPPORT */
+
+    if ((method = strstr(Line, "Method=")) == NULL)
+	goto failed;
+    /*
+     * Go past "Method=".
+     */
+    method += 7;
+    method_number = atoi(method);
+
+    /*
+     * Set up the sug_filenames recall buffer.
+     */
+    FnameTotal = (sug_filenames ? HTList_count(sug_filenames) : 0);
+    recall = ((FnameTotal >= 1) ? RECALL_URL : NORECALL);
+    FnameNum = FnameTotal;
+
+    if (method_number < 0) {
+	/*
+	 * Write to local file.
+	 */
+	_statusline(FILENAME_PROMPT);
+      retry:
+	if (sug_file)
+	    LYstrncpy(buffer, sug_file, ((sizeof(buffer) / 2) - 1));
+	else
+	    *buffer = '\0';
+      check_recall:
+	if ((ch = LYgetstr(buffer,
+			   VISIBLE, (sizeof(buffer) / 2), recall)) < 0 ||
+	    *buffer == '\0' || ch == UPARROW || ch == DNARROW) {
+	    if (recall && ch == UPARROW) {
+		if (FirstRecall) {
+		    FirstRecall = FALSE;
+		    /*
+		     * Use the last Fname in the list.  - FM
+		     */
+		    FnameNum = 0;
+		} else {
+		    /*
+		     * Go back to the previous Fname in the list.  - FM
+		     */
+		    FnameNum++;
+		}
+		if (FnameNum >= FnameTotal) {
+		    /*
+		     * Reset the FirstRecall flag, and use sug_file or a blank.
+		     * - FM
+		     */
+		    FirstRecall = TRUE;
+		    FnameNum = FnameTotal;
+		    _statusline(FILENAME_PROMPT);
+		    goto retry;
+		} else if ((cp = (char *) HTList_objectAt(sug_filenames,
+							  FnameNum)) != NULL) {
+		    LYstrncpy(buffer, cp, sizeof(buffer) - 1);
+		    if (FnameTotal == 1) {
+			_statusline(EDIT_THE_PREV_FILENAME);
+		    } else {
+			_statusline(EDIT_A_PREV_FILENAME);
+		    }
+		    goto check_recall;
+		}
+	    } else if (recall && ch == DNARROW) {
+		if (FirstRecall) {
+		    FirstRecall = FALSE;
+		    /*
+		     * Use the first Fname in the list.  - FM
+		     */
+		    FnameNum = FnameTotal - 1;
+		} else {
+		    /*
+		     * Advance to the next Fname in the list.  - FM
+		     */
+		    FnameNum--;
+		}
+		if (FnameNum < 0) {
+		    /*
+		     * Set the FirstRecall flag, and use sug_file or a blank.
+		     * - FM
+		     */
+		    FirstRecall = TRUE;
+		    FnameNum = FnameTotal;
+		    _statusline(FILENAME_PROMPT);
+		    goto retry;
+		} else if ((cp = (char *) HTList_objectAt(sug_filenames,
+							  FnameNum)) != NULL) {
+		    LYstrncpy(buffer, cp, sizeof(buffer) - 1);
+		    if (FnameTotal == 1) {
+			_statusline(EDIT_THE_PREV_FILENAME);
+		    } else {
+			_statusline(EDIT_A_PREV_FILENAME);
+		    }
+		    goto check_recall;
+		}
+	    }
+
+	    /*
+	     * Save cancelled.
+	     */
+	    goto cancelled;
+	}
+
+	strcpy(command, buffer);
+	if (!LYValidateFilename(buffer, command))
+	    goto cancelled;
+#ifdef HAVE_POPEN
+	else if (LYIsPipeCommand(buffer)) {
+	    /* I don't know how to download to a pipe */
+	    HTAlert(CANNOT_WRITE_TO_FILE);
+	    _statusline(NEW_FILENAME_PROMPT);
+	    FirstRecall = TRUE;
+	    FnameNum = FnameTotal;
+	    goto retry;
+	}
+#endif
+
+	/*
+	 * See if it already exists.
+	 */
+	switch (LYValidateOutput(buffer)) {
+	case 'Y':
+	    break;
+	case 'N':
+	    _statusline(NEW_FILENAME_PROMPT);
+	    FirstRecall = TRUE;
+	    FnameNum = FnameTotal;
+	    goto retry;
+	default:
+	    FREE(Line);
+	    return;
+	}
+
+	/*
+	 * See if we can write to it.
+	 */
+	CTRACE((tfp, "LYDownload: filename is %s\n", buffer));
+
+	if (!LYCanWriteFile(buffer)) {
+	    FirstRecall = TRUE;
+	    FnameNum = FnameTotal;
+	    goto retry;
+	}
+	SecondS = TRUE;
+
+	HTInfoMsg(SAVING);
+#ifdef VMS
+	/*
+	 * Try rename() first.  - FM
+	 */
+	CTRACE((tfp, "command: rename(%s, %s)\n", file, buffer));
+	if (rename(file, buffer)) {
+	    /*
+	     * Failed.  Use spawned COPY_COMMAND.  - FM
+	     */
+	    CTRACE((tfp, "         FAILED!\n"));
+	    LYCopyFile(file, buffer);
+	} else {
+	    /*
+	     * We don't have the temporary file (it was renamed to a permanent
+	     * file), so set a flag to pop out of the download menu.  - FM
+	     */
+	    LYDidRename = TRUE;
+	}
+	chmod(buffer, HIDE_CHMOD);
+#else /* Unix: */
+
+	LYCopyFile(file, buffer);
+	LYRelaxFilePermissions(buffer);
+#endif /* VMS */
+
+    } else {
+	/*
+	 * Use configured download commands.
+	 */
+	buffer[0] = '\0';
+	for (count = 0, download_command = downloaders;
+	     count < method_number;
+	     count++, download_command = download_command->next) ;	/* null body */
+
+	/*
+	 * Commands have the form "command %s [etc]" where %s is the filename.
+	 */
+	if (download_command->command != NULL) {
+	    /*
+	     * Check for two '%s' and ask for the local filename if there is.
+	     */
+	    if (HTCountCommandArgs(download_command->command) >= 2) {
+		_statusline(FILENAME_PROMPT);
+	      again:
+		if (sug_file) {
+		    strncpy(buffer, sug_file, (sizeof(buffer) / 2) - 1);
+		} else {
+		    *buffer = '\0';
+		}
+	      check_again:
+		if ((ch = LYgetstr(buffer, VISIBLE,
+				   sizeof(buffer), recall)) < 0 ||
+		    *buffer == '\0' || ch == UPARROW || ch == DNARROW) {
+		    if (recall && ch == UPARROW) {
+			if (FirstRecall) {
+			    FirstRecall = FALSE;
+			    /*
+			     * Use the last Fname in the list.  - FM
+			     */
+			    FnameNum = 0;
+			} else {
+			    /*
+			     * Go back to the previous Fname in the list.  - FM
+			     */
+			    FnameNum++;
+			}
+			if (FnameNum >= FnameTotal) {
+			    /*
+			     * Reset the FirstRecall flag, and use sug_file or
+			     * a blank.  - FM
+			     */
+			    FirstRecall = TRUE;
+			    FnameNum = FnameTotal;
+			    _statusline(FILENAME_PROMPT);
+			    goto again;
+			} else if ((cp = (char *) HTList_objectAt(sug_filenames,
+								  FnameNum))
+				   != NULL) {
+			    LYstrncpy(buffer, cp, sizeof(buffer) - 1);
+			    if (FnameTotal == 1) {
+				_statusline(EDIT_THE_PREV_FILENAME);
+			    } else {
+				_statusline(EDIT_A_PREV_FILENAME);
+			    }
+			    goto check_again;
+			}
+		    } else if (recall && ch == DNARROW) {
+			if (FirstRecall) {
+			    FirstRecall = FALSE;
+			    /*
+			     * Use the first Fname in the list.  - FM
+			     */
+			    FnameNum = FnameTotal - 1;
+			} else {
+			    /*
+			     * Advance to the next Fname in the list.  - FM
+			     */
+			    FnameNum--;
+			}
+			if (FnameNum < 0) {
+			    /*
+			     * Set the FirstRecall flag, and use sug_file or a
+			     * blank.  - FM
+			     */
+			    FirstRecall = TRUE;
+			    FnameNum = FnameTotal;
+			    _statusline(FILENAME_PROMPT);
+			    goto again;
+			} else if ((cp = (char *) HTList_objectAt(sug_filenames,
+								  FnameNum))
+				   != NULL) {
+			    LYstrncpy(buffer, cp, sizeof(buffer) - 1);
+			    if (FnameTotal == 1) {
+				_statusline(EDIT_THE_PREV_FILENAME);
+			    } else {
+				_statusline(EDIT_A_PREV_FILENAME);
+			    }
+			    goto check_again;
+			}
+		    }
+
+		    /*
+		     * Download cancelled.
+		     */
+		    goto cancelled;
+		}
+
+		if (no_dotfiles || !show_dotfiles) {
+		    if (*LYPathLeaf(buffer) == '.') {
+			HTAlert(FILENAME_CANNOT_BE_DOT);
+			_statusline(NEW_FILENAME_PROMPT);
+			goto again;
+		    }
+		}
+		/*
+		 * Cancel if the user entered "/dev/null" on Unix, or an "nl:"
+		 * path on VMS.  - FM
+		 */
+		if (LYIsNullDevice(buffer)) {
+		    goto cancelled;
+		}
+		SecondS = TRUE;
+	    }
+
+	    /*
+	     * The following is considered a bug by the community.  If the
+	     * command only takes one argument on the command line, then the
+	     * suggested file name is not used.  It actually is not a bug at
+	     * all and does as it should, putting both names on the command
+	     * line.
+	     */
+	    count = 1;
+	    HTAddParam(&the_command, download_command->command, count, file);
+	    if (HTCountCommandArgs(download_command->command) > 1)
+		HTAddParam(&the_command, download_command->command, ++count, buffer);
+	    HTEndParam(&the_command, download_command->command, count);
+
+	} else {
+	    HTAlert(MISCONF_DOWNLOAD_COMMAND);
+	    goto failed;
+	}
+
+	CTRACE((tfp, "command: %s\n", the_command));
+	stop_curses();
+	LYSystem(the_command);
+	FREE(the_command);
+	start_curses();
+	/* don't remove(file); */
+    }
+
+    if (SecondS == TRUE) {
+#ifdef VMS
+	if (0 == strncasecomp(buffer, "sys$disk:", 9)) {
+	    if (0 == strncmp((buffer + 9), "[]", 2)) {
+		HTAddSugFilename(buffer + 11);
+	    } else {
+		HTAddSugFilename(buffer + 9);
+	    }
+	} else {
+	    HTAddSugFilename(buffer);
+	}
+#else
+	HTAddSugFilename(buffer);
+#endif /* VMS */
+    }
+    FREE(Line);
+    return;
+
+  failed:
+    HTAlert(CANNOT_DOWNLOAD_FILE);
+    FREE(Line);
+    return;
+
+  cancelled:
+    HTInfoMsg(CANCELLING);
+    FREE(Line);
+    return;
+}
+
+/*
+ * Compare a filename with a given suffix, which we have set to give a rough
+ * idea of its content.
+ */
+static int SuffixIs(char *filename, const char *suffix)
+{
+    size_t have = strlen(filename);
+    size_t need = strlen(suffix);
+
+    return have > need && !strcmp(filename + have - need, suffix);
+}
+
+/*
+ * LYdownload_options writes out the current download choices to a file so that
+ * the user can select downloaders in the same way that they select all other
+ * links.  Download links look like:
+ * LYNXDOWNLOAD://Method=<#>/File=<STRING>/SugFile=<STRING>
+ */
+int LYdownload_options(char **newfile, char *data_file)
+{
+    static char tempfile[LY_MAXPATH] = "\0";
+    char *downloaded_url = NULL;
+    char *sug_filename = NULL;
+    FILE *fp0;
+    lynx_list_item_type *cur_download;
+    int count;
+
+    /*
+     * Get a suggested filename.
+     */
+    StrAllocCopy(sug_filename, *newfile);
+    change_sug_filename(sug_filename);
+
+    if ((fp0 = InternalPageFP(tempfile, TRUE)) == 0)
+	return (-1);
+
+    StrAllocCopy(downloaded_url, *newfile);
+    LYLocalFileToURL(newfile, tempfile);
+
+    LYstrncpy(LYValidDownloadFile,
+	      data_file,
+	      (sizeof(LYValidDownloadFile) - 1));
+    LYforce_no_cache = TRUE;	/* don't cache this doc */
+
+    BeginInternalPage(fp0, DOWNLOAD_OPTIONS_TITLE, DOWNLOAD_OPTIONS_HELP);
+
+    fprintf(fp0, "<pre>\n");
+    fprintf(fp0, "<em>%s</em> %s\n",
+	    gettext("Downloaded link:"),
+	    downloaded_url);
+    FREE(downloaded_url);
+
+    fprintf(fp0, "<em>%s</em> %s\n",
+	    gettext("Suggested file name:"),
+	    sug_filename);
+
+    fprintf(fp0, "\n%s\n",
+	    (user_mode == NOVICE_MODE)
+	    ? gettext("Standard download options:")
+	    : gettext("Download options:"));
+
+    if (!no_disk_save) {
+#if defined(DIRED_SUPPORT)
+	/*
+	 * Disable save to disk option for local files.
+	 */
+	if (!lynx_edit_mode)
+#endif /* DIRED_SUPPORT */
+	{
+	    fprintf(fp0,
+		    "   <a href=\"%s//Method=-1/File=%s/SugFile=%s%s\">%s</a>\n",
+		    STR_LYNXDOWNLOAD,
+		    data_file,
+		    NonNull(lynx_save_space),
+		    sug_filename,
+		    gettext("Save to disk"));
+	    /*
+	     * If it is not a binary file, offer the opportunity to view the
+	     * downloaded temporary file (see HTSaveToFile).
+	     */
+	    if (SuffixIs(data_file, HTML_SUFFIX)
+		|| SuffixIs(data_file, TEXT_SUFFIX)) {
+		char *target = NULL;
+		char *source = LYAddPathToSave(data_file);
+
+		LYLocalFileToURL(&target, source);
+		fprintf(fp0,
+			"   <a href=\"%s\">%s</a>\n",
+			target,
+			gettext("View temporary file"));
+
+		FREE(source);
+		FREE(target);
+	    }
+	}
+    } else {
+	fprintf(fp0, "   <em>%s</em>\n", gettext("Save to disk disabled."));
+    }
+
+    if (user_mode == NOVICE_MODE)
+	fprintf(fp0, "\n%s\n", gettext("Local additions:"));
+
+    if (downloaders != NULL) {
+	for (count = 0, cur_download = downloaders; cur_download != NULL;
+	     cur_download = cur_download->next, count++) {
+	    if (!no_download || cur_download->always_enabled) {
+		fprintf(fp0,
+			"   <a href=\"%s//Method=%d/File=%s/SugFile=%s\">",
+			STR_LYNXDOWNLOAD, count, data_file, sug_filename);
+		fprintf(fp0, "%s", (cur_download->name
+				    ? cur_download->name
+				    : gettext("No Name Given")));
+		fprintf(fp0, "</a>\n");
+	    }
+	}
+    }
+
+    fprintf(fp0, "</pre>\n");
+    EndInternalPage(fp0);
+    LYCloseTempFP(fp0);
+    LYRegisterUIPage(*newfile, UIP_DOWNLOAD_OPTIONS);
+
+    /*
+     * Free off temp copy.
+     */
+    FREE(sug_filename);
+
+    return (0);
+}
diff --git a/src/LYDownload.h b/src/LYDownload.h
new file mode 100644
index 00000000..5926df8b
--- /dev/null
+++ b/src/LYDownload.h
@@ -0,0 +1,21 @@
+#ifndef LYDOWNLOAD_H
+#define LYDOWNLOAD_H
+
+#ifndef LYSTRUCTS_H
+#include <LYStructs.h>
+#endif /* LYSTRUCTS_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    extern void LYDownload(char *line);
+    extern int LYdownload_options(char **newfile, char *data_file);
+
+#ifdef VMS
+    extern BOOLEAN LYDidRename;
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYDOWNLOAD_H */
diff --git a/src/LYEdit.c b/src/LYEdit.c
new file mode 100644
index 00000000..4763652f
--- /dev/null
+++ b/src/LYEdit.c
@@ -0,0 +1,298 @@
+/* $LynxId: LYEdit.c,v 1.40 2009/11/22 17:25:19 tom Exp $ */
+#include <HTUtils.h>
+#include <HTParse.h>
+#include <HTAlert.h>
+#include <LYCurses.h>
+#include <LYUtils.h>
+#include <LYGlobalDefs.h>
+#include <LYStrings.h>
+#include <LYEdit.h>
+#ifdef VMS
+#include <unixio.h>
+#endif /* VMS */
+
+#include <LYLeaks.h>
+#include <www_wait.h>
+
+BOOLEAN editor_can_position(void)
+{
+    char *value;
+    HTList *p = positionable_editor;
+    static const char *table[] =
+    {
+#ifdef VMS
+	"sedt",
+#else
+	"emacs",		/* + xemacs */
+	"jed",
+	"jmacs",
+	"joe",			/* + rjoe */
+	"jove",
+	"jstar",
+	"nano",
+	"pico",			/* + jpico */
+	"vi"			/* + vim, xvi, vile, elvis, view... + likely false matches */
+#endif
+    };
+    unsigned n;
+
+    for (n = 0; n < TABLESIZE(table); n++) {
+	if (LYstrstr(editor, table[n]) != 0) {
+	    return TRUE;
+	}
+    }
+    /*
+     * This really isn't right.  LYstrstr() might be too lax,
+     * but this should at least match basename to basename...
+     */
+    if (positionable_editor != NULL) {
+	while ((value = (char *) HTList_nextObject(p)) != NULL) {
+	    if (strcmp(editor, value) == 0) {
+		return TRUE;
+	    }
+	}
+    }
+    return FALSE;
+}
+
+/*
+ * In edit mode invoke the given (or default) editor to display and edit the
+ * current file.  For editors listed in 'editor_can_position()', Lynx will open
+ * the file to the same line that the screen cursor is on (or close...) when
+ * editing is invoked.
+ *
+ * Returns FALSE if file is uneditable.
+ */
+int edit_current_file(char *newfile,
+		      int cur,
+		      int lineno)
+{
+    int result = FALSE;
+    char *filename = NULL;
+
+#if !(defined(VMS) || defined(USE_DOS_DRIVES))
+    char *colon;
+#endif
+    char *number_sign;
+    char position[80];
+
+#if defined(VMS) || defined(CANT_EDIT_UNWRITABLE_FILES)
+    FILE *fp;
+#endif
+
+    CTRACE((tfp, "edit_current_file(newfile=%s, cur=%d, lineno=%d)\n",
+	    newfile, cur, lineno));
+
+    /*
+     * If it's a remote file then we can't edit it.
+     */
+    if (!LYisLocalFile(newfile)) {
+	HTUserMsg(CANNOT_EDIT_REMOTE_FILES);
+	return FALSE;
+    }
+
+    /*
+     * If there's a fragment, trim it.  - FM
+     */
+    number_sign = trimPoundSelector(newfile);
+
+    /*
+     * On Unix, first try to open it as a completely referenced file, then via
+     * the path alone.
+     *
+     * On VMS, only try the path.
+     */
+#if defined (VMS) || defined (USE_DOS_DRIVES)
+    filename = HTParse(newfile, "", PARSE_PATH + PARSE_PUNCTUATION);
+    HTUnEscape(filename);
+    StrAllocCopy(filename, HTSYS_name(filename));
+    if (!LYCanReadFile(filename)) {
+#ifdef SH_EX
+	HTUserMsg2(COULD_NOT_EDIT_FILE, filename);
+#else
+	HTAlert(COULD_NOT_ACCESS_FILE);
+#endif
+	CTRACE((tfp, "filename: '%s'\n", filename));
+	goto done;
+    }
+#else /* something like UNIX */
+    if (strncmp(newfile, "file://localhost/", 16) == 0)
+	colon = newfile + 16;
+    else
+	colon = strchr(newfile, ':');
+    StrAllocCopy(filename, (colon + 1));
+    HTUnEscape(filename);
+    if (!LYCanReadFile(filename)) {
+	FREE(filename);
+	filename = HTParse(newfile, "", PARSE_PATH + PARSE_PUNCTUATION);
+	HTUnEscape(filename);
+	if (!LYCanReadFile(HTSYS_name(filename))) {
+	    HTAlert(COULD_NOT_ACCESS_FILE);
+	    goto done;
+	}
+    }
+#endif
+
+#if defined(VMS) || defined(CANT_EDIT_UNWRITABLE_FILES)
+    /*
+     * Don't allow editing if user lacks append access.
+     */
+    if ((fp = fopen(filename, TXT_A)) == NULL) {
+	HTUserMsg(NOAUTH_TO_EDIT_FILE);
+	goto done;
+    }
+    fclose(fp);
+#endif /* VMS || CANT_EDIT_UNWRITABLE_FILES */
+
+    /*
+     * Make sure cur is at least zero.  - FM
+     */
+    if (cur < 0) {
+	cur = 0;
+    }
+
+    /*
+     * Set up the command for the editor.  - FM
+     */
+    if (lineno >= 0) {
+	*position = 0;
+#ifdef VMS
+	lineno--;
+#endif
+	lineno += (nlinks ? links[cur].ly : 0);
+	if (lineno > 0)
+	    sprintf(position, "%d", lineno);
+    } else {
+	*position = '\0';
+    }
+
+    edit_temporary_file(filename, position, NULL);
+    result = TRUE;
+
+  done:
+    /*
+     * Restore the fragment if there was one.  - FM
+     */
+    restorePoundSelector(number_sign);
+
+    FREE(filename);
+    CTRACE((tfp, "edit_current_file returns %d\n", result));
+    return (result);
+}
+
+void edit_temporary_file(char *filename,
+			 const char *position,
+			 const char *message)
+{
+#ifdef UNIX
+    struct stat stat_info;
+#endif
+    const char *format = "%s %s";
+    char *command = NULL;
+    const char *editor_arg = "";
+    int params = 1;
+    int rv;
+
+    if (LYstrstr(editor, "pico")) {
+	editor_arg = " -t";	/* No prompt for filename to use */
+    }
+    if (editor_can_position() && *position) {
+#ifdef VMS
+	format = "%s %s -%s%s";
+	HTAddXpand(&command, format, params++, editor);
+	HTAddParam(&command, format, params++, filename);
+	HTAddParam(&command, format, params++, position);
+	HTAddParam(&command, format, params++, editor_arg);
+	HTEndParam(&command, format, params);
+#else
+	format = "%s +%s%s %s";
+	HTAddXpand(&command, format, params++, editor);
+	HTAddParam(&command, format, params++, position);
+	HTAddParam(&command, format, params++, editor_arg);
+	HTAddParam(&command, format, params++, filename);
+	HTEndParam(&command, format, params);
+#endif
+    }
+#ifdef DOSPATH
+    else if (strncmp(editor, "VZ", 2) == 0) {
+	/* for Vz editor */
+	format = "%s %s -%s";
+	HTAddXpand(&command, format, params++, editor);
+	HTAddParam(&command, format, params++, HTDOS_short_name(filename));
+	HTAddParam(&command, format, params++, position);
+	HTEndParam(&command, format, params);
+    } else if (strncmp(editor, "edit", 4) == 0) {
+	/* for standard editor */
+	HTAddXpand(&command, format, params++, editor);
+	HTAddParam(&command, format, params++, HTDOS_short_name(filename));
+	HTEndParam(&command, format, params);
+    }
+#endif
+    else {
+#ifdef _WINDOWS
+	if (strchr(editor, ' '))
+	    HTAddXpand(&command, format, params++, HTDOS_short_name(editor));
+	else
+	    HTAddXpand(&command, format, params++, editor);
+#else
+	HTAddXpand(&command, format, params++, editor);
+#endif
+	HTAddParam(&command, format, params++, filename);
+	HTEndParam(&command, format, params);
+    }
+    if (message != NULL) {
+	_statusline(message);
+    }
+
+    CTRACE((tfp, "LYEdit: %s\n", command));
+    CTRACE_SLEEP(MessageSecs);
+
+    stop_curses();
+
+#ifdef UNIX
+    set_errno(0);
+#endif
+    if ((rv = LYSystem(command)) != 0) {	/* Spawn Editor */
+	start_curses();
+	/*
+	 * If something went wrong, we should probably return soon; currently
+	 * we don't, but at least put out a message.  - kw
+	 */
+	{
+#if defined(UNIX) && defined(WIFEXITED)
+	    int save_err = errno;
+
+	    CTRACE((tfp, "ExtEditForm: system() returned %d (0x%x), %s\n",
+		    rv, rv,
+		    (save_err
+		     ? LYStrerror(save_err)
+		     : "reason unknown")));
+	    LYFixCursesOn("show error warning:");
+	    if (rv == -1) {
+		HTUserMsg2(gettext("Error starting editor, %s"),
+			   LYStrerror(save_err));
+	    } else if (WIFSIGNALED(rv)) {
+		HTAlwaysAlert(NULL, gettext("Editor killed by signal"));
+	    } else if (WIFEXITED(rv) && WEXITSTATUS(rv) != 127) {
+		char exitcode[80];
+
+		sprintf(exitcode, "%d", WEXITSTATUS(rv));
+		HTUserMsg2(gettext("Editor returned with error status %s"),
+			   exitcode);
+	    } else
+#endif
+		HTAlwaysAlert(NULL, ERROR_SPAWNING_EDITOR);
+	}
+    } else {
+	start_curses();
+    }
+#ifdef UNIX
+    /*
+     * Delete backup file, if that's your style.
+     */
+    HTSprintf0(&command, "%s~", filename);
+    if (stat(command, &stat_info) == 0)
+	remove(command);
+#endif
+    FREE(command);
+}
diff --git a/src/LYEdit.h b/src/LYEdit.h
new file mode 100644
index 00000000..3c070628
--- /dev/null
+++ b/src/LYEdit.h
@@ -0,0 +1,18 @@
+#ifndef LYEDIT_H
+#define LYEDIT_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    extern BOOLEAN editor_can_position(void);
+    extern int edit_current_file(char *newfile, int cur, int lineno);
+    extern void edit_temporary_file(char *filename, const char *position, const char *message);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYEDIT_H */
diff --git a/src/LYEditmap.c b/src/LYEditmap.c
new file mode 100644
index 00000000..04fdee7f
--- /dev/null
+++ b/src/LYEditmap.c
@@ -0,0 +1,1299 @@
+/*
+ * $LynxId: LYEditmap.c,v 1.27 2008/09/07 23:00:47 tom Exp $
+ *
+ * LYEditMap.c
+ * Keybindings for line and form editting.
+ */
+
+#include <HTUtils.h>
+#include <LYGlobalDefs.h>
+#include <LYStrings.h>
+#include <LYKeymap.h>		/* KEYMAP_SIZE, LKC_*, LYK_* - kw */
+
+/* * * * * LynxEditactionCodes * * * * */
+#ifdef EXP_ALT_BINDINGS
+
+/* Last valid index for the (lynxkeycode+modifier -> lynxeditactioncode)
+ * tables.  Currently all three tables are the same.  - kw
+ */
+#define LAST_MOD1_LKC	0x111
+#define LAST_MOD2_LKC	0x111
+#define LAST_MOD3_LKC	0x111
+
+/*  Get (lynxkeycode+modifier -> lynxeditactioncode) mapping, intermediate.
+ */
+#define LKC_TO_LEC_M1(c) ((c)>LAST_MOD1_LKC? (int)LYE_UNMOD: Mod1Binding[c])
+#define LKC_TO_LEC_M2(c) ((c)>LAST_MOD2_LKC? (int)LYE_UNMOD: Mod2Binding[c])
+#define LKC_TO_LEC_M3(c) ((c)>LAST_MOD3_LKC? (int)LYE_UNMOD: Mod3Binding[c])
+
+#endif /* EXP_ALT_BINDINGS */
+
+int current_lineedit = 0;	/* Index into LYLineEditors[]   */
+
+int escape_bound = 0;		/* User wanted Escape to perform actions?  */
+
+/*
+ * See LYStrings.h for the LYE definitions.
+ */
+/* *INDENT-OFF* */
+static LYEditCode DefaultEditBinding[KEYMAP_SIZE - 1] = {
+
+LYE_NOP,        LYE_BOL,        LYE_DELPW,      LYE_ABORT,
+/* nul          ^A              ^B              ^C      */
+
+LYE_DELN,       LYE_EOL,        LYE_DELNW,      LYE_ABORT,
+/* ^D           ^E              ^F              ^G      */
+
+LYE_DELP,       LYE_TAB,      LYE_ENTER,      LYE_LOWER,
+/* bs           tab             nl              ^K      */
+
+LYE_NOP,        LYE_ENTER,      LYE_FORWW,      LYE_ABORT,
+/* ^L           cr              ^N              ^O      */
+
+LYE_BACKW,      LYE_NOP,        LYE_DELN,       LYE_NOP,
+/* ^P           XON             ^R              XOFF    */
+
+#ifdef CAN_CUT_AND_PASTE
+LYE_UPPER,      LYE_ERASE,      LYE_LKCMD,      LYE_PASTE,
+#else
+LYE_UPPER,      LYE_ERASE,      LYE_LKCMD,      LYE_NOP,
+#endif
+/* ^T           ^U              ^V              ^W      */
+
+LYE_SETM1,      LYE_NOP,        LYE_NOP,        LYE_NOP,
+/* ^X           ^Y              ^Z              ESC     */
+
+LYE_NOP,        LYE_NOP,        LYE_SWMAP,      LYE_DELEL,
+/* ^\           ^]              ^^              ^_      */
+
+/* sp .. RUBOUT                                         */
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_DELP,
+
+/* 80..9F ISO-8859-1 8-bit escape characters. */
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+#ifdef CJK_EX	/* 1997/11/03 (Mon) 20:30:54 */
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+#else
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_AIX,
+/*                                               97 AIX    */
+#endif
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+
+/* A0..FF (permissible ISO-8859-1) 8-bit characters. */
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+
+/* 100..10F function key definitions in LYStrings.h */
+LYE_FORM_PASS,  LYE_FORM_PASS,  LYE_FORW,       LYE_BACK,
+/* UPARROW      DNARROW         RTARROW         LTARROW     */
+
+LYE_FORM_PASS,  LYE_FORM_PASS,  LYE_BOL,        LYE_EOL,
+/* PGDOWN       PGUP            HOME            END         */
+
+#if (defined(_WINDOWS) || defined(__DJGPP__))
+
+LYE_FORM_PASS,  LYE_NOP,        LYE_NOP,        LYE_NOP,
+/* F1 */
+
+#else
+
+LYE_FORM_PASS,  LYE_TAB,        LYE_BOL,        LYE_EOL,
+/* F1           Do key          Find key        Select key  */
+
+#endif /* _WINDOWS || __DJGPP__ */
+
+LYE_NOP,        LYE_DELP,       LYE_NOP,        LYE_FORM_PASS,
+/* Insert key   Remove key      DO_NOTHING      Back tab */
+
+/* 110..18F */
+#if (defined(_WINDOWS) || defined(__DJGPP__)) && defined(USE_SLANG) && !defined(DJGPP_KEYHANDLER)
+
+LYE_DELP,       LYE_ENTER,      LYE_NOP,        LYE_NOP,
+/* Backspace    Enter */
+
+#else
+
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+
+#endif /* USE_SLANG &&(_WINDOWS || __DJGPP) && !DJGPP_KEYHANDLER */
+
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+/*             MOUSE_KEY  */
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+/* 190..20F */
+
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+/* 210..28F */
+
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+/* 290..293 */
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+};
+
+/*
+ * Add your favorite key bindings HERE
+ */
+
+/* KED-01 */ /* Default except: ^B=cursor-backward,  ^F=cursor-forward,   */
+             /*                 ^K=delete-to-EOL,    ^X=delete-to-BOL,    */
+             /*                 ^R=delete-prev-word, ^T=delete-next-word, */
+             /*                 ^^=upper-case-line,  ^_=lower-case-line   */
+/* Why the difference for tab? - kw */
+
+#ifdef EXP_ALT_BINDINGS
+static LYEditCode BetterEditBinding[KEYMAP_SIZE-1]={
+
+LYE_NOP,        LYE_BOL,        LYE_BACK,       LYE_ABORT,
+/* nul          ^A              ^B              ^C      */
+
+LYE_DELN,       LYE_EOL,        LYE_FORW,       LYE_ABORT,
+/* ^D           ^E              ^F              ^G      */
+
+LYE_DELP,       LYE_ENTER,      LYE_ENTER,      LYE_DELEL,
+/* bs           tab             nl              ^K      */
+
+LYE_NOP,        LYE_ENTER,      LYE_FORWW,      LYE_ABORT,
+/* ^L           cr              ^N              ^O      */
+
+LYE_BACKW,      LYE_NOP,        LYE_DELPW,      LYE_NOP,
+/* ^P           XON             ^R              XOFF    */
+
+#ifdef CAN_CUT_AND_PASTE
+LYE_DELNW,      LYE_ERASE,      LYE_LKCMD,      LYE_PASTE,
+#else
+LYE_DELNW,      LYE_ERASE,      LYE_LKCMD,      LYE_NOP,
+#endif
+/* ^T           ^U              ^V              ^W      */
+
+LYE_SETM1,      LYE_NOP,        LYE_NOP,        LYE_NOP,
+/* ^X           ^Y              ^Z              ESC     */
+
+LYE_NOP,        LYE_NOP,        LYE_UPPER,      LYE_LOWER,
+/* ^\           ^]              ^^              ^_      */
+
+/* sp .. RUBOUT                                         */
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_DELP,
+
+/* 80..9F ISO-8859-1 8-bit escape characters. */
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+#ifdef CJK_EX	/* 1997/11/03 (Mon) 20:30:54 */
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+#else
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_AIX,
+/*                                               97 AIX    */
+#endif
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+
+/* A0..FF (permissible ISO-8859-1) 8-bit characters. */
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+
+/* 100..10F function key definitions in LYStrings.h */
+LYE_FORM_PASS,  LYE_FORM_PASS,  LYE_FORW,       LYE_BACK,
+/* UPARROW      DNARROW         RTARROW         LTARROW     */
+
+LYE_FORM_PASS,  LYE_FORM_PASS,  LYE_BOL,        LYE_EOL,
+/* PGDOWN       PGUP            HOME            END         */
+
+#if (defined(_WINDOWS) || defined(__DJGPP__))
+
+LYE_FORM_PASS,  LYE_NOP,        LYE_NOP,        LYE_NOP,
+/* F1 */
+
+#else
+
+LYE_FORM_PASS,  LYE_TAB,        LYE_BOL,        LYE_EOL,
+/* F1           Do key          Find key        Select key  */
+
+#endif /* _WINDOWS || __DJGPP__ */
+
+LYE_NOP,        LYE_DELP,       LYE_NOP,        LYE_FORM_PASS,
+/* Insert key   Remove key      DO_NOTHING      Back tab */
+
+/* 110..18F */
+#if (defined(_WINDOWS) || defined(__DJGPP__)) && defined(USE_SLANG) && !defined(DJGPP_KEYHANDLER)
+
+LYE_DELP,       LYE_ENTER,      LYE_NOP,        LYE_NOP,
+/* Backspace    Enter */
+
+#else
+
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+
+#endif /* USE_SLANG &&(_WINDOWS || __DJGPP) && !DJGPP_KEYHANDLER */
+
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+/*             MOUSE_KEY  */
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+/* 190..20F */
+
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+/* 210..28F */
+
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+/* 290..293 */
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+};
+
+/* kw     */ /* Default except: ^B=cursor-backward[+],^F=cursor-forward[+], */
+             /*                 ^K=delete-to-EOL[+][++],^X=Modifier Prefix, */
+             /*                 ^[ (ESC)=Modifier Prefix,                   */
+             /*                 ^R=BACKW,             ^S=FORWW,             */
+             /*                 ^T=transpose-chars,                         */
+             /*                 ^U=delete-to-BOL,     ^W=delete-prev-word,  */
+             /*                 ^@ (NUL)=SETMARK,     ^Y=YANK,              */
+             /*                 ^_=ABORT (undo),                            */
+             /*                 ^P=FORM_PASS,         ^N=FORM_PASS,         */
+             /*                 ^O=FORM_PASS,         ^L=FORM_PASS,         */
+             /*                 ^\=FORM_PASS,         ^]=FORM_PASS,         */
+             /*                 ^Z=FORM_PASS,         F1=FORM_PASS,         */
+             /*                 ^E=EOL[++],           Remove=DELN           */
+             /* [+]: same as BetterEditBinding                              */
+             /* [++]: additionally set double-key modifier                  */
+
+/* Default where BetterEditBinding deviates:          ^^=SWMAP,            */
+             /*                tab=LYE_TAB                                 */
+
+/* Some functions for which the modifier binding is preferred:             */
+             /*         M-bs,M-del=delete-prev-word, M-d=delete-next-word, */
+             /*                M-b=BACKW,            M-f=FORWW,            */
+
+static LYEditCode BashlikeEditBinding[KEYMAP_SIZE-1]={
+
+LYE_SETMARK,    LYE_BOL,        LYE_BACK,       LYE_ABORT,
+/* nul          ^A              ^B              ^C      */
+
+LYE_DELN,       LYE_EOL|LYE_DF, LYE_FORW,       LYE_ABORT,
+/* ^D           ^E              ^F              ^G      */
+
+LYE_DELP,       LYE_TAB,        LYE_ENTER,      LYE_DELEL|LYE_DF,
+/* bs           tab             nl              ^K      */
+
+LYE_FORM_PASS,  LYE_ENTER,      LYE_FORM_PASS,  LYE_FORM_PASS,
+/* ^L           cr              ^N              ^O      */
+
+LYE_FORM_PASS,  LYE_NOP,        LYE_BACKW,      LYE_FORWW,
+/* ^P           XON             ^R              ^S/XOFF */
+
+LYE_TPOS,       LYE_DELBL,      LYE_LKCMD,      LYE_DELPW,
+/* ^T           ^U              ^V              ^W      */
+
+LYE_SETM1,      LYE_YANK,       LYE_FORM_PASS,  LYE_SETM2,
+/* ^X           ^Y              ^Z              ESC     */
+
+LYE_FORM_PASS,  LYE_FORM_PASS,  LYE_SWMAP,      LYE_ABORT,
+/* ^\           ^]              ^^              ^_      */
+
+/* sp .. RUBOUT                                         */
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_DELP,
+
+/* 80..9F ISO-8859-1 8-bit escape characters. */
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_AIX,
+/*                                               97 AIX    */
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+
+/* A0..FF (permissible ISO-8859-1) 8-bit characters. */
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+LYE_CHAR,       LYE_CHAR,       LYE_CHAR,       LYE_CHAR,
+
+/* 100..10F function key definitions in LYStrings.h */
+LYE_FORM_PASS,  LYE_FORM_PASS,  LYE_FORW,       LYE_BACK,
+/* UPARROW      DNARROW         RTARROW         LTARROW     */
+
+LYE_FORM_PASS,  LYE_FORM_PASS,  LYE_BOL,        LYE_EOL,
+/* PGDOWN       PGUP            HOME            END         */
+
+#if (defined(_WINDOWS) || defined(__DJGPP__))
+
+LYE_FORM_PASS,  LYE_NOP,        LYE_NOP,        LYE_NOP,
+/* F1 */
+
+#else
+
+LYE_FORM_PASS,  LYE_TAB,        LYE_BOL,        LYE_EOL,
+/* F1           Do key          Find key        Select key  */
+
+#endif /* _WINDOWS || __DJGPP__ */
+
+LYE_NOP,        LYE_DELN,       LYE_NOP,        LYE_FORM_PASS,
+/* Insert key   Remove key      DO_NOTHING      Back tab */
+
+/* 110..18F */
+#if (defined(_WINDOWS) || defined(__DJGPP__)) && defined(USE_SLANG) && !defined(DJGPP_KEYHANDLER)
+
+LYE_DELP,       LYE_ENTER,      LYE_NOP,        LYE_NOP,
+/* Backspace    Enter */
+
+#else
+
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+
+#endif /* USE_SLANG &&(_WINDOWS || __DJGPP) && !DJGPP_KEYHANDLER */
+
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+/*             MOUSE_KEY  */
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+/* 190..20F */
+
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+/* 210..28F */
+
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+/* 290..293 */
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+};
+
+/* Oh no, not another one of those tables...
+ *
+ * If modifier bit is set in a lynxkeycode, it is first looked up here.  Note
+ * the type different from the previous tables (short vs.  char), since we want
+ * to hold larger values.  OTOH we can keep the size shorter, everything beyond
+ * the end is effectively LYE_UNMOD (ignore modifier) by virtue of the
+ * LKC_TO_LEC_M1 macro.
+ *
+ * Currently this table isn't specific to the current_lineedit value, it is
+ * shared by all alternative "Bindings" to save space.  However, if the
+ * modifier flag is set only by a LYE_SETMn lynxeditaction, this table can have
+ * effect only for those Bindings that map a lynxkeycode to LYE_SETMn.  ( This
+ * doesn't apply if the modifier is already being set in LYgetch().  ) - kw
+ */
+static short Mod1Binding[LAST_MOD1_LKC+1]={
+
+LYE_NOP,        LYE_BOL,        LYE_BACKW,      LYE_UNMOD,
+/* nul          ^A              ^B              ^C      */
+
+LYE_FORM_LAC|LYK_NEXT_LINK,
+                LYE_FORM_LAC|LYK_EDIT_TEXTAREA,
+                                LYE_FORWW,      LYE_ABORT,
+/* ^D           ^E              ^F              ^G      */
+
+LYE_DELPW,      LYE_UNMOD,      LYE_ENTER,     LYE_FORM_LAC|LYK_LPOS_NEXT_LINK,
+/* bs           tab             nl              ^K      */
+
+LYE_FORM_PASS,  LYE_ENTER,      LYE_FORWW,      LYE_UNMOD,
+/* ^L           cr              ^N              ^O      */
+
+LYE_BACKW,      LYE_NOP,        LYE_BACKW,      LYE_NOP,
+/* ^P           XON             ^R              ^S/XOFF */
+
+LYE_NOP,        LYE_FORM_PASS,  LYE_NOP,        LYE_KILLREG,
+/* ^T           ^U              ^V              ^W      */
+
+LYE_XPMARK,     LYE_UNMOD,      LYE_FORM_PASS,  LYE_NOP,
+/* ^X           ^Y              ^Z              ESC     */
+
+LYE_FORM_PASS,  LYE_FORM_PASS,  LYE_UNMOD,      LYE_NOP,
+/* ^\           ^]              ^^              ^_      */
+
+/* sp .. ?                                              */
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_FORM_PASS,
+
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_FORM_LAC|LYK_HOME,
+                LYE_UNMOD,      LYE_FORM_LAC|LYK_END,
+                                                LYE_UNMOD,
+
+/* @, A .. Z, [, \, ], ^, _                             */
+LYE_C1CHAR,     LYE_C1CHAR,     LYE_C1CHAR,     LYE_C1CHAR,
+LYE_C1CHAR,     LYE_C1CHAR,     LYE_C1CHAR,     LYE_C1CHAR,
+LYE_C1CHAR,     LYE_C1CHAR,     LYE_C1CHAR,     LYE_C1CHAR,
+LYE_C1CHAR,     LYE_C1CHAR,     LYE_C1CHAR,     LYE_C1CHAR,
+LYE_C1CHAR,     LYE_C1CHAR,     LYE_C1CHAR,     LYE_C1CHAR,
+LYE_C1CHAR,     LYE_C1CHAR,     LYE_C1CHAR,     LYE_C1CHAR,
+LYE_C1CHAR,     LYE_C1CHAR,     LYE_C1CHAR,     LYE_C1CHAR,
+LYE_C1CHAR,     LYE_C1CHAR,     LYE_C1CHAR,     LYE_C1CHAR,
+
+/* `, a .. z, {, |, }, ~, RUBOUT                        */
+LYE_UNMOD,      LYE_BOL,        LYE_BACKW,      LYE_UNMOD,
+LYE_DELNW,      LYE_FORM_LAC|LYK_EDIT_TEXTAREA,
+                                LYE_FORWW,      LYE_FORM_LAC|LYK_GROW_TEXTAREA,
+LYE_CHAR,       LYE_FORM_LAC|LYK_INSERT_FILE,
+                                LYE_CHAR,       LYE_ERASE,
+LYE_LOWER,      LYE_CHAR,       LYE_FORM_PASS,  LYE_UNMOD,
+LYE_CHAR,       LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_UPPER,      LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_DELPW,
+
+/* 80..9F ISO-8859-1 8-bit escape characters. */
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+LYE_NOP,        LYE_NOP,        LYE_NOP,        LYE_NOP,
+
+/* A0..FF (permissible ISO-8859-1) 8-bit characters. */
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+
+/* 100..10F function key definitions in LYStrings.h */
+LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+/* UPARROW      DNARROW         RTARROW         LTARROW     */
+
+LYE_UNMOD,      LYE_UNMOD,      LYE_FORM_PASS,  LYE_FORM_PASS,
+/* PGDOWN       PGUP            HOME            END         */
+
+#if (defined(_WINDOWS) || defined(__DJGPP__))
+
+LYE_FORM_LAC|LYK_DWIMHELP,
+                LYE_UNMOD,      LYE_UNMOD,      LYE_UNMOD,
+/* F1 */
+
+#else
+
+LYE_FORM_LAC|LYK_DWIMHELP,
+                LYE_UNMOD,  LYE_FORM_LAC|LYK_WHEREIS, LYE_FORM_LAC|LYK_NEXT,
+/* F1           Do key          Find key        Select key  */
+
+#endif /* _WINDOWS || __DJGPP__ */
+
+LYE_UNMOD,      LYE_NOP,        LYE_UNMOD,      LYE_UNMOD,
+/* Insert key   Remove key      DO_NOTHING      Back tab */
+
+/* 110..111 */
+#if (defined(_WINDOWS) || defined(__DJGPP__)) && defined(USE_SLANG) && !defined(DJGPP_KEYHANDLER)
+
+LYE_DELPW,      LYE_UNMOD,
+/* Backspace    Enter */
+
+#else
+
+LYE_UNMOD,      LYE_UNMOD,
+
+#endif /* USE_SLANG &&(_WINDOWS || __DJGPP) && !DJGPP_KEYHANDLER */
+};
+
+/*  Two more tables here, but currently they are all the same.
+    In other words, we are cheating to save space, until there
+    is a need for different tables. - kw */
+static short *Mod2Binding = Mod1Binding;
+static short *Mod3Binding = Mod1Binding;
+
+#endif /* EXP_ALT_BINDINGS */
+/* *INDENT-ON* */
+
+/*
+ * Add the array name to LYLineEditors
+ */
+
+LYEditCode *LYLineEditors[] =
+{
+    DefaultEditBinding,		/* You can't please everyone, so you ... DW */
+#ifdef EXP_ALT_BINDINGS
+    BetterEditBinding,		/* No, you certainly can't ... /ked 10/27/98 */
+    BashlikeEditBinding,	/* and one more... - kw 1999-02-15 */
+#endif
+};
+
+/*
+ * Add the name that the user will see below.
+ * The order of LYLineEditors and LYLineditNames MUST be the same.
+ */
+const char *LYLineeditNames[] =
+{
+    "Default Binding",
+#ifdef EXP_ALT_BINDINGS
+    "Alternate Bindings",
+    "Bash-like Bindings",
+#endif
+    (char *) 0
+};
+
+/*
+ * Add the URL (relative to helpfilepath) used for context-dependent
+ * help on form field editing.
+ *
+ * The order must correspond to that of LYLineditNames.
+ */
+const char *LYLineeditHelpURLs[] =
+{
+    EDIT_HELP,
+#ifdef EXP_ALT_BINDINGS
+    ALT_EDIT_HELP,
+    BASHLIKE_EDIT_HELP,
+#endif
+    (char *) 0
+};
+
+int EditBinding(int xlkc)
+{
+    int editaction, xleac = LYE_UNMOD;
+    int c = xlkc & LKC_MASK;
+
+    if (xlkc == -1)
+	return LYE_NOP;		/* maybe LYE_ABORT? or LYE_FORM_LAC|LYK_UNKNOWN? */
+#ifdef NOT_ASCII
+    if (c < 256) {
+	c = TOASCII(c);
+    }
+#endif
+#ifdef EXP_ALT_BINDINGS
+    /*
+     * Get intermediate code from one of the lynxkeycode+modifier tables if
+     * applicable, otherwise get the lynxeditactioncode directly.  If we have
+     * more than one modifier bits, the first currently wins.  - kw
+     */
+    if (xlkc & LKC_ISLECLAC) {
+	return LKC2_TO_LEC(xlkc);
+    } else if (xlkc & LKC_MOD1) {
+	xleac = LKC_TO_LEC_M1(c);
+    } else if (xlkc & LKC_MOD2) {
+	xleac = LKC_TO_LEC_M2(c);
+    } else if (xlkc & LKC_MOD3) {
+	xleac = LKC_TO_LEC_M3(c);
+    } else {
+	xleac = UCH(CurrentLineEditor()[c]);
+    }
+#endif
+    /*
+     * If we have an intermediate code that says "same as without modifier",
+     * look that up now; otherwise we are already done.  - kw
+     */
+    if (xleac == LYE_UNMOD) {
+	editaction = CurrentLineEditor()[c];
+    } else {
+	editaction = xleac;
+    }
+    return editaction;
+}
+
+/*
+ * Install lec as the lynxeditaction for lynxkeycode xlkc.  func must be
+ * present in the revmap table.  For normal (non-modifier) lynxkeycodes,
+ * select_edi selects which of the alternative line-editor binding tables is
+ * modified.  If select_edi is positive, only the table given by it is modified
+ * (the DefaultEditBinding table is numbered 1).  If select_edi is 0, all
+ * tables are modified.  If select_edi is negative, all tables except the one
+ * given by abs(select_edi) are modified.  returns TRUE if the mapping was
+ * made, FALSE if not.  Note that this remapping cannot be undone (as might be
+ * desirable as a result of re-parsing lynx.cfg), we don't remember the
+ * original editaction from the Bindings tables anywhere.  - kw
+ */
+BOOL LYRemapEditBinding(int xlkc,
+			int lec,
+			int select_edi)
+{
+    int j;
+    int c = xlkc & LKC_MASK;
+    BOOLEAN success = FALSE;
+
+    if (xlkc < 0 || (xlkc & LKC_ISLAC) || c >= KEYMAP_SIZE + 1)
+	return FALSE;
+#ifdef EXP_ALT_BINDINGS
+    if (xlkc & LKC_MOD1) {
+	if (c > LAST_MOD1_LKC)
+	    return FALSE;
+	else
+	    Mod1Binding[c] = (short) lec;
+	return TRUE;
+    } else if (xlkc & LKC_MOD2) {
+	if (c > LAST_MOD2_LKC)
+	    return FALSE;
+	else
+	    Mod2Binding[c] = (short) lec;
+	return TRUE;
+    } else if (xlkc & LKC_MOD3) {
+	if (c > LAST_MOD3_LKC)
+	    return FALSE;
+	else
+	    Mod3Binding[c] = (short) lec;
+	return TRUE;
+    } else
+#endif /* EXP_ALT_BINDINGS */
+    {
+#ifndef UCHAR_MAX
+#define UCHAR_MAX 255
+#endif
+	if ((unsigned int) lec > UCHAR_MAX)
+	    return FALSE;	/* cannot do, doesn't fit in a char - kw */
+	if (select_edi > 0) {
+	    if ((unsigned int) select_edi < TABLESIZE(LYLineEditors)) {
+		LYLineEditors[select_edi - 1][c] = (LYEditCode) lec;
+		success = TRUE;
+	    }
+	} else {
+	    for (j = 0; LYLineeditNames[j]; j++) {
+		success = TRUE;
+		if (select_edi < 0 && j + 1 + select_edi == 0)
+		    continue;
+		LYLineEditors[j][c] = (LYEditCode) lec;
+	    }
+	}
+    }
+    return success;
+}
+
+/*
+ * Macro to walk through lkc-indexed tables up to imax, in the (ASCII) order
+ *     97 - 122  ('a' - 'z'),
+ *     32 -  96  (' ' - '`', includes 'A' - 'Z'),
+ *    123 - 126  ('{' - '~'),
+ *      0 -  31  (^@  - ^_),
+ *    256 - imax,
+ *    127 - 255
+ */
+#define NEXT_I(i,imax) ((i==122) ? 32 : (i==96) ? 123 : (i==126) ? 0 :\
+			(i==31) ? 256 : (i==imax) ? 127 :\
+			(i==255) ? (-1) :i+1)
+#define FIRST_I 97
+
+int LYKeyForEditAction(int lec)
+{
+    int editaction, i;
+
+    for (i = FIRST_I; i >= 0; i = NEXT_I(i, KEYMAP_SIZE - 2)) {
+	editaction = CurrentLineEditor()[i];
+	if (editaction == lec) {
+#ifdef NOT_ASCII
+	    if (i < 256) {
+		return FROMASCII(i);
+	    } else
+#endif
+		return i;
+	}
+    }
+    return (-1);
+}
+
+/*
+ * Given a lynxactioncode, return a key (lynxkeycode) or sequence of two keys
+ * that results in the given action while forms-editing.  The main keycode is
+ * returned as function value, possibly with modifier bits set; in addition, if
+ * applicable, a key that sets the required modifier flag is returned in
+ * *pmodkey if (pmodkey!=NULL).  Non-lineediting bindings that would require
+ * typing LYE_LKCMD (default ^V) to activate are not checked here, the caller
+ * should do that separately if required.  If no key is bound by current
+ * line-editor bindings to the action, -1 is returned.
+ *
+ * This is all a bit long - it is general enough to continue to work should the
+ * three Mod<N>Binding[] become different tables.  - kw
+ */
+int LYEditKeyForAction(int lac,
+		       int *pmodkey)
+{
+    int editaction, i, c;
+    int mod1found = -1, mod2found = -1, mod3found = -1;
+
+    if (pmodkey)
+	*pmodkey = -1;
+    for (i = FIRST_I; i >= 0; i = NEXT_I(i, KEYMAP_SIZE - 2)) {
+	editaction = CurrentLineEditor()[i];
+#ifdef NOT_ASCII
+	if (i < 256) {
+	    c = FROMASCII(i);
+	} else
+#endif
+	    c = i;
+	if (editaction == (lac | LYE_FORM_LAC))
+	    return c;
+	if (editaction == LYE_FORM_PASS) {
+#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE)
+	    if (lynx_edit_mode && !no_dired_support && lac &&
+		LKC_TO_LAC(key_override, c) == lac)
+		return c;
+#endif /* DIRED_SUPPORT && OK_OVERRIDE */
+	    if (LKC_TO_LAC(keymap, c) == lac)
+		return c;
+	}
+	if (editaction == LYE_TAB) {
+#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE)
+	    if (lynx_edit_mode && !no_dired_support && lac &&
+		LKC_TO_LAC(key_override, '\t') == lac)
+		return c;
+#endif /* DIRED_SUPPORT && OK_OVERRIDE */
+	    if (LKC_TO_LAC(keymap, '\t') == lac)
+		return c;
+	}
+	if (editaction == LYE_SETM1 && mod1found < 0)
+	    mod1found = i;
+	if (editaction == LYE_SETM2 && mod2found < 0)
+	    mod2found = i;
+	if ((editaction & LYE_DF) && mod3found < 0)
+	    mod3found = i;
+    }
+#ifdef EXP_ALT_BINDINGS
+    if (mod3found >= 0) {
+	for (i = mod3found; i >= 0; i = NEXT_I(i, LAST_MOD3_LKC)) {
+	    editaction = CurrentLineEditor()[i];
+	    if (!(editaction & LYE_DF))
+		continue;
+	    editaction = Mod3Binding[i];
+#ifdef NOT_ASCII
+	    if (i < 256) {
+		c = FROMASCII(i);
+	    } else
+#endif
+		c = i;
+	    if (pmodkey)
+		*pmodkey = c;
+	    if (editaction == (lac | LYE_FORM_LAC))
+		return (c | LKC_MOD3);
+	    if (editaction == LYE_FORM_PASS) {
+#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE)
+		if (lynx_edit_mode && !no_dired_support && lac &&
+		    LKC_TO_LAC(key_override, c) == lac)
+		    return (c | LKC_MOD3);
+#endif /* DIRED_SUPPORT && OK_OVERRIDE */
+		if (LKC_TO_LAC(keymap, c) == lac)
+		    return (c | LKC_MOD3);
+	    }
+	    if (editaction == LYE_TAB) {
+#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE)
+		if (lynx_edit_mode && !no_dired_support && lac &&
+		    LKC_TO_LAC(key_override, '\t') == lac)
+		    return (c | LKC_MOD3);
+#endif /* DIRED_SUPPORT && OK_OVERRIDE */
+		if (LKC_TO_LAC(keymap, '\t') == lac)
+		    return (c | LKC_MOD3);
+	    }
+	}
+    }
+    if (mod1found >= 0) {
+	if (pmodkey) {
+#ifdef NOT_ASCII
+	    if (mod1found < 256) {
+		*pmodkey = FROMASCII(mod1found);
+	    } else
+#endif
+		*pmodkey = mod1found;
+	}
+	for (i = FIRST_I; i >= 0; i = NEXT_I(i, LAST_MOD1_LKC)) {
+	    editaction = Mod1Binding[i];
+#ifdef NOT_ASCII
+	    if (i < 256) {
+		c = FROMASCII(i);
+	    } else
+#endif
+		c = i;
+	    if (editaction == (lac | LYE_FORM_LAC))
+		return (c | LKC_MOD1);
+	    if (editaction == LYE_FORM_PASS) {
+#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE)
+		if (lynx_edit_mode && !no_dired_support && lac &&
+		    LKC_TO_LAC(key_override, c) == lac)
+		    return (c | LKC_MOD1);
+#endif /* DIRED_SUPPORT && OK_OVERRIDE */
+		if (LKC_TO_LAC(keymap, c) == lac)
+		    return (c | LKC_MOD1);
+	    }
+	    if (editaction == LYE_TAB) {
+#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE)
+		if (lynx_edit_mode && !no_dired_support && lac &&
+		    LKC_TO_LAC(key_override, '\t') == lac)
+		    return (c | LKC_MOD1);
+#endif /* DIRED_SUPPORT && OK_OVERRIDE */
+		if (LKC_TO_LAC(keymap, '\t') == lac)
+		    return (c | LKC_MOD1);
+	    }
+	}
+    }
+    if (mod2found >= 0) {
+	if (pmodkey) {
+#ifdef NOT_ASCII
+	    if (mod1found < 256) {
+		*pmodkey = FROMASCII(mod1found);
+	    } else
+#endif
+		*pmodkey = mod1found;
+	}
+	for (i = FIRST_I; i >= 0; i = NEXT_I(i, LAST_MOD2_LKC)) {
+	    editaction = Mod2Binding[i];
+#ifdef NOT_ASCII
+	    if (i < 256) {
+		c = FROMASCII(i);
+	    } else
+#endif
+		c = i;
+	    if (editaction == (lac | LYE_FORM_LAC))
+		return (c | LKC_MOD2);
+	    if (editaction == LYE_FORM_PASS) {
+#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE)
+		if (lynx_edit_mode && !no_dired_support && lac &&
+		    LKC_TO_LAC(key_override, c) == lac)
+		    return (c | LKC_MOD2);
+#endif /* DIRED_SUPPORT && OK_OVERRIDE */
+		if (LKC_TO_LAC(keymap, c) == lac)
+		    return (c | LKC_MOD2);
+	    }
+	    if (editaction == LYE_TAB) {
+#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE)
+		if (lynx_edit_mode && !no_dired_support && lac &&
+		    LKC_TO_LAC(key_override, '\t') == lac)
+		    return (c | LKC_MOD2);
+#endif /* DIRED_SUPPORT && OK_OVERRIDE */
+		if (LKC_TO_LAC(keymap, '\t') == lac)
+		    return (c | LKC_MOD2);
+	    }
+	}
+    }
+#endif /* EXP_ALT_BINDINGS */
+    if (pmodkey)
+	*pmodkey = -1;
+    return (-1);
+}
+
+/*
+ * Dummy initializer to ensure this module is linked
+ * if the external model is common block, and the
+ * module is ever placed in a library. - FM
+ */
+int LYEditmapDeclared(void)
+{
+    int status = 1;
+
+    return status;
+}
diff --git a/src/LYExtern.c b/src/LYExtern.c
new file mode 100644
index 00000000..0e5783c8
--- /dev/null
+++ b/src/LYExtern.c
@@ -0,0 +1,418 @@
+/*
+ * $LynxId: LYExtern.c,v 1.42 2009/01/01 22:07:00 tom Exp $
+ *
+ External application support.
+ This feature allows lynx to pass a given URL to an external program.
+ It was written for three reasons.
+ 1) To overcome the deficiency	of Lynx_386 not supporting ftp and news.
+    External programs can be used instead by passing the URL.
+
+ 2) To allow for background transfers in multitasking systems.
+    I use wget for http and ftp transfers via the external command.
+
+ 3) To allow for new URLs to be used through lynx.
+    URLs can be made up such as mymail: to spawn desired applications
+    via the external command.
+
+ See lynx.cfg for other info.
+*/
+
+#include <LYUtils.h>
+
+#ifdef USE_EXTERNALS
+
+#include <HTAlert.h>
+#include <LYGlobalDefs.h>
+#include <LYExtern.h>
+#include <LYLeaks.h>
+#include <LYCurses.h>
+#include <LYReadCFG.h>
+#include <LYStrings.h>
+
+#ifdef WIN_EX
+/* ASCII char -> HEX digit */
+#define ASC2HEXD(x) (((x) >= '0' && (x) <= '9') ?               \
+		     ((x) - '0') : (toupper(x) - 'A' + 10))
+
+/* Decodes the forms %xy in a URL to the character the hexadecimal
+   code of which is xy. xy are hexadecimal digits from
+   [0123456789ABCDEF] (case-insensitive). If x or y are not hex-digits
+   or '%' is near '\0', the whole sequence is inserted literally. */
+
+static char *decode_string(char *s)
+{
+    char *save_s;
+    char *p = s;
+
+    save_s = s;
+    for (; *s; s++, p++) {
+	if (*s != '%')
+	    *p = *s;
+	else {
+	    /* Do nothing if at the end of the string. Or if the chars
+	       are not hex-digits. */
+	    if (!*(s + 1) || !*(s + 2)
+		|| !(isxdigit(*(s + 1)) && isxdigit(*(s + 2)))) {
+		*p = *s;
+		continue;
+	    }
+	    *p = (char) ((ASC2HEXD(*(s + 1)) << 4) + ASC2HEXD(*(s + 2)));
+	    s += 2;
+	}
+    }
+    *p = '\0';
+    return save_s;
+}
+#endif /* WIN_EX */
+
+#ifdef WIN_EX
+/*
+ *  Delete dangerous characters as local path.
+ *  We delete '<>|' and also '%"'.
+ *  '%' should be deleted because it's difficut to escape for all cases.
+ *  So we can't treat paths which include '%'.
+ *  '"' should be deleted because it's a obstacle to quote whole path.
+ */
+static void delete_danger_characters(char *src)
+{
+    char *dst;
+
+    for (dst = src; *src != '\0'; src++) {
+	if (strchr("<>|%\"", *src) == NULL) {
+	    *dst = *src;
+	    dst++;
+	}
+    }
+    *dst = '\0';
+}
+
+static char *escapeParameter(CONST char *parameter)
+{
+    size_t i;
+    size_t last = strlen(parameter);
+    size_t n = 0;
+    size_t encoded = 0;
+    size_t escaped = 0;
+    char *result;
+    char *needs_encoded = "<>|";
+    char *needs_escaped = "%";
+    char *needs_escaped_NT = "%&^";
+
+    for (i = 0; i < last; ++i) {
+	if (strchr(needs_encoded, parameter[i]) != NULL) {
+	    ++encoded;
+	}
+	if (system_is_NT) {
+	    if (strchr(needs_escaped_NT, parameter[i]) != NULL) {
+		++escaped;
+	    }
+	} else if (strchr(needs_escaped, parameter[i]) != NULL) {
+	    ++escaped;
+	}
+    }
+
+    result = (char *) malloc(last + encoded * 2 + escaped + 1);
+    if (result == NULL)
+	outofmem(__FILE__, "escapeParameter");
+
+    n = 0;
+    for (i = 0; i < last; i++) {
+	if (strchr(needs_encoded, parameter[i]) != NULL) {
+	    sprintf(result + n, "%%%02X", (unsigned char) parameter[i]);
+	    n += 3;
+	    continue;
+	}
+	if (system_is_NT) {
+	    if (strchr(needs_escaped_NT, parameter[i]) != NULL) {
+		result[n++] = '^';
+		result[n++] = parameter[i];
+		continue;
+	    }
+	} else if (strchr(needs_escaped, parameter[i]) != NULL) {
+	    result[n++] = '%';	/* parameter[i] is '%' */
+	    result[n++] = parameter[i];
+	    continue;
+	}
+	result[n++] = parameter[i];
+    }
+    result[n] = '\0';
+
+    return result;
+}
+#endif /* WIN_EX */
+
+static void format(char **result,
+		   char *fmt,
+		   char *parm)
+{
+    *result = NULL;
+    HTAddParam(result, fmt, 1, parm);
+    HTEndParam(result, fmt, 1);
+}
+
+/*
+ * Format the given command into a buffer, returning the resulting string.
+ *
+ * It is too dangerous to leave any URL that may come along unquoted.  They
+ * often contain '&', ';', and '?' chars, and who knows what else may occur.
+ * Prevent spoofing of the shell.  Dunno how this needs to be modified for VMS
+ * or DOS.  - kw
+ */
+static char *format_command(char *command,
+			    char *param)
+{
+    char *cmdbuf = NULL;
+
+#if defined(WIN_EX)
+    char pram_string[LY_MAXPATH];
+    char *escaped = NULL;
+
+    if (strnicmp("file://localhost/", param, 17) == 0) {
+	/* decode local path parameter for programs to be
+	   able to interpret - TH */
+	LYstrncpy(pram_string, param, sizeof(pram_string) - 1);
+	decode_string(pram_string);
+	param = pram_string;
+    } else {
+	/* encode or escape URL parameter - TH */
+	escaped = escapeParameter(param);
+	param = escaped;
+    }
+
+    if (isMAILTO_URL(param)) {
+	format(&cmdbuf, command, param + 7);
+    } else if (strnicmp("telnet://", param, 9) == 0) {
+	char host[sizeof(pram_string)];
+	int last_pos;
+
+	LYstrncpy(host, param + 9, sizeof(host));
+	last_pos = strlen(host) - 1;
+	if (last_pos > 1 && host[last_pos] == '/')
+	    host[last_pos] = '\0';
+
+	format(&cmdbuf, command, host);
+    } else if (strnicmp("file://localhost/", param, 17) == 0) {
+	char e_buff[LY_MAXPATH], *p;
+
+	p = param + 17;
+	delete_danger_characters(p);
+	*e_buff = 0;
+	if (strchr(p, ':') == NULL) {
+	    sprintf(e_buff, "%.3s/", windows_drive);
+	}
+	strncat(e_buff, p, sizeof(e_buff) - strlen(e_buff) - 1);
+	p = strrchr(e_buff, '.');
+	if (p) {
+	    trimPoundSelector(p);
+	}
+
+	/* Less ==> short filename with backslashes,
+	 * less ==> long filename with forward slashes, may be quoted
+	 */
+	if (ISUPPER(command[0])) {
+	    char *short_name = HTDOS_short_name(e_buff);
+
+	    p = quote_pathname(short_name);
+	    format(&cmdbuf, command, p);
+	    FREE(p);
+	} else {
+	    p = quote_pathname(e_buff);
+	    format(&cmdbuf, command, p);
+	    FREE(p);
+	}
+    } else {
+	format(&cmdbuf, command, param);
+    }
+    FREE(escaped);
+#else
+    format(&cmdbuf, command, param);
+#endif
+    return cmdbuf;
+}
+
+/*
+ * Find the EXTERNAL command which matches the given name 'param'.  If there is
+ * more than one possibility, make a popup menu of the matching commands and
+ * allow the user to select one.  Return the selected command.
+ */
+static char *lookup_external(char *param,
+			     BOOL only_overriders)
+{
+    int pass, num_disabled, num_matched, num_choices, cur_choice;
+    int length = 0;
+    char *cmdbuf = NULL;
+    char **choices = 0;
+    lynx_list_item_type *ptr = 0;
+
+    for (pass = 0; pass < 2; pass++) {
+	num_disabled = 0;
+	num_matched = 0;
+	num_choices = 0;
+	for (ptr = externals; ptr != 0; ptr = ptr->next) {
+
+	    if (match_item_by_name(ptr, param, only_overriders)) {
+		++num_matched;
+		CTRACE((tfp, "EXTERNAL: '%s' <==> '%s'\n", ptr->name, param));
+		if (no_externals && !ptr->always_enabled && !only_overriders) {
+		    ++num_disabled;
+		} else {
+		    if (pass == 0) {
+			length++;
+		    } else if (pass != 0) {
+			cmdbuf = format_command(ptr->command, param);
+			if (length > 1)
+			    choices[num_choices] = cmdbuf;
+		    }
+		    num_choices++;
+		}
+	    }
+	}
+	if (length > 1) {
+	    if (pass == 0) {
+		choices = typecallocn(char *, (unsigned) length + 1);
+	    } else {
+		choices[num_choices] = 0;
+	    }
+	}
+    }
+
+    if (num_disabled != 0
+	&& num_disabled == num_matched) {
+	HTUserMsg(EXTERNALS_DISABLED);
+    } else if (num_choices > 1) {
+	int old_y, old_x;
+
+	LYGetYX(old_y, old_x);
+	cur_choice = LYhandlePopupList(-1,
+				       0,
+				       old_x,
+				       (const char **) choices,
+				       -1,
+				       -1,
+				       FALSE,
+				       TRUE);
+	wmove(LYwin, old_y, old_x);
+	CTRACE((tfp, "selected choice %d of %d\n", cur_choice, num_choices));
+	if (cur_choice < 0) {
+	    HTInfoMsg(CANCELLED);
+	    cmdbuf = 0;
+	}
+	for (pass = 0; choices[pass] != 0; pass++) {
+	    if (pass == cur_choice) {
+		cmdbuf = choices[pass];
+	    } else {
+		FREE(choices[pass]);
+	    }
+	}
+	FREE(choices);
+    }
+    return cmdbuf;
+}
+
+BOOL run_external(char *param,
+		  BOOL only_overriders)
+{
+#ifdef WIN_EX
+    int status;
+#endif
+    int redraw_flag = TRUE;
+    char *cmdbuf = NULL;
+    BOOL found = FALSE;
+    int confirmed = TRUE;
+
+    if (externals == NULL)
+	return 0;
+
+#ifdef WIN_EX			/* 1998/01/26 (Mon) 09:16:13 */
+    if (param == NULL) {
+	HTInfoMsg(gettext("External command is null"));
+	return 0;
+    }
+#endif
+
+    cmdbuf = lookup_external(param, only_overriders);
+    if (non_empty(cmdbuf)) {
+#ifdef WIN_EX			/* 1997/10/17 (Fri) 14:07:50 */
+	int len;
+	char buff[LY_MAXPATH];
+
+	CTRACE((tfp, "Lynx EXTERNAL: '%s'\n", cmdbuf));
+#ifdef WIN_GUI			/* 1997/11/06 (Thu) 14:17:15 */
+	confirmed = MessageBox(GetForegroundWindow(), cmdbuf,
+			       "Lynx (EXTERNAL COMMAND EXEC)",
+			       MB_ICONQUESTION | MB_SETFOREGROUND | MB_OKCANCEL)
+	    != IDCANCEL;
+#else
+	confirmed = HTConfirm(LYElideString(cmdbuf, 40)) != NO;
+#endif
+	if (confirmed) {
+	    len = strlen(cmdbuf);
+	    if (len > 255) {
+		sprintf(buff, "Lynx: command line too long (%d > 255)", len);
+#ifdef WIN_GUI			/* 1997/11/06 (Thu) 14:17:02 */
+		MessageBox(GetForegroundWindow(), buff,
+			   "Lynx (EXTERNAL COMMAND EXEC)",
+			   MB_ICONEXCLAMATION | MB_SETFOREGROUND | MB_OK);
+		SetConsoleTitle("Lynx for Win32");
+#else
+		HTConfirm(LYElideString(buff, 40));
+#endif
+		confirmed = FALSE;
+	    } else {
+		SetConsoleTitle(cmdbuf);
+	    }
+	}
+
+	if (strnicmp(cmdbuf, "start ", 6) == 0)
+	    redraw_flag = FALSE;
+	else
+	    redraw_flag = TRUE;
+#else
+	HTUserMsg(cmdbuf);
+#endif
+	found = TRUE;
+	if (confirmed) {
+	    if (redraw_flag) {
+		stop_curses();
+		fflush(stdout);
+	    }
+
+	    /* command running. */
+#ifdef WIN_EX			/* 1997/10/17 (Fri) 14:07:50 */
+#if defined(__CYGWIN__) || defined(__MINGW32__)
+	    status = system(cmdbuf);
+#else
+	    status = xsystem(cmdbuf);
+#endif
+	    if (status != 0) {
+		sprintf(buff,
+			"EXEC code = %04x (%2d, %2d)\r\n"
+			"'%s'",
+			status, (status / 256), (status & 0xff),
+			cmdbuf);
+#ifdef SH_EX			/* WIN_GUI for ERROR only */
+		MessageBox(GetForegroundWindow(), buff,
+			   "Lynx (EXTERNAL COMMAND EXEC)",
+			   MB_ICONSTOP | MB_SETFOREGROUND | MB_OK);
+#else
+		HTConfirm(LYElideString(buff, 40));
+#endif /* 1 */
+	    }
+#else /* Not WIN_EX */
+	    LYSystem(cmdbuf);
+#endif /* WIN_EX */
+
+#if defined(WIN_EX)
+	    SetConsoleTitle("Lynx for Win32");
+#endif
+	    if (redraw_flag) {
+		fflush(stdout);
+		start_curses();
+	    }
+	}
+    }
+
+    FREE(cmdbuf);
+    return found;
+}
+#endif /* USE_EXTERNALS */
diff --git a/src/LYExtern.h b/src/LYExtern.h
new file mode 100644
index 00000000..e88bbd6d
--- /dev/null
+++ b/src/LYExtern.h
@@ -0,0 +1,16 @@
+/* $LynxId: LYExtern.h,v 1.13 2008/12/29 18:59:39 tom Exp $ */
+#ifndef EXTERNALS_H
+#define EXTERNALS_H
+
+#ifndef LYSTRUCTS_H
+#include <LYStructs.h>
+#endif /* LYSTRUCTS_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    extern BOOL run_external(char *c, BOOL only_overriders);
+#ifdef __cplusplus
+}
+#endif
+#endif				/* EXTERNALS_H */
diff --git a/src/LYForms.c b/src/LYForms.c
new file mode 100644
index 00000000..76d5a582
--- /dev/null
+++ b/src/LYForms.c
@@ -0,0 +1,1039 @@
+/* $LynxId: LYForms.c,v 1.84 2010/04/30 00:00:55 tom Exp $ */
+#include <HTUtils.h>
+#include <HTCJK.h>
+#include <HTTP.h>
+#include <HTAlert.h>
+#include <LYCurses.h>
+#include <GridText.h>
+#include <LYCharSets.h>
+#include <UCAux.h>
+#include <LYGlobalDefs.h>
+#include <LYUtils.h>
+#include <LYStrings.h>
+#include <LYKeymap.h>
+#include <LYClean.h>
+
+#include <LYLeaks.h>
+
+#ifdef USE_COLOR_STYLE
+#include <AttrList.h>
+#include <LYHash.h>
+#endif
+
+#if defined(VMS) && !defined(USE_SLANG)
+#define CTRL_W_HACK DO_NOTHING
+#else
+#define CTRL_W_HACK 23		/* CTRL-W refresh without clearok */
+#endif /* VMS && !USE_SLANG */
+
+static int form_getstr(int cur,
+		       BOOLEAN use_last_tfpos,
+		       BOOLEAN redraw_only);
+
+/*
+ * Returns an array of pointers to the given list
+ */
+static char **options_list(OptionType * opt_ptr)
+{
+    char **result = 0;
+    size_t len;
+    int pass;
+    OptionType *tmp_ptr;
+
+    for (pass = 0; pass < 2; pass++) {
+	for (tmp_ptr = opt_ptr, len = 0; tmp_ptr != 0; tmp_ptr = tmp_ptr->next) {
+	    if (pass != 0)
+		result[len] = tmp_ptr->name;
+	    len++;
+	}
+	if (pass == 0) {
+	    len++;
+	    result = typecallocn(char *, len);
+	} else {
+	    result[len] = 0;
+	}
+    }
+
+    return result;
+}
+
+int change_form_link_ex(int cur,
+			DocInfo *newdoc,
+			BOOLEAN *refresh_screen,
+			BOOLEAN use_last_tfpos,
+			BOOLEAN immediate_submit,
+			BOOLEAN redraw_only)
+{
+    FormInfo *form = links[cur].l_form;
+    char *link_name = form->name;
+    char *link_value = form->value;
+    int newdoc_changed = 0;
+    int c = DO_NOTHING;
+    int title_adjust = (no_title ? -TITLE_LINES : 0);
+    char **my_data = 0;
+
+    /*
+     * If there is no form to perform action on, don't do anything.
+     */
+    if (form == NULL) {
+	return (c);
+    }
+    my_data = options_list(form->select_list);
+
+    /*
+     * Move to the link position.
+     */
+    LYmove(links[cur].ly + title_adjust, links[cur].lx);
+
+    switch (form->type) {
+    case F_CHECKBOX_TYPE:
+	if (form->disabled == YES)
+	    break;
+	LYSetHilite(cur, form->num_value ? unchecked_box : checked_box);
+	form->num_value = !form->num_value;
+	break;
+
+    case F_OPTION_LIST_TYPE:
+	if (form->select_list == 0) {
+	    HTAlert(BAD_HTML_NO_POPUP);
+	    c = DO_NOTHING;
+	    break;
+	}
+
+	if (form->disabled == YES) {
+	    (void) LYhandlePopupList(form->num_value,
+				     links[cur].ly,
+				     links[cur].lx,
+				     (const char **) my_data,
+				     form->size,
+				     form->size_l,
+				     form->disabled,
+				     FALSE);
+#if CTRL_W_HACK != DO_NOTHING
+	    if (!enable_scrollback)
+		c = CTRL_W_HACK;	/* CTRL-W refresh without clearok */
+	    else
+#endif
+		c = 12;		/* CTRL-L for repaint */
+	    break;
+	}
+	form->num_value = LYhandlePopupList(form->num_value,
+					    links[cur].ly,
+					    links[cur].lx,
+					    (const char **) my_data,
+					    form->size,
+					    form->size_l,
+					    form->disabled,
+					    FALSE);
+	{
+	    OptionType *opt_ptr = form->select_list;
+	    int i;
+
+	    for (i = 0; i < form->num_value; i++, opt_ptr = opt_ptr->next) ;	/* null body */
+	    /*
+	     * Set the name.
+	     */
+	    form->value = opt_ptr->name;
+	    /*
+	     * Set the value.
+	     */
+	    form->cp_submit_value = opt_ptr->cp_submit_value;
+	    /*
+	     * Set charset in which we have the submit value.  - kw
+	     */
+	    form->value_cs = opt_ptr->value_cs;
+	}
+#if CTRL_W_HACK != DO_NOTHING
+	if (!enable_scrollback)
+	    c = CTRL_W_HACK;	/* CTRL-W refresh without clearok */
+	else
+#endif
+	    c = 12;		/* CTRL-L for repaint */
+	break;
+
+    case F_RADIO_TYPE:
+	if (form->disabled == YES)
+	    break;
+	/*
+	 * Radio buttons must have one and only one down at a time!
+	 */
+	if (form->num_value) {
+	    if (user_mode == NOVICE_MODE) {
+		HTUserMsg(NEED_CHECKED_RADIO_BUTTON);
+	    }
+	} else {
+	    int i;
+
+	    /*
+	     * Run though list of the links on the screen and unselect any that
+	     * are selected.  :)
+	     */
+	    lynx_start_radio_color();
+	    for (i = 0; i < nlinks; i++) {
+		if (links[i].type == WWW_FORM_LINK_TYPE
+		    && links[i].l_form->type == F_RADIO_TYPE
+		    && links[i].l_form->number == form->number
+		/*
+		 * If it has the same name and its on...
+		 */
+		    && !strcmp(links[i].l_form->name, form->name)
+		    && links[i].l_form->num_value) {
+		    LYmove(links[i].ly, links[i].lx);
+		    LYaddstr(unchecked_radio);
+		    LYSetHilite(i, unchecked_radio);
+		}
+	    }
+	    lynx_stop_radio_color();
+	    /*
+	     * Will unselect other button and select this one.
+	     */
+	    HText_activateRadioButton(form);
+	    /*
+	     * Now highlight this one.
+	     */
+	    LYSetHilite(cur, checked_radio);
+	}
+	break;
+
+    case F_FILE_TYPE:
+    case F_TEXT_TYPE:
+    case F_TEXTAREA_TYPE:
+    case F_PASSWORD_TYPE:
+	c = form_getstr(cur, use_last_tfpos, redraw_only);
+	LYSetHilite(cur, ((form->type == F_PASSWORD_TYPE)
+			  ? STARS(LYstrCells(form->value))
+			  : form->value));
+	break;
+
+    case F_RESET_TYPE:
+	if (form->disabled == YES)
+	    break;
+	HText_ResetForm(form);
+	*refresh_screen = TRUE;
+	break;
+
+    case F_TEXT_SUBMIT_TYPE:
+	if (redraw_only) {
+	    c = form_getstr(cur, use_last_tfpos, TRUE);
+	    break;
+	}
+	if (!immediate_submit)
+	    c = form_getstr(cur, use_last_tfpos, FALSE);
+	if (form->disabled == YES &&
+	    (c == '\r' || c == '\n' || immediate_submit)) {
+	    if (peek_mouse_link() >= 0)
+		c = LAC_TO_LKC0(LYK_ACTIVATE);
+	    else
+		c = '\t';
+	    break;
+	}
+	/*
+	 * If immediate_submit is set, we didn't enter the line editor above,
+	 * and will now try to call HText_SubmitForm() directly.  If
+	 * immediate_submit is not set, c is the lynxkeycode returned from line
+	 * editing.  Then if c indicates that a key was pressed that means we
+	 * should submit, but with some extra considerations (i.e.  NOCACHE,
+	 * DOWNLOAD, different from simple Enter), or if we should act on some
+	 * *other* link selected with the mouse, we'll just return c and leave
+	 * it to mainloop() to do the right thing; if everything checks out, it
+	 * should call this function again, with immediate_submit set.
+	 *
+	 * If c indicates that line editing ended with Enter, we still defer to
+	 * mainloop() for further checking if the submit action URL could
+	 * require more checks than we do here.  Only in the remaining cases do
+	 * we proceed to call HText_SubmitForm() directly before returning.  -
+	 * kw
+	 */
+	if (immediate_submit ||
+	    ((c == '\r' || c == '\n' || c == LAC_TO_LKC0(LYK_SUBMIT)) &&
+	     peek_mouse_link() == -1)) {
+	    LYSetHilite(cur, form->value);
+#ifdef TEXT_SUBMIT_CONFIRM_WANTED
+	    if (!immediate_submit && (c == '\r' || c == '\n') &&
+		!HTConfirmDefault(NO_SUBMIT_BUTTON_QUERY), YES) {
+		/* User was prompted and declined; if canceled with ^G
+		 * let mainloop stay on this field, otherwise move on to
+		 * the next field or link. - kw
+		 */
+		if (HTLastConfirmCancelled())
+		    c = DO_NOTHING;
+		else
+		    c = LAC_TO_LKC(LYK_NEXT_LINK);
+		break;
+	    }
+#endif
+	    if (isEmpty(form->submit_action)) {
+		HTUserMsg(NO_FORM_ACTION);
+		c = DO_NOTHING;
+		break;
+	    } else if (form->submit_method == URL_MAIL_METHOD && no_mail) {
+		HTAlert(FORM_MAILTO_DISALLOWED);
+		c = DO_NOTHING;
+		break;
+	    } else if (!immediate_submit &&
+		       ((no_file_url &&
+			 isFILE_URL(form->submit_action)) ||
+			!strncasecomp(form->submit_action, "lynx", 4))) {
+		c = LAC_TO_LKC0(LYK_SUBMIT);
+		break;
+	    } else {
+		if (form->no_cache &&
+		    form->submit_method != URL_MAIL_METHOD) {
+		    LYforce_no_cache = TRUE;
+		    reloading = TRUE;
+		}
+		newdoc_changed =
+		    HText_SubmitForm(form, newdoc, link_name, form->value);
+	    }
+	    if (form->submit_method == URL_MAIL_METHOD) {
+		*refresh_screen = TRUE;
+	    } else {
+		/*
+		 * Returns new document URL.
+		 */
+		newdoc->link = 0;
+		newdoc->internal_link = FALSE;
+	    }
+	    c = DO_NOTHING;
+	    break;
+	} else {
+	    LYSetHilite(cur, form->value);
+	}
+	break;
+
+    case F_SUBMIT_TYPE:
+    case F_IMAGE_SUBMIT_TYPE:
+	if (form->disabled == YES)
+	    break;
+	if (form->no_cache &&
+	    form->submit_method != URL_MAIL_METHOD) {
+	    LYforce_no_cache = TRUE;
+	    reloading = TRUE;
+	}
+	newdoc_changed =
+	    HText_SubmitForm(form, newdoc, link_name, link_value);
+	if (form->submit_method == URL_MAIL_METHOD)
+	    *refresh_screen = TRUE;
+	else {
+	    /* returns new document URL */
+	    newdoc->link = 0;
+	    newdoc->internal_link = FALSE;
+	}
+	break;
+
+    }
+
+    if (newdoc_changed) {
+	c = LKC_DONE;
+    } else {
+	/*
+	 * These flags may have been set in mainloop, anticipating that a
+	 * request will be submitted.  But if we haven't filled in newdoc, that
+	 * won't actually be the case, so unset them.  - kw
+	 */
+	LYforce_no_cache = FALSE;
+	reloading = FALSE;
+    }
+    FREE(my_data);
+    return (c);
+}
+
+int change_form_link(int cur,
+		     DocInfo *newdoc,
+		     BOOLEAN *refresh_screen,
+		     BOOLEAN use_last_tfpos,
+		     BOOLEAN immediate_submit)
+{
+    /*pass all our args and FALSE as last arg */
+    return change_form_link_ex(cur,
+			       newdoc,
+			       refresh_screen,
+			       use_last_tfpos,
+			       immediate_submit,
+			       FALSE /*redraw_only */ );
+}
+
+static int LastTFPos = -1;	/* remember last text field position */
+
+static void LYSetLastTFPos(int pos)
+{
+    LastTFPos = pos;
+}
+
+static int form_getstr(int cur,
+		       BOOLEAN use_last_tfpos,
+		       BOOLEAN redraw_only)
+{
+    FormInfo *form = links[cur].l_form;
+    char *value = form->value;
+    int ch;
+    int far_col;
+    unsigned max_length;
+    int startcol, startline;
+    BOOL HaveMaxlength = FALSE;
+    int action, repeat;
+    int last_xlkc = -1;
+
+#ifdef SUPPORT_MULTIBYTE_EDIT
+    BOOL refresh_mb = TRUE;
+#endif
+
+    EditFieldData MyEdit;
+    BOOLEAN Edited = FALSE;	/* Value might be updated? */
+
+    /*
+     * Get the initial position of the cursor.
+     */
+    LYGetYX(startline, startcol);
+    if ((startcol + form->size) > LYcolLimit)
+	far_col = LYcolLimit;
+    else
+	far_col = (startcol + form->size);
+
+    /*
+     * Make sure the form field value does not exceed our buffer.  - FM
+     */
+    max_length = ((form->maxlength > 0 &&
+		   form->maxlength < sizeof(MyEdit.buffer))
+		  ? form->maxlength
+		  : (sizeof(MyEdit.buffer) - 1));
+    if (strlen(form->value) > max_length) {
+	/*
+	 * We can't fit the entire value into the editing buffer, so enter as
+	 * much of the tail as fits.  - FM
+	 */
+	value += (strlen(form->value) - max_length);
+	if (!form->disabled &&
+	    !(form->submit_method == URL_MAIL_METHOD && no_mail)) {
+	    /*
+	     * If we can edit it, report that we are using the tail.  - FM
+	     */
+	    HTUserMsg(FORM_VALUE_TOO_LONG);
+	    show_formlink_statusline(form, redraw_only ? FOR_PANEL : FOR_INPUT);
+	    LYmove(startline, startcol);
+	}
+    }
+
+    /*
+     * Print panned line
+     */
+    LYSetupEdit(&MyEdit, value, (int) max_length, (far_col - startcol));
+    MyEdit.pad = '_';
+    MyEdit.hidden = (BOOL) (form->type == F_PASSWORD_TYPE);
+    if (use_last_tfpos && LastTFPos >= 0 && LastTFPos < MyEdit.strlen) {
+#if defined(TEXTFIELDS_MAY_NEED_ACTIVATION) && defined(INACTIVE_INPUT_STYLE_VH)
+	if (redraw_only) {
+	    if (!(MyEdit.strlen >= MyEdit.dspwdth &&
+		  LastTFPos >= MyEdit.dspwdth - MyEdit.margin)) {
+		MyEdit.pos = LastTFPos;
+		if (MyEdit.strlen >= MyEdit.dspwdth)
+		    textinput_redrawn = FALSE;
+	    }
+	} else
+#endif /* TEXTFIELDS_MAY_NEED_ACTIVATION && INACTIVE_INPUT_STYLE_VH */
+	    MyEdit.pos = LastTFPos;
+#ifdef ENHANCED_LINEEDIT
+	if (MyEdit.pos == 0)
+	    MyEdit.mark = -1 - MyEdit.strlen;	/* Do not show the region. */
+#endif
+    }
+    /* Try to prepare for setting position based on the last mouse event */
+#if defined(TEXTFIELDS_MAY_NEED_ACTIVATION) && defined(INACTIVE_INPUT_STYLE_VH)
+    if (!redraw_only) {
+	if (peek_mouse_levent()) {
+	    if (!use_last_tfpos && !textinput_redrawn) {
+		MyEdit.pos = 0;
+	    }
+	}
+	textinput_redrawn = FALSE;
+    }
+#else
+    if (peek_mouse_levent()) {
+	if (!use_last_tfpos)
+	    MyEdit.pos = 0;
+    }
+#endif /* TEXTFIELDS_MAY_NEED_ACTIVATION && INACTIVE_INPUT_STYLE_VH */
+    LYRefreshEdit(&MyEdit);
+    if (redraw_only) {
+	return 0;		/*return value won't be analysed */
+    }
+
+    /*
+     * And go for it!
+     */
+    for (;;) {
+      again:
+	repeat = -1;
+	get_mouse_link();	/* Reset mouse_link. */
+
+	ch = LYgetch_input();
+#ifdef SUPPORT_MULTIBYTE_EDIT
+	if (!refresh_mb
+	    && (EditBinding(ch) != LYE_CHAR)
+#ifndef WIN_EX
+	    && (EditBinding(ch) != LYE_AIX)
+#endif
+	    )
+	    goto again;
+#endif /* SUPPORT_MULTIBYTE_EDIT */
+#ifdef VMS
+	if (HadVMSInterrupt) {
+	    HadVMSInterrupt = FALSE;
+	    ch = LYCharINTERRUPT2;
+	}
+#endif /* VMS */
+
+	action = 0;
+#ifdef USE_MOUSE
+#  if defined(NCURSES) || defined(PDCURSES)
+	if (ch != -1 && (ch & LKC_ISLAC) && !(ch & LKC_ISLECLAC))	/* already lynxactioncode? */
+	    break;		/* @@@ maybe move these 2 lines outside ifdef -kw */
+	if (ch == MOUSE_KEY) {	/* Need to process ourselves */
+#if defined(PDCURSES)
+	    int curx, cury;
+
+	    request_mouse_pos();
+	    LYGetYX(cury, curx);
+	    if (MOUSE_Y_POS == cury) {
+		repeat = MOUSE_X_POS - curx;
+		if (repeat < 0) {
+		    action = LYE_BACK;
+		    repeat = -repeat;
+		} else
+		    action = LYE_FORW;
+	    }
+#else
+	    MEVENT event;
+	    int curx, cury;
+
+	    getmouse(&event);
+	    LYGetYX(cury, curx);
+	    if (event.y == cury) {
+		repeat = event.x - curx;
+		if (repeat < 0) {
+		    action = LYE_BACK;
+		    repeat = -repeat;
+		} else
+		    action = LYE_FORW;
+	    }
+#endif /* PDCURSES */
+	    else {
+		/* Mouse event passed to us as MOUSE_KEY, and apparently not on
+		 * this field's line?  Something is not as it should be...
+		 *
+		 * A call to statusline() may have happened, possibly from
+		 * within a mouse menu.  Let's at least make sure here that the
+		 * cursor position gets restored.  - kw
+		 */
+		MyEdit.dirty = TRUE;
+	    }
+	} else
+#  endif			/* NCURSES || PDCURSES */
+#endif /* USE_MOUSE */
+
+	{
+	    if (!(ch & LKC_ISLECLAC))
+		ch |= MyEdit.current_modifiers;
+	    MyEdit.current_modifiers = 0;
+	    if (last_xlkc != -1) {
+		if (ch == last_xlkc)
+		    ch |= LKC_MOD3;
+	    }
+	}
+	if (peek_mouse_link() != -1)
+	    break;
+
+	if (!action)
+	    action = EditBinding(ch);
+	if ((action & LYE_DF) && !(action & LYE_FORM_LAC)) {
+	    last_xlkc = ch;
+	    action &= ~LYE_DF;
+	} else {
+	    last_xlkc = -1;
+	}
+
+	if (action == LYE_SETM1) {
+	    /*
+	     * Set flag for modifier 1.
+	     */
+	    MyEdit.current_modifiers |= LKC_MOD1;
+	    continue;
+	}
+	if (action == LYE_SETM2) {
+	    /*
+	     * Set flag for modifier 2.
+	     */
+	    MyEdit.current_modifiers |= LKC_MOD2;
+	    continue;
+	}
+	/*
+	 * Filter out global navigation keys that should not be passed to line
+	 * editor, and LYK_REFRESH.
+	 */
+	if (action == LYE_ENTER)
+	    break;
+	if (action == LYE_FORM_PASS)
+	    break;
+	if (action & LYE_FORM_LAC) {
+	    ch = (action & LAC_MASK) | LKC_ISLAC;
+	    break;
+	}
+	if (action == LYE_LKCMD) {
+	    _statusline(ENTER_LYNX_COMMAND);
+	    ch = LYgetch();
+#ifdef VMS
+	    if (HadVMSInterrupt) {
+		HadVMSInterrupt = FALSE;
+		ch = LYCharINTERRUPT2;
+	    }
+#endif /* VMS */
+	    break;
+	}
+#ifdef CAN_CUT_AND_PASTE	/* 1998/10/01 (Thu) 19:19:22 */
+	if (action == LYE_PASTE) {
+	    unsigned char *s = (unsigned char *) get_clip_grab(), *e;
+	    char *buf = NULL;
+	    int len;
+
+	    if (!s)
+		break;
+	    len = (int) strlen((const char *) s);
+	    e = s + len;
+
+	    if (len > 0) {
+		unsigned char *e1 = s;
+
+		while (e1 < e) {
+		    if (*e1 < ' ') {	/* Stop here? */
+			if (e1 > s)
+			    LYEditInsert(&MyEdit, s, e1 - s, -1, TRUE);
+			s = e1;
+			if (*e1 == '\t') {	/* Replace by space */
+			    LYEditInsert(&MyEdit, (unsigned char *) " ", 1,
+					 -1, TRUE);
+			    s = ++e1;
+			} else
+			    break;
+		    } else
+			++e1;
+		}
+		if (e1 > s)
+		    LYEditInsert(&MyEdit, s, e1 - s, -1, TRUE);
+		while (e1 < e && *e1 == '\r')
+		    e1++;
+		if (e1 + 1 < e && *e1 == '\n')
+		    StrAllocCopy(buf, (char *) e1 + 1);		/* Survive _release() */
+		get_clip_release();
+		if (MyEdit.strlen >= (int) max_length) {
+		    HaveMaxlength = TRUE;
+		} else if (HaveMaxlength &&
+			   MyEdit.strlen < (int) max_length) {
+		    HaveMaxlength = FALSE;
+		    _statusline(ENTER_TEXT_ARROWS_OR_TAB);
+		}
+		if (strcmp(value, MyEdit.buffer) != 0) {
+		    Edited = TRUE;
+		}
+		if (buf) {
+		    put_clip(buf);
+		    FREE(buf);
+		    ch = '\n';	/* Sometimes moves to the next line */
+		    break;
+		}
+		LYRefreshEdit(&MyEdit);
+	    } else {
+		HTInfoMsg(gettext("Clipboard empty or Not text data."));
+		continue;
+	    }
+	}
+#endif
+#ifndef WIN_EX
+	if (action == LYE_AIX &&
+	    (!IS_CJK_TTY && LYlowest_eightbit[current_char_set] > 0x97))
+	    break;
+#endif
+	if (action == LYE_TAB) {
+	    ch = (int) ('\t');
+	    break;
+	}
+	if (action == LYE_ABORT) {
+	    return (DO_NOTHING);
+	}
+	if (action == LYE_STOP) {
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+	    textfields_need_activation = TRUE;
+	    break;
+#else
+#ifdef ENHANCED_LINEEDIT
+	    if (MyEdit.mark >= 0)
+		MyEdit.mark = -1 - MyEdit.strlen;	/* Disable. */
+#endif
+#endif
+	}
+	if (action == LYE_NOP && LKC_TO_LAC(keymap, ch) == LYK_REFRESH)
+	    break;
+#ifdef SH_EX
+/* ASATAKU emacskey hack 1997/08/26 (Tue) 09:19:23 */
+	if (emacs_keys &&
+	    (EditBinding(ch) == LYE_FORWW || EditBinding(ch) == LYE_BACKW))
+	    goto breakfor;
+/* ASATAKU emacskey hack */
+#endif
+	switch (ch) {
+	default:
+	    /* [ 1999/04/14 (Wed) 15:01:33 ]
+	     * Left arrow in column 0 deserves special treatment here, else
+	     * you can get trapped in a form without submit button!
+	     */
+	    if (action == LYE_BACK && MyEdit.pos == 0 && repeat == -1) {
+		int c = YES;	/* Go back immediately if no changes */
+
+		if (textfield_prompt_at_left_edge) {
+		    c = HTConfirmDefault(PREV_DOC_QUERY, NO);
+		} else if (strcmp(MyEdit.buffer, value)) {
+		    c = HTConfirmDefault(PREV_DOC_QUERY, NO);
+		}
+		if (c == YES) {
+		    return (ch);
+		} else {
+		    if (form->disabled == YES)
+			_statusline(ARROWS_OR_TAB_TO_MOVE);
+		    else
+			_statusline(ENTER_TEXT_ARROWS_OR_TAB);
+		}
+	    }
+	    if (form->disabled == YES) {
+		/*
+		 * Allow actions that don't modify the contents even in
+		 * disabled form fields, so the user can scroll through the
+		 * line for reading if necessary.  - kw
+		 */
+		switch (action) {
+		case LYE_BOL:
+		case LYE_EOL:
+		case LYE_FORW:
+		case LYE_FORW_RL:
+		case LYE_BACK:
+		case LYE_BACK_LL:
+		case LYE_FORWW:
+		case LYE_BACKW:
+#ifdef EXP_KEYBOARD_LAYOUT
+		case LYE_SWMAP:
+#endif
+#ifdef ENHANCED_LINEEDIT
+		case LYE_SETMARK:
+		case LYE_XPMARK:
+#endif
+		    break;
+		default:
+		    goto again;
+		}
+	    }
+	    /*
+	     * Make sure the statusline uses editmode help.
+	     */
+	    if (repeat < 0)
+		repeat = 1;
+	    while (repeat--) {
+		int rc = LYEdit1(&MyEdit, ch, action & ~LYE_DF, TRUE);
+
+		if (rc < 0) {
+		    ch = -rc;
+		    /* FORW_RL and BACK_LL may require special attention.
+		       BACK_LL wanted to switch to the previous link on
+		       the same line.  However, if there is no such link,
+		       then we would either disactivate the form
+		       (with -tna), or will reenter the form, thus we jump
+		       to the end of the line; both are counterintuitive.
+		       Unfortunately, we do not have access to curdoc.link,
+		       so we deduce it ourselves.  We don't have the info
+		       to do it inside LYLineEdit().
+		       This should work for prompts too.  */
+		    if ((action != LYE_BACK_LL && action != LYE_FORW_RL)
+			|| (cur >= 0
+			    && cur < nlinks
+			    && (action == LYE_FORW_RL
+				? cur < nlinks - 1
+				: cur > 0)
+			    && links[cur + ((action == LYE_FORW_RL)
+					    ? 1
+					    : -1)].ly
+			    == links[cur].ly))
+			goto breakfor;
+		}
+#ifdef SUPPORT_MULTIBYTE_EDIT
+		if (rc == 0) {
+		    if (IS_CJK_TTY && (0x80 <= ch)
+			&& (ch <= 0xfe) && refresh_mb)
+			refresh_mb = FALSE;
+		    else
+			refresh_mb = TRUE;
+		} else {
+		    if (!refresh_mb) {
+			LYEdit1(&MyEdit, 0, LYE_DELP, TRUE);
+		    }
+		}
+#endif /* SUPPORT_MULTIBYTE_EDIT */
+	    }
+	    if (MyEdit.strlen >= (int) max_length) {
+		HaveMaxlength = TRUE;
+	    } else if (HaveMaxlength &&
+		       MyEdit.strlen < (int) max_length) {
+		HaveMaxlength = FALSE;
+		_statusline(ENTER_TEXT_ARROWS_OR_TAB);
+	    }
+	    if (strcmp(value, MyEdit.buffer)) {
+		Edited = TRUE;
+	    }
+#ifdef SUPPORT_MULTIBYTE_EDIT
+	    if (refresh_mb)
+#endif
+		LYRefreshEdit(&MyEdit);
+	    LYSetLastTFPos(MyEdit.pos);
+	}
+    }
+  breakfor:
+    if (Edited) {
+
+	/*
+	 * Load the new value.
+	 */
+	if (value == form->value) {
+	    /*
+	     * The previous value did fit in the line buffer, so replace it
+	     * with the new value.  - FM
+	     */
+	    StrAllocCopy(form->value, MyEdit.buffer);
+	} else {
+	    int old_len = (int) strlen(form->value);
+	    int new_len = (int) strlen(value);
+
+	    /*
+	     * Combine the modified tail with the unmodified head.  - FM
+	     */
+	    form->value[(old_len > new_len) ? (old_len - new_len) : 0] = '\0';
+	    StrAllocCat(form->value, MyEdit.buffer);
+	    HTUserMsg(FORM_TAIL_COMBINED_WITH_HEAD);
+	}
+
+	/* 2.8.4pre.3 - most browsers appear to preserve trailing spaces -VH */
+	/*
+	 * Remove trailing spaces
+	 *
+	 * Do we really need to do that here?  Trailing spaces will only be
+	 * there if user keyed them in.  Rather rude to throw away their hard
+	 * earned spaces.  Better deal with trailing spaces when submitting the
+	 * form????
+	 */
+	if (LYtrimInputFields) {
+	    LYTrimTrailing(form->value);
+	}
+
+	/*
+	 * If the field has been changed, assume that it is now in current
+	 * display character set, even if for some reason it wasn't!  Hopefully
+	 * a user will only submit the form if the non-ASCII characters are
+	 * displayed correctly, which means (assuming that the display
+	 * character set has been set truthfully) the user confirms by changing
+	 * the field that the character encoding is right.  - kw
+	 */
+	if (non_empty(form->value))
+	    form->value_cs = current_char_set;
+    }
+    return (ch);
+}
+
+/*
+ * Display statusline info tailored for the current form field.
+ */
+void show_formlink_statusline(const FormInfo * form,
+			      int for_what)
+{
+    switch (form->type) {
+    case F_PASSWORD_TYPE:
+	if (form->disabled == YES)
+	    statusline(FORM_LINK_PASSWORD_UNM_MSG);
+	else
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+	if (for_what == FOR_PANEL)
+	    statusline(FORM_LINK_PASSWORD_MESSAGE_INA);
+	else
+#endif
+	    statusline(FORM_LINK_PASSWORD_MESSAGE);
+	break;
+    case F_OPTION_LIST_TYPE:
+	if (form->disabled == YES)
+	    statusline(FORM_LINK_OPTION_LIST_UNM_MSG);
+	else
+	    statusline(FORM_LINK_OPTION_LIST_MESSAGE);
+	break;
+    case F_CHECKBOX_TYPE:
+	if (form->disabled == YES)
+	    statusline(FORM_LINK_CHECKBOX_UNM_MSG);
+	else
+	    statusline(FORM_LINK_CHECKBOX_MESSAGE);
+	break;
+    case F_RADIO_TYPE:
+	if (form->disabled == YES)
+	    statusline(FORM_LINK_RADIO_UNM_MSG);
+	else
+	    statusline(FORM_LINK_RADIO_MESSAGE);
+	break;
+    case F_TEXT_SUBMIT_TYPE:
+	if (form->disabled == YES) {
+	    statusline(FORM_LINK_TEXT_SUBMIT_UNM_MSG);
+	} else if (form->submit_method ==
+		   URL_MAIL_METHOD) {
+	    if (no_mail)
+		statusline(FORM_LINK_TEXT_SUBMIT_MAILTO_DIS_MSG);
+	    else
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+	    if (for_what == FOR_PANEL)
+		statusline(FORM_TEXT_SUBMIT_MAILTO_MSG_INA);
+	    else
+#endif
+		statusline(FORM_LINK_TEXT_SUBMIT_MAILTO_MSG);
+	} else if (form->no_cache) {
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+	    if (for_what == FOR_PANEL)
+		statusline(FORM_TEXT_RESUBMIT_MESSAGE_INA);
+	    else
+#endif
+		statusline(FORM_LINK_TEXT_RESUBMIT_MESSAGE);
+	} else {
+	    char *submit_str = NULL;
+	    char *xkey_info = key_for_func_ext(LYK_NOCACHE, for_what);
+
+	    if (non_empty(xkey_info)) {
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+		if (for_what == FOR_PANEL)
+		    HTSprintf0(&submit_str, FORM_TEXT_SUBMIT_MESSAGE_INA_X,
+			       xkey_info);
+		else
+#endif
+		    HTSprintf0(&submit_str, FORM_LINK_TEXT_SUBMIT_MESSAGE_X,
+			       xkey_info);
+		statusline(submit_str);
+		FREE(submit_str);
+	    } else {
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+		if (for_what == FOR_PANEL)
+		    statusline(FORM_LINK_TEXT_SUBMIT_MESSAGE_INA);
+		else
+#endif
+		    statusline(FORM_LINK_TEXT_SUBMIT_MESSAGE);
+	    }
+	    FREE(xkey_info);
+	}
+	break;
+    case F_SUBMIT_TYPE:
+    case F_IMAGE_SUBMIT_TYPE:
+	if (form->disabled == YES) {
+	    statusline(FORM_LINK_SUBMIT_DIS_MSG);
+	} else if (form->submit_method ==
+		   URL_MAIL_METHOD) {
+	    if (no_mail) {
+		statusline(FORM_LINK_SUBMIT_MAILTO_DIS_MSG);
+	    } else {
+		if (user_mode == ADVANCED_MODE) {
+		    char *submit_str = NULL;
+
+		    StrAllocCopy(submit_str, FORM_LINK_SUBMIT_MAILTO_PREFIX);
+		    StrAllocCat(submit_str, form->submit_action);
+		    statusline(submit_str);
+		    FREE(submit_str);
+		} else {
+		    statusline(FORM_LINK_SUBMIT_MAILTO_MSG);
+		}
+	    }
+	} else if (form->no_cache) {
+	    if (user_mode == ADVANCED_MODE) {
+		char *submit_str = NULL;
+
+		StrAllocCopy(submit_str, FORM_LINK_RESUBMIT_PREFIX);
+		StrAllocCat(submit_str, form->submit_action);
+		statusline(submit_str);
+		FREE(submit_str);
+	    } else {
+		statusline(FORM_LINK_RESUBMIT_MESSAGE);
+	    }
+	} else {
+	    if (user_mode == ADVANCED_MODE) {
+		char *submit_str = NULL;
+
+		StrAllocCopy(submit_str, FORM_LINK_SUBMIT_PREFIX);
+		StrAllocCat(submit_str, form->submit_action);
+		statusline(submit_str);
+		FREE(submit_str);
+	    } else {
+		statusline(FORM_LINK_SUBMIT_MESSAGE);
+	    }
+	}
+	break;
+    case F_RESET_TYPE:
+	if (form->disabled == YES)
+	    statusline(FORM_LINK_RESET_DIS_MSG);
+	else
+	    statusline(FORM_LINK_RESET_MESSAGE);
+	break;
+    case F_BUTTON_TYPE:
+	if (form->disabled == YES)
+	    statusline(FORM_LINK_BUTTON_DIS_MSG);
+	else
+	    statusline(FORM_LINK_BUTTON_MESSAGE);
+	break;
+    case F_FILE_TYPE:
+	if (form->disabled == YES)
+	    statusline(FORM_LINK_FILE_UNM_MSG);
+	else
+	    statusline(FORM_LINK_FILE_MESSAGE);
+	break;
+    case F_TEXT_TYPE:
+	if (form->disabled == YES)
+	    statusline(FORM_LINK_TEXT_UNM_MSG);
+	else
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+	if (for_what == FOR_PANEL)
+	    statusline(FORM_LINK_TEXT_MESSAGE_INA);
+	else
+#endif
+	    statusline(FORM_LINK_TEXT_MESSAGE);
+	break;
+    case F_TEXTAREA_TYPE:
+	if (form->disabled == YES) {
+	    statusline(FORM_LINK_TEXT_UNM_MSG);
+	} else {
+	    char *submit_str = NULL;
+	    char *xkey_info = NULL;
+
+	    if (!no_editor && non_empty(editor)) {
+		xkey_info = key_for_func_ext(LYK_EDIT_TEXTAREA, for_what);
+#ifdef TEXTAREA_AUTOEXTEDIT
+		if (!xkey_info)
+		    xkey_info = key_for_func_ext(LYK_DWIMEDIT, for_what);
+#endif
+	    }
+	    if (non_empty(xkey_info)) {
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+		if (for_what == FOR_PANEL)
+		    HTSprintf0(&submit_str, FORM_LINK_TEXTAREA_MESSAGE_INA_E,
+			       xkey_info);
+		else
+#endif
+		    HTSprintf0(&submit_str, FORM_LINK_TEXTAREA_MESSAGE_E,
+			       xkey_info);
+		statusline(submit_str);
+		FREE(submit_str);
+	    } else {
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+		if (for_what == FOR_PANEL)
+		    statusline(FORM_LINK_TEXTAREA_MESSAGE_INA);
+		else
+#endif
+		    statusline(FORM_LINK_TEXTAREA_MESSAGE);
+	    }
+	    FREE(xkey_info);
+	}
+	break;
+    }
+}
diff --git a/src/LYGCurses.h b/src/LYGCurses.h
new file mode 100644
index 00000000..bdb1e5ef
--- /dev/null
+++ b/src/LYGCurses.h
@@ -0,0 +1,246 @@
+#ifndef __CURSES_LOADED
+#define __CURSES_LOADED	1
+
+#include <ssdef.h>
+#include <stdio.h>
+#include <smgdef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#define	reg	register
+#ifndef	TRUE
+#define	TRUE	(1)
+#define	FALSE	(0)
+#endif
+#define	ERR	(0)
+#define	OK	(1)
+#define	_SUBWIN		0001
+#define	_ENDLINE	0002
+#define	_FULLWIN	0004
+#define	_SCROLLWIN	0010
+#define	_FLUSH		0020
+#define	_STANDOUT	0200
+#define	_NOECHO		001
+#define	_NONL		002
+#define	_NOCRMODE	004
+#define	_NORAW		010
+#define	_BLINK		SMG$M_BLINK
+#define	_BOLD		SMG$M_BOLD
+#define	_REVERSE	SMG$M_REVERSE
+#define	_UNDERLINE	SMG$M_UNDERLINE
+    struct _win_st {
+	int _cur_y, _cur_x;
+	int _max_y, _max_x;
+	int _beg_y, _beg_x;
+	short _flags;
+	char _clear, _leave, _scroll, _wrap;
+	char **_y;
+	short *_firstch, *_lastch;
+	struct _win_st *_next, *_parent, *_child;
+	int _id;
+    };
+
+    struct _kb_st {
+	int _id;
+	unsigned char _flags;
+	struct {
+	    unsigned short length;
+	    unsigned char type;
+	    unsigned char pclass;
+	    char *address;
+	} _buffer_desc;
+	int _count;
+	char *_ptr;
+    };
+
+    struct _pb_st {
+	int _id;
+	int _rows, _cols;
+	union SMGDEF *_attr;
+	int _attr_size;
+    };
+
+#define	_KEYBOARD	struct _kb_st
+#define	WINDOW		struct _win_st
+#define	_PASTEBOARD	struct _pb_st
+
+    extern int LINES __asm("_$$PsectAttributes_NOSHR$$LINES");
+    extern int COLS __asm("_$$PsectAttributes_NOSHR$$COLS");
+    extern WINDOW *stdscr __asm("_$$PsectAttributes_NOSHR$$stdscr");
+    extern WINDOW *curscr __asm("_$$PsectAttributes_NOSHR$$curscr");
+    extern _KEYBOARD *stdkb __asm("_$$PsectAttributes_NOSHR$$stdkb");
+    extern _PASTEBOARD *stdpb __asm("_$$PsectAttributes_NOSHR$$stdpb");
+
+#define	getch()		wgetch	(stdscr)
+#define	addch(ch)	waddch	(stdscr, ch)
+#define	addstr(string)	waddstr	(stdscr, string)
+#define	move(y, x)	wmove	(stdscr, y, x)
+#define	refresh()	wrefresh (stdscr)
+#define	clear()		wclear	(stdscr)
+#define	clrtobot()	wclrtobot (stdscr)
+#define	clrtoeol()	wclrtoeol (stdscr)
+#define	delch()		wdelch 	(stdscr)
+#define	erase()		werase (stdscr)
+#define	insch(ch)	winsch	(stdscr, ch)
+#define	insertln()	winsertln (stdscr)
+#define	standout()	wstandout (stdscr)
+#define	standend()	wstandend (stdscr)
+#define	getstr(string)	wgetstr	(stdscr, string)
+#define	inch()		winch	(stdscr)
+#define	setattr(attr)	wsetattr (stdscr, attr)
+#define	clrattr(attr)	wclrattr (stdscr, attr)
+#define	deleteln()	wdeleteln (stdscr)
+#define	insstr(string)	winsstr (stdscr, string)
+
+#define mvwaddch(win,y,x,ch)	(wmove(win,y,x)==ERR)?ERR:waddch(win,ch)
+#define mvwgetch(win,y,x)	(wmove(win,y,x)==ERR)?ERR:wgetch(win)
+#define mvwaddstr(win,y,x,str)	(wmove(win,y,x)==ERR)?ERR:waddstr(win,str)
+#define mvwinsstr(win,y,x,str)	(wmove(win,y,x)==ERR)?ERR:winsstr(win,str)
+#define mvwgetstr(win,y,x,str)	(wmove(win,y,x)==ERR)?ERR:wgetstr(win,str)
+#define mvwinch(win,y,x)	(wmove(win,y,x)==ERR)?ERR:winch(win)
+#define mvwdelch(win,y,x)	(wmove(win,y,x)==ERR)?ERR:wdelch(win)
+#define mvwinsch(win,y,x,ch)	(wmove(win,y,x)==ERR)?ERR:winsch(win,ch)
+#define mvwdeleteln(win,y,x)	(wmove(win,y,x)==ERR)?ERR:wdeleteln(win)
+#define mvaddch(y,x,ch)	mvwaddch (stdscr, y, x, ch)
+#define mvgetch(y,x)		mvwgetch (stdscr, y, x)
+#define mvaddstr(y,x,str)	mvwaddstr (stdscr, y, x, str)
+#define mvinsstr(y,x,str)	mvwinsstr (stdscr, y, x, str)
+#define mvgetstr(y,x,str)	mvwgetstr (stdscr, y, x, str)
+#define mvinch(y,x)		mvwinch (stdscr, y, x)
+#define mvdelch(y,x)		mvwdelch (stdscr, y, x)
+#define mvinsch(y,x,ch)	mvwinsch (stdscr, y, x, ch)
+#define mvdeleteln(y,x)	mvwdeleteln (stdscr, y, x)
+#define mvcur(ly,lx,ny,nx)	wmove (stdscr, ny, nx)
+#pragma standard
+
+#define clearok(win, bf)	(win->_clear = bf)
+#define leaveok(win, bf)	(win->_leave = bf)
+#define scrollok(win, bf)	(win->_scroll = bf)
+#define wrapok(win, bf)	(win->_wrap = bf)
+#define flushok(win,bf) (bf ? win->_flags |= _FLUSH : (win->_flags &= ~_FLUSH))
+#define getyx(win,y,x)		y = win->_cur_y, x = win->_cur_x
+
+#define echo()			(stdkb->_flags &= ~_NOECHO)
+#define noecho()		(stdkb->_flags |= _NOECHO)
+#define nl()			(stdkb->_flags &= ~_NONL)
+#define nonl()			(stdkb->_flags |= _NONL)
+#define crmode()		((stdkb->_flags &= ~_NOCRMODE), nonl ())
+#define nocrmode()		(stdkb->_flags |= _NOCRMODE)
+#define raw()			(stdkb->_flags &= ~_NORAW)
+#define noraw()		(stdkb->_flags |= _NORAW)
+
+#define check(status)	if (!(status & SS$_NORMAL))	\
+			{	c$$translate (status); 	\
+				return ERR;		\
+			}
+
+#define bool int
+
+    int waddch(WINDOW * win, char ch);
+
+    int waddstr(WINDOW * win, char *str);
+
+    int box(WINDOW * win, char vert, char hor);
+
+    int wclear(WINDOW * win);
+
+    int wclrattr(WINDOW * win, int attr);
+
+    int wclrtobot(WINDOW * win);
+
+    int wclrtoeol(WINDOW * win);
+
+    int wdelch(WINDOW * win);
+
+    int wdeleteln(WINDOW * win);
+
+    int delwin(WINDOW * win);
+
+    int endwin(void);
+
+    int werase(WINDOW * win);
+
+    int wgetch(WINDOW * win);
+
+    int wgetstr(WINDOW * win, char *str);
+
+    char winch(WINDOW * win);
+
+    WINDOW *initscr(void);
+
+    int winsch(WINDOW * win, char ch);
+
+    int winsertln(WINDOW * win);
+
+    int winsstr(WINDOW * win, char *str);
+
+    int longname(char *termbuf, char *name);
+
+    int mvwin(WINDOW * win, int st_row, int st_col);
+
+    int wmove(WINDOW * win, int y, int x);
+
+    WINDOW *newwin(int numlines, int numcols, int begin_y, int begin_x);
+
+    int overlay(WINDOW * win1, WINDOW * win2);
+
+    int overwrite(WINDOW * win1, WINDOW * win2);
+
+#pragma NOSTANDARD
+#undef printw
+#undef wprintw
+#undef wscanw
+#undef scanw
+#pragma STANDARD
+
+    int printw(char *format_spec,...);
+
+    int wprintw(WINDOW * win, char *format_spec,...);
+
+    int wrefresh(WINDOW * win);
+
+    int wscanw(WINDOW * win, char *format_spec,...);
+
+    int scanw(char *fmt, int arg1);
+
+    int scroll(WINDOW * win);
+
+    int wsetattr(WINDOW * win, int attr);
+
+    WINDOW *subwin(WINDOW * win, int numlines, int numcols,
+		   int begin_y, int begin_x);
+
+    int wstandend(WINDOW * win);
+
+    int wstandout(WINDOW * win);
+
+    int touchwin(WINDOW * win);
+
+#if defined(CC$mixed_float) || defined(CC$VAXCSHR)
+
+#ifndef CC$gfloat
+#define CC$gfloat 0
+#endif
+
+#if CC$gfloat
+
+#define printw  vaxc$gprintw
+#define scanw   vaxc$gscanw
+#define wprintw vaxc$gwprintw
+#define wscanw  vaxc$gwscanw
+
+#else
+
+#define printw  vaxc$dprintw
+#define scanw   vaxc$dscanw
+#define wprintw vaxc$dwprintw
+#define wscanw  vaxc$dwscanw
+
+#endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* __CURSES_LOADED */
diff --git a/src/LYGetFile.c b/src/LYGetFile.c
new file mode 100644
index 00000000..751dd189
--- /dev/null
+++ b/src/LYGetFile.c
@@ -0,0 +1,1576 @@
+/* $LynxId: LYGetFile.c,v 1.80 2010/04/29 09:16:49 tom Exp $ */
+#include <HTUtils.h>
+#include <HTTP.h>
+#include <HTAnchor.h>		/* Anchor class */
+#include <HTAccess.h>
+#include <HTParse.h>
+#include <LYCurses.h>
+#include <GridText.h>
+#include <LYGlobalDefs.h>
+#include <LYUtils.h>
+#include <LYCharSets.h>
+#include <LYCharUtils.h>
+#include <HTAlert.h>
+#include <LYSignal.h>
+#include <LYGetFile.h>
+#include <LYPrint.h>
+#include <LYOptions.h>
+#include <LYStrings.h>
+#include <LYClean.h>
+#include <LYDownload.h>
+#include <LYNews.h>
+#include <LYMail.h>
+#include <LYKeymap.h>
+#include <LYBookmark.h>
+#include <LYMap.h>
+#include <LYList.h>
+#ifdef DIRED_SUPPORT
+#include <LYLocal.h>
+#endif /* DIRED_SUPPORT */
+#include <LYReadCFG.h>
+#include <LYHistory.h>
+#include <LYPrettySrc.h>
+
+#include <LYexit.h>
+#include <LYLeaks.h>
+
+static int fix_httplike_urls(DocInfo *doc, UrlTypes type);
+
+#ifdef VMS
+#define STRNADDRCOMP strncasecomp
+#else
+#define STRNADDRCOMP strncmp
+#endif /* !VMS */
+
+int HTNoDataOK = 0;
+
+/*
+ * getfile is the main mechanism to load a new document (or a previously loaded
+ * one whose rendering is cached in a HText structure) from mainloop, nearly
+ * everything goes through it.
+ * It should return one of the values
+ *     NORMAL     - requested document loaded successfully, usually [always?]
+ *                  its rendering is available as HTMainText.  It can be an
+ *                  HTTP error message page or similar, we make no
+ *                  distinction here.
+ *     NOT_FOUND  - requested document cannot be accessed, and the reason
+ *                  is a real error (as may be caused by an invalid link),
+ *                  not just that lynx disallows access because of some
+ *                  permission restrictions, and we have no error page
+ *                  to show for it either.
+ *     NULLFILE   - requested document not loaded into HTMainText, either
+ *                  some interactive protocol was requested (like telnet),
+ *                  or lynx does not allow access.
+ * The distinction between NOT_FOUND and NULLFILE is not very crucial, but
+ * getting it right prevents mainloop from exiting with the wrong message if it
+ * happens for the first file, and from logging (or not logging) errors
+ * inappropriately with -traversal, and from sending bogus error mail with
+ * MAIL_SYSTEM_ERROR_LOGGING:TRUE.  - kw
+ */
+int getfile(DocInfo *doc, int *target)
+{
+    UrlTypes url_type = NOT_A_URL_TYPE;
+    char *pound;
+    char *cp = NULL;
+    char *temp = NULL;
+    DocAddress WWWDoc;		/* a WWW absolute doc address struct */
+
+    /*
+     * Reset LYCancelDownload to prevent unwanted delayed effect.  - KW
+     */
+    if (LYCancelDownload) {
+	CTRACE((tfp, "getfile:    resetting LYCancelDownload to FALSE\n"));
+	LYCancelDownload = FALSE;
+    }
+
+    /*
+     * Reset fake 'Z' to prevent unwanted delayed effect.  - kw
+     */
+    LYFakeZap(NO);
+
+    /*
+     * Reset redirection counter to prevent bogus TOO_MANY_REDIRECTIONS in rare
+     * situations if the previous cycle got to the limit, but did not fail for
+     * that reason because the URL of the final location was handled specially,
+     * not via HTLoadAbsolute.  - kw
+     */
+    redirection_attempts = 0;
+
+  Try_Redirected_URL:
+    /*
+     * Load the WWWDoc struct in case we need to use it.
+     */
+    WWWDoc.address = doc->address;
+    WWWDoc.post_data = doc->post_data;
+    WWWDoc.post_content_type = doc->post_content_type;
+    WWWDoc.bookmark = doc->bookmark;
+    WWWDoc.isHEAD = doc->isHEAD;
+    WWWDoc.safe = doc->safe;
+
+    /*
+     * Reset HTPermitRedir, it has done its job if it was set.  - kw
+     */
+    HTPermitRedir = FALSE;
+
+    /*
+     * Reset WWW_Download_File just in case.
+     */
+    FREE(WWW_Download_File);
+
+    /*
+     * Reset redirect_post_content just in case.
+     */
+    redirect_post_content = FALSE;
+
+    /*
+     * This flag is a hack to allow us to pass on the fact that 'no data' may
+     * not really be an error although HTLoadAbsolute returned NO.  There
+     * should be a better way...  HT_NO_DATA should always mean 'not data but
+     * not an error', and be passed on to us as that, but current usage if
+     * HT_NO_DATA vs HT_NOT_LOADED has to be reviewed everywhere.  Anyway, some
+     * protocol module can set it to say 'I really mean it', we have to reset
+     * it here.  - kw
+     */
+    HTNoDataOK = 0;
+
+    CTRACE((tfp, "getfile: getting %s\n\n", doc->address));
+
+    /*
+     * Protect against denial of service attacks via the port 19 CHARGEN
+     * service, and block connections to the port 25 ESMTP service.  Also
+     * reject any likely spoof attempts via wrap arounds at 65536.  - FM
+     */
+    if ((temp = HTParse(doc->address, "", PARSE_HOST)) != NULL &&
+	strlen(temp) > 3) {
+	char *cp1;
+
+	if ((cp1 = strchr(temp, '@')) == NULL)
+	    cp1 = temp;
+	if ((cp = strrchr(cp1, ':')) != NULL) {
+	    long int value;
+
+	    cp++;
+	    if (sscanf(cp, "%ld", &value) == 1) {
+		if (value == 19 || value == 65555) {
+		    HTAlert(PORT_NINETEEN_INVALID);
+		    FREE(temp);
+		    return (NULLFILE);
+		} else if (value == 25 || value == 65561) {
+		    HTAlert(PORT_TWENTYFIVE_INVALID);
+		    FREE(temp);
+		    return (NULLFILE);
+		} else if (value > 65535 || value < 0) {
+		    char *msg = 0;
+
+		    HTSprintf0(&msg, PORT_INVALID, (unsigned long) value);
+		    HTAlert(msg);
+		    FREE(msg);
+		    FREE(temp);
+		    return (NULLFILE);
+		}
+	    } else if (isdigit(UCH(*cp))) {
+		HTAlert(URL_PORT_BAD);
+		FREE(temp);
+		return (NULLFILE);
+	    }
+	}
+    }
+    cp = NULL;
+    FREE(temp);
+
+    /*
+     * Check to see if this is a universal document ID that lib WWW wants to
+     * handle.
+     *
+     * Some special URL's we handle ourselves.  :)
+     */
+    if ((url_type = is_url(doc->address)) != 0) {
+	if (LYValidate && !LYPermitURL) {
+	    if (!(url_type == HTTP_URL_TYPE ||
+		  url_type == HTTPS_URL_TYPE ||
+		  url_type == LYNXHIST_URL_TYPE ||
+		  url_type == LYNXKEYMAP_URL_TYPE ||
+		  url_type == LYNXIMGMAP_URL_TYPE ||
+		  url_type == LYNXCOOKIE_URL_TYPE ||
+#ifdef USE_CACHEJAR
+		  url_type == LYNXCACHE_URL_TYPE ||
+#endif
+		  url_type == LYNXMESSAGES_URL_TYPE ||
+		  (url_type == LYNXOPTIONS_URL_TYPE &&
+		   WWWDoc.post_data) ||
+		  0 == STRNADDRCOMP(WWWDoc.address, helpfilepath,
+				    strlen(helpfilepath)) ||
+		  (lynxlistfile != NULL &&
+		   0 == STRNADDRCOMP(WWWDoc.address, lynxlistfile,
+				     strlen(lynxlistfile))) ||
+		  (lynxlinksfile != NULL &&
+		   0 == STRNADDRCOMP(WWWDoc.address, lynxlinksfile,
+				     strlen(lynxlinksfile))) ||
+		  (lynxjumpfile != NULL &&
+		   0 == STRNADDRCOMP(WWWDoc.address, lynxjumpfile,
+				     strlen(lynxjumpfile))))) {
+		HTUserMsg(NOT_HTTP_URL_OR_ACTION);
+		return (NULLFILE);
+	    }
+	}
+	if (traversal) {
+	    /*
+	     * Only traverse http URLs.
+	     */
+	    if (url_type != HTTP_URL_TYPE &&
+		url_type != LYNXIMGMAP_URL_TYPE) {
+		return (NULLFILE);
+	    }
+	} else if (check_realm && !LYPermitURL && !LYJumpFileURL) {
+	    if (!(0 == strncmp(startrealm, WWWDoc.address,
+			       strlen(startrealm)) ||
+		  url_type == LYNXHIST_URL_TYPE ||
+		  url_type == LYNXKEYMAP_URL_TYPE ||
+		  url_type == LYNXIMGMAP_URL_TYPE ||
+		  url_type == LYNXCOOKIE_URL_TYPE ||
+#ifdef USE_CACHEJAR
+		  url_type == LYNXCACHE_URL_TYPE ||
+#endif
+		  url_type == LYNXPRINT_URL_TYPE ||
+		  url_type == LYNXOPTIONS_URL_TYPE ||
+		  url_type == LYNXCFG_URL_TYPE ||
+		  url_type == LYNXCOMPILE_OPTS_URL_TYPE ||
+		  url_type == LYNXMESSAGES_URL_TYPE ||
+		  url_type == LYNXDOWNLOAD_URL_TYPE ||
+		  url_type == MAILTO_URL_TYPE ||
+		  url_type == NEWSPOST_URL_TYPE ||
+		  url_type == NEWSREPLY_URL_TYPE ||
+		  url_type == SNEWSPOST_URL_TYPE ||
+		  url_type == SNEWSREPLY_URL_TYPE ||
+		  (!LYUserSpecifiedURL &&
+		   (url_type == LYNXEXEC_URL_TYPE ||
+		    url_type == LYNXPROG_URL_TYPE ||
+		    url_type == LYNXCGI_URL_TYPE)) ||
+		  (WWWDoc.bookmark != NULL &&
+		   *WWWDoc.bookmark != '\0') ||
+		  0 == STRNADDRCOMP(WWWDoc.address, helpfilepath,
+				    strlen(helpfilepath)) ||
+		  (lynxlistfile != NULL &&
+		   0 == STRNADDRCOMP(WWWDoc.address, lynxlistfile,
+				     strlen(lynxlistfile))) ||
+		  (lynxjumpfile != NULL &&
+		   0 == STRNADDRCOMP(WWWDoc.address, lynxjumpfile,
+				     strlen(lynxjumpfile))))) {
+		HTUserMsg(NOT_IN_STARTING_REALM);
+		return (NULLFILE);
+	    }
+	}
+	if (WWWDoc.post_data &&
+	    url_type != HTTP_URL_TYPE &&
+	    url_type != HTTPS_URL_TYPE &&
+	    url_type != LYNXCGI_URL_TYPE &&
+	    url_type != LYNXIMGMAP_URL_TYPE &&
+	    url_type != GOPHER_URL_TYPE &&
+	    url_type != CSO_URL_TYPE &&
+	    url_type != PROXY_URL_TYPE &&
+	    url_type != LYNXOPTIONS_URL_TYPE &&
+	    !(url_type == FILE_URL_TYPE &&
+	      (LYIsUIPage(WWWDoc.address, UIP_LIST_PAGE) ||
+	       LYIsUIPage(WWWDoc.address, UIP_ADDRLIST_PAGE)))) {
+	    CTRACE((tfp, "getfile: dropping post_data!\n"));
+	    HTAlert(IGNORED_POST);
+	    LYFreePostData(doc);
+	    WWWDoc.post_data = NULL;
+	    WWWDoc.post_content_type = NULL;
+	}
+#ifdef SYSLOG_REQUESTED_URLS
+	LYSyslog(doc->address);
+#endif
+	if (url_type == UNKNOWN_URL_TYPE ||
+	    url_type == AFS_URL_TYPE ||
+	    url_type == PROSPERO_URL_TYPE) {
+	    HTAlert(UNSUPPORTED_URL_SCHEME);
+	    return (NULLFILE);
+
+	} else if (url_type == DATA_URL_TYPE) {
+	    HTAlert(UNSUPPORTED_DATA_URL);
+	    return (NULLFILE);
+
+	} else if (url_type == LYNXPRINT_URL_TYPE) {
+	    return (printfile(doc));
+
+#ifndef NO_OPTION_FORMS
+	} else if (url_type == LYNXOPTIONS_URL_TYPE) {
+	    /* proceed forms-based options menu */
+	    return (postoptions(doc));
+#endif
+
+	} else if (url_type == LYNXCFG_URL_TYPE &&
+		   !no_lynxcfg_info) {
+	    /* @@@ maybe we should generate a specific error message
+	       if attempted but restricted. - kw */
+	    /* show/change/reload lynx.cfg settings */
+	    return (lynx_cfg_infopage(doc));
+
+#if defined(HAVE_CONFIG_H) && !defined(NO_CONFIG_INFO)
+	} else if (url_type == LYNXCOMPILE_OPTS_URL_TYPE &&
+		   !no_compileopts_info) {
+	    /* @@@ maybe we should generate a specific error message
+	       if attempted but restricted or not supported. - kw */
+	    /* show compile-time settings */
+	    return (lynx_compile_opts(doc));
+#endif
+
+#ifndef DISABLE_NEWS
+	} else if (url_type == NEWSPOST_URL_TYPE ||
+		   url_type == NEWSREPLY_URL_TYPE ||
+		   url_type == SNEWSPOST_URL_TYPE ||
+		   url_type == SNEWSREPLY_URL_TYPE) {
+
+	    if (no_newspost) {
+		HTUserMsg(NEWSPOSTING_DISABLED);
+		return (NULLFILE);
+	    } else if (!news_ok && (url_type == NEWSPOST_URL_TYPE ||
+				    url_type == NEWSREPLY_URL_TYPE)) {
+		HTUserMsg(NEWS_DISABLED);
+		return (NULLFILE);
+	    } else {
+		HTLoadAbsolute(&WWWDoc);
+		return (NULLFILE);
+	    }
+#endif
+
+	} else if (url_type == LYNXDOWNLOAD_URL_TYPE) {
+	    LYDownload(doc->address);
+#ifdef VMS
+	    if (LYDidRename) {
+		/*
+		 * The temporary file was saved to disk via a rename(), so we
+		 * can't access the temporary file again via the download menu. 
+		 * Clear the flag, and return NULLFILE to pop.  - FM
+		 */
+		LYDidRename = FALSE;
+		return (NULLFILE);
+	    }
+#endif /* VMS */
+	    return (NORMAL);
+	} else if (url_type == LYNXDIRED_URL_TYPE) {
+#ifdef DIRED_SUPPORT
+	    if (!no_dired_support) {
+		local_dired(doc);
+		WWWDoc.address = doc->address;
+		WWWDoc.post_data = doc->post_data;
+		WWWDoc.post_content_type = doc->post_content_type;
+		WWWDoc.bookmark = doc->bookmark;
+		WWWDoc.isHEAD = doc->isHEAD;
+		WWWDoc.safe = doc->safe;
+
+		if (!HTLoadAbsolute(&WWWDoc)) {
+		    return (NOT_FOUND);
+		}
+		return (NORMAL);
+	    }
+#endif /* DIRED_SUPPORT */
+	    HTUserMsg(DIRED_DISABLED);
+	    return (NULLFILE);
+	}
+
+	if (LYNoRefererHeader == FALSE &&
+	    LYNoRefererForThis == FALSE) {
+	    const char *ref_url = HTLoadedDocumentURL();
+
+	    if (isLYNXIMGMAP(ref_url))
+		ref_url += LEN_LYNXIMGMAP;
+	    if (no_filereferer == TRUE && isFILE_URL(ref_url)) {
+		LYNoRefererForThis = TRUE;
+	    }
+	    if (LYNoRefererForThis == FALSE &&
+		(cp = strchr(ref_url, '?')) != NULL &&
+		strchr(cp, '=') != NULL) {
+		/*
+		 * Don't send a Referer header if the URL is the reply from a
+		 * form with method GET, in case the content has personal data
+		 * (e.g., a password or credit card number) which would become
+		 * visible in logs.  - FM
+		 *
+		 * Changed 1999-11-01 to be controlled by REFERER_WITH_QUERY
+		 * option.  - kw
+		 */
+		if (LYRefererWithQuery == 'S') {	/* SEND */
+		    StrAllocCopy(LYRequestReferer, ref_url);
+		} else if (LYRefererWithQuery == 'P') {		/* PARTIAL */
+		    FREE(LYRequestReferer);	/* just to be sure */
+		    LYRequestReferer = HTParse(ref_url, "",
+					       PARSE_ACCESS
+					       | PARSE_HOST
+					       | PARSE_STRICTPATH
+					       | PARSE_PUNCTUATION);
+		} else {	/* Everything else - don't send Referer */
+		    LYNoRefererForThis = TRUE;
+		}
+		cp = NULL;
+	    } else if (LYNoRefererForThis == FALSE) {
+		StrAllocCopy(LYRequestReferer, ref_url);
+	    }
+	} else {
+	    StrAllocCopy(LYRequestReferer, HTLoadedDocumentURL());
+	}
+	if (url_type == LYNXHIST_URL_TYPE) {
+	    /*
+	     * 'doc' will change to the new file if we had a successful
+	     * LYpop_num(), and the return value will be FALSE if we had a
+	     * cancel.  - FM
+	     */
+	    if ((historytarget(doc) == FALSE) ||
+		!doc || !doc->address) {
+		return (NOT_FOUND);
+	    }
+
+	    /*
+	     * We changed it so reload.
+	     */
+	    WWWDoc.address = doc->address;
+	    WWWDoc.post_data = doc->post_data;
+	    WWWDoc.post_content_type = doc->post_content_type;
+	    WWWDoc.bookmark = doc->bookmark;
+	    WWWDoc.isHEAD = doc->isHEAD;
+	    WWWDoc.safe = doc->safe;
+#ifndef DONT_TRACK_INTERNAL_LINKS
+	    if (doc->internal_link && !reloading) {
+		LYinternal_flag = TRUE;
+	    }
+#endif
+
+#ifdef DIRED_SUPPORT
+	    lynx_edit_mode = FALSE;
+#endif /* DIRED_SUPPORT */
+	    if (!HTLoadAbsolute(&WWWDoc)) {
+		return (NOT_FOUND);
+	    }
+	    return (NORMAL);
+
+	} else if (url_type == LYNXEXEC_URL_TYPE ||
+		   url_type == LYNXPROG_URL_TYPE) {
+#ifdef EXEC_LINKS
+	    if (no_exec &&
+		!exec_ok(HTLoadedDocumentURL(),
+			 doc->address + 9, ALWAYS_EXEC_PATH)) {
+		HTUserMsg(EXECUTION_DISABLED);
+	    } else if (no_bookmark_exec &&
+		       HTLoadedDocumentBookmark()) {
+		HTUserMsg(BOOKMARK_EXEC_DISABLED);
+	    } else if (local_exec || (local_exec_on_local_files &&
+				      exec_ok(HTLoadedDocumentURL(),
+					      doc->address + 9, EXEC_PATH))) {
+
+		char *p = NULL;
+
+		/*
+		 * Bug puts slash on end if none is in the string.
+		 */
+		char *last_slash = strrchr(doc->address, '/');
+
+		if (last_slash - doc->address
+		    == (int) strlen(doc->address) - 1)
+		    doc->address[strlen(doc->address) - 1] = '\0';
+
+		/*
+		 * Convert '~' to $HOME.
+		 */
+		StrAllocCopy(p, doc->address);
+		LYTildeExpand(&p, TRUE);
+
+		/*
+		 * Show URL before executing it.
+		 */
+		HTInfoMsg(doc->address);
+		stop_curses();
+		/*
+		 * Run the command.
+		 */
+		if (strstr(p, "//") == p + 9)
+		    LYSystem(p + 11);
+		else
+		    LYSystem(p + 9);
+		FREE(p);
+
+		if (url_type != LYNXPROG_URL_TYPE) {
+		    /*
+		     * Make sure user gets to see screen output.
+		     */
+#ifndef VMS
+		    signal(SIGINT, SIG_IGN);
+#endif /* !VMS */
+		    printf("\n%s", RETURN_TO_LYNX);
+		    fflush(stdout);
+		    (void) LYgetch();
+#ifdef VMS
+		    HadVMSInterrupt = FALSE;
+#endif /* VMS */
+		}
+		if (!dump_output_immediately) {
+		    start_curses();
+		    LYAddVisitedLink(doc);
+		}
+
+	    } else {
+		char *buf = 0;
+
+		HTSprintf0(&buf,
+			   EXECUTION_DISABLED_FOR_FILE,
+			   key_for_func(LYK_OPTIONS));
+		HTAlert(buf);
+		FREE(buf);
+	    }
+#else /* no exec_links */
+	    HTUserMsg(EXECUTION_NOT_COMPILED);
+#endif /* EXEC_LINKS */
+	    return (NULLFILE);
+
+	} else if (url_type == MAILTO_URL_TYPE) {
+	    if (no_mail) {
+		HTUserMsg(MAIL_DISABLED);
+	    } else {
+		HTParentAnchor *tmpanchor = HTAnchor_findAddress(&WWWDoc);
+		const char *title;
+		char *tmptitle = NULL;
+
+		title = "";
+		if (HTAnchor_title(tmpanchor)) {
+		    title = HTAnchor_title(tmpanchor);
+		} else if (HTMainAnchor && !LYUserSpecifiedURL) {
+		    title = HTAnchor_subject(HTMainAnchor);
+		    if (non_empty(title)) {
+			if (strncasecomp(title, "Re:", 3)) {
+			    StrAllocCopy(tmptitle, "Re: ");
+			    StrAllocCat(tmptitle, title);
+			    title = tmptitle;
+			}
+		    } else {
+			title = "";
+		    }
+		}
+		cp = strchr(doc->address, ':') + 1;
+		reply_by_mail(cp,
+			      ((HTMainAnchor && !LYUserSpecifiedURL) ?
+			       (char *) HTMainAnchor->address :
+			       (char *) doc->address),
+			      title,
+			      (HTMainAnchor && !LYUserSpecifiedURL) ?
+			      HTMainAnchor->message_id : NULL);
+		FREE(tmptitle);
+	    }
+	    return (NULLFILE);
+
+	    /*
+	     * From here on we could have a remote host, so check if that's
+	     * allowed.
+	     */
+	} else if (local_host_only &&
+		   url_type != LYNXKEYMAP_URL_TYPE &&
+		   url_type != LYNXIMGMAP_URL_TYPE &&
+		   url_type != LYNXCOOKIE_URL_TYPE &&
+		   url_type != LYNXMESSAGES_URL_TYPE &&
+#ifdef USE_CACHEJAR
+		   url_type != LYNXCACHE_URL_TYPE &&
+#endif
+		   url_type != LYNXCGI_URL_TYPE &&
+		   !(url_type == NEWS_URL_TYPE &&
+		     strncmp(doc->address, "news://", 7)) &&
+		   !(LYisLocalHost(doc->address) ||
+		     LYisLocalAlias(doc->address))) {
+	    HTUserMsg(ACCESS_ONLY_LOCALHOST);
+	    return (NULLFILE);
+
+	    /*
+	     * Disable www telnet access if not telnet_ok.
+	     */
+	} else if (url_type == TELNET_URL_TYPE ||
+		   url_type == TN3270_URL_TYPE ||
+		   url_type == TELNET_GOPHER_URL_TYPE) {
+	    char *proxy;
+
+	    if (!telnet_ok) {
+		HTUserMsg(TELNET_DISABLED);
+		return (NULLFILE);
+	    } else if (no_telnet_port && strchr(doc->address + 7, ':')) {
+		HTUserMsg(TELNET_PORT_SPECS_DISABLED);
+		return (NULLFILE);
+		/*
+		 * Detect weird case where interactive protocol would be
+		 * proxied, and to a non-interactive protocol at that.
+		 */
+	    } else if ((proxy = LYGetEnv(((url_type == TN3270_URL_TYPE)
+					  ? "tn3270_proxy"
+					  :
+					  ((url_type == TELNET_GOPHER_URL_TYPE)
+					   ? "gopher_proxy"
+					   : "telnet_proxy")))) != NULL &&
+		       !override_proxy(doc->address) &&
+		       (!isTELNET_URL(proxy) &&
+			!isTN3270_URL(proxy) &&
+			!isRLOGIN_URL(proxy))) {
+		/* Do nothing, fall through to generic code - kw */
+	    } else {
+		stop_curses();
+		HTLoadAbsolute(&WWWDoc);
+		if (!dump_output_immediately) {
+		    start_curses();
+		    fflush(stdout);
+		    LYAddVisitedLink(doc);
+		}
+		return (NULLFILE);
+	    }
+
+	    /*
+	     * Disable www news access if not news_ok.
+	     */
+#ifndef DISABLE_NEWS
+	} else if (!news_ok && (url_type == NEWS_URL_TYPE ||
+				url_type == NNTP_URL_TYPE)) {
+	    HTUserMsg(NEWS_DISABLED);
+	    return (NULLFILE);
+#endif
+
+	} else if (url_type == RLOGIN_URL_TYPE) {
+	    char *proxy;
+
+	    if (!rlogin_ok) {
+		HTUserMsg(RLOGIN_DISABLED);
+		return (NULLFILE);
+		/*
+		 * Detect weird case where interactive protocol would be
+		 * proxied, and to a non-interactive protocol at that.
+		 */
+	    } else if ((proxy = LYGetEnv("rlogin_proxy")) != NULL &&
+		       !override_proxy(doc->address) &&
+		       (!isTELNET_URL(proxy) &&
+			!isTN3270_URL(proxy) &&
+			!isRLOGIN_URL(proxy))) {
+		/* Do nothing, fall through to generic code - kw */
+	    } else {
+		stop_curses();
+		HTLoadAbsolute(&WWWDoc);
+		fflush(stdout);
+		if (!dump_output_immediately) {
+		    start_curses();
+		    LYAddVisitedLink(doc);
+		}
+		return (NULLFILE);
+	    }
+
+	    /*
+	     * If it's a gopher index type and there isn't a search term
+	     * already attached then do this.  Otherwise just load it!
+	     */
+	} else if (url_type == INDEX_GOPHER_URL_TYPE &&
+		   strchr(doc->address, '?') == NULL) {
+	    int status;
+
+	    /*
+	     * Make sure we don't have a gopher+ escaped tab instead of a
+	     * gopher0 question mark delimiting the search term.  - FM
+	     */
+	    if ((cp = strstr(doc->address, "%09")) != NULL) {
+		*cp = '\0';
+		StrAllocCopy(temp, doc->address);
+		cp += 3;
+		if (*cp && strncmp(cp, "%09", 3)) {
+		    StrAllocCat(temp, "?");
+		    StrAllocCat(temp, cp);
+		    if ((cp = strstr(temp, "%09")) != NULL) {
+			*cp = '\0';
+		    }
+		}
+		StrAllocCopy(doc->address, temp);
+		FREE(temp);
+		goto Try_Redirected_URL;
+	    }
+	    /*
+	     * Load it because the do_www_search routine uses the base url of
+	     * the currently loaded document :(
+	     */
+	    if (!HTLoadAbsolute(&WWWDoc)) {
+		return (NOT_FOUND);
+	    }
+	    status = do_www_search(doc);
+	    if (status == NULLFILE) {
+		LYpop(doc);
+		WWWDoc.address = doc->address;
+		WWWDoc.post_data = doc->post_data;
+		WWWDoc.post_content_type = doc->post_content_type;
+		WWWDoc.bookmark = doc->bookmark;
+		WWWDoc.isHEAD = doc->isHEAD;
+		WWWDoc.safe = doc->safe;
+		status = HTLoadAbsolute(&WWWDoc);
+#ifdef DIRED_SUPPORT
+	    } else {
+		lynx_edit_mode = FALSE;
+#endif /* DIRED_SUPPORT */
+	    }
+	    return (status);
+	}
+
+	if (!ftp_ok
+	    && (url_type == FTP_URL_TYPE
+		|| url_type == NCFTP_URL_TYPE)) {
+	    HTUserMsg(FTP_DISABLED);
+	    return (NULLFILE);
+	} else if (url_type == HTML_GOPHER_URL_TYPE) {
+	    char *tmp = NULL;
+
+	    /*
+	     * If tuple's Path=GET%20/...  convert to an http URL.
+	     */
+	    if ((cp = strchr(doc->address + 9, '/')) != NULL &&
+		0 == strncmp(++cp, "hGET%20/", 8)) {
+		StrAllocCopy(tmp, "http://");
+		CTRACE((tfp, "getfile: URL '%s'\n",
+			doc->address));
+		*cp = '\0';
+		StrAllocCat(tmp, doc->address + 9);
+		/*
+		 * If the port is defaulted, it should stay 70.
+		 */
+		if (strchr(tmp + 6, ':') == NULL) {
+		    StrAllocCat(tmp, "70/");
+		    tmp[strlen(tmp) - 4] = ':';
+		}
+		if (strlen(cp + 7) > 1)
+		    StrAllocCat(tmp, cp + 8);
+		StrAllocCopy(doc->address, tmp);
+		CTRACE((tfp, "  changed to '%s'\n",
+			doc->address));
+		FREE(tmp);
+		url_type = HTTP_URL_TYPE;
+	    }
+	}
+
+	if (url_type == HTTP_URL_TYPE ||
+	    url_type == HTTPS_URL_TYPE ||
+	    url_type == FTP_URL_TYPE ||
+	    url_type == NCFTP_URL_TYPE ||
+	    url_type == CSO_URL_TYPE) {
+	    fix_httplike_urls(doc, url_type);
+	}
+
+	WWWDoc.address = doc->address;	/* possible reload */
+#ifdef DIRED_SUPPORT
+	lynx_edit_mode = FALSE;
+#endif /* DIRED_SUPPORT */
+
+#ifndef DISABLE_BIBP
+	if (url_type == BIBP_URL_TYPE) {
+	    char *bibpTmp = NULL;
+
+	    if (!BibP_bibhost_checked)
+		LYCheckBibHost();
+	    if (BibP_bibhost_available) {
+		StrAllocCopy(bibpTmp, BibP_bibhost);
+	    } else if (HTMainAnchor && HTAnchor_citehost(HTMainAnchor)) {
+		StrAllocCopy(bibpTmp, HTAnchor_citehost(HTMainAnchor));
+	    } else {
+		StrAllocCopy(bibpTmp, BibP_globalserver);
+	    }
+	    if (HTMainAnchor && HTAnchor_citehost(HTMainAnchor)) {
+		StrAllocCat(bibpTmp, "bibp1.0/resolve?citehost=");
+		StrAllocCat(bibpTmp, HTAnchor_citehost(HTMainAnchor));
+		StrAllocCat(bibpTmp, "&usin=");
+	    } else {
+		StrAllocCat(bibpTmp, "bibp1.0/resolve?usin=");
+	    }
+	    StrAllocCat(bibpTmp, doc->address + 5);	/* USIN after bibp: */
+	    StrAllocCopy(doc->address, bibpTmp);
+	    WWWDoc.address = doc->address;
+	    FREE(bibpTmp);
+	}
+#endif /* !DISABLE_BIBP */
+
+	if (url_type == FILE_URL_TYPE) {
+	    /*
+	     * If a file URL has a '~' as the lead character of its first
+	     * symbolic element, convert the '~' to Home_Dir(), then append
+	     * the rest of of path, if present, skipping "user" if "~user"
+	     * was entered, simplifying, and eliminating any residual
+	     * relative elements.  - FM
+	     */
+	    LYTildeExpand(&(doc->address), TRUE);
+	    WWWDoc.address = doc->address;
+	}
+	CTRACE_SLEEP(MessageSecs);
+	user_message(WWW_WAIT_MESSAGE, doc->address);
+
+	if (TRACE) {
+#ifdef USE_SLANG
+	    if (LYCursesON) {
+		LYaddstr("*\n");
+		LYrefresh();
+	    }
+#endif /* USE_SLANG */
+	    CTRACE((tfp, "\n"));
+	}
+
+	if (!HTLoadAbsolute(&WWWDoc)) {
+	    /*
+	     * Check for redirection.
+	     */
+	    if (use_this_url_instead != NULL) {
+		if (!is_url(use_this_url_instead)) {
+		    /*
+		     * The server did not return a complete URL in its
+		     * Location:  header, probably due to a FORM or other
+		     * CGI script written by someone who doesn't know that
+		     * the http protocol requires that it be a complete
+		     * URL, or using a server which does not treat such a
+		     * redirect string from the script as an instruction to
+		     * resolve it versus the initial request, check
+		     * authentication with that URL, and then act on it
+		     * without returning redirection to us.  We'll violate
+		     * the http protocol and resolve it ourselves using the
+		     * URL of the original request as the BASE, rather than
+		     * doing the RIGHT thing and returning an invalid
+		     * address message.  - FM
+		     */
+		    HTUserMsg(LOCATION_NOT_ABSOLUTE);
+		    temp = HTParse(use_this_url_instead,
+				   WWWDoc.address,
+				   PARSE_ALL);
+		    if (non_empty(temp)) {
+			StrAllocCopy(use_this_url_instead, temp);
+		    }
+		    FREE(temp);
+		}
+		url_type = is_url(use_this_url_instead);
+		if (!HTPermitRedir &&
+		    (url_type == LYNXDOWNLOAD_URL_TYPE ||
+		     url_type == LYNXEXEC_URL_TYPE ||
+		     url_type == LYNXPROG_URL_TYPE ||
+#ifdef DIRED_SUPPORT
+		     url_type == LYNXDIRED_URL_TYPE ||
+#endif /* DIRED_SUPPORT */
+		     url_type == LYNXPRINT_URL_TYPE ||
+		     url_type == LYNXOPTIONS_URL_TYPE ||
+		     url_type == LYNXCFG_URL_TYPE ||
+		     url_type == LYNXCOMPILE_OPTS_URL_TYPE ||
+		     url_type == LYNXHIST_URL_TYPE ||
+		     url_type == LYNXCOOKIE_URL_TYPE ||
+#ifdef USE_CACHEJAR
+		     url_type == LYNXCACHE_URL_TYPE ||
+#endif
+		     url_type == LYNXMESSAGES_URL_TYPE ||
+		     (LYValidate &&
+		      url_type != HTTP_URL_TYPE &&
+		      url_type != HTTPS_URL_TYPE) ||
+		     ((no_file_url || no_goto_file) &&
+		      url_type == FILE_URL_TYPE) ||
+		     (no_goto_lynxcgi &&
+		      url_type == LYNXCGI_URL_TYPE) ||
+#ifndef DISABLE_BIBP
+		     (no_goto_bibp &&
+		      url_type == BIBP_URL_TYPE) ||
+#endif
+		     (no_goto_cso &&
+		      url_type == CSO_URL_TYPE) ||
+		     (no_goto_finger &&
+		      url_type == FINGER_URL_TYPE) ||
+		     (no_goto_ftp &&
+		      (url_type == FTP_URL_TYPE ||
+		       url_type == NCFTP_URL_TYPE)) ||
+		     (no_goto_gopher &&
+		      url_type == GOPHER_URL_TYPE) ||
+		     (no_goto_http &&
+		      url_type == HTTP_URL_TYPE) ||
+		     (no_goto_https &&
+		      url_type == HTTPS_URL_TYPE) ||
+		     (no_goto_mailto &&
+		      url_type == MAILTO_URL_TYPE) ||
+#ifndef DISABLE_NEWS
+		     (no_goto_news &&
+		      url_type == NEWS_URL_TYPE) ||
+		     (no_goto_nntp &&
+		      url_type == NNTP_URL_TYPE) ||
+#endif
+		     (no_goto_rlogin &&
+		      url_type == RLOGIN_URL_TYPE) ||
+#ifndef DISABLE_NEWS
+		     (no_goto_snews &&
+		      url_type == SNEWS_URL_TYPE) ||
+#endif
+		     (no_goto_telnet &&
+		      url_type == TELNET_URL_TYPE) ||
+		     (no_goto_tn3270 &&
+		      url_type == TN3270_URL_TYPE) ||
+		     (no_goto_wais &&
+		      url_type == WAIS_URL_TYPE))) {
+		    /*
+		     * Some schemes are not acceptable from server
+		     * redirections.  - KW & FM
+		     */
+		    HTAlert(ILLEGAL_REDIRECTION_URL);
+		    if (LYCursesON) {
+			HTUserMsg2(WWW_ILLEGAL_URL_MESSAGE,
+				   use_this_url_instead);
+		    } else {
+			fprintf(stderr,
+				WWW_ILLEGAL_URL_MESSAGE,
+				use_this_url_instead);
+		    }
+		    FREE(use_this_url_instead);
+		    return (NULLFILE);
+		}
+		if ((pound = findPoundSelector(doc->address)) != NULL
+		    && findPoundSelector(use_this_url_instead) == NULL) {
+		    /*
+		     * Our requested URL had a fragment associated with it,
+		     * and the redirection URL doesn't, so we'll append the
+		     * fragment associated with the original request.  If
+		     * it's bogus for the redirection URL, we'll be
+		     * positioned at the top of that document, so there's
+		     * no harm done.  - FM
+		     */
+		    CTRACE((tfp,
+			    "getfile: Adding fragment '%s' to redirection URL.\n",
+			    pound));
+		    StrAllocCat(use_this_url_instead, pound);
+		    doc->link = -1;
+		}
+		CTRACE_SLEEP(MessageSecs);
+		HTUserMsg2(WWW_USING_MESSAGE, use_this_url_instead);
+		CTRACE((tfp, "\n"));
+		StrAllocCopy(doc->address,
+			     use_this_url_instead);
+		FREE(use_this_url_instead);
+		if (redirect_post_content == FALSE) {
+		    /*
+		     * Freeing the content also yields a GET request.  - FM
+		     */
+		    LYFreePostData(doc);
+		}
+		/*
+		 * Go to top to check for URL's which get special handling
+		 * and/or security checks in Lynx.  - FM
+		 */
+		goto Try_Redirected_URL;
+	    }
+	    if (HTNoDataOK) {
+		return (NULLFILE);
+	    } else {
+		return (NOT_FOUND);
+	    }
+	} else {
+
+	    lynx_mode = NORMAL_LYNX_MODE;
+
+	    /*
+	     * Some URL's don't actually return a document; compare
+	     * doc->address with the document that is actually loaded and
+	     * return NULLFILE if not loaded.  If www_search_result is not -1
+	     * then this is a reference to a named anchor within the same
+	     * document; do NOT return NULLFILE in that case.
+	     */
+
+	    /*
+	     * Check for a #fragment selector.
+	     */
+	    pound = findPoundSelector(doc->address);
+
+	    /*
+	     * Check to see if there is a temp file waiting for us to
+	     * download.
+	     */
+	    if (WWW_Download_File) {
+		HTParentAnchor *tmpanchor = HTAnchor_findAddress(&WWWDoc);
+		char *fname = NULL;
+
+		/*
+		 * Check for a suggested filename from the
+		 * Content-Disposition header.  - FM
+		 */
+		if (HTAnchor_SugFname(tmpanchor) != NULL) {
+		    StrAllocCopy(fname, HTAnchor_SugFname(tmpanchor));
+		} else {
+		    StrAllocCopy(fname, doc->address);
+		}
+		/*
+		 * Check whether this is a compressed file, which we don't
+		 * uncompress for downloads, and adjust any suffix
+		 * appropriately.  - FM
+		 */
+		HTCheckFnameForCompression(&fname, tmpanchor, FALSE);
+
+		if (LYdownload_options(&fname,
+				       WWW_Download_File) < 0) {
+		    FREE(fname);
+		    return (NOT_FOUND);
+		}
+		LYAddVisitedLink(doc);
+		StrAllocCopy(doc->address, fname);
+		FREE(fname);
+		doc->internal_link = FALSE;
+		WWWDoc.address = doc->address;
+		LYFreePostData(doc);
+		WWWDoc.post_data = NULL;
+		WWWDoc.post_content_type = NULL;
+		WWWDoc.bookmark = doc->bookmark = FALSE;
+		WWWDoc.isHEAD = doc->isHEAD = FALSE;
+		WWWDoc.safe = doc->safe = FALSE;
+		HTOutputFormat = WWW_PRESENT;
+		if (!HTLoadAbsolute(&WWWDoc)) {
+		    return (NOT_FOUND);
+		} else {
+		    return (NORMAL);
+		}
+
+	    } else if (pound == NULL &&
+		/*
+		 * HTAnchor hash-table searches are now case-sensitive
+		 * (hopefully, without anchor deletion problems), so this
+		 * is too.  - FM
+		 */
+		       (strcmp(doc->address,
+			       HTLoadedDocumentURL()) ||
+		/*
+		 * Also check the post_data elements.  - FM
+		 */
+			!BINEQ(doc->post_data,
+			       HTLoadedDocumentPost_data()) ||
+		/*
+		 * Also check the isHEAD element.  - FM
+		 */
+			doc->isHEAD != HTLoadedDocumentIsHEAD())) {
+		/*
+		 * Nothing needed to be shown.
+		 */
+		LYAddVisitedLink(doc);
+		return (NULLFILE);
+
+	    } else {
+		if (pound != NULL) {
+		    if (!HTMainText) {	/* this should not happen... */
+			return (NULLFILE);	/* but it can. - kw */
+		    }
+		    /*
+		     * May set www_search_result.
+		     */
+		    if (HTFindPoundSelector(pound + 1)) {
+			*target = www_search_result;
+			doc->link = -1;
+		    }
+		}
+		return (NORMAL);
+	    }
+	}
+    } else {
+	CTRACE_SLEEP(MessageSecs);
+	HTUserMsg2(WWW_BAD_ADDR_MESSAGE, doc->address);
+	CTRACE((tfp, "\n"));
+	return (NULLFILE);
+    }
+}
+
+/*
+ * Set source mode for the next retrieval via getfile or HTreparse_document.
+ * mode == -1:  force normal presentation
+ * mode == 1:  force source presentation
+ * mode == 0:  reset to normal if it was set to source
+ * - kw
+ */
+void srcmode_for_next_retrieval(int mode)
+{
+    if (mode < 0) {
+	HTOutputFormat = WWW_PRESENT;
+#ifdef USE_PRETTYSRC
+	psrc_view = FALSE;
+#endif
+
+    } else if (mode == 0) {
+	if (HTOutputFormat == WWW_SOURCE)
+	    HTOutputFormat = WWW_PRESENT;
+#ifdef USE_PRETTYSRC
+	else if (LYpsrc)
+	    psrc_view = FALSE;
+#endif
+
+    } else {
+#ifdef USE_PRETTYSRC
+	if (LYpsrc)
+	    psrc_view = TRUE;
+	else
+	    HTOutputFormat = WWW_SOURCE;
+#else
+	HTOutputFormat = WWW_SOURCE;
+#endif
+    }
+}
+
+/*
+ * The user wants to select a link or page by number.
+ *
+ * If follow_link_number returns DO_LINK_STUFF do_link will be run immediately
+ * following its execution.
+ *
+ * If follow_link_number returns DO_GOTOLINK_STUFF it has updated the passed in
+ * doc for positioning on a link.
+ *
+ * If follow_link_number returns DO_GOTOPAGE_STUFF it has set doc->line to the
+ * top line of the desired page for displaying that page.
+ *
+ * If follow_link_number returns PRINT_ERROR an error message will be given to
+ * the user.
+ *
+ * If follow_link_number returns DO_FORMS_STUFF some forms stuff will be done. 
+ * (Not yet implemented.)
+ *
+ * If follow_link_number returns DO_NOTHING nothing special will run after it.
+ */
+int follow_link_number(int c,
+		       int cur,
+		       DocInfo *doc,
+		       int *num)
+{
+    char temp[120];
+    char *p = temp;
+    int rel = 0;
+    int new_top, new_link;
+    BOOL want_go;
+    int curline = *num;		/* passed in from mainloop() */
+
+    CTRACE((tfp, "follow_link_number(%d,%d,...)\n", c, cur));
+    temp[0] = (char) c;
+    temp[1] = '\0';
+    *num = -1;
+    _statusline(FOLLOW_LINK_NUMBER);
+    /*
+     * Get the number, possibly with a letter suffix, from the user.
+     */
+    if (LYgetstr(temp, VISIBLE, sizeof(temp), NORECALL) < 0 || *temp == 0) {
+	HTInfoMsg(CANCELLED);
+	return (DO_NOTHING);
+    }
+    *num = atoi(p);
+    while (isdigit(UCH(*p)))
+	++p;
+    c = *p;			/* reuse c; 0 or g or p or + or - */
+    switch (c) {
+    case '+':
+    case '-':
+	/* 123+ or 123- */
+	rel = c;
+	c = *++p;
+	break;
+    default:
+	rel = *++p;
+	break;
+    case 0:
+	break;
+    }
+    /* don't currently check for errors typing suffix */
+
+    CTRACE((tfp, "  temp=%s, *num=%d, rel='%c'\n", temp, *num, rel));
+    /*
+     * Check if we had a 'p' or 'P' following the number as a flag for
+     * displaying the page with that number.  - FM
+     */
+    if ((c == 'p' || c == 'P') && display_lines == 0) {
+	CTRACE((tfp, " curline=%d, LYlines=%d, display too small!\n",
+		curline, LYlines));
+	return (PRINT_ERROR);
+    } else if (c == 'p' || c == 'P') {
+	int nlines = HText_getNumOfLines();
+	int npages = ((nlines + 1) > display_lines) ?
+	(((nlines + 1) + (display_lines - 1)) / (display_lines))
+	: 1;
+	int curpage = ((curline + 1) > display_lines) ?
+	(((curline + 1) + (display_lines - 1)) / (display_lines))
+	: 1;
+
+	CTRACE((tfp, " nlines=%d, npages=%d, curline=%d, curpage=%d\n",
+		nlines, npages, curline, curpage));
+	if (*num < 1)
+	    *num = rel ? 0 : 1;
+	if (rel == '+')
+	    *num = curpage + *num;
+	else if (rel == '-')
+	    *num = curpage - *num;
+	doc->line = (npages <= 1) ?
+	    1 :
+	    ((*num <= npages) ? (((*num - 1) * display_lines) + 1)
+	     : (((npages - 1) * display_lines) + 1));
+	return (DO_GOTOPAGE_STUFF);
+    }
+
+    /*
+     * Check if we want to make the link corresponding to the number the
+     * current link, rather than ACTIVATE-ing it.
+     */
+    want_go = (BOOL) (c == 'g' || c == 'G');
+
+    /* If rel, add or subtract num from current link, or
+     * nearest previous/subsequent link if current link is not on screen.
+     */
+    if (rel)
+	*num = HTGetRelLinkNum(*num, rel, cur);
+    /*
+     * If we have a valid number, act on it.
+     */
+    if (*num > 0) {
+	int info;
+	char *text = NULL;
+
+	/*
+	 * Get the lname, and hightext, directly from www structures and add it
+	 * to the cur link so that we can pass it transparently on to
+	 * getfile(), and load new_top and new_link if we instead want to make
+	 * the link number current.  These things are done so that a link can
+	 * be selected anywhere in the current document, whether it is
+	 * displayed on the screen or not!
+	 */
+	info = HTGetLinkInfo(*num,
+			     want_go,
+			     &new_top,
+			     &new_link,
+			     &text,
+			     &links[cur].lname);
+	if (text != NULL)
+	    LYSetHilite(cur, text);
+	if (info == WWW_INTERN_LINK_TYPE) {
+	    links[cur].type = WWW_INTERN_LINK_TYPE;
+	    return (DO_LINK_STUFF);
+	} else if (info == LINK_LINE_FOUND) {
+	    doc->line = new_top + 1;
+	    doc->link = new_link;
+	    return (DO_GOTOLINK_STUFF);
+	} else if (info) {
+	    links[cur].type = WWW_LINK_TYPE;
+	    return (DO_LINK_STUFF);
+	} else {
+	    return (PRINT_ERROR);
+	}
+    } else {
+	return (PRINT_ERROR);
+    }
+}
+
+#if defined(EXEC_LINKS) || defined(LYNXCGI_LINKS)
+
+struct trust {
+    char *src;
+    char *path;
+    int type;
+    struct trust *next;
+};
+
+static struct trust trusted_exec_default =
+{
+    "file://localhost/", "", EXEC_PATH, NULL
+};
+static struct trust always_trusted_exec_default =
+{
+    "none", "", ALWAYS_EXEC_PATH, NULL
+};
+static struct trust trusted_cgi_default =
+{
+    "none", "", CGI_PATH, NULL
+};
+
+static struct trust *trusted_exec = &trusted_exec_default;
+static struct trust *always_trusted_exec = &always_trusted_exec_default;
+static struct trust *trusted_cgi = &trusted_cgi_default;
+
+#ifdef LY_FIND_LEAKS
+static void LYTrusted_free(void)
+{
+    struct trust *cur;
+    struct trust *next;
+
+    if (trusted_exec != &trusted_exec_default) {
+	cur = trusted_exec;
+	while (cur) {
+	    FREE(cur->src);
+	    FREE(cur->path);
+	    next = cur->next;
+	    FREE(cur);
+	    cur = next;
+	}
+    }
+
+    if (always_trusted_exec != &always_trusted_exec_default) {
+	cur = always_trusted_exec;
+	while (cur) {
+	    FREE(cur->src);
+	    FREE(cur->path);
+	    next = cur->next;
+	    FREE(cur);
+	    cur = next;
+	}
+    }
+
+    if (trusted_cgi != &trusted_cgi_default) {
+	cur = trusted_cgi;
+	while (cur) {
+	    FREE(cur->src);
+	    FREE(cur->path);
+	    next = cur->next;
+	    FREE(cur);
+	    cur = next;
+	}
+    }
+
+    return;
+}
+#endif /* LY_FIND_LEAKS */
+
+void add_trusted(char *str,
+		 int type)
+{
+    struct trust *tp;
+    char *path;
+    char *src = str;
+    int Type = type;
+    static BOOLEAN first = TRUE;
+
+    if (!src)
+	return;
+    if (first) {
+#ifdef LY_FIND_LEAKS
+	atexit(LYTrusted_free);
+#endif
+	first = FALSE;
+    }
+
+    path = strchr(src, '\t');
+    if (path)
+	*path++ = '\0';
+    else
+	path = "";
+
+    tp = (struct trust *) malloc(sizeof(*tp));
+    if (tp == NULL)
+	outofmem(__FILE__, "add_trusted");
+
+    assert(tp != NULL);
+
+    tp->src = NULL;
+    tp->path = NULL;
+    tp->type = Type;
+    StrAllocCopy(tp->src, src);
+    StrAllocCopy(tp->path, path);
+    if (Type == EXEC_PATH) {
+	if (trusted_exec == &trusted_exec_default)
+	    tp->next = NULL;
+	else
+	    tp->next = trusted_exec;
+	trusted_exec = tp;
+    } else if (Type == ALWAYS_EXEC_PATH) {
+	if (always_trusted_exec == &always_trusted_exec_default)
+	    tp->next = NULL;
+	else
+	    tp->next = always_trusted_exec;
+	always_trusted_exec = tp;
+    } else if (Type == CGI_PATH) {
+	if (trusted_cgi == &trusted_cgi_default)
+	    tp->next = NULL;
+	else
+	    tp->next = trusted_cgi;
+	trusted_cgi = tp;
+    }
+}
+
+/*
+ * Check to see if the supplied paths is allowed to be executed.
+ */
+BOOLEAN exec_ok(const char *source,
+		const char *linktext,
+		int type)
+{
+    struct trust *tp;
+    const char *cp;
+    const char *allowed_extra_chars;
+    int Type = type;
+
+    /*
+     * Always OK if it is a jump file shortcut.
+     */
+    if (LYJumpFileURL)
+	return TRUE;
+
+    /*
+     * Choose the trust structure based on the type.
+     */
+    if (Type == EXEC_PATH) {
+	tp = trusted_exec;
+    } else if (Type == ALWAYS_EXEC_PATH) {
+	tp = always_trusted_exec;
+    } else if (Type == CGI_PATH) {
+	tp = trusted_cgi;
+    } else {
+	HTAlert(MALFORMED_EXEC_REQUEST);
+	return FALSE;
+    }
+
+#ifdef VMS
+    /*
+     * Security:  reject on relative path.
+     */
+    if ((cp = strchr(linktext, '[')) != NULL) {
+	char *cp1;
+
+	if (((cp1 = strchr(cp, '-')) != NULL) &&
+	    strchr(cp1, ']') != NULL) {
+	    while (cp1[1] == '-')
+		cp1++;
+	    if (cp1[1] == ']' ||
+		cp1[1] == '.') {
+		HTAlert(RELPATH_IN_EXEC_LINK);
+		return FALSE;
+	    }
+	}
+    }
+#else
+    /*
+     * Security:  reject on relative path.
+     */
+    if (strstr(linktext, "../") != NULL) {
+	HTAlert(RELPATH_IN_EXEC_LINK);
+	return FALSE;
+    }
+
+    /*
+     * Security:  reject on strange character.
+     */
+    if (Type == CGI_PATH)
+	allowed_extra_chars = " _-:./@~$&+=\t";
+    else
+	allowed_extra_chars = " _-:./@~$+=\t";
+    for (cp = linktext; *cp != '\0'; cp++) {
+	if (!isalnum(UCH(*cp)) && !strchr(allowed_extra_chars, *cp)) {
+	    char *buf = 0;
+
+	    HTSprintf0(&buf,
+		       BADCHAR_IN_EXEC_LINK,
+		       *cp);
+	    HTAlert(buf);
+	    FREE(buf);
+	    return FALSE;
+	}
+    }
+#endif /* VMS */
+
+  check_tp_for_entry:
+    while (tp) {
+	if (tp->type == Type) {
+	    char const *command = linktext;
+
+	    if (strstr(command, "//") == linktext) {
+		command += 2;
+	    }
+	    CTRACE((tfp, "comparing source\n\t'%s'\n\t'%s'\n", source, tp->src));
+	    CTRACE((tfp, "comparing command\n\t'%s'\n\t'%s'\n", command, tp->path));
+	    if (STRNADDRCOMP(source, tp->src, strlen(tp->src)) == 0 &&
+		STRNADDRCOMP(command, tp->path, strlen(tp->path)) == 0)
+		return TRUE;
+	}
+	tp = tp->next;
+    }
+    if (Type == EXEC_PATH &&
+	always_trusted_exec != &always_trusted_exec_default) {
+	Type = ALWAYS_EXEC_PATH;
+	tp = always_trusted_exec;
+	goto check_tp_for_entry;
+    }
+    if (!(no_exec && type == ALWAYS_EXEC_PATH))
+	HTAlert(BADLOCPATH_IN_EXEC_LINK);
+    return FALSE;
+}
+#endif /* EXEC_LINKS || LYNXCGI_LINKS */
+
+static int fix_httplike_urls(DocInfo *doc, UrlTypes type)
+{
+    char *slash;
+
+    /*
+     * If there's a fragment present, our simplistic methods won't work.  - kw
+     */
+    if (findPoundSelector(doc->address) != NULL)
+	return 0;
+
+#ifndef DISABLE_FTP
+    /*
+     * If it's an ftp URL with a trailing slash, trim it off.
+     */
+    if (type == FTP_URL_TYPE &&
+	LYIsHtmlSep(doc->address[strlen(doc->address) - 1])) {
+	char *path = HTParse(doc->address, "", PARSE_PATH | PARSE_PUNCTUATION);
+
+	/*
+	 * If the path is a lone slash, we're done.  - FM
+	 */
+	if (path) {
+	    if (LYIsHtmlSep(path[0]) && path[1] == '\0') {
+		FREE(path);
+		return 0;
+	    }
+	    FREE(path);
+	}
+
+	/*
+	 * If we're proxying ftp, don't trim anything.  - KW
+	 */
+	if ((LYGetEnv("ftp_proxy") != NULL) &&
+	    !override_proxy(doc->address))
+	    return 0;
+
+	/*
+	 * If we get to here, trim the trailing slash.  - FM
+	 */
+	CTRACE((tfp, "fix_httplike_urls: URL '%s'\n", doc->address));
+	LYTrimHtmlSep(doc->address);
+	CTRACE((tfp, "            changed to '%s'\n", doc->address));
+	CTRACE_SLEEP(MessageSecs);
+    } else if (type == NCFTP_URL_TYPE) {
+	char *path = NULL;
+	char *first = doc->address;
+	char *second = strchr(first, ':');
+
+	CTRACE((tfp, "fix_httplike_urls: URL '%s'\n", doc->address));
+
+	*second++ = '\0';
+	HTSprintf0(&path, "%s//%s%s", STR_FTP_URL, first, second);
+	FREE(doc->address);
+	doc->address = path;
+
+	CTRACE((tfp, "            changed to '%s'\n", doc->address));
+	CTRACE_SLEEP(MessageSecs);
+    }
+#endif /* DISABLE_FTP */
+
+    /*
+     * If there isn't a slash besides the two at the beginning, append one.
+     */
+    if ((slash = strrchr(doc->address, '/')) != NULL) {
+	if (!LYIsHtmlSep(*(slash - 1)) || *(slash - 2) != ':') {
+	    return (0);
+	}
+	if (type == HTTP_URL_TYPE ||
+	    type == HTTPS_URL_TYPE) {
+	    if ((slash - 2) != strchr(doc->address, ':')) {
+		/*
+		 * Turns out we were not looking at the right slash after all,
+		 * there must have been more than one "://" which is valid at
+		 * least for http URLs (later occurrences can be part of a
+		 * query string, for example), so leave this alone, too.  - kw
+		 */
+		return (0);
+	    }
+	    if (strchr(doc->address, '?')) {
+		/*
+		 * If there is a question mark that appears to be part of the
+		 * hostname, don't append anything either.  Leave it to HTParse
+		 * to interpret the question mark as ending the hostname.  - kw
+		 */
+		return (0);
+	    }
+	}
+    }
+    CTRACE((tfp, "fix_httplike_urls: URL '%s'\n", doc->address));
+    LYAddHtmlSep(&(doc->address));
+    CTRACE((tfp, "            changed to '%s'\n", doc->address));
+    CTRACE_SLEEP(MessageSecs);
+
+    return (1);
+}
diff --git a/src/LYGetFile.h b/src/LYGetFile.h
new file mode 100644
index 00000000..f204d07c
--- /dev/null
+++ b/src/LYGetFile.h
@@ -0,0 +1,38 @@
+#ifndef LYGETFILE_H
+#define LYGETFILE_H
+
+#include <LYStructs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#define NOT_FOUND 0
+#define NORMAL 1
+#define NULLFILE 3
+    extern int getfile(DocInfo *doc, int *target);
+    extern void srcmode_for_next_retrieval(int);
+    extern int follow_link_number(int c,
+				  int cur,
+				  DocInfo *doc,
+				  int *num);
+    extern void add_trusted(char *str, int type);
+    extern BOOLEAN exec_ok(const char *source, const char *linkpath, int type);
+
+    extern char *WWW_Download_File;
+
+/* values for follow_link_number() */
+#define DO_LINK_STUFF		1
+#define DO_GOTOLINK_STUFF	2
+#define DO_GOTOPAGE_STUFF	3
+#define DO_FORMS_STUFF		4
+#define PRINT_ERROR		5
+
+/* values for add_trusted() and exec_ok() */
+#define EXEC_PATH 0
+#define ALWAYS_EXEC_PATH  1
+#define CGI_PATH  2
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYGETFILE_H */
diff --git a/src/LYGlobalDefs.h b/src/LYGlobalDefs.h
new file mode 100644
index 00000000..fa92e044
--- /dev/null
+++ b/src/LYGlobalDefs.h
@@ -0,0 +1,689 @@
+/*
+ * $LynxId: LYGlobalDefs.h,v 1.122 2009/11/21 15:24:48 tom Exp $
+ *
+ * global variable definitions
+ */
+
+#ifndef LYGLOBALDEFS_H
+#define LYGLOBALDEFS_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif /* HTUTILS_H */
+
+#ifndef LYSTRUCTS_H
+#include <LYStructs.h>
+#endif /* LYSTRUCTS_H */
+
+/* Of the following definitions, currently unused are and could
+   be removed (at least):
+   CURRENT_KEYMAP_HELP
+*/
+#if defined(HAVE_CONFIG_H) && defined(HAVE_LYHELP_H)
+#include <LYHelp.h>
+#else
+#define ALT_EDIT_HELP		"keystrokes/alt_edit_help.html"
+#define BASHLIKE_EDIT_HELP	"keystrokes/bashlike_edit_help.html"
+#define COOKIE_JAR_HELP		"Lynx_users_guide.html#Cookies"
+#define CACHE_JAR_HELP		"Lynx_users_guide.html#Cache"
+#define CURRENT_KEYMAP_HELP	"keystrokes/keystroke_help.html"
+#define DIRED_MENU_HELP		"keystrokes/dired_help.html"
+#define DOWNLOAD_OPTIONS_HELP	"Lynx_users_guide.html#RemoteSource"
+#define EDIT_HELP		"keystrokes/edit_help.html"
+#define HISTORY_PAGE_HELP	"keystrokes/history_help.html"
+#define LIST_PAGE_HELP		"keystrokes/follow_help.html"
+#define LYNXCFG_HELP		"lynx.cfg"
+#define OPTIONS_HELP		"keystrokes/option_help.html"
+#define PRINT_OPTIONS_HELP	"keystrokes/print_help.html"
+#define UPLOAD_OPTIONS_HELP	"Lynx_users_guide.html#DirEd"
+#define VISITED_LINKS_HELP	"keystrokes/visited_help.html"
+#endif /* LYHELP_H */
+
+#ifdef USE_SOURCE_CACHE
+#include <HTChunk.h>
+#endif
+
+#include <LYMail.h>		/* to get ifdef's for mail-variables */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef SOCKS
+    extern BOOLEAN socks_flag;
+    extern unsigned long socks_bind_remoteAddr;
+#endif				/* SOCKS */
+
+#ifdef IGNORE_CTRL_C
+    extern BOOLEAN sigint;
+#endif				/* IGNORE_CTRL_C */
+
+#if USE_VMS_MAILER
+    extern char *mail_adrs;
+    extern BOOLEAN UseFixedRecords;	/* convert binary files to FIXED 512 records */
+#endif				/* VMS */
+
+#ifndef VMS
+    extern char *list_format;
+#endif				/* !VMS */
+    extern char *ftp_format;
+
+    typedef enum {
+	BAD_HTML_IGNORE = 0
+	,BAD_HTML_TRACE
+	,BAD_HTML_MESSAGE
+	,BAD_HTML_WARN
+    } enumBadHtml;
+
+    extern int cfg_bad_html;	/* enumBadHtml */
+
+#ifdef DIRED_SUPPORT
+
+    typedef enum {
+	DIRS_FIRST = 0
+	,FILES_FIRST
+	,MIXED_STYLE
+    } enumDirListStyle;
+
+    typedef enum {
+	ORDER_BY_NAME
+	,ORDER_BY_SIZE
+	,ORDER_BY_DATE
+	,ORDER_BY_MODE
+	,ORDER_BY_TYPE
+	,ORDER_BY_USER
+	,ORDER_BY_GROUP
+    } enumDirListOrder;
+
+    extern BOOLEAN lynx_edit_mode;
+    extern BOOLEAN no_dired_support;
+    extern HTList *tagged;
+    extern int LYAutoUncacheDirLists;
+    extern int dir_list_style;	/* enumDirListStyle */
+    extern int dir_list_order;	/* enumDirListOrder */
+
+#ifdef OK_OVERRIDE
+    extern BOOLEAN prev_lynx_edit_mode;
+#endif				/* OK_OVERRIDE */
+
+#ifdef OK_PERMIT
+    extern BOOLEAN no_change_exec_perms;
+#endif				/* OK_PERMIT */
+
+#endif				/* DIRED_SUPPORT */
+
+    extern int HTCacheSize;	/* the number of documents cached in memory */
+
+#if defined(VMS) && defined(VAXC) && !defined(__DECC)
+    extern int HTVirtualMemorySize;	/* bytes allocated and not yet freed  */
+#endif				/* VMS && VAXC && !__DECC */
+
+#if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)
+    extern BOOLEAN local_exec;	/* TRUE to enable local program execution */
+    extern BOOLEAN local_exec_on_local_files;	/* TRUE to enable local program  *
+
+						 * execution in local files only */
+#endif				/* defined(EXEC_LINKS) || defined(EXEC_SCRIPTS) */
+
+#if defined(LYNXCGI_LINKS) && !defined(VMS)	/* WebSter Mods -jkt */
+    extern char *LYCgiDocumentRoot;	/* DOCUMENT_ROOT in the lynxcgi env */
+#endif				/* LYNXCGI_LINKS */
+
+/* Values to which keypad_mode can be set */
+#define NUMBERS_AS_ARROWS 0
+#define LINKS_ARE_NUMBERED 1
+#define LINKS_AND_FIELDS_ARE_NUMBERED 2
+#define FIELDS_ARE_NUMBERED 3
+
+#define links_are_numbered() \
+	    (keypad_mode == LINKS_ARE_NUMBERED || \
+	     keypad_mode == LINKS_AND_FIELDS_ARE_NUMBERED)
+
+#define fields_are_numbered() \
+	    (keypad_mode == FIELDS_ARE_NUMBERED || \
+	     keypad_mode == LINKS_AND_FIELDS_ARE_NUMBERED)
+
+#define HIDDENLINKS_MERGE	0
+#define HIDDENLINKS_SEPARATE	1
+#define HIDDENLINKS_IGNORE	2
+
+#define NOVICE_MODE 	  0
+#define INTERMEDIATE_MODE 1
+#define ADVANCED_MODE 	  2
+    extern BOOLEAN LYUseNoviceLineTwo;	/* True if TOGGLE_HELP is not mapped */
+
+#define MAX_LINE 1024		/* No window can be wider than this */
+#define MAX_COLS (MAX_LINE-10)	/* we don't expect wider than this */
+#define DFT_COLS 80		/* ...and normally only this */
+#define DFT_ROWS 24		/* ...corresponding nominal height */
+
+    extern char star_string[MAX_LINE + 1];	/* from GridText.c */
+
+#define STARS(n) \
+ ((n) >= MAX_LINE ? star_string : &star_string[(MAX_LINE-1)] - (n))
+
+    typedef enum {
+	SHOW_COLOR_UNKNOWN = -1
+	,SHOW_COLOR_NEVER = 0	/* positive numbers are index in LYOptions.c */
+	,SHOW_COLOR_OFF
+	,SHOW_COLOR_ON
+	,SHOW_COLOR_ALWAYS
+    } enumShowColor;
+
+    extern int LYShowColor;	/* Show color or monochrome?        */
+    extern int LYrcShowColor;	/* ... as read or last written      */
+
+    typedef enum {
+	MBM_OFF = 0
+	,MBM_STANDARD
+	,MBM_ADVANCED
+    } enumMultiBookmarks;
+
+#if !defined(NO_OPTION_FORMS) && !defined(NO_OPTION_MENU)
+    extern BOOLEAN LYUseFormsOptions;	/* use Forms-based options menu */
+
+#else
+#define LYUseFormsOptions FALSE	/* simplify ifdef'ing in LYMainLoop.c */
+#endif
+
+    typedef enum {
+	rateOFF = 0
+	,rateBYTES = 1
+	,rateKB
+#ifdef USE_READPROGRESS
+	,rateEtaBYTES
+	,rateEtaKB
+#endif
+#ifdef USE_PROGRESSBAR
+	,rateBAR
+#endif
+    } TransferRate;
+
+#ifdef USE_READPROGRESS
+#  define rateEtaKB_maybe	rateEtaKB
+#else
+#  define rateEtaKB_maybe	rateKB
+#endif
+
+#define TITLE_LINES  1
+
+    extern BOOLEAN LYCursesON;	/* start_curses()->TRUE, stop_curses()->FALSE */
+    extern BOOLEAN LYJumpFileURL;	/* URL from the jump file shortcuts? */
+    extern BOOLEAN LYNewsPosting;	/* News posting supported if TRUE */
+#ifdef USE_SESSIONS
+    extern BOOLEAN LYAutoSession;	/* Auto restore/save session? */
+#endif
+    extern BOOLEAN LYShowCursor;	/* Show the cursor or hide it?      */
+    extern BOOLEAN LYShowTransferRate;
+    extern BOOLEAN LYUnderlineLinks;	/* Show the links underlined vs bold */
+    extern BOOLEAN LYUseDefShoCur;	/* Command line -show_cursor toggle */
+    extern BOOLEAN LYUserSpecifiedURL;	/* URL from a goto or document? */
+    extern BOOLEAN LYfind_leaks;
+    extern BOOLEAN LYforce_HTML_mode;
+    extern BOOLEAN LYforce_no_cache;
+    extern BOOLEAN LYinternal_flag;	/* don't need fresh copy, was internal link */
+    extern BOOLEAN LYoverride_no_cache;		/* don't need fresh copy, from history */
+    extern BOOLEAN LYresubmit_posts;
+    extern BOOLEAN LYtrimInputFields;
+    extern BOOLEAN LYxhtml_parsing;
+    extern BOOLEAN bold_H1;
+    extern BOOLEAN bold_headers;
+    extern BOOLEAN bold_name_anchors;
+    extern BOOLEAN case_sensitive;	/* TRUE to turn on case sensitive search */
+    extern BOOLEAN check_mail;	/* TRUE to report unread/new mail messages */
+    extern BOOLEAN child_lynx;	/* TRUE to exit with an arrow */
+    extern BOOLEAN dump_links_only;
+    extern BOOLEAN dump_output_immediately;
+    extern BOOLEAN dump_to_stderr;
+    extern BOOLEAN emacs_keys;	/* TRUE to turn on emacs-like key movement */
+    extern BOOLEAN error_logging;	/* TRUE to mail error messages */
+    extern BOOLEAN ftp_ok;
+    extern BOOLEAN goto_buffer;	/* TRUE if offering default goto URL */
+    extern BOOLEAN is_www_index;
+    extern BOOLEAN jump_buffer;	/* TRUE if offering default shortcut */
+    extern BOOLEAN long_url_ok;
+    extern BOOLEAN lynx_mode;
+    extern BOOLEAN more_text;	/* is there more document to display? */
+    extern BOOLEAN news_ok;
+    extern BOOLEAN number_fields_on_left;
+    extern BOOLEAN number_links_on_left;
+    extern BOOLEAN recent_sizechange;
+    extern BOOLEAN rlogin_ok;
+    extern BOOLEAN syslog_requested_urls;
+    extern BOOLEAN system_editor;	/* True if locked-down editor */
+    extern BOOLEAN telnet_ok;
+    extern BOOLEAN verbose_img;	/* display filenames of images?     */
+    extern BOOLEAN vi_keys;	/* TRUE to turn on vi-like key movement */
+
+    extern HTList *Goto_URLs;
+    extern HTList *positionable_editor;
+
+    extern char *LYRequestReferer;	/* Referer, may be set in getfile() */
+    extern char *LYRequestTitle;	/* newdoc.title in calls to getfile() */
+    extern char *LYTransferName;	/* abbreviation for Kilobytes */
+    extern char *LynxHome;
+#ifdef USE_SESSIONS
+    extern char *LYSessionFile;	/* file for auto-session */
+    extern char *session_file;	/* file for -session= */
+    extern char *sessionin_file;	/* file for -sessionin= */
+    extern char *sessionout_file;	/* file for -sessionout= */
+#endif
+    extern char *LynxSigFile;	/* Signature file, in or off home */
+    extern char *helpfile;
+    extern char *helpfilepath;
+    extern char *jumpprompt;	/* The default jump statusline prompt */
+    extern char *language;
+    extern char *lynx_cfg_file;	/* location of active lynx.cfg file */
+    extern char *lynx_cmd_logfile;	/* file to write keystroke commands, if any */
+    extern char *lynx_cmd_script;	/* file to read keystroke commands, if any */
+    extern char *lynx_save_space;
+    extern char *lynx_temp_space;
+    extern char *lynxjumpfile;
+    extern char *lynxlinksfile;
+    extern char *lynxlistfile;
+    extern char *original_dir;
+    extern char *pref_charset;	/* Lynx's preferred character set - MM */
+    extern char *startfile;
+    extern char *syslog_txt;	/* syslog arb text for session */
+    extern char *system_mail;
+    extern char *system_mail_flags;
+    extern char *x_display;
+    extern char empty_string[];
+
+    extern const char *checked_box;	/* form boxes */
+    extern const char *checked_radio;	/* form radio buttons */
+    extern const char *unchecked_box;	/* form boxes */
+    extern const char *unchecked_radio;		/* form radio buttons */
+
+    extern int LYAcceptEncoding;
+    extern int LYAcceptMedia;
+    extern int LYTransferRate;	/* see enum TransferRate */
+    extern int display_lines;	/* number of lines in the display */
+    extern int dump_output_width;
+    extern int dump_server_status;
+    extern int keypad_mode;	/* NUMBERS_AS_ARROWS or LINKS_ARE_NUMBERED */
+    extern int lynx_temp_subspace;
+    extern int max_cookies_buffer;
+    extern int max_cookies_domain;
+    extern int max_cookies_global;
+#ifdef USE_SESSIONS
+    extern short session_limit;	/* maximal entries saved/restored
+				   in session file */
+#endif
+    extern int user_mode;	/* novice or advanced */
+    extern int www_search_result;
+
+    extern BOOLEAN exec_frozen;
+    extern BOOLEAN had_restrictions_all;	/* parsed these restriction options */
+    extern BOOLEAN had_restrictions_default;	/* flags to note whether we have... */
+    extern BOOLEAN no_bookmark;
+    extern BOOLEAN no_bookmark_exec;
+    extern BOOLEAN no_chdir;
+    extern BOOLEAN no_compileopts_info;
+    extern BOOLEAN no_disk_save;
+    extern BOOLEAN no_dotfiles;
+    extern BOOLEAN no_download;
+    extern BOOLEAN no_editor;
+    extern BOOLEAN no_exec;
+    extern BOOLEAN no_file_url;
+    extern BOOLEAN no_goto;
+    extern BOOLEAN no_goto_configinfo;
+    extern BOOLEAN no_goto_cso;
+    extern BOOLEAN no_goto_file;
+    extern BOOLEAN no_goto_finger;
+    extern BOOLEAN no_goto_ftp;
+    extern BOOLEAN no_goto_gopher;
+    extern BOOLEAN no_goto_http;
+    extern BOOLEAN no_goto_https;
+    extern BOOLEAN no_goto_lynxcgi;
+    extern BOOLEAN no_goto_lynxexec;
+    extern BOOLEAN no_goto_lynxprog;
+    extern BOOLEAN no_goto_mailto;
+    extern BOOLEAN no_goto_news;
+    extern BOOLEAN no_goto_nntp;
+    extern BOOLEAN no_goto_rlogin;
+    extern BOOLEAN no_goto_snews;
+    extern BOOLEAN no_goto_telnet;
+    extern BOOLEAN no_goto_tn3270;
+    extern BOOLEAN no_goto_wais;
+    extern BOOLEAN no_inside_ftp;
+    extern BOOLEAN no_inside_news;
+    extern BOOLEAN no_inside_rlogin;
+    extern BOOLEAN no_inside_telnet;	/* this and following are restrictions */
+    extern BOOLEAN no_jump;
+    extern BOOLEAN no_lynxcfg_info;
+    extern BOOLEAN no_lynxcfg_xinfo;
+    extern BOOLEAN no_lynxcgi;
+    extern BOOLEAN no_mail;
+    extern BOOLEAN no_multibook;
+    extern BOOLEAN no_newspost;
+    extern BOOLEAN no_option_save;
+    extern BOOLEAN no_outside_ftp;
+    extern BOOLEAN no_outside_news;
+    extern BOOLEAN no_outside_rlogin;
+    extern BOOLEAN no_outside_telnet;
+    extern BOOLEAN no_print;	/* TRUE to disable printing */
+    extern BOOLEAN no_shell;
+    extern BOOLEAN no_suspend;
+    extern BOOLEAN no_telnet_port;
+    extern BOOLEAN no_useragent;
+
+    extern BOOLEAN no_statusline;
+    extern BOOLEAN no_filereferer;
+    extern char LYRefererWithQuery;	/* 'S', 'P', or 'D' */
+    extern BOOLEAN local_host_only;
+    extern BOOLEAN override_no_download;
+    extern BOOLEAN show_dotfiles;	/* From rcfile if no_dotfiles is false */
+    extern char *indexfile;
+    extern char *anonftp_password;
+    extern char *personal_mail_address;
+    extern char *homepage;	/* startfile or command line argument */
+    extern char *editor;	/* if non empty it enables edit mode with
+
+				 * the editor that is named */
+    extern char *jumpfile;
+    extern char *bookmark_page;
+    extern char *BookmarkPage;
+    extern char *personal_type_map;
+    extern char *global_type_map;
+    extern char *global_extension_map;
+    extern char *personal_extension_map;
+    extern char *LYHostName;
+    extern char *LYLocalDomain;
+    extern BOOLEAN use_underscore;
+    extern BOOLEAN no_list;
+    extern BOOLEAN no_margins;
+    extern BOOLEAN no_pause;
+    extern BOOLEAN no_title;
+    extern BOOLEAN historical_comments;
+    extern BOOLEAN minimal_comments;
+    extern BOOLEAN soft_dquotes;
+
+#ifdef USE_SOURCE_CACHE
+    extern BOOLEAN source_cache_file_error;
+    extern int LYCacheSource;
+
+#define SOURCE_CACHE_NONE	0
+#define SOURCE_CACHE_FILE	1
+#define SOURCE_CACHE_MEMORY	2
+
+    extern int LYCacheSourceForAborted;
+
+#define SOURCE_CACHE_FOR_ABORTED_KEEP 1
+#define SOURCE_CACHE_FOR_ABORTED_DROP 0
+#endif
+
+    extern BOOLEAN LYCancelDownload;
+    extern BOOLEAN LYRestricted;	/* whether we had -anonymous option */
+    extern BOOLEAN LYValidate;
+    extern BOOLEAN LYPermitURL;
+    extern BOOLEAN enable_scrollback;	/* Clear screen before displaying new page */
+    extern BOOLEAN keep_mime_headers;	/* Include mime headers and *
+
+					 * force source dump              */
+    extern BOOLEAN no_url_redirection;	/* Don't follow URL redirections */
+
+#ifdef DISP_PARTIAL
+    extern BOOLEAN display_partial;	/* Display document while loading */
+    extern int NumOfLines_partial;	/* -//- "current" number of lines */
+    extern int partial_threshold;
+    extern BOOLEAN debug_display_partial;	/* show with MessageSecs delay */
+    extern BOOLEAN display_partial_flag;	/* permanent flag, not mutable */
+#endif
+    extern char *form_post_data;	/* User data for post form */
+    extern char *form_get_data;	/* User data for get form */
+    extern char *http_error_file;	/* Place HTTP status code in this file */
+    extern char *authentication_info[2];	/* Id:Password for protected documents */
+    extern char *proxyauth_info[2];	/* Id:Password for protected proxy server */
+    extern BOOLEAN HEAD_request;	/* Do a HEAD request */
+    extern BOOLEAN scan_for_buried_news_references;
+    extern BOOLEAN bookmark_start;	/* Use bookmarks as startfile */
+    extern BOOLEAN clickable_images;
+    extern BOOLEAN nested_tables;
+    extern BOOLEAN pseudo_inline_alts;
+    extern BOOLEAN crawl;
+    extern BOOLEAN traversal;
+    extern BOOLEAN check_realm;
+    extern char *startrealm;
+    extern BOOLEAN more_links;
+    extern int crawl_count;
+    extern BOOLEAN LYCancelledFetch;
+    extern const char *LYToolbarName;
+
+    extern BOOLEAN nomore;
+    extern int AlertSecs;
+    extern int InfoSecs;
+    extern int MessageSecs;
+    extern int DebugSecs;
+    extern int ReplaySecs;
+
+    extern char *LYUserAgent;	/* Lynx User-Agent header */
+    extern char *LYUserAgentDefault;	/* Lynx default User-Agent header */
+    extern BOOLEAN LYNoRefererHeader;	/* Never send Referer header? */
+    extern BOOLEAN LYNoRefererForThis;	/* No Referer header for this URL? */
+    extern BOOLEAN LYNoFromHeader;	/* Never send From header?    */
+    extern BOOLEAN LYSendUserAgent;	/* send Lynx User-Agent header? */
+    extern BOOLEAN LYListNewsNumbers;
+    extern BOOLEAN LYUseMouse;
+    extern BOOLEAN LYListNewsDates;
+
+    extern BOOLEAN LYRawMode;
+    extern BOOLEAN LYDefaultRawMode;
+    extern BOOLEAN LYUseDefaultRawMode;
+    extern char *UCAssume_MIMEcharset;
+    extern BOOLEAN UCSaveBookmarksInUnicode;	/* in titles,  chars >127 save as &#xUUUU */
+    extern BOOLEAN UCForce8bitTOUPPER;	/* disable locale case-conversion for >127 */
+    extern int outgoing_mail_charset;	/* translate outgoing mail to this charset */
+
+    extern BOOLEAN LYisConfiguredForX;
+    extern char *URLDomainPrefixes;
+    extern char *URLDomainSuffixes;
+    extern BOOLEAN startfile_ok;
+    extern BOOLEAN LYSelectPopups;	/* Cast popups to radio buttons? */
+    extern BOOLEAN LYUseDefSelPop;	/* Command line -popup toggle    */
+    extern int LYMultiBookmarks;	/* Multi bookmark support on?    */
+    extern BOOLEAN LYMBMBlocked;	/* Force MBM support off?        */
+    extern int LYStatusLine;	/* Line for statusline() or -1   */
+    extern BOOLEAN LYCollapseBRs;	/* Collapse serial BRs?          */
+    extern BOOLEAN LYSetCookies;	/* Process Set-Cookie headers?   */
+    extern BOOLEAN LYAcceptAllCookies;	/* accept ALL cookies?           */
+
+    extern char *LYCookieAcceptDomains;		/* domains to accept all cookies */
+    extern char *LYCookieRejectDomains;		/* domains to reject all cookies */
+    extern char *LYCookieStrictCheckDomains;	/* domains to check strictly    */
+    extern char *LYCookieLooseCheckDomains;	/* domains to check loosely      */
+    extern char *LYCookieQueryCheckDomains;	/* domains to check w/a query    */
+    extern char *LYCookieSAcceptDomains;	/* domains to accept all cookies */
+    extern char *LYCookieSRejectDomains;	/* domains to reject all cookies */
+    extern char *LYCookieSStrictCheckDomains;	/* domains to check strictly    */
+    extern char *LYCookieSLooseCheckDomains;	/* domains to check loosely      */
+    extern char *LYCookieSQueryCheckDomains;	/* domains to check w/a query    */
+
+#ifndef DISABLE_BIBP
+    extern BOOLEAN no_goto_bibp;
+    extern char *BibP_globalserver;	/* global server for bibp: links */
+    extern char *BibP_bibhost;	/* local server for bibp: links  */
+    extern BOOLEAN BibP_bibhost_checked;	/* bibhost has been checked      */
+    extern BOOLEAN BibP_bibhost_available;	/* bibhost is responding         */
+#endif
+
+#ifndef DISABLE_FTP
+    extern BOOLEAN ftp_local_passive;
+    extern BOOLEAN ftp_passive;	/* TRUE if we want to use passive mode ftp */
+    extern HTList *broken_ftp_epsv;
+    extern HTList *broken_ftp_retr;
+    extern char *ftp_lasthost;
+#endif
+
+#ifdef USE_PERSISTENT_COOKIES
+    extern BOOLEAN persistent_cookies;
+    extern char *LYCookieFile;	/* cookie read file              */
+    extern char *LYCookieSaveFile;	/* cookie save file              */
+#endif				/* USE_PERSISTENT_COOKIES */
+
+    extern char *XLoadImageCommand;	/* Default image viewer for X    */
+
+#ifdef USE_EXTERNALS
+    extern BOOLEAN no_externals;	/* don't allow the use of externals */
+#endif
+
+    extern BOOLEAN LYNoISMAPifUSEMAP;	/* Omit ISMAP link if MAP present? */
+    extern int LYHiddenLinks;
+
+    extern char *SSL_cert_file;	/* Default CA CERT file */
+
+    extern int Old_DTD;
+
+#define MBM_V_MAXFILES  25	/* Max number of sub-bookmark files */
+
+/*
+ *  Arrays that holds the names of sub-bookmark files
+ *  and their descriptions.
+ */
+    extern char *MBM_A_subbookmark[MBM_V_MAXFILES + 1];
+    extern char *MBM_A_subdescript[MBM_V_MAXFILES + 1];
+
+    extern BOOLEAN LYForceSSLCookiesSecure;
+    extern BOOLEAN LYNoCc;
+    extern BOOLEAN LYNonRestartingSIGWINCH;
+    extern BOOLEAN LYPreparsedSource;	/* Show source as preparsed?     */
+    extern BOOLEAN LYPrependBaseToSource;
+    extern BOOLEAN LYPrependCharsetToSource;
+    extern BOOLEAN LYQuitDefaultYes;
+    extern BOOLEAN LYReuseTempfiles;
+    extern BOOLEAN LYSeekFragAREAinCur;
+    extern BOOLEAN LYSeekFragMAPinCur;
+    extern BOOLEAN LYStripDotDotURLs;	/* Try to fix ../ in some URLs?  */
+    extern BOOLEAN LYUseBuiltinSuffixes;
+    extern BOOLEAN dont_wrap_pre;
+
+    extern int cookie_noprompt;
+
+    typedef enum {
+	FORCE_PROMPT_DFT	/* force a prompt, use the result */
+	,FORCE_PROMPT_YES	/* assume "yes" where a prompt would be used */
+	,FORCE_PROMPT_NO	/* assume "no" where a prompt would be used */
+    } FORCE_PROMPT;
+
+#ifdef USE_SSL
+    extern int ssl_noprompt;
+#endif
+
+#ifdef MISC_EXP
+    extern int LYNoZapKey;	/* 0: off (do 'z' checking), 1: full, 2: initially */
+#endif
+
+#ifdef USE_JUSTIFY_ELTS
+    extern BOOLEAN ok_justify;
+    extern int justify_max_void_percent;
+#endif
+
+#ifdef USE_LOCALE_CHARSET
+    extern BOOLEAN LYLocaleCharset;
+#endif
+
+#ifndef NO_DUMP_WITH_BACKSPACES
+    extern BOOLEAN with_backspaces;
+#endif
+
+#if defined(PDCURSES) && defined(PDC_BUILD) && PDC_BUILD >= 2401
+    extern int scrsize_x;
+    extern int scrsize_y;
+#endif
+
+#ifndef NO_LYNX_TRACE
+    extern FILE *LYTraceLogFP;	/* Pointer for TRACE log         */
+    extern char *LYTraceLogPath;	/* Path for TRACE log            */
+#endif
+    extern BOOLEAN LYUseTraceLog;	/* Use a TRACE log?              */
+
+    extern BOOLEAN force_empty_hrefless_a;
+    extern int connect_timeout;
+    extern int reading_timeout;
+
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+    extern BOOL textfields_need_activation;
+    extern BOOLEAN textfields_activation_option;
+
+#ifdef INACTIVE_INPUT_STYLE_VH
+    extern BOOL textinput_redrawn;
+#endif
+#else
+#define textfields_need_activation FALSE
+#endif				/* TEXTFIELDS_MAY_NEED_ACTIVATION */
+
+    extern BOOLEAN textfield_prompt_at_left_edge;
+
+#ifndef VMS
+    extern BOOLEAN LYNoCore;
+    extern BOOLEAN restore_sigpipe_for_children;
+#endif				/* !VMS */
+
+#if defined(USE_COLOR_STYLE)
+    extern char *lynx_lss_file;
+#endif
+
+#ifdef USE_DEFAULT_COLORS
+    extern BOOLEAN LYuse_default_colors;
+#endif
+
+    extern int HTNoDataOK;	/* HT_NO_DATA-is-ok hack */
+    extern BOOLEAN FileInitAlreadyDone;
+
+#ifdef __DJGPP__
+    extern BOOLEAN watt_debug;
+    extern BOOLEAN dj_is_bash;
+#endif				/* __DJGPP__ */
+
+#ifdef WIN_EX
+/* LYMain.c */
+    extern BOOLEAN focus_window;
+    extern BOOLEAN system_is_NT;
+    extern char windows_drive[4];
+    extern int lynx_timeout;
+    CRITICAL_SECTION critSec_DNS;
+    CRITICAL_SECTION critSec_READ;
+#endif				/* _WINDOWS */
+
+    extern BOOLEAN show_cfg;
+    extern BOOLEAN no_table_center;
+
+#if USE_BLAT_MAILER
+    extern BOOLEAN mail_is_blat;
+#endif
+
+#if defined(__CYGWIN__)
+    extern void cygwin_conv_to_full_win32_path(char *posix, char *dos);
+    extern void cygwin_conv_to_full_posix_path(char *dos, char *posix);
+    extern int setmode(int handle, int amode);
+#endif
+
+#if !defined(__CYGWIN__) && defined(__CYGWIN32__)
+#define __CYGWIN__
+
+#define	cygwin_conv_to_full_win32_path(p, q) \
+	cygwin32_conv_to_full_win32_path(p, q)
+
+#define	cygwin_conv_to_full_posix_path(p, q) \
+	cygwin32_conv_to_full_posix_path(p, q)
+#endif
+
+#ifdef USE_SCROLLBAR
+/* GridText.c */
+    extern BOOLEAN LYShowScrollbar;
+    extern BOOLEAN LYsb_arrow;
+    extern int LYsb_begin;
+    extern int LYsb_end;
+#endif
+
+#ifdef MARK_HIDDEN_LINKS
+    extern char *hidden_link_marker;
+#endif
+
+#ifdef USE_BLINK
+    extern BOOLEAN term_blink_is_boldbg;
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYGLOBALDEFS_H */
diff --git a/src/LYHash.c b/src/LYHash.c
new file mode 100644
index 00000000..4b86c6e6
--- /dev/null
+++ b/src/LYHash.c
@@ -0,0 +1,59 @@
+/*
+ * $LynxId: LYHash.c,v 1.16 2008/12/31 22:10:38 tom Exp $
+ *
+ * A hash table for the (fake) CSS support in Lynx-rp
+ * (c) 1996 Rob Partington
+ * rewritten 1997 by Klaus Weide.
+ */
+#include <LYHash.h>
+#include <LYUtils.h>
+
+#ifdef USE_COLOR_STYLE
+
+/*
+ * This is the same function as the private HASH_FUNCTION() in HTAnchor.c, but
+ * with a different value for HASH_SIZE.
+ */
+
+#define HASH_SIZE CSHASHSIZE
+#define HASH_OF(h, v) ((int)((h) * 3 + UCH(v)) % HASH_SIZE)
+
+int hash_code(const char *string)
+{
+    int hash;
+    const char *p;
+
+    for (p = string, hash = 0; *p; p++)
+	hash = HASH_OF(hash, *p);
+
+    return hash;
+}
+
+int hash_code_lowercase_on_fly(const char *string)
+{
+    int hash;
+    const char *p;
+
+    for (p = string, hash = 0; *p; p++)
+	hash = HASH_OF(hash, TOLOWER(*p));
+
+    return hash;
+}
+
+int hash_code_aggregate_char(char c, int hash)
+{
+    return HASH_OF(hash, c);
+}
+
+int hash_code_aggregate_lower_str(const char *string, int hash_was)
+{
+    int hash;
+    const char *p;
+
+    for (p = string, hash = hash_was; *p; p++)
+	hash = HASH_OF(hash, TOLOWER(*p));
+
+    return hash;
+}
+
+#endif /* USE_COLOR_STYLE */
diff --git a/src/LYHash.h b/src/LYHash.h
new file mode 100644
index 00000000..c60e24d6
--- /dev/null
+++ b/src/LYHash.h
@@ -0,0 +1,83 @@
+/* $LynxId: LYHash.h,v 1.23 2007/07/22 23:08:36 tom Exp $ */
+#ifndef _LYHASH_H_
+#define _LYHASH_H_ 1
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    /* define OMIT_SCN_KEEPING to 1 to omit keeping of Style_className
+     * in HTML.c when lss support is on. 1 to increase performance.
+     */
+#define OMIT_SCN_KEEPING 0
+    struct _hashbucket {
+	char *name;		/* name of this item */
+	int code;		/* code of this item */
+	int color;		/* color highlighting to be done */
+	int mono;		/* mono highlighting to be done */
+	int cattr;		/* attributes to go with the color */
+	struct _hashbucket *next;	/* next item */
+    };
+
+    typedef struct _hashbucket bucket;
+
+#ifndef CSHASHSIZE
+#define CSHASHSIZE 8193
+#endif
+
+#define NOSTYLE -1
+
+    /* hashStyles[] is used in CTRACE when NOSTYLE is passed as 'style' to
+     * curses_w_style
+     */
+    extern bucket hashStyles[CSHASHSIZE];
+    extern int hash_code(const char *string);
+    extern bucket nostyle_bucket;
+
+    extern int hash_code_lowercase_on_fly(const char *string);
+    extern int hash_code_aggregate_char(char c, int hash);
+    extern int hash_code_aggregate_lower_str(const char *c, int hash_was);
+
+    extern int s_a;
+    extern int s_aedit;
+    extern int s_aedit_arr;
+    extern int s_aedit_pad;
+    extern int s_aedit_sel;
+    extern int s_alert;
+    extern int s_alink;
+    extern int s_curedit;
+    extern int s_forw_backw;
+    extern int s_hot_paste;
+    extern int s_menu_active;
+    extern int s_menu_bg;
+    extern int s_menu_entry;
+    extern int s_menu_frame;
+    extern int s_menu_number;
+    extern int s_menu_sb;
+    extern int s_normal;
+    extern int s_prompt_edit;
+    extern int s_prompt_edit_arr;
+    extern int s_prompt_edit_pad;
+    extern int s_prompt_sel;
+    extern int s_status;
+    extern int s_title;
+    extern int s_whereis;
+
+#ifdef USE_SCROLLBAR
+    extern int s_sb_aa;
+    extern int s_sb_bar;
+    extern int s_sb_bg;
+    extern int s_sb_naa;
+#endif
+
+#if OMIT_SCN_KEEPING
+    extern bucket special_bucket;
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* _LYHASH_H_ */
diff --git a/src/LYHistory.c b/src/LYHistory.c
new file mode 100644
index 00000000..f9712534
--- /dev/null
+++ b/src/LYHistory.c
@@ -0,0 +1,1173 @@
+/*
+ * $LynxId: LYHistory.c,v 1.77 2010/04/29 08:55:40 tom Exp $
+ */
+#include <HTUtils.h>
+#include <HTTP.h>
+#include <GridText.h>
+#include <HTAlert.h>
+#include <HText.h>
+#include <LYGlobalDefs.h>
+#include <LYUtils.h>
+#include <LYHistory.h>
+#include <LYPrint.h>
+#include <LYDownload.h>
+#include <LYOptions.h>
+#include <LYKeymap.h>
+#include <LYList.h>
+#include <LYShowInfo.h>
+#include <LYStrings.h>
+#include <LYCharUtils.h>
+#include <LYCharSets.h>
+#include <LYrcFile.h>
+#ifdef DISP_PARTIAL
+#include <LYMainLoop.h>
+#endif
+
+#ifdef DIRED_SUPPORT
+#include <LYUpload.h>
+#include <LYLocal.h>
+#endif /* DIRED_SUPPORT */
+
+#include <LYexit.h>
+#include <LYLeaks.h>
+#include <HTCJK.h>
+
+HTList *Visited_Links = NULL;	/* List of safe popped docs. */
+int Visited_Links_As = VISITED_LINKS_AS_LATEST | VISITED_LINKS_REVERSE;
+
+static VisitedLink *PrevVisitedLink = NULL;	/* NULL on auxillary */
+static VisitedLink *PrevActiveVisitedLink = NULL;	/* Last non-auxillary */
+static VisitedLink Latest_first;
+static VisitedLink Latest_last;
+static VisitedLink *Latest_tree;
+static VisitedLink *First_tree;
+static VisitedLink *Last_by_first;
+
+int nhist_extra;
+
+#ifdef LY_FIND_LEAKS
+static int already_registered_free_messages_stack = 0;
+static int already_registered_clean_all_history = 0;
+#endif
+
+#ifdef LY_FIND_LEAKS
+/*
+ * Utility for freeing the list of visited links.  - FM
+ */
+static void Visited_Links_free(void)
+{
+    VisitedLink *vl;
+    HTList *cur = Visited_Links;
+
+    PrevVisitedLink = NULL;
+    PrevActiveVisitedLink = NULL;
+    if (!cur)
+	return;
+
+    while (NULL != (vl = (VisitedLink *) HTList_nextObject(cur))) {
+	FREE(vl->address);
+	FREE(vl->title);
+	FREE(vl);
+    }
+    HTList_delete(Visited_Links);
+    Visited_Links = NULL;
+    Latest_last.prev_latest = &Latest_first;
+    Latest_first.next_latest = &Latest_last;
+    Last_by_first = Latest_tree = First_tree = 0;
+    return;
+}
+#endif /* LY_FIND_LEAKS */
+
+#ifdef DEBUG
+static void trace_history(const char *tag)
+{
+    if (TRACE) {
+	CTRACE((tfp, "HISTORY %s %d/%d (%d extra)\n",
+		tag, nhist, size_history, nhist_extra));
+	CTRACE_FLUSH(tfp);
+    }
+}
+#else
+#define trace_history(tag)	/* nothing */
+#endif /* DEBUG */
+
+/*
+ * Utility for listing visited links, making any repeated links the most
+ * current in the list.  - FM
+ */
+void LYAddVisitedLink(DocInfo *doc)
+{
+    VisitedLink *tmp;
+    HTList *cur;
+    const char *title = (doc->title ? doc->title : NO_TITLE);
+
+    if (isEmpty(doc->address)) {
+	PrevVisitedLink = NULL;
+	return;
+    }
+
+    /*
+     * Exclude POST or HEAD replies, and bookmark, menu or list files.  - FM
+     */
+    if (doc->post_data || doc->isHEAD || doc->bookmark ||
+	(			/* special url or a temp file */
+	    (!strncmp(doc->address, "LYNX", 4) ||
+	     !strncmp(doc->address, "file://localhost/", 17)))) {
+	int related = 1;	/* First approximation only */
+
+	if (LYIsUIPage(doc->address, UIP_HISTORY) ||
+	    LYIsUIPage(doc->address, UIP_VLINKS) ||
+	    LYIsUIPage(doc->address, UIP_SHOWINFO) ||
+	    isLYNXMESSAGES(doc->address) ||
+	    ((related = 0) != 0) ||
+#ifdef DIRED_SUPPORT
+	    LYIsUIPage(doc->address, UIP_DIRED_MENU) ||
+	    LYIsUIPage(doc->address, UIP_UPLOAD_OPTIONS) ||
+	    LYIsUIPage(doc->address, UIP_PERMIT_OPTIONS) ||
+#endif /* DIRED_SUPPORT */
+	    LYIsUIPage(doc->address, UIP_PRINT_OPTIONS) ||
+	    LYIsUIPage(doc->address, UIP_DOWNLOAD_OPTIONS) ||
+	    LYIsUIPage(doc->address, UIP_OPTIONS_MENU) ||
+	    isLYNXKEYMAP(doc->address) ||
+	    LYIsUIPage(doc->address, UIP_LIST_PAGE) ||
+#ifdef EXP_ADDRLIST_PAGE
+	    LYIsUIPage(doc->address, UIP_ADDRLIST_PAGE) ||
+#endif
+	    LYIsUIPage(doc->address, UIP_CONFIG_DEF) ||
+	    LYIsUIPage(doc->address, UIP_LYNXCFG) ||
+	    isLYNXCOOKIE(doc->address) ||
+	    LYIsUIPage(doc->address, UIP_TRACELOG)) {
+	    if (!related)
+		PrevVisitedLink = NULL;
+	    return;
+	}
+    }
+
+    if (!Visited_Links) {
+	Visited_Links = HTList_new();
+#ifdef LY_FIND_LEAKS
+	atexit(Visited_Links_free);
+#endif
+	Latest_last.prev_latest = &Latest_first;
+	Latest_first.next_latest = &Latest_last;
+	Latest_last.next_latest = NULL;		/* Find bugs quick! */
+	Latest_first.prev_latest = NULL;
+	Last_by_first = Latest_tree = First_tree = NULL;
+    }
+
+    cur = Visited_Links;
+    while (NULL != (tmp = (VisitedLink *) HTList_nextObject(cur))) {
+	if (!strcmp(NonNull(tmp->address),
+		    NonNull(doc->address))) {
+	    PrevVisitedLink = PrevActiveVisitedLink = tmp;
+	    /* Already visited.  Update the last-visited info. */
+	    if (tmp->next_latest == &Latest_last)	/* optimization */
+		return;
+
+	    /* Remove from "latest" chain */
+	    tmp->prev_latest->next_latest = tmp->next_latest;
+	    tmp->next_latest->prev_latest = tmp->prev_latest;
+
+	    /* Insert at the end of the "latest" chain */
+	    Latest_last.prev_latest->next_latest = tmp;
+	    tmp->prev_latest = Latest_last.prev_latest;
+	    tmp->next_latest = &Latest_last;
+	    Latest_last.prev_latest = tmp;
+	    return;
+	}
+    }
+
+    if ((tmp = typecalloc(VisitedLink)) == NULL)
+	outofmem(__FILE__, "LYAddVisitedLink");
+
+    assert(tmp != NULL);
+
+    StrAllocCopy(tmp->address, doc->address);
+    LYformTitle(&(tmp->title), title);
+
+    /* First-visited chain */
+    HTList_appendObject(Visited_Links, tmp);	/* At end */
+    tmp->prev_first = Last_by_first;
+    Last_by_first = tmp;
+
+    /* Tree structure */
+    if (PrevVisitedLink) {
+	VisitedLink *a = PrevVisitedLink;
+	VisitedLink *b = a->next_tree;
+	int l = PrevVisitedLink->level;
+
+	/* Find last on the deeper levels */
+	while (b && b->level > l)
+	    a = b, b = b->next_tree;
+
+	if (!b)			/* a == Latest_tree */
+	    Latest_tree = tmp;
+	tmp->next_tree = a->next_tree;
+	a->next_tree = tmp;
+
+	tmp->level = PrevVisitedLink->level + 1;
+    } else {
+	if (Latest_tree)
+	    Latest_tree->next_tree = tmp;
+	tmp->level = 0;
+	tmp->next_tree = NULL;
+	Latest_tree = tmp;
+    }
+    PrevVisitedLink = PrevActiveVisitedLink = tmp;
+    if (!First_tree)
+	First_tree = tmp;
+
+    /* "latest" chain */
+    Latest_last.prev_latest->next_latest = tmp;
+    tmp->prev_latest = Latest_last.prev_latest;
+    tmp->next_latest = &Latest_last;
+    Latest_last.prev_latest = tmp;
+
+    return;
+}
+
+/*
+ * Returns true if this is a page that we would push onto the stack if not
+ * forced.  If docurl is NULL, only the title is considered; otherwise also
+ * check the URL whether it is (likely to be) a generated special page.
+ */
+BOOLEAN LYwouldPush(const char *title,
+		    const char *docurl)
+{
+    BOOLEAN rc = FALSE;
+
+    /*
+     * All non-pushable generated pages have URLs that begin with
+     * "file://localhost/" and end with HTML_SUFFIX.  - kw
+     */
+    if (docurl) {
+	size_t ulen;
+
+	if (strncmp(docurl, "file://localhost/", 17) != 0 ||
+	    (ulen = strlen(docurl)) <= strlen(HTML_SUFFIX) ||
+	    strcmp(docurl + ulen - strlen(HTML_SUFFIX), HTML_SUFFIX) != 0) {
+	    /*
+	     * If it is not a local HTML file, it may be a Web page that
+	     * accidentally has the same title.  So return TRUE now.  - kw
+	     */
+	    return TRUE;
+	}
+    }
+
+    if (docurl) {
+	rc = (BOOLEAN)
+	    !(LYIsUIPage(docurl, UIP_HISTORY)
+	      || LYIsUIPage(docurl, UIP_PRINT_OPTIONS)
+#ifdef DIRED_SUPPORT
+	      || LYIsUIPage(docurl, UIP_DIRED_MENU)
+	      || LYIsUIPage(docurl, UIP_UPLOAD_OPTIONS)
+	      || LYIsUIPage(docurl, UIP_PERMIT_OPTIONS)
+#endif /* DIRED_SUPPORT */
+	    );
+    } else {
+	rc = (BOOLEAN)
+	    !(!strcmp(title, HISTORY_PAGE_TITLE)
+	      || !strcmp(title, PRINT_OPTIONS_TITLE)
+#ifdef DIRED_SUPPORT
+	      || !strcmp(title, DIRED_MENU_TITLE)
+	      || !strcmp(title, UPLOAD_OPTIONS_TITLE)
+	      || !strcmp(title, PERMIT_OPTIONS_TITLE)
+#endif /* DIRED_SUPPORT */
+	    );
+    }
+    return rc;
+}
+
+/*
+ * Free post-data for 'DocInfo'
+ */
+void LYFreePostData(DocInfo *doc)
+{
+    BStrFree(doc->post_data);
+    FREE(doc->post_content_type);
+}
+
+/*
+ * Free strings associated with a 'DocInfo' struct.
+ */
+void LYFreeDocInfo(DocInfo *doc)
+{
+    FREE(doc->title);
+    FREE(doc->address);
+    FREE(doc->bookmark);
+    LYFreePostData(doc);
+}
+
+/*
+ * Free the information in the last history entry.
+ */
+static void clean_extra_history(void)
+{
+    trace_history("clean_extra_history");
+    nhist += nhist_extra;
+    while (nhist_extra > 0) {
+	nhist--;
+	LYFreeDocInfo(&HDOC(nhist));
+	nhist_extra--;
+    }
+    trace_history("...clean_extra_history");
+}
+
+/*
+ * Free the entire history stack, for auditing memory leaks.
+ */
+#ifdef LY_FIND_LEAKS
+static void clean_all_history(void)
+{
+    trace_history("clean_all_history");
+    clean_extra_history();
+    while (nhist > 0) {
+	nhist--;
+	LYFreeDocInfo(&HDOC(nhist));
+    }
+    trace_history("...clean_all_history");
+}
+#endif
+
+/* FIXME What is the relationship to are_different() from the mainloop?! */
+static int are_identical(HistInfo * doc, DocInfo *doc1)
+{
+    return (STREQ(doc1->address, doc->hdoc.address)
+	    && BINEQ(doc1->post_data, doc->hdoc.post_data)
+	    && !strcmp(NonNull(doc1->bookmark),
+		       NonNull(doc->hdoc.bookmark))
+	    && doc1->isHEAD == doc->hdoc.isHEAD);
+}
+
+void LYAllocHistory(int entries)
+{
+    CTRACE((tfp, "LYAllocHistory %d vs %d\n", entries, size_history));
+    if (entries + 1 >= size_history) {
+	unsigned want;
+	int save = size_history;
+
+	size_history = (entries + 2) * 2;
+	want = (unsigned) size_history *sizeof(*history);
+
+	if (history == 0) {
+	    history = (HistInfo *) malloc(want);
+	} else {
+	    history = (HistInfo *) realloc(history, want);
+	}
+	if (history == 0)
+	    outofmem(__FILE__, "LYAllocHistory");
+
+	assert(history != NULL);
+
+	while (save < size_history) {
+	    memset(&history[save++], 0, sizeof(history[0]));
+	}
+    }
+    CTRACE((tfp, "...LYAllocHistory %d vs %d\n", entries, size_history));
+}
+
+/*
+ * Push the current filename, link and line number onto the history list.
+ */
+int LYpush(DocInfo *doc, BOOLEAN force_push)
+{
+    /*
+     * Don't push NULL file names.
+     */
+    if (*doc->address == '\0')
+	return 0;
+
+    /*
+     * Check whether this is a document we don't push unless forced.  - FM
+     */
+    if (!force_push) {
+	/*
+	 * Don't push the history, printer, or download lists.
+	 */
+	if (!LYwouldPush(doc->title, doc->address)) {
+	    if (!LYforce_no_cache)
+		LYoverride_no_cache = TRUE;
+	    return 0;
+	}
+    }
+
+    /*
+     * If file is identical to one before it, don't push it.
+     * But do not duplicate it if there is only one on the stack,
+     * note that HDOC() starts from 0, so nhist should be > 0.
+     */
+    if (nhist >= 1 && are_identical(&(history[nhist - 1]), doc)) {
+	if (HDOC(nhist - 1).internal_link == doc->internal_link) {
+	    /* But it is nice to have the last position remembered!
+	       - kw */
+	    HDOC(nhist - 1).link = doc->link;
+	    HDOC(nhist - 1).line = doc->line;
+	    return 0;
+	}
+    }
+
+    /*
+     * If file is identical to the current document, just move the pointer.
+     */
+    if (nhist_extra >= 1 && are_identical(&(history[nhist]), doc)) {
+	HDOC(nhist).link = doc->link;
+	HDOC(nhist).line = doc->line;
+	nhist_extra--;
+	LYAllocHistory(nhist);
+	nhist++;
+	trace_history("LYpush: just move the cursor");
+	return 1;
+    }
+
+    clean_extra_history();
+#ifdef LY_FIND_LEAKS
+    if (!already_registered_clean_all_history) {
+	already_registered_clean_all_history = 1;
+	atexit(clean_all_history);
+    }
+#endif
+
+    /*
+     * OK, push it...
+     */
+    LYAllocHistory(nhist);
+    HDOC(nhist).link = doc->link;
+    HDOC(nhist).line = doc->line;
+
+    HDOC(nhist).title = NULL;
+    LYformTitle(&(HDOC(nhist).title), doc->title);
+
+    HDOC(nhist).address = NULL;
+    StrAllocCopy(HDOC(nhist).address, doc->address);
+
+    HDOC(nhist).post_data = NULL;
+    BStrCopy(HDOC(nhist).post_data, doc->post_data);
+
+    HDOC(nhist).post_content_type = NULL;
+    StrAllocCopy(HDOC(nhist).post_content_type, doc->post_content_type);
+
+    HDOC(nhist).bookmark = NULL;
+    StrAllocCopy(HDOC(nhist).bookmark, doc->bookmark);
+
+    HDOC(nhist).isHEAD = doc->isHEAD;
+    HDOC(nhist).safe = doc->safe;
+
+    HDOC(nhist).internal_link = FALSE;	/* by default */
+    history[nhist].intern_seq_start = -1;	/* by default */
+    if (doc->internal_link) {
+	/* Now some tricky stuff: if the caller thinks that the doc
+	   to push was the result of following an internal
+	   (fragment) link, we check whether we believe it.
+	   It is only accepted as valid if the immediately preceding
+	   item on the history stack is actually the same document
+	   except for fragment and location info.  I.e. the Parent
+	   Anchors are the same.
+	   Also of course this requires that this is not the first
+	   history item. - kw */
+	if (nhist > 0) {
+	    DocAddress WWWDoc;
+	    HTParentAnchor *thisparent, *thatparent = NULL;
+
+	    WWWDoc.address = doc->address;
+	    WWWDoc.post_data = doc->post_data;
+	    WWWDoc.post_content_type = doc->post_content_type;
+	    WWWDoc.bookmark = doc->bookmark;
+	    WWWDoc.isHEAD = doc->isHEAD;
+	    WWWDoc.safe = doc->safe;
+	    thisparent =
+		HTAnchor_findAddress(&WWWDoc);
+	    /* Now find the ParentAnchor for the previous history
+	     * item - kw
+	     */
+	    if (thisparent) {
+		/* If the last-pushed item is a LYNXIMGMAP but THIS one
+		 * isn't, compare the physical URLs instead. - kw
+		 */
+		if (isLYNXIMGMAP(HDOC(nhist - 1).address) &&
+		    !isLYNXIMGMAP(doc->address)) {
+		    WWWDoc.address = HDOC(nhist - 1).address + LEN_LYNXIMGMAP;
+		    /*
+		     * If THIS item is a LYNXIMGMAP but the last-pushed one
+		     * isn't, fake it by using THIS item's address for
+		     * thatparent... - kw
+		     */
+		} else if (isLYNXIMGMAP(doc->address) &&
+			   !isLYNXIMGMAP(HDOC(nhist - 1).address)) {
+		    char *temp = NULL;
+
+		    StrAllocCopy(temp, STR_LYNXIMGMAP);
+		    StrAllocCat(temp, doc->address + LEN_LYNXIMGMAP);
+		    WWWDoc.address = temp;
+		    WWWDoc.post_content_type = HDOC(nhist - 1).post_content_type;
+		    WWWDoc.bookmark = HDOC(nhist - 1).bookmark;
+		    WWWDoc.isHEAD = HDOC(nhist - 1).isHEAD;
+		    WWWDoc.safe = HDOC(nhist - 1).safe;
+		    thatparent =
+			HTAnchor_findAddress(&WWWDoc);
+		    FREE(temp);
+		} else {
+		    WWWDoc.address = HDOC(nhist - 1).address;
+		}
+		if (!thatparent) {	/* if not yet done */
+		    WWWDoc.post_data = HDOC(nhist - 1).post_data;
+		    WWWDoc.post_content_type = HDOC(nhist - 1).post_content_type;
+		    WWWDoc.bookmark = HDOC(nhist - 1).bookmark;
+		    WWWDoc.isHEAD = HDOC(nhist - 1).isHEAD;
+		    WWWDoc.safe = HDOC(nhist - 1).safe;
+		    thatparent =
+			HTAnchor_findAddress(&WWWDoc);
+		}
+		/* In addition to equality of the ParentAnchors, require
+		 * that IF we have a HTMainText (i.e., it wasn't just
+		 * HTuncache'd by mainloop), THEN it has to be consistent
+		 * with what we are trying to push.
+		 *
+		 * This may be overkill...  - kw
+		 */
+		if (thatparent == thisparent &&
+		    (!HTMainText || HTMainAnchor == thisparent)
+		    ) {
+		    HDOC(nhist).internal_link = TRUE;
+		    history[nhist].intern_seq_start =
+			history[nhist - 1].intern_seq_start >= 0 ?
+			history[nhist - 1].intern_seq_start : nhist - 1;
+		    CTRACE((tfp, "\nLYpush: pushed as internal link, OK\n"));
+		}
+	    }
+	}
+	if (!HDOC(nhist).internal_link) {
+	    CTRACE((tfp, "\nLYpush: push as internal link requested, %s\n",
+		    "but didn't check out!"));
+	}
+    }
+    CTRACE((tfp, "\nLYpush[%d]: address:%s\n        title:%s\n",
+	    nhist, doc->address, doc->title));
+    nhist++;
+    return 1;
+}
+
+/*
+ * Pop the previous filename, link and line number from the history list.
+ */
+void LYpop(DocInfo *doc)
+{
+    if (nhist > 0) {
+	clean_extra_history();
+	nhist--;
+
+	LYFreeDocInfo(doc);
+
+	*doc = HDOC(nhist);
+
+#ifdef DISP_PARTIAL
+	/* assume we pop the 'doc' to show it soon... */
+	LYSetNewline(doc->line);	/* reinitialize */
+#endif /* DISP_PARTIAL */
+	CTRACE((tfp, "LYpop[%d]: address:%s\n     title:%s\n",
+		nhist, doc->address, doc->title));
+    }
+}
+
+/*
+ * Move to the previous filename, link and line number from the history list.
+ */
+void LYhist_prev(DocInfo *doc)
+{
+    trace_history("LYhist_prev");
+    if (nhist > 0 && (nhist_extra || nhist < size_history)) {
+	nhist--;
+	nhist_extra++;
+	LYpop_num(nhist, doc);
+	trace_history("...LYhist_prev");
+    }
+}
+
+/*
+ * Called before calling LYhist_prev().
+ */
+void LYhist_prev_register(DocInfo *doc)
+{
+    trace_history("LYhist_prev_register");
+    if (nhist > 1) {
+	if (nhist_extra) {	/* Make something to return back */
+	    /* Store the new position */
+	    HDOC(nhist).link = doc->link;
+	    HDOC(nhist).line = doc->line;
+	} else if (LYpush(doc, 0)) {
+	    nhist--;
+	    nhist_extra++;
+	}
+	trace_history("...LYhist_prev_register");
+    }
+}
+
+/*
+ * Move to the next filename, link and line number from the history.
+ */
+int LYhist_next(DocInfo *doc, DocInfo *newdoc)
+{
+    if (nhist_extra <= 1)	/* == 1 when we are the last one */
+	return 0;
+    /* Store the new position */
+    HDOC(nhist).link = doc->link;
+    HDOC(nhist).line = doc->line;
+    LYAllocHistory(nhist);
+    nhist++;
+    nhist_extra--;
+    LYpop_num(nhist, newdoc);
+    return 1;
+}
+
+/*
+ * Pop the specified hist entry, link and line number from the history list but
+ * don't actually remove the entry, just return it.
+ * (This procedure is badly named :)
+ */
+void LYpop_num(int number,
+	       DocInfo *doc)
+{
+    if (number >= 0 && nhist + nhist_extra > number) {
+	doc->link = HDOC(number).link;
+	doc->line = HDOC(number).line;
+	StrAllocCopy(doc->title, HDOC(number).title);
+	StrAllocCopy(doc->address, HDOC(number).address);
+	BStrCopy(doc->post_data, HDOC(number).post_data);
+	StrAllocCopy(doc->post_content_type, HDOC(number).post_content_type);
+	StrAllocCopy(doc->bookmark, HDOC(number).bookmark);
+	doc->isHEAD = HDOC(number).isHEAD;
+	doc->safe = HDOC(number).safe;
+	doc->internal_link = HDOC(number).internal_link;	/* ?? */
+#ifdef DISP_PARTIAL
+	/* assume we pop the 'doc' to show it soon... */
+	LYSetNewline(doc->line);	/* reinitialize */
+#endif /* DISP_PARTIAL */
+	if (TRACE) {
+	    CTRACE((tfp, "LYpop_num(%d)\n", number));
+	    CTRACE((tfp, "  link    %d\n", doc->link));
+	    CTRACE((tfp, "  line    %d\n", doc->line));
+	    CTRACE((tfp, "  title   %s\n", NonNull(doc->title)));
+	    CTRACE((tfp, "  address %s\n", NonNull(doc->address)));
+	}
+    }
+}
+
+/*
+ * This procedure outputs the history buffer into a temporary file.
+ */
+int showhistory(char **newfile)
+{
+    static char tempfile[LY_MAXPATH] = "\0";
+    char *Title = NULL;
+    int x = 0;
+    FILE *fp0;
+
+    if ((fp0 = InternalPageFP(tempfile, TRUE)) == 0)
+	return (-1);
+
+    LYLocalFileToURL(newfile, tempfile);
+
+    LYforce_HTML_mode = TRUE;	/* force this file to be HTML */
+    LYforce_no_cache = TRUE;	/* force this file to be new */
+
+    BeginInternalPage(fp0, HISTORY_PAGE_TITLE, HISTORY_PAGE_HELP);
+
+    fprintf(fp0, "<p align=right> <a href=\"%s\">[%s]</a>\n",
+	    STR_LYNXMESSAGES, STATUSLINES_TITLE);
+
+    fprintf(fp0, "<pre>\n");
+
+    fprintf(fp0, "<em>%s</em>\n", gettext("You selected:"));
+    for (x = nhist + nhist_extra - 1; x >= 0; x--) {
+	/*
+	 * The number of the document in the hist stack, its title in a link,
+	 * and its address.  - FM
+	 */
+	if (HDOC(x).title != NULL) {
+	    StrAllocCopy(Title, HDOC(x).title);
+	    LYEntify(&Title, TRUE);
+	    LYTrimLeading(Title);
+	    LYTrimTrailing(Title);
+	    if (*Title == '\0')
+		StrAllocCopy(Title, NO_TITLE);
+	} else {
+	    StrAllocCopy(Title, NO_TITLE);
+	}
+	fprintf(fp0,
+		"%s<em>%d</em>. <tab id=t%d><a href=\"%s%d\">%s</a>\n",
+		(x > 99 ? "" : x < 10 ? "  " : " "),
+		x, x, STR_LYNXHIST, x, Title);
+	if (HDOC(x).address != NULL) {
+	    StrAllocCopy(Title, HDOC(x).address);
+	    LYEntify(&Title, TRUE);
+	} else {
+	    StrAllocCopy(Title, gettext("(no address)"));
+	}
+	if (HDOC(x).internal_link) {
+	    if (history[x].intern_seq_start == history[nhist - 1].intern_seq_start)
+		StrAllocCat(Title, gettext(" (internal)"));
+	    else
+		StrAllocCat(Title, gettext(" (was internal)"));
+	}
+	fprintf(fp0, "<tab to=t%d>%s\n", x, Title);
+    }
+    fprintf(fp0, "</pre>\n");
+    EndInternalPage(fp0);
+
+    LYCloseTempFP(fp0);
+    FREE(Title);
+    return (0);
+}
+
+/*
+ * This function makes the history page seem like any other type of file since
+ * more info is needed than can be provided by the normal link structure.  We
+ * saved out the history number to a special URL.
+ *
+ * The info looks like:  LYNXHIST:#
+ */
+BOOLEAN historytarget(DocInfo *newdoc)
+{
+    int number;
+    DocAddress WWWDoc;
+    HTParentAnchor *tmpanchor;
+    HText *text;
+    BOOLEAN treat_as_intern = FALSE;
+
+    if ((!newdoc || !newdoc->address) ||
+	strlen(newdoc->address) < 10 || !isdigit(UCH(*(newdoc->address + 9))))
+	return (FALSE);
+
+    if ((number = atoi(newdoc->address + 9)) > nhist + nhist_extra || number < 0)
+	return (FALSE);
+
+    /*
+     * Optimization: assume we came from the History Page,
+     * so never return back - always a new version next time.
+     * But check first whether HTMainText is really the History
+     * Page document - in some obscure situations this may not be
+     * the case.  If HTMainText seems to be a History Page document,
+     * also check that it really hasn't been pushed. - LP, kw
+     */
+    if (HTMainText && nhist > 0 &&
+	!strcmp(HTLoadedDocumentTitle(), HISTORY_PAGE_TITLE) &&
+	LYIsUIPage3(HTLoadedDocumentURL(), UIP_HISTORY, 0) &&
+	strcmp(HTLoadedDocumentURL(), HDOC(nhist - 1).address)) {
+	HTuncache_current_document();	/* don't waste the cache */
+    }
+
+    LYpop_num(number, newdoc);
+    if (((newdoc->internal_link &&
+	  history[number].intern_seq_start == history[nhist - 1].intern_seq_start)
+	 || (number < nhist - 1 &&
+	     HDOC(nhist - 1).internal_link &&
+	     number == history[nhist - 1].intern_seq_start))
+	&& !(LYforce_no_cache == TRUE && LYoverride_no_cache == FALSE)) {
+#ifndef DONT_TRACK_INTERNAL_LINKS
+	LYforce_no_cache = FALSE;
+	LYinternal_flag = TRUE;
+	newdoc->internal_link = TRUE;
+	treat_as_intern = TRUE;
+#endif
+    } else {
+	newdoc->internal_link = FALSE;
+    }
+    /*
+     * If we have POST content, and have LYresubmit_posts set or have no_cache
+     * set or do not still have the text cached, ask the user whether to
+     * resubmit the form.  - FM
+     */
+    if (newdoc->post_data != NULL) {
+	WWWDoc.address = newdoc->address;
+	WWWDoc.post_data = newdoc->post_data;
+	WWWDoc.post_content_type = newdoc->post_content_type;
+	WWWDoc.bookmark = newdoc->bookmark;
+	WWWDoc.isHEAD = newdoc->isHEAD;
+	WWWDoc.safe = newdoc->safe;
+	tmpanchor = HTAnchor_findAddress(&WWWDoc);
+	text = (HText *) HTAnchor_document(tmpanchor);
+	if (((((LYresubmit_posts == TRUE) ||
+	       (LYforce_no_cache == TRUE &&
+		LYoverride_no_cache == FALSE)) &&
+	      !(treat_as_intern && !reloading)) ||
+	     text == NULL) &&
+	    (isLYNXIMGMAP(newdoc->address) ||
+	     HTConfirm(CONFIRM_POST_RESUBMISSION) == TRUE)) {
+	    LYforce_no_cache = TRUE;
+	    LYoverride_no_cache = FALSE;
+	} else if (text != NULL) {
+	    LYforce_no_cache = FALSE;
+	    LYoverride_no_cache = TRUE;
+	} else {
+	    HTInfoMsg(CANCELLED);
+	    return (FALSE);
+	}
+    }
+
+    if (number != 0)
+	StrAllocCat(newdoc->title, gettext(" (From History)"));
+    return (TRUE);
+}
+
+/*
+ * This procedure outputs the Visited Links list into a temporary file.  - FM
+ * Returns links's number to make active (1-based), or 0 if not required.
+ */
+int LYShowVisitedLinks(char **newfile)
+{
+    static char tempfile[LY_MAXPATH] = "\0";
+    char *Title = NULL;
+    char *Address = NULL;
+    int x, tot;
+    FILE *fp0;
+    VisitedLink *vl;
+    HTList *cur = Visited_Links;
+    int offset;
+    int ret = 0;
+    const char *arrow, *post_arrow;
+
+    if (!cur)
+	return (-1);
+
+    if ((fp0 = InternalPageFP(tempfile, TRUE)) == 0)
+	return (-1);
+
+    LYLocalFileToURL(newfile, tempfile);
+    LYRegisterUIPage(*newfile, UIP_VLINKS);
+
+    LYforce_HTML_mode = TRUE;	/* force this file to be HTML */
+    LYforce_no_cache = TRUE;	/* force this file to be new */
+
+    BeginInternalPage(fp0, VISITED_LINKS_TITLE, VISITED_LINKS_HELP);
+
+#ifndef NO_OPTION_FORMS
+    fprintf(fp0, "<form action=\"%s\" method=\"post\">\n", STR_LYNXOPTIONS);
+    LYMenuVisitedLinks(fp0, FALSE);
+    fprintf(fp0, "<input type=\"submit\" value=\"Accept Changes\">\n");
+    fprintf(fp0, "</form>\n");
+    fprintf(fp0, "<P>\n");
+#endif
+
+    fprintf(fp0, "<pre>\n");
+    fprintf(fp0, "<em>%s</em>\n",
+	    gettext("You visited (POSTs, bookmark, menu and list files excluded):"));
+    if (Visited_Links_As & VISITED_LINKS_REVERSE)
+	tot = x = HTList_count(Visited_Links);
+    else
+	tot = x = -1;
+
+    if (Visited_Links_As & VISITED_LINKS_AS_TREE) {
+	vl = First_tree;
+    } else if (Visited_Links_As & VISITED_LINKS_AS_LATEST) {
+	if (Visited_Links_As & VISITED_LINKS_REVERSE)
+	    vl = Latest_last.prev_latest;
+	else
+	    vl = Latest_first.next_latest;
+	if (vl == &Latest_last || vl == &Latest_first)
+	    vl = NULL;
+    } else {
+	if (Visited_Links_As & VISITED_LINKS_REVERSE)
+	    vl = Last_by_first;
+	else
+	    vl = (VisitedLink *) HTList_nextObject(cur);
+    }
+    while (NULL != vl) {
+	/*
+	 * The number of the document (most recent highest), its title in a
+	 * link, and its address.  - FM
+	 */
+	post_arrow = arrow = "";
+	if (Visited_Links_As & VISITED_LINKS_REVERSE)
+	    x--;
+	else
+	    x++;
+	if (vl == PrevActiveVisitedLink) {
+	    if (Visited_Links_As & VISITED_LINKS_REVERSE)
+		ret = tot - x + 2;
+	    else
+		ret = x + 3;
+	}
+	if (vl == PrevActiveVisitedLink) {
+	    post_arrow = "<A NAME=current></A>";
+	    /* Otherwise levels 0 and 1 look the same when with arrow: */
+	    arrow = (vl->level && (Visited_Links_As & VISITED_LINKS_AS_TREE))
+		? "==>" : "=>";
+	    StrAllocCat(*newfile, "#current");
+	}
+	if (Visited_Links_As & VISITED_LINKS_AS_TREE) {
+	    offset = 2 * vl->level;
+	    if (offset > 24)
+		offset = (offset + 24) / 2;
+	    if (offset > LYcols * 3 / 4)
+		offset = LYcols * 3 / 4;
+	} else
+	    offset = (x > 99 ? 0 : x < 10 ? 2 : 1);
+	if (non_empty(vl->title)) {
+	    StrAllocCopy(Title, vl->title);
+	    LYEntify(&Title, TRUE);
+	    LYTrimLeading(Title);
+	    LYTrimTrailing(Title);
+	    if (*Title == '\0')
+		StrAllocCopy(Title, NO_TITLE);
+	} else {
+	    StrAllocCopy(Title, NO_TITLE);
+	}
+	if (non_empty(vl->address)) {
+	    StrAllocCopy(Address, vl->address);
+	    LYEntify(&Address, FALSE);
+	    fprintf(fp0,
+		    "%-*s%s<em>%d</em>. <tab id=t%d><a href=\"%s\">%s</a>\n",
+		    offset, arrow, post_arrow,
+		    x, x, Address, Title);
+	} else {
+	    fprintf(fp0,
+		    "%-*s%s<em>%d</em>. <tab id=t%d><em>%s</em>\n",
+		    offset, arrow, post_arrow,
+		    x, x, Title);
+	}
+	if (Address != NULL) {
+	    StrAllocCopy(Address, vl->address);
+	    LYEntify(&Address, TRUE);
+	}
+	fprintf(fp0, "<tab to=t%d>%s\n", x,
+		((Address != NULL) ? Address : gettext("(no address)")));
+	if (Visited_Links_As & VISITED_LINKS_AS_TREE)
+	    vl = vl->next_tree;
+	else if (Visited_Links_As & VISITED_LINKS_AS_LATEST) {
+	    if (Visited_Links_As & VISITED_LINKS_REVERSE)
+		vl = vl->prev_latest;
+	    else
+		vl = vl->next_latest;
+	    if (vl == &Latest_last || vl == &Latest_first)
+		vl = NULL;
+	} else {
+	    if (Visited_Links_As & VISITED_LINKS_REVERSE)
+		vl = vl->prev_first;
+	    else
+		vl = (VisitedLink *) HTList_nextObject(cur);
+	}
+    }
+    fprintf(fp0, "</pre>\n");
+    EndInternalPage(fp0);
+
+    LYCloseTempFP(fp0);
+    FREE(Title);
+    FREE(Address);
+    return (ret);
+}
+
+/*
+ * Keep cycled buffer for statusline messages.
+ * But allow user to change how big it will be from userdefs.h
+ */
+#ifndef STATUSBUFSIZE
+#define STATUSBUFSIZE   40
+#endif
+
+int status_buf_size = STATUSBUFSIZE;
+
+static char **buffstack;
+static int topOfStack = 0;
+
+#ifdef LY_FIND_LEAKS
+static void free_messages_stack(void)
+{
+    if (buffstack != 0) {
+	topOfStack = status_buf_size;
+
+	while (--topOfStack >= 0) {
+	    FREE(buffstack[topOfStack]);
+	}
+	FREE(buffstack);
+    }
+}
+#endif
+
+static void to_stack(char *str)
+{
+    /*
+     * Cycle buffer:
+     */
+    if (topOfStack >= status_buf_size) {
+	topOfStack = 0;
+    }
+
+    /*
+     * Register string.
+     */
+    if (buffstack == 0)
+	buffstack = typecallocn(char *, status_buf_size);
+
+    FREE(buffstack[topOfStack]);
+    buffstack[topOfStack] = str;
+    topOfStack++;
+#ifdef LY_FIND_LEAKS
+    if (!already_registered_free_messages_stack) {
+	already_registered_free_messages_stack = 1;
+	atexit(free_messages_stack);
+    }
+#endif
+    if (topOfStack >= status_buf_size) {
+	topOfStack = 0;
+    }
+}
+
+/*
+ * Dump statusline messages into the buffer.
+ * Called from mainloop() when exit immediately with an error:
+ * can not access startfile (first_file) so a couple of alert messages
+ * will be very useful on exit.
+ * (Don't expect everyone will look a trace log in case of difficulties:))
+ */
+void LYstatusline_messages_on_exit(char **buf)
+{
+    int i;
+
+    if (buffstack != 0) {
+	StrAllocCat(*buf, "\n");
+	/* print messages in chronological order:
+	 * probably a single message but let's do it.
+	 */
+	i = topOfStack - 1;
+	while (++i < status_buf_size) {
+	    if (buffstack[i] != NULL) {
+		StrAllocCat(*buf, buffstack[i]);
+		StrAllocCat(*buf, "\n");
+	    }
+	}
+	i = -1;
+	while (++i < topOfStack) {
+	    if (buffstack[i] != NULL) {
+		StrAllocCat(*buf, buffstack[i]);
+		StrAllocCat(*buf, "\n");
+	    }
+	}
+    }
+}
+
+void LYstore_message2(const char *message,
+		      const char *argument)
+{
+
+    if (message != NULL) {
+	char *temp = NULL;
+
+	HTSprintf0(&temp, message, NonNull(argument));
+	to_stack(temp);
+    }
+}
+
+void LYstore_message(const char *message)
+{
+    if (message != NULL) {
+	char *temp = NULL;
+
+	StrAllocCopy(temp, message);
+	to_stack(temp);
+    }
+}
+
+/*     LYLoadMESSAGES
+ *     --------------
+ *     Create a text/html stream with a list of recent statusline messages.
+ *     LYNXMESSAGES:/ internal page.
+ *     [implementation based on LYLoadKeymap()].
+ */
+
+struct _HTStream {
+    HTStreamClass *isa;
+};
+
+static int LYLoadMESSAGES(const char *arg GCC_UNUSED,
+			  HTParentAnchor *anAnchor,
+			  HTFormat format_out,
+			  HTStream *sink)
+{
+    HTFormat format_in = WWW_HTML;
+    HTStream *target = NULL;
+    char *buf = NULL;
+    int nummsg = 0;
+
+    int i;
+    char *temp = NULL;
+
+    if (buffstack != 0) {
+	i = status_buf_size;
+	while (--i >= 0) {
+	    if (buffstack[i] != NULL)
+		nummsg++;
+	}
+    }
+
+    /*
+     * Set up the stream.  - FM
+     */
+    target = HTStreamStack(format_in, format_out, sink, anAnchor);
+
+    if (!target || target == NULL) {
+	HTSprintf0(&buf, CANNOT_CONVERT_I_TO_O,
+		   HTAtom_name(format_in), HTAtom_name(format_out));
+	HTAlert(buf);
+	FREE(buf);
+	return (HT_NOT_LOADED);
+    }
+    anAnchor->no_cache = TRUE;
+
+#define PUTS(buf)    (*target->isa->put_block)(target, buf, (int) strlen(buf))
+
+    HTSprintf0(&buf, "<html>\n<head>\n");
+    PUTS(buf);
+    /*
+     * This page is a list of messages in display character set.
+     */
+    HTSprintf0(&buf, "<META %s content=\"text/html;charset=%s\">\n",
+	       "http-equiv=\"content-type\"",
+	       LYCharSet_UC[current_char_set].MIMEname);
+    PUTS(buf);
+    HTSprintf0(&buf, "<title>%s</title>\n</head>\n<body>\n",
+	       STATUSLINES_TITLE);
+    PUTS(buf);
+
+    if (nummsg != 0) {
+	HTSprintf0(&buf, "<ol>\n");
+	PUTS(buf);
+	/* print messages in reverse order: */
+	i = topOfStack;
+	while (--i >= 0) {
+	    if (buffstack[i] != NULL) {
+		StrAllocCopy(temp, buffstack[i]);
+		LYEntify(&temp, TRUE);
+		HTSprintf0(&buf, "<li value=%d> <em>%s</em>\n", nummsg, temp);
+		nummsg--;
+		PUTS(buf);
+	    }
+	}
+	i = status_buf_size;
+	while (--i >= topOfStack) {
+	    if (buffstack[i] != NULL) {
+		StrAllocCopy(temp, buffstack[i]);
+		LYEntify(&temp, TRUE);
+		HTSprintf0(&buf, "<li value=%d> <em>%s</em>\n", nummsg, temp);
+		nummsg--;
+		PUTS(buf);
+	    }
+	}
+	FREE(temp);
+	HTSprintf0(&buf, "</ol>\n</body>\n</html>\n");
+    } else {
+	HTSprintf0(&buf, "<p>%s\n</body>\n</html>\n",
+		   gettext("(No messages yet)"));
+    }
+    PUTS(buf);
+
+    (*target->isa->_free) (target);
+    FREE(buf);
+    return (HT_LOADED);
+}
+
+#ifdef GLOBALDEF_IS_MACRO
+#define _LYMESSAGES_C_GLOBALDEF_1_INIT { "LYNXMESSAGES", LYLoadMESSAGES, 0}
+GLOBALDEF(HTProtocol, LYLynxStatusMessages, _LYMESSAGES_C_GLOBALDEF_1_INIT);
+#else
+GLOBALDEF HTProtocol LYLynxStatusMessages =
+{"LYNXMESSAGES", LYLoadMESSAGES, 0};
+#endif /* GLOBALDEF_IS_MACRO */
diff --git a/src/LYHistory.h b/src/LYHistory.h
new file mode 100644
index 00000000..260ba45e
--- /dev/null
+++ b/src/LYHistory.h
@@ -0,0 +1,39 @@
+/*
+ * $LynxId: LYHistory.h,v 1.20 2009/06/07 16:57:13 tom Exp $
+ */
+#ifndef LYHISTORY_H
+#define LYHISTORY_H
+
+#ifndef LYSTRUCTS_H
+#include <LYStructs.h>
+#endif /* LYSTRUCTS_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    extern BOOLEAN LYwouldPush(const char *title, const char *docurl);
+    extern BOOLEAN historytarget(DocInfo *newdoc);
+    extern int LYShowVisitedLinks(char **newfile);
+    extern int LYhist_next(DocInfo *doc, DocInfo *newdoc);
+    extern int LYpush(DocInfo *doc, BOOLEAN force_push);
+    extern int showhistory(char **newfile);
+    extern void LYAddVisitedLink(DocInfo *doc);
+    extern void LYAllocHistory(int entries);
+    extern void LYFreePostData(DocInfo *data);
+    extern void LYFreeDocInfo(DocInfo *data);
+    extern void LYhist_prev(DocInfo *doc);
+    extern void LYhist_prev_register(DocInfo *doc);
+    extern void LYpop(DocInfo *doc);
+    extern void LYpop_num(int number, DocInfo *doc);
+    extern void LYstatusline_messages_on_exit(char **buf);
+    extern void LYstore_message(const char *message);
+    extern void LYstore_message2(const char *message, const char *argument);
+
+    extern HTList *Visited_Links;
+    extern int nhist_extra;
+    extern int status_buf_size;
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYHISTORY_H */
diff --git a/src/LYIcon.rc b/src/LYIcon.rc
new file mode 100644
index 00000000..9b6054c1
--- /dev/null
+++ b/src/LYIcon.rc
@@ -0,0 +1 @@
+100	ICON	"../samples/lynx.ico"
diff --git a/src/LYJump.c b/src/LYJump.c
new file mode 100644
index 00000000..dbf0dd67
--- /dev/null
+++ b/src/LYJump.c
@@ -0,0 +1,501 @@
+/*
+ * $LynxId: LYJump.c,v 1.36 2010/05/02 20:17:37 tom Exp $
+ */
+#include <HTUtils.h>
+#include <HTAlert.h>
+#include <LYUtils.h>
+#include <LYStrings.h>
+#include <LYGlobalDefs.h>
+#include <LYJump.h>
+#include <LYKeymap.h>
+#include <GridText.h>
+
+#include <LYLeaks.h>
+
+#ifdef _WINDOWS
+#include <stdlib.h>		/* bsearch() */
+#endif
+
+#ifdef VMS
+#include <fab.h>
+#endif /* VMS */
+
+struct JumpTable *JThead = NULL;
+
+static int LYCompare(const void *e1, const void *e2);
+static unsigned LYRead_Jumpfile(struct JumpTable *jtp);
+
+void LYJumpTable_free(void)
+{
+    struct JumpTable *cur = JThead;
+    struct JumpTable *next;
+
+    while (cur) {
+	next = cur->next;
+	FREE(cur->msg);
+	FREE(cur->file);
+	FREE(cur->shortcut);
+	if (cur->history) {
+	    LYFreeStringList(cur->history);
+	    cur->history = NULL;
+	}
+	FREE(cur->table);
+	FREE(cur->mp);
+	FREE(cur);
+	cur = next;
+    }
+    JThead = NULL;
+    return;
+}
+
+/*
+ * Utility for listing shortcuts, making any repeated
+ * shortcut the most current in the list. - FM
+ */
+void LYAddJumpShortcut(HTList *historyp, char *shortcut)
+{
+    char *tmp = NULL;
+    char *old;
+    HTList *cur = historyp;
+
+    if (!historyp || isEmpty(shortcut))
+	return;
+
+    StrAllocCopy(tmp, shortcut);
+
+    while (NULL != (old = (char *) HTList_nextObject(cur))) {
+	if (!strcmp(old, tmp)) {
+	    HTList_removeObject(historyp, old);
+	    FREE(old);
+	    break;
+	}
+    }
+    HTList_addObject(historyp, tmp);
+
+    return;
+}
+
+BOOL LYJumpInit(char *config)
+{
+    struct JumpTable *jtp;
+    char *cp;
+
+    /*
+     * Create a JumpTable structure.
+     */
+    jtp = typecalloc(struct JumpTable);
+
+    if (jtp == NULL) {
+	outofmem(__FILE__, "LYJumpInit");
+    }
+
+    assert(jtp != NULL);
+
+    /*
+     * config is JUMPFILE:path[:optional_key[:optional_prompt]]
+     *
+     * Skip JUMPFILE.
+     */
+    cp = strtok(config, ":\n");
+    if (!cp) {
+	FREE(jtp);
+	return FALSE;
+    }
+
+    /*
+     * Get the path.
+     */
+    cp = strtok(NULL, ":\n");
+    if (!cp) {
+	FREE(jtp);
+	return FALSE;
+    }
+    StrAllocCopy(jtp->file, cp);
+#ifdef LY_FIND_LEAKS
+    if (!JThead)
+	atexit(LYJumpTable_free);
+#endif /* LY_FIND_LEAKS */
+
+    /*
+     * Get the key, if present.
+     */
+    cp = strtok(NULL, ":\n");
+
+    /*
+     * If no key, check whether we are resetting the default jumps file.
+     */
+    if (!cp && JThead) {
+	struct JumpTable *jtptmp = JThead;
+
+	jumpfile = jtp->file;
+	FREE(jtp);
+	while (jtptmp && jtptmp->key)
+	    jtptmp = jtptmp->next;
+	if (!jtptmp)
+	    return FALSE;
+	StrAllocCopy(jtptmp->file, jumpfile);
+	StrAllocCopy(jtptmp->msg, jumpprompt);
+	return TRUE;
+    }
+
+    /*
+     * If a key is present and we have no default, create one,
+     * using the path from config, and the current jumpprompt.
+     */
+    if (cp && !JThead) {
+	JThead = jtp;
+	StrAllocCopy(JThead->msg, jumpprompt);
+	if (!jumpfile)
+	    StrAllocCopy(jumpfile, JThead->file);
+	jtp = typecalloc(struct JumpTable);
+
+	if (jtp == NULL) {
+	    outofmem(__FILE__, "LYJumpInit");
+	}
+
+	assert(jtp != NULL);
+
+	StrAllocCopy(jtp->file, JThead->file);
+    }
+
+    /*
+     * Complete the initialization of config.
+     */
+    if (cp) {
+	jtp->key = remap(cp, "JUMP", FALSE);	/* key is present, (re)map it */
+	cp = strtok(NULL, "\n");	/* get prompt, if present */
+	if (non_empty(cp))
+	    StrAllocCopy(jtp->msg, cp);		/* prompt is present, load it */
+	else
+	    cp = NULL;
+    }
+    if (!cp)			/* no prompt, use default */
+	StrAllocCopy(jtp->msg, jumpprompt);
+    if (jtp->msg[strlen(jtp->msg) - 1] != ' ')	/* ensure a trailing space */
+	StrAllocCat(jtp->msg, " ");
+    jtp->history = HTList_new();
+    jtp->next = JThead;
+    JThead = jtp;
+    return TRUE;
+}
+
+char *LYJump(int key)
+{
+    JumpDatum seeking;
+    JumpDatum *found;
+    static char buf[124];
+    char *bp, *cp;
+    struct JumpTable *jtp;
+    int ch;
+    RecallType recall;
+    int ShortcutTotal;
+    int ShortcutNum;
+    BOOLEAN FirstShortcutRecall = TRUE;
+
+    if (!JThead)
+	return NULL;
+    jtp = JThead;
+    while (jtp && jtp->key && jtp->key != key)
+	jtp = jtp->next;
+    if (!jtp) {
+	char *msg = 0;
+
+	HTSprintf0(&msg, KEY_NOT_MAPPED_TO_JUMP_FILE, key);
+	HTAlert(msg);
+	FREE(msg);
+	return NULL;
+    }
+    if (!jtp->table)
+	jtp->nel = LYRead_Jumpfile(jtp);
+    if (jtp->nel == 0)
+	return NULL;
+
+    if (!jump_buffer || isEmpty(jtp->shortcut))
+	*buf = '\0';
+    else if (non_empty(jtp->shortcut)) {
+	if (strlen(jtp->shortcut) > 119)
+	    jtp->shortcut[119] = '\0';
+	strcpy(buf, jtp->shortcut);
+    }
+
+    ShortcutTotal = (jtp->history ? HTList_count(jtp->history) : 0);
+    if (jump_buffer && *buf) {
+	recall = ((ShortcutTotal > 1) ? RECALL_URL : NORECALL);
+	ShortcutNum = 0;
+	FirstShortcutRecall = FALSE;
+    } else {
+	recall = ((ShortcutTotal >= 1) ? RECALL_URL : NORECALL);
+	ShortcutNum = ShortcutTotal;
+	FirstShortcutRecall = TRUE;
+    }
+
+    statusline(jtp->msg);
+    if ((ch = LYgetstr(buf, VISIBLE, (sizeof(buf) - 4), recall)) < 0) {
+	/*
+	 * User cancelled the Jump via ^G. - FM
+	 */
+	HTInfoMsg(CANCELLED);
+	return NULL;
+    }
+
+  check_recall:
+    bp = buf;
+    if (TOUPPER(key) == 'G' && strncmp(buf, "o ", 2) == 0)
+	bp++;
+    bp = LYSkipBlanks(bp);
+    if (*bp == '\0' &&
+	!(recall && (ch == UPARROW || ch == DNARROW))) {
+	/*
+	 * User cancelled the Jump via a zero-length string. - FM
+	 */
+	*buf = '\0';
+	StrAllocCopy(jtp->shortcut, buf);
+	HTInfoMsg(CANCELLED);
+	return NULL;
+    }
+#ifdef PERMIT_GOTO_FROM_JUMP
+    if (strchr(bp, ':') || strchr(bp, '/')) {
+	char *temp = NULL;
+
+	LYJumpFileURL = FALSE;
+	if (no_goto) {
+	    *buf = '\0';
+	    StrAllocCopy(jtp->shortcut, buf);
+	    HTUserMsg(RANDOM_URL_DISALLOWED);
+	    return NULL;
+	}
+	sprintf(buf, "Go %.*s", (int) sizeof(buf) - 4, bp);
+	return (bp = buf);
+    }
+#endif /* PERMIT_GOTO_FROM_JUMP */
+
+    if (recall && ch == UPARROW) {
+	if (FirstShortcutRecall) {
+	    /*
+	     * Use last Shortcut in the list. - FM
+	     */
+	    FirstShortcutRecall = FALSE;
+	    ShortcutNum = 0;
+	} else {
+	    /*
+	     * Go back to the previous Shortcut in the list. - FM
+	     */
+	    ShortcutNum++;
+	}
+	if (ShortcutNum >= ShortcutTotal)
+	    /*
+	     * Roll around to the last Shortcut in the list. - FM
+	     */
+	    ShortcutNum = 0;
+	if ((cp = (char *) HTList_objectAt(jtp->history,
+					   ShortcutNum)) != NULL) {
+	    LYstrncpy(buf, cp, sizeof(buf) - 1);
+	    if (jump_buffer && jtp->shortcut &&
+		!strcmp(buf, jtp->shortcut)) {
+		_statusline(EDIT_CURRENT_SHORTCUT);
+	    } else if ((jump_buffer && ShortcutTotal == 2) ||
+		       (!jump_buffer && ShortcutTotal == 1)) {
+		_statusline(EDIT_THE_PREV_SHORTCUT);
+	    } else {
+		_statusline(EDIT_A_PREV_SHORTCUT);
+	    }
+	    if ((ch = LYgetstr(buf, VISIBLE,
+			       sizeof(buf), recall)) < 0) {
+		/*
+		 * User cancelled the jump via ^G.
+		 */
+		HTInfoMsg(CANCELLED);
+		return NULL;
+	    }
+	    goto check_recall;
+	}
+    } else if (recall && ch == DNARROW) {
+	if (FirstShortcutRecall) {
+	    /*
+	     * Use the first Shortcut in the list. - FM
+	     */
+	    FirstShortcutRecall = FALSE;
+	    ShortcutNum = ShortcutTotal - 1;
+	} else {
+	    /*
+	     * Advance to the next Shortcut in the list. - FM
+	     */
+	    ShortcutNum--;
+	}
+	if (ShortcutNum < 0)
+	    /*
+	     * Roll around to the first Shortcut in the list. - FM
+	     */
+	    ShortcutNum = ShortcutTotal - 1;
+	if ((cp = (char *) HTList_objectAt(jtp->history,
+					   ShortcutNum)) != NULL) {
+	    LYstrncpy(buf, cp, sizeof(buf) - 1);
+	    if (jump_buffer && jtp->shortcut &&
+		!strcmp(buf, jtp->shortcut)) {
+		_statusline(EDIT_CURRENT_SHORTCUT);
+	    } else if ((jump_buffer && ShortcutTotal == 2) ||
+		       (!jump_buffer && ShortcutTotal == 1)) {
+		_statusline(EDIT_THE_PREV_SHORTCUT);
+	    } else {
+		_statusline(EDIT_A_PREV_SHORTCUT);
+	    }
+	    if ((ch = LYgetstr(buf, VISIBLE, sizeof(buf), recall)) < 0) {
+		/*
+		 * User cancelled the jump via ^G.
+		 */
+		HTInfoMsg(CANCELLED);
+		return NULL;
+	    }
+	    goto check_recall;
+	}
+    }
+
+    seeking.key = bp;
+    found = (JumpDatum *) bsearch((char *) &seeking, (char *) jtp->table,
+				  jtp->nel, sizeof(JumpDatum), LYCompare);
+    if (!found) {
+	user_message("Unknown target '%s'", buf);
+	LYSleepAlert();
+    }
+
+    StrAllocCopy(jtp->shortcut, bp);
+    LYAddJumpShortcut(jtp->history, jtp->shortcut);
+    return found ? found->url : NULL;
+}
+
+static unsigned LYRead_Jumpfile(struct JumpTable *jtp)
+{
+    struct stat st;
+    unsigned int nel;
+    char *mp;
+    int fd;
+
+#ifdef VMS
+    int blocksize = 1024;
+    FILE *fp;
+    BOOL IsStream_LF = TRUE;
+#endif /* VMS */
+    char *cp;
+    unsigned i;
+
+    if (isEmpty(jtp->file))
+	return 0;
+
+    CTRACE((tfp, "Read Jumpfile %s\n", jtp->file));
+    if (stat(jtp->file, &st) < 0) {
+	HTAlert(CANNOT_LOCATE_JUMP_FILE);
+	return 0;
+    }
+
+    /* allocate storage to read entire file */
+    if ((mp = typecallocn(char, (size_t) st.st_size + 1)) == NULL) {
+	HTAlert(OUTOF_MEM_FOR_JUMP_FILE);
+	return 0;
+    }
+#ifdef VMS
+    if (st.st_fab_rfm != (char) FAB$C_STMLF) {
+	/** It's a record-oriented file. **/
+	IsStream_LF = FALSE;
+	if ((fp = fopen(jtp->file, "r", "mbc=32")) == NULL) {
+	    HTAlert(CANNOT_OPEN_JUMP_FILE);
+	    FREE(mp);
+	    return 0;
+	}
+    } else if ((fd = open(jtp->file, O_RDONLY, "mbc=32")) < 0)
+#else
+    if ((fd = open(jtp->file, O_RDONLY)) < 0)
+#endif /* VMS */
+    {
+	HTAlert(CANNOT_OPEN_JUMP_FILE);
+	FREE(mp);
+	return 0;
+    }
+#ifdef VMS
+    if (IsStream_LF) {
+    /** Handle as a stream. **/
+#endif /* VMS */
+	if (read(fd, mp, (size_t) st.st_size) < st.st_size) {
+	    HTAlert(ERROR_READING_JUMP_FILE);
+	    FREE(mp);
+	    return 0;
+	}
+	mp[st.st_size] = '\0';
+	close(fd);
+#ifdef VMS
+    } else {
+	/** Handle as a series of records. **/
+	if (fgets(mp, blocksize, fp) == NULL) {
+	    HTAlert(ERROR_READING_JUMP_FILE);
+	    FREE(mp);
+	    return 0;
+	} else
+	    while (fgets(mp + strlen(mp), blocksize, fp) != NULL) {
+		;
+	    }
+	LYCloseInput(fp);
+    }
+#endif /* VMS */
+
+    /* quick scan for approximate number of entries */
+    nel = 0;
+    cp = mp;
+    while ((cp = strchr(cp, '\n')) != NULL) {
+	nel++;
+	cp++;
+    }
+
+    jtp->table = (JumpDatum *) malloc(nel * sizeof(JumpDatum));
+    if (jtp->table == NULL) {
+	HTAlert(OUTOF_MEM_FOR_JUMP_TABLE);
+	FREE(mp);
+	return 0;
+    }
+
+    cp = jtp->mp = mp;
+    for (i = 0; i < nel;) {
+	if (strncmp(cp, "<!--", 4) == 0 || strncmp(cp, "<dl>", 4) == 0) {
+	    cp = strchr(cp, '\n');
+	    if (cp == NULL)
+		break;
+	    cp++;
+	    continue;
+	}
+	cp = LYstrstr(cp, "<dt>");
+	if (cp == NULL)
+	    break;
+	cp += 4;
+	jtp->table[i].key = cp;
+	cp = LYstrstr(cp, "<dd>");
+	if (cp == NULL)
+	    break;
+	*cp = '\0';
+	cp += 4;
+	cp = LYstrstr(cp, "href=\"");
+	if (cp == NULL)
+	    break;
+	cp += 6;
+	jtp->table[i].url = cp;
+	cp = strchr(cp, '"');
+	if (cp == NULL)
+	    break;
+	*cp = '\0';
+	cp++;
+	cp = strchr(cp, '\n');
+	if (cp == NULL)
+	    break;
+	cp++;
+	CTRACE((tfp, "Read jumpfile[%u] key='%s', url='%s'\n",
+		i, jtp->table[i].key, jtp->table[i].url));
+	i++;
+	if (!cp)
+	    break;
+    }
+
+    return i;
+}
+
+static int LYCompare(const void *e1, const void *e2)
+{
+    return strcasecomp(((const JumpDatum *) e1)->key,
+		       ((const JumpDatum *) e2)->key);
+}
diff --git a/src/LYJump.h b/src/LYJump.h
new file mode 100644
index 00000000..159bdd68
--- /dev/null
+++ b/src/LYJump.h
@@ -0,0 +1,36 @@
+/* $LynxId: LYJump.h,v 1.8 2009/01/01 22:41:42 tom Exp $ */
+#ifndef LYJUMP_H
+#define LYJUMP_H
+
+#include <HTList.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    typedef struct _JumpDatum {
+	char *key;
+	char *url;
+    } JumpDatum;
+
+    struct JumpTable {
+	int key;
+	unsigned nel;
+	char *msg;
+	char *file;
+	char *shortcut;
+	HTList *history;
+	JumpDatum *table;
+	struct JumpTable *next;
+	char *mp;
+    };
+
+    extern struct JumpTable *JThead;
+    extern void LYJumpTable_free(void);
+    extern void LYAddJumpShortcut(HTList *the_history, char *shortcut);
+    extern BOOL LYJumpInit(char *config);
+    extern char *LYJump(int key);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYJUMP_H */
diff --git a/src/LYJustify.h b/src/LYJustify.h
new file mode 100644
index 00000000..997cd050
--- /dev/null
+++ b/src/LYJustify.h
@@ -0,0 +1,83 @@
+/*
+ * $LynxId: LYJustify.h,v 1.8 2009/11/21 15:24:48 tom Exp $
+ *
+ * Justification for lynx - implemented by Vlad Harchev <hvv@hippo.ru>
+ * 11 July 1999
+ */
+
+#ifndef LYJUSTIFY_H
+#define LYJUSTIFY_H
+
+#include <HTUtils.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef USE_JUSTIFY_ELTS
+    extern BOOL can_justify_here;
+    extern BOOL can_justify_here_saved;
+
+    extern BOOL can_justify_this_line;
+    extern int wait_for_this_stacked_elt;
+    extern BOOL form_in_htext;
+
+/* this is the element with SGML_EMPTY content, so it won't get on the stack,
+ * so we can't trap it with wait_for_this_stacked_elt
+ */
+    extern BOOL in_DT;
+
+/*disabled by default*/
+/*#define DEBUG_JUSTIFY*/
+#ifdef DEBUG_JUSTIFY
+    extern BOOL can_justify_stack_depth;	/* can be 0 or 1 if all code is correct */
+
+#  define CAN_JUSTIFY_STACK_INC ++can_justify_stack_depth;\
+	assert(can_justify_stack_depth < 2 && can_justify_stack_depth >=0 );
+#  define CAN_JUSTIFY_STACK_DEC --can_justify_stack_depth;\
+	assert(can_justify_stack_depth < 2 && can_justify_stack_depth >=0 );
+#else
+#  define CAN_JUSTIFY_STACK_INC	/* nothing */
+#  define CAN_JUSTIFY_STACK_DEC	/* nothing */
+#endif
+
+#define CAN_JUSTIFY_PUSH(x) can_justify_here_saved=can_justify_here;\
+	can_justify_here=(x); CAN_JUSTIFY_STACK_INC
+#define CAN_JUSTIFY_POP can_justify_here=can_justify_here_saved;\
+	CAN_JUSTIFY_STACK_INC
+#define CAN_JUSTIFY_SET(x) can_justify_here=(x);
+
+/*
+ * This is used to indicate that starting from the current offset in current
+ * line justification can take place (in order the gap between some prefix and
+ * the word not to be enlarged.
+ * For example, when forming OL,
+ *     1.21 foo
+ * 	   ^justification can start here so that gap between 1.21 and "foo"
+ *	   will not be enlarged.
+ * This is a macro (that uses 'me').
+ */
+#define CAN_JUSTIFY_START  mark_justify_start_position(me->text);
+#define CANT_JUSTIFY_THIS_LINE can_justify_this_line = FALSE
+#define EMIT_IFDEF_USE_JUSTIFY_ELTS(x) x
+    /*defined in order not to wrap single line of code  into #ifdef/#endif */
+
+    extern void ht_justify_cleanup(void);
+    extern void mark_justify_start_position(void *text);
+
+#else				/* ! USE_JUSTIFY_ELTS */
+/*
+ * define empty macros so that they can be used without wrapping them in
+ * #ifdef USE_JUSTIFY_ELTS/#endif
+ */
+#define CAN_JUSTIFY_PUSH(x)
+#define CAN_JUSTIFY_POP
+#define CAN_JUSTIFY_SET(x)
+#define CAN_JUSTIFY_START
+#define CANT_JUSTIFY_THIS_LINE
+#define EMIT_IFDEF_USE_JUSTIFY_ELTS(x)
+#endif				/* USE_JUSTIFY_ELTS */
+#define CAN_JUSTIFY_PUSH_F CAN_JUSTIFY_PUSH(FALSE)
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYJUSTIFY_H */
diff --git a/src/LYKeymap.c b/src/LYKeymap.c
new file mode 100644
index 00000000..c0c4205a
--- /dev/null
+++ b/src/LYKeymap.c
@@ -0,0 +1,1821 @@
+/* $LynxId: LYKeymap.c,v 1.69 2009/11/21 17:05:33 Bela.Lubkin Exp $ */
+#include <HTUtils.h>
+#include <LYUtils.h>
+#include <LYGlobalDefs.h>
+#include <LYKeymap.h>
+#include <LYCharSets.h>		/* for LYlowest_eightbit - kw */
+#include <HTAccess.h>
+#include <HTFormat.h>
+#include <HTAlert.h>
+#include <LYStrings.h>		/* for USE_KEYMAP stuff - kw */
+
+#include <LYLeaks.h>
+
+#ifdef EXP_KEYBOARD_LAYOUT
+#include <jcuken_kb.h>
+#include <yawerty_kb.h>
+#include <rot13_kb.h>
+#endif
+
+#define PUTS(buf)    (*target->isa->put_block)(target, buf, (int) strlen(buf))
+
+#ifdef EXP_KEYBOARD_LAYOUT
+int current_layout = 0;		/* Index into LYKbLayouts[]   */
+
+LYKbLayout_t *LYKbLayouts[] =
+{
+    kb_layout_rot13,
+    kb_layout_jcuken,
+    kb_layout_yawerty
+};
+
+const char *LYKbLayoutNames[] =
+{
+    "ROT13'd keyboard layout",
+    "JCUKEN Cyrillic, for AT 101-key kbd",
+    "YAWERTY Cyrillic, for DEC LK201 kbd",
+    (char *) 0
+};
+#endif /* EXP_KEYBOARD_LAYOUT */
+
+struct _HTStream {
+    HTStreamClass *isa;
+};
+
+/* * * Tables mapping LynxKeyCodes to LynxActionCodes  * * */
+
+/*
+ * Lynxkeycodes include all single-byte keys as well as codes for function keys
+ * and some special purposes.  See LYStrings.h.  Extended lynxkeycode values
+ * can also contain flags for modifiers and other purposes, but here only the
+ * base values are mapped to lynxactioncodes.  They are called `keystrokes' in
+ * lynx.cfg.
+ *
+ * Lynxactioncodes (confusingly, constants are named LYK_foo and typed as
+ * LYKeymapCode) specify key `functions', see LYKeymap.h.
+ */
+
+/* the character gets 1 added to it before lookup,
+ * so that EOF maps to 0
+ */
+/* *INDENT-OFF* */
+LYKeymap_t keymap[KEYMAP_SIZE] = {
+
+0,
+/* EOF */
+
+LYK_DO_NOTHING,     LYK_HOME,       LYK_PREV_PAGE,     0,
+/* nul */           /* ^A */        /* ^B */       /* ^C */
+
+LYK_ABORT,          LYK_END,        LYK_NEXT_PAGE,     0,
+/* ^D */            /* ^E */        /* ^F */       /* ^G */
+
+LYK_HISTORY,    LYK_FASTFORW_LINK,  LYK_ACTIVATE,  LYK_COOKIE_JAR,
+/* bs */            /* ht */        /* nl */       /* ^K */
+
+LYK_REFRESH,      LYK_ACTIVATE,     LYK_DOWN_TWO,      0,
+/* ^L */            /* cr */        /* ^N */       /* ^O */
+
+LYK_UP_TWO,       LYK_CHG_CENTER,   LYK_RELOAD,    LYK_TO_CLIPBOARD,
+/* ^P */            /* XON */       /* ^R */       /* ^S */
+
+LYK_TRACE_TOGGLE,  LYK_NEXT_DOC,  LYK_SWITCH_DTD,  LYK_REFRESH,
+/* ^T */            /* ^U */        /* ^V */       /* ^W */
+
+#ifdef USE_CACHEJAR
+LYK_CACHE_JAR,          0,              0,             0,
+/* ^X */            /* ^Y */        /* ^Z */       /* ESC */
+#else
+0,                      0,              0,             0,
+/* ^X */            /* ^Y */        /* ^Z */       /* ESC */
+#endif
+
+0,                      0,              0,             0,
+/* ^\ */            /* ^] */        /* ^^ */       /* ^_ */
+
+LYK_NEXT_PAGE,       LYK_SHELL,  LYK_SOFT_DQUOTES,  LYK_TOOLBAR,
+/* sp */             /* ! */         /* " */        /* # */
+
+LYK_LAST_LINK,          0,              0,          LYK_HISTORICAL,
+/* $ */              /* % */         /* & */        /* ' */
+
+LYK_UP_HALF,      LYK_DOWN_HALF, LYK_IMAGE_TOGGLE,  LYK_NEXT_PAGE,
+/* ( */              /* ) */         /* * */        /* + */
+
+LYK_EXTERN_PAGE,  LYK_PREV_PAGE, LYK_EXTERN_LINK,   LYK_WHEREIS,
+/* , */              /* - */         /* . */        /* / */
+
+LYK_F_LINK_NUM,      LYK_1,          LYK_2,         LYK_3,
+/* 0 */              /* 1 */         /* 2 */        /* 3 */
+
+LYK_4,               LYK_5,          LYK_6,         LYK_7,
+/* 4 */              /* 5 */         /* 6 */        /* 7 */
+
+LYK_8,               LYK_9,         LYK_COMMAND,    LYK_TRACE_LOG,
+/* 8 */              /* 9 */         /* : */        /* ; */
+
+LYK_UP_LINK,         LYK_INFO,     LYK_DOWN_LINK,   LYK_HELP,
+/* < */              /* = */         /* > */        /* ? */
+
+#ifndef SUPPORT_CHDIR
+LYK_RAW_TOGGLE,      LYK_ADDRLIST, LYK_PREV_PAGE,   LYK_COMMENT,
+/* @ */              /* A */         /* B */        /* C */
+#else
+LYK_RAW_TOGGLE,      LYK_ADDRLIST, LYK_PREV_PAGE,   LYK_CHDIR,
+/* @ */              /* A */         /* B */        /* C */
+#endif
+
+LYK_DOWNLOAD,        LYK_ELGOTO,  LYK_DIRED_MENU,   LYK_ECGOTO,
+/* D */              /* E */         /* F */        /* G */
+
+#ifdef KANJI_CODE_OVERRIDE
+LYK_HELP,            LYK_INDEX,      LYK_CHG_KCODE, LYK_KEYMAP,
+/* H */              /* I */         /* J */        /* K */
+
+#else
+LYK_HELP,            LYK_INDEX,      LYK_JUMP,      LYK_KEYMAP,
+/* H */              /* I */         /* J */        /* K */
+#endif
+
+LYK_LIST,          LYK_MAIN_MENU,    LYK_PREV,      LYK_OPTIONS,
+/* L */              /* M */         /* N */        /* O */
+
+LYK_PRINT,          LYK_ABORT,    LYK_DEL_BOOKMARK, LYK_INDEX_SEARCH,
+/* P */              /* Q */         /* R */        /* S */
+
+LYK_TAG_LINK,      LYK_PREV_DOC,    LYK_VLINKS,         0,
+/* T */              /* U */         /* V */        /* W */
+
+LYK_NOCACHE,            0,        LYK_INTERRUPT,    LYK_INLINE_TOGGLE,
+/* X */              /* Y */         /* Z */        /* [ */
+
+LYK_SOURCE,          LYK_HEAD,    LYK_FIRST_LINK,   LYK_CLEAR_AUTH,
+/* \ */              /* ] */         /* ^ */        /* _ */
+
+LYK_MINIMAL,   LYK_ADD_BOOKMARK,  LYK_PREV_PAGE,    LYK_COMMENT,
+/* ` */              /* a */         /* b */        /* c */
+
+LYK_DOWNLOAD,        LYK_EDIT,    LYK_DIRED_MENU,   LYK_GOTO,
+/* d */              /* e */         /* f */        /* g */
+
+LYK_HELP,            LYK_INDEX,      LYK_JUMP,      LYK_KEYMAP,
+/* h */              /* i */         /* j */        /* k */
+
+LYK_LIST,         LYK_MAIN_MENU,     LYK_NEXT,      LYK_OPTIONS,
+/* l */              /* m */         /* n */        /* o */
+
+LYK_PRINT,           LYK_QUIT,    LYK_DEL_BOOKMARK, LYK_INDEX_SEARCH,
+/* p */              /* q */         /* r */        /* s */
+
+LYK_TAG_LINK,     LYK_PREV_DOC,   LYK_VIEW_BOOKMARK,   0,
+/* t */              /* u */         /* v */        /* w */
+
+LYK_NOCACHE,            0,          LYK_INTERRUPT, LYK_SHIFT_LEFT,
+/* x */              /* y */          /* z */       /* { */
+
+LYK_LINEWRAP_TOGGLE, LYK_SHIFT_RIGHT, LYK_NESTED_TABLES, LYK_HISTORY,
+/* | */               /* } */         /* ~ */       /* del */
+
+
+/* 80..9F (illegal ISO-8859-1) 8-bit characters. */
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+
+/* A0..FF (permissible ISO-8859-1) 8-bit characters. */
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+
+/* 100..10F function key definitions in LYStrings.h */
+LYK_PREV_LINK,    LYK_NEXT_LINK,    LYK_ACTIVATE,   LYK_PREV_DOC,
+/* UPARROW */     /* DNARROW */     /* RTARROW */   /* LTARROW */
+
+LYK_NEXT_PAGE,    LYK_PREV_PAGE,    LYK_HOME,       LYK_END,
+/* PGDOWN */      /* PGUP */        /* HOME */      /* END */
+
+#if (defined(_WINDOWS) || defined(__DJGPP__))
+
+LYK_DWIMHELP,          0,              0,             0,
+/* F1*/
+#else
+
+LYK_DWIMHELP,     LYK_ACTIVATE,     LYK_HOME,       LYK_END,
+/* F1*/ 	  /* Do key */      /* Find key */  /* Select key */
+
+#endif /* _WINDOWS || __DJGPP__ */
+
+LYK_UP_TWO,       LYK_DOWN_TWO,     LYK_DO_NOTHING, LYK_FASTBACKW_LINK,
+/* Insert key */  /* Remove key */  /* DO_NOTHING*/ /* Back tab */
+
+/* 110..18F */
+
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,             LYK_DO_NOTHING,      0,             0,
+               /* 0x11d: MOUSE_KEY */
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+#ifdef DJGPP_KEYHANDLER
+   0,                  LYK_ABORT,      0,             0,
+                       /* ALT_X */
+#else
+   0,                  0,              0,             0,
+#endif /* DJGPP_KEYHANDLER */
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+/* 190..20F */
+
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+#if (defined(_WINDOWS) || defined(__DJGPP__) || defined(__CYGWIN__)) && !defined(USE_SLANG) /* PDCurses */
+   LYK_ABORT,          0,              0,             0,
+   /* ALT_X */
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              LYK_WHEREIS,   0,
+                                       /* KP_SLASH */
+   0,                  0,              0,           LYK_IMAGE_TOGGLE,
+                                                    /* KP_* */
+   LYK_PREV_PAGE,      LYK_NEXT_PAGE,  0,             0,
+   /* KP_- */          /* KP_+ */
+#else
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+#endif /* (_WINDOWS || __DJGPP__ || __CYGWIN__) && !USE_SLANG */
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+/* 210..28F */
+
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   /* 290...293 */
+   LYK_CHANGE_LINK,    0,              0,             0,
+};
+
+#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE)
+/*
+ * This table is used to override the standard keyboard assignments
+ * when lynx_edit_mode is in effect and keyboard overrides have been
+ * allowed at compile time.
+ */
+
+LYKeymap_t key_override[KEYMAP_SIZE] = {
+
+    0,
+/* EOF */
+
+    0,                  0,              0,            0,
+/* nul */           /* ^A */        /* ^B */      /* ^C */
+
+    0,                  0,              0,            0,
+/* ^D */            /* ^E */        /* ^F */      /* ^G */
+
+    0,                  0,              0,            0,
+/* bs */            /* ht */        /* nl */      /* ^K */
+
+    0,                  0,              0,            0,
+/* ^L */            /* cr */        /* ^N */      /* ^O */
+
+    0,                  0,              0,            0,
+/* ^P */            /* XON */       /* ^R */      /* XOFF */
+
+    0,            LYK_NEXT_DOC,         0,            0,
+/* ^T */            /* ^U */        /* ^V */      /* ^W */
+
+    0,                  0,              0,            0,
+/* ^X */            /* ^Y */        /* ^Z */      /* ESC */
+
+    0,                  0,              0,            0,
+/* ^\ */            /* ^] */        /* ^^ */      /* ^_ */
+
+    0,                 0,              0,            0,
+/* sp */            /* ! */         /* " */       /* # */
+
+   0,                  0,              0,            0,
+/* $ */             /* % */         /* & */       /* ' */
+
+    0,                 0,              0,            0,
+/* ( */             /* ) */         /* * */       /* + */
+
+    0,                 0,         LYK_TAG_LINK,      0,
+/* , */             /* - */         /* . */       /* / */
+
+   0,                  0,              0,            0,
+/* 0 */             /* 1 */         /* 2 */       /* 3 */
+
+   0,                  0,              0,            0,
+/* 4 */             /* 5 */         /* 6 */       /* 7 */
+
+   0,                  0,              0,             0,
+/* 8 */             /* 9 */         /* : */        /* ; */
+
+   0,                  0,              0,             0,
+/* < */             /* = */         /* > */        /* ? */
+#ifndef SUPPORT_CHDIR
+   0,                  0,              0,         LYK_CREATE,
+/* @ */             /* A */         /* B */        /* C */
+#else
+   0,                  0,              0,         LYK_CHDIR,
+/* @ */             /* A */         /* B */        /* C */
+#endif
+
+   0,                  0,        LYK_DIRED_MENU,       0,
+/* D */             /* E */         /* F */        /* G */
+
+   0,                  0,              0,             0,
+/* H */             /* I */         /* J */        /* K */
+
+   0,             LYK_MODIFY,          0,             0,
+/* L */             /* M */         /* N */        /* O */
+
+   0,                  0,         LYK_REMOVE,         0,
+/* P */             /* Q */         /* R */        /* S */
+
+LYK_TAG_LINK,     LYK_UPLOAD,          0,             0,
+/* T */             /* U */         /* V */        /* W */
+
+   0,                  0,              0,             0,
+/* X */             /* Y */         /* Z */        /* [ */
+
+   0,                  0,              0,             0,
+/* \ */             /* ] */         /* ^ */        /* _ */
+
+0,                     0,              0,         LYK_CREATE,
+/* ` */             /* a */         /* b */        /* c */
+
+   0,                  0,       LYK_DIRED_MENU,       0,
+/* d */             /* e */         /* f */        /* g */
+
+   0,                  0,              0,             0,
+/* h */             /* i */         /* j */        /* k */
+
+0,                LYK_MODIFY,          0,             0,
+/* l */             /* m */         /* n */        /* o */
+
+   0,                  0,          LYK_REMOVE,        0,
+/* p */             /* q */         /* r */        /* s */
+
+LYK_TAG_LINK,      LYK_UPLOAD,         0,             0,
+/* t */             /* u */         /* v */         /* w */
+
+   0,                  0,               0,            0,
+/* x */             /* y */          /* z */       /* { */
+
+   0,                   0,             0,              0,
+/* | */              /* } */         /* ~ */       /* del */
+
+/* 80..9F (illegal ISO-8859-1) 8-bit characters. */
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+
+/* A0..FF (permissible ISO-8859-1) 8-bit characters. */
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+
+/* 100..10F function key definitions in LYStrings.h */
+   0,                   0,             0,              0,
+/* UPARROW */     /* DNARROW */     /* RTARROW */   /* LTARROW */
+
+   0,                  0,              0,              0,
+/* PGDOWN */      /* PGUP */        /* HOME */      /* END */
+
+   0,                  0,              0,              0,
+/* F1*/ 	  /* Do key */      /* Find key */  /* Select key */
+
+   0,                  0,           LYK_DO_NOTHING,    0,
+/* Insert key */  /* Remove key */  /* DO_NOTHING */ /* Back tab */
+
+/* 110..18F */
+
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+/* 190..20F */
+
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+/* 210..28F */
+
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   0,                  0,              0,             0,
+   /* 290...293 */
+   0,                  0,              0,             0,
+};
+#endif /* DIRED_SUPPORT && OK_OVERRIDE */
+
+#define DATA(code, name, doc) { code, name, doc }
+/* The order of this array must match the LYKeymapCode enum in LYKeymap.h */
+static Kcmd revmap[] = {
+    DATA(
+	LYK_UNKNOWN, "UNMAPPED",
+	NULL ),
+    DATA(
+	LYK_COMMAND, "COMMAND",
+	"prompt for, execute a command" ),
+    DATA(
+	LYK_1, "1",
+	NULL ),
+    DATA(
+	LYK_2, "2",
+	NULL ),
+    DATA(
+	LYK_3, "3",
+	NULL ),
+    DATA(
+	LYK_4, "4",
+	NULL ),
+    DATA(
+	LYK_5, "5",
+	NULL ),
+    DATA(
+	LYK_6, "6",
+	NULL ),
+    DATA(
+	LYK_7, "7",
+	NULL ),
+    DATA(
+	LYK_8, "8",
+	NULL ),
+    DATA(
+	LYK_9, "9",
+	NULL ),
+    DATA(
+	LYK_SOURCE, "SOURCE",
+	"toggle source/presentation for current document" ),
+    DATA(
+	LYK_RELOAD, "RELOAD",
+	"reload the current document" ),
+    DATA(
+	LYK_QUIT, "QUIT",
+	"quit the browser" ),
+    DATA(
+	LYK_ABORT, "ABORT",
+	"quit the browser unconditionally" ),
+    DATA(
+	LYK_NEXT_PAGE, "NEXT_PAGE",
+	"view the next page of the document" ),
+    DATA(
+	LYK_PREV_PAGE, "PREV_PAGE",
+	"view the previous page of the document" ),
+    DATA(
+	LYK_UP_TWO, "UP_TWO",
+	"go back two lines in the document" ),
+    DATA(
+	LYK_DOWN_TWO, "DOWN_TWO",
+	"go forward two lines in the document" ),
+    DATA(
+	LYK_UP_HALF, "UP_HALF",
+	"go back half a page in the document" ),
+    DATA(
+	LYK_DOWN_HALF, "DOWN_HALF",
+	"go forward half a page in the document" ),
+    DATA(
+	LYK_REFRESH, "REFRESH",
+	"refresh the screen to clear garbled text" ),
+    DATA(
+	LYK_HOME, "HOME",
+	"go to the beginning of the current document" ),
+    DATA(
+	LYK_END, "END",
+	"go to the end of the current document" ),
+    DATA(
+	LYK_FIRST_LINK, "FIRST_LINK",
+	"make the first link on the line current" ),
+    DATA(
+	LYK_LAST_LINK, "LAST_LINK",
+	"make the last link on the line current" ),
+    DATA(
+	LYK_PREV_LINK, "PREV_LINK",
+	"make the previous link current" ),
+    DATA(
+	LYK_NEXT_LINK, "NEXT_LINK",
+	"make the next link current" ),
+    DATA(
+	LYK_LPOS_PREV_LINK, "LPOS_PREV_LINK",
+	"make previous link current, same column for input" ),
+    DATA(
+	LYK_LPOS_NEXT_LINK, "LPOS_NEXT_LINK",
+	"make next link current, same column for input" ),
+    DATA(
+	LYK_FASTBACKW_LINK, "FASTBACKW_LINK",
+	"previous link or text area, only stops on links" ),
+    DATA(
+	LYK_FASTFORW_LINK, "FASTFORW_LINK",
+	"next link or text area, only stops on links" ),
+    DATA(
+	LYK_UP_LINK, "UP_LINK",
+	"move up the page to a previous link" ),
+    DATA(
+	LYK_DOWN_LINK, "DOWN_LINK",
+	"move down the page to another link" ),
+    DATA(
+	LYK_RIGHT_LINK, "RIGHT_LINK",
+	"move right to another link" ),
+    DATA(
+	LYK_LEFT_LINK, "LEFT_LINK",
+	"move left to a previous link" ),
+    DATA(
+	LYK_HISTORY, "HISTORY",
+	"display stack of currently-suspended documents" ),
+    DATA(
+	LYK_PREV_DOC, "PREV_DOC",
+	"go back to the previous document" ),
+    DATA(
+	LYK_NEXT_DOC, "NEXT_DOC",
+	"undo going back to the previous document" ),
+    DATA(
+	LYK_ACTIVATE, "ACTIVATE",
+	"go to the document given by the current link" ),
+    DATA(
+	LYK_SUBMIT, "MOUSE_SUBMIT",
+	"DO NOT MAP:  follow current link, submit" ),
+    DATA(
+	LYK_GOTO, "GOTO",
+	"go to a document given as a URL" ),
+    DATA(
+	LYK_ECGOTO, "ECGOTO",
+	"edit the current document's URL and go to it" ),
+    DATA(
+	LYK_HELP, "HELP",
+	"display help on using the browser" ),
+    DATA(
+	LYK_DWIMHELP, "DWIMHELP",
+	"display help page that may depend on context" ),
+    DATA(
+	LYK_INDEX, "INDEX",
+	"display an index of potentially useful documents" ),
+    DATA(
+	LYK_NOCACHE, "NOCACHE",
+	"force submission of form or link with no-cache" ),
+    DATA(
+	LYK_INTERRUPT, "INTERRUPT",
+	"interrupt network connection or transmission" ),
+    DATA(
+	LYK_MAIN_MENU, "MAIN_MENU",
+	"return to the first screen (home page)" ),
+    DATA(
+	LYK_OPTIONS, "OPTIONS",
+	"display and change option settings" ),
+    DATA(
+	LYK_INDEX_SEARCH, "INDEX_SEARCH",
+	"allow searching of an index" ),
+    DATA(
+	LYK_WHEREIS, "WHEREIS",
+	"search within the current document" ),
+    DATA(
+	LYK_PREV, "PREV",
+	"search for the previous occurence" ),
+    DATA(
+	LYK_NEXT, "NEXT",
+	"search for the next occurence" ),
+    DATA(
+	LYK_COMMENT, "COMMENT",
+	"send a comment to the author of the current document" ),
+    DATA(
+	LYK_EDIT, "EDIT",
+	"edit the current document or a form's textarea" ),
+    DATA(
+	LYK_INFO, "INFO",
+	"display information on the current document and link" ),
+    DATA(
+	LYK_PRINT, "PRINT",
+	"display choices for printing the current document" ),
+    DATA(
+	LYK_ADD_BOOKMARK, "ADD_BOOKMARK",
+	"add to your personal bookmark list" ),
+    DATA(
+	LYK_DEL_BOOKMARK, "DEL_BOOKMARK",
+	"delete from your personal bookmark list" ),
+    DATA(
+	LYK_VIEW_BOOKMARK, "VIEW_BOOKMARK",
+	"view your personal bookmark list" ),
+    DATA(
+	LYK_VLINKS, "VLINKS",
+	"list links visited during the current Lynx session" ),
+    DATA(
+	LYK_SHELL, "SHELL",
+	"escape from the browser to the system" ),
+    DATA(
+	LYK_DOWNLOAD, "DOWNLOAD",
+	"download the current link to your computer" ),
+    DATA(
+	LYK_TRACE_TOGGLE, "TRACE_TOGGLE",
+	"toggle tracing of browser operations" ),
+    DATA(
+	LYK_TRACE_LOG, "TRACE_LOG",
+	"view trace log if started in the current session" ),
+    DATA(
+	LYK_IMAGE_TOGGLE, "IMAGE_TOGGLE",
+	"toggle handling of all images as links" ),
+    DATA(
+	LYK_INLINE_TOGGLE, "INLINE_TOGGLE",
+	"toggle pseudo-ALTs for inlines with no ALT string" ),
+    DATA(
+	LYK_HEAD, "HEAD",
+	"send a HEAD request for the current document or link" ),
+    DATA(
+	LYK_DO_NOTHING, "DO_NOTHING",
+	NULL ),
+    DATA(
+	LYK_TOGGLE_HELP, "TOGGLE_HELP",
+	"show other commands in the novice help menu" ),
+    DATA(
+	LYK_JUMP, "JUMP",
+	"go directly to a target document or action" ),
+    DATA(
+	LYK_KEYMAP, "KEYMAP",
+	"display the current key map" ),
+    DATA(
+	LYK_LIST, "LIST",
+	"list the references (links) in the current document" ),
+    DATA(
+	LYK_TOOLBAR, "TOOLBAR",
+	"go to Toolbar or Banner in the current document" ),
+    DATA(
+	LYK_HISTORICAL, "HISTORICAL",
+	"toggle historical vs.  valid/minimal comment parsing" ),
+    DATA(
+	LYK_MINIMAL, "MINIMAL",
+	"toggle minimal vs.  valid comment parsing" ),
+    DATA(
+	LYK_SOFT_DQUOTES, "SOFT_DQUOTES",
+	"toggle valid vs.  soft double-quote parsing" ),
+    DATA(
+	LYK_RAW_TOGGLE, "RAW_TOGGLE",
+	"toggle raw 8-bit translations or CJK mode ON or OFF" ),
+    DATA(
+	LYK_COOKIE_JAR, "COOKIE_JAR",
+	"examine the Cookie Jar" ),
+    DATA(
+	LYK_F_LINK_NUM, "F_LINK_NUM",
+	"invoke the 'Follow link (or page) number:' prompt" ),
+    DATA(
+	LYK_CLEAR_AUTH, "CLEAR_AUTH",
+	"clear all authorization info for this session" ),
+    DATA(
+	LYK_SWITCH_DTD, "SWITCH_DTD",
+	"switch between two ways of parsing HTML" ),
+    DATA(
+	LYK_ELGOTO, "ELGOTO",
+	"edit the current link's URL or ACTION and go to it" ),
+    DATA(
+	LYK_CHANGE_LINK, "CHANGE_LINK",
+	"force reset of the current link on the page" ),
+    DATA(
+	LYK_DWIMEDIT, "DWIMEDIT",
+	"use external editor for context-dependent purpose" ),
+    DATA(
+	LYK_EDIT_TEXTAREA, "EDITTEXTAREA",
+	"use an external editor to edit a form's textarea" ),
+    DATA(
+	LYK_GROW_TEXTAREA, "GROWTEXTAREA",
+	"add 5 new blank lines to the bottom of a textarea" ),
+    DATA(
+	LYK_INSERT_FILE, "INSERTFILE",
+	"insert file into a textarea (just above cursorline)" ),
+#ifdef EXP_ADDRLIST_PAGE
+    DATA(
+	LYK_ADDRLIST, "ADDRLIST",
+	"like LIST command, but always shows the links' URLs" ),
+#endif
+#ifdef USE_EXTERNALS
+    DATA(
+	LYK_EXTERN_LINK, "EXTERN_LINK",
+	"run external program with current link" ),
+    DATA(
+	LYK_EXTERN_PAGE, "EXTERN_PAGE",
+	"run external program with current page" ),
+#endif
+#ifdef VMS
+    DATA(
+	LYK_DIRED_MENU, "DIRED_MENU",
+	"invoke File/Directory Manager, if available" ),
+#else
+#ifdef DIRED_SUPPORT
+    DATA(
+	LYK_DIRED_MENU, "DIRED_MENU",
+	"display a full menu of file operations" ),
+    DATA(
+	LYK_CREATE, "CREATE",
+	"create a new file or directory" ),
+    DATA(
+	LYK_REMOVE, "REMOVE",
+	"remove a file or directory" ),
+    DATA(
+	LYK_MODIFY, "MODIFY",
+	"modify the name or location of a file or directory" ),
+    DATA(
+	LYK_TAG_LINK, "TAG_LINK",
+	"tag a file or directory for later action" ),
+    DATA(
+	LYK_UPLOAD, "UPLOAD",
+	"upload from your computer to the current directory" ),
+    DATA(
+	LYK_INSTALL, "INSTALL",
+	"install file or tagged files into a system area" ),
+#endif /* DIRED_SUPPORT */
+    DATA(
+	LYK_CHG_CENTER, "CHANGE_CENTER",
+	"toggle center alignment in HTML TABLE" ),
+#ifdef KANJI_CODE_OVERRIDE
+    DATA(
+	LYK_CHG_KCODE, "CHANGE_KCODE",
+	"Change Kanji code" ),
+#endif
+#endif /* VMS */
+#ifdef SUPPORT_CHDIR
+    DATA(
+	LYK_CHDIR, "CHDIR",
+	"change current directory" ),
+#endif
+#ifdef USE_CURSES_PADS
+    DATA(
+	LYK_SHIFT_LEFT, "SHIFT_LEFT",
+	"shift the screen left" ),
+    DATA(
+	LYK_SHIFT_RIGHT, "SHIFT_RIGHT",
+	"shift the screen right" ),
+    DATA(
+	LYK_LINEWRAP_TOGGLE, "LINEWRAP_TOGGLE",
+	"toggle linewrap on/off" ),
+#endif
+#ifdef CAN_CUT_AND_PASTE
+    DATA(
+	LYK_PASTE_URL, "PASTE_URL",
+	"Goto the URL in the clipboard" ),
+    DATA(
+	LYK_TO_CLIPBOARD, "TO_CLIPBOARD",
+	"link's URL to Clip Board" ),
+#endif
+#ifdef EXP_NESTED_TABLES
+    DATA(
+	LYK_NESTED_TABLES, "NESTED_TABLES",
+	"toggle nested-table parsing on/off" ),
+#endif
+#ifdef USE_CACHEJAR
+    DATA(
+	LYK_CACHE_JAR, "CACHE_JAR",
+	"examine list of cached documents" ),
+#endif
+    DATA(
+	LYK_UNKNOWN, NULL,
+	"" )
+};
+#undef DATA
+
+static const struct {
+    int key;
+    const char *name;
+} named_keys[] = {
+    { '\t',		"<tab>" },
+    { '\r',		"<return>" },
+    { CH_ESC,		"ESC" },
+    { ' ',		"<space>" },
+    { '<',		"<" },
+    { '>',		">" },
+    { CH_DEL,		"<delete>" },
+    { UPARROW,		"Up Arrow" },
+    { DNARROW,		"Down Arrow" },
+    { RTARROW,		"Right Arrow" },
+    { LTARROW,		"Left Arrow" },
+    { PGDOWN,		"Page Down" },
+    { PGUP,		"Page Up" },
+    { HOME,		"Home" },
+    { END_KEY,		"End" },
+    { F1,		"F1" },
+    { DO_KEY,		"Do key" },
+    { FIND_KEY,		"Find key" },
+    { SELECT_KEY,	"Select key" },
+    { INSERT_KEY,	"Insert key" },
+    { REMOVE_KEY,	"Remove key" },
+    { DO_NOTHING,	"(DO_NOTHING)" },
+    { BACKTAB_KEY,	"Back Tab" },
+    { MOUSE_KEY,	"mouse pseudo key" },
+};
+
+struct emap {
+    const char *name;
+    const int   code;
+    const char *descr;
+};
+
+static struct emap ekmap[] = {
+  {"NOP",	LYE_NOP,	"Do Nothing"},
+  {"CHAR",	LYE_CHAR,	"Insert printable char"},
+  {"ENTER",	LYE_ENTER,	"Input complete, return char/lynxkeycode"},
+  {"TAB",	LYE_TAB,	"Input complete, return TAB"},
+  {"STOP",	LYE_STOP,	"Input deactivated"},
+  {"ABORT",	LYE_ABORT,	"Input cancelled"},
+
+  {"PASS",	LYE_FORM_PASS,  "In fields: input complete, or Do Nothing"},
+
+  {"DELN",	LYE_DELN,	"Delete next/curr char"},
+  {"DELP",	LYE_DELP,	"Delete prev      char"},
+  {"DELNW",	LYE_DELNW,	"Delete next word"},
+  {"DELPW",	LYE_DELPW,	"Delete prev word"},
+
+  {"ERASE",	LYE_ERASE,	"Erase the line"},
+
+  {"BOL",	LYE_BOL,	"Go to begin of line"},
+  {"EOL",	LYE_EOL,	"Go to end   of line"},
+  {"FORW",	LYE_FORW,	"Cursor forwards"},
+  {"FORW_RL",	LYE_FORW_RL,	"Cursor forwards or right link"},
+  {"BACK",	LYE_BACK,	"Cursor backwards"},
+  {"BACK_LL",	LYE_BACK_LL,	"Cursor backwards or left link"},
+  {"FORWW",	LYE_FORWW,	"Word forward"},
+  {"BACKW",	LYE_BACKW,	"Word back"},
+
+  {"LOWER",	LYE_LOWER,	"Lower case the line"},
+  {"UPPER",	LYE_UPPER,	"Upper case the line"},
+
+  {"LKCMD",	LYE_LKCMD,	"Invoke command prompt"},
+
+  {"AIX",	LYE_AIX,	"Hex 97"},
+
+  {"DELBL",	LYE_DELBL,	"Delete back to BOL"},
+  {"DELEL",	LYE_DELEL,	"Delete thru EOL"},
+
+  {"SWMAP",	LYE_SWMAP,	"Switch input keymap"},
+
+  {"TPOS",	LYE_TPOS,	"Transpose characters"},
+
+  {"SETM1",	LYE_SETM1,	"Set modifier 1 flag"},
+  {"SETM2",	LYE_SETM2,	"Set modifier 2 flag"},
+  {"UNMOD",	LYE_UNMOD,	"Fall back to no-modifier command"},
+
+  {"C1CHAR",	LYE_C1CHAR,	"Insert C1 char if printable"},
+
+  {"SETMARK",	LYE_SETMARK,	"emacs-like set-mark-command"},
+  {"XPMARK",	LYE_XPMARK,	"emacs-like exchange-point-and-mark"},
+  {"KILLREG",	LYE_KILLREG,	"emacs-like kill-region"},
+  {"YANK",	LYE_YANK,	"emacs-like yank"},
+#ifdef CAN_CUT_AND_PASTE
+  {"PASTE",	LYE_PASTE,	"ClipBoard to Lynx"},
+#endif
+};
+/* *INDENT-ON* */
+
+/*
+ * Build a list of Lynx's commands, for use in the tab-completion in LYgetstr.
+ */
+HTList *LYcommandList(void)
+{
+    static HTList *myList = NULL;
+
+    if (myList == NULL) {
+	unsigned j;
+
+	myList = HTList_new();
+	for (j = 0; revmap[j].name != 0; j++) {
+	    if (revmap[j].doc != 0) {
+		char *data = NULL;
+
+		StrAllocCopy(data, revmap[j].name);
+		HTList_addObject(myList, data);
+	    }
+	}
+    }
+    return myList;
+}
+
+/*
+ * Find the given keycode.
+ */
+Kcmd *LYKeycodeToKcmd(LYKeymapCode code)
+{
+    unsigned j;
+    Kcmd *result = 0;
+
+    if (code > LYK_UNKNOWN) {
+	for (j = 0; revmap[j].name != 0; j++) {
+	    if (revmap[j].code == code) {
+		result = revmap + j;
+		break;
+	    }
+	}
+    }
+    return result;
+}
+
+/*
+ * Find the given command-name, accepting an abbreviation if it is unique.
+ */
+Kcmd *LYStringToKcmd(const char *name)
+{
+    unsigned need = strlen(name);
+    unsigned j;
+    BOOL exact = FALSE;
+    Kcmd *result = 0;
+    Kcmd *maybe = 0;
+
+    if (non_empty(name)) {
+	for (j = 0; revmap[j].name != 0; j++) {
+	    if (!strcasecomp(revmap[j].name, name)) {
+		result = revmap + j;
+		break;
+	    } else if (!exact
+		       && !strncasecomp(revmap[j].name, name, (int) need)) {
+		if (maybe == 0) {
+		    maybe = revmap + j;
+		} else {
+		    if (revmap[j].name[need] != 0
+			&& maybe->name[need] != 0) {
+			maybe = 0;
+			exact = TRUE;
+		    }
+		}
+	    }
+	}
+    }
+    return (result != 0) ? result : maybe;
+}
+
+char *LYKeycodeToString(int c,
+			BOOLEAN upper8)
+{
+    static char buf[30];
+    unsigned n;
+    BOOLEAN named = FALSE;
+
+    for (n = 0; n < TABLESIZE(named_keys); n++) {
+	if (named_keys[n].key == c) {
+	    named = TRUE;
+	    strcpy(buf, named_keys[n].name);
+	    break;
+	}
+    }
+
+    if (!named) {
+	if (c <= 0377
+	    && TOASCII(c) > TOASCII(' ')
+	    && TOASCII(c) < 0177)
+	    sprintf(buf, "%c", c);
+	else if (upper8
+		 && TOASCII(c) > TOASCII(' ')
+		 && c <= 0377
+		 && c <= LYlowest_eightbit[current_char_set])
+	    sprintf(buf, "%c", c);
+	else if (TOASCII(c) < TOASCII(' '))
+	    sprintf(buf, "^%c", FROMASCII(TOASCII(c) | 0100));
+	else if (c >= 0400)
+	    sprintf(buf, "key-0x%x", c);
+	else
+	    sprintf(buf, "0x%x", c);
+    }
+    return buf;
+}
+
+int LYStringToKeycode(char *src)
+{
+    unsigned n;
+    int key = -1;
+    int len = (int) strlen(src);
+
+    if (len == 1) {
+	key = *src;
+    } else if (len == 2 && *src == '^') {
+	key = src[1] & 0x1f;
+    } else if (len > 2 && !strncasecomp(src, "0x", 2)) {
+	char *dst = 0;
+
+	key = strtol(src, &dst, 0);
+	if (non_empty(dst))
+	    key = -1;
+    } else if (len > 6 && !strncasecomp(src, "key-", 4)) {
+	char *dst = 0;
+
+	key = strtol(src + 4, &dst, 0);
+	if (isEmpty(dst))
+	    key = -1;
+    }
+    if (key < 0) {
+	for (n = 0; n < TABLESIZE(named_keys); n++) {
+	    if (!strcasecomp(named_keys[n].name, src)) {
+		key = named_keys[n].key;
+		break;
+	    }
+	}
+    }
+    return key;
+}
+
+#define PRETTY_LEN 11
+
+static char *pretty_html(int c)
+{
+    char *src = LYKeycodeToString(c, TRUE);
+
+    if (src != 0) {
+	/* *INDENT-OFF* */
+	static const struct {
+	    int	code;
+	    const char *name;
+	} table[] = {
+	    { '<',	"&lt;" },
+	    { '>',	"&gt;" },
+	    { '"',	"&quot;" },
+	    { '&',	"&amp;" }
+	};
+	/* *INDENT-ON* */
+
+	static char buf[30];
+	char *dst = buf;
+	int adj = 0;
+	unsigned n;
+	BOOLEAN found;
+
+	while ((c = *src++) != 0) {
+	    found = FALSE;
+	    for (n = 0; n < TABLESIZE(table); n++) {
+		if (c == table[n].code) {
+		    found = TRUE;
+		    strcpy(dst, table[n].name);
+		    adj += (int) strlen(dst) - 1;
+		    dst += (int) strlen(dst);
+		    break;
+		}
+	    }
+	    if (!found) {
+		*dst++ = (char) c;
+	    }
+	}
+	adj -= (dst - buf) - PRETTY_LEN;
+	while (adj-- > 0)
+	    *dst++ = ' ';
+	*dst = 0;
+	return buf;
+    }
+
+    return 0;
+}
+
+static char *format_binding(LYKeymap_t * table, int i)
+{
+    LYKeymapCode the_key = (LYKeymapCode) table[i];
+    char *buf = 0;
+    char *formatted;
+    Kcmd *rmap = LYKeycodeToKcmd(the_key);
+
+    if (rmap != 0
+	&& rmap->name != 0
+	&& rmap->doc != 0
+	&& (formatted = pretty_html(i - 1)) != 0) {
+	HTSprintf0(&buf, "%-*s %-13s %s\n",
+		   PRETTY_LEN, formatted,
+		   rmap->name,
+		   rmap->doc);
+	return buf;
+    }
+    return 0;
+}
+
+/* if both is true, produce an additional line for the corresponding
+   uppercase key if its binding is different. - kw */
+static void print_binding(HTStream *target, int i,
+			  BOOLEAN both)
+{
+    char *buf;
+    LYKeymapCode lac1 = LYK_UNKNOWN;	/* 0 */
+
+#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE)
+    if (prev_lynx_edit_mode && !no_dired_support &&
+	(lac1 = (LYKeymapCode) key_override[i]) != LYK_UNKNOWN) {
+	if ((buf = format_binding(key_override, i)) != 0) {
+	    PUTS(buf);
+	    FREE(buf);
+	}
+    } else
+#endif /* DIRED_SUPPORT && OK_OVERRIDE */
+    if ((buf = format_binding(keymap, i)) != 0) {
+	lac1 = (LYKeymapCode) keymap[i];
+	PUTS(buf);
+	FREE(buf);
+    }
+
+    if (!both)
+	return;
+    i -= ' ';			/* corresponding uppercase key */
+
+#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE)
+    if (prev_lynx_edit_mode && !no_dired_support && key_override[i]) {
+	if (key_override[i] != lac1 &&
+	    (buf = format_binding(key_override, i)) != 0) {
+	    PUTS(buf);
+	    FREE(buf);
+	}
+    } else
+#endif /* DIRED_SUPPORT && OK_OVERRIDE */
+    if (keymap[i] != lac1 && (buf = format_binding(keymap, i)) != 0) {
+	PUTS(buf);
+	FREE(buf);
+    }
+}
+
+/*
+ * Return lynxactioncode whose name is the string func.  returns -1 if not
+ * found.  - kw
+ */
+int lacname_to_lac(const char *func)
+{
+    Kcmd *mp = LYStringToKcmd(func);
+
+    return (mp != 0) ? (int) mp->code : -1;
+}
+
+/*
+ * Return editactioncode whose name is the string func.  func must be present
+ * in the ekmap table.  returns -1 if not found.  - kw
+ */
+int lecname_to_lec(const char *func)
+{
+    int i;
+    struct emap *mp;
+
+    if (non_empty(func)) {
+	for (i = 0, mp = ekmap; (*mp).name != NULL; mp++, i++) {
+	    if (strcmp((*mp).name, func) == 0) {
+		return (*mp).code;
+	    }
+	}
+    }
+    return (-1);
+}
+
+/*
+ * Return lynxkeycode represented by string src.  returns -1 if not valid.
+ *
+ * This is simpler than what map_string_to_keysym() does for USE_KEYMAP, but
+ * compatible with revmap() used for processing KEYMAP options in the
+ * configuration file.  - kw
+ */
+int lkcstring_to_lkc(const char *src)
+{
+    int c = -1;
+
+    if (strlen(src) == 1)
+	c = *src;
+    else if (strlen(src) == 2 && *src == '^')
+	c = src[1] & 037;
+    else if (strlen(src) >= 2 && isdigit(UCH(*src))) {
+	if (sscanf(src, "%d", &c) != 1)
+	    return (-1);
+#ifdef USE_KEYMAPS
+    } else {
+	map_string_to_keysym(src, &c);
+#ifndef USE_SLANG
+	if (c >= 0) {
+	    if ((c & LKC_MASK) > 255 && !(c & LKC_ISLKC))
+		return (-1);	/* Don't accept untranslated curses KEY_* */
+	    else
+		c &= ~LKC_ISLKC;
+	}
+#endif
+#endif
+    }
+    if (c == CH_ESC)
+	escape_bound = 1;
+    if (c < -1)
+	return (-1);
+    else
+	return c;
+}
+
+static int LYLoadKeymap(const char *arg GCC_UNUSED,
+			HTParentAnchor *anAnchor,
+			HTFormat format_out,
+			HTStream *sink)
+{
+    HTFormat format_in = WWW_HTML;
+    HTStream *target;
+    char *buf = 0;
+    int i;
+
+    /*
+     * Set up the stream.  - FM
+     */
+    target = HTStreamStack(format_in, format_out, sink, anAnchor);
+    if (!target || target == NULL) {
+	HTSprintf0(&buf, CANNOT_CONVERT_I_TO_O,
+		   HTAtom_name(format_in), HTAtom_name(format_out));
+	HTAlert(buf);
+	FREE(buf);
+	return (HT_NOT_LOADED);
+    }
+    anAnchor->no_cache = TRUE;
+
+    HTSprintf0(&buf, "<html>\n<head>\n<title>%s</title>\n</head>\n<body>\n",
+	       CURRENT_KEYMAP_TITLE);
+    PUTS(buf);
+    HTSprintf0(&buf, "<pre>\n");
+    PUTS(buf);
+
+    for (i = 'a' + 1; i <= 'z' + 1; i++) {
+	print_binding(target, i, TRUE);
+    }
+    for (i = 1; i < KEYMAP_SIZE; i++) {
+	/*
+	 * Don't show CHANGE_LINK if mouse not enabled.
+	 */
+	if ((i >= 0200 || i <= ' ' || !isalpha(i - 1)) &&
+	    (LYUseMouse || (keymap[i] != LYK_CHANGE_LINK))) {
+	    print_binding(target, i, FALSE);
+	}
+    }
+
+    HTSprintf0(&buf, "</pre>\n</body>\n</html>\n");
+    PUTS(buf);
+
+    (*target->isa->_free) (target);
+    FREE(buf);
+    return (HT_LOADED);
+}
+
+#ifdef GLOBALDEF_IS_MACRO
+#define _LYKEYMAP_C_GLOBALDEF_1_INIT { "LYNXKEYMAP", LYLoadKeymap, 0}
+GLOBALDEF(HTProtocol, LYLynxKeymap, _LYKEYMAP_C_GLOBALDEF_1_INIT);
+#else
+GLOBALDEF HTProtocol LYLynxKeymap =
+{"LYNXKEYMAP", LYLoadKeymap, 0};
+#endif /* GLOBALDEF_IS_MACRO */
+
+/*
+ * Install func as the mapping for key.
+ * If for_dired is TRUE, install it in the key_override[] table
+ * for Dired mode, otherwise in the general keymap[] table.
+ * If DIRED_SUPPORT or OK_OVERRIDE is not defined, don't do anything
+ * when for_dired is requested.
+ * returns lynxkeycode value != 0 if the mapping was made, 0 if not.
+ */
+int remap(char *key,
+	  const char *func,
+	  BOOLEAN for_dired)
+{
+    Kcmd *mp;
+    int c;
+
+#if !defined(DIRED_SUPPORT) || !defined(OK_OVERRIDE)
+    if (for_dired)
+	return 0;
+#endif
+    if (func == NULL)
+	return 0;
+    c = lkcstring_to_lkc(key);
+    if (c <= -1)
+	return 0;
+    else if (c >= 0) {
+	/* Remapping of key actions is supported only for basic
+	 * lynxkeycodes, without modifiers etc.!  If we get somehow
+	 * called for an invalid lynxkeycode, fail or silently ignore
+	 * modifiers. - kw
+	 */
+	if (c & (LKC_ISLECLAC | LKC_ISLAC))
+	    return 0;
+	if ((c & LKC_MASK) != c)
+	    c &= LKC_MASK;
+    }
+    if (c + 1 >= KEYMAP_SIZE)
+	return 0;
+    if ((mp = LYStringToKcmd(func)) != 0) {
+#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE)
+	if (for_dired)
+	    key_override[c + 1] = mp->code;
+	else
+#endif
+	    keymap[c + 1] = (LYKeymap_t) mp->code;
+	return (c ? c : (int) LAC_TO_LKC0(mp->code));	/* don't return 0, successful */
+    }
+    return 0;
+}
+
+typedef struct {
+    int code;
+    LYKeymap_t map;
+    LYKeymap_t save;
+} ANY_KEYS;
+
+/*
+ * Save the given keys in the table, setting them to the map'd value.
+ */
+static void set_any_keys(ANY_KEYS * table, int size)
+{
+    int j, k;
+
+    for (j = 0; j < size; ++j) {
+	k = table[j].code + 1;
+	table[j].save = keymap[k];
+	keymap[k] = table[j].map;
+    }
+}
+
+/*
+ * Restore the given keys from the table.
+ */
+static void reset_any_keys(ANY_KEYS * table, int size)
+{
+    int j, k;
+
+    for (j = 0; j < size; ++j) {
+	k = table[j].code + 1;
+	keymap[k] = table[j].save;
+    }
+}
+
+static ANY_KEYS vms_keys_table[] =
+{
+    {26, LYK_ABORT, 0},		/* control-Z */
+    {'$', LYK_SHELL, 0},
+};
+
+void set_vms_keys(void)
+{
+    set_any_keys(vms_keys_table, TABLESIZE(vms_keys_table));
+}
+
+static ANY_KEYS vi_keys_table[] =
+{
+    {'h', LYK_PREV_DOC, 0},
+    {'j', LYK_NEXT_LINK, 0},
+    {'k', LYK_PREV_LINK, 0},
+    {'l', LYK_ACTIVATE, 0},
+};
+
+static BOOLEAN did_vi_keys;
+
+void set_vi_keys(void)
+{
+    set_any_keys(vi_keys_table, TABLESIZE(vi_keys_table));
+    did_vi_keys = TRUE;
+}
+
+void reset_vi_keys(void)
+{
+    if (did_vi_keys) {
+	reset_any_keys(vi_keys_table, TABLESIZE(vi_keys_table));
+	did_vi_keys = FALSE;
+    }
+}
+
+static ANY_KEYS emacs_keys_table[] =
+{
+    {2, LYK_PREV_DOC, 0},	/* ^B */
+    {14, LYK_NEXT_LINK, 0},	/* ^N */
+    {16, LYK_PREV_LINK, 0},	/* ^P */
+    {6, LYK_ACTIVATE, 0},	/* ^F */
+};
+
+static BOOLEAN did_emacs_keys;
+
+void set_emacs_keys(void)
+{
+    set_any_keys(emacs_keys_table, TABLESIZE(emacs_keys_table));
+    did_emacs_keys = TRUE;
+}
+
+void reset_emacs_keys(void)
+{
+    if (did_emacs_keys) {
+	reset_any_keys(emacs_keys_table, TABLESIZE(emacs_keys_table));
+	did_emacs_keys = FALSE;
+    }
+}
+
+/*
+ * Map numbers to functions as labeled on the IBM Enhanced keypad, and save
+ * their original mapping for reset_numbers_as_arrows().  - FM
+ */
+static ANY_KEYS number_keys_table[] =
+{
+    {'1', LYK_END, 0},
+    {'2', LYK_NEXT_LINK, 0},
+    {'3', LYK_NEXT_PAGE, 0},
+    {'4', LYK_PREV_DOC, 0},
+    {'5', LYK_DO_NOTHING, 0},
+    {'6', LYK_ACTIVATE, 0},
+    {'7', LYK_HOME, 0},
+    {'8', LYK_PREV_LINK, 0},
+    {'9', LYK_PREV_PAGE, 0},
+};
+
+static BOOLEAN did_number_keys;
+
+void set_numbers_as_arrows(void)
+{
+    set_any_keys(number_keys_table, TABLESIZE(number_keys_table));
+    did_number_keys = TRUE;
+}
+
+void reset_numbers_as_arrows(void)
+{
+    if (did_number_keys) {
+	reset_any_keys(number_keys_table, TABLESIZE(number_keys_table));
+	did_number_keys = FALSE;
+    }
+}
+
+char *key_for_func(int func)
+{
+    static char *buf;
+    int i;
+    char *formatted;
+
+    if ((i = LYReverseKeymap(func)) >= 0) {
+	formatted = LYKeycodeToString(i, TRUE);
+	StrAllocCopy(buf, formatted != 0 ? formatted : "?");
+    } else if (buf == 0) {
+	StrAllocCopy(buf, "");
+    }
+    return buf;
+}
+
+/*
+ * Given one or two keys as lynxkeycodes, returns an allocated string
+ * representing the key(s) suitable for statusline messages, or NULL if no
+ * valid lynxkeycode is passed in (i.e., lkc_first < 0 or some other failure). 
+ * The caller must free the string.  - kw
+ */
+char *fmt_keys(int lkc_first,
+	       int lkc_second)
+{
+    char *buf = NULL;
+    BOOLEAN quotes = FALSE;
+    char *fmt_first;
+    char *fmt_second;
+
+    if (lkc_first < 0)
+	return NULL;
+    fmt_first = LYKeycodeToString(lkc_first, TRUE);
+    if (fmt_first && strlen(fmt_first) == 1 && *fmt_first != '\'') {
+	quotes = TRUE;
+    }
+    if (quotes) {
+	if (lkc_second < 0) {
+	    HTSprintf0(&buf, "'%s'", fmt_first);
+	    return buf;
+	} else {
+	    HTSprintf0(&buf, "'%s", fmt_first);
+	}
+    } else {
+	StrAllocCopy(buf, fmt_first);
+    }
+    if (lkc_second >= 0) {
+	fmt_second = LYKeycodeToString(lkc_second, TRUE);
+	if (!fmt_second) {
+	    FREE(buf);
+	    return NULL;
+	}
+	HTSprintf(&buf, "%s%s%s",
+		  (((strlen(fmt_second) > 2 && *fmt_second != '<') ||
+		    (strlen(buf) > 2 && buf[strlen(buf) - 1] != '>'))
+		   ? " "
+		   : ""),
+		  fmt_second, quotes ? "'" : "");
+    }
+    return buf;
+}
+
+/*
+ * This function returns the (int)ch mapped to the LYK_foo value passed to it
+ * as an argument.  It is like LYReverseKeymap, only the order of search is
+ * different; e.g., small ASCII letters will be returned in preference to
+ * capital ones.  Cf.  LYKeyForEditAction, LYEditKeyForAction in LYEditmap.c
+ * which use the same order to find a best key.  In addition, this function
+ * takes the dired override map into account while LYReverseKeymap doesn't. 
+ * The caller must free the returned string.  - kw
+ */
+#define FIRST_I 97
+#define NEXT_I(i,imax) ((i==122) ? 32 : (i==96) ? 123 : (i==126) ? 0 :\
+			(i==31) ? 256 : (i==imax) ? 127 :\
+			(i==255) ? (-1) :i+1)
+static int best_reverse_keymap(int lac)
+{
+    int i, c;
+
+    for (i = FIRST_I; i >= 0; i = NEXT_I(i, KEYMAP_SIZE - 2)) {
+#ifdef NOT_ASCII
+	if (i < 256) {
+	    c = FROMASCII(i);
+	} else
+#endif
+	    c = i;
+#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE)
+	if (lynx_edit_mode && !no_dired_support && lac &&
+	    LKC_TO_LAC(key_override, c) == lac)
+	    return c;
+#endif /* DIRED_SUPPORT && OK_OVERRIDE */
+	if (LKC_TO_LAC(keymap, c) == lac) {
+	    return c;
+	}
+    }
+
+    return (-1);
+}
+
+/*
+ * This function returns a string representing a key mapped to a LYK_foo
+ * function, or NULL if not found.  The string may represent a pair of keys. 
+ * if context_code is FOR_INPUT, an appropriate binding for use while in the
+ * (forms) line editor is sought.  - kw
+ */
+char *key_for_func_ext(int lac,
+		       int context_code)
+{
+    int lkc, modkey = -1;
+
+    if (context_code == FOR_INPUT) {
+	lkc = LYEditKeyForAction(lac, &modkey);
+	if (lkc >= 0) {
+	    if (lkc & (LKC_MOD1 | LKC_MOD2 | LKC_MOD3)) {
+		return fmt_keys(modkey, lkc & ~(LKC_MOD1 | LKC_MOD2 | LKC_MOD3));
+	    } else {
+		return fmt_keys(lkc, -1);
+	    }
+	}
+    }
+    lkc = best_reverse_keymap(lac);
+    if (lkc < 0)
+	return NULL;
+    if (context_code == FOR_INPUT) {
+	modkey = LYKeyForEditAction(LYE_LKCMD);
+	if (modkey < 0)
+	    return NULL;
+	return fmt_keys(modkey, lkc);
+    } else {
+	return fmt_keys(lkc, -1);
+    }
+}
+
+/*
+ * This function returns TRUE if the ch is non-alphanumeric and maps to KeyName
+ * (LYK_foo in the keymap[] array).  - FM
+ */
+BOOLEAN LYisNonAlnumKeyname(int ch,
+			    int KeyName)
+{
+    if (ch < 0 || ch >= KEYMAP_SIZE)
+	return (FALSE);
+    if (ch > 0
+	&& strchr("0123456789\
+ABCDEFGHIJKLMNOPQRSTUVWXYZ\
+abcdefghijklmnopqrstuvwxyz", ch) != NULL)
+	return (FALSE);
+
+    return (BOOL) (keymap[ch + 1] == KeyName);
+}
+
+/*
+ * This function returns the (int)ch mapped to the LYK_foo value passed to it
+ * as an argument.  - FM
+ */
+int LYReverseKeymap(int KeyName)
+{
+    int i;
+
+    for (i = 1; i < KEYMAP_SIZE; i++) {
+	if (keymap[i] == KeyName) {
+	    return (i - 1);
+	}
+    }
+
+    return (-1);
+}
+
+#ifdef EXP_KEYBOARD_LAYOUT
+int LYSetKbLayout(char *layout_id)
+{
+    int i;
+
+    for (i = 0; i < (int) TABLESIZE(LYKbLayoutNames) - 1; i++) {
+	if (!strcmp(LYKbLayoutNames[i], layout_id)) {
+	    current_layout = i;
+	    return (-1);
+	}
+    }
+
+    return 0;
+}
+#endif
diff --git a/src/LYKeymap.h b/src/LYKeymap.h
new file mode 100644
index 00000000..aa49c17b
--- /dev/null
+++ b/src/LYKeymap.h
@@ -0,0 +1,290 @@
+#ifndef LYKEYMAP_H
+#define LYKEYMAP_H
+
+#include <HTUtils.h>
+#include <HTList.h>
+#include <LYCurses.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    extern BOOLEAN LYisNonAlnumKeyname(int ch, int KeyName);
+    extern HTList *LYcommandList(void);
+    extern char *LYKeycodeToString(int c, BOOLEAN upper8);
+    extern char *fmt_keys(int lkc_first, int lkc_second);
+    extern char *key_for_func(int func);
+    extern char *key_for_func_ext(int lac, int context_code);
+    extern int LYReverseKeymap(int KeyName);
+    extern int LYStringToKeycode(char *src);
+    extern int lacname_to_lac(const char *func);
+    extern int lecname_to_lec(const char *func);
+    extern int lkcstring_to_lkc(const char *src);
+    extern int remap(char *key, const char *func, BOOLEAN for_dired);
+    extern void print_keymap(char **newfile);
+    extern void reset_emacs_keys(void);
+    extern void reset_numbers_as_arrows(void);
+    extern void reset_vi_keys(void);
+    extern void set_emacs_keys(void);
+    extern void set_numbers_as_arrows(void);
+    extern void set_vi_keys(void);
+    extern void set_vms_keys(void);
+
+/* We only use unsigned keycodes; if there's a problem matching with enum
+ * (which is supposed to be 'int'), that would be okay, but not as clean
+ * for type-checking.
+ */
+    typedef unsigned short LYKeymap_t;
+
+#define KEYMAP_SIZE 661
+    extern LYKeymap_t keymap[KEYMAP_SIZE];	/* main keymap matrix */
+
+#ifdef EXP_KEYBOARD_LAYOUT
+    typedef unsigned short LYKbLayout_t;
+    extern int current_layout;
+    extern LYKbLayout_t *LYKbLayouts[];
+    extern const char *LYKbLayoutNames[];
+    extern int LYSetKbLayout(char *layout_id);
+#endif
+
+#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE)
+    extern LYKeymap_t key_override[];
+#endif
+
+/* * *  LynxKeyCodes  * * */
+#define LKC_ISLECLAC	0x8000	/* flag: contains lynxaction + editaction */
+#define LKC_MOD1	0x4000	/* a modifier bit - currently for ^x-map */
+#define LKC_MOD2	0x2000	/* another one - currently for esc-map */
+#define LKC_MOD3	0x1000	/* another one - currently for double-map */
+#define LKC_ISLAC	0x0800	/* flag: lynxkeycode already lynxactioncode */
+
+/* Used to distinguish internal Lynx keycodes of (say) extended ncurses once. */
+#define LKC_ISLKC	0x0400	/* flag: already lynxkeycode (not native) */
+    /* 0x0400  is MOUSE_KEYSYM for slang in LYStrings.c */
+#define LKC_MASK	0x07FF	/* mask for lynxkeycode proper */
+
+#define LKC_DONE	0x07FE	/* special value - operation done, not-a-key */
+
+/* * *  LynxActionCodes  * * */
+#define LAC_SHIFT	8	/* shift for lynxactioncode - must not
+				   overwrite any assigned LYK_* values */
+#define LAC_MASK	((1<<LAC_SHIFT)-1)
+    /* mask for lynxactioncode - must cover all
+       assigned LYK_* values */
+
+/*  Return lkc masking single actioncode, given an lkc masking a lac + lec */
+#define LKC2_TO_LKC(c)   (((c) == -1 || !((c) & LKC_ISLECLAC)) ? (c) : \
+			    (((c) & LAC_MASK) | LKC_ISLAC))
+
+/*  Return lynxeditactioncode, given an lkc masking a lac + lec */
+#define LKC2_TO_LEC(c)   (((c) == -1 || !((c) & LKC_ISLECLAC)) ? (c) : \
+			    ((((c)&~LKC_ISLECLAC)>>LAC_SHIFT) & LAC_MASK))
+
+/*  Convert lynxkeycode to lynxactioncode.  Modifiers are dropped.  */
+#define LKC_TO_LAC(ktab,c) (((c) == -1) ? ktab[0] : \
+			    ((c) & (LKC_ISLECLAC|LKC_ISLAC)) ? ((c) & LAC_MASK) : \
+			    ktab[((c) & LKC_MASK) + 1])
+
+/*  Mask lynxactioncode as a lynxkeycode.  */
+#define LAC_TO_LKC0(a) ((a)|LKC_ISLAC)
+
+/*  Mask a lynxactioncode and an editactioncode as a lynxkeycode.  */
+#define LACLEC_TO_LKC0(a,b) ((a)|((b)<<LAC_SHIFT)|LKC_ISLECLAC)
+
+/*  Convert lynxactioncode to a lynxkeycode, attempting reverse mapping.  */
+#define LAC_TO_LKC(a) ((LYReverseKeymap(a)>=0)?LYReverseKeymap(a):LAC_TO_LKC0(a))
+
+/*  Simplify a lynxkeycode:
+    attempt reverse mapping if a single masked lynxactioncode, drop modifiers.  */
+#define LKC_TO_C(c) ((c&LKC_ISLECLAC)? c : (c&LKC_ISLAC)? LAC_TO_LKC(c&LAC_MASK) : (c&LKC_MASK))
+
+#define LKC_HAS_ESC_MOD(c) (c >= 0 && !(c&LKC_ISLECLAC) && (c&LKC_MOD2))
+
+/* *  The defined LynxActionCodes  * */
+
+/* Variables for holding and passing around lynxactioncodes are generally of
+ * type int, the types LYKeymap_t and LYKeymapCodes are currently only used for
+ * the definitions.  That could change.  - kw
+ *
+ * The values in this enum are indexed against the command names in the
+ * 'revmap[]' array in LYKeymap.c
+ */
+    typedef enum {
+	LYK_UNKNOWN = 0
+	,LYK_COMMAND
+	,LYK_1
+	,LYK_2
+	,LYK_3
+	,LYK_4
+	,LYK_5
+	,LYK_6
+	,LYK_7
+	,LYK_8
+	,LYK_9
+	,LYK_SOURCE
+	,LYK_RELOAD
+	,LYK_QUIT
+	,LYK_ABORT
+	,LYK_NEXT_PAGE
+	,LYK_PREV_PAGE
+	,LYK_UP_TWO
+	,LYK_DOWN_TWO
+	,LYK_UP_HALF
+	,LYK_DOWN_HALF
+	,LYK_REFRESH
+	,LYK_HOME
+	,LYK_END
+	,LYK_FIRST_LINK
+	,LYK_LAST_LINK
+	,LYK_PREV_LINK
+	,LYK_NEXT_LINK
+	,LYK_LPOS_PREV_LINK
+	,LYK_LPOS_NEXT_LINK
+	,LYK_FASTBACKW_LINK
+	,LYK_FASTFORW_LINK
+	,LYK_UP_LINK
+	,LYK_DOWN_LINK
+	,LYK_RIGHT_LINK
+	,LYK_LEFT_LINK
+	,LYK_HISTORY
+	,LYK_PREV_DOC
+	,LYK_NEXT_DOC
+	,LYK_ACTIVATE
+	,LYK_SUBMIT		/* mostly like LYK_ACTIVATE, for mouse use, don't map */
+	,LYK_GOTO
+	,LYK_ECGOTO
+	,LYK_HELP
+	,LYK_DWIMHELP
+	,LYK_INDEX
+	,LYK_NOCACHE
+	,LYK_INTERRUPT
+	,LYK_MAIN_MENU
+	,LYK_OPTIONS
+	,LYK_INDEX_SEARCH
+	,LYK_WHEREIS
+	,LYK_PREV
+	,LYK_NEXT
+	,LYK_COMMENT
+	,LYK_EDIT
+	,LYK_INFO
+	,LYK_PRINT
+	,LYK_ADD_BOOKMARK
+	,LYK_DEL_BOOKMARK
+	,LYK_VIEW_BOOKMARK
+	,LYK_VLINKS
+	,LYK_SHELL
+	,LYK_DOWNLOAD
+	,LYK_TRACE_TOGGLE
+	,LYK_TRACE_LOG
+	,LYK_IMAGE_TOGGLE
+	,LYK_INLINE_TOGGLE
+	,LYK_HEAD
+	,LYK_DO_NOTHING
+	,LYK_TOGGLE_HELP
+	,LYK_JUMP
+	,LYK_KEYMAP
+	,LYK_LIST
+	,LYK_TOOLBAR
+	,LYK_HISTORICAL
+	,LYK_MINIMAL
+	,LYK_SOFT_DQUOTES
+	,LYK_RAW_TOGGLE
+	,LYK_COOKIE_JAR
+	,LYK_F_LINK_NUM
+	,LYK_CLEAR_AUTH
+	,LYK_SWITCH_DTD
+	,LYK_ELGOTO
+	,LYK_CHANGE_LINK
+	,LYK_DWIMEDIT
+	,LYK_EDIT_TEXTAREA
+	,LYK_GROW_TEXTAREA
+	,LYK_INSERT_FILE
+
+#ifdef EXP_ADDRLIST_PAGE
+	,LYK_ADDRLIST
+#else
+#define LYK_ADDRLIST      LYK_ADD_BOOKMARK
+#endif
+
+#ifdef USE_EXTERNALS
+	,LYK_EXTERN_LINK
+	,LYK_EXTERN_PAGE
+#else
+#define LYK_EXTERN_LINK   LYK_UNKNOWN
+#define LYK_EXTERN_PAGE   LYK_UNKNOWN
+#endif				/* !defined(USE_EXTERNALS) */
+
+#if defined(VMS) || defined(DIRED_SUPPORT)
+	,LYK_DIRED_MENU
+#else
+#define LYK_DIRED_MENU    LYK_UNKNOWN
+#endif				/* VMS || DIRED_SUPPORT */
+
+#ifdef DIRED_SUPPORT
+	,LYK_CREATE
+	,LYK_REMOVE
+	,LYK_MODIFY
+	,LYK_TAG_LINK
+	,LYK_UPLOAD
+	,LYK_INSTALL
+#else
+#define LYK_TAG_LINK      LYK_UNKNOWN
+#endif				/* DIRED_SUPPORT */
+
+	,LYK_CHG_CENTER
+
+#ifdef KANJI_CODE_OVERRIDE
+	,LYK_CHG_KCODE
+#endif
+
+#ifdef SUPPORT_CHDIR
+	,LYK_CHDIR
+#endif
+
+#ifdef USE_CURSES_PADS
+	,LYK_SHIFT_LEFT
+	,LYK_SHIFT_RIGHT
+	,LYK_LINEWRAP_TOGGLE
+#else
+#define LYK_SHIFT_LEFT      LYK_UNKNOWN
+#define LYK_SHIFT_RIGHT     LYK_UNKNOWN
+#define LYK_LINEWRAP_TOGGLE LYK_UNKNOWN
+#endif
+
+#ifdef CAN_CUT_AND_PASTE
+	,LYK_PASTE_URL
+	,LYK_TO_CLIPBOARD
+#else
+#define LYK_PASTE_URL      LYK_UNKNOWN
+#define LYK_TO_CLIPBOARD   LYK_UNKNOWN
+#endif
+
+#ifdef EXP_NESTED_TABLES
+	,LYK_NESTED_TABLES
+#else
+#define LYK_NESTED_TABLES  LYK_UNKNOWN
+#endif
+
+#ifdef USE_CACHEJAR
+	,LYK_CACHE_JAR
+#else
+#define LYK_CACHE_JAR LYK_UNKNOWN
+#endif
+
+    } LYKeymapCode;
+
+/*
+ * Symbol table for internal commands.
+ */
+    typedef struct {
+	LYKeymapCode code;
+	const char *name;
+	const char *doc;
+    } Kcmd;
+
+    extern Kcmd *LYKeycodeToKcmd(LYKeymapCode code);
+    extern Kcmd *LYStringToKcmd(const char *name);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYKEYMAP_H */
diff --git a/src/LYLeaks.c b/src/LYLeaks.c
new file mode 100644
index 00000000..e7a4ee5d
--- /dev/null
+++ b/src/LYLeaks.c
@@ -0,0 +1,1017 @@
+/*
+ * $LynxId: LYLeaks.c,v 1.32 2007/07/23 22:54:46 tom Exp $
+ *
+ *	Copyright (c) 1994, University of Kansas, All Rights Reserved
+ *	(this file was rewritten twice - 1998/1999 and 2003/2004)
+ *
+ *	This code will be used only if LY_FIND_LEAKS is defined.
+ *
+ *  Revision History:
+ *	05-26-94	created Lynx 2-3-1 Garrett Arch Blythe
+ *	10-30-97	modified to handle StrAllocCopy() and
+ *			  StrAllocCat(). - KW & FM
+ */
+
+/*
+ *	Disable the overriding of the memory routines for this file.
+ */
+#define NO_MEMORY_TRACKING
+
+#include <HTUtils.h>
+#include <LYexit.h>
+#include <LYLeaks.h>
+#include <LYUtils.h>
+#include <LYGlobalDefs.h>
+
+#ifdef LY_FIND_LEAKS
+
+static AllocationList *ALp_RunTimeAllocations = NULL;
+
+#define LEAK_SUMMARY
+
+#ifdef LEAK_SUMMARY
+
+static long now_allocated = 0;
+static long peak_alloced = 0;
+
+static long total_alloced = 0;
+static long total_freed = 0;
+
+static long count_mallocs = 0;
+static long count_frees = 0;
+
+static void CountMallocs(long size)
+{
+    ++count_mallocs;
+    total_alloced += size;
+    now_allocated += size;
+    if (peak_alloced < now_allocated)
+	peak_alloced = now_allocated;
+}
+
+static void CountFrees(long size)
+{
+    ++count_frees;
+    total_freed += size;
+    now_allocated -= size;
+}
+
+#else
+#define CountMallocs() ++count_mallocs
+#define CountFrees()		/* nothing */
+#endif
+
+/*
+ *  Purpose:	Add a new allocation item to the list.
+ *  Arguments:		ALp_new The new item to add.
+ *  Return Value:	void
+ *  Remarks/Portability/Dependencies/Restrictions:
+ *		Static function made to make code reusable in projects beyond
+ *		Lynx (some might ask why not use HTList).
+ *  Revision History:
+ *	05-26-94	created Lynx 2-3-1 Garrett Arch Blythe
+ */
+static void AddToList(AllocationList * ALp_new)
+{
+    /*
+     * Just make this the first item in the list.
+     */
+    ALp_new->ALp_Next = ALp_RunTimeAllocations;
+    ALp_RunTimeAllocations = ALp_new;
+}
+
+/*
+ *  Purpose:	Find the place in the list where vp_find is currently
+ *		tracked.
+ *  Arguments:		vp_find A pointer to look for in the list.
+ *  Return Value:	AllocationList *	Either vp_find's place in the
+ *						list or NULL if not found.
+ *  Remarks/Portability/Dependencies/Restrictions:
+ *		Static function made to make code reusable in projects outside
+ *		of Lynx (some might ask why not use HTList).
+ *  Revision History:
+ *	05-26-94	created Lynx 2-3-1 Garrett Arch Blythe
+ */
+static AllocationList *FindInList(void *vp_find)
+{
+    AllocationList *ALp_find = ALp_RunTimeAllocations;
+
+    /*
+     * Go through the list of allocated pointers until end of list or vp_find
+     * is found.
+     */
+    while (ALp_find != NULL) {
+	if (ALp_find->vp_Alloced == vp_find) {
+	    break;
+	}
+	ALp_find = ALp_find->ALp_Next;
+    }
+
+    return (ALp_find);
+}
+
+/*
+ *  Purpose:	Remove the specified item from the list.
+ *  Arguments:		ALp_del The item to remove from the list.
+ *  Return Value:	void
+ *  Remarks/Portability/Dependencies/Restrictions:
+ *		Static function made to make code reusable in projects outside
+ *		of Lynx (some might ask why not use HTList).
+ *  Revision History:
+ *	05-26-94	created Lynx 2-3-1 Garrett Arch Blythe
+ */
+static void RemoveFromList(AllocationList * ALp_del)
+{
+    AllocationList *ALp_findbefore = ALp_RunTimeAllocations;
+
+    /*
+     * There is one special case, where the item to remove is the first in the
+     * list.
+     */
+    if (ALp_del == ALp_findbefore) {
+	ALp_RunTimeAllocations = ALp_del->ALp_Next;
+    } else {
+
+	/*
+	 * Loop through checking all of the next values, if a match don't
+	 * continue.  Always assume the item will be found.
+	 */
+	while (ALp_findbefore->ALp_Next != ALp_del) {
+	    ALp_findbefore = ALp_findbefore->ALp_Next;
+	}
+
+	/*
+	 * We are one item before the one to get rid of.  Get rid of it.
+	 */
+	ALp_findbefore->ALp_Next = ALp_del->ALp_Next;
+    }
+}
+
+/*
+ * Make the malloc-sequence available for debugging/tracing.
+ */
+#ifndef LYLeakSequence
+long LYLeakSequence(void)
+{
+    return count_mallocs;
+}
+#endif
+
+/*
+ *  Purpose:	Print a report of all memory left unallocated by
+ *		Lynx code or attempted unallocations on
+ *		pointers that are not valid and then free
+ *		all unfreed memory.
+ *  Arguments:		void
+ *  Return Value:	void
+ *  Remarks/Portability/Dependencies/Restrictions:
+ *		This function should be registered for execution with the
+ *		atexit (stdlib.h) function as the first statement
+ *		in main.
+ *		All output of this function is sent to the file defined in
+ *		the header LYLeaks.h (LEAKAGE_SINK).
+ */
+void LYLeaks(void)
+{
+    AllocationList *ALp_head;
+    size_t st_total = (size_t) 0;
+    FILE *Fp_leakagesink;
+
+    CTRACE((tfp, "entering LYLeaks, flag=%d\n", LYfind_leaks));
+
+    if (LYfind_leaks == FALSE) {
+	/*
+	 * Free MY leaks too, in case someone else is watching.
+	 */
+	while (ALp_RunTimeAllocations != NULL) {
+	    ALp_head = ALp_RunTimeAllocations;
+	    ALp_RunTimeAllocations = ALp_head->ALp_Next;
+	    free(ALp_head);
+	}
+	return;
+    }
+
+    /*
+     * Open the leakage sink to take all the output.  Recreate the file each
+     * time.  Do nothing if unable to open the file.
+     */
+    Fp_leakagesink = LYNewTxtFile(LEAKAGE_SINK);
+    if (Fp_leakagesink == NULL) {
+	return;
+    }
+
+    while (ALp_RunTimeAllocations != NULL) {
+	/*
+	 * Take the head off of the run time allocation list.
+	 */
+	ALp_head = ALp_RunTimeAllocations;
+	ALp_RunTimeAllocations = ALp_head->ALp_Next;
+
+	/*
+	 * Print the type of leak/error.  Release memory when we no longer
+	 * need it.
+	 */
+	if (ALp_head->vp_Alloced == NULL) {
+	    /*
+	     * If there is realloc information on the bad request, then it was
+	     * a bad pointer value in a realloc statement.
+	     */
+	    fprintf(Fp_leakagesink, "%s.\n",
+		    gettext("Invalid pointer detected."));
+	    fprintf(Fp_leakagesink, "%s\t%ld\n",
+		    gettext("Sequence:"),
+		    ALp_head->st_Sequence);
+	    fprintf(Fp_leakagesink, "%s\t%p\n",
+		    gettext("Pointer:"), ALp_head->vp_BadRequest);
+
+	    /*
+	     * Don't free the bad request, it is an invalid pointer.  If the
+	     * free source information is empty, we should check the realloc
+	     * information too since it can get passed bad pointer values also.
+	     */
+	    if (ALp_head->SL_memory.cp_FileName == NULL) {
+		fprintf(Fp_leakagesink, "%s\t%s\n",
+			gettext("FileName:"),
+			ALp_head->SL_realloc.cp_FileName);
+		fprintf(Fp_leakagesink, "%s\t%d\n",
+			gettext("LineCount:"),
+			ALp_head->SL_realloc.ssi_LineNumber);
+	    } else {
+		fprintf(Fp_leakagesink, "%s\t%s\n",
+			gettext("FileName:"),
+			ALp_head->SL_memory.cp_FileName);
+		fprintf(Fp_leakagesink, "%s\t%d\n",
+			gettext("LineCount:"),
+			ALp_head->SL_memory.ssi_LineNumber);
+	    }
+	} else {
+	    size_t i_counter;
+	    char *value = (char *) (ALp_head->vp_Alloced);
+
+	    /*
+	     * Increment the count of total memory lost and then print the
+	     * information.
+	     */
+	    st_total += ALp_head->st_Bytes;
+
+	    fprintf(Fp_leakagesink, "%s\n",
+		    gettext("Memory leak detected."));
+	    fprintf(Fp_leakagesink, "%s\t%ld\n",
+		    gettext("Sequence:"),
+		    ALp_head->st_Sequence);
+	    fprintf(Fp_leakagesink, "%s\t%p\n",
+		    gettext("Pointer:"),
+		    ALp_head->vp_Alloced);
+	    fprintf(Fp_leakagesink, "%s\t",
+		    gettext("Contains:"));
+	    for (i_counter = 0;
+		 i_counter < ALp_head->st_Bytes &&
+		 i_counter < MAX_CONTENT_LENGTH;
+		 i_counter++) {
+		if (isprint(UCH(value[i_counter]))) {
+		    fprintf(Fp_leakagesink, "%c", value[i_counter]);
+		} else {
+		    fprintf(Fp_leakagesink, "|");
+		}
+	    }
+	    fprintf(Fp_leakagesink, "\n");
+	    fprintf(Fp_leakagesink, "%s\t%d\n",
+		    gettext("ByteSize:"),
+		    (int) (ALp_head->st_Bytes));
+	    fprintf(Fp_leakagesink, "%s\t%s\n",
+		    gettext("FileName:"),
+		    ALp_head->SL_memory.cp_FileName);
+	    fprintf(Fp_leakagesink, "%s\t%d\n",
+		    gettext("LineCount:"),
+		    ALp_head->SL_memory.ssi_LineNumber);
+	    /*
+	     * Give the last time the pointer was realloced if it happened
+	     * also.
+	     */
+	    if (ALp_head->SL_realloc.cp_FileName != NULL) {
+		fprintf(Fp_leakagesink, "%s\t%s\n",
+			gettext("realloced:"),
+			ALp_head->SL_realloc.cp_FileName);
+		fprintf(Fp_leakagesink, "%s\t%d\n",
+			gettext("LineCount:"),
+			ALp_head->SL_realloc.ssi_LineNumber);
+	    }
+	    fflush(Fp_leakagesink);
+	    FREE(ALp_head->vp_Alloced);
+	}
+
+	/*
+	 * Create a blank line and release the memory held by the item.
+	 */
+	fprintf(Fp_leakagesink, "\n");
+	FREE(ALp_head);
+    }
+
+    /*
+     * Give a grand total of the leakage.  Close the output file.
+     */
+    fprintf(Fp_leakagesink, "%s\t%u\n",
+	    gettext("Total memory leakage this run:"),
+	    (unsigned) st_total);
+#ifdef LEAK_SUMMARY
+    fprintf(Fp_leakagesink, "%s\t%ld\n", gettext("Peak allocation"), peak_alloced);
+    fprintf(Fp_leakagesink, "%s\t%ld\n", gettext("Bytes allocated"), total_alloced);
+    fprintf(Fp_leakagesink, "%s\t%ld\n", gettext("Total mallocs"), count_mallocs);
+    fprintf(Fp_leakagesink, "%s\t%ld\n", gettext("Total frees"), count_frees);
+#endif
+    fclose(Fp_leakagesink);
+
+    HTSYS_purge(LEAKAGE_SINK);
+}
+
+/*
+ *  Purpose:	Capture allocations using malloc (stdlib.h) and track
+ *		the information in a list.
+ *  Arguments:	st_bytes	The size of the allocation requested
+ *				in bytes.
+ *		cp_File		The file from which the request for
+ *				allocation came from.
+ *		ssi_Line	The line number in cp_File where the
+ *				allocation request came from.
+ *  Return Value:	void *	A pointer to the allocated memory or NULL on
+ *				failure as per malloc (stdlib.h)
+ *  Remarks/Portability/Dependencies/Restrictions:
+ *		If no memory is allocated, then no entry is added to the
+ *		allocation list.
+ *  Revision History:
+ *	05-26-94	created Lynx 2-3-1 Garrett Arch Blythe
+ */
+void *LYLeakMalloc(size_t st_bytes, const char *cp_File,
+		   const short ssi_Line)
+{
+    void *vp_malloc;
+
+    if (LYfind_leaks == FALSE) {
+	vp_malloc = (void *) malloc(st_bytes);
+    } else {
+
+	/*
+	 * Do the actual allocation.
+	 */
+	vp_malloc = (void *) malloc(st_bytes);
+	CountMallocs(st_bytes);
+
+	/*
+	 * Only on successful allocation do we track any information.
+	 */
+	if (vp_malloc != NULL) {
+	    /*
+	     * Further allocate memory to store the information.  Just return
+	     * on failure to allocate more.
+	     */
+	    AllocationList *ALp_new = typecalloc(AllocationList);
+
+	    if (ALp_new != NULL) {
+		/*
+		 * Copy over the relevant information.  There is no need to
+		 * allocate more memory for the file name as it is a static
+		 * string anyway.
+		 */
+		ALp_new->st_Sequence = count_mallocs;
+		ALp_new->vp_Alloced = vp_malloc;
+		ALp_new->st_Bytes = st_bytes;
+		ALp_new->SL_memory.cp_FileName = cp_File;
+		ALp_new->SL_memory.ssi_LineNumber = ssi_Line;
+
+		/*
+		 * Add the new item to the allocation list.
+		 */
+		AddToList(ALp_new);
+	    }
+	}
+    }
+    return (vp_malloc);
+}
+
+/*
+ *  Purpose:	Add information about new allocation to the list,
+ *		after a call to malloc or calloc or an equivalent
+ *		function which may or may not have already created
+ *		a list entry.
+ *  Arguments:	vp_malloc	The pointer to newly allocated memory.
+ *  Arguments:	st_bytes	The size of the allocation requested
+ *				in bytes.
+ *		cp_File		The file from which the request for
+ *				allocation came from.
+ *		ssi_Line	The line number in cp_File where the
+ *				allocation request came from.
+ *  Return Value:	void *	A pointer to the allocated memory or NULL on
+ *				failure.
+ *  Remarks/Portability/Dependencies/Restrictions:
+ *		If no memory is allocated, then no entry is added to the
+ *		allocation list.
+ *  Revision History:
+ *	1999-02-08	created, modelled after LYLeakMalloc - kw
+ */
+AllocationList *LYLeak_mark_malloced(void *vp_malloced,
+				     size_t st_bytes,
+				     const char *cp_File,
+				     const short ssi_Line)
+{
+    AllocationList *ALp_new = NULL;
+
+    if (LYfind_leaks != FALSE) {
+	/*
+	 * The actual allocation has already been done!
+	 *
+	 * Only on successful allocation do we track any information.
+	 */
+	if (vp_malloced != NULL) {
+	    /*
+	     * See if there is already an entry.  If so, just update the source
+	     * location info.
+	     */
+	    ALp_new = FindInList(vp_malloced);
+	    if (ALp_new) {
+		ALp_new->SL_memory.cp_FileName = cp_File;
+		ALp_new->SL_memory.ssi_LineNumber = ssi_Line;
+	    } else {
+		/*
+		 * Further allocate memory to store the information.  Just
+		 * return on failure to allocate more.
+		 */
+		ALp_new = typecalloc(AllocationList);
+		if (ALp_new != NULL) {
+		    /*
+		     * Copy over the relevant information.
+		     */
+		    ALp_new->vp_Alloced = vp_malloced;
+		    ALp_new->st_Bytes = st_bytes;
+		    ALp_new->SL_memory.cp_FileName = cp_File;
+		    ALp_new->SL_memory.ssi_LineNumber = ssi_Line;
+
+		    /*
+		     * Add the new item to the allocation list.
+		     */
+		    AddToList(ALp_new);
+		}
+	    }
+	}
+    }
+    return (ALp_new);
+}
+
+/*
+ *  Purpose:	Capture allocations by calloc (stdlib.h) and
+ *		save relevant information in a list.
+ *  Arguments:	st_number	The number of items to allocate.
+ *		st_bytes	The size of each item.
+ *		cp_File		The file which wants to allocation.
+ *		ssi_Line	The line number in cp_File requesting
+ *				the allocation.
+ *  Return Value:	void *	The allocated memory, or NULL on failure as
+ *				per calloc (stdlib.h)
+ *  Remarks/Portability/Dependencies/Restrictions:
+ *		If no memory can be allocated, then no entry will be added
+ *		to the list.
+ *  Revision History:
+ *		05-26-94	created Lynx 2-3-1 Garrett Arch Blythe
+ */
+void *LYLeakCalloc(size_t st_number, size_t st_bytes, const char *cp_File,
+		   const short ssi_Line)
+{
+    void *vp_calloc;
+
+    if (LYfind_leaks == FALSE) {
+	vp_calloc = (void *) calloc(st_number, st_bytes);
+    } else {
+
+	/*
+	 * Allocate the requested memory.
+	 */
+	vp_calloc = (void *) calloc(st_number, st_bytes);
+	CountMallocs(st_bytes);
+
+	/*
+	 * Only if the allocation was a success do we track information.
+	 */
+	if (vp_calloc != NULL) {
+	    /*
+	     * Allocate memory for the item to be in the list.  If unable, just
+	     * return.
+	     */
+	    AllocationList *ALp_new = typecalloc(AllocationList);
+
+	    if (ALp_new != NULL) {
+
+		/*
+		 * Copy over the relevant information.  There is no need to
+		 * allocate memory for the file name as it is a static string
+		 * anyway.
+		 */
+		ALp_new->st_Sequence = count_mallocs;
+		ALp_new->vp_Alloced = vp_calloc;
+		ALp_new->st_Bytes = (st_number * st_bytes);
+		ALp_new->SL_memory.cp_FileName = cp_File;
+		ALp_new->SL_memory.ssi_LineNumber = ssi_Line;
+
+		/*
+		 * Add the item to the allocation list.
+		 */
+		AddToList(ALp_new);
+	    }
+	}
+    }
+    return (vp_calloc);
+}
+
+/*
+ *  Purpose:	Capture any realloc (stdlib.h) calls in order to
+ *		properly keep track of our run time allocation
+ *		table.
+ *  Arguments:	vp_Alloced	The previously allocated block of
+ *				memory to resize.  If NULL,
+ *				realloc works just like
+ *				malloc.
+ *		st_newBytes	The new size of the chunk of memory.
+ *		cp_File		The file containing the realloc.
+ *		ssi_Line	The line containing the realloc in cp_File.
+ *  Return Value:	void *	The new pointer value (could be the same) or
+ *				NULL if unable to resize (old block
+ *				still exists).
+ *  Remarks/Portability/Dependencies/Restrictions:
+ *		If unable to resize vp_Alloced, then no change in the
+ *		allocation list will be made.
+ *		If vp_Alloced is an invalid pointer value, the program will
+ *		exit after one last entry is added to the allocation list.
+ *  Revision History:
+ *	05-26-94	created Lynx 2-3-1 Garrett Arch Blythe
+ */
+void *LYLeakRealloc(void *vp_Alloced,
+		    size_t st_newBytes,
+		    const char *cp_File,
+		    const short ssi_Line)
+{
+    void *vp_realloc;
+    AllocationList *ALp_renew;
+
+    if (LYfind_leaks == FALSE) {
+	vp_realloc = (void *) realloc(vp_Alloced, st_newBytes);
+
+    } else if (vp_Alloced == NULL) {
+	/*
+	 * If we are asked to resize a NULL pointer, this is just a malloc
+	 * call.
+	 */
+	vp_realloc = LYLeakMalloc(st_newBytes, cp_File, ssi_Line);
+
+    } else {
+
+	/*
+	 * Find the current vp_Alloced block in the list.  If NULL, this is an
+	 * invalid pointer value.
+	 */
+	ALp_renew = FindInList(vp_Alloced);
+	if (ALp_renew == NULL) {
+	    /*
+	     * Track the invalid pointer value and then exit.  If unable to
+	     * allocate, just exit.
+	     */
+	    AllocationList *ALp_new = typecalloc(AllocationList);
+
+	    if (ALp_new == NULL) {
+		exit_immediately(EXIT_FAILURE);
+	    }
+
+	    /*
+	     * Set the information up; no need to allocate file name since it is a
+	     * static string.
+	     */
+	    ALp_new->vp_Alloced = NULL;
+	    ALp_new->vp_BadRequest = vp_Alloced;
+	    ALp_new->SL_realloc.cp_FileName = cp_File;
+	    ALp_new->SL_realloc.ssi_LineNumber = ssi_Line;
+
+	    /*
+	     * Add the item to the list.  Exit.
+	     */
+	    AddToList(ALp_new);
+	    exit_immediately(EXIT_FAILURE);
+	}
+
+	/*
+	 * Perform the resize.  If not NULL, record the information.
+	 */
+	vp_realloc = (void *) realloc(vp_Alloced, st_newBytes);
+	CountMallocs(st_newBytes);
+	CountFrees(ALp_renew->st_Bytes);
+
+	if (vp_realloc != NULL) {
+	    ALp_renew->st_Sequence = count_mallocs;
+	    ALp_renew->vp_Alloced = vp_realloc;
+	    ALp_renew->st_Bytes = st_newBytes;
+
+	    /*
+	     * Update the realloc information, too.  No need to allocate file name,
+	     * static string.
+	     */
+	    ALp_renew->SL_realloc.cp_FileName = cp_File;
+	    ALp_renew->SL_realloc.ssi_LineNumber = ssi_Line;
+	}
+    }
+    return (vp_realloc);
+}
+
+/*
+ *  Purpose:	Add information about reallocated memory to the list,
+ *		after a call to realloc or an equivalent
+ *		function which has not already created or updated
+ *		a list entry.
+ *  Arguments:	ALp_old		List entry for previously allocated
+ *				block of memory to resize.  If NULL,
+ *				mark_realloced works just like
+ *				mark_malloced.
+ *		vp_realloced	The new pointer, after resizing.
+ *		st_newBytes	The new size of the chunk of memory.
+ *		cp_File		The file to record.
+ *		ssi_Line	The line to record.
+ *  Return Value:		Pointer to new or updated list entry
+ *				for this memory block.
+ *				NULL on allocation error.
+ *  Revision History:
+ *	1999-02-11	created kw
+ */
+#if defined(LY_FIND_LEAKS) && defined(LY_FIND_LEAKS_EXTENDED)
+static AllocationList *mark_realloced(AllocationList * ALp_old, void *vp_realloced,
+				      size_t st_newBytes,
+				      const char *cp_File,
+				      const short ssi_Line)
+{
+    /*
+     * If there is no list entry for the old allocation, treat this as if a new
+     * allocation had happened.
+     */
+    if (ALp_old == NULL) {
+	return (LYLeak_mark_malloced(vp_realloced, st_newBytes, cp_File, ssi_Line));
+    }
+
+    /*
+     * ALp_old represents the memory block before reallocation.  Assume that if
+     * we get here, there isn't yet a list entry for the new, possibly
+     * different, address after realloc, that is our list hasn't been updated -
+     * so we're going to do that now.
+     */
+
+    if (vp_realloced != NULL) {
+	ALp_old->vp_Alloced = vp_realloced;
+	ALp_old->st_Bytes = st_newBytes;
+	ALp_old->SL_realloc.cp_FileName = cp_File;
+	ALp_old->SL_realloc.ssi_LineNumber = ssi_Line;
+    }
+
+    return (ALp_old);
+}
+#endif /* not LY_FIND_LEAKS and LY_FIND_LEAKS_EXTENDED */
+
+/*
+ *  Purpose:	Capture all requests to free information and also
+ *		remove items from the allocation list.
+ *  Arguments:	vp_Alloced	The memory to free.
+ *		cp_File		The file calling free.
+ *		ssi_Line	The line of cp_File calling free.
+ *  Return Value:	void
+ *  Remarks/Portability/Dependencies/Restrictions:
+ *		If the pointer value is invalid, then an item will be added
+ *		to the list and nothing else is done.
+ *		I really like the name of this function and one day hope
+ *		that Lynx is Leak Free.
+ *  Revision History:
+ *	05-26-94	created Lynx 2-3-1 Garrett Arch Blythe
+ */
+void LYLeakFree(void *vp_Alloced,
+		const char *cp_File,
+		const short ssi_Line)
+{
+    AllocationList *ALp_free;
+
+    if (LYfind_leaks == FALSE) {
+	free(vp_Alloced);
+    } else {
+
+	/*
+	 * Find the pointer in the allocated list.  If not found, bad pointer. 
+	 * If found, free list item and vp_Alloced.
+	 */
+	ALp_free = FindInList(vp_Alloced);
+	if (ALp_free == NULL) {
+	    /*
+	     * Create the final entry before exiting marking this error.  If
+	     * unable to allocate more memory just exit.
+	     */
+	    AllocationList *ALp_new = typecalloc(AllocationList);
+
+	    if (ALp_new == NULL) {
+		exit_immediately(EXIT_FAILURE);
+	    }
+
+	    /*
+	     * Set up the information, no memory need be allocated for the file
+	     * name since it is a static string.
+	     */
+	    ALp_new->vp_Alloced = NULL;
+	    ALp_new->vp_BadRequest = vp_Alloced;
+	    ALp_new->SL_memory.cp_FileName = cp_File;
+	    ALp_new->SL_memory.ssi_LineNumber = ssi_Line;
+
+	    /*
+	     * Add the entry to the list and then return.
+	     */
+	    AddToList(ALp_new);
+	} else {
+	    /*
+	     * Free off the memory.  Take entry out of allocation list.
+	     */
+	    CountFrees(ALp_free->st_Bytes);
+	    RemoveFromList(ALp_free);
+	    FREE(ALp_free);
+	    free(vp_Alloced);
+	}
+    }
+}
+
+/*
+ *  Allocates a new copy of a string, and returns it.
+ *  Tracks allocations by using other LYLeakFoo functions.
+ *  Equivalent to HTSACopy in HTUtils.c - KW
+ */
+char *LYLeakSACopy(char **dest,
+		   const char *src,
+		   const char *cp_File,
+		   const short ssi_Line)
+{
+    if (src != NULL && src == *dest) {
+	CTRACE((tfp,
+		"LYLeakSACopy: *dest equals src, contains \"%s\"\n",
+		src));
+    } else {
+	if (*dest) {
+	    LYLeakFree(*dest, cp_File, ssi_Line);
+	    *dest = NULL;
+	}
+	if (src) {
+	    *dest = (char *) LYLeakMalloc(strlen(src) + 1, cp_File, ssi_Line);
+	    if (*dest == NULL)
+		outofmem(__FILE__, "LYLeakSACopy");
+	    strcpy(*dest, src);
+	}
+    }
+    return *dest;
+}
+
+/*
+ *  String Allocate and Concatenate.
+ *  Tracks allocations by using other LYLeakFoo functions.
+ *  Equivalent to HTSACat in HTUtils.c - KW
+ */
+char *LYLeakSACat(char **dest,
+		  const char *src,
+		  const char *cp_File,
+		  const short ssi_Line)
+{
+    if (src && *src) {
+	if (src == *dest) {
+	    CTRACE((tfp,
+		    "LYLeakSACat:  *dest equals src, contains \"%s\"\n",
+		    src));
+	} else if (*dest) {
+	    int length = strlen(*dest);
+
+	    *dest = (char *) LYLeakRealloc(*dest,
+					   (length + strlen(src) + 1),
+					   cp_File,
+					   ssi_Line);
+	    if (*dest == NULL)
+		outofmem(__FILE__, "LYLeakSACat");
+	    strcpy(*dest + length, src);
+	} else {
+	    *dest = (char *) LYLeakMalloc((strlen(src) + 1),
+					  cp_File,
+					  ssi_Line);
+	    if (*dest == NULL)
+		outofmem(__FILE__, "LYLeakSACat");
+	    strcpy(*dest, src);
+	}
+    }
+    return *dest;
+}
+
+#if defined(LY_FIND_LEAKS) && defined(LY_FIND_LEAKS_EXTENDED)
+
+const char *leak_cp_File_hack = __FILE__;
+short leak_ssi_Line_hack = __LINE__;
+
+/*
+ * Purpose:	A wrapper around StrAllocVsprintf (the workhorse of
+ *		HTSprintf/HTSprintf0, implemented in HTString.c) that
+ *		tries to make sure that our allocation list is always
+ *		properly updated, whether StrAllocVsprintf itself was
+ *		compiled with memory tracking or not (or even a mixture,
+ *		like tracking the freeing but not the new allocation).
+ *		Some source files can be compiled with LY_FIND_LEAKS_EXTENDED
+ *		in effect while others only have LY_FIND_LEAKS in effect,
+ *		and as long as HTString.c is complied with memory tracking
+ *		(of either kind) string objects allocated by HTSprintf/
+ *		HTSprintf0 (or otherwise) can be passed around among them and
+ *		manipulated both ways.
+ *  Arguments:	dest		As for StrAllocVsprintf.
+ *		cp_File		The source file of the caller (i.e. the
+ *				caller of HTSprintf/HTSprintf0, hopefully).
+ *		ssi_Line	The line of cp_File calling.
+ *		inuse,fmt,ap	As for StrAllocVsprintf.
+ *  Return Value:	The char pointer to resulting string, as set
+ *			by StrAllocVsprintf, or
+ *			NULL if dest==0 (wrong use!).
+ *  Remarks/Portability/Dependencies/Restrictions:
+ *		The price for generality is severe inefficiency: several
+ *		list lookups are done to be on the safe side.
+ *		We don't get he real allocation size, only a minimum based
+ *		on the string length of the result.  So the amount of memory
+ *		leakage may get underestimated.
+ *		If *dest is an invalid pointer value on entry (i.e. was not
+ *		tracked), the program will exit after one last entry is added
+ *		to the allocation list.
+ *		If StrAllocVsprintf fails to return a valid string via the
+ *		indirect string pointer (its first parameter), invalid memory
+ *		access will result and the program will probably terminate
+ *		with a signal.  This can happen if, on entry, *dest is NULL
+ *		and fmt is empty or NULL, so just Don't Do That.
+ *  Revision History:
+ *	1999-02-11	created kw
+ *	1999-10-15	added comments kw
+ */
+static char *LYLeakSAVsprintf(char **dest,
+			      const char *cp_File,
+			      const short ssi_Line,
+			      size_t inuse,
+			      const char *fmt,
+			      va_list * ap)
+{
+    AllocationList *ALp_old;
+    void *vp_oldAlloced;
+
+    const char *old_cp_File = __FILE__;
+    short old_ssi_Line = __LINE__;
+
+    if (!dest)
+	return NULL;
+
+    if (LYfind_leaks == FALSE) {
+	StrAllocVsprintf(dest, inuse, fmt, ap);
+	return (*dest);
+    }
+
+    vp_oldAlloced = *dest;
+    if (!vp_oldAlloced) {
+	StrAllocVsprintf(dest, inuse, fmt, ap);
+	LYLeak_mark_malloced(*dest, strlen(*dest) + 1, cp_File, ssi_Line);
+	return (*dest);
+    } else {
+	void *vp_realloced;
+
+	ALp_old = FindInList(vp_oldAlloced);
+	if (ALp_old == NULL) {
+	    /*
+	     * Track the invalid pointer value and then exit.  If unable to
+	     * allocate, just exit.
+	     */
+	    AllocationList *ALp_new = typecalloc(AllocationList);
+
+	    if (ALp_new == NULL) {
+		exit_immediately(EXIT_FAILURE);
+	    }
+
+	    /*
+	     * Set the information up; no need to allocate file name since it
+	     * is a static string.
+	     */
+	    ALp_new->vp_Alloced = NULL;
+	    ALp_new->vp_BadRequest = vp_oldAlloced;
+	    ALp_new->SL_realloc.cp_FileName = cp_File;
+	    ALp_new->SL_realloc.ssi_LineNumber = ssi_Line;
+
+	    /*
+	     * Add the item to the list.  Exit.
+	     */
+	    AddToList(ALp_new);
+	    exit_immediately(EXIT_FAILURE);
+	}
+
+	old_cp_File = ALp_old->SL_memory.cp_FileName;
+	old_ssi_Line = ALp_old->SL_memory.ssi_LineNumber;
+	/*
+	 * DO THE REAL WORK, by calling StrAllocVsprintf.  If result is not
+	 * NULL, record the information.
+	 */
+	StrAllocVsprintf(dest, inuse, fmt, ap);
+	vp_realloced = (void *) *dest;
+	if (vp_realloced != NULL) {
+	    AllocationList *ALp_new = FindInList(vp_realloced);
+
+	    if (!ALp_new) {
+		/* Look up again, list may have changed! - kw */
+		ALp_old = FindInList(vp_oldAlloced);
+		if (ALp_old == NULL) {
+		    LYLeak_mark_malloced(*dest, strlen(*dest) + 1, cp_File, ssi_Line);
+		    return (*dest);
+		}
+		mark_realloced(ALp_old, *dest, strlen(*dest) + 1, cp_File, ssi_Line);
+		return (*dest);
+	    }
+	    if (vp_realloced == vp_oldAlloced) {
+		ALp_new->SL_memory.cp_FileName = old_cp_File;
+		ALp_new->SL_memory.ssi_LineNumber = old_ssi_Line;
+		ALp_new->SL_realloc.cp_FileName = cp_File;
+		ALp_new->SL_realloc.ssi_LineNumber = ssi_Line;
+		return (*dest);
+	    }
+	    /* Look up again, list may have changed! - kw */
+	    ALp_old = FindInList(vp_oldAlloced);
+	    if (ALp_old == NULL) {
+		ALp_new->SL_memory.cp_FileName = old_cp_File;
+		ALp_new->SL_memory.ssi_LineNumber = old_ssi_Line;
+		ALp_new->SL_realloc.cp_FileName = cp_File;
+		ALp_new->SL_realloc.ssi_LineNumber = ssi_Line;
+	    } else {
+		ALp_new->SL_memory.cp_FileName = old_cp_File;
+		ALp_new->SL_memory.ssi_LineNumber = old_ssi_Line;
+		ALp_new->SL_realloc.cp_FileName = cp_File;
+		ALp_new->SL_realloc.ssi_LineNumber = ssi_Line;
+	    }
+	}
+	return (*dest);
+    }
+}
+
+/* Note: the following may need updating if HTSprintf in HTString.c
+ * is changed. - kw */
+static char *LYLeakHTSprintf(char **pstr, const char *fmt,...)
+{
+    char *str;
+    size_t inuse = 0;
+    va_list ap;
+
+    LYva_start(ap, fmt);
+
+    if (pstr != 0 && *pstr != 0)
+	inuse = strlen(*pstr);
+    str = LYLeakSAVsprintf(pstr, leak_cp_File_hack, leak_ssi_Line_hack,
+			   inuse, fmt, &ap);
+
+    va_end(ap);
+    return str;
+}
+
+/* Note: the following may need updating if HTSprintf0 in HTString.c
+ * is changed. - kw */
+static char *LYLeakHTSprintf0(char **pstr, const char *fmt,...)
+{
+    char *str;
+    va_list ap;
+
+    LYva_start(ap, fmt);
+
+    str = LYLeakSAVsprintf(pstr, leak_cp_File_hack, leak_ssi_Line_hack,
+			   0, fmt, &ap);
+
+    va_end(ap);
+    return str;
+}
+
+/*
+ * HTSprintf and HTSprintf0 will be defined such that they effectively call one
+ * of the following two functions that store away a copy to the File & Line
+ * info in temporary hack variables, and then call the real function (which is
+ * returned here as a function pointer) to the regular HTSprintf/HTSprintf0
+ * arguments.  It's probably a bit inefficient, but that shouldn't be
+ * noticeable compared to all the time that memory tracking takes up for list
+ * traversal.  - kw
+ */
+HTSprintflike *Get_htsprintf_fn(const char *cp_File,
+				const short ssi_Line)
+{
+    leak_cp_File_hack = cp_File;
+    leak_ssi_Line_hack = ssi_Line;
+    return &LYLeakHTSprintf;
+}
+
+HTSprintflike *Get_htsprintf0_fn(const char *cp_File,
+				 const short ssi_Line)
+{
+    leak_cp_File_hack = cp_File;
+    leak_ssi_Line_hack = ssi_Line;
+    return &LYLeakHTSprintf0;
+}
+
+#endif /* LY_FIND_LEAKS and LY_FIND_LEAKS_EXTENDED */
+#else
+/* Standard C forbids an empty file */
+void no_leak_checking(void);
+void no_leak_checking(void)
+{
+}
+#endif /* LY_FIND_LEAKS */
diff --git a/src/LYList.c b/src/LYList.c
new file mode 100644
index 00000000..f1687986
--- /dev/null
+++ b/src/LYList.c
@@ -0,0 +1,347 @@
+/*			Lynx Document Reference List Support	      LYList.c
+ *			====================================
+ *
+ *	Author: FM	Foteos Macrides (macrides@sci.wfbr.edu)
+ *
+ */
+
+#include <HTUtils.h>
+#include <HTAlert.h>
+#include <LYUtils.h>
+#include <GridText.h>
+#include <LYList.h>
+#include <LYMap.h>
+#include <LYClean.h>
+#include <LYGlobalDefs.h>
+#include <LYCharUtils.h>
+#include <LYCharSets.h>
+#include <LYStrings.h>
+#include <LYHistory.h>
+
+#ifdef DIRED_SUPPORT
+#include <LYUpload.h>
+#include <LYLocal.h>
+#endif /* DIRED_SUPPORT */
+
+#include <LYexit.h>
+#include <LYLeaks.h>
+
+/*	showlist - F.Macrides (macrides@sci.wfeb.edu)
+ *	--------
+ *	Create a temporary text/html file with a list of links to
+ *	HyperText References in the current document.
+ *
+ *  On entry
+ *	titles		Set:	if we want titles where available
+ *			Clear:	we only get addresses.
+ */
+
+int showlist(DocInfo *newdoc, BOOLEAN titles)
+{
+    int cnt;
+    int refs, hidden_links;
+    static char tempfile[LY_MAXPATH];
+    static BOOLEAN last_titles = TRUE;
+    FILE *fp0;
+    char *Address = NULL, *Title = NULL, *cp = NULL;
+    char *LinkTitle = NULL;	/* Rel stored as property of link, not of dest */
+    BOOLEAN intern_w_post = FALSE;
+    const char *desc = "unknown field or link";
+    void *helper;
+
+    refs = HText_sourceAnchors(HTMainText);
+    hidden_links = HText_HiddenLinkCount(HTMainText);
+    if (refs <= 0 && hidden_links > 0 &&
+	LYHiddenLinks != HIDDENLINKS_SEPARATE) {
+	HTUserMsg(NO_VISIBLE_REFS_FROM_DOC);
+	return (-1);
+    }
+    if (refs <= 0 && hidden_links <= 0) {
+	HTUserMsg(NO_REFS_FROM_DOC);
+	return (-1);
+    }
+
+    if ((fp0 = InternalPageFP(tempfile, titles == last_titles)) == 0)
+	return (-1);
+
+    LYLocalFileToURL(&(newdoc->address), tempfile);
+
+    LYRegisterUIPage(newdoc->address,
+		     titles ? UIP_LIST_PAGE : UIP_ADDRLIST_PAGE);
+    last_titles = titles;
+    LYforce_HTML_mode = TRUE;	/* force this file to be HTML */
+    LYforce_no_cache = TRUE;	/* force this file to be new */
+
+#ifdef EXP_ADDRLIST_PAGE
+    if (titles != TRUE)
+	BeginInternalPage(fp0, ADDRLIST_PAGE_TITLE, LIST_PAGE_HELP);
+    else
+#endif
+	BeginInternalPage(fp0, LIST_PAGE_TITLE, LIST_PAGE_HELP);
+
+    StrAllocCopy(Address, HTLoadedDocumentURL());
+    LYEntify(&Address, FALSE);
+    fprintf(fp0, "%s%s<p>\n", gettext("References in "),
+	    (non_empty(Address)
+	     ? Address
+	     : gettext("this document:")));
+    FREE(Address);
+    if (refs > 0) {
+	fprintf(fp0, "<%s compact>\n", ((keypad_mode == NUMBERS_AS_ARROWS) ?
+					"ol" : "ul"));
+	if (hidden_links > 0)
+	    fprintf(fp0, "<lh><em>%s</em>\n", gettext("Visible links:"));
+    }
+    if (hidden_links > 0) {
+	if (LYHiddenLinks == HIDDENLINKS_IGNORE)
+	    hidden_links = 0;
+    }
+    helper = NULL;		/* init */
+    for (cnt = 1; cnt <= refs; cnt++) {
+	HTChildAnchor *child = HText_childNextNumber(cnt, &helper);
+	HTAnchor *dest_intl = NULL;
+	HTAnchor *dest;
+	HTParentAnchor *parent;
+	char *address;
+	const char *title;
+
+	if (child == 0) {
+	    /*
+	     * child should not be 0 unless form field numbering is on and cnt
+	     * is the number of a form input field.  HText_FormDescNumber()
+	     * will set desc to a description of what type of input field this
+	     * is.  We'll list it to ensure that the link numbers on the list
+	     * page match the numbering in the original document, but won't
+	     * create a forward link to the form.  - FM && LE
+	     *
+	     * Changed to create a fake hidden link, to get the numbering right
+	     * in connection with always treating this file as
+	     * HIDDENLINKS_MERGE in GridText.c - kw
+	     */
+	    if (fields_are_numbered()) {
+		HText_FormDescNumber(cnt, &desc);
+		fprintf(fp0,
+			"<li><a id=%d href=\"#%d\">form field</a> = <em>%s</em>\n",
+			cnt, cnt, desc);
+	    }
+	    continue;
+	}
+#ifndef DONT_TRACK_INTERNAL_LINKS
+	dest_intl = HTAnchor_followTypedLink(child, HTInternalLink);
+#endif
+	dest = dest_intl ?
+	    dest_intl : HTAnchor_followLink(child);
+	parent = HTAnchor_parent(dest);
+	if (!intern_w_post && dest_intl &&
+	    HTMainAnchor &&
+	    HTMainAnchor->post_data &&
+	    parent->post_data &&
+	    BINEQ(HTMainAnchor->post_data, parent->post_data)) {
+	    /*
+	     * Set flag to note that we had at least one internal link, if the
+	     * document from which we are generating the list has associated
+	     * POST data; after an extra check that the link destination really
+	     * has the same POST data so that we can believe it is an internal
+	     * link.
+	     */
+	    intern_w_post = TRUE;
+	}
+	address = HTAnchor_address(dest);
+	title = titles ? HTAnchor_title(parent) : NULL;
+	if (dest_intl) {
+	    HTSprintf0(&LinkTitle, "(internal)");
+	} else if (titles && child->type &&
+		   dest == child->dest &&
+		   !strncmp(HTAtom_name(child->type),
+			    "RelTitle: ", 10)) {
+	    HTSprintf0(&LinkTitle, "(%s)", HTAtom_name(child->type) + 10);
+	} else {
+	    FREE(LinkTitle);
+	}
+	StrAllocCopy(Address, address);
+	FREE(address);
+	LYEntify(&Address, TRUE);
+	if (non_empty(title)) {
+	    LYformTitle(&Title, title);
+	    LYEntify(&Title, TRUE);
+	    if (*Title) {
+		cp = findPoundSelector(Address);
+	    } else {
+		FREE(Title);
+	    }
+	}
+
+	fprintf(fp0, "<li><a href=\"%s\"%s>%s%s%s%s%s</a>\n", Address,
+		dest_intl ? " TYPE=\"internal link\"" : "",
+		NonNull(LinkTitle),
+		((HTAnchor *) parent != dest) && Title ? "in " : "",
+		(char *) (Title ? Title : Address),
+		(Title && cp) ? " - " : "",
+		(Title && cp) ? (cp + 1) : "");
+
+	FREE(Address);
+	FREE(Title);
+    }
+    FREE(LinkTitle);
+
+    if (hidden_links > 0) {
+	if (refs > 0)
+	    fprintf(fp0, "\n</%s>\n\n<p>\n",
+		    ((keypad_mode == NUMBERS_AS_ARROWS) ?
+		     "ol" : "ul"));
+	fprintf(fp0, "<%s compact>\n", ((keypad_mode == NUMBERS_AS_ARROWS) ?
+					"ol continue" : "ul"));
+	fprintf(fp0, "<lh><em>%s</em>\n", gettext("Hidden links:"));
+    }
+
+    for (cnt = 0; cnt < hidden_links; cnt++) {
+	StrAllocCopy(Address, HText_HiddenLinkAt(HTMainText, cnt));
+	LYEntify(&Address, FALSE);
+	if (isEmpty(Address)) {
+	    FREE(Address);
+	    continue;
+	}
+	fprintf(fp0, "<li><a href=\"%s\">%s</a>\n", Address, Address);
+
+	FREE(Address);
+    }
+
+    fprintf(fp0, "\n</%s>\n", ((keypad_mode == NUMBERS_AS_ARROWS) ?
+			       "ol" : "ul"));
+    EndInternalPage(fp0);
+    LYCloseTempFP(fp0);
+
+    /*
+     * Make necessary changes to newdoc before returning to caller.  If the
+     * intern_w_post flag is set, we keep the POST data in newdoc that have
+     * been passed in.  They should be the same as in the loaded document for
+     * which we generated the list.  In that case the file we have written will
+     * be associated with the same POST data when it is loaded after we are
+     * done here, so that following one of the links we have marked as
+     * "internal link" can lead back to the underlying document with the right
+     * address+post_data combination.  - kw
+     */
+    if (intern_w_post) {
+	newdoc->internal_link = TRUE;
+    } else {
+	LYFreePostData(newdoc);
+	newdoc->internal_link = FALSE;
+    }
+    newdoc->isHEAD = FALSE;
+    newdoc->safe = FALSE;
+    return (0);
+}
+
+static void print_refs(FILE *fp, BOOLEAN titles, int refs)
+{
+    int cnt;
+    char *address = NULL;
+    const char *desc = gettext("unknown field or link");
+    void *helper = NULL;	/* init */
+
+    for (cnt = 1; cnt <= refs; cnt++) {
+	HTChildAnchor *child = HText_childNextNumber(cnt, &helper);
+	HTAnchor *dest;
+	HTParentAnchor *parent;
+	const char *title;
+
+	if (child == 0) {
+	    /*
+	     * child should not be 0 unless form field numbering is on and
+	     * cnt is the number of a form input field. 
+	     * HText_FormDescNumber() will set desc to a description of
+	     * what type of input field this is.  We'll create a
+	     * within-document link to ensure that the link numbers on the
+	     * list page match the numbering in the original document, but
+	     * won't create a forward link to the form.  - FM && LE
+	     */
+	    if (fields_are_numbered()) {
+		HText_FormDescNumber(cnt, &desc);
+		fprintf(fp, "%4d. form field = %s\n", cnt, desc);
+	    }
+	    continue;
+	}
+	dest = HTAnchor_followLink(child);
+	/*
+	 * Ignore if child anchor points to itself, i.e., we had something
+	 * like <A NAME=xyz HREF="#xyz"> and it is not treated as a hidden
+	 * link.  Useful if someone 'P'rints the List Page (which isn't a
+	 * very useful action to do, but anyway...) - kw
+	 */
+	if (dest == (HTAnchor *) child)
+	    continue;
+	parent = HTAnchor_parent(dest);
+	title = titles ? HTAnchor_title(parent) : NULL;
+	address = HTAnchor_address(dest);
+	if (links_are_numbered())
+	    fprintf(fp, "%4d. ", cnt);
+	fprintf(fp, "%s%s\n",
+		((HTAnchor *) parent != dest) && title ? "in " : "",
+		(title ? title : address));
+	FREE(address);
+#ifdef VMS
+	if (HadVMSInterrupt)
+	    break;
+#endif /* VMS */
+    }
+}
+
+static void print_hidden_refs(FILE *fp, int refs, int hidden_links)
+{
+    int cnt;
+    char *address = NULL;
+
+    fprintf(fp, "%s   %s\n", ((refs > 0) ? "\n" : ""),
+	    gettext("Hidden links:"));
+    for (cnt = 0; cnt < hidden_links; cnt++) {
+	StrAllocCopy(address, HText_HiddenLinkAt(HTMainText, cnt));
+	if (isEmpty(address)) {
+	    FREE(address);
+	    continue;
+	}
+
+	if (links_are_numbered())
+	    fprintf(fp, "%4d. ", ((cnt + 1) + refs));
+	fprintf(fp, "%s\n", address);
+	FREE(address);
+#ifdef VMS
+	if (HadVMSInterrupt)
+	    break;
+#endif /* VMS */
+    }
+}
+
+/*	printlist - F.Macrides (macrides@sci.wfeb.edu)
+ *	---------
+ *	Print a text/plain list of HyperText References
+ *	in the current document.
+ *
+ *  On entry
+ *	titles		Set:	if we want titles where available
+ *			Clear:	we only get addresses.
+ */
+void printlist(FILE *fp, BOOLEAN titles)
+{
+    int refs, hidden_links;
+
+    refs = HText_sourceAnchors(HTMainText);
+    if (refs > 0 || LYHiddenLinks == HIDDENLINKS_SEPARATE) {
+	hidden_links = HText_HiddenLinkCount(HTMainText);
+	if (refs > 0 || hidden_links > 0) {
+	    if (links_are_numbered() || fields_are_numbered())
+		fprintf(fp, "\n%s\n\n", gettext("References"));
+	    if (LYHiddenLinks == HIDDENLINKS_IGNORE)
+		hidden_links = 0;
+	    if (hidden_links > 0) {
+		fprintf(fp, "   %s\n", gettext("Visible links"));
+	    }
+	    print_refs(fp, titles, refs);
+
+	    if (hidden_links > 0) {
+		print_hidden_refs(fp, refs, hidden_links);
+	    }
+	}
+    }
+    LYPrintImgMaps(fp);
+    return;
+}
diff --git a/src/LYList.h b/src/LYList.h
new file mode 100644
index 00000000..3d99adbe
--- /dev/null
+++ b/src/LYList.h
@@ -0,0 +1,15 @@
+#ifndef LYLIST_H
+#define LYLIST_H
+
+#include <LYStructs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    extern int showlist(DocInfo *newdoc, BOOLEAN titles);
+    extern void printlist(FILE *fp, BOOLEAN titles);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYLIST_H */
diff --git a/src/LYLocal.c b/src/LYLocal.c
new file mode 100644
index 00000000..932b2aa8
--- /dev/null
+++ b/src/LYLocal.c
@@ -0,0 +1,2629 @@
+/*
+ * $LynxId: LYLocal.c,v 1.106 2010/04/29 23:43:55 tom Exp $
+ *
+ *  Routines to manipulate the local filesystem.
+ *  Written by: Rick Mallett, Carleton University
+ *  Report problems to rmallett@ccs.carleton.ca
+ *  Modified 18-Dec-95 David Trueman (david@cs.dal.ca):
+ *	Added OK_PERMIT compilation option.
+ *	Support replacement of compiled-in f)ull menu configuration via
+ *	  DIRED_MENU definitions in lynx.cfg, so that more than one menu
+ *	  can be driven by the same executable.
+ *  Modified Oct-96 Klaus Weide (kweide@tezcat.com):
+ *	Changed to use the library's HTList_* functions and macros for
+ *	  managing the list of tagged file URLs.
+ *	Keep track of proper level of URL escaping, so that unusual filenames
+ *	  which contain #% etc. are handled properly (some HTUnEscapeSome()'s
+ *	  left in to be conservative, and to document where superfluous
+ *	  unescaping took place before).
+ *	Dynamic memory instead of fixed length buffers in a few cases.
+ *	Other minor changes to make things work as intended.
+ *  Modified Jun-97 Klaus Weide (kweide@tezcat.com) & FM:
+ *	Modified the code handling DIRED_MENU to do more careful
+ *	  checking of the selected file.  In addition to "TAG", "FILE", and
+ *	  "DIR", DIRED_MENU definitions in lynx.cfg now also recognize LINK as
+ *	  a type.  DIRED_MENU definitions with a type field of "LINK" are only
+ *	  used if the current selection is a symbolic link ("FILE" and "DIR"
+ *	  definitions are not used in that case).  The default menu
+ *	  definitions have been updated to reflect this change, and to avoid
+ *	  the showing of menu items whose action would always fail - KW
+ *	Cast all code into the Lynx programming style. - FM
+ */
+
+#include <HTUtils.h>
+#include <HTAAProt.h>
+#include <HTFile.h>
+#include <HTAlert.h>
+#include <HTParse.h>
+#include <LYCurses.h>
+#include <LYGlobalDefs.h>
+#include <LYUtils.h>
+#include <LYStrings.h>
+#include <LYCharUtils.h>
+#include <LYStructs.h>
+#include <LYHistory.h>
+#include <LYUpload.h>
+#include <LYLocal.h>
+#include <LYClean.h>
+#include <www_wait.h>
+
+#ifdef SUPPORT_CHDIR
+#include <LYMainLoop.h>
+#endif
+
+#include <LYLeaks.h>
+
+#undef USE_COMPRESS
+
+#ifdef __DJGPP__
+#define EXT_TAR_GZ ".tgz"
+#define EXT_TAR_Z  ".taz"
+#define EXT_Z      ".z"
+#else
+#define EXT_TAR_GZ ".tar.gz"
+#define EXT_TAR_Z  ".tar.Z"
+#define EXT_Z      ".Z"
+#endif
+
+#ifndef DIRED_MAXBUF
+#define DIRED_MAXBUF 512
+#endif
+
+#ifdef DIRED_SUPPORT
+
+#ifdef OK_INSTALL
+#ifdef FNAMES_8_3
+#define INSTALLDIRS_FILE "instdirs.htm"
+#else
+#define INSTALLDIRS_FILE ".installdirs.html"
+#endif /* FNAMES_8_3 */
+#endif /* OK_INSTALL */
+
+static char *get_filename(const char *prompt,
+			  char *buf,
+			  size_t bufsize);
+
+#ifdef OK_PERMIT
+static int permit_location(char *destpath,
+			   char *srcpath,
+			   char **newpath);
+#endif /* OK_PERMIT */
+/* *INDENT-OFF* */
+static char *render_item ( const char *	s,
+	const char *	path,
+	const char *	dir,
+	char *		buf,
+	int		bufsize,
+	BOOLEAN		url_syntax);
+
+struct dired_menu {
+    int cond;
+#define DE_TAG     1
+#define DE_DIR     2
+#define DE_FILE    3
+#define DE_SYMLINK 4
+    char *sfx;
+    const char *c_sfx;
+    char *link;
+    const char *c_link;
+    char *rest;
+    const char *c_rest;
+    char *href;
+    const char *c_href;
+    struct dired_menu *next;
+};
+
+#define GetDiredSuffix(p) ((p)->sfx  ? (p)->sfx  : (p)->c_sfx)
+#define GetDiredLink(p)   ((p)->link ? (p)->link : (p)->c_link)
+#define GetDiredRest(p)   ((p)->rest ? (p)->rest : (p)->c_rest)
+#define GetDiredHref(p)   ((p)->href ? (p)->href : (p)->c_href)
+
+#undef DATA
+#define DATA(cond, sfx, link, rest, href) { \
+	cond, \
+	NULL, sfx, \
+	NULL, link, \
+	NULL, rest, \
+	NULL, href, \
+	NULL }
+
+static struct dired_menu *menu_head = NULL;
+static struct dired_menu defmenu[] = {
+
+/*
+ * The following initializations determine the contents of the f)ull menu
+ * selection when in dired mode.  If any menu entries are defined in the
+ * configuration file via DIRED_MENU lines, then these default entries are
+ * discarded entirely.
+ */
+#ifdef SUPPORT_CHDIR
+DATA( 0,              "", "Change directory",
+                      "", "LYNXDIRED://CHDIR"),
+#endif
+DATA( 0,              "", "New File",
+"(in current directory)", "LYNXDIRED://NEW_FILE%d"),
+
+DATA( 0,              "", "New Directory",
+"(in current directory)", "LYNXDIRED://NEW_FOLDER%d"),
+
+#ifdef OK_INSTALL
+DATA( DE_FILE,        "", "Install",
+"selected file to new location", "LYNXDIRED://INSTALL_SRC%p"),
+/* The following (installing a directory) doesn't work for me, at least
+   with the "install" from GNU fileutils 4.0.  I leave it in anyway, in
+   case one compiles with INSTALL_PATH / INSTALL_ARGS defined to some
+   other command for which it works (like a script, or maybe "cp -a"). - kw
+*/
+DATA( DE_DIR,         "", "Install",
+"selected directory to new location", "LYNXDIRED://INSTALL_SRC%p"),
+#endif /* OK_INSTALL */
+
+DATA( DE_FILE,        "", "Modify File Name",
+"(of current selection)", "LYNXDIRED://MODIFY_NAME%p"),
+DATA( DE_DIR,         "", "Modify Directory Name",
+"(of current selection)", "LYNXDIRED://MODIFY_NAME%p"),
+#ifdef S_IFLNK
+DATA( DE_SYMLINK,     "", "Modify Name",
+"(of selected symbolic link)", "LYNXDIRED://MODIFY_NAME%p"),
+#endif  /* S_IFLNK */
+
+#ifdef OK_PERMIT
+DATA( DE_FILE,        "", "Modify File Permissions",
+"(of current selection)", "LYNXDIRED://PERMIT_SRC%p"),
+DATA( DE_DIR,         "", "Modify Directory Permissions",
+"(of current selection)", "LYNXDIRED://PERMIT_SRC%p"),
+#endif /* OK_PERMIT */
+
+DATA( DE_FILE,        "", "Change Location",
+"(of selected file)"    , "LYNXDIRED://MODIFY_LOCATION%p"),
+DATA( DE_DIR,         "", "Change Location",
+"(of selected directory)", "LYNXDIRED://MODIFY_LOCATION%p"),
+#ifdef S_IFLNK
+DATA( DE_SYMLINK,     "", "Change Location",
+"(of selected symbolic link)", "LYNXDIRED://MODIFY_LOCATION%p"),
+#endif /* S_IFLNK */
+
+DATA( DE_FILE,        "", "Remove File",
+   "(current selection)", "LYNXDIRED://REMOVE_SINGLE%p"),
+DATA( DE_DIR,         "", "Remove Directory",
+   "(current selection)", "LYNXDIRED://REMOVE_SINGLE%p"),
+#ifdef S_IFLNK
+DATA( DE_SYMLINK,     "", "Remove Symbolic Link",
+   "(current selection)", "LYNXDIRED://REMOVE_SINGLE%p"),
+#endif /* S_IFLNK */
+
+#if defined(OK_UUDECODE) && !defined(ARCHIVE_ONLY)
+DATA( DE_FILE,        "", "UUDecode",
+   "(current selection)", "LYNXDIRED://UUDECODE%p"),
+#endif /* OK_UUDECODE && !ARCHIVE_ONLY */
+
+#if defined(OK_TAR) && !defined(ARCHIVE_ONLY)
+DATA( DE_FILE,        EXT_TAR_Z, "Expand",
+   "(current selection)", "LYNXDIRED://UNTAR_Z%p"),
+#endif /* OK_TAR && !ARCHIVE_ONLY */
+
+#if defined(OK_TAR) && defined(OK_GZIP) && !defined(ARCHIVE_ONLY)
+DATA( DE_FILE,        ".tar.gz", "Expand",
+   "(current selection)", "LYNXDIRED://UNTAR_GZ%p"),
+
+DATA( DE_FILE,        ".tgz", "Expand",
+   "(current selection)", "LYNXDIRED://UNTAR_GZ%p"),
+#endif /* OK_TAR && OK_GZIP && !ARCHIVE_ONLY */
+
+#ifndef ARCHIVE_ONLY
+DATA( DE_FILE,        EXT_Z, "Uncompress",
+   "(current selection)", "LYNXDIRED://DECOMPRESS%p"),
+#endif /* ARCHIVE_ONLY */
+
+#if defined(OK_GZIP) && !defined(ARCHIVE_ONLY)
+DATA( DE_FILE,        ".gz", "Uncompress",
+   "(current selection)", "LYNXDIRED://UNGZIP%p"),
+#endif /* OK_GZIP && !ARCHIVE_ONLY */
+
+#if defined(OK_ZIP) && !defined(ARCHIVE_ONLY)
+DATA( DE_FILE,        ".zip", "Uncompress",
+   "(current selection)", "LYNXDIRED://UNZIP%p"),
+#endif /* OK_ZIP && !ARCHIVE_ONLY */
+
+#if defined(OK_TAR) && !defined(ARCHIVE_ONLY)
+DATA( DE_FILE,        ".tar", "UnTar",
+   "(current selection)", "LYNXDIRED://UNTAR%p"),
+#endif /* OK_TAR && !ARCHIVE_ONLY */
+
+#ifdef OK_TAR
+DATA( DE_DIR,         "", "Tar",
+   "(current selection)", "LYNXDIRED://TAR%p"),
+#endif /* OK_TAR */
+
+#if defined(OK_TAR) && defined(OK_GZIP)
+DATA( DE_DIR,         "", "Tar and compress",
+      "(using GNU gzip)", "LYNXDIRED://TAR_GZ%p"),
+#endif /* OK_TAR && OK_GZIP */
+
+#if defined(OK_TAR) && defined(USE_COMPRESS)
+DATA( DE_DIR,         "", "Tar and compress",
+      "(using compress)", "LYNXDIRED://TAR_Z%p"),
+#endif /* OK_TAR && USE_COMPRESS */
+
+#ifdef OK_ZIP
+DATA( DE_DIR,         "", "Package and compress",
+           "(using zip)", "LYNXDIRED://ZIP%p"),
+#endif /* OK_ZIP */
+
+DATA( DE_FILE,        "", "Compress",
+ "(using Unix compress)", "LYNXDIRED://COMPRESS%p"),
+
+#ifdef OK_GZIP
+DATA( DE_FILE,        "", "Compress",
+          "(using gzip)", "LYNXDIRED://GZIP%p"),
+#endif /* OK_GZIP */
+
+#ifdef OK_ZIP
+DATA( DE_FILE,        "", "Compress",
+           "(using zip)", "LYNXDIRED://ZIP%p"),
+#endif /* OK_ZIP */
+
+DATA( DE_TAG,         "", "Move all tagged items to another location.",
+                      "", "LYNXDIRED://MOVE_TAGGED%d"),
+
+#ifdef OK_INSTALL
+DATA( DE_TAG,         "", "Install tagged files into another directory.",
+                      "", "LYNXDIRED://INSTALL_SRC%00"),
+#endif
+
+DATA( DE_TAG,         "", "Remove all tagged files and directories.",
+                      "", "LYNXDIRED://REMOVE_TAGGED"),
+
+DATA( DE_TAG,         "", "Untag all tagged files and directories.",
+                      "", "LYNXDIRED://CLEAR_TAGGED"),
+
+DATA( 0,              NULL, NULL,
+                      NULL, NULL),
+};
+#undef DATA
+/* *INDENT-ON* */
+
+static BOOLEAN cannot_stat(const char *name)
+{
+    char *tmpbuf = 0;
+
+    HTSprintf0(&tmpbuf, gettext("Unable to get status of '%s'."), name);
+    HTAlert(tmpbuf);
+    FREE(tmpbuf);
+    return FALSE;
+}
+
+#define OK_STAT(name, sb) (stat(name, sb) == 0)
+
+static BOOLEAN ok_stat(const char *name, struct stat *sb)
+{
+    BOOLEAN rc = TRUE;
+
+    CTRACE((tfp, "testing ok_stat(%s)\n", name));
+    if (!OK_STAT(name, sb)) {
+#ifdef DOSPATH
+	size_t len = strlen(name);
+
+	/*
+	 * If a path ends with '\' or ':', we can guess that it may be
+	 * a directory name.  Adding a '.' (after a '\') will produce a
+	 * pathname that stat() will accept as a directory name.
+	 */
+	if (len != 0 && (name[len - 1] == '\\' || name[len - 1] == ':')) {
+	    char *temp = malloc(len + 3);
+
+	    if (temp != 0) {
+		strcpy(temp, name);
+		if (temp[len - 1] == '\\') {
+		    strcpy(temp + len, ".");
+		} else {
+		    strcpy(temp + len, "\\.");
+		}
+		rc = OK_STAT(temp, sb);
+		free(temp);
+	    } else {
+		rc = FALSE;
+	    }
+	} else
+#endif
+	    rc = FALSE;
+    }
+
+    if (rc == FALSE)
+	rc = cannot_stat(name);
+
+    return rc;
+}
+
+#ifdef HAVE_LSTAT
+static BOOLEAN ok_lstat(char *name, struct stat *sb)
+{
+    CTRACE((tfp, "testing ok_lstat(%s)\n", name));
+    if (lstat(name, sb) < 0) {
+	return cannot_stat(name);
+    }
+    return TRUE;
+}
+#else
+#define ok_lstat(name,sb) ok_stat(name,sb)
+#endif
+
+static BOOLEAN ok_file_or_dir(struct stat *sb)
+{
+    if (!S_ISDIR(sb->st_mode)
+	&& !S_ISREG(sb->st_mode)) {
+	HTAlert(gettext("The selected item is not a file or a directory!  Request ignored."));
+	return FALSE;
+    }
+    return TRUE;
+}
+
+#ifdef OK_INSTALL		/* currently only used in local_install */
+static BOOLEAN ok_localname(char *dst, const char *src)
+{
+    struct stat dir_info;
+
+    if (!ok_stat(src, &dir_info)
+	|| !ok_file_or_dir(&dir_info)) {
+	return FALSE;
+    }
+    if (strlen(src) >= DIRED_MAXBUF) {
+	CTRACE((tfp, "filename too long in ok_localname!\n"));
+	return FALSE;
+    }
+    strcpy(dst, src);
+    return TRUE;
+}
+#endif /* OK_INSTALL */
+
+/*
+ * Execute DIRED command, return -1 or 0 on failure, 1 success.
+ */
+static int LYExecv(const char *path,
+		   char **argv,
+		   char *msg)
+{
+    int rc = 0;
+
+#if defined(VMS)
+    CTRACE((tfp, "LYExecv:  Called inappropriately! (path=%s)\n", path));
+#else
+    int n;
+    char *tmpbuf = 0;
+
+#if defined(__DJGPP__) || defined(_WINDOWS)
+    stop_curses();
+    HTSprintf0(&tmpbuf, "%s", path);
+    for (n = 1; argv[n] != 0; n++)
+	HTSprintf(&tmpbuf, " %s", argv[n]);
+    HTSprintf(&tmpbuf, "\n");
+    rc = LYSystem(tmpbuf) ? 0 : 1;
+#else
+    int pid;
+
+#ifdef HAVE_TYPE_UNIONWAIT
+    union wait wstatus;
+
+#else
+    int wstatus;
+#endif
+
+    if (TRACE) {
+	CTRACE((tfp, "LYExecv path='%s'\n", path));
+	for (n = 0; argv[n] != 0; n++)
+	    CTRACE((tfp, "argv[%d] = '%s'\n", n, argv[n]));
+    }
+
+    rc = 1;			/* It will work */
+    stop_curses();
+    pid = fork();		/* fork and execute command */
+
+    switch (pid) {
+    case -1:
+	HTSprintf0(&tmpbuf, gettext("Unable to %s due to system error!"), msg);
+	rc = 0;
+	break;			/* don't fall thru! - KW */
+
+    case 0:			/* child */
+#ifdef USE_EXECVP
+	execvp(path, argv);	/* this uses our $PATH */
+#else
+	execv(path, argv);
+#endif
+	exit(EXIT_FAILURE);	/* execv failed, give wait() something to look at */
+	/*NOTREACHED */
+
+    default:			/* parent */
+#if !HAVE_WAITPID
+	while (wait(&wstatus) != pid) ;		/* do nothing */
+#else
+	while (-1 == waitpid(pid, &wstatus, 0)) {	/* wait for child */
+#ifdef EINTR
+	    if (errno == EINTR)
+		continue;
+#endif /* EINTR */
+#ifdef ERESTARTSYS
+	    if (errno == ERESTARTSYS)
+		continue;
+#endif /* ERESTARTSYS */
+	    break;
+	}
+#endif /* !HAVE_WAITPID */
+	if ((WIFEXITED(wstatus)
+	     && (WEXITSTATUS(wstatus) != 0))
+	    || (WIFSIGNALED(wstatus)
+		&& (WTERMSIG(wstatus) > 0))) {	/* error return */
+	    HTSprintf0(&tmpbuf,
+		       gettext("Probable failure to %s due to system error!"),
+		       msg);
+	    rc = 0;
+	}
+    }
+#endif /* __DJGPP__ */
+
+    if (rc == 0) {
+	/*
+	 * Screen may have message from the failed execv'd command.  Give user
+	 * time to look at it before screen refresh.
+	 */
+	LYSleepAlert();
+    }
+    start_curses();
+    if (tmpbuf != 0) {
+	if (rc == 0)
+	    HTAlert(tmpbuf);
+	FREE(tmpbuf);
+    }
+#endif /* VMS || _WINDOWS */
+    CTRACE((tfp, "LYexecv ->%d\n", rc));
+    return (rc);
+}
+
+static int make_directory(char *path)
+{
+    int code;
+    const char *program;
+
+    if ((program = HTGetProgramPath(ppMKDIR)) != NULL) {
+	char *args[5];
+	char *msg = 0;
+
+	HTSprintf0(&msg, "make directory %s", path);
+	args[0] = "mkdir";
+	args[1] = path;
+	args[2] = (char *) 0;
+	code = (LYExecv(program, args, msg) <= 0) ? -1 : 1;
+	FREE(msg);
+    } else {
+#ifdef _WINDOWS
+	code = mkdir(path) ? -1 : 1;
+#else
+	code = mkdir(path, 0777) ? -1 : 1;
+#endif
+	CTRACE((tfp, "builtin mkdir ->%d\n\t%s\n", code, path));
+    }
+    return (code);
+}
+
+static int remove_file(char *path)
+{
+    int code;
+    const char *program;
+
+    if ((program = HTGetProgramPath(ppRM)) != NULL) {
+	char *args[5];
+	char *tmpbuf = NULL;
+
+	args[0] = "rm";
+	args[1] = "-f";
+	args[2] = path;
+	args[3] = (char *) 0;
+	HTSprintf0(&tmpbuf, gettext("remove %s"), path);
+	code = LYExecv(program, args, tmpbuf);
+	FREE(tmpbuf);
+    } else {
+	code = remove(path) ? -1 : 1;
+	CTRACE((tfp, "builtin remove ->%d\n\t%s\n", code, path));
+    }
+    return (code);
+}
+
+static int remove_directory(char *path)
+{
+    int code;
+    const char *program;
+
+    if ((program = HTGetProgramPath(ppRMDIR)) != NULL) {
+	char *args[5];
+	char *tmpbuf = NULL;
+
+	args[0] = "rmdir";
+	args[1] = path;
+	args[2] = (char *) 0;
+	HTSprintf0(&tmpbuf, gettext("remove %s"), path);
+	code = LYExecv(program, args, tmpbuf);
+	FREE(tmpbuf);
+    } else {
+	code = rmdir(path) ? -1 : 1;
+	CTRACE((tfp, "builtin rmdir ->%d\n\t%s\n", code, path));
+    }
+    return (code);
+}
+
+static int touch_file(char *path)
+{
+    int code;
+    const char *program;
+
+    if ((program = HTGetProgramPath(ppTOUCH)) != NULL) {
+	char *args[5];
+	char *msg = NULL;
+
+	HTSprintf0(&msg, gettext("touch %s"), path);
+	args[0] = "touch";
+	args[1] = path;
+	args[2] = (char *) 0;
+	code = (LYExecv(program, args, msg) <= 0) ? -1 : 1;
+	FREE(msg);
+    } else {
+	FILE *fp;
+
+	if ((fp = fopen(path, BIN_W)) != 0) {
+	    fclose(fp);
+	    code = 1;
+	} else {
+	    code = -1;
+	}
+	CTRACE((tfp, "builtin touch ->%d\n\t%s\n", code, path));
+    }
+    return (code);
+}
+
+static int move_file(char *source, char *target)
+{
+    int code;
+    const char *program;
+
+    if ((program = HTGetProgramPath(ppMV)) != NULL) {
+	char *msg = 0;
+	char *args[5];
+
+	HTSprintf0(&msg, gettext("move %s to %s"), source, target);
+	args[0] = "mv";
+	args[1] = source;
+	args[2] = target;
+	args[3] = (char *) 0;
+	code = (LYExecv(program, args, msg) <= 0) ? -1 : 1;
+	FREE(msg);
+    } else {
+	struct stat sb;
+	char *actual = 0;
+
+	/* the caller sets up a target directory; we need a file path */
+	if (stat(target, &sb) == 0
+	    && S_ISDIR(sb.st_mode)) {
+	    HTSprintf0(&actual, "%s/%s", target, LYPathLeaf(source));
+	    CTRACE((tfp, "move_file source=%s, target=%s\n", source, target));
+	    target = actual;
+	}
+	code = rename(source, target);
+	CTRACE((tfp, "builtin move ->%d\n\tsource=%s\n\ttarget=%s\n",
+		code, source, target));
+	if (code != 0) {	/* it failed */
+	    if ((code = LYCopyFile(source, target)) >= 0) {
+		code = remove(source);
+		CTRACE((tfp, "...remove source after copying ->%d\n", code));
+	    }
+	}
+	if (code == 0)
+	    code = 1;
+	if (actual != target) {
+	    FREE(actual);
+	}
+    }
+    return code;
+}
+
+static BOOLEAN not_already_exists(char *name)
+{
+    struct stat dir_info;
+
+    if (!OK_STAT(name, &dir_info)) {
+	if (errno != ENOENT) {
+	    cannot_stat(name);
+	} else {
+	    return TRUE;
+	}
+    } else if (S_ISDIR(dir_info.st_mode)) {
+	HTAlert(gettext("There is already a directory with that name!  Request ignored."));
+    } else if (S_ISREG(dir_info.st_mode)) {
+	HTAlert(gettext("There is already a file with that name!  Request ignored."));
+    } else {
+	HTAlert(gettext("The specified name is already in use!  Request ignored."));
+    }
+    return FALSE;
+}
+
+static BOOLEAN dir_has_same_owner(struct stat *dst_info,
+				  struct stat *src_info)
+{
+    if (S_ISDIR(dst_info->st_mode)) {
+	if (dst_info->st_uid == src_info->st_uid) {
+	    return TRUE;
+	} else {
+	    HTAlert(gettext("Destination has different owner!  Request denied."));
+	}
+    } else {
+	HTAlert(gettext("Destination is not a valid directory!  Request denied."));
+    }
+    return FALSE;
+}
+
+/*
+ * Make sure the source and target are not the same location.
+ */
+static BOOLEAN same_location(struct stat *dst_info,
+			     struct stat *src_info)
+{
+    BOOLEAN result = FALSE;
+
+#ifdef UNIX
+    if (src_info->st_dev == dst_info->st_dev &&
+	src_info->st_ino == dst_info->st_ino) {
+	HTAlert(gettext("Source and destination are the same location!  Request ignored!"));
+	result = TRUE;
+    }
+#endif
+    return result;
+}
+
+/*
+ * Remove all tagged files and directories.
+ */
+static int remove_tagged(void)
+{
+    int ans;
+    BOOL will_clear = TRUE;
+    char *cp;
+    char *tmpbuf = NULL;
+    char *testpath = NULL;
+    struct stat dir_info;
+    int count;
+    HTList *tag;
+
+    if (HTList_isEmpty(tagged))	/* should never happen */
+	return 0;
+
+    ans = HTConfirm(gettext("Remove all tagged files and directories?"));
+
+    count = 0;
+    tag = tagged;
+    while (ans == YES && (cp = (char *) HTList_nextObject(tag)) != NULL) {
+	if (is_url(cp) == FILE_URL_TYPE) {	/* unnecessary check */
+	    testpath = HTfullURL_toFile(cp);
+	    LYTrimPathSep(testpath);
+	    will_clear = TRUE;
+
+	    /*
+	     * Check the current status of the path to be deleted.
+	     */
+	    if (!ok_stat(testpath, &dir_info)) {
+		will_clear = FALSE;
+		break;
+	    } else {
+		if (remove_file(testpath) <= 0) {
+		    if (count == 0)
+			count = -1;
+		    will_clear = FALSE;
+		    break;
+		}
+		++count;
+		FREE(testpath);
+	    }
+	}
+    }
+    FREE(testpath);
+    FREE(tmpbuf);
+    if (will_clear)
+	clear_tags();
+    return count;
+}
+
+static char *parse_directory(char *path)
+{
+    char *result;
+
+    if (path) {
+	path = strip_trailing_slash(path);
+	path = HTParse(".", path, PARSE_PATH + PARSE_PUNCTUATION);
+	result = HTURLPath_toFile(path, TRUE, FALSE);
+	FREE(path);
+    } else {			/* Last resort, should never happen. */
+	result = HTURLPath_toFile(".", TRUE, FALSE);
+    }
+    return result;
+}
+
+/*
+ * Move all tagged files and directories to a new location.
+ *
+ * The 'testpath' parameter is the current location, used for resolving
+ * relative target specifications.
+ */
+static int modify_tagged(char *testpath)
+{
+    char *cp;
+    char given_target[MAX_LINE];
+    char *dst_path = NULL;
+    char *src_path = NULL;
+    char *old_path = NULL;
+    struct stat src_info;
+    struct stat dst_info;
+    int count = 0;
+    HTList *tag;
+
+    CTRACE((tfp, "modify_tagged(%s)\n", testpath));
+
+    if (HTList_isEmpty(tagged))	/* should never happen */
+	return 0;
+
+    _statusline(gettext("Enter new location for tagged items: "));
+
+    given_target[0] = '\0';
+    LYgetstr(given_target, VISIBLE, sizeof(given_target), NORECALL);
+    if (strlen(given_target)) {
+	/*
+	 * Replace ~/ references to the home directory.
+	 */
+	if (LYIsTilde(given_target[0]) && LYIsPathSep(given_target[1])) {
+	    char *cp1 = NULL;
+
+	    StrAllocCopy(cp1, Home_Dir());
+	    StrAllocCat(cp1, (given_target + 1));
+	    if (strlen(cp1) > (sizeof(given_target) - 1)) {
+		HTAlert(gettext("Path too long"));
+		FREE(cp1);
+		return 0;
+	    }
+	    LYstrncpy(given_target, cp1, sizeof(given_target) - 1);
+	    FREE(cp1);
+	}
+
+	/*
+	 * If path is relative, prefix it with current location.
+	 */
+	if (!LYIsPathSep(given_target[0])) {
+	    dst_path = HTLocalName(testpath);
+	    LYAddPathSep(&dst_path);
+	    StrAllocCat(dst_path, given_target);
+	} else {
+	    dst_path = HTLocalName(given_target);
+	}
+
+	if (!ok_stat(dst_path, &dst_info)) {
+	    FREE(dst_path);
+	    return 0;
+	}
+
+	/*
+	 * Determine the ownership of the current location, using the directory
+	 * containing the file or subdir from each of the tagged files.
+	 */
+	for (tag = tagged; (cp = (char *) HTList_nextObject(tag)) != NULL;) {
+	    src_path = parse_directory(cp);
+
+	    if (isEmpty(old_path) || strcmp(old_path, src_path)) {
+		if (!ok_stat(src_path, &src_info)
+		    || same_location(&src_info, &dst_info)
+		    || !dir_has_same_owner(&dst_info, &src_info)) {
+		    FREE(src_path);
+		    return 0;
+		}
+	    }
+	    StrAllocCopy(old_path, src_path);
+	    FREE(src_path);
+	}
+
+	/*
+	 * Move all tagged items to the target location.
+	 */
+	for (tag = tagged; (cp = (char *) HTList_nextObject(tag)) != NULL;) {
+	    src_path = HTfullURL_toFile(cp);
+
+	    if (move_file(src_path, dst_path) < 0) {
+		if (count == 0)
+		    count = -1;
+		break;
+	    }
+	    FREE(src_path);
+	    ++count;
+	}
+	clear_tags();
+	FREE(src_path);
+	FREE(dst_path);
+    }
+    return count;
+}
+
+/*
+ * Modify the name of the specified item.
+ */
+static int modify_name(char *testpath)
+{
+    const char *cp;
+    char tmpbuf[DIRED_MAXBUF];
+    char *newpath = NULL;
+    struct stat dir_info;
+    int code = 0;
+
+    /*
+     * Determine the status of the selected item.
+     */
+    testpath = strip_trailing_slash(testpath);
+
+    if (ok_stat(testpath, &dir_info)) {
+	/*
+	 * Change the name of the file or directory.
+	 */
+	if (S_ISDIR(dir_info.st_mode)) {
+	    cp = gettext("Enter new name for directory: ");
+	} else if (S_ISREG(dir_info.st_mode)) {
+	    cp = gettext("Enter new name for file: ");
+	} else {
+	    return ok_file_or_dir(&dir_info);
+	}
+	LYstrncpy(tmpbuf, LYPathLeaf(testpath), sizeof(tmpbuf) - 1);
+	if (get_filename(cp, tmpbuf, sizeof(tmpbuf)) == NULL)
+	    return 0;
+
+	/*
+	 * Do not allow the user to also change the location at this time.
+	 */
+	if (LYLastPathSep(tmpbuf) != 0) {
+	    HTAlert(gettext("Illegal character (path-separator) found! Request ignored."));
+	} else if (strlen(tmpbuf)) {
+	    if ((cp = LYLastPathSep(testpath)) != NULL)
+		HTSprintf0(&newpath, "%.*s%s",
+			   (int) (cp - testpath + 1), testpath, tmpbuf);
+	    else
+		StrAllocCopy(newpath, tmpbuf);
+
+	    /*
+	     * Make sure the destination does not already exist.
+	     */
+	    if (not_already_exists(newpath)) {
+		code = move_file(testpath, newpath);
+	    }
+	    FREE(newpath);
+
+	}
+    }
+    return code;
+}
+
+/*
+ * Change the location of a file or directory.
+ */
+static int modify_location(char *testpath)
+{
+    const char *cp;
+    char *sp;
+    char tmpbuf[MAX_LINE];
+    char *newpath = NULL;
+    char *savepath = NULL;
+    struct stat old_info;
+    struct stat dir_info;
+    int code = 0;
+
+    /*
+     * Determine the status of the selected item.
+     */
+    testpath = strip_trailing_slash(testpath);
+    if (!ok_stat(testpath, &dir_info)) {
+	return 0;
+    }
+
+    /*
+     * Change the location of the file or directory.
+     */
+    if (S_ISDIR(dir_info.st_mode)) {
+	cp = gettext("Enter new location for directory: ");
+    } else if (S_ISREG(dir_info.st_mode)) {
+	cp = gettext("Enter new location for file: ");
+    } else {
+	return ok_file_or_dir(&dir_info);
+    }
+    LYstrncpy(tmpbuf, testpath, sizeof(tmpbuf) - 1);
+    *LYPathLeaf(tmpbuf) = '\0';
+    if (get_filename(cp, tmpbuf, sizeof(tmpbuf)) == NULL)
+	return 0;
+    if (strlen(tmpbuf)) {
+	StrAllocCopy(savepath, testpath);
+	StrAllocCopy(newpath, testpath);
+
+	/*
+	 * Allow ~/ references to the home directory.
+	 */
+	if (LYIsTilde(tmpbuf[0])
+	    && (tmpbuf[1] == '\0' || LYIsPathSep(tmpbuf[1]))) {
+	    StrAllocCopy(newpath, Home_Dir());
+	    StrAllocCat(newpath, (tmpbuf + 1));
+	    LYstrncpy(tmpbuf, newpath, sizeof(tmpbuf) - 1);
+	}
+	if (LYisAbsPath(tmpbuf)) {
+	    StrAllocCopy(newpath, tmpbuf);
+	} else if ((sp = LYLastPathSep(newpath)) != NULL) {
+	    *++sp = '\0';
+	    StrAllocCat(newpath, tmpbuf);
+	} else {
+	    HTAlert(gettext("Unexpected failure - unable to find trailing path separator"));
+	    FREE(newpath);
+	    FREE(savepath);
+	    return 0;
+	}
+
+	/*
+	 * Make sure the source and target have the same owner (uid).
+	 */
+	old_info = dir_info;
+	if (!ok_stat(newpath, &dir_info)) {
+	    code = 0;
+	} else if (same_location(&old_info, &dir_info)) {
+	    code = 0;
+	} else if (dir_has_same_owner(&dir_info, &old_info)) {
+	    code = move_file(savepath, newpath);
+	}
+	FREE(newpath);
+	FREE(savepath);
+    }
+    return code;
+}
+
+/*
+ * Modify name or location of a file or directory on localhost.
+ */
+int local_modify(DocInfo *doc, char **newpath)
+{
+    int ans;
+    char *cp;
+    char testpath[DIRED_MAXBUF];	/* a bit ridiculous */
+    int count;
+
+    if (!HTList_isEmpty(tagged)) {
+	cp = HTpartURL_toFile(doc->address);
+
+	count = modify_tagged(cp);
+	FREE(cp);
+
+	if (doc->link > (nlinks - count - 1))
+	    doc->link = (nlinks - count - 1);
+	doc->link = ((doc->link < 0)
+		     ? 0
+		     : doc->link);
+
+	return count;
+    } else if (doc->link < 0 || doc->link > nlinks) {
+	/*
+	 * Added protection.
+	 */
+	return 0;
+    }
+
+    /*
+     * Do not allow simultaneous change of name and location as in Unix.  This
+     * reduces functionality but reduces difficulty for the novice.
+     */
+#ifdef OK_PERMIT
+    _statusline(gettext("Modify name, location, or permission (n, l, or p): "));
+#else
+    _statusline(gettext("Modify name or location (n or l): "));
+#endif /* OK_PERMIT */
+    ans = LYgetch_single();
+
+    if (strchr("NLP", ans) != NULL) {
+	cp = HTfullURL_toFile(links[doc->link].lname);
+	if (strlen(cp) >= DIRED_MAXBUF) {
+	    FREE(cp);
+	    return 0;
+	}
+	LYstrncpy(testpath, cp, sizeof(testpath) - 1);
+	FREE(cp);
+
+	if (ans == 'N') {
+	    return modify_name(testpath);
+	} else if (ans == 'L') {
+	    if (modify_location(testpath)) {
+		if (doc->link == (nlinks - 1))
+		    --doc->link;
+		return 1;
+	    }
+#ifdef OK_PERMIT
+	} else if (ans == 'P') {
+	    return (permit_location(NULL, testpath, newpath));
+#endif /* OK_PERMIT */
+	} else {
+	    /*
+	     * Code for changing ownership needed here.
+	     */
+	    HTAlert(gettext("This feature not yet implemented!"));
+	}
+    }
+    return 0;
+}
+
+#define BadChars() ((!no_dotfiles && show_dotfiles) \
+		    ? "~/" \
+		    : ".~/")
+
+/*
+ * Create a new empty file in the current directory.
+ */
+static int create_file(char *current_location)
+{
+    int code = FALSE;
+    char tmpbuf[DIRED_MAXBUF];
+    char *testpath = NULL;
+
+    tmpbuf[0] = '\0';
+    if (get_filename(gettext("Enter name of file to create: "),
+		     tmpbuf, sizeof(tmpbuf)) != NULL) {
+
+	if (strstr(tmpbuf, "//") != NULL) {
+	    HTAlert(gettext("Illegal redirection \"//\" found! Request ignored."));
+	} else if (strlen(tmpbuf) && strchr(BadChars(), tmpbuf[0]) == NULL) {
+	    StrAllocCopy(testpath, current_location);
+	    LYAddPathSep(&testpath);
+
+	    /*
+	     * Append the target filename to the current location.
+	     */
+	    StrAllocCat(testpath, tmpbuf);
+
+	    /*
+	     * Make sure the target does not already exist
+	     */
+	    if (not_already_exists(testpath)) {
+		code = touch_file(testpath);
+	    }
+	    FREE(testpath);
+	}
+    }
+    return code;
+}
+
+/*
+ * Create a new directory in the current directory.
+ */
+static int create_directory(char *current_location)
+{
+    int code = FALSE;
+    char tmpbuf[DIRED_MAXBUF];
+    char *testpath = NULL;
+
+    tmpbuf[0] = '\0';
+    if (get_filename(gettext("Enter name for new directory: "),
+		     tmpbuf, sizeof(tmpbuf)) != NULL) {
+
+	if (strstr(tmpbuf, "//") != NULL) {
+	    HTAlert(gettext("Illegal redirection \"//\" found! Request ignored."));
+	} else if (strlen(tmpbuf) && strchr(BadChars(), tmpbuf[0]) == NULL) {
+	    StrAllocCopy(testpath, current_location);
+	    LYAddPathSep(&testpath);
+
+	    StrAllocCat(testpath, tmpbuf);
+
+	    /*
+	     * Make sure the target does not already exist.
+	     */
+	    if (not_already_exists(testpath)) {
+		code = make_directory(testpath);
+	    }
+	    FREE(testpath);
+	}
+    }
+    return code;
+}
+
+/*
+ * Create a file or a directory at the current location.
+ */
+int local_create(DocInfo *doc)
+{
+    int ans;
+    char *cp;
+    char testpath[DIRED_MAXBUF];
+
+    cp = HTfullURL_toFile(doc->address);
+    if (strlen(cp) >= DIRED_MAXBUF) {
+	FREE(cp);
+	return 0;
+    }
+    strcpy(testpath, cp);
+    FREE(cp);
+
+    _statusline(gettext("Create file or directory (f or d): "));
+    ans = LYgetch_single();
+
+    if (ans == 'F') {
+	return (create_file(testpath));
+    } else if (ans == 'D') {
+	return (create_directory(testpath));
+    } else {
+	return 0;
+    }
+}
+
+/*
+ * Remove a single file or directory.
+ */
+static int remove_single(char *testpath)
+{
+    int code = 0;
+    char *cp;
+    char *tmpbuf = 0;
+    struct stat dir_info;
+    BOOL is_directory = FALSE;
+
+    if (!ok_lstat(testpath, &dir_info)) {
+	return 0;
+    }
+
+    /*
+     * Locate the filename portion of the path.
+     */
+    if ((cp = LYLastPathSep(testpath)) != NULL) {
+	++cp;
+    } else {
+	cp = testpath;
+    }
+    if (S_ISDIR(dir_info.st_mode)) {
+	/*
+	 * This strlen stuff will probably screw up intl translations.  Course,
+	 * it's probably broken for screen sizes other 80, too -jes
+	 */
+	if (strlen(cp) < 37) {
+	    HTSprintf0(&tmpbuf,
+		       gettext("Remove directory '%s'?"), cp);
+	} else {
+	    HTSprintf0(&tmpbuf,
+		       gettext("Remove directory?"));
+	}
+	is_directory = TRUE;
+    } else if (S_ISREG(dir_info.st_mode)) {
+	if (strlen(cp) < 60) {
+	    HTSprintf0(&tmpbuf, gettext("Remove file '%s'?"), cp);
+	} else {
+	    HTSprintf0(&tmpbuf, gettext("Remove file?"));
+	}
+#ifdef S_IFLNK
+    } else if (S_ISLNK(dir_info.st_mode)) {
+	if (strlen(cp) < 50) {
+	    HTSprintf0(&tmpbuf, gettext("Remove symbolic link '%s'?"), cp);
+	} else {
+	    HTSprintf0(&tmpbuf, gettext("Remove symbolic link?"));
+	}
+#endif
+    } else {
+	cannot_stat(testpath);
+	FREE(tmpbuf);
+	return 0;
+    }
+
+    if (HTConfirm(tmpbuf) == YES) {
+	code = is_directory
+	    ? remove_directory(testpath)
+	    : remove_file(testpath);
+    }
+    FREE(tmpbuf);
+    return code;
+}
+
+/*
+ * Remove a file or a directory.
+ */
+int local_remove(DocInfo *doc)
+{
+    char *cp, *tp;
+    char testpath[DIRED_MAXBUF];
+    int count, i;
+
+    if (!HTList_isEmpty(tagged)) {
+	count = remove_tagged();
+	if (doc->link > (nlinks - count - 1))
+	    doc->link = (nlinks - count - 1);
+	doc->link = ((doc->link < 0)
+		     ? 0
+		     : doc->link);
+	return count;
+    } else if (doc->link < 0 || doc->link > nlinks) {
+	return 0;
+    }
+    cp = links[doc->link].lname;
+    if (is_url(cp) == FILE_URL_TYPE) {
+	tp = HTfullURL_toFile(cp);
+	if (strlen(tp) >= DIRED_MAXBUF) {
+	    FREE(tp);
+	    return 0;
+	}
+	strcpy(testpath, tp);
+	FREE(tp);
+
+	if ((i = (int) strlen(testpath)) && testpath[i - 1] == '/')
+	    testpath[(i - 1)] = '\0';
+
+	if (remove_single(testpath)) {
+	    if (doc->link == (nlinks - 1))
+		--doc->link;
+	    return 1;
+	}
+    }
+    return 0;
+}
+
+#ifdef OK_PERMIT
+
+static char LYValidPermitFile[LY_MAXPATH] = "\0";
+
+static long permit_bits(char *string_mode)
+{
+    if (!strcmp(string_mode, "IRUSR"))
+	return S_IRUSR;
+    if (!strcmp(string_mode, "IWUSR"))
+	return S_IWUSR;
+    if (!strcmp(string_mode, "IXUSR"))
+	return S_IXUSR;
+    if (!strcmp(string_mode, "IRGRP"))
+	return S_IRGRP;
+    if (!strcmp(string_mode, "IWGRP"))
+	return S_IWGRP;
+    if (!strcmp(string_mode, "IXGRP"))
+	return S_IXGRP;
+    if (!strcmp(string_mode, "IROTH"))
+	return S_IROTH;
+    if (!strcmp(string_mode, "IWOTH"))
+	return S_IWOTH;
+    if (!strcmp(string_mode, "IXOTH"))
+	return S_IXOTH;
+    /* Don't include setuid and friends; use shell access for that. */
+    return 0;
+}
+
+/*
+ * Handle DIRED permissions.
+ */
+static int permit_location(char *destpath,
+			   char *srcpath,
+			   char **newpath)
+{
+    int code = 0;
+
+#ifndef UNIX
+    HTAlert(gettext("Sorry, don't know how to permit non-UNIX files yet."));
+#else
+    static char tempfile[LY_MAXPATH] = "\0";
+    char *cp;
+    char tmpdst[LY_MAXPATH];
+    struct stat dir_info;
+    const char *program;
+
+    if (srcpath) {
+	/*
+	 * Create form.
+	 */
+	FILE *fp0;
+	char *user_filename;
+	const char *group_name;
+
+	srcpath = strip_trailing_slash(srcpath);
+
+	/*
+	 * A couple of sanity tests.
+	 */
+	if (!ok_lstat(srcpath, &dir_info)
+	    || !ok_file_or_dir(&dir_info))
+	    return code;
+
+	user_filename = LYPathLeaf(srcpath);
+
+	LYRemoveTemp(tempfile);
+	if ((fp0 = LYOpenTemp(tempfile, HTML_SUFFIX, "w")) == NULL) {
+	    HTAlert(gettext("Unable to open permit options file"));
+	    return (code);
+	}
+
+	/*
+	 * Make the tempfile a URL.
+	 */
+	LYLocalFileToURL(newpath, tempfile);
+	LYRegisterUIPage(*newpath, UIP_PERMIT_OPTIONS);
+
+	group_name = HTAA_GidToName((int) dir_info.st_gid);
+	LYstrncpy(LYValidPermitFile,
+		  srcpath,
+		  (sizeof(LYValidPermitFile) - 1));
+
+	fprintf(fp0, "<Html><Head>\n<Title>%s</Title>\n</Head>\n<Body>\n",
+		PERMIT_OPTIONS_TITLE);
+	fprintf(fp0, "<H1>%s%s</H1>\n", PERMISSIONS_SEGMENT, user_filename);
+	{
+	    /*
+	     * Prevent filenames which include '#' or '?' from messing it up.
+	     */
+	    char *srcpath_url = HTEscape(srcpath, URL_PATH);
+
+	    fprintf(fp0, "<Form Action=\"%s//PERMIT_LOCATION%s\">\n",
+		    STR_LYNXDIRED, srcpath_url);
+	    FREE(srcpath_url);
+	}
+
+	fprintf(fp0, "<Ol><Li>%s<Br><Br>\n",
+		gettext("Specify permissions below:"));
+	fprintf(fp0, "%s:<Br>\n", gettext("Owner:"));
+	fprintf(fp0,
+		"<Input Type=\"checkbox\" Name=\"mode\" Value=\"IRUSR\" %s> Read<Br>\n",
+		(dir_info.st_mode & S_IRUSR) ? "checked" : "");
+	fprintf(fp0,
+		"<Input Type=\"checkbox\" Name=\"mode\" Value=\"IWUSR\" %s> Write<Br>\n",
+		(dir_info.st_mode & S_IWUSR) ? "checked" : "");
+	/*
+	 * If restricted, only change eXecute permissions on directories.
+	 */
+	if (!no_change_exec_perms || S_ISDIR(dir_info.st_mode))
+	    fprintf(fp0,
+		    "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IXUSR\" %s> %s<Br>\n",
+		    (dir_info.st_mode & S_IXUSR) ? "checked" : "",
+		    S_ISDIR(dir_info.st_mode) ? "Search" : "Execute");
+
+	fprintf(fp0, "%s %s:<Br>\n", gettext("Group"), group_name);
+	fprintf(fp0,
+		"<Input Type=\"checkbox\" Name=\"mode\" Value=\"IRGRP\" %s> Read<Br>\n",
+		(dir_info.st_mode & S_IRGRP) ? "checked" : "");
+	fprintf(fp0,
+		"<Input Type=\"checkbox\" Name=\"mode\" Value=\"IWGRP\" %s> Write<Br>\n",
+		(dir_info.st_mode & S_IWGRP) ? "checked" : "");
+	/*
+	 * If restricted, only change eXecute permissions on directories.
+	 */
+	if (!no_change_exec_perms || S_ISDIR(dir_info.st_mode))
+	    fprintf(fp0,
+		    "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IXGRP\" %s> %s<Br>\n",
+		    (dir_info.st_mode & S_IXGRP) ? "checked" : "",
+		    S_ISDIR(dir_info.st_mode) ? "Search" : "Execute");
+
+	fprintf(fp0, "%s<Br>\n", gettext("Others:"));
+	fprintf(fp0,
+		"<Input Type=\"checkbox\" Name=\"mode\" Value=\"IROTH\" %s> Read<Br>\n",
+		(dir_info.st_mode & S_IROTH) ? "checked" : "");
+	fprintf(fp0,
+		"<Input Type=\"checkbox\" Name=\"mode\" Value=\"IWOTH\" %s> Write<Br>\n",
+		(dir_info.st_mode & S_IWOTH) ? "checked" : "");
+	/*
+	 * If restricted, only change eXecute permissions on directories.
+	 */
+	if (!no_change_exec_perms || S_ISDIR(dir_info.st_mode))
+	    fprintf(fp0,
+		    "<Input Type=\"checkbox\" Name=\"mode\" Value=\"IXOTH\" %s> %s<Br>\n",
+		    (dir_info.st_mode & S_IXOTH) ? "checked" : "",
+		    S_ISDIR(dir_info.st_mode) ? "Search" : "Execute");
+
+	fprintf(fp0,
+		"<Br>\n<Li><Input Type=\"submit\" Value=\"Submit\">  %s %s %s.\n</Ol>\n</Form>\n",
+		gettext("form to permit"),
+		S_ISDIR(dir_info.st_mode) ? "directory" : "file",
+		user_filename);
+	fprintf(fp0, "</Body></Html>");
+	LYCloseTempFP(fp0);
+
+	LYforce_no_cache = TRUE;
+	code = PERMIT_FORM_RESULT;	/* Special flag for LYMainLoop */
+
+    } else {			/* The form being activated. */
+	mode_t new_mode = 0;
+
+	/*
+	 * Make sure we have a valid set-permission file comparison string
+	 * loaded via a previous call with srcpath != NULL.  - KW
+	 */
+	if (LYValidPermitFile[0] == '\0') {
+	    if (LYCursesON)
+		HTAlert(INVALID_PERMIT_URL);
+	    else
+		fprintf(stderr, "%s\n", INVALID_PERMIT_URL);
+	    CTRACE((tfp, "permit_location: called for <%s>.\n",
+		    (destpath ?
+		     destpath : "NULL URL pointer")));
+	    return code;
+	}
+	cp = destpath;
+	while (*cp != '\0' && *cp != '?') {	/* Find filename */
+	    cp++;
+	}
+	if (*cp == '\0') {
+	    return (code);	/* Nothing to permit. */
+	}
+	*cp++ = '\0';		/* Null terminate file name and
+				   start working on the masks. */
+
+	/* Will now operate only on filename part. */
+	if ((destpath = HTURLPath_toFile(destpath, TRUE, FALSE)) == 0)
+	    return (code);
+	if (strlen(destpath) >= LY_MAXPATH) {
+	    FREE(destpath);
+	    return (code);
+	}
+	strcpy(tmpdst, destpath);
+	FREE(destpath);
+	destpath = tmpdst;
+
+	/*
+	 * Make sure that the file string is the one from the last displayed
+	 * File Permissions menu.  - KW
+	 */
+	if (strcmp(destpath, LYValidPermitFile)) {
+	    if (LYCursesON)
+		HTAlert(INVALID_PERMIT_URL);
+	    else
+		fprintf(stderr, "%s\n", INVALID_PERMIT_URL);
+	    CTRACE((tfp, "permit_location: called for file '%s'.\n",
+		    destpath));
+	    return code;
+	}
+
+	/*
+	 * A couple of sanity tests.
+	 */
+	destpath = strip_trailing_slash(destpath);
+	if (!ok_stat(destpath, &dir_info)
+	    || !ok_file_or_dir(&dir_info)) {
+	    return code;
+	}
+
+	/*
+	 * Cycle over permission strings.
+	 */
+	while (*cp != '\0') {
+	    char *cr = cp;
+
+	    while (*cr != '\0' && *cr != '&') {		/* GET data split by '&'. */
+		cr++;
+	    }
+	    if (*cr != '\0') {
+		*cr++ = '\0';
+	    }
+	    if (strncmp(cp, "mode=", 5) == 0) {		/* Magic string. */
+		long mask = permit_bits(cp + 5);
+
+		if (mask != 0) {
+		    /*
+		     * If restricted, only change eXecute permissions on
+		     * directories.
+		     */
+		    if (!no_change_exec_perms
+			|| strchr(cp + 5, 'X') == NULL
+			|| S_ISDIR(dir_info.st_mode))
+			new_mode |= mask;
+		} else {
+		    HTAlert(gettext("Invalid mode format."));
+		    return code;
+		}
+	    } else {
+		HTAlert(gettext("Invalid syntax format."));
+		return code;
+	    }
+
+	    cp = cr;
+	}
+
+	/*
+	 * Call chmod().
+	 */
+	code = 1;
+	if ((program = HTGetProgramPath(ppCHMOD)) != NULL) {
+	    char *args[5];
+	    char amode[10];
+	    char *tmpbuf = NULL;
+
+	    HTSprintf0(&tmpbuf, "chmod %.4o %s", (unsigned int) new_mode, destpath);
+	    sprintf(amode, "%.4o", (unsigned int) new_mode);
+	    args[0] = "chmod";
+	    args[1] = amode;
+	    args[2] = destpath;
+	    args[3] = (char *) 0;
+	    if (LYExecv(program, args, tmpbuf) <= 0) {
+		code = -1;
+	    }
+	    FREE(tmpbuf);
+	} else {
+	    if (chmod(destpath, new_mode) < 0) {
+		code = -1;
+	    }
+	    CTRACE((tfp, "builtin chmod %.4o ->%d\n\t%s\n",
+		    new_mode, code, destpath));
+	}
+	if (code == 1)
+	    LYforce_no_cache = TRUE;	/* Force update of dired listing. */
+    }
+#endif /* !UNIX */
+    return code;
+}
+#endif /* OK_PERMIT */
+
+/*
+ * Display or remove a tag from a given link.
+ */
+void tagflag(int flag,
+	     int cur)
+{
+    if (nlinks > 0) {
+	LYmove(links[cur].ly, 2);
+	lynx_stop_reverse();
+	if (flag == ON) {
+	    LYaddch('+');
+	} else {
+	    LYaddch(' ');
+	}
+
+#if defined(FANCY_CURSES) || defined(USE_SLANG)
+	if (!LYShowCursor)
+	    LYHideCursor();	/* get cursor out of the way */
+	else
+#endif /* FANCY CURSES || USE_SLANG */
+	    /*
+	     * Never hide the cursor if there's no FANCY CURSES.
+	     */
+	    LYmove(links[cur].ly, links[cur].lx);
+
+	LYrefresh();
+    }
+}
+
+/*
+ * Handle DIRED tags.
+ */
+void showtags(HTList *t)
+{
+    int i;
+    HTList *s;
+    char *name;
+
+    for (i = 0; i < nlinks; i++) {
+	s = t;
+	while ((name = (char *) HTList_nextObject(s)) != NULL) {
+	    if (!strcmp(links[i].lname, name)) {
+		tagflag(ON, i);
+		break;
+	    }
+	}
+    }
+}
+
+static char *DirectoryOf(char *pathname)
+{
+    char *result = 0;
+    char *leaf;
+
+    StrAllocCopy(result, pathname);
+    leaf = LYPathLeaf(result);
+
+    if (leaf != result) {
+	const char *result1 = 0;
+
+	*leaf = '\0';
+	if (!LYisRootPath(result))
+	    LYTrimPathSep(result);
+	result1 = wwwName(result);
+	StrAllocCopy(result, result1);
+    }
+    return result;
+}
+
+#ifdef __DJGPP__
+/*
+ * Convert filenames to acceptable 8+3 names when necessary.  Make a copy of
+ * the parameter if we must modify it.
+ */
+static char *LYonedot(char *line)
+{
+    char *dot;
+    static char line1[LY_MAXPATH];
+
+    if (pathconf(line, _PC_NAME_MAX) <= 12) {
+	LYstrncpy(line1, line, sizeof(line1) - 1);
+	for (;;) {
+	    if ((dot = strrchr(line1, '.')) == 0
+		|| LYLastPathSep(dot) != 0) {
+		break;
+	    } else if (strlen(dot) == 1) {
+		*dot = 0;
+	    } else {
+		*dot = '_';
+	    }
+	}
+	return (line1);
+    }
+    return (line);
+}
+#else
+#define LYonedot(path) path
+#endif /*  __DJGPP__ */
+
+static char *match_op(const char *prefix,
+		      char *data)
+{
+    int len = (int) strlen(prefix);
+
+    if (!strncmp("LYNXDIRED://", data, 12)
+	&& !strncmp(prefix, data + 12, (unsigned) len)) {
+	len += 12;
+#if defined(USE_DOS_DRIVES)
+	if (data[len] == '/') {	/* this is normal */
+	    len++;
+	}
+#endif
+	return data + len;
+    }
+    return 0;
+}
+
+/*
+ * Construct the appropriate system command taking care to escape all path
+ * references to avoid spoofing the shell.
+ */
+static char *build_command(char *line,
+			   char *dirName,
+			   char *arg)
+{
+    char *buffer = NULL;
+    const char *program;
+    const char *tar_path = HTGetProgramPath(ppTAR);
+
+    if ((arg = match_op("DECOMPRESS", line)) != 0) {
+#define FMT_UNCOMPRESS "%s %s"
+	if ((program = HTGetProgramPath(ppUNCOMPRESS)) != NULL) {
+	    HTAddParam(&buffer, FMT_UNCOMPRESS, 1, program);
+	    HTAddParam(&buffer, FMT_UNCOMPRESS, 2, arg);
+	    HTEndParam(&buffer, FMT_UNCOMPRESS, 2);
+	}
+	return buffer;
+    }
+#if defined(OK_UUDECODE) && !defined(ARCHIVE_ONLY)
+    if ((arg = match_op("UUDECODE", line)) != 0) {
+#define FMT_UUDECODE "%s %s"
+	if ((program = HTGetProgramPath(ppUUDECODE)) != NULL) {
+	    HTAddParam(&buffer, FMT_UUDECODE, 1, program);
+	    HTAddParam(&buffer, FMT_UUDECODE, 2, arg);
+	    HTEndParam(&buffer, FMT_UUDECODE, 2);
+	    HTAlert(gettext("Warning!  UUDecoded file will exist in the directory you started Lynx."));
+	}
+	return buffer;
+    }
+#endif /* OK_UUDECODE && !ARCHIVE_ONLY */
+
+#ifdef OK_TAR
+    if (tar_path != NULL) {
+# ifndef ARCHIVE_ONLY
+#  ifdef OK_GZIP
+	if ((arg = match_op("UNTAR_GZ", line)) != 0) {
+#define FMT_UNTAR_GZ "cd %s; %s -qdc %s |  %s %s %s"
+	    if ((program = HTGetProgramPath(ppGZIP)) != NULL) {
+		dirName = DirectoryOf(arg);
+		HTAddParam(&buffer, FMT_UNTAR_GZ, 1, dirName);
+		HTAddParam(&buffer, FMT_UNTAR_GZ, 2, program);
+		HTAddParam(&buffer, FMT_UNTAR_GZ, 3, arg);
+		HTAddParam(&buffer, FMT_UNTAR_GZ, 4, tar_path);
+		HTAddToCmd(&buffer, FMT_UNTAR_GZ, 5, TAR_DOWN_OPTIONS);
+		HTAddToCmd(&buffer, FMT_UNTAR_GZ, 6, TAR_PIPE_OPTIONS);
+		HTEndParam(&buffer, FMT_UNTAR_GZ, 6);
+	    }
+	    return buffer;
+	}
+#  endif			/* OK_GZIP */
+	if ((arg = match_op("UNTAR_Z", line)) != 0) {
+#define FMT_UNTAR_Z "cd %s; %s %s |  %s %s %s"
+	    if ((program = HTGetProgramPath(ppZCAT)) != NULL) {
+		dirName = DirectoryOf(arg);
+		HTAddParam(&buffer, FMT_UNTAR_Z, 1, dirName);
+		HTAddParam(&buffer, FMT_UNTAR_Z, 2, program);
+		HTAddParam(&buffer, FMT_UNTAR_Z, 3, arg);
+		HTAddParam(&buffer, FMT_UNTAR_Z, 4, tar_path);
+		HTAddToCmd(&buffer, FMT_UNTAR_Z, 5, TAR_DOWN_OPTIONS);
+		HTAddToCmd(&buffer, FMT_UNTAR_Z, 6, TAR_PIPE_OPTIONS);
+		HTEndParam(&buffer, FMT_UNTAR_Z, 6);
+	    }
+	    return buffer;
+	}
+	if ((arg = match_op("UNTAR", line)) != 0) {
+#define FMT_UNTAR "cd %s; %s %s %s"
+	    dirName = DirectoryOf(arg);
+	    HTAddParam(&buffer, FMT_UNTAR, 1, dirName);
+	    HTAddParam(&buffer, FMT_UNTAR, 2, tar_path);
+	    HTAddToCmd(&buffer, FMT_UNTAR, 3, TAR_DOWN_OPTIONS);
+	    HTAddParam(&buffer, FMT_UNTAR, 4, arg);
+	    HTEndParam(&buffer, FMT_UNTAR, 4);
+	    return buffer;
+	}
+# endif				/* !ARCHIVE_ONLY */
+
+# ifdef OK_GZIP
+	if ((arg = match_op("TAR_GZ", line)) != 0) {
+#define FMT_TAR_GZ "cd %s; %s %s %s %s | %s -qc >%s%s"
+	    if ((program = HTGetProgramPath(ppGZIP)) != NULL) {
+		dirName = DirectoryOf(arg);
+		HTAddParam(&buffer, FMT_TAR_GZ, 1, dirName);
+		HTAddParam(&buffer, FMT_TAR_GZ, 2, tar_path);
+		HTAddToCmd(&buffer, FMT_TAR_GZ, 3, TAR_UP_OPTIONS);
+		HTAddToCmd(&buffer, FMT_TAR_GZ, 4, TAR_PIPE_OPTIONS);
+		HTAddParam(&buffer, FMT_TAR_GZ, 5, LYPathLeaf(arg));
+		HTAddParam(&buffer, FMT_TAR_GZ, 6, program);
+		HTAddParam(&buffer, FMT_TAR_GZ, 7, LYonedot(LYPathLeaf(arg)));
+		HTAddParam(&buffer, FMT_TAR_GZ, 8, EXT_TAR_GZ);
+		HTEndParam(&buffer, FMT_TAR_GZ, 8);
+	    }
+	    return buffer;
+	}
+# endif				/* OK_GZIP */
+
+	if ((arg = match_op("TAR_Z", line)) != 0) {
+#define FMT_TAR_Z "cd %s; %s %s %s %s | %s >%s%s"
+	    if ((program = HTGetProgramPath(ppCOMPRESS)) != NULL) {
+		dirName = DirectoryOf(arg);
+		HTAddParam(&buffer, FMT_TAR_Z, 1, dirName);
+		HTAddParam(&buffer, FMT_TAR_Z, 2, tar_path);
+		HTAddToCmd(&buffer, FMT_TAR_Z, 3, TAR_UP_OPTIONS);
+		HTAddToCmd(&buffer, FMT_TAR_Z, 4, TAR_PIPE_OPTIONS);
+		HTAddParam(&buffer, FMT_TAR_Z, 5, LYPathLeaf(arg));
+		HTAddParam(&buffer, FMT_TAR_Z, 6, program);
+		HTAddParam(&buffer, FMT_TAR_Z, 7, LYonedot(LYPathLeaf(arg)));
+		HTAddParam(&buffer, FMT_TAR_Z, 8, EXT_TAR_Z);
+		HTEndParam(&buffer, FMT_TAR_Z, 8);
+	    }
+	    return buffer;
+	}
+
+	if ((arg = match_op("TAR", line)) != 0) {
+#define FMT_TAR "cd %s; %s %s %s %s.tar %s"
+	    dirName = DirectoryOf(arg);
+	    HTAddParam(&buffer, FMT_TAR, 1, dirName);
+	    HTAddParam(&buffer, FMT_TAR, 2, tar_path);
+	    HTAddToCmd(&buffer, FMT_TAR, 3, TAR_UP_OPTIONS);
+	    HTAddToCmd(&buffer, FMT_TAR, 4, TAR_FILE_OPTIONS);
+	    HTAddParam(&buffer, FMT_TAR, 5, LYonedot(LYPathLeaf(arg)));
+	    HTAddParam(&buffer, FMT_TAR, 6, LYPathLeaf(arg));
+	    HTEndParam(&buffer, FMT_TAR, 6);
+	    return buffer;
+	}
+    }
+#endif /* OK_TAR */
+
+#ifdef OK_GZIP
+    if ((arg = match_op("GZIP", line)) != 0) {
+#define FMT_GZIP "%s -q %s"
+	if ((program = HTGetProgramPath(ppGZIP)) != NULL) {
+	    HTAddParam(&buffer, FMT_GZIP, 1, program);
+	    HTAddParam(&buffer, FMT_GZIP, 2, arg);
+	    HTEndParam(&buffer, FMT_GZIP, 2);
+	}
+	return buffer;
+    }
+#ifndef ARCHIVE_ONLY
+    if ((arg = match_op("UNGZIP", line)) != 0) {
+#define FMT_UNGZIP "%s -d %s"
+	if ((program = HTGetProgramPath(ppGZIP)) != NULL) {
+	    HTAddParam(&buffer, FMT_UNGZIP, 1, program);
+	    HTAddParam(&buffer, FMT_UNGZIP, 2, arg);
+	    HTEndParam(&buffer, FMT_UNGZIP, 2);
+	}
+	return buffer;
+    }
+#endif /* !ARCHIVE_ONLY */
+#endif /* OK_GZIP */
+
+#ifdef OK_ZIP
+    if ((arg = match_op("ZIP", line)) != 0) {
+#define FMT_ZIP "cd %s; %s -rq %s.zip %s"
+	if ((program = HTGetProgramPath(ppZIP)) != NULL) {
+	    dirName = DirectoryOf(arg);
+	    HTAddParam(&buffer, FMT_ZIP, 1, dirName);
+	    HTAddParam(&buffer, FMT_ZIP, 2, program);
+	    HTAddParam(&buffer, FMT_ZIP, 3, LYonedot(LYPathLeaf(arg)));
+	    HTAddParam(&buffer, FMT_ZIP, 4, LYPathLeaf(arg));
+	    HTEndParam(&buffer, FMT_ZIP, 4);
+	}
+	return buffer;
+    }
+#if !defined(ARCHIVE_ONLY)
+    if ((arg = match_op("UNZIP", line)) != 0) {
+#define FMT_UNZIP "cd %s; %s -q %s"
+	if ((program = HTGetProgramPath(ppUNZIP)) != NULL) {
+	    dirName = DirectoryOf(arg);
+	    HTAddParam(&buffer, FMT_UNZIP, 1, dirName);
+	    HTAddParam(&buffer, FMT_UNZIP, 2, program);
+	    HTAddParam(&buffer, FMT_UNZIP, 3, arg);
+	    HTEndParam(&buffer, FMT_UNZIP, 3);
+	}
+	return buffer;
+    }
+# endif				/* !ARCHIVE_ONLY */
+#endif /* OK_ZIP */
+
+    if ((arg = match_op("COMPRESS", line)) != 0) {
+#define FMT_COMPRESS "%s %s"
+	if ((program = HTGetProgramPath(ppCOMPRESS)) != NULL) {
+	    HTAddParam(&buffer, FMT_COMPRESS, 1, program);
+	    HTAddParam(&buffer, FMT_COMPRESS, 2, arg);
+	    HTEndParam(&buffer, FMT_COMPRESS, 2);
+	}
+	return buffer;
+    }
+
+    return NULL;
+}
+
+/*
+ * Perform file management operations for LYNXDIRED URL's.  Attempt to be
+ * consistent.  These are (pseudo) URLs - i.e., they should be in URL syntax: 
+ * some bytes will be URL-escaped with '%'.  This is necessary because these
+ * (pseudo) URLs will go through some of the same kinds of interpretations and
+ * mutilations as real ones:  HTParse, stripping off #fragments etc.  (Some
+ * access schemes currently have special rules about not escaping parsing '#'
+ * "the URL way" built into HTParse, but that doesn't look like a clean way.)
+ */
+int local_dired(DocInfo *doc)
+{
+    char *line_url;		/* will point to doc's address, which is a URL */
+    char *line = NULL;		/* same as line_url, but HTUnEscaped, will be alloced */
+    char *arg = NULL;		/* ...will point into line[] */
+    char *tp = NULL;
+    char *tmpbuf = NULL;
+    char *buffer = NULL;
+    char *dirName = NULL;
+    BOOL do_pop_doc = TRUE;
+
+    line_url = doc->address;
+    CTRACE((tfp, "local_dired: called for <%s>.\n",
+	    (line_url
+	     ? line_url
+	     : gettext("NULL URL pointer"))));
+    HTUnEscapeSome(line_url, "/");	/* don't mess too much with *doc */
+
+    StrAllocCopy(line, line_url);
+    HTUnEscape(line);		/* _file_ (not URL) syntax, for those functions
+				   that need it.  Don't forget to FREE it. */
+    if (match_op("CHDIR", line) != 0) {
+#ifdef SUPPORT_CHDIR
+	handle_LYK_CHDIR();
+	do_pop_doc = FALSE;
+#endif
+	arg = 0;		/* do something to avoid cc's complaints */
+    } else if ((arg = match_op("NEW_FILE", line)) != 0) {
+	if (create_file(arg) > 0)
+	    LYforce_no_cache = TRUE;
+    } else if ((arg = match_op("NEW_FOLDER", line)) != 0) {
+	if (create_directory(arg) > 0)
+	    LYforce_no_cache = TRUE;
+#ifdef OK_INSTALL
+    } else if ((arg = match_op("INSTALL_SRC", line)) != 0) {
+	local_install(NULL, arg, &tp);
+	if (tp) {
+	    FREE(doc->address);
+	    doc->address = tp;
+	}
+	FREE(line);
+	return 0;
+    } else if ((arg = match_op("INSTALL_DEST", line)) != 0) {
+	local_install(arg, NULL, &tp);
+	LYpop(doc);
+#endif /* OK_INSTALL */
+    } else if ((arg = match_op("MODIFY_NAME", line)) != 0) {
+	if (modify_name(arg) > 0)
+	    LYforce_no_cache = TRUE;
+    } else if ((arg = match_op("MODIFY_LOCATION", line)) != 0) {
+	if (modify_location(arg) > 0)
+	    LYforce_no_cache = TRUE;
+    } else if ((arg = match_op("MOVE_TAGGED", line_url)) != 0) {
+	if (modify_tagged(arg) > 0)
+	    LYforce_no_cache = TRUE;
+#ifdef OK_PERMIT
+    } else if ((arg = match_op("PERMIT_SRC", line)) != 0) {
+	permit_location(NULL, arg, &tp);
+	if (tp) {
+	    /*
+	     * One of the checks may have failed.
+	     */
+	    FREE(doc->address);
+	    doc->address = tp;
+	}
+	FREE(line);
+	return 0;
+    } else if ((arg = match_op("PERMIT_LOCATION", line_url)) != 0) {
+	permit_location(arg, NULL, &tp);
+#endif /* OK_PERMIT */
+    } else if ((arg = match_op("REMOVE_SINGLE", line)) != 0) {
+	if (remove_single(arg) > 0)
+	    LYforce_no_cache = TRUE;
+    } else if (match_op("REMOVE_TAGGED", line) != 0) {
+	if (remove_tagged())
+	    LYforce_no_cache = TRUE;
+    } else if (match_op("CLEAR_TAGGED", line) != 0) {
+	clear_tags();
+    } else if ((arg = match_op("UPLOAD", line)) != 0) {
+	/*
+	 * They're written by LYUpload_options() HTUnEscaped; don't want to
+	 * change that for now...  so pass through without more unescaping. 
+	 * Directory names containing '#' will probably fail.
+	 */
+	if (LYUpload(line_url))
+	    LYforce_no_cache = TRUE;
+    } else {
+	LYTrimPathSep(line);
+	if (LYLastPathSep(line) == NULL) {
+	    FREE(line);
+	    return 0;
+	}
+
+	buffer = build_command(line, dirName, arg);
+
+	if (buffer != 0) {
+	    if ((int) strlen(buffer) < LYcolLimit - 14) {
+		HTSprintf0(&tmpbuf, gettext("Executing %s "), buffer);
+	    } else {
+		HTSprintf0(&tmpbuf,
+			   gettext("Executing system command. This might take a while."));
+	    }
+	    _statusline(tmpbuf);
+	    stop_curses();
+	    printf("%s\r\n", tmpbuf);
+	    LYSystem(buffer);
+#ifdef VMS
+	    HadVMSInterrupt = FALSE;
+#endif /* VMS */
+	    start_curses();
+	    LYforce_no_cache = TRUE;
+	}
+    }
+
+    FREE(dirName);
+    FREE(tmpbuf);
+    FREE(buffer);
+    FREE(line);
+    FREE(tp);
+    if (do_pop_doc)
+	LYpop(doc);
+    return 0;
+}
+
+/*
+ * Provide a menu of file management options.
+ */
+int dired_options(DocInfo *doc, char **newfile)
+{
+    static char tempfile[LY_MAXPATH];
+    const char *my_suffix;
+    char *path;
+    char *dir;
+    lynx_list_item_type *nxt;
+    struct stat dir_info;
+    FILE *fp0;
+    char *dir_url;
+    char *path_url;
+    BOOLEAN nothing_tagged;
+    int count;
+    struct dired_menu *mp;
+    char buf[2048];
+
+    if ((fp0 = InternalPageFP(tempfile, FALSE)) == 0)
+	return (0);
+
+    /*
+     * Make the tempfile a URL.
+     */
+    LYLocalFileToURL(newfile, tempfile);
+    LYRegisterUIPage(*newfile, UIP_DIRED_MENU);
+
+    if (doc->link > -1 && doc->link < (nlinks + 1)) {
+	path = HTfullURL_toFile(links[doc->link].lname);
+	LYTrimPathSep(path);
+
+	if (!ok_lstat(path, &dir_info)) {
+	    LYCloseTempFP(fp0);
+	    FREE(path);
+	    return 0;
+	}
+
+    } else {
+	StrAllocCopy(path, "");
+    }
+
+    dir = HTfullURL_toFile(doc->address);
+    LYTrimPathSep(dir);
+
+    nothing_tagged = (BOOL) (HTList_isEmpty(tagged));
+
+    BeginInternalPage(fp0, DIRED_MENU_TITLE, DIRED_MENU_HELP);
+
+    fprintf(fp0, "<em>%s</em> %s<br>\n", gettext("Current directory:"), dir);
+
+    if (nothing_tagged) {
+	fprintf(fp0, "<em>%s</em> ", gettext("Current selection:"));
+	if (strlen(path)) {
+	    fprintf(fp0, "%s<p>\n", path);
+	} else {
+	    fprintf(fp0, "%s.<p>\n", gettext("Nothing currently selected."));
+	}
+    } else {
+	/*
+	 * Write out number of tagged items, and names of first few of them
+	 * relative to current (in the DIRED sense) directory.
+	 */
+	int n = HTList_count(tagged);
+	char *cp1 = NULL;
+	char *cd = NULL;
+	int i, m;
+
+#define NUM_TAGS_TO_WRITE 10
+	fprintf(fp0, "<em>%s</em> %d %s",
+		gettext("Current selection:"),
+		n, ((n == 1)
+		    ? gettext("tagged item:")
+		    : gettext("tagged items:")));
+	StrAllocCopy(cd, doc->address);
+	HTUnEscapeSome(cd, "/");
+	LYAddHtmlSep(&cd);
+	m = (n < NUM_TAGS_TO_WRITE) ? n : NUM_TAGS_TO_WRITE;
+	for (i = 1; i <= m; i++) {
+	    cp1 = HTRelative((char *) HTList_objectAt(tagged, i - 1),
+			     (*cd ? cd : "file://localhost"));
+	    HTUnEscape(cp1);
+	    LYEntify(&cp1, TRUE);	/* _should_ do this everywhere... */
+	    fprintf(fp0, "%s<br>\n&nbsp;&nbsp;&nbsp;%s",
+		    (i == 1 ? "" : " ,"), cp1);
+	    FREE(cp1);
+	}
+	if (n > m) {
+	    fprintf(fp0, " , ...");
+	}
+	fprintf(fp0, "<p>\n");
+	FREE(cd);
+    }
+
+    /*
+     * If menu_head is NULL then use defaults and link them together now.
+     */
+    if (menu_head == NULL) {
+	for (mp = defmenu; GetDiredHref(mp) != NULL; mp++)
+	    mp->next = (mp + 1);
+	(--mp)->next = NULL;
+	menu_head = defmenu;
+    }
+
+    for (mp = menu_head; mp != NULL; mp = mp->next) {
+	if (mp->cond != DE_TAG && !nothing_tagged)
+	    continue;
+	if (mp->cond == DE_TAG && nothing_tagged)
+	    continue;
+	if (mp->cond == DE_DIR &&
+	    (!*path || !S_ISDIR(dir_info.st_mode)))
+	    continue;
+	if (mp->cond == DE_FILE &&
+	    (!*path || !S_ISREG(dir_info.st_mode)))
+	    continue;
+#ifdef S_IFLNK
+	if (mp->cond == DE_SYMLINK &&
+	    (!*path || !S_ISLNK(dir_info.st_mode)))
+	    continue;
+#endif
+	my_suffix = GetDiredSuffix(mp);
+	if (non_empty(my_suffix) &&
+	    (strlen(path) < strlen(my_suffix) ||
+	     strcmp(my_suffix, &path[(strlen(path) - strlen(my_suffix))]) != 0))
+	    continue;
+	dir_url = HTEscape(dir, URL_PATH);
+	path_url = HTEscape(path, URL_PATH);
+	fprintf(fp0, "<a href=\"%s",
+		render_item(GetDiredHref(mp),
+			    path_url, dir_url, buf, sizeof(buf), YES));
+	fprintf(fp0, "\">%s</a> ",
+		render_item(GetDiredLink(mp),
+			    path, dir, buf, sizeof(buf), NO));
+	fprintf(fp0, "%s<br>\n",
+		render_item(GetDiredRest(mp),
+			    path, dir, buf, sizeof(buf), NO));
+	FREE(dir_url);
+	FREE(path_url);
+    }
+    FREE(path);
+
+    if (uploaders != NULL) {
+	fprintf(fp0, "<p>Upload to current directory:<p>\n");
+	for (count = 0, nxt = uploaders;
+	     nxt != NULL;
+	     nxt = nxt->next, count++) {
+	    fprintf(fp0,
+		    "<a href=\"LYNXDIRED://UPLOAD=%d/TO=%s\"> %s </a><br>\n",
+		    count, dir, nxt->name);
+	}
+    }
+    FREE(dir);
+
+    EndInternalPage(fp0);
+    LYCloseTempFP(fp0);
+
+    LYforce_no_cache = TRUE;
+
+    return (0);
+}
+
+/*
+ * Check DIRED filename.
+ */
+static char *get_filename(const char *prompt,
+			  char *buf,
+			  size_t bufsize)
+{
+    char *cp;
+
+    _statusline(prompt);
+
+    LYgetstr(buf, VISIBLE, bufsize, NORECALL);
+    if (strstr(buf, "../") != NULL) {
+	HTAlert(gettext("Illegal filename; request ignored."));
+	return NULL;
+    }
+
+    if (no_dotfiles || !show_dotfiles) {
+	cp = LYLastPathSep(buf);	/* find last slash */
+	if (cp)
+	    cp += 1;
+	else
+	    cp = buf;
+	if (*cp == '.') {
+	    HTAlert(gettext("Illegal filename; request ignored."));
+	    return NULL;
+	}
+    }
+    return buf;
+}
+
+#ifdef OK_INSTALL
+
+#define LYEXECV_MAX_ARGC 15
+/* these are quasi-constant once they have been allocated: */
+static char **install_argp = NULL;	/* args for execv install */
+static char *install_path = NULL;	/* auxiliary */
+
+#ifdef LY_FIND_LEAKS
+static void clear_install_path(void)
+{
+    FREE(install_argp);
+    FREE(install_path);
+}
+#endif /* LY_FIND_LEAKS */
+
+/*
+ * Fill in args array for execv (or execvp etc.) call, after first allocating
+ * it if necessary.  No fancy parsing, cmd_args is just split at spaces.  Leave
+ * room for reserve additional args to be added by caller.
+ *
+ * On success *argvp points to new args vector, *pathp is auxiliary.  On
+ * success returns index of next argument, else -1.  This is generic enough
+ * that it could be used for other calls than install, except the atexit call. 
+ * Go through this trouble for install because INSTALL_ARGS may be significant,
+ * and someone may configure it with more than one significant flags.  - kw
+ */
+static int fill_argv_for_execv(char ***argvp,
+			       char **pathp,
+			       char *cmd_path,
+			       const char *cmd_args,
+			       int reserve)
+{
+    int n = 0;
+
+    char **args;
+    char *cp;
+
+    if (*argvp == NULL) {
+	*argvp = typecallocn(char *, LYEXECV_MAX_ARGC + 1);
+
+	if (!*argvp)
+	    return (-1);
+#ifdef LY_FIND_LEAKS
+	atexit(clear_install_path);
+#endif
+    }
+    args = *argvp;
+    args[n++] = cmd_path;
+    if (cmd_args) {
+	StrAllocCopy(*pathp, cmd_args);
+	cp = strtok(*pathp, " ");
+	if (cp) {
+	    while (cp && (n < LYEXECV_MAX_ARGC - reserve)) {
+		args[n++] = cp;
+		cp = strtok(NULL, " ");
+	    }
+	    if (cp && (n >= LYEXECV_MAX_ARGC - reserve)) {
+		CTRACE((tfp, "Too many args for '%s' in '%s'!\n",
+			NONNULL(cmd_path), cmd_args));
+		return (-1);
+	    }
+	} else {
+	    args[n++] = *pathp;
+	}
+    }
+    args[n] = (char *) 0;
+    return (n);
+}
+
+/*
+ * Install the specified file or directory.
+ */
+BOOLEAN local_install(char *destpath,
+		      char *srcpath,
+		      char **newpath)
+{
+    char *tmpbuf = NULL;
+    static char savepath[DIRED_MAXBUF];		/* This will be the link that
+
+						   is to be installed. */
+    struct stat dir_info;
+    char **args;
+    HTList *tag;
+    char *cp = NULL;
+    char *tmpdest = NULL;
+    int count = 0;
+    int n = 0;			/* indices into 'args[]' */
+    static int src = -1;
+    const char *program;
+
+    if ((program = HTGetProgramPath(ppINSTALL)) == NULL) {
+	HTAlert(gettext("Install in the selected directory not permitted."));
+	return 0;
+    }
+
+    /*
+     * Determine the status of the selected item.
+     */
+    if (srcpath) {
+	srcpath = strip_trailing_slash(srcpath);
+	if (is_url(srcpath)) {
+	    char *local_src = HTfullURL_toFile(srcpath);
+
+	    if (!ok_localname(savepath, local_src)) {
+		FREE(local_src);
+		return 0;
+	    }
+	    FREE(local_src);
+	} else if (!HTList_isEmpty(tagged) &&
+		   srcpath[0] == '\0') {
+	    savepath[0] = '\0';	/* will always use tagged list - kw */
+	} else if (!ok_localname(savepath, srcpath)) {
+	    return 0;
+	}
+	LYforce_no_cache = TRUE;
+	LYLocalFileToURL(newpath, Home_Dir());
+	LYAddHtmlSep(newpath);
+	StrAllocCat(*newpath, INSTALLDIRS_FILE);
+	LYRegisterUIPage(*newpath, UIP_INSTALL);
+	return 0;
+    }
+
+    /* deal with ~/ or /~/ at the beginning - kw */
+    if (LYIsTilde(destpath[0]) &&
+	(LYIsPathSep(destpath[1]) || destpath[1] == '\0')) {
+	cp = &destpath[1];
+    } else if (LYIsPathSep(destpath[0]) && LYIsTilde(destpath[1]) &&
+	       (LYIsPathSep(destpath[2]) || destpath[2] == '\0')) {
+	cp = &destpath[2];
+    }
+    if (cp) {
+	/* If found, allocate new string, make destpath point to it - kw */
+	StrAllocCopy(tmpdest, Home_Dir());
+	if (cp[0] && cp[1]) {
+	    LYAddPathSep(&tmpdest);
+	    StrAllocCat(tmpdest, cp + 1);
+	}
+	destpath = tmpdest;
+    }
+
+    destpath = strip_trailing_slash(destpath);
+
+    if (!ok_stat(destpath, &dir_info)) {
+	FREE(tmpdest);
+	return 0;
+    } else if (!S_ISDIR(dir_info.st_mode)) {
+	HTAlert(gettext("The selected item is not a directory!  Request ignored."));
+	FREE(tmpdest);
+	return 0;
+    } else if (0 /*directory not writable */ ) {
+	HTAlert(gettext("Install in the selected directory not permitted."));
+	FREE(tmpdest);
+	return 0;
+    }
+
+    statusline(gettext("Just a moment, ..."));
+
+    /* fill in the fixed args, if not already done - kw */
+    if (src > 0 && install_argp) {
+	n = src;
+	n++;
+    } else {
+	n = fill_argv_for_execv(&install_argp, &install_path,
+				"install",
+#ifdef INSTALL_ARGS
+				INSTALL_ARGS,
+#else
+				NULL,
+#endif /* INSTALL_ARGS */
+				2);
+	if (n <= 0) {
+	    src = 0;
+	    HTAlert(gettext("Error building install args"));
+	    FREE(tmpdest);
+	    return 0;
+	}
+	src = n++;
+    }
+    args = install_argp;
+
+    args[n++] = destpath;
+    args[n] = (char *) 0;
+    tag = tagged;
+
+    if (HTList_isEmpty(tagged)) {
+	/* simplistic detection of identical src and dest - kw */
+	if (!strcmp(savepath, destpath)) {
+	    HTUserMsg2(gettext("Source and target are the same: %s"),
+		       savepath);
+	    FREE(tmpdest);
+	    return (-1);	/* don't do it */
+	} else if (!strncmp(savepath, destpath, strlen(destpath)) &&
+		   LYIsPathSep(savepath[strlen(destpath)]) &&
+		   LYLastPathSep(savepath + strlen(destpath) + 1) == 0) {
+	    HTUserMsg2(gettext("Already in target directory: %s"),
+		       savepath);
+	    FREE(tmpdest);
+	    return 0;		/* don't do it */
+	}
+	args[src] = savepath;
+	HTSprintf0(&tmpbuf, "install %s in %s", savepath, destpath);
+	if (LYExecv(program, args, tmpbuf) <= 0) {
+	    FREE(tmpbuf);
+	    FREE(tmpdest);
+	    return (-1);
+	}
+	count++;
+    } else {
+	char *name;
+
+	HTSprintf0(&tmpbuf, "install in %s", destpath);
+	while ((name = (char *) HTList_nextObject(tag))) {
+	    int err;
+
+	    args[src] = HTfullURL_toFile(name);
+
+	    /* simplistic detection of identical src and dest - kw */
+	    if (!strcmp(args[src], destpath)) {
+		HTUserMsg2(gettext("Source and target are the same: %s"),
+			   args[src]);
+		FREE(args[src]);
+		continue;	/* skip this source file */
+	    } else if (!strncmp(args[src], destpath, strlen(destpath)) &&
+		       LYIsPathSep(args[src][strlen(destpath)]) &&
+		       LYLastPathSep(args[src] + strlen(destpath) + 1) == 0) {
+		HTUserMsg2(gettext("Already in target directory: %s"),
+			   args[src]);
+		FREE(args[src]);
+		continue;	/* skip this source file */
+	    }
+	    err = (LYExecv(program, args, tmpbuf) <= 0);
+	    FREE(args[src]);
+	    if (err) {
+		FREE(tmpbuf);
+		FREE(tmpdest);
+		return ((count == 0) ? -1 : count);
+	    }
+	    count++;
+	}
+	clear_tags();
+    }
+    FREE(tmpbuf);
+    FREE(tmpdest);
+    HTInfoMsg(gettext("Installation complete"));
+    return count;
+}
+#endif /* OK_INSTALL */
+
+/*
+ * Clear DIRED tags.
+ */
+void clear_tags(void)
+{
+    char *cp = NULL;
+
+    while ((cp = (char *) HTList_removeLastObject(tagged)) != NULL) {
+	FREE(cp);
+    }
+    if (HTList_isEmpty(tagged))
+	FREE(tagged);
+}
+
+/*
+ * Handle DIRED menu item.
+ */
+void add_menu_item(char *str)
+{
+    struct dired_menu *tmp, *mp;
+    char *cp;
+
+    /*
+     * First custom menu definition causes entire default menu to be discarded.
+     */
+    if (menu_head == defmenu)
+	menu_head = NULL;
+
+    tmp = typecalloc(struct dired_menu);
+
+    if (tmp == NULL)
+	outofmem(__FILE__, "add_menu_item");
+
+    assert(tmp != NULL);
+
+    /*
+     * Conditional on tagged != NULL ?
+     */
+    cp = strchr(str, ':');
+    *cp++ = '\0';
+    if (strcasecomp(str, "tag") == 0) {
+	tmp->cond = DE_TAG;
+    } else if (strcasecomp(str, "dir") == 0) {
+	tmp->cond = DE_DIR;
+    } else if (strcasecomp(str, "file") == 0) {
+	tmp->cond = DE_FILE;
+#ifdef S_IFLNK
+    } else if (strcasecomp(str, "link") == 0) {
+	tmp->cond = DE_SYMLINK;
+#endif /* S_IFLNK */
+    }
+
+    /*
+     * Conditional on matching suffix.
+     */
+    str = cp;
+    cp = strchr(str, ':');
+    *cp++ = '\0';
+    StrAllocCopy(tmp->sfx, str);
+
+    str = cp;
+    cp = strchr(str, ':');
+    *cp++ = '\0';
+    StrAllocCopy(tmp->link, str);
+
+    str = cp;
+    cp = strchr(str, ':');
+    *cp++ = '\0';
+    StrAllocCopy(tmp->rest, str);
+
+    StrAllocCopy(tmp->href, cp);
+
+    if (menu_head) {
+	for (mp = menu_head; mp && mp->next != NULL; mp = mp->next) {
+	    ;
+	}
+	if (mp != NULL)
+	    mp->next = tmp;
+    } else
+	menu_head = tmp;
+}
+
+void reset_dired_menu(void)
+{
+    if (menu_head != defmenu) {
+	struct dired_menu *mp, *mp_next = NULL;
+
+	for (mp = menu_head; mp != NULL; mp = mp_next) {
+	    FREE(mp->sfx);
+	    FREE(mp->link);
+	    FREE(mp->rest);
+	    FREE(mp->href);
+	    mp_next = mp_next;
+	    FREE(mp);
+	}
+	menu_head = NULL;
+    }
+}
+
+/*
+ * Create URL for DIRED HREF value.
+ */
+static char *render_item(const char *s,
+			 const char *path,
+			 const char *dir,
+			 char *buf,
+			 int bufsize,
+			 BOOLEAN url_syntax)
+{
+    const char *cp;
+    char *bp;
+    char overrun = '\0';
+    char *taglist = NULL;
+
+#define BP_INC (bp>buf+bufsize-2 ?  &overrun : bp++)
+    /* Buffer overrun could happen for very long
+       tag list, if %l or %t are used */
+    bp = buf;
+    while (*s && !overrun) {
+	if (*s == '%') {
+	    s++;
+	    switch (*s) {
+	    case '%':
+		*BP_INC = '%';
+		break;
+	    case 'p':
+		cp = path;
+		if (!LYIsHtmlSep(*cp))
+		    *BP_INC = '/';
+		while (*cp)
+		    *BP_INC = *cp++;
+		break;
+	    case 'd':
+		cp = dir;
+		if (!LYIsHtmlSep(*cp))
+		    *BP_INC = '/';
+		while (*cp)
+		    *BP_INC = *cp++;
+		break;
+	    case 'f':
+		cp = LYLastPathSep(path);
+		if (cp)
+		    cp++;
+		else
+		    cp = path;
+		while (*cp)
+		    *BP_INC = *cp++;
+		break;
+	    case 'l':
+	    case 't':
+		if (!HTList_isEmpty(tagged)) {
+		    HTList *cur = tagged;
+		    char *name;
+
+		    while (!overrun &&
+			   (name = (char *) HTList_nextObject(cur)) != NULL) {
+			if (*s == 'l' && (cp = strrchr(name, '/')))
+			    cp++;
+			else
+			    cp = name;
+			StrAllocCat(taglist, cp);
+			StrAllocCat(taglist, " ");	/* should this be %20? */
+		    }
+		}
+		if (taglist) {
+		    /* could HTUnescape here... */
+		    cp = taglist;
+		    while (*cp)
+			*BP_INC = *cp++;
+		    FREE(taglist);
+		}
+		break;
+	    default:
+		*BP_INC = '%';
+		*BP_INC = *s;
+		break;
+	    }
+	} else {
+	    /*
+	     * Other chars come from the lynx.cfg or the default.  Let's assume
+	     * there isn't anything weird there that needs escaping.
+	     */
+	    *BP_INC = *s;
+	}
+	s++;
+    }
+    if (overrun & url_syntax) {
+	HTAlert(gettext("Temporary URL or list would be too long."));
+	bp = buf;		/* set to start, will return empty string as URL */
+    }
+    *bp = '\0';
+    return buf;
+}
+
+#endif /* DIRED_SUPPORT */
diff --git a/src/LYLocal.h b/src/LYLocal.h
new file mode 100644
index 00000000..9a7fe240
--- /dev/null
+++ b/src/LYLocal.h
@@ -0,0 +1,36 @@
+#ifndef LYLOCAL_H
+#define LYLOCAL_H
+
+#include <HTUtils.h>
+#include <LYStructs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef DIRED_SUPPORT
+/* Special return code for LYMainLoop.c */
+#define PERMIT_FORM_RESULT (-99)
+    extern int local_create(DocInfo *doc);
+    extern int local_modify(DocInfo *doc, char **newpath);
+    extern int local_remove(DocInfo *doc);
+
+#ifdef OK_INSTALL
+    extern BOOLEAN local_install(char *destpath, char *srcpath, char **newpath);
+#endif
+
+/* MainLoop needs to know about this one for atexit cleanup */
+    extern void clear_tags(void);
+
+    extern int dired_options(DocInfo *doc, char **newfile);
+    extern int local_dired(DocInfo *doc);
+    extern void add_menu_item(char *str);
+    extern void reset_dired_menu(void);
+    extern void showtags(HTList *tag);
+    extern void tagflag(int flag, int cur);
+
+#endif				/* DIRED_SUPPORT */
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYLOCAL_H */
diff --git a/src/LYMail.c b/src/LYMail.c
new file mode 100644
index 00000000..9cfc25ed
--- /dev/null
+++ b/src/LYMail.c
@@ -0,0 +1,1735 @@
+/*
+ * $LynxId: LYMail.c,v 1.73 2010/04/29 09:16:49 tom Exp $
+ */
+#include <HTUtils.h>
+#include <HTParse.h>
+#include <LYGlobalDefs.h>
+#include <HTAlert.h>
+#include <LYCurses.h>
+#include <LYSignal.h>
+#include <LYUtils.h>
+#include <LYClean.h>
+#include <LYStrings.h>
+#include <GridText.h>
+#include <LYMail.h>
+#include <LYEdit.h>
+#include <LYCharSets.h>		/* to get current charset for mail header */
+
+#include <LYLeaks.h>
+
+#define MAX_SUBJECT 70
+
+BOOLEAN term_letter;		/* Global variable for async i/o. */
+
+static void terminate_letter(int sig GCC_UNUSED)
+{
+    term_letter = TRUE;
+    /* Reassert the AST */
+    signal(SIGINT, terminate_letter);
+#if USE_VMS_MAILER || defined(PDCURSES)
+    /*
+     * Refresh the screen to get rid of the "interrupt" message.
+     */
+    if (!dump_output_immediately) {
+	lynx_force_repaint();
+	LYrefresh();
+    }
+#endif /* VMS */
+}
+
+/* HTUnEscape with control-code nuking */
+static void SafeHTUnEscape(char *string)
+{
+    int i;
+    int flg = FALSE;
+
+    HTUnEscape(string);
+    for (i = 0; string[i] != '\0'; i++) {
+	/* FIXME: this is no longer explicitly 7-bit ASCII,
+	   but are there portability problems? */
+	if ((!LYIsASCII(string[i])) || !isprint(UCH(string[i]))) {
+	    string[i] = '?';
+	    flg = TRUE;
+	}
+    }
+    if (flg)
+	HTAlert(MAILTO_SQUASH_CTL);
+}
+
+static void remove_tildes(char *string)
+{
+    /*
+     * Change the first character to a space if it is a '~'.
+     */
+    if (*string == '~')
+	*string = ' ';
+}
+
+static void comma_append(char **dst,
+			 char *src)
+{
+    if (*src) {
+	while (*src == ',' || isspace(UCH(*src)))
+	    src++;
+	if (*src) {
+	    if (isEmpty(*dst)) {
+		StrAllocCopy(*dst, src);
+	    } else {
+		StrAllocCat(*dst, ",");
+		StrAllocCat(*dst, src);
+	    }
+	}
+    }
+}
+
+static void extract_field(char **dst,
+			  char *src,
+			  const char *keyword)
+{
+    int len = (int) strlen(keyword);
+    char *cp, *cp1;
+
+    cp = (src + 1);
+    while (*cp != '\0') {
+	if ((*(cp - 1) == '?' || *(cp - 1) == '&') &&
+	    !strncasecomp(cp, keyword, len)) {
+	    cp += len;
+	    if ((cp1 = strchr(cp, '&')) != NULL) {
+		*cp1 = '\0';
+	    }
+	    comma_append(dst, cp);
+	    if (cp1) {
+		*cp1 = '&';
+		cp = cp1;
+		cp1 = NULL;
+	    } else {
+		break;
+	    }
+	}
+	cp++;
+    }
+    CTRACE((tfp, "extract_field(%s) = '%s'\n", keyword, NONNULL(*dst)));
+}
+
+/*
+ * Seek and handle a subject=foo.  - FM
+ */
+static void extract_subject(char *dst,
+			    char *src)
+{
+    const char *keyword = "subject=";
+    int len = (int) strlen(keyword);
+    char *cp, *cp1;
+
+    cp = (src + 1);
+    while (*cp != '\0') {
+	if ((*(cp - 1) == '?' || *(cp - 1) == '&') &&
+	    !strncasecomp(cp, keyword, len))
+	    break;
+	cp++;
+    }
+    if (*cp) {
+	cp += len;
+	if ((cp1 = strchr(cp, '&')) != NULL) {
+	    *cp1 = '\0';
+	}
+	if (*cp) {
+	    strncpy(dst, cp, MAX_SUBJECT);
+	    dst[MAX_SUBJECT] = '\0';
+	    SafeHTUnEscape(dst);
+	}
+	if (cp1) {
+	    *cp1 = '&';
+	    cp1 = NULL;
+	}
+    }
+    CTRACE((tfp, "extract_subject(%s) = '%s'\n", keyword, NONNULL(dst)));
+}
+
+/*
+ * Seek and handle body=foo fields.  - FM
+ */
+static void extract_body(char **dst,
+			 char *src)
+{
+    const char *keyword = "body=";
+    int len = (int) strlen(keyword);
+    int i;
+    char *cp, *cp0, *cp1, *temp = 0;
+
+    cp = (src + 1);
+    while (*cp != '\0') {
+	if ((*(cp - 1) == '?' || *(cp - 1) == '&') &&
+	    !strncasecomp(cp, keyword, len)) {
+	    cp += len;
+	    if ((cp1 = strchr(cp, '&')) != NULL) {
+		*cp1 = '\0';
+	    }
+	    if (*cp) {
+		/*
+		 * Break up the value into lines with a maximum length of 78. 
+		 * - FM
+		 */
+		StrAllocCopy(temp, cp);
+		HTUnEscape(temp);
+		cp0 = temp;
+		while ((cp = strchr(cp0, '\n')) != NULL) {
+		    *cp = '\0';
+		    if (cp > cp0) {
+			if (*(cp - 1) == '\r') {
+			    *(cp - 1) = '\0';
+			}
+		    }
+		    i = 0;
+		    len = (int) strlen(cp0);
+		    while (len > 78) {
+			HTSprintf(dst, "%.78s\n", &cp0[i]);
+			i += 78;
+			len = (int) strlen(&cp0[i]);
+		    }
+		    HTSprintf(dst, "%s\n", &cp0[i]);
+		    cp0 = (cp + 1);
+		}
+		i = 0;
+		len = (int) strlen(cp0);
+		while (len > 78) {
+		    HTSprintf(dst, "%.78s\n", &cp0[i]);
+		    i += 78;
+		    len = (int) strlen(&cp0[i]);
+		}
+		if (len) {
+		    HTSprintf(dst, "%s\n", &cp0[i]);
+		}
+		FREE(temp);
+	    }
+	    if (cp1) {
+		*cp1 = '&';
+		cp = cp1;
+		cp1 = NULL;
+	    } else {
+		break;
+	    }
+	}
+	cp++;
+    }
+    CTRACE((tfp, "extract_body(%s) = '%s'\n", keyword, NONNULL(*dst)));
+}
+
+/*
+ * Convert any Explorer semi-colon Internet address separators to commas - FM
+ */
+static BOOLEAN trim_comma(char *address)
+{
+    if (address[(strlen(address) - 1)] == ',')
+	address[(strlen(address) - 1)] = '\0';
+    return (BOOL) (*address == '\0');
+}
+
+/*
+ * Convert any Explorer semi-colon Internet address separators to commas - FM
+ */
+static BOOLEAN convert_explorer(char *address)
+{
+    char *cp = address;
+    char *cp0;
+    char *cp1;
+
+    while ((cp1 = strchr(cp, '@')) != NULL) {
+	cp1++;
+	if ((cp0 = strchr(cp1, ';')) != NULL) {
+	    *cp0 = ',';
+	    cp1 = cp0 + 1;
+	}
+	cp = cp1;
+    }
+    return trim_comma(address);
+}
+
+/*
+ * reply_by_mail() prompts line-by-line for header information, allowing
+ * scrolling of the screen.
+ */
+static int header_prompt(const char *label,
+			 char **result,
+			 unsigned limit)
+{
+    char buffer[LINESIZE];
+    int ok;
+
+    if (*result != 0) {
+	LYaddstr(CTRL_U_TO_ERASE);
+	LYstrncpy(buffer, *result, sizeof(buffer) - 1);
+    } else
+	*buffer = 0;
+
+    if (limit > sizeof(buffer))
+	limit = sizeof(buffer);
+
+    LYaddstr(gettext(label));
+    LYaddstr(": ");
+    ok = (LYgetstr(buffer, VISIBLE, limit, NORECALL) >= 0
+	  && !term_letter);
+    LYaddstr("\n");
+
+    if (ok) {
+	remove_tildes(buffer);
+	StrAllocCopy(*result, buffer);
+    }
+    term_letter = FALSE;
+    return ok;
+}
+
+static void show_addresses(char *addresses)
+{
+    char *cp = addresses;
+    char *cp1;
+
+    while ((cp1 = strchr(cp, ',')) != NULL) {
+	*cp1 = '\0';
+	while (*cp == ' ')
+	    cp++;
+	if (*cp) {
+	    LYaddstr(cp);
+	    LYaddstr(",\n  ");
+	}
+	*cp1 = ',';
+	cp = (cp1 + 1);
+    }
+    if (*cp) {
+	LYaddstr(cp);
+    }
+}
+
+#if USE_BLAT_MAILER
+
+/*
+syntax:
+Blat <filename> -t <recipient> [optional switches (see below)]
+
+<filename>    : file with the message body
+-t <recipient>: recipient list (comma separated)
+-s <subj>     : subject line
+-f <sender>   : overrides the default sender address (must be known to server)
+-i <addr>     : a 'From:' address, not necessarily known to the SMTP server.
+-c <recipient>: carbon copy recipient list (comma separated)
+-b <recipient>: blind carbon copy recipient list (comma separated)
+-h            : displays this help.
+-mime         : MIME Quoted-Printable Content-Transfer-Encoding.
+-q            : supresses *all* output.
+-server <addr>: overrides the default SMTP server to be used.
+
+*/
+
+static char *blat_cmd(char *mail_cmd,
+		      char *filename,
+		      char *address,
+		      char *subject,
+		      char *ccaddr,
+		      char *mail_addr)
+{
+    static char *b_cmd;
+
+#ifdef USE_ALT_BLAT_MAILER
+
+    HTSprintf0(&b_cmd, "%s %s -t \"%s\" -s \"%s\" %s%s%s%s",
+	       mail_cmd,
+	       filename,
+	       address,
+	       subject,
+	       system_mail_flags,
+	       ccaddr ? " -c \"" : "",
+	       NonNull(ccaddr),
+	       ccaddr ? "\"" : "");
+
+#else /* !USE_ALT_BLAT_MAILER */
+
+    static char bl_cmd_file[512];
+    FILE *fp;
+
+#ifdef __CYGWIN__
+    char dosname[LY_MAXPATH];
+#endif
+
+    bl_cmd_file[0] = '\0';
+    if ((fp = LYOpenTemp(bl_cmd_file, ".blt", "w")) == NULL) {
+	HTAlert(FORM_MAILTO_FAILED);
+	return NULL;
+    }
+#ifdef __CYGWIN__
+    cygwin_conv_to_full_win32_path(filename, dosname);
+    fprintf(fp, "%s\n", dosname);
+#else
+    fprintf(fp, "%s\n", filename);
+#endif
+    fprintf(fp, "-t\n%s\n", address);
+    if (subject)
+	fprintf(fp, "-s\n%s\n", subject);
+    if (non_empty(mail_addr)) {
+	fprintf(fp, "-f\n%s\n", mail_addr);
+    }
+    if (non_empty(ccaddr)) {
+	fprintf(fp, "-c\n%s\n", ccaddr);
+    }
+    LYCloseOutput(fp);
+
+#ifdef __CYGWIN__
+    cygwin_conv_to_full_win32_path(bl_cmd_file, dosname);
+    HTSprintf0(&b_cmd, "%s \"@%s\"", mail_cmd, dosname);
+#else
+    HTSprintf0(&b_cmd, "%s @%s", mail_cmd, bl_cmd_file);
+#endif
+
+#endif /* USE_ALT_BLAT_MAILER */
+
+    return b_cmd;
+}
+
+#endif /* USE_BLAT_MAILER */
+
+#if USE_VMS_MAILER
+BOOLEAN LYMailPMDF(void)
+{
+    return (system_mail != 0)
+	? !strncasecomp(system_mail, "PMDF SEND", 9)
+	: FALSE;
+}
+
+/*
+ * Add all of the people in the address field to the command
+ */
+static void vms_append_addrs(char **cmd, char *address, char *option)
+{
+    BOOLEAN first = TRUE;
+    char *cp;
+    char *address_ptr1;
+    char *address_ptr2;
+
+    address_ptr1 = address;
+    do {
+	if ((cp = strchr(address_ptr1, ',')) != NULL) {
+	    address_ptr2 = (cp + 1);
+	    *cp = '\0';
+	} else {
+	    address_ptr2 = NULL;
+	}
+
+	/*
+	 * 4 letters is arbitrarily the smallest possible mail address, at
+	 * least for lynx.  That way extra spaces won't confuse the mailer and
+	 * give a blank address.
+	 */
+	if (strlen(address_ptr1) > 3) {
+	    if (!first) {
+		StrAllocCat(*cmd, ",");
+	    }
+	    HTSprintf(cmd, mail_adrs, address_ptr1);
+	    if (*option && LYMailPMDF())
+		StrAllocCat(*cmd, option);
+	    first = FALSE;
+	}
+	address_ptr1 = address_ptr2;
+    } while (address_ptr1 != NULL);
+}
+
+static void remove_quotes(char *string)
+{
+    while (*string != 0) {
+	if (strchr("\"&|", *string) != 0)
+	    *string = ' ';
+	string++;
+    }
+}
+#else
+#if CAN_PIPE_TO_MAILER
+
+/*
+ * Open a pipe to the mailer
+ */
+FILE *LYPipeToMailer(void)
+{
+    char *buffer = NULL;
+    FILE *fp = NULL;
+
+    if (LYSystemMail()) {
+	HTSprintf0(&buffer, "%s %s", system_mail, system_mail_flags);
+	fp = popen(buffer, "w");
+	CTRACE((tfp, "popen(%s) %s\n", buffer, fp != 0 ? "OK" : "FAIL"));
+	FREE(buffer);
+    }
+    return fp;
+}
+#else /* DOS, Win32, etc. */
+
+int LYSendMailFile(char *the_address,
+		   char *the_filename,
+		   char *the_subject GCC_UNUSED,
+		   char *the_ccaddr GCC_UNUSED,
+		   char *message)
+{
+    char *cmd = NULL;
+
+#ifdef __DJGPP__
+    char *shell;
+#endif /* __DJGPP__ */
+    int code;
+
+    if (!LYSystemMail())
+	return 0;
+
+#if USE_BLAT_MAILER
+    if (mail_is_blat)
+	StrAllocCopy(cmd,
+		     blat_cmd(system_mail,
+			      the_filename,
+			      the_address,
+			      the_subject,
+			      the_ccaddr,
+			      personal_mail_address
+		     )
+	    );
+    else
+#endif
+#ifdef __DJGPP__
+    if ((shell = LYGetEnv("SHELL")) != NULL) {
+	if (strstr(shell, "sh") != NULL) {
+	    HTSprintf0(&cmd, "%s -c %s -t \"%s\" -F %s",
+		       shell,
+		       system_mail,
+		       the_address,
+		       the_filename);
+	} else {
+	    HTSprintf0(&cmd, "%s /c %s -t \"%s\" -F %s",
+		       shell,
+		       system_mail,
+		       the_address,
+		       the_filename);
+	}
+    } else {
+	HTSprintf0(&cmd, "%s -t \"%s\" -F %s",
+		   system_mail,
+		   the_address,
+		   the_filename);
+    }
+#else
+	HTSprintf0(&cmd, "%s -t \"%s\" -F %s",
+		   system_mail,
+		   the_address,
+		   the_filename);
+#endif /* __DJGPP__ */
+
+    stop_curses();
+    SetOutputMode(O_TEXT);
+    printf("%s\n\n$ %s\n\n%s",
+	   *message ? message : gettext("Sending"),
+	   cmd, PLEASE_WAIT);
+    code = LYSystem(cmd);
+    LYSleepMsg();
+    start_curses();
+    SetOutputMode(O_BINARY);
+
+    FREE(cmd);
+
+    return code;
+}
+#endif /* CAN_PIPE_TO_FILE */
+#endif /* USE_VMS_MAILER */
+
+/*
+ *  mailform() sends form content to the mailto address(es). - FM
+ */
+void mailform(const char *mailto_address,
+	      const char *mailto_subject,
+	      const char *mailto_content,
+	      const char *mailto_type)
+{
+    FILE *fd;
+    char *address = NULL;
+    char *ccaddr = NULL;
+    char *keywords = NULL;
+    char *cp = NULL;
+    char self[MAX_SUBJECT + 10];
+    char subject[MAX_SUBJECT + 10];
+    char *searchpart = NULL;
+    char buf[512];
+    int len, i;
+
+#if USE_VMS_MAILER
+    static char *cmd;
+    char *command = NULL;
+    BOOLEAN isPMDF = LYMailPMDF();
+    char hdrfile[LY_MAXPATH];
+#endif
+#if !CAN_PIPE_TO_MAILER
+    char my_tmpfile[LY_MAXPATH];
+#endif
+
+    CTRACE((tfp, "mailto_address: \"%s\"\n", NONNULL(mailto_address)));
+    CTRACE((tfp, "mailto_subject: \"%s\"\n", NONNULL(mailto_subject)));
+    CTRACE((tfp, "mailto_content: \"%s\"\n", NONNULL(mailto_content)));
+    CTRACE((tfp, "mailto_type:    \"%s\"\n", NONNULL(mailto_type)));
+
+    if (!LYSystemMail())
+	return;
+
+    if (!mailto_address || !mailto_content) {
+	HTAlert(BAD_FORM_MAILTO);
+	return;
+    }
+    subject[0] = '\0';
+    self[0] = '\0';
+
+    if ((cp = (char *) strchr(mailto_address, '\n')) != NULL)
+	*cp = '\0';
+    StrAllocCopy(address, mailto_address);
+
+    /*
+     * Check for a ?searchpart.  - FM
+     */
+    if ((cp = strchr(address, '?')) != NULL) {
+	StrAllocCopy(searchpart, cp);
+	*cp = '\0';
+	cp = (searchpart + 1);
+	if (*cp != '\0') {
+	    /*
+	     * Seek and handle a subject=foo.  - FM
+	     */
+	    extract_subject(subject, searchpart);
+
+	    /*
+	     * Seek and handle to=address(es) fields.  Appends to address.  -
+	     * FM
+	     */
+	    extract_field(&address, searchpart, "to=");
+
+	    /*
+	     * Seek and handle cc=address(es) fields.  Excludes Bcc=address(es)
+	     * as unsafe.  We may append our own cc (below) as a list for the
+	     * actual mailing.  - FM
+	     */
+	    extract_field(&ccaddr, searchpart, "cc=");
+
+	    /*
+	     * Seek and handle keywords=term(s) fields.  - FM
+	     */
+	    extract_field(&keywords, searchpart, "keywords=");
+
+	    if (keywords != NULL) {
+		if (*keywords != '\0') {
+		    SafeHTUnEscape(keywords);
+		} else {
+		    FREE(keywords);
+		}
+	    }
+
+	    FREE(searchpart);
+	}
+    }
+
+    if (convert_explorer(address)) {
+	HTAlert(BAD_FORM_MAILTO);
+	goto cleanup;
+    }
+    if (ccaddr != NULL) {
+	if (convert_explorer(ccaddr)) {
+	    FREE(ccaddr);
+	}
+    }
+
+    /*
+     * Unescape the address and ccaddr fields.  - FM
+     */
+    SafeHTUnEscape(address);
+    if (ccaddr != NULL) {
+	SafeHTUnEscape(ccaddr);
+    }
+
+    /*
+     * Allow user to edit the default Subject - FM
+     */
+    if (subject[0] == '\0') {
+	if (non_empty(mailto_subject)) {
+	    LYstrncpy(subject, mailto_subject, MAX_SUBJECT);
+	} else {
+	    sprintf(subject, "mailto:%.63s", address);
+	}
+    }
+    _statusline(SUBJECT_PROMPT);
+    if (LYgetstr(subject, VISIBLE, MAX_SUBJECT, NORECALL) < 0) {
+	/*
+	 * User cancelled via ^G. - FM
+	 */
+	HTInfoMsg(FORM_MAILTO_CANCELLED);
+	goto cleanup;
+    }
+
+    /*
+     * Allow user to specify a self copy via a CC:  entry, if permitted.  - FM
+     */
+    if (!LYNoCc) {
+	sprintf(self, "%.*s", MAX_SUBJECT,
+		isEmpty(personal_mail_address) ? "" : personal_mail_address);
+	_statusline("Cc: ");
+	if (LYgetstr(self, VISIBLE, MAX_SUBJECT, NORECALL) < 0) {
+	    /*
+	     * User cancelled via ^G. - FM
+	     */
+	    HTInfoMsg(FORM_MAILTO_CANCELLED);
+	    goto cleanup;
+	}
+	remove_tildes(self);
+	if (ccaddr == NULL) {
+	    StrAllocCopy(ccaddr, self);
+	} else {
+	    StrAllocCat(ccaddr, ",");
+	    StrAllocCat(ccaddr, self);
+	}
+    }
+#if CAN_PIPE_TO_MAILER
+    if ((fd = LYPipeToMailer()) == 0) {
+	HTAlert(FORM_MAILTO_FAILED);
+	goto cleanup;
+    }
+
+    if (non_empty(mailto_type)) {
+	fprintf(fd, "Mime-Version: 1.0\n");
+	fprintf(fd, "Content-Type: %s\n", mailto_type);
+    }
+    fprintf(fd, "To: %s\n", address);
+    if (non_empty(personal_mail_address))
+	fprintf(fd, "From: %s\n", personal_mail_address);
+    if (non_empty(ccaddr))
+	fprintf(fd, "Cc: %s\n", ccaddr);
+    fprintf(fd, "Subject: %s\n\n", subject);
+    if (non_empty(keywords))
+	fprintf(fd, "Keywords: %s\n", keywords);
+    _statusline(SENDING_FORM_CONTENT);
+#else /* e.g., VMS, DOS */
+    if ((fd = LYOpenTemp(my_tmpfile, ".txt", "w")) == NULL) {
+	HTAlert(FORM_MAILTO_FAILED);
+	goto cleanup;
+    }
+#if USE_VMS_MAILER
+    if (isPMDF) {
+	FILE *hfd;
+
+	if ((hfd = LYOpenTemp(hdrfile, ".txt", "w")) == NULL) {
+	    HTAlert(FORM_MAILTO_FAILED);
+	    LYCloseTempFP(fd);
+	    goto cleanup;
+	}
+	if (non_empty(mailto_type)) {
+	    fprintf(hfd, "Mime-Version: 1.0\n");
+	    fprintf(hfd, "Content-Type: %s\n", mailto_type);
+	    if (non_empty(personal_mail_address))
+		fprintf(hfd, "From: %s\n", personal_mail_address);
+	}
+	/*
+	 * For PMDF, put any keywords and the subject in the header file and
+	 * close it.  - FM
+	 */
+	if (non_empty(keywords)) {
+	    fprintf(hfd, "Keywords: %s\n", keywords);
+	}
+	fprintf(hfd, "Subject: %s\n\n", subject);
+	LYCloseTempFP(hfd);
+    } else if (mailto_type &&
+	       !strncasecomp(mailto_type, "multipart/form-data", 19)) {
+	/*
+	 * Ugh!  There's no good way to include headers while we're still using
+	 * "generic" VMS MAIL, so we'll put this in the body of the message.  -
+	 * FM
+	 */
+	fprintf(fd, "X-Content-Type: %s\n\n", mailto_type);
+    }
+#else /* !VMS (DOS) */
+#if USE_BLAT_MAILER
+    if (mail_is_blat) {
+	if (strlen(subject) > MAX_SUBJECT)
+	    subject[MAX_SUBJECT] = '\0';
+    } else
+#endif
+    {
+	if (non_empty(mailto_type)) {
+	    fprintf(fd, "Mime-Version: 1.0\n");
+	    fprintf(fd, "Content-Type: %s\n", mailto_type);
+	}
+	fprintf(fd, "To: %s\n", address);
+	if (non_empty(personal_mail_address))
+	    fprintf(fd, "From: %s\n", personal_mail_address);
+	fprintf(fd, "Subject: %.70s\n\n", subject);
+    }
+#endif /* VMS */
+#endif /* CAN_PIPE_TO_MAILER */
+
+    /*
+     * Break up the content into lines with a maximum length of 78.  If the
+     * ENCTYPE was text/plain, we have physical newlines and should take them
+     * into account.  Otherwise, the actual newline characters in the content
+     * are hex escaped.  - FM
+     */
+    while ((cp = strchr(mailto_content, '\n')) != NULL) {
+	*cp = '\0';
+	i = 0;
+	len = (int) strlen(mailto_content);
+	while (len > 78) {
+	    strncpy(buf, &mailto_content[i], 78);
+	    buf[78] = '\0';
+	    fprintf(fd, "%s\n", buf);
+	    i += 78;
+	    len = (int) strlen(&mailto_content[i]);
+	}
+	fprintf(fd, "%s\n", &mailto_content[i]);
+	mailto_content = (cp + 1);
+    }
+    i = 0;
+    len = (int) strlen(mailto_content);
+    while (len > 78) {
+	strncpy(buf, &mailto_content[i], 78);
+	buf[78] = '\0';
+	fprintf(fd, "%s\n", buf);
+	i += 78;
+	len = (int) strlen(&mailto_content[i]);
+    }
+    if (len)
+	fprintf(fd, "%s\n", &mailto_content[i]);
+
+#if CAN_PIPE_TO_MAILER
+    pclose(fd);
+    LYSleepMsg();
+#else
+    LYCloseTempFP(fd);
+#if USE_VMS_MAILER
+    /*
+     * Set the mail command.  - FM
+     */
+    if (isPMDF) {
+	/*
+	 * Now set up the command.  - FM
+	 */
+	HTSprintf0(&cmd,
+		   "%s %s %s,%s ",
+		   system_mail,
+		   system_mail_flags,
+		   hdrfile,
+		   my_tmpfile);
+    } else {
+	/*
+	 * For "generic" VMS MAIL, include the subject in the command, and
+	 * ignore any keywords to minimize risk of them making the line too
+	 * long or having problem characters.  - FM
+	 */
+	HTSprintf0(&cmd,
+		   "%s %s%s/subject=\"%s\" %s ",
+		   system_mail,
+		   system_mail_flags,
+		   (strncasecomp(system_mail, "MAIL", 4) ? "" : "/noself"),
+		   subject,
+		   my_tmpfile);
+    }
+    StrAllocCopy(command, cmd);
+
+    vms_append_addrs(&command, address, "");
+    if (non_empty(ccaddr)) {
+	vms_append_addrs(&command, ccaddr, "/CC");
+    }
+
+    stop_curses();
+    printf("%s\n\n$ %s\n\n%s", SENDING_FORM_CONTENT, command, PLEASE_WAIT);
+    LYSystem(command);		/* Mail (VMS) */
+    FREE(command);
+    LYSleepAlert();
+    start_curses();
+    LYRemoveTemp(my_tmpfile);
+    if (isPMDF)
+	LYRemoveTemp(hdrfile);
+#else /* DOS */
+    LYSendMailFile(address,
+		   my_tmpfile,
+		   subject,
+		   ccaddr,
+		   SENDING_FORM_CONTENT);
+    LYRemoveTemp(my_tmpfile);
+#endif /* USE_VMS_MAILER */
+#endif /* CAN_PIPE_TO_MAILER */
+
+  cleanup:
+    FREE(address);
+    FREE(ccaddr);
+    FREE(keywords);
+    return;
+}
+
+/*
+ *  mailmsg() sends a message to the owner of the file, if one is defined,
+ *  telling of errors (i.e., link not available).
+ */
+void mailmsg(int cur,
+	     char *owner_address,
+	     char *filename,
+	     char *linkname)
+{
+    FILE *fd, *fp;
+    char *address = NULL;
+    char *searchpart = NULL;
+    char *cmd = NULL, *cp;
+
+#ifdef ALERTMAIL
+    BOOLEAN skip_parsing = FALSE;
+#endif
+#if !CAN_PIPE_TO_MAILER
+    char *ccaddr;
+    char subject[128];
+    char my_tmpfile[LY_MAXPATH];
+#endif
+#if USE_VMS_MAILER
+    BOOLEAN isPMDF = LYMailPMDF();
+    char hdrfile[LY_MAXPATH];
+    char *command = NULL;
+
+    CTRACE((tfp, "mailmsg(%d, \"%s\", \"%s\", \"%s\")\n", cur,
+	    NONNULL(owner_address),
+	    NONNULL(filename),
+	    NONNULL(linkname)));
+
+#endif /* VMS */
+
+    if (!LYSystemMail())
+	return;
+
+#ifdef ALERTMAIL
+    if (owner_address == NULL) {
+	owner_address = ALERTMAIL;
+	skip_parsing = TRUE;
+    }
+#endif
+
+    if (isEmpty(owner_address))
+	return;
+    if ((cp = (char *) strchr(owner_address, '\n')) != NULL) {
+#ifdef ALERTMAIL
+	if (skip_parsing)
+	    return;		/* invalidly defined - ignore - kw */
+#else
+	*cp = '\0';
+#endif
+    }
+    if (!strncasecomp(owner_address, "lynx-dev@", 9)) {
+	/*
+	 * Silently refuse sending bad link messages to lynx-dev.
+	 */
+	return;
+    }
+    StrAllocCopy(address, owner_address);
+
+#ifdef ALERTMAIL
+    /*
+     * If we are using a fixed address given by ALERTMAIL, it is supposed to
+     * already be in usable form, without URL-isms like ?-searchpart and
+     * URL-escaping.  So skip some code.  - kw
+     */
+    if (!skip_parsing)
+#endif
+    {
+	/*
+	 * Check for a ?searchpart.  - FM
+	 */
+	if ((cp = strchr(address, '?')) != NULL) {
+	    StrAllocCopy(searchpart, cp);
+	    *cp = '\0';
+	    cp = (searchpart + 1);
+	    if (*cp != '\0') {
+		/*
+		 * Seek and handle to=address(es) fields.
+		 * Appends to address.  We ignore any other
+		 * headers in the ?searchpart.  - FM
+		 */
+		extract_field(&address, searchpart, "to=");
+	    }
+	}
+
+	convert_explorer(address);
+
+	/*
+	 * Unescape the address field.  - FM
+	 */
+	SafeHTUnEscape(address);
+    }
+
+    if (trim_comma(address)) {
+	FREE(address);
+	CTRACE((tfp, "mailmsg: No address in '%s'.\n", owner_address));
+	return;
+    }
+#if CAN_PIPE_TO_MAILER
+    if ((fd = LYPipeToMailer()) == 0) {
+	FREE(address);
+	CTRACE((tfp, "mailmsg: '%s' failed.\n", cmd));
+	return;
+    }
+
+    fprintf(fd, "To: %s\n", address);
+    fprintf(fd, "Subject: Lynx Error in %s\n", filename);
+    if (non_empty(personal_mail_address)) {
+	fprintf(fd, "Cc: %s\n", personal_mail_address);
+    }
+    fprintf(fd, "X-URL: %s\n", filename);
+    fprintf(fd, "X-Mailer: %s, Version %s\n\n", LYNX_NAME, LYNX_VERSION);
+#else
+    if ((fd = LYOpenTemp(my_tmpfile, ".txt", "w")) == NULL) {
+	CTRACE((tfp, "mailmsg: Could not fopen '%s'.\n", my_tmpfile));
+	FREE(address);
+	return;
+    }
+    sprintf(subject, "Lynx Error in %.56s", filename);
+    ccaddr = personal_mail_address;
+#if USE_VMS_MAILER
+    if (isPMDF) {
+	FILE *hfd;
+
+	if ((hfd = LYOpenTemp(hdrfile, ".txt", "w")) == NULL) {
+	    CTRACE((tfp, "mailmsg: Could not fopen '%s'.\n", hdrfile));
+	    FREE(address);
+	    return;
+	}
+
+	if (non_empty(personal_mail_address)) {
+	    fprintf(fd, "Cc: %s\n", personal_mail_address);
+	}
+	fprintf(fd, "X-URL: %s\n", filename);
+	fprintf(fd, "X-Mailer: %s, Version %s\n\n", LYNX_NAME, LYNX_VERSION);
+	/*
+	 * For PMDF, put the subject in the header file and close it.  - FM
+	 */
+	fprintf(hfd, "Subject: Lynx Error in %.56s\n\n", filename);
+	LYCloseTempFP(hfd);
+    }
+#endif /* USE_VMS_MAILER */
+#endif /* CAN_PIPE_TO_MAILER */
+
+    fprintf(fd, gettext("The link   %s :?: %s \n"),
+	    links[cur].lname, links[cur].target);
+    fprintf(fd, gettext("called \"%s\"\n"), LYGetHiliteStr(cur, 0));
+    fprintf(fd, gettext("in the file \"%s\" called \"%s\"\n"), filename, linkname);
+    fprintf(fd, "%s\n\n", gettext("was requested but was not available."));
+    fprintf(fd, "%s\n\n", gettext("Thought you might want to know."));
+
+    fprintf(fd, "%s\n", gettext("This message was automatically generated by"));
+    fprintf(fd, "%s %s", LYNX_NAME, LYNX_VERSION);
+    if ((LynxSigFile != NULL) &&
+	(fp = fopen(LynxSigFile, TXT_R)) != NULL) {
+	fputs("-- \n", fd);
+	while (LYSafeGets(&cmd, fp) != NULL)
+	    fputs(cmd, fd);
+	LYCloseInput(fp);
+    }
+#if CAN_PIPE_TO_MAILER
+    pclose(fd);
+#else
+    LYCloseTempFP(fd);
+#if USE_VMS_MAILER
+    if (isPMDF) {
+	/*
+	 * Now set up the command.  - FM
+	 */
+	HTSprintf0(&command,
+		   "%s %s %s,%s ",
+		   system_mail,
+		   system_mail_flags,
+		   hdrfile,
+		   my_tmpfile);
+    } else {
+	/*
+	 * For "generic" VMS MAIL, include the subject in the command.  - FM
+	 */
+	HTSprintf0(&command,
+		   "%s %s/self/subject=\"Lynx Error in %.56s\" %s ",
+		   system_mail,
+		   system_mail_flags,
+		   filename,
+		   my_tmpfile);
+    }
+    vms_append_addrs(&command, address, "");
+
+    LYSystem(command);		/* VMS */
+    FREE(command);
+    FREE(cmd);
+    LYRemoveTemp(my_tmpfile);
+    if (isPMDF) {
+	LYRemoveTemp(hdrfile);
+    }
+#else /* DOS */
+    LYSendMailFile(address,
+		   my_tmpfile,
+		   subject,
+		   ccaddr,
+		   "");
+    LYRemoveTemp(my_tmpfile);
+#endif /* USE_VMS_MAILER */
+#endif /* CAN_PIPE_TO_MAILER */
+
+    if (traversal) {
+	FILE *ofp;
+
+	if ((ofp = LYAppendToTxtFile(TRAVERSE_ERRORS)) == NULL) {
+	    if ((ofp = LYNewTxtFile(TRAVERSE_ERRORS)) == NULL) {
+		perror(NOOPEN_TRAV_ERR_FILE);
+		exit_immediately(EXIT_FAILURE);
+	    }
+	}
+
+	fprintf(ofp, "%s\t%s \tin %s\n",
+		links[cur].lname, links[cur].target, filename);
+	LYCloseOutput(ofp);
+    }
+
+    FREE(address);
+    return;
+}
+
+/*
+ *  reply_by_mail() invokes sendmail on Unix or mail on VMS to send
+ *  a comment from the users to the owner
+ */
+void reply_by_mail(char *mail_address,
+		   char *filename,
+		   const char *title,
+		   const char *refid)
+{
+#ifndef NO_ANONYMOUS_EMAIL
+    static char *personal_name = NULL;
+#endif
+    char user_input[LINESIZE];
+    FILE *fd, *fp;
+    const char *label = NULL;
+    char *from_address = NULL;
+    char *cc_address = NULL;
+    char *to_address = NULL;
+    char *the_subject = NULL;
+    char *ccaddr = NULL;
+    char *keywords = NULL;
+    char *searchpart = NULL;
+    char *body = NULL;
+    char *cp = NULL, *cp1 = NULL;
+    int i;
+    int c = 0;			/* user input */
+    char my_tmpfile[LY_MAXPATH];
+    char default_subject[MAX_SUBJECT + 10];
+
+#if USE_VMS_MAILER
+    char *command = NULL;
+    BOOLEAN isPMDF = LYMailPMDF();
+    char hdrfile[LY_MAXPATH];
+    FILE *hfd = 0;
+
+#else
+#if !CAN_PIPE_TO_MAILER
+    char tmpfile2[LY_MAXPATH];
+#endif
+    char buf[4096];		/* 512 */
+    char *header = NULL;
+    int n;
+#endif /* USE_VMS_MAILER */
+
+    CTRACE((tfp, "reply_by_mail(\"%s\", \"%s\", \"%s\", \"%s\")\n",
+	    NONNULL(mail_address),
+	    NONNULL(filename),
+	    NONNULL(title),
+	    NONNULL(refid)));
+
+    term_letter = FALSE;
+
+    if (!LYSystemMail())
+	return;
+
+    if (isEmpty(mail_address)) {
+	HTAlert(NO_ADDRESS_IN_MAILTO_URL);
+	return;
+    }
+    StrAllocCopy(to_address, mail_address);
+
+    if ((fd = LYOpenTemp(my_tmpfile, ".txt", "w")) == NULL) {
+	HTAlert(MAILTO_URL_TEMPOPEN_FAILED);
+	return;
+    }
+#if USE_VMS_MAILER
+    if (isPMDF) {
+	if ((hfd = LYOpenTemp(hdrfile, ".txt", "w")) == NULL) {
+	    HTAlert(MAILTO_URL_TEMPOPEN_FAILED);
+	    return;
+	}
+    }
+#endif /* VMS */
+    default_subject[0] = '\0';
+
+    /*
+     * Check for a ?searchpart.  - FM
+     */
+    if ((cp = strchr(to_address, '?')) != NULL) {
+	StrAllocCopy(searchpart, cp);
+	*cp = '\0';
+	cp = (searchpart + 1);
+	if (*cp != '\0') {
+	    /*
+	     * Seek and handle a subject=foo.  - FM
+	     */
+	    extract_subject(default_subject, searchpart);
+
+	    /*
+	     * Seek and handle to=address(es) fields.  Appends to address.  -
+	     * FM
+	     */
+	    extract_field(&to_address, searchpart, "to=");
+
+	    /*
+	     * Seek and handle cc=address(es) fields.  Excludes Bcc=address(es)
+	     * as unsafe.  We may append our own cc (below) as a list for the
+	     * actual mailing.  - FM
+	     */
+	    extract_field(&ccaddr, searchpart, "cc=");
+
+	    /*
+	     * Seek and handle keywords=term(s) fields.  - FM
+	     */
+	    extract_field(&keywords, searchpart, "keywords=");
+
+	    if (keywords != NULL) {
+		if (*keywords != '\0') {
+		    SafeHTUnEscape(keywords);
+		} else {
+		    FREE(keywords);
+		}
+	    }
+
+	    /*
+	     * Seek and handle body=foo fields.  - FM
+	     */
+	    extract_body(&body, searchpart);
+
+	    FREE(searchpart);
+	}
+    }
+
+    if (convert_explorer(to_address)) {
+	HTAlert(NO_ADDRESS_IN_MAILTO_URL);
+	goto cancelled;
+    }
+    if (ccaddr != NULL) {
+	if (convert_explorer(ccaddr)) {
+	    FREE(ccaddr);
+	}
+    }
+
+    /*
+     * Unescape the address and ccaddr fields.  - FM
+     */
+    SafeHTUnEscape(to_address);
+    if (ccaddr != NULL) {
+	SafeHTUnEscape(ccaddr);
+    }
+
+    /*
+     * Set the default subject.  - FM
+     */
+    if ((default_subject[0] == '\0') && non_empty(title)) {
+	strncpy(default_subject, title, MAX_SUBJECT);
+	default_subject[MAX_SUBJECT] = '\0';
+    }
+
+    /*
+     * Use ^G to cancel mailing of comment and don't let SIGINTs exit lynx.
+     */
+    signal(SIGINT, terminate_letter);
+
+#if USE_VMS_MAILER
+    if (isPMDF || !body) {
+	/*
+	 * Put the X-URL and X-Mailer lines in the hdrfile for PMDF or
+	 * my_tmpfile for VMS MAIL.  - FM
+	 */
+	fprintf((isPMDF ? hfd : fd),
+		"X-URL: %s%s\n",
+		isEmpty(filename) ? STR_MAILTO_URL : filename,
+		isEmpty(filename) ? to_address : "");
+	fprintf((isPMDF ? hfd : fd),
+		"X-Mailer: %s, Version %s\n", LYNX_NAME, LYNX_VERSION);
+#ifdef NO_ANONYMOUS_EMAIL
+	if (!isPMDF) {
+	    fprintf(fd, "\n");
+	}
+#endif /* NO_ANONYMOUS_EMAIL */
+    }
+#else /* Unix/DOS/Windows */
+    /*
+     * Put the To:  line in the header.
+     */
+#ifndef DOSPATH
+    HTSprintf(&header, "To: %s\n", to_address);
+#endif
+
+    /*
+     * Put the Mime-Version, Content-Type and Content-Transfer-Encoding in the
+     * header.  This assumes that the same character set is used for composing
+     * the mail which is currently selected as display character set...  Don't
+     * send a charset if we have a CJK character set selected, since it may not
+     * be appropriate for mail...  Also don't use an unofficial "x-" charset. 
+     * Also if the charset would be "us-ascii" (7-bit replacements selected,
+     * don't send any MIME headers.  - kw
+     */
+    if (strncasecomp(LYCharSet_UC[current_char_set].MIMEname,
+		     "us-ascii", 8) != 0) {
+	StrAllocCat(header, "Mime-Version: 1.0\n");
+	if (!LYHaveCJKCharacterSet &&
+	    strncasecomp(LYCharSet_UC[current_char_set].MIMEname, "x-", 2)
+	    != 0) {
+	    HTSprintf(&header, "Content-Type: text/plain; charset=%s\n",
+		      LYCharSet_UC[current_char_set].MIMEname);
+	}
+	StrAllocCat(header, "Content-Transfer-Encoding: 8bit\n");
+    }
+    /*
+     * Put the X-URL and X-Mailer lines in the header.
+     */
+    if (non_empty(filename)) {
+	HTSprintf(&header, "X-URL: %s\n", filename);
+    } else {
+	HTSprintf(&header, "X-URL: mailto:%s\n", to_address);
+    }
+    HTSprintf(&header, "X-Mailer: %s, Version %s\n", LYNX_NAME, LYNX_VERSION);
+
+    if (non_empty(refid)) {
+	HTSprintf(&header, "In-Reply-To: <%s>\n", refid);
+    }
+#endif /* VMS */
+
+    /*
+     * Clear the screen and inform the user.
+     */
+    LYclear();
+    LYmove(2, 0);
+    scrollok(LYwin, TRUE);	/* Enable scrolling. */
+    if (body)
+	LYaddstr(SENDING_MESSAGE_WITH_BODY_TO);
+    else
+	LYaddstr(SENDING_COMMENT_TO);
+    show_addresses(to_address);
+    if (
+#if USE_VMS_MAILER
+	   (isPMDF == TRUE) &&
+#endif /* VMS */
+	   (cp = ccaddr) != NULL) {
+	if (strchr(cp, ',') != NULL) {
+	    LYaddstr(WITH_COPIES_TO);
+	} else {
+	    LYaddstr(WITH_COPY_TO);
+	}
+	show_addresses(ccaddr);
+    }
+    LYaddstr(CTRL_G_TO_CANCEL_SEND);
+
+#if USE_VMS_MAILER
+    if (isPMDF || !body) {
+#endif /* USE_VMS_MAILER */
+#ifndef NO_ANONYMOUS_EMAIL
+	/*
+	 * Get the user's personal name.
+	 */
+	LYaddstr(ENTER_NAME_OR_BLANK);
+#if USE_VMS_MAILER
+	if (isPMDF) {
+	    label = "Personal_name: ";
+	} else {
+	    label = "X-Personal_name: ";
+	}
+#else
+	label = "X-Personal_Name: ";
+#endif /* USE_VMS_MAILER */
+	if (!header_prompt(label, &personal_name, LINESIZE)) {
+	    goto cancelled;
+	}
+	if (*personal_name) {
+#if USE_VMS_MAILER
+	    fprintf((isPMDF ? hfd : fd), "%s: %s\n", label, personal_name);
+#else
+	    HTSprintf(&header, "%s: %s\n", label, personal_name);
+#endif /* VMS */
+	}
+
+	/*
+	 * Get the user's return address.
+	 */
+	LYaddstr(ENTER_MAIL_ADDRESS_OR_OTHER);
+	LYaddstr(MEANS_TO_CONTACT_FOR_RESPONSE);
+#if USE_VMS_MAILER
+	if (isPMDF) {
+	    label = "From";
+	} else {
+	    label = "X-From";
+	}
+#else
+	label = "From";
+#endif /* VMS */
+	/* Add the personal mail address if there is one. */
+	if (personal_mail_address)
+	    StrAllocCopy(from_address, personal_mail_address);
+	if (!header_prompt(label, &from_address, LINESIZE)) {
+	    goto cancelled;
+	}
+#if USE_VMS_MAILER
+	if (*from_address) {
+	    fprintf(isPMDF ? hfd : fd, "%s: %s\n", label, from_address);
+	}
+	if (!isPMDF) {
+	    fprintf(fd, "\n");
+	}
+#else
+	HTSprintf(&header, "%s: %s\n", label, from_address);
+#endif /* USE_VMS_MAILER */
+#endif /* !NO_ANONYMOUS_EMAIL */
+#if USE_VMS_MAILER
+    }
+#endif /* USE_VMS_MAILER */
+
+    /*
+     * Get the subject line.
+     */
+    LYaddstr(ENTER_SUBJECT_LINE);
+    label = "Subject";
+    if (*default_subject) {
+	StrAllocCopy(the_subject, default_subject);
+    } else if (non_empty(filename)) {
+	HTSprintf(&the_subject, "%s", filename);
+    } else {
+	HTSprintf(&the_subject, "mailto:%s", to_address);
+    }
+    if (!header_prompt(label, &the_subject, MAX_SUBJECT)) {
+	goto cancelled;
+    }
+
+    /*
+     * Offer a CC line, if permitted.  - FM
+     */
+    if (!LYNoCc) {
+	LYaddstr(ENTER_ADDRESS_FOR_CC);
+	LYaddstr(BLANK_FOR_NO_COPY);
+	if (personal_mail_address)
+	    StrAllocCopy(cc_address, personal_mail_address);
+	if (!header_prompt("Cc", &cc_address, LINESIZE)) {
+	    goto cancelled;
+	}
+	comma_append(&ccaddr, cc_address);
+    }
+#if !USE_VMS_MAILER
+    HTSprintf(&header, "%s: %s\n", label, the_subject);
+#if !CAN_PIPE_TO_MAILER
+    if (*to_address) {
+	HTSprintf(&header, "To: %s\n", to_address);
+    }
+#endif
+
+    /*
+     * Add the Cc:  header.  - FM
+     */
+    if (non_empty(ccaddr)) {
+	HTSprintf(&header, "Cc: %s\n", ccaddr);
+    }
+
+    /*
+     * Add the Keywords:  header.  - FM
+     */
+    if (non_empty(keywords)) {
+	HTSprintf(&header, "Keywords: %s\n", keywords);
+    }
+
+    /*
+     * Terminate the header.
+     */
+    StrAllocCat(header, "\n");
+    CTRACE((tfp, "**header==\n%s", header));
+#endif /* !VMS */
+
+    if (!no_editor && non_empty(editor)) {
+
+	if (body) {
+	    cp1 = body;
+	    while ((cp = strchr(cp1, '\n')) != NULL) {
+		*cp++ = '\0';
+		fprintf(fd, "%s\n", cp1);
+		cp1 = cp;
+	    }
+	} else if (strcmp(HTLoadedDocumentURL(), "")) {
+	    /*
+	     * Ask if the user wants to include the original message.
+	     */
+	    BOOLEAN is_preparsed = (BOOL) (LYPreparsedSource &&
+					   HTisDocumentSource());
+
+	    if (HTConfirm(is_preparsed
+			  ? INC_PREPARSED_MSG_PROMPT
+			  : INC_ORIG_MSG_PROMPT) == YES) {
+		print_wwwfile_to_fd(fd, TRUE, (BOOL) !is_preparsed);
+	    }
+	}
+	LYCloseTempFP(fd);	/* Close the tmpfile. */
+	scrollok(LYwin, FALSE);	/* Stop scrolling.    */
+
+	if (term_letter || LYCharIsINTERRUPT(c))
+	    goto cleanup;
+
+	/*
+	 * Spawn the users editor on the mail file
+	 */
+	edit_temporary_file(my_tmpfile, "", SPAWNING_EDITOR_FOR_MAIL);
+
+    } else if (body) {
+	/*
+	 * Let user review the body.  - FM
+	 */
+	LYclear();
+	LYmove(0, 0);
+	LYaddstr(REVIEW_MESSAGE_BODY);
+	LYrefresh();
+	cp1 = body;
+	i = (LYlines - 5);
+	while ((cp = strchr(cp1, '\n')) != NULL) {
+	    if (i <= 0) {
+		LYaddstr(RETURN_TO_CONTINUE);
+		LYrefresh();
+		c = LYgetch();
+		LYaddstr("\n");
+		if (term_letter || LYCharIsINTERRUPT(c)) {
+		    goto cancelled;
+		}
+		i = (LYlines - 2);
+	    }
+	    *cp++ = '\0';
+	    fprintf(fd, "%s\n", cp1);
+	    LYaddstr(cp1);
+	    LYaddstr("\n");
+	    cp1 = cp;
+	    i--;
+	}
+	while (i >= 0) {
+	    LYaddstr("\n");
+	    i--;
+	}
+	LYrefresh();
+	LYCloseTempFP(fd);	/* Close the tmpfile.     */
+	scrollok(LYwin, FALSE);	/* Stop scrolling.        */
+
+    } else {
+	/*
+	 * Use the internal line editor for the message.
+	 */
+	LYaddstr(ENTER_MESSAGE_BELOW);
+	LYaddstr(ENTER_PERIOD_WHEN_DONE_A);
+	LYaddstr(ENTER_PERIOD_WHEN_DONE_B);
+	LYaddstr("\n\n");
+	LYrefresh();
+	*user_input = '\0';
+	if (LYgetstr(user_input, VISIBLE, sizeof(user_input), NORECALL) < 0 ||
+	    term_letter || STREQ(user_input, ".")) {
+	    goto cancelled;
+	}
+
+	while (!STREQ(user_input, ".") && !term_letter) {
+	    LYaddstr("\n");
+	    remove_tildes(user_input);
+	    fprintf(fd, "%s\n", user_input);
+	    *user_input = '\0';
+	    if (LYgetstr(user_input, VISIBLE,
+			 sizeof(user_input), NORECALL) < 0) {
+		goto cancelled;
+	    }
+	}
+
+	fprintf(fd, "\n");	/* Terminate the message. */
+	LYCloseTempFP(fd);	/* Close the tmpfile.     */
+	scrollok(LYwin, FALSE);	/* Stop scrolling.        */
+    }
+
+#if !USE_VMS_MAILER
+    /*
+     * Ignore CTRL-C on this last question.
+     */
+    signal(SIGINT, SIG_IGN);
+#endif /* !VMS */
+    LYStatusLine = (LYlines - 1);
+    c = HTConfirm(body ? SEND_MESSAGE_PROMPT : SEND_COMMENT_PROMPT);
+    LYStatusLine = -1;
+    if (c != YES) {
+	LYclear();		/* clear the screen */
+	goto cleanup;
+    }
+    if ((body == NULL && LynxSigFile != NULL) &&
+	(fp = fopen(LynxSigFile, TXT_R)) != NULL) {
+	LYStatusLine = (LYlines - 1);
+	if (term_letter) {
+	    _user_message(APPEND_SIG_FILE, LynxSigFile);
+	    c = 0;
+	} else {
+	    char *msg = NULL;
+
+	    HTSprintf0(&msg, APPEND_SIG_FILE, LynxSigFile);
+	    c = HTConfirm(msg);
+	    FREE(msg);
+	}
+	LYStatusLine = -1;
+	if (c == YES) {
+	    if ((fd = fopen(my_tmpfile, TXT_A)) != NULL) {
+		char *buffer = NULL;
+
+		fputs("-- \n", fd);
+		while (LYSafeGets(&buffer, fp) != NULL) {
+		    fputs(buffer, fd);
+		}
+		LYCloseOutput(fd);
+		FREE(buffer);
+	    }
+	}
+	LYCloseInput(fp);
+    }
+    LYclear();			/* Clear the screen. */
+
+    /*
+     * Send the message.
+     */
+#if USE_VMS_MAILER
+    /*
+     * Set the mail command.  - FM
+     */
+    if (isPMDF) {
+	/*
+	 * For PMDF, put any keywords and the subject in the header file and
+	 * close it.  - FM
+	 */
+	if (non_empty(keywords)) {
+	    fprintf(hfd, "Keywords: %s\n", keywords);
+	}
+	fprintf(hfd, "Subject: %s\n\n", the_subject);
+	LYCloseTempFP(hfd);
+	/*
+	 * Now set up the command.  - FM
+	 */
+	HTSprintf0(&command, "%s %s %s,%s ",
+		   system_mail,
+		   system_mail_flags,
+		   hdrfile,
+		   my_tmpfile);
+    } else {
+	/*
+	 * For "generic" VMS MAIL, include the subject in the command, and
+	 * ignore any keywords to minimize risk of them making the line too
+	 * long or having problem characters.  - FM
+	 */
+	HTSprintf0(&command, "%s %s%s/subject=\"%s\" %s ",
+		   system_mail,
+		   system_mail_flags,
+		   (strncasecomp(system_mail, "MAIL", 4) ? "" : "/noself"),
+		   the_subject,
+		   my_tmpfile);
+    }
+
+    vms_append_addrs(&command, to_address, "");
+    if (non_empty(ccaddr)) {
+	vms_append_addrs(&command, ccaddr, "/CC");
+    }
+
+    stop_curses();
+    printf("%s\n\n$ %s\n\n%s", SENDING_COMMENT, command, PLEASE_WAIT);
+    LYSystem(command);		/* SENDING COMMENT (VMS) */
+    FREE(command);
+    LYSleepAlert();
+    start_curses();
+#else /* Unix/DOS/Windows */
+    /*
+     * Send the tmpfile into sendmail.
+     */
+    _statusline(SENDING_YOUR_MSG);
+#if CAN_PIPE_TO_MAILER
+    signal(SIGINT, SIG_IGN);
+    if ((fp = LYPipeToMailer()) == 0) {
+	HTInfoMsg(CANCELLED);
+    }
+#else
+    if ((fp = LYOpenTemp(tmpfile2, ".txt", "w")) == NULL) {
+	HTAlert(MAILTO_URL_TEMPOPEN_FAILED);
+    }
+#endif /* CAN_PIPE_TO_MAILER */
+    if (fp != 0) {
+	fd = fopen(my_tmpfile, TXT_R);
+	if (fd == NULL) {
+	    HTInfoMsg(CANCELLED);
+#if CAN_PIPE_TO_MAILER
+	    pclose(fp);
+#else
+	    LYCloseTempFP(fp);
+#endif /* CAN_PIPE_TO_MAILER */
+	} else {
+#if USE_BLAT_MAILER
+	    if (!mail_is_blat)
+		fputs(header, fp);
+#else
+	    fputs(header, fp);
+#endif
+	    while ((n = (int) fread(buf, 1, sizeof(buf), fd)) != 0) {
+		fwrite(buf, 1, (size_t) n, fp);
+	    }
+#if CAN_PIPE_TO_MAILER
+	    pclose(fp);
+#else
+	    LYCloseTempFP(fp);	/* Close the tmpfile. */
+	    LYSendMailFile(to_address,
+			   tmpfile2,
+			   the_subject,
+			   ccaddr,
+			   SENDING_COMMENT);
+	    LYRemoveTemp(tmpfile2);	/* Delete the tmpfile. */
+#endif /* CAN_PIPE_TO_MAILER */
+	    LYCloseInput(fd);	/* Close the tmpfile. */
+	}
+    }
+#endif /* USE_VMS_MAILER */
+    goto cleanup;
+
+    /*
+     * Come here to cleanup and exit.
+     */
+  cancelled:
+    HTInfoMsg(CANCELLED);
+    LYCloseTempFP(fd);		/* Close the tmpfile.   */
+    scrollok(LYwin, FALSE);	/* Stop scrolling.      */
+  cleanup:
+    signal(SIGINT, cleanup_sig);
+    term_letter = FALSE;
+
+#if USE_VMS_MAILER
+    while (LYRemoveTemp(my_tmpfile) == 0) ;	/* Delete the tmpfile(s). */
+    if (isPMDF) {
+	LYRemoveTemp(hdrfile);	/* Delete the hdrfile. */
+    }
+#else
+    FREE(header);
+    LYRemoveTemp(my_tmpfile);	/* Delete the tmpfile. */
+#endif /* VMS */
+
+    FREE(from_address);
+    FREE(the_subject);
+    FREE(cc_address);
+    FREE(to_address);
+    FREE(ccaddr);
+    FREE(keywords);
+    FREE(body);
+    return;
+}
+
+/*
+ * Check that we have configured values for system mailer.
+ */
+BOOLEAN LYSystemMail(void)
+{
+    if (system_mail == 0 || !strcmp(system_mail, "unknown")) {
+	HTAlert(gettext("No system mailer configured"));
+	return FALSE;
+    }
+    return TRUE;
+}
diff --git a/src/LYMail.h b/src/LYMail.h
new file mode 100644
index 00000000..7e58bbf7
--- /dev/null
+++ b/src/LYMail.h
@@ -0,0 +1,56 @@
+#ifndef LYMAIL_H
+#define LYMAIL_H
+
+#ifndef LYSTRUCTS_H
+#include <LYStructs.h>
+#endif /* LYSTRUCTS_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef SH_EX
+#define USE_BLAT_MAILER 1
+#else
+#define USE_BLAT_MAILER 0
+#endif
+#ifdef VMS
+#define USE_VMS_MAILER 1
+#else
+#define USE_VMS_MAILER 0
+#endif
+/*
+ * Ifdef's in case we have a working popen/pclose, useful for piping to the
+ * mail program.
+ */
+#if !defined(HAVE_POPEN) || USE_VMS_MAILER || defined(DOSPATH) || defined(__CYGWIN__)
+#define CAN_PIPE_TO_MAILER 0
+#else
+#define CAN_PIPE_TO_MAILER 1
+#endif
+    extern BOOLEAN term_letter;
+
+    extern BOOLEAN LYSystemMail(void);
+    extern BOOLEAN LYMailPMDF(void);
+    extern FILE *LYPipeToMailer(void);
+    extern int LYSendMailFile(char *the_address,
+			      char *the_filename,
+			      char *the_subject,
+			      char *the_ccaddr,
+			      char *message);
+    extern void mailform(const char *mailto_address,
+			 const char *mailto_subject,
+			 const char *mailto_content,
+			 const char *mailto_type);
+    extern void mailmsg(int cur,
+			char *owner_address,
+			char *filename,
+			char *linkname);
+    extern void reply_by_mail(char *mail_address,
+			      char *filename,
+			      const char *title,
+			      const char *refid);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYMAIL_H */
diff --git a/src/LYMain.c b/src/LYMain.c
new file mode 100644
index 00000000..9301d64c
--- /dev/null
+++ b/src/LYMain.c
@@ -0,0 +1,4439 @@
+/*
+ * $LynxId: LYMain.c,v 1.212 2010/04/30 23:49:43 tom Exp $
+ */
+#include <HTUtils.h>
+#include <HTTP.h>
+#include <HTParse.h>
+#include <HTAccess.h>
+#include <HTList.h>
+#include <HTFile.h>
+#include <UCMap.h>
+#include <UCDefs.h>
+#include <HTInit.h>
+#include <HTAlert.h>
+#include <LYCurses.h>
+#include <LYStyle.h>
+#include <HTML.h>
+#include <LYUtils.h>
+#include <LYGlobalDefs.h>
+#include <LYOptions.h>
+#include <LYSignal.h>
+#include <LYGetFile.h>
+#include <LYStrings.h>
+#include <LYClean.h>
+#include <LYCharSets.h>
+#include <LYCharUtils.h>
+#include <LYReadCFG.h>
+#include <LYrcFile.h>
+#include <LYKeymap.h>
+#include <HTForms.h>
+#include <LYList.h>
+#include <LYJump.h>
+
+#ifdef USE_SESSIONS
+#include <LYSession.h>
+#endif
+
+#include <LYMainLoop.h>
+#include <LYBookmark.h>
+#include <LYCookie.h>
+#include <LYPrettySrc.h>
+#include <LYShowInfo.h>
+#include <LYHistory.h>
+
+#ifdef VMS
+#include <HTFTP.h>
+#endif /* !DECNET */
+
+#ifdef __DJGPP__
+#include <dos.h>
+#include <dpmi.h>
+#include <io.h>
+#include <sys/stat.h>
+#include <sys/exceptn.h>
+#endif /* __DJGPP__ */
+
+#ifdef __EMX__
+#include <io.h>
+#endif
+
+#if defined(LOCALE) && (!defined(HAVE_LIBINTL_H) || !defined(LC_ALL))
+#undef gettext			/* Solaris locale.h prototypes gettext() */
+#include <locale.h>
+#ifndef HAVE_GETTEXT
+#define gettext(s) s
+#endif
+#endif /* LOCALE */
+
+#include <LYexit.h>
+#include <LYLeaks.h>
+
+/* ahhhhhhhhhh!! Global variables :-< */
+#ifdef SOCKS
+BOOLEAN socks_flag = TRUE;
+#endif /* SOCKS */
+
+#ifdef IGNORE_CTRL_C
+BOOLEAN sigint = FALSE;
+#endif /* IGNORE_CTRL_C */
+
+#ifdef __DJGPP__
+static char init_ctrl_break[1];
+#endif /* __DJGPP__ */
+
+#if USE_VMS_MAILER
+char *mail_adrs = NULL;		/* the mask for a VMS mail transport */
+#endif
+
+#ifdef VMS
+	       /* create FIXED 512 binaries */
+BOOLEAN UseFixedRecords = USE_FIXED_RECORDS;
+#endif /* VMS */
+
+#ifndef VMS
+static char *lynx_version_putenv_command = NULL;
+char *list_format = NULL;	/* LONG_LIST formatting mask */
+#endif /* !VMS */
+
+char *ftp_format = NULL;	/* LONG_LIST formatting mask */
+
+#ifdef SYSLOG_REQUESTED_URLS
+char *syslog_txt = NULL;	/* syslog arb text for session */
+BOOLEAN syslog_requested_urls = FALSE;
+#endif
+
+int cfg_bad_html = BAD_HTML_WARN;
+
+#ifdef DIRED_SUPPORT
+BOOLEAN lynx_edit_mode = FALSE;
+BOOLEAN no_dired_support = FALSE;
+HTList *tagged = NULL;
+int LYAutoUncacheDirLists = 2;	/* default dired uncaching behavior */
+int dir_list_order = ORDER_BY_NAME;
+int dir_list_style = MIXED_STYLE;
+
+#ifdef OK_OVERRIDE
+BOOLEAN prev_lynx_edit_mode = FALSE;
+#endif /* OK_OVERRIDE */
+
+#ifdef OK_PERMIT
+#ifdef NO_CHANGE_EXECUTE_PERMS
+BOOLEAN no_change_exec_perms = TRUE;
+
+#else
+BOOLEAN no_change_exec_perms = FALSE;
+#endif /* NO_CHANGE_EXECUTE_PERMS */
+#endif /* OK_PERMIT */
+
+#endif /* DIRED_SUPPORT */
+
+	   /* Number of docs cached in memory */
+int HTCacheSize = DEFAULT_CACHE_SIZE;
+
+#if defined(VMS) && defined(VAXC) && !defined(__DECC)
+	   /* Don't dump doc cache unless this size is exceeded */
+int HTVirtualMemorySize = DEFAULT_VIRTUAL_MEMORY_SIZE;
+#endif /* VMS && VAXC && !_DECC */
+
+#if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)
+#ifndef NEVER_ALLOW_REMOTE_EXEC
+BOOLEAN local_exec = LOCAL_EXECUTION_LINKS_ALWAYS_ON;
+
+#else
+BOOLEAN local_exec = FALSE;
+#endif /* NEVER_ALLOW_REMOTE_EXEC */
+BOOLEAN local_exec_on_local_files =
+LOCAL_EXECUTION_LINKS_ON_BUT_NOT_REMOTE;
+#endif /* EXEC_LINKS || EXEC_SCRIPTS */
+
+#if defined(LYNXCGI_LINKS) && !defined(VMS)	/* WebSter Mods -jkt */
+char *LYCgiDocumentRoot = NULL;	/* DOCUMENT_ROOT in the lynxcgi env */
+#endif /* LYNXCGI_LINKS */
+
+#ifdef REVERSE_CLEAR_SCREEN_PROBLEM
+BOOLEAN enable_scrollback = TRUE;
+
+#else
+BOOLEAN enable_scrollback = FALSE;
+#endif /* REVERSE_CLEAR_SCREEN_PROBLEM */
+
+char empty_string[] =
+{'\0'};
+
+int display_lines;		/* number of lines in display */
+int www_search_result = -1;
+
+			       /* linked list of printers */
+lynx_list_item_type *printers = NULL;
+
+			    /* linked list of download options */
+lynx_list_item_type *downloaders = NULL;
+
+			    /* linked list of upload options */
+#ifdef USE_EXTERNALS
+lynx_list_item_type *externals = NULL;
+
+			    /* linked list of external options */
+#endif
+
+lynx_list_item_type *uploaders = NULL;
+int LYShowColor = SHOW_COLOR_UNKNOWN;	/* to show or not */
+int LYrcShowColor = SHOW_COLOR_UNKNOWN;		/* ... last used */
+
+#if !defined(NO_OPTION_FORMS) && !defined(NO_OPTION_MENU)
+BOOLEAN LYUseFormsOptions = TRUE;	/* use forms-based options menu */
+#endif
+
+BOOLEAN LYJumpFileURL = FALSE;	/* always FALSE the first time */
+BOOLEAN LYPermitURL = FALSE;
+BOOLEAN LYRestricted = FALSE;	/* whether we have -anonymous option */
+BOOLEAN LYShowCursor = SHOW_CURSOR;	/* to show or not to show */
+BOOLEAN LYUnderlineLinks = UNDERLINE_LINKS;	/* Show the links underlined vs bold */
+BOOLEAN LYUseDefShoCur = TRUE;	/* Command line -show_cursor toggle */
+BOOLEAN LYUserSpecifiedURL = TRUE;	/* always TRUE  the first time */
+BOOLEAN LYValidate = FALSE;
+BOOLEAN LYforce_no_cache = FALSE;
+BOOLEAN LYinternal_flag = FALSE;	/* override no-cache b/c internal link */
+BOOLEAN LYoverride_no_cache = FALSE;	/*override no-cache b/c history etc */
+BOOLEAN LYresubmit_posts = ALWAYS_RESUBMIT_POSTS;
+BOOLEAN LYtrimInputFields = FALSE;
+BOOLEAN LYxhtml_parsing = FALSE;
+BOOLEAN bold_H1 = FALSE;
+BOOLEAN bold_headers = FALSE;
+BOOLEAN bold_name_anchors = FALSE;
+BOOLEAN case_sensitive = CASE_SENSITIVE_ALWAYS_ON;
+BOOLEAN check_mail = CHECKMAIL;
+BOOLEAN child_lynx = FALSE;
+BOOLEAN dump_links_only = FALSE;
+BOOLEAN dump_output_immediately = FALSE;
+BOOLEAN dump_to_stderr = FALSE;
+BOOLEAN emacs_keys = EMACS_KEYS_ALWAYS_ON;
+BOOLEAN error_logging = MAIL_SYSTEM_ERROR_LOGGING;
+BOOLEAN goto_buffer = GOTOBUFFER;	/* TRUE if offering default goto URL */
+BOOLEAN historical_comments = FALSE;
+BOOLEAN is_www_index = FALSE;
+BOOLEAN jump_buffer = JUMPBUFFER;	/* TRUE if offering default shortcut */
+BOOLEAN lynx_mode = NORMAL_LYNX_MODE;
+BOOLEAN minimal_comments = FALSE;
+BOOLEAN number_fields_on_left = TRUE;
+BOOLEAN number_links_on_left = TRUE;
+BOOLEAN recent_sizechange = FALSE;	/* the window size changed recently? */
+BOOLEAN soft_dquotes = FALSE;
+BOOLEAN use_underscore = SUBSTITUTE_UNDERSCORES;
+BOOLEAN verbose_img = VERBOSE_IMAGES;	/* show filenames or not */
+BOOLEAN vi_keys = VI_KEYS_ALWAYS_ON;
+int keypad_mode = DEFAULT_KEYPAD_MODE;
+int user_mode = NOVICE_MODE;
+
+BOOLEAN telnet_ok = TRUE;
+
+#ifndef DISABLE_NEWS
+BOOLEAN news_ok = TRUE;
+#endif
+BOOLEAN rlogin_ok = TRUE;
+BOOLEAN long_url_ok = FALSE;
+BOOLEAN ftp_ok = TRUE;
+BOOLEAN system_editor = FALSE;
+
+BOOLEAN had_restrictions_default = FALSE;
+BOOLEAN had_restrictions_all = FALSE;
+
+BOOLEAN exec_frozen = FALSE;
+BOOLEAN no_bookmark = FALSE;
+BOOLEAN no_bookmark_exec = FALSE;
+BOOLEAN no_chdir = FALSE;
+BOOLEAN no_disk_save = FALSE;
+BOOLEAN no_dotfiles = NO_DOT_FILES;
+BOOLEAN no_download = FALSE;
+BOOLEAN no_editor = FALSE;
+BOOLEAN no_exec = FALSE;
+BOOLEAN no_file_url = FALSE;
+BOOLEAN no_goto = FALSE;
+BOOLEAN no_goto_configinfo = FALSE;
+BOOLEAN no_goto_cso = FALSE;
+BOOLEAN no_goto_file = FALSE;
+BOOLEAN no_goto_finger = FALSE;
+BOOLEAN no_goto_ftp = FALSE;
+BOOLEAN no_goto_gopher = FALSE;
+BOOLEAN no_goto_http = FALSE;
+BOOLEAN no_goto_https = FALSE;
+BOOLEAN no_goto_lynxcgi = FALSE;
+BOOLEAN no_goto_lynxexec = FALSE;
+BOOLEAN no_goto_lynxprog = FALSE;
+BOOLEAN no_goto_mailto = FALSE;
+BOOLEAN no_goto_rlogin = FALSE;
+BOOLEAN no_goto_telnet = FALSE;
+BOOLEAN no_goto_tn3270 = FALSE;
+BOOLEAN no_goto_wais = FALSE;
+BOOLEAN no_inside_ftp = FALSE;
+BOOLEAN no_inside_rlogin = FALSE;
+BOOLEAN no_inside_telnet = FALSE;
+BOOLEAN no_jump = FALSE;
+BOOLEAN no_lynxcfg_info = FALSE;
+BOOLEAN no_lynxcgi = FALSE;
+BOOLEAN no_mail = FALSE;
+BOOLEAN no_multibook = FALSE;
+BOOLEAN no_option_save = FALSE;
+BOOLEAN no_outside_ftp = FALSE;
+BOOLEAN no_outside_rlogin = FALSE;
+BOOLEAN no_outside_telnet = FALSE;
+BOOLEAN no_print = FALSE;
+BOOLEAN no_shell = FALSE;
+BOOLEAN no_suspend = FALSE;
+BOOLEAN no_telnet_port = FALSE;
+BOOLEAN no_useragent = FALSE;
+
+#ifndef DISABLE_FTP
+BOOLEAN ftp_passive = FTP_PASSIVE;	/* TRUE if doing ftp in passive mode */
+BOOLEAN ftp_local_passive;
+HTList *broken_ftp_epsv = NULL;
+HTList *broken_ftp_retr = NULL;
+char *ftp_lasthost = NULL;
+#endif
+
+#ifndef DISABLE_NEWS
+BOOLEAN no_goto_news = FALSE;
+BOOLEAN no_goto_nntp = FALSE;
+BOOLEAN no_goto_snews = FALSE;
+BOOLEAN no_inside_news = FALSE;
+BOOLEAN no_newspost = FALSE;
+BOOLEAN no_outside_news = FALSE;
+#endif
+
+#ifdef USE_EXTERNALS
+BOOLEAN no_externals = FALSE;
+#endif
+
+#ifndef NO_CONFIG_INFO
+BOOLEAN no_lynxcfg_xinfo = FALSE;
+
+#ifdef HAVE_CONFIG_H
+BOOLEAN no_compileopts_info = FALSE;
+#endif
+#endif
+
+BOOLEAN no_statusline = FALSE;
+BOOLEAN no_filereferer = TRUE;
+char LYRefererWithQuery = 'D';	/* 'D' for drop */
+BOOLEAN local_host_only = FALSE;
+BOOLEAN override_no_download = FALSE;
+BOOLEAN show_dotfiles = FALSE;	/* From rcfile if no_dotfiles is false */
+BOOLEAN LYforce_HTML_mode = FALSE;
+BOOLEAN LYfind_leaks = TRUE;
+
+#ifdef __DJGPP__
+BOOLEAN watt_debug = FALSE;	/* WATT-32 debugging */
+BOOLEAN dj_is_bash = FALSE;	/* Check for bash shell under DJGPP */
+#endif /* __DJGPP__ */
+
+#ifdef WIN_EX
+BOOLEAN focus_window = FALSE;	/* 1998/10/05 (Mon) 17:18:42 */
+char windows_drive[4];		/* 1998/01/13 (Tue) 21:13:24 */
+#endif
+
+#ifdef _WINDOWS
+#define	TIMEOUT	180		/* 1998/03/30 (Mon) 14:50:44 */
+int lynx_timeout = TIMEOUT;
+CRITICAL_SECTION critSec_DNS;	/* 1998/09/03 (Thu) 22:01:56 */
+CRITICAL_SECTION critSec_READ;	/* 1998/09/03 (Thu) 22:01:56 */
+#endif /* _WINDOWS */
+
+#if defined(WIN_EX)
+BOOLEAN system_is_NT = FALSE;
+#endif
+
+BOOLEAN show_cfg = FALSE;
+
+BOOLEAN no_table_center = FALSE;	/* 1998/10/09 (Fri) 15:12:49 */
+
+#if USE_BLAT_MAILER
+BOOLEAN mail_is_blat = TRUE;
+#endif
+
+#ifdef USE_BLINK
+#  ifdef __EMX__
+BOOLEAN term_blink_is_boldbg = TRUE;
+
+#  else
+BOOLEAN term_blink_is_boldbg = FALSE;
+
+#  endif
+#endif
+
+BOOLEAN HEAD_request = FALSE;
+BOOLEAN LYAcceptAllCookies = ACCEPT_ALL_COOKIES;	/* take all cookies? */
+BOOLEAN LYCancelledFetch = FALSE;	/* TRUE if cancelled binary fetch */
+BOOLEAN LYCollapseBRs = COLLAPSE_BR_TAGS;	/* Collapse serial BRs? */
+BOOLEAN LYDefaultRawMode;
+BOOLEAN LYListNewsDates = LIST_NEWS_DATES;
+BOOLEAN LYListNewsNumbers = LIST_NEWS_NUMBERS;
+BOOLEAN LYMBMBlocked = BLOCK_MULTI_BOOKMARKS;
+BOOLEAN LYNewsPosting = NEWS_POSTING;	/* News posting supported? */
+BOOLEAN LYNoFromHeader = TRUE;	/* Never send From header?         */
+BOOLEAN LYNoRefererForThis = FALSE;	/* No Referer header for this URL? */
+BOOLEAN LYNoRefererHeader = FALSE;	/* Never send Referer header?     */
+BOOLEAN LYRawMode;
+BOOLEAN LYSelectPopups = USE_SELECT_POPUPS;
+BOOLEAN LYSendUserAgent = SEND_USERAGENT;	/* send Lynx User-Agent header? */
+BOOLEAN LYSetCookies = SET_COOKIES;	/* Process Set-Cookie headers? */
+BOOLEAN LYUseDefSelPop = TRUE;	/* Command line -popup toggle */
+BOOLEAN LYUseDefaultRawMode = TRUE;
+BOOLEAN LYUseMouse = FALSE;
+BOOLEAN LYisConfiguredForX = FALSE;
+BOOLEAN UCForce8bitTOUPPER = FALSE;	/* override locale for case-conversion? */
+BOOLEAN UCSaveBookmarksInUnicode = FALSE;
+BOOLEAN bookmark_start = FALSE;
+BOOLEAN check_realm = FALSE;	/* Restrict to the starting realm? */
+BOOLEAN clickable_images = MAKE_LINKS_FOR_ALL_IMAGES;
+BOOLEAN crawl = FALSE;		/* Do crawl? */
+BOOLEAN keep_mime_headers = FALSE;	/* Include mime headers with source dump */
+BOOLEAN more_text = FALSE;	/* is there more text to display? */
+BOOLEAN more_links = FALSE;	/* Links beyond a displayed page with no links? */
+BOOLEAN no_list = FALSE;
+BOOLEAN no_margins = FALSE;
+BOOLEAN no_pause = FALSE;
+BOOLEAN no_title = FALSE;
+BOOLEAN no_url_redirection = FALSE;	/* Don't follow URL redirections */
+BOOLEAN pseudo_inline_alts = MAKE_PSEUDO_ALTS_FOR_INLINES;
+BOOLEAN scan_for_buried_news_references = TRUE;
+BOOLEAN startfile_ok = FALSE;
+static BOOLEAN startfile_stdin = FALSE;
+BOOLEAN traversal = FALSE;	/* Do traversals? */
+
+char *BookmarkPage = NULL;	/* the name of the current bookmark page */
+char *LYCookieAcceptDomains = NULL;	/* domains to accept all cookies */
+char *LYCookieLooseCheckDomains = NULL;		/* check loosely   */
+char *LYCookieQueryCheckDomains = NULL;		/* check w/a query */
+char *LYCookieRejectDomains = NULL;	/* domains to reject all cookies */
+char *LYCookieSAcceptDomains = NULL;	/* domains to accept all cookies */
+char *LYCookieSLooseCheckDomains = NULL;	/* check loosely   */
+char *LYCookieSQueryCheckDomains = NULL;	/* check w/a query */
+char *LYCookieSRejectDomains = NULL;	/* domains to reject all cookies */
+char *LYCookieSStrictCheckDomains = NULL;	/* check strictly  */
+char *LYCookieStrictCheckDomains = NULL;	/* check strictly  */
+char *LYHostName = NULL;	/* treat as a local host name */
+char *LYLocalDomain = NULL;	/* treat as a local domain tail */
+char *LYUserAgent = NULL;	/* Lynx User-Agent header          */
+char *LYUserAgentDefault = NULL;	/* Lynx default User-Agent header  */
+char *LynxHome = NULL;		/* the default Home HREF. */
+char *LynxSigFile = NULL;	/* Signature file, in or off home */
+char *UCAssume_MIMEcharset = NULL;
+char *URLDomainPrefixes = NULL;
+char *URLDomainSuffixes = NULL;
+char *anonftp_password = NULL;	/* anonymous ftp password (default: email) */
+char *authentication_info[2] =
+{NULL, NULL};			/* Id:Password for protected documents */
+char *bookmark_page = NULL;	/* the name of the default bookmark page */
+char *editor = NULL;		/* the name of the current editor */
+char *form_get_data = NULL;	/* User data for get form */
+char *form_post_data = NULL;	/* User data for post form */
+char *global_extension_map = NULL;	/* global mime.types */
+char *global_type_map = NULL;	/* global mailcap */
+char *helpfile = NULL;		/* the main help file */
+char *helpfilepath = NULL;	/* the path to the help file set */
+char *homepage = NULL;		/* home page or main screen */
+char *http_error_file = NULL;	/* Place HTTP status code in this file */
+char *indexfile = NULL;		/* an index file if there is one */
+char *jumpfile = NULL;		/* the name of the default jumps file */
+char *jumpprompt = NULL;	/* the default jumps prompt */
+char *language = NULL;		/* preferred language */
+char *lynx_cfg_file = NULL;	/* location of active lynx.cfg */
+char *lynx_cmd_logfile;		/* file to write keystroke commands, if any */
+char *lynx_cmd_script;		/* file to read keystroke commands, if any */
+char *lynx_save_space = NULL;	/* The prefix for save to disk paths */
+char *lynx_temp_space = NULL;	/* The prefix for temporary file paths */
+char *lynxjumpfile = NULL;	/* the current jump file URL */
+char *lynxlinksfile = NULL;	/* the current visited links file URL */
+char *lynxlistfile = NULL;	/* the current list file URL */
+char *original_dir = NULL;	/* the original directory */
+char *personal_extension_map = NULL;	/* .mime.types */
+char *personal_mail_address = NULL;	/* the users mail address */
+char *personal_type_map = NULL;	/* .mailcap */
+char *pref_charset = NULL;	/* preferred character set */
+char *proxyauth_info[2] =
+{NULL, NULL};			/* Id:Password for protected proxy servers */
+
+#ifdef USE_SESSIONS
+BOOLEAN LYAutoSession = FALSE;	/* enable/disable auto saving/restoring of */
+
+				/* session */
+char *LYSessionFile = NULL;	/* the session file from lynx.cfg */
+char *session_file = NULL;	/* the current session file */
+char *sessionin_file = NULL;	/* only resume session from this file */
+char *sessionout_file = NULL;	/* only save session to this file */
+short session_limit = 250;	/* maximal number of entries saved per */
+
+				/* session file, rest will be ignored */
+#endif /* USE_SESSIONS */
+char *startfile = NULL;		/* the first file */
+char *startrealm = NULL;	/* the startfile realm */
+char *system_mail = NULL;	/* The path for sending mail */
+char *system_mail_flags = NULL;	/* Flags for sending mail */
+char *x_display = NULL;		/* display environment variable */
+
+HistInfo *history;
+int nhist = 0;			/* number of used history entries */
+int size_history;		/* number of allocated history entries */
+
+LinkInfo links[MAXLINKS];
+
+BOOLEAN nomore = FALSE;		/* display -more- string in statusline messages */
+int AlertSecs;			/* time-delay for HTAlert() messages   */
+int DebugSecs;			/* time-delay for HTProgress messages */
+int InfoSecs;			/* time-delay for Information messages */
+int LYMultiBookmarks = MULTI_BOOKMARK_SUPPORT;
+int LYStatusLine = -1;		/* Line for statusline() if > -1 */
+int LYcols = DFT_COLS;
+int LYlines = DFT_ROWS;
+int MessageSecs;		/* time-delay for important Messages   */
+int ReplaySecs;			/* time-delay for command-scripts */
+int crawl_count = 0;		/* Starting number for lnk#.dat files in crawls */
+int dump_output_width = 0;
+int dump_server_status = 0;
+int lynx_temp_subspace = 0;	/* > 0 if we made temp-directory */
+int max_cookies_domain = 50;
+int max_cookies_global = 500;
+int max_cookies_buffer = 4096;
+int nlinks = 0;			/* number of links in memory */
+int outgoing_mail_charset = -1;	/* translate mail to this charset */
+
+#ifndef DISABLE_BIBP
+BOOLEAN BibP_bibhost_available = FALSE;		/* until check succeeds  */
+BOOLEAN BibP_bibhost_checked = FALSE;	/*  until LYCheckBibHost   */
+BOOLEAN no_goto_bibp = FALSE;
+char *BibP_bibhost = NULL;	/* local server for bibp: links  */
+char *BibP_globalserver = NULL;	/* global server for bibp: links */
+#endif
+
+#ifdef USE_PERSISTENT_COOKIES
+BOOLEAN persistent_cookies = FALSE;	/* disabled by default! */
+char *LYCookieFile = NULL;	/* cookie read file */
+char *LYCookieSaveFile = NULL;	/* cookie save file */
+#endif /* USE_PERSISTENT_COOKIES */
+
+#ifdef EXP_NESTED_TABLES
+BOOLEAN nested_tables =
+#if defined(USE_COLOR_STYLE)
+TRUE
+#else
+FALSE				/* see 2001-08-15  */
+#endif
+ ;
+#endif
+
+BOOLEAN LYShowTransferRate = TRUE;
+int LYTransferRate = rateKB;
+int LYAcceptEncoding = encodingALL;
+int LYAcceptMedia = mediaOpt1;
+char *LYTransferName = NULL;
+
+char *XLoadImageCommand = NULL;	/* Default image viewer for X */
+BOOLEAN LYNoISMAPifUSEMAP = FALSE;	/* Omit ISMAP link if MAP present? */
+int LYHiddenLinks = HIDDENLINKS_SEPARATE;	/* Show hidden links? */
+
+char *SSL_cert_file = NULL;	/* Default CA CERT file */
+
+int Old_DTD = NO;
+static BOOLEAN DTD_recovery = NO;
+
+#ifndef NO_LYNX_TRACE
+FILE *LYTraceLogFP = NULL;	/* Pointer for TRACE log  */
+#endif
+char *LYTraceLogPath = NULL;	/* Path for TRACE log      */
+BOOLEAN LYUseTraceLog = USE_TRACE_LOG;	/* Use a TRACE log?        */
+
+BOOLEAN LYSeekFragMAPinCur = TRUE;
+BOOLEAN LYSeekFragAREAinCur = TRUE;
+BOOLEAN LYStripDotDotURLs = TRUE;	/* Try to fix ../ in some URLs? */
+BOOLEAN LYForceSSLCookiesSecure = FALSE;
+BOOLEAN LYNoCc = FALSE;
+BOOLEAN LYPreparsedSource = FALSE;	/* Show source as preparsed? */
+BOOLEAN LYPrependBaseToSource = TRUE;
+BOOLEAN LYPrependCharsetToSource = TRUE;
+BOOLEAN LYQuitDefaultYes = QUIT_DEFAULT_YES;
+BOOLEAN dont_wrap_pre = FALSE;
+
+int cookie_noprompt;
+
+#ifdef USE_SSL
+int ssl_noprompt = FORCE_PROMPT_DFT;
+#endif
+
+int connect_timeout = 18000; /*=180000*0.1 - used in HTDoConnect.*/
+int reading_timeout = 18000; /*=180000*0.1 - used in HTDoConnect.*/
+
+#ifdef USE_JUSTIFY_ELTS
+BOOLEAN ok_justify = FALSE;
+int justify_max_void_percent = 35;
+#endif
+
+#ifdef USE_LOCALE_CHARSET
+BOOLEAN LYLocaleCharset = FALSE;
+#endif
+
+#ifndef NO_DUMP_WITH_BACKSPACES
+BOOLEAN with_backspaces = FALSE;
+#endif
+
+#if defined(PDCURSES) && defined(PDC_BUILD) && PDC_BUILD >= 2401
+int scrsize_x = 0;
+int scrsize_y = 0;
+#endif
+
+BOOLEAN force_empty_hrefless_a = FALSE;
+
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+BOOL textfields_need_activation = FALSE;
+BOOLEAN textfields_activation_option = FALSE;
+#endif
+
+BOOLEAN textfield_prompt_at_left_edge = FALSE;
+
+#ifdef MARK_HIDDEN_LINKS
+char *hidden_link_marker = NULL;
+#endif
+
+#ifdef DISP_PARTIAL
+BOOLEAN display_partial_flag = TRUE;	/* Display document during download */
+BOOLEAN debug_display_partial = FALSE;	/* Show with MessageSecs delay */
+int partial_threshold = -1;	/* # of lines to be d/l'ed until we repaint */
+#endif
+
+BOOLEAN LYNonRestartingSIGWINCH = FALSE;
+BOOLEAN LYReuseTempfiles = FALSE;
+BOOLEAN LYUseBuiltinSuffixes = TRUE;
+
+#ifdef MISC_EXP
+int LYNoZapKey = 0;		/* 0: off (do z checking), 1: full, 2: initially */
+#endif
+
+#ifndef DISABLE_NEWS
+#include <HTNews.h>
+#endif
+
+BOOLEAN FileInitAlreadyDone = FALSE;
+
+static BOOLEAN stack_dump = FALSE;
+static char *terminal = NULL;
+static const char *pgm;
+static BOOLEAN no_numbers = FALSE;
+static BOOLEAN number_links = FALSE;
+static BOOLEAN number_fields = FALSE;
+static BOOLEAN LYPrependBase = FALSE;
+static HTList *LYStdinArgs = NULL;
+HTList *positionable_editor = NULL;
+
+#ifndef EXTENDED_OPTION_LOGIC
+/* if set then '--' will be recognized as the end of options */
+#define EXTENDED_OPTION_LOGIC 1
+#endif
+
+#ifndef EXTENDED_STARTFILE_RECALL
+/* if set then additional non-option args (before the last one) will be
+   made available for 'g'oto recall - kw */
+#define EXTENDED_STARTFILE_RECALL 1
+#endif
+
+#if EXTENDED_STARTFILE_RECALL
+static char *nonoption = 0;
+#endif
+
+#ifndef OPTNAME_ALLOW_DASHES
+/* if set, then will allow dashes and underscores to be used interchangeable
+   in commandline option's names - VH */
+#define OPTNAME_ALLOW_DASHES 1
+#endif
+
+static BOOL parse_arg(char **arg, unsigned mask, int *countp);
+static void print_help_and_exit(int exit_status) GCC_NORETURN;
+static void print_help_strings(const char *name,
+			       const char *help,
+			       const char *value,
+			       BOOLEAN option);
+
+#ifndef VMS
+BOOLEAN LYNoCore = NO_FORCED_CORE_DUMP;
+BOOLEAN restore_sigpipe_for_children = FALSE;
+static void FatalProblem(int sig);
+#endif /* !VMS */
+
+#if defined(USE_COLOR_STYLE)
+char *lynx_lss_file2 = NULL;	/* from command-line options */
+char *lynx_lss_file = NULL;	/* from config-file, etc. */
+#endif
+
+#ifdef USE_DEFAULT_COLORS
+BOOLEAN LYuse_default_colors = TRUE;
+#endif
+
+#ifdef __DJGPP__
+static void LY_set_ctrl_break(int setting)
+{
+    (void) signal(SIGINT, (setting ? SIG_DFL : SIG_IGN));
+    setcbrk(setting);
+}
+
+static int LY_get_ctrl_break(void)
+{
+    __dpmi_regs regs;
+
+    regs.h.ah = 0x33;
+    regs.h.al = 0x00;
+    __dpmi_int(0x21, &regs);
+    return ((int) regs.h.dl);
+}
+
+static void reset_break(void)
+{
+    LY_set_ctrl_break(init_ctrl_break[0]);
+}
+#endif /* __DJGPP__ */
+
+#if defined(WIN_EX)
+static int is_windows_nt(void)
+{
+    DWORD version;
+
+    version = GetVersion();
+    if ((version & 0x80000000) == 0)
+	return 1;
+    else
+	return 0;
+}
+#endif
+
+#ifdef LY_FIND_LEAKS
+static void free_lynx_globals(void)
+{
+    int i;
+
+    FREE(ftp_format);
+#ifndef VMS
+    FREE(list_format);
+#ifdef LYNXCGI_LINKS		/* WebSter Mods -jkt */
+    FREE(LYCgiDocumentRoot);
+#endif /* LYNXCGI_LINKS */
+    free_lynx_cfg();
+#endif /* !VMS */
+
+#ifdef SYSLOG_REQUESTED_URLS
+    FREE(syslog_txt);
+#endif
+
+#ifdef VMS
+    Define_VMSLogical("LYNX_VERSION", "");
+#endif /* VMS */
+#ifndef VMS
+    FREE(lynx_version_putenv_command);
+#endif
+
+#if USE_VMS_MAILER
+    FREE(mail_adrs);
+#endif
+
+    FREE(LynxHome);
+    FREE(history);
+    FREE(homepage);
+    FREE(original_dir);
+    FREE(startfile);
+    FREE(helpfile);
+    FREE(helpfilepath);
+    FREE(jumpprompt);
+#ifdef JUMPFILE
+    FREE(jumpfile);
+#endif /* JUMPFILE */
+    FREE(indexfile);
+    FREE(x_display);
+    FREE(global_type_map);
+    FREE(personal_type_map);
+    FREE(global_extension_map);
+    FREE(personal_extension_map);
+    FREE(language);
+    FREE(pref_charset);
+    FREE(LynxSigFile);
+    FREE(system_mail);
+    FREE(system_mail_flags);
+#ifndef DISABLE_BIBP
+    FREE(BibP_bibhost);
+    FREE(BibP_globalserver);
+#endif
+#ifdef USE_PERSISTENT_COOKIES
+    FREE(LYCookieFile);
+    FREE(LYCookieSaveFile);
+#endif
+    FREE(LYCookieAcceptDomains);
+    FREE(LYCookieRejectDomains);
+    FREE(LYCookieLooseCheckDomains);
+    FREE(LYCookieStrictCheckDomains);
+    FREE(LYCookieQueryCheckDomains);
+    FREE(LYUserAgent);
+    FREE(LYUserAgentDefault);
+    FREE(LYHostName);
+    FREE(LYLocalDomain);
+    FREE(lynx_save_space);
+    FREE(bookmark_page);
+    FREE(BookmarkPage);
+    for (i = 0; i <= MBM_V_MAXFILES; i++) {
+	FREE(MBM_A_subbookmark[i]);
+	FREE(MBM_A_subdescript[i]);
+    }
+    FREE(editor);
+    FREE(authentication_info[0]);
+    FREE(authentication_info[1]);
+    FREE(proxyauth_info[0]);
+    FREE(proxyauth_info[1]);
+    FREE(lynxjumpfile);
+#ifndef DISABLE_FTP
+    FREE(ftp_lasthost);
+    LYFreeStringList(broken_ftp_epsv);
+    LYFreeStringList(broken_ftp_retr);
+#endif
+    FREE(startrealm);
+    FREE(personal_mail_address);
+    FREE(anonftp_password);
+    FREE(URLDomainPrefixes);
+    FREE(URLDomainSuffixes);
+    FREE(XLoadImageCommand);
+    FREE(lynx_temp_space);
+    FREE(LYTransferName);
+    FREE(LYTraceLogPath);
+    FREE(lynx_cfg_file);
+#if defined(USE_COLOR_STYLE)
+    FREE(lynx_lss_file2);
+    FREE(lynx_lss_file);
+#endif
+    FREE(UCAssume_MIMEcharset);
+    LYUIPages_free();
+    LYFreeHilites(0, nlinks);
+    nlinks = 0;
+    LYFreeStringList(LYcommandList());
+    HTInitProgramPaths();
+#if EXTENDED_STARTFILE_RECALL
+    FREE(nonoption);
+#endif
+    LYFreeStringList(positionable_editor);
+
+    return;
+}
+#endif /* LY_FIND_LEAKS */
+
+/*
+ * This function frees the LYStdinArgs list.  - FM
+ */
+static void LYStdinArgs_free(void)
+{
+    LYFreeStringList(LYStdinArgs);
+    LYStdinArgs = NULL;
+}
+
+void reset_signals(void)
+{
+#ifndef NOSIGHUP
+    (void) signal(SIGHUP, SIG_DFL);
+#endif /* NOSIGHUP */
+    (void) signal(SIGTERM, SIG_DFL);
+#ifndef VMS
+    (void) signal(SIGINT, SIG_DFL);
+#endif /* !VMS */
+#ifdef SIGTSTP
+    if (no_suspend)
+	(void) signal(SIGTSTP, SIG_DFL);
+#endif /* SIGTSTP */
+}
+
+void exit_immediately(int code)
+{
+    reset_signals();
+    exit(code);
+}
+
+#ifdef  EBCDIC
+static void FixCharacters(void)
+{
+    int c;
+    int work1[256], work2[256];
+
+    for (c = 0; c < 256; c++) {
+	work1[c] = keymap[c + 1];
+	work2[c] = key_override[c + 1];
+    }
+    for (c = 0; c < 256; c++) {
+	keymap[IBM1047[c] + 1] = work1[c];
+	key_override[IBM1047[c] + 1] = work2[c];
+    }
+}
+#endif /* EBCDIC */
+
+static BOOL GetStdin(char **buf,
+		     BOOL marker)
+{
+    if (LYSafeGets(buf, stdin) != 0
+	&& (!marker || strncmp(*buf, "---", 3) != 0)) {
+	LYTrimTrailing(*buf);
+	CTRACE((tfp, "...data: %s\n", *buf));
+	return TRUE;
+    }
+    CTRACE((tfp, "...mark: %s\n", *buf ? *buf : ""));
+    return FALSE;
+}
+
+#ifdef WIN32
+static BOOL cleanup_win32(DWORD fdwCtrlType)
+{
+    switch (fdwCtrlType) {
+    case CTRL_CLOSE_EVENT:
+	cleanup_sig(-1);
+	return TRUE;
+    default:
+	return FALSE;
+    }
+}
+#endif
+
+/*
+ * Append the SSL version to lynx version or user-agent string.
+ */
+#ifdef USE_SSL
+static void append_ssl_version(char **target,
+			       const char *separator)
+{
+    char SSLLibraryVersion[256];
+    char *SSLcp;
+
+    HTSprintf(target, " SSL-MM%s1.4.1", separator);
+
+#undef LYNX_SSL_VERSION
+
+#if defined(SSLEAY_VERSION)
+#define LYNX_SSL_VERSION SSLeay_version(SSLEAY_VERSION)
+#else
+#if defined(OPENSSL_VERSION_TEXT)
+#define LYNX_SSL_VERSION OPENSSL_VERSION_TEXT
+#else
+#if defined(GNUTLS_VERSION)
+#define LYNX_SSL_VERSION GNUTLS_VERSION
+#endif /* GNUTLS_VERSION */
+#endif /* OPENSSL_VERSION_TEXT */
+#endif
+
+#ifdef LYNX_SSL_VERSION
+    if (*separator == ' ')
+	StrAllocCat(*target, ",");
+    LYstrncpy(SSLLibraryVersion, LYNX_SSL_VERSION, sizeof(SSLLibraryVersion) - 1);
+    if ((SSLcp = strchr(SSLLibraryVersion, ' ')) != NULL) {
+	*SSLcp++ = *separator;
+	if ((SSLcp = strchr(SSLcp, ' ')) != NULL) {
+	    *SSLcp = '\0';
+	    StrAllocCat(*target, " ");
+	    StrAllocCat(*target, SSLLibraryVersion);
+	}
+    }
+#endif /* LYNX_SSL_VERSION */
+}
+#endif /* USE_SSL */
+
+static void SetLocale(void)
+{
+#ifdef LOCALE
+    /*
+     * LOCALE support for international characters.
+     */
+    setlocale(LC_ALL, "");
+#endif /* LOCALE */
+    /* Set the text message domain.  */
+#if defined(HAVE_LIBINTL_H) || defined(HAVE_LIBGETTEXT_H)
+    {
+	char *cp;
+
+	if ((cp = LYGetEnv("LYNX_LOCALEDIR")) == 0)
+	    cp = LOCALEDIR;
+	bindtextdomain("lynx", cp);
+	textdomain("lynx");
+    }
+#endif /* HAVE_LIBINTL_H */
+}
+
+/*
+ * Wow!  Someone wants to start up Lynx.
+ */
+int main(int argc,
+	 char **argv)
+{
+    int i;			/* indexing variable */
+    int status = 0;		/* exit status */
+    char *temp = NULL;
+    char *cp;
+    FILE *fp;
+    struct stat dir_info;
+    char filename[LY_MAXPATH];
+    BOOL LYGetStdinArgs = FALSE;
+
+#ifdef _WINDOWS
+    WSADATA WSAData;
+#endif /* _WINDOWS */
+
+    /*
+     * Just in case someone has the idea to install lynx set-uid, let's try
+     * to discourage it.
+     */
+#if defined(GETUID) && defined(SETUID)
+    setuid(getuid());
+#endif
+
+#ifdef    NOT_ASCII
+    FixCharacters();
+#endif /* NOT_ASCII */
+
+#ifndef DISABLE_FTP
+    /* malloc a sizeof(char) so 1st strcmp() won't dump in HTLoadFile() */
+    ftp_lasthost = typecalloc(char);
+#endif
+
+#ifdef USE_CHARSET_CHOICE
+    memset((char *) charset_subsets, 0, sizeof(charset_subset_t) * MAXCHARSETS);
+#endif
+
+#ifdef _WINDOWS
+    {
+	int err;
+	WORD wVerReq;
+
+	wVerReq = MAKEWORD(1, 1);
+
+	err = WSAStartup(wVerReq, &WSAData);
+	if (err != 0) {
+	    puts(gettext("No Winsock found, sorry."));
+	    sleep(5);
+	    return 1;
+	}
+    }
+
+    /* 1998/09/03 (Thu) 22:02:32 */
+    InitializeCriticalSection(&critSec_DNS);
+    InitializeCriticalSection(&critSec_READ);
+
+#endif /* _WINDOWS */
+
+#if defined(WIN_EX)
+    /* 1997/10/19 (Sun) 21:40:54 */
+    system_is_NT = (BOOL) is_windows_nt();
+
+    /* 1998/01/13 (Tue) 21:13:47 */
+    GetWindowsDirectory(filename, sizeof filename);
+    windows_drive[0] = filename[0];
+    windows_drive[1] = filename[1];
+    windows_drive[2] = '\0';
+#endif
+
+#ifdef __DJGPP__
+    if (LY_get_ctrl_break() == 0) {
+	LY_set_ctrl_break(TRUE);
+	init_ctrl_break[0] = 0;
+    } else {
+	init_ctrl_break[0] = 1;
+    }
+    __djgpp_set_sigquit_key(0x082D);	/* Bind ALT-X to SIGQUIT */
+    signal(SIGQUIT, cleanup_sig);
+    atexit(reset_break);
+
+    if (((cp = LYGetEnv("SHELL")) != NULL)
+	&& (strstr(LYPathLeaf(cp), "sh") != NULL))
+	dj_is_bash = TRUE;
+#endif /* __DJGPP__ */
+
+    /*
+     * To prevent corrupting binary data on DOS, MS-WINDOWS or OS/2
+     * we open files and stdout in BINARY mode by default.
+     * Where necessary we should open and (close!) TEXT mode.
+     * (use LYNewTxtFile/LYAppendToTxtFile to open text files for writing)
+     */
+    SetDefaultMode(O_BINARY);
+    SetOutputMode(O_BINARY);
+
+#ifdef DOSPATH
+    if (LYGetEnv("TERM") == NULL)
+	putenv("TERM=vt100");
+#endif
+
+    LYShowColor = (SHOW_COLOR ? SHOW_COLOR_ON : SHOW_COLOR_OFF);
+    /*
+     * Set up the argument list.
+     */
+    pgm = argv[0];
+    cp = NULL;
+    if ((cp = LYLastPathSep(pgm)) != NULL) {
+	pgm = cp + 1;
+    }
+
+    /*
+     * Set up trace, the anonymous account defaults, validate restrictions,
+     * and/or the nosocks flag, if requested, and an alternate configuration
+     * file, if specified, NOW.  Also, if we only want the help menu, output
+     * that and exit.  - FM
+     */
+#ifndef NO_LYNX_TRACE
+    if (LYGetEnv("LYNX_TRACE") != 0) {
+	WWW_TraceFlag = TRUE;
+    }
+#endif
+
+    /*
+     * Set up the TRACE log path, and logging if appropriate.  - FM
+     */
+    if ((cp = LYGetEnv("LYNX_TRACE_FILE")) == 0)
+	cp = FNAME_LYNX_TRACE;
+    LYTraceLogPath = typeMallocn(char, LY_MAXPATH);
+
+    LYAddPathToHome(LYTraceLogPath, LY_MAXPATH, cp);
+
+    /*
+     * Act on -version, -trace and -trace-mask NOW.
+     */
+    for (i = 1; i < argc; i++) {
+	parse_arg(&argv[i], 1, &i);
+    }
+    LYOpenTraceLog();
+
+#ifdef LY_FIND_LEAKS
+    /*
+     * Register the final function to be executed when being exited.  Will
+     * display memory leaks if the -find-leaks option is used.
+     */
+    atexit(LYLeaks);
+    /*
+     * Register the function which will free our allocated globals.
+     */
+    atexit(free_lynx_globals);
+#endif /* LY_FIND_LEAKS */
+
+    SetLocale();
+
+    /*
+     * Initialize our startup and global variables.
+     */
+#ifdef ULTRIX
+    /*
+     * Need this for Ultrix.
+     */
+    terminal = LYGetEnv("TERM");
+    if ((terminal == NULL) || !strncasecomp(terminal, "xterm", 5))
+	terminal = "vt100";
+#endif /* ULTRIX */
+    /*
+     * Zero the links and history struct arrays.
+     */
+    memset((void *) links, 0, sizeof(LinkInfo) * MAXLINKS);
+    LYAllocHistory(8);
+    /*
+     * Zero the MultiBookmark arrays.
+     */
+    memset((void *) MBM_A_subbookmark, 0, sizeof(char) * (MBM_V_MAXFILES + 1));
+    memset((void *) MBM_A_subdescript, 0, sizeof(char) * (MBM_V_MAXFILES + 1));
+
+#ifndef VMS
+    StrAllocCopy(list_format, LIST_FORMAT);
+#endif /* !VMS */
+    StrAllocCopy(ftp_format, FTP_FORMAT);
+
+    AlertSecs = SECS2Secs(ALERTSECS);
+    DebugSecs = SECS2Secs(DEBUGSECS);
+    InfoSecs = SECS2Secs(INFOSECS);
+    MessageSecs = SECS2Secs(MESSAGESECS);
+    ReplaySecs = SECS2Secs(REPLAYSECS);
+
+    StrAllocCopy(LYTransferName, "KiB");
+    StrAllocCopy(helpfile, HELPFILE);
+    StrAllocCopy(startfile, STARTFILE);
+    LYEscapeStartfile(&startfile);
+    StrAllocCopy(indexfile, DEFAULT_INDEX_FILE);
+    StrAllocCopy(global_type_map, GLOBAL_MAILCAP);
+    StrAllocCopy(personal_type_map, PERSONAL_MAILCAP);
+    StrAllocCopy(global_extension_map, GLOBAL_EXTENSION_MAP);
+    StrAllocCopy(personal_extension_map, PERSONAL_EXTENSION_MAP);
+    StrAllocCopy(language, PREFERRED_LANGUAGE);
+    StrAllocCopy(pref_charset, PREFERRED_CHARSET);
+    StrAllocCopy(system_mail, SYSTEM_MAIL);
+#ifdef SYSTEM_MAIL_FLAGS
+    StrAllocCopy(system_mail_flags, SYSTEM_MAIL_FLAGS);
+#else
+    StrAllocCopy(system_mail_flags, "");
+#endif
+
+    StrAllocCopy(LYUserAgent, LYNX_NAME);
+    StrAllocCat(LYUserAgent, "/");
+    StrAllocCat(LYUserAgent, LYNX_VERSION);
+    if (HTLibraryVersion) {
+	StrAllocCat(LYUserAgent, " libwww-FM/");
+	StrAllocCat(LYUserAgent, HTLibraryVersion);
+    }
+#ifdef USE_SSL
+    append_ssl_version(&LYUserAgent, "/");
+#endif /* USE_SSL */
+    StrAllocCopy(LYUserAgentDefault, LYUserAgent);
+
+#ifdef VMS
+    Define_VMSLogical("LYNX_VERSION", LYNX_VERSION);
+#else
+    StrAllocCopy(lynx_version_putenv_command, "LYNX_VERSION=");
+    StrAllocCat(lynx_version_putenv_command, LYNX_VERSION);
+    (void) putenv(lynx_version_putenv_command);
+    /* Note: you must not free the data passed to 'putenv()' until you give it
+     * a new value for that variable.
+     */
+#endif /* VMS */
+
+    if ((cp = LYGetEnv("LYNX_TEMP_SPACE")) != NULL)
+	StrAllocCopy(lynx_temp_space, cp);
+#if defined (UNIX) || defined (__DJGPP__)
+    else if ((cp = LYGetEnv("TMPDIR")) != NULL)
+	StrAllocCopy(lynx_temp_space, cp);
+#endif
+#if defined (DOSPATH) || defined (__EMX__)
+    else if ((cp = LYGetEnv("TEMP")) != NULL)
+	StrAllocCopy(lynx_temp_space, cp);
+    else if ((cp = LYGetEnv("TMP")) != NULL)
+	StrAllocCopy(lynx_temp_space, cp);
+#endif
+    else
+#ifdef TEMP_SPACE
+	StrAllocCopy(lynx_temp_space, TEMP_SPACE);
+#else
+    {
+	puts(gettext("You MUST define a valid TMP or TEMP area!"));
+	exit_immediately(EXIT_FAILURE);
+    }
+#endif
+
+#ifdef WIN_EX			/* for Windows 2000 ... 1999/08/23 (Mon) 08:24:35 */
+    if (access(lynx_temp_space, 0) != 0)
+#endif
+	LYTildeExpand(&lynx_temp_space, TRUE);
+
+    if ((cp = strstr(lynx_temp_space, "$USER")) != NULL) {
+	char *cp1;
+
+	if ((cp1 = LYGetEnv("USER")) != NULL) {
+	    *cp = '\0';
+	    StrAllocCopy(temp, lynx_temp_space);
+	    *cp = '$';
+	    StrAllocCat(temp, cp1);
+	    cp += 5;
+	    StrAllocCat(temp, cp);
+	    StrAllocCopy(lynx_temp_space, temp);
+	    FREE(temp);
+	}
+    }
+#ifdef VMS
+    LYLowerCase(lynx_temp_space);
+    if (strchr(lynx_temp_space, '/') != NULL) {
+	if (strlen(lynx_temp_space) == 1) {
+	    StrAllocCopy(lynx_temp_space, "sys$scratch:");
+	} else {
+	    LYAddPathSep(&lynx_temp_space);
+	    StrAllocCopy(temp, HTVMS_name("", lynx_temp_space));
+	    StrAllocCopy(lynx_temp_space, temp);
+	    FREE(temp);
+	}
+    }
+    if (strchr(lynx_temp_space, ':') == NULL &&
+	strchr(lynx_temp_space, ']') == NULL) {
+	StrAllocCat(lynx_temp_space, ":");
+    }
+#else
+    LYAddPathSep(&lynx_temp_space);
+    StrAllocCopy(lynx_temp_space, HTSYS_name(lynx_temp_space));
+#endif /* VMS */
+
+    if ((HTStat(lynx_temp_space, &dir_info) < 0
+#if defined(MULTI_USER_UNIX)
+	 && mkdir(lynx_temp_space, 0700) < 0
+#endif
+	)
+	|| !S_ISDIR(dir_info.st_mode)) {
+	fprintf(stderr, "%s: %s\n",
+		lynx_temp_space,
+		gettext("No such directory"));
+	exit_immediately(EXIT_FAILURE);
+    }
+#if USE_VMS_MAILER
+#ifndef MAIL_ADRS
+#define MAIL_ADRS "\"IN%%\"\"%s\"\"\""
+#endif
+    StrAllocCopy(mail_adrs, MAIL_ADRS);
+#endif
+
+#ifdef LYNX_HOST_NAME
+    StrAllocCopy(LYHostName, LYNX_HOST_NAME);
+#else
+    StrAllocCopy(LYHostName, HTHostName());
+#endif /* LYNX_HOST_NAME */
+
+    StrAllocCopy(LYLocalDomain, LOCAL_DOMAIN);
+    StrAllocCopy(URLDomainPrefixes, URL_DOMAIN_PREFIXES);
+    StrAllocCopy(URLDomainSuffixes, URL_DOMAIN_SUFFIXES);
+    StrAllocCopy(XLoadImageCommand, XLOADIMAGE_COMMAND);
+    StrAllocCopy(SSL_cert_file, SSL_CERT_FILE);
+
+#ifndef DISABLE_BIBP
+    StrAllocCopy(BibP_globalserver, BIBP_GLOBAL_SERVER);
+    StrAllocCopy(BibP_bibhost, "http://bibhost/");	/* protocol specified. */
+#endif
+
+    /*
+     * Disable news posting if the compilation-based LYNewsPosting value is
+     * FALSE.  This may be changed further down via lynx.cfg or the
+     * -restriction command line switch.  - FM
+     */
+#ifndef DISABLE_NEWS
+    no_newspost = (BOOL) (LYNewsPosting == FALSE);
+#endif
+
+    for (i = 1; i < argc; i++) {
+	parse_arg(&argv[i], 2, &i);
+    }
+
+    /*
+     * If we have a lone "-" switch for getting arguments from stdin, get them
+     * NOW, and act on the relevant ones, saving the others into an HTList for
+     * handling after the other initializations.  The primary purpose of this
+     * feature is to allow for the potentially very long command line that can
+     * be associated with post or get data.  The original implementation
+     * required that the lone "-" be the only command line argument, but that
+     * precluded its use when the lynx command is aliased with other arguments.
+     * When interactive, the stdin input is terminated by by Control-D on Unix
+     * or Control-Z on VMS, and each argument is terminated by a RETURN.  When
+     * the argument is -get_data or -post_data, the data are terminated by a
+     * "---" string, alone on the line (also terminated by RETURN).  - FM
+     */
+    for (i = 1; i < argc; i++) {
+	if (strcmp(argv[i], "-") == 0) {
+	    LYGetStdinArgs = TRUE;
+	    break;
+	}
+    }
+    if (LYGetStdinArgs == TRUE) {
+	char *buf = NULL;
+
+	CTRACE((tfp, "processing stdin arguments\n"));
+	while (GetStdin(&buf, TRUE)) {
+	    char *noargv[2];
+
+	    noargv[0] = buf;
+	    noargv[1] = NULL;
+	    LYTrimTrailing(buf);
+
+	    if (parse_arg(&noargv[0], 2, (int *) 0) == FALSE
+		&& buf[0] != '\0') {
+		char *argument = NULL;
+
+		if (LYStdinArgs == NULL) {
+		    LYStdinArgs = HTList_new();
+#ifdef LY_FIND_LEAKS
+		    atexit(LYStdinArgs_free);
+#endif
+		}
+		StrAllocCopy(argument, buf);
+		HTList_appendObject(LYStdinArgs, argument);
+		CTRACE((tfp, "...StdinArg:%s\n", argument));
+	    } else {
+		CTRACE((tfp, "...complete:%s\n", buf));
+	    }
+	}
+	CTRACE((tfp, "...done with stdin arguments\n"));
+	FREE(buf);
+    }
+#ifdef SOCKS
+    if (socks_flag)
+	SOCKSinit(argv[0]);
+#endif /* SOCKS */
+
+    /*
+     * If we had -validate set all of the restrictions and disallow a TRACE log
+     * NOW.  - FM
+     */
+    if (LYValidate == TRUE) {
+	parse_restrictions("all");
+	LYUseTraceLog = FALSE;
+    }
+
+    /*
+     * If we didn't get and act on a -validate or -anonymous switch, but can
+     * verify that this is the anonymous account, set the default restrictions
+     * for that account and disallow a TRACE log NOW.  - FM
+     */
+    if (!LYValidate && !LYRestricted &&
+	strlen(ANONYMOUS_USER) > 0 &&
+#if defined (VMS) || defined (NOUSERS)
+	!strcasecomp((LYGetEnv("USER") == NULL ? " " : LYGetEnv("USER")),
+		     ANONYMOUS_USER)
+#else
+#ifdef HAVE_CUSERID
+	STREQ((char *) cuserid((char *) NULL), ANONYMOUS_USER)
+#else
+	STREQ(((char *) getlogin() == NULL ? " " : getlogin()), ANONYMOUS_USER)
+#endif /* HAVE_CUSERID */
+#endif /* VMS */
+	) {
+	parse_restrictions("default");
+	LYRestricted = TRUE;
+	LYUseTraceLog = FALSE;
+    }
+#ifdef USE_CMD_LOGGING
+    /*
+     * Open command-script, if specified
+     */
+    if (lynx_cmd_script != 0) {
+	LYTildeExpand(&lynx_cmd_script, TRUE);
+	LYOpenCmdScript();
+    }
+    /*
+     * Open command-logging, if specified
+     */
+    if (lynx_cmd_logfile != 0) {
+	LYTildeExpand(&lynx_cmd_logfile, TRUE);
+	LYOpenCmdLogfile(argc, argv);
+    }
+#endif
+
+    /*
+     * Set up the default jump file stuff.  - FM
+     */
+    StrAllocCopy(jumpprompt, JUMP_PROMPT);
+#ifdef JUMPFILE
+    StrAllocCopy(jumpfile, JUMPFILE);
+    {
+	temp = NULL;
+	HTSprintf0(&temp, "JUMPFILE:%s", jumpfile);
+	if (!LYJumpInit(temp)) {
+	    CTRACE((tfp, "Failed to register %s\n", temp));
+	}
+	FREE(temp);
+    }
+#endif /* JUMPFILE */
+
+    /*
+     * If no alternate configuration file was specified on the command line,
+     * see if it's in the environment.
+     */
+    if (!lynx_cfg_file) {
+	if (((cp = LYGetEnv("LYNX_CFG")) != NULL) ||
+	    (cp = LYGetEnv("lynx_cfg")) != NULL)
+	    StrAllocCopy(lynx_cfg_file, cp);
+    }
+
+    /*
+     * If we still don't have a configuration file, use the userdefs.h
+     * definition.
+     */
+    if (!lynx_cfg_file)
+	StrAllocCopy(lynx_cfg_file, LYNX_CFG_FILE);
+
+#ifndef _WINDOWS		/* avoid the whole ~ thing for now */
+    LYTildeExpand(&lynx_cfg_file, FALSE);
+#endif
+
+    /*
+     * If the configuration file is not available, inform the user and exit.
+     */
+    if (!LYCanReadFile(lynx_cfg_file)) {
+	fprintf(stderr,
+		gettext("\nConfiguration file \"%s\" is not available.\n\n"),
+		lynx_cfg_file);
+	exit_immediately(EXIT_FAILURE);
+    }
+
+    /*
+     * Make sure we have the character sets declared.  This will initialize the
+     * CHARTRANS handling.  - KW
+     */
+    if (!LYCharSetsDeclared()) {
+	fprintf(stderr, gettext("\nLynx character sets not declared.\n\n"));
+	exit_immediately(EXIT_FAILURE);
+    }
+    /*
+     * (**) in Lynx, UCLYhndl_HTFile_for_unspec and UCLYhndl_for_unrec may be
+     * valid or not, but current_char_set and UCLYhndl_for_unspec SHOULD ALWAYS
+     * be a valid charset.  Initialized here and may be changed later from
+     * lynx.cfg/command_line/options_menu.  - LP (**)
+     */
+    /*
+     * Set up the compilation default character set.  - FM
+     */
+#ifdef CAN_AUTODETECT_DISPLAY_CHARSET
+    if (auto_display_charset >= 0)
+	current_char_set = auto_display_charset;
+    else
+#endif
+	current_char_set = safeUCGetLYhndl_byMIME(CHARACTER_SET);
+    /*
+     * Set up HTTP default for unlabeled charset (iso-8859-1).
+     */
+    UCLYhndl_for_unspec = LATIN1;
+    StrAllocCopy(UCAssume_MIMEcharset,
+		 LYCharSet_UC[UCLYhndl_for_unspec].MIMEname);
+
+    /*
+     * Make sure we have the edit map declared.  - FM
+     */
+    if (!LYEditmapDeclared()) {
+	fprintf(stderr, gettext("\nLynx edit map not declared.\n\n"));
+	exit_immediately(EXIT_FAILURE);
+    }
+#ifdef USE_COLOR_TABLE
+    /*
+     * Set up default foreground and background colors.
+     */
+    lynx_setup_colors();
+#endif /* USE_COLOR_TABLE */
+
+    /*
+     * Set the original directory, used for default download
+     */
+    if (!strcmp(Current_Dir(filename), ".")) {
+	if ((cp = LYGetEnv("PWD")) != 0)
+	    StrAllocCopy(original_dir, cp);
+    } else {
+	StrAllocCopy(original_dir, filename);
+    }
+
+    /*
+     * Set the compilation default signature file.  - FM
+     */
+    LYstrncpy(filename, LYNX_SIG_FILE, sizeof(filename) - 1);
+    if (LYPathOffHomeOK(filename, sizeof(filename))) {
+	StrAllocCopy(LynxSigFile, filename);
+	LYAddPathToHome(filename, sizeof(filename), LynxSigFile);
+	StrAllocCopy(LynxSigFile, filename);
+	CTRACE((tfp, "LYNX_SIG_FILE set to '%s'\n", LynxSigFile));
+    } else {
+	CTRACE((tfp, "LYNX_SIG_FILE '%s' is bad. Ignoring.\n", LYNX_SIG_FILE));
+    }
+
+#ifdef USE_PRETTYSRC
+    /*this is required for checking the tagspecs when parsing cfg file by
+       LYReadCFG.c:parse_html_src_spec -HV */
+    HTSwitchDTD(TRUE);
+#endif
+    /*
+     * Process the configuration file.
+     */
+    read_cfg(lynx_cfg_file, "main program", 1, (FILE *) 0);
+
+#if defined(USE_COLOR_STYLE)
+    /*
+     * A command-line "-lss" always overrides the config-file, even if it is
+     * an empty string such as -lss="".
+     */
+    if (lynx_lss_file2 != 0) {
+	FREE(lynx_lss_file);
+	lynx_lss_file = lynx_lss_file2;
+	lynx_lss_file2 = 0;
+    }
+
+    /*
+     * If no alternate lynx-style file was specified on the command line, see
+     * if it's in the environment.
+     */
+    if (!lynx_lss_file) {
+	if (((cp = LYGetEnv("LYNX_LSS")) != NULL) ||
+	    (cp = LYGetEnv("lynx_lss")) != NULL)
+	    StrAllocCopy(lynx_lss_file, cp);
+    }
+
+    /*
+     * If we still don't have a lynx-style file, use the userdefs.h definition.
+     */
+    if (!lynx_lss_file)
+	StrAllocCopy(lynx_lss_file, LYNX_LSS_FILE);
+
+    LYTildeExpand(&lynx_lss_file, TRUE);
+
+    /*
+     * If the lynx-style file is not available, inform the user and exit.
+     */
+    if (non_empty(lynx_lss_file) && !LYCanReadFile(lynx_lss_file)) {
+	fprintf(stderr, gettext("\nLynx file \"%s\" is not available.\n\n"),
+		lynx_lss_file);
+	exit_immediately(EXIT_FAILURE);
+    } else {
+	style_readFromFile(lynx_lss_file);
+    }
+#endif /* USE_COLOR_STYLE */
+
+    /*
+     * Process the RC file.
+     */
+    read_rc(NULL);
+
+#ifdef USE_LOCALE_CHARSET
+    LYFindLocaleCharset();
+#endif
+
+    /*
+     * Get WWW_HOME environment variable if it exists.
+     */
+    if ((cp = LYGetEnv("WWW_HOME")) != NULL) {
+	StrAllocCopy(startfile, cp);
+	LYEscapeStartfile(&startfile);
+    }
+
+    /*
+     * Set the LynxHome URL.  If it's a file URL and the
+     * host is defaulted, force in "//localhost", and if
+     * it's not an absolute URL, make it one. - FM
+     */
+    StrAllocCopy(LynxHome, startfile);
+    LYEnsureAbsoluteURL(&LynxHome, "LynxHome", FALSE);
+
+    /*
+     * Process any command line arguments not already handled.  - FM
+     * May set startfile as a side-effect.
+     */
+    for (i = 1; i < argc; i++) {
+	parse_arg(&argv[i], 4, &i);
+    }
+
+    /*
+     * Process any stdin-derived arguments for a lone "-" which we've loaded
+     * into LYStdinArgs.  - FM
+     */
+    if (LYStdinArgs != NULL) {
+	char *my_args[2];
+	HTList *cur = LYStdinArgs;
+
+	my_args[1] = NULL;
+	while (NULL != (my_args[0] = (char *) HTList_nextObject(cur))) {
+	    parse_arg(my_args, 4, (int *) 0);
+	}
+	LYStdinArgs_free();
+    }
+#ifdef HAVE_TTYNAME
+    /*
+     * If the input is not a tty, we are either running in cron, or are
+     * getting input via a pipe:
+     *
+     * a) in cron, none of stdin/stdout/stderr are tty's.
+     * b) from a pipe, we should have either "-" or "-stdin" options.
+     */
+    if (!LYGetStdinArgs
+	&& !startfile_stdin
+	&& !isatty(fileno(stdin))
+	&& (isatty(fileno(stdout) || isatty(fileno(stderr))))) {
+	int ignored = 0;
+
+	while (fgetc(stdin) != EOF) {
+	    ++ignored;
+	}
+	if (ignored) {
+	    fprintf(stderr,
+		    gettext("Ignored %d characters from standard input.\n"), ignored);
+	    fprintf(stderr,
+		    gettext("Use \"-stdin\" or \"-\" to tell how to handle piped input.\n"));
+	}
+    }
+#endif /* HAVE_TTYNAME */
+
+#ifdef CAN_SWITCH_DISPLAY_CHARSET
+    if (current_char_set == auto_display_charset)	/* Better: explicit option */
+	switch_display_charsets = 1;
+#endif
+
+#if defined (TTY_DEVICE) || defined(HAVE_TTYNAME)
+    /*
+     * If we are told to read the startfile from standard input, do it now,
+     * after we have read all of the option data from standard input.
+     * Later we'll use LYReopenInput().
+     */
+    if (startfile_stdin) {
+	char result[LY_MAXPATH];
+	char *buf = NULL;
+
+	CTRACE((tfp, "processing stdin startfile\n"));
+	if ((fp = LYOpenTemp(result, HTML_SUFFIX, "w")) != 0) {
+	    StrAllocCopy(startfile, result);
+	    while (GetStdin(&buf, FALSE)) {
+		fputs(buf, fp);
+		fputc('\n', fp);
+	    }
+	    FREE(buf);
+	    LYCloseTempFP(fp);
+	}
+	CTRACE((tfp, "...done stdin startfile\n"));
+    }
+#endif
+
+    /*
+     * Initialize other things based on the configuration read.
+     */
+
+#ifdef USE_PRETTYSRC
+    if ((!Old_DTD) != TRUE)	/* skip if they are already initialized -HV */
+#endif
+	HTSwitchDTD(!Old_DTD);
+
+    /*
+     * Set up the proper character set with the desired
+     * startup raw 8-bit or CJK mode handling.  - FM
+     */
+    HTMLUseCharacterSet(current_char_set);
+
+#ifdef USE_PERSISTENT_COOKIES
+    /*
+     * Sod it, this looks like a reasonable place to load the
+     * cookies file, probably.  - RP
+     *
+     * And to set LYCookieSaveFile. - BJP
+     */
+    if (persistent_cookies) {
+	if (LYCookieFile == NULL) {
+	    LYCookieFile = typeMallocn(char, LY_MAXPATH);
+
+	    LYAddPathToHome(LYCookieFile, LY_MAXPATH, FNAME_LYNX_COOKIES);
+	} else {
+	    LYTildeExpand(&LYCookieFile, FALSE);
+	}
+	LYLoadCookies(LYCookieFile);
+    }
+
+    /* tilde-expand LYCookieSaveFile */
+    if (LYCookieSaveFile != NULL) {
+	LYTildeExpand(&LYCookieSaveFile, FALSE);
+    }
+
+    /*
+     * In dump_output_immediately mode, LYCookieSaveFile defaults to
+     * /dev/null, otherwise it defaults to LYCookieFile.
+     */
+
+    if (LYCookieSaveFile == NULL) {
+	if (dump_output_immediately) {
+	    StrAllocCopy(LYCookieSaveFile, "/dev/null");
+	} else {
+	    StrAllocCopy(LYCookieSaveFile, LYCookieFile);
+	}
+    }
+#endif
+
+    /*
+     * Check for a help file URL in the environment. Overiding
+     * compiled-in default and configuration file setting, if found.
+     */
+    if ((cp = LYGetEnv("LYNX_HELPFILE")) != NULL)
+	StrAllocCopy(helpfile, cp);
+
+    /*
+     * Set up our help and about file base paths. - FM
+     */
+    StrAllocCopy(helpfilepath, helpfile);
+    if ((cp = LYPathLeaf(helpfilepath)) != helpfilepath)
+	*cp = '\0';
+    LYAddHtmlSep(&helpfilepath);
+
+    /*
+     * Check for a save space path in the environment.  If one was set in the
+     * configuration file, that one will be overridden.  - FM
+     */
+    if ((cp = LYGetEnv("LYNX_SAVE_SPACE")) != NULL)
+	StrAllocCopy(lynx_save_space, cp);
+
+    /*
+     * We have a save space path, make sure it's valid.  - FM
+     */
+    if (lynx_save_space && *lynx_save_space == '\0') {
+	FREE(lynx_save_space);
+    }
+    if (lynx_save_space) {
+	LYTildeExpand(&lynx_save_space, TRUE);
+#ifdef VMS
+	LYLowerCase(lynx_save_space);
+	if (strchr(lynx_save_space, '/') != NULL) {
+	    if (strlen(lynx_save_space) == 1) {
+		StrAllocCopy(lynx_save_space, "sys$login:");
+	    } else {
+		LYAddPathSep(&lynx_save_space);
+		StrAllocCopy(temp, HTVMS_name("", lynx_save_space));
+		StrAllocCopy(lynx_save_space, temp);
+		FREE(temp);
+	    }
+	}
+	if (strchr(lynx_save_space, ':') == NULL &&
+	    strchr(lynx_save_space, ']') == NULL) {
+	    StrAllocCat(lynx_save_space, ":");
+	}
+#else
+	LYAddPathSep(&lynx_save_space);
+#endif /* VMS */
+    }
+
+    /*
+     * Set up the file extension and mime type maps from src/HTInit.c and the
+     * global and personal mime.types and mailcap files.  These will override
+     * any SUFFIX or VIEWER maps in userdefs.h or the configuration file, if
+     * they overlap.
+     */
+    HTFormatInit();
+    if (!FileInitAlreadyDone)
+	HTFileInit();
+
+    if (!LYCheckUserAgent()) {
+	HTAlwaysAlert(gettext("Warning:"), UA_NO_LYNX_WARNING);
+    }
+    if (show_cfg) {
+	cleanup();
+	exit_immediately(EXIT_SUCCESS);
+    }
+#ifdef USE_SLANG
+    if (LYShowColor >= SHOW_COLOR_ON &&
+	!(Lynx_Color_Flags & SL_LYNX_USE_COLOR)) {
+	Lynx_Color_Flags |= SL_LYNX_USE_COLOR;
+    } else if ((Lynx_Color_Flags & SL_LYNX_USE_COLOR) ||
+	       LYGetEnv("COLORTERM") != NULL) {
+	if (LYShowColor != SHOW_COLOR_NEVER &&
+	    LYShowColor != SHOW_COLOR_ALWAYS) {
+	    LYShowColor = SHOW_COLOR_ON;
+	}
+    }
+#endif /* USE_SLANG */
+
+    if (LYPreparsedSource) {
+	HTPreparsedFormatInit();
+    }
+#if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)
+#ifdef NEVER_ALLOW_REMOTE_EXEC
+    if (local_exec) {
+	local_exec = FALSE;
+	local_exec_on_local_files = TRUE;
+    }
+#endif /* NEVER_ALLOW_REMOTE_EXEC */
+#endif /* EXEC_LINKS || EXEC_SCRIPTS */
+
+    if (emacs_keys)
+	set_emacs_keys();
+
+    if (vi_keys)
+	set_vi_keys();
+
+    if (no_numbers) {
+	number_links = FALSE;
+	number_fields = FALSE;
+	keypad_mode = NUMBERS_AS_ARROWS;
+	set_numbers_as_arrows();
+    }
+
+    if (crawl) {
+	/* No numbered links by default, as documented
+	   in CRAWL.announce. - kw */
+	if (!number_links) {
+	    keypad_mode = NUMBERS_AS_ARROWS;
+	}
+    }
+
+    if (!links_are_numbered()) {
+	if (number_fields)
+	    keypad_mode = LINKS_AND_FIELDS_ARE_NUMBERED;
+	if (number_links)
+	    keypad_mode = LINKS_ARE_NUMBERED;
+	set_numbers_as_arrows();
+    }
+
+    /*
+     * Check the -popup command line toggle.  - FM
+     */
+    if (LYUseDefSelPop == FALSE) {
+	LYSelectPopups = (BOOLEAN) !LYSelectPopups;
+    }
+
+    /*
+     * Check the -show_cursor command line toggle.  - FM
+     */
+    if (LYUseDefShoCur == FALSE) {
+	LYShowCursor = (BOOLEAN) !LYShowCursor;
+    }
+
+    /*
+     * Check the -base command line switch with -source.  - FM
+     */
+    if (LYPrependBase && HTOutputFormat == HTAtom_for("www/download")) {
+	LYPrependBaseToSource = TRUE;
+    }
+
+    /*
+     * Disable multiple bookmark support if not interactive, so it doesn't
+     * crash on curses functions, or if the support was blocked via userdefs.h
+     * and/or lynx.cfg, or via command line restrictions.  - FM
+     */
+    if (no_multibook)
+	LYMBMBlocked = TRUE;
+    if (dump_output_immediately || LYMBMBlocked || no_multibook) {
+	LYMultiBookmarks = MBM_OFF;
+	LYMBMBlocked = TRUE;
+	no_multibook = TRUE;
+    }
+#ifdef USE_SOURCE_CACHE
+    /*
+     * Disable source caching if not interactive.
+     */
+    if (dump_output_immediately)
+	LYCacheSource = SOURCE_CACHE_NONE;
+#endif
+#ifdef DISP_PARTIAL
+    /*
+     * Disable partial mode if not interactive.
+     */
+    if (dump_output_immediately)
+	display_partial_flag = FALSE;
+#endif
+
+#ifdef VMS
+    set_vms_keys();
+#endif /* VMS */
+
+#if defined (__DJGPP__)
+    if (watt_debug)
+	dbug_init();
+    sock_init();
+
+    __system_flags =
+	__system_emulate_chdir |	/* handle `cd' internally */
+	__system_handle_null_commands |		/* ignore cmds with no effect */
+	__system_allow_long_cmds |	/* handle commands > 126 chars   */
+	__system_use_shell |	/* use $SHELL if set */
+	__system_allow_multiple_cmds |	/* allow `cmd1; cmd2; ...' */
+	__system_redirect;	/* redirect internally */
+
+    /* This speeds up stat() tremendously */
+    _djstat_flags |= _STAT_INODE | _STAT_EXEC_MAGIC | _STAT_DIRSIZE;
+#endif /* __DJGPP__ */
+
+    /* trap interrupts */
+#ifdef WIN32
+    SetConsoleCtrlHandler((PHANDLER_ROUTINE) cleanup_win32, TRUE);
+#endif
+
+#ifndef NOSIGHUP
+    if (!dump_output_immediately)
+	(void) signal(SIGHUP, cleanup_sig);
+#endif /* NOSIGHUP */
+
+    (void) signal(SIGTERM, cleanup_sig);
+#ifdef SIGWINCH
+    LYExtSignal(SIGWINCH, size_change);
+#endif /* SIGWINCH */
+#ifndef VMS
+    if (!TRACE && !dump_output_immediately && !stack_dump) {
+	(void) signal(SIGINT, cleanup_sig);
+#ifndef __linux__
+#ifdef SIGBUS
+	(void) signal(SIGBUS, FatalProblem);
+#endif /* SIGBUS */
+#endif /* !__linux__ */
+	(void) signal(SIGSEGV, FatalProblem);
+	(void) signal(SIGILL, FatalProblem);
+	/*
+	 * Since we're doing lots of TCP, just ignore SIGPIPE altogether.
+	 *
+	 * HTTCP.c should deal with a broken pipe for servers.  Rick Mallet's
+	 * check after c = GetChar() in LYStrings.c should deal with a
+	 * disconnected terminal.  So the runaway CPU time problem on Unix
+	 * should not occur any more.
+	 */
+#ifdef SIGPIPE
+	if (signal(SIGPIPE, SIG_IGN) != SIG_IGN)
+	    restore_sigpipe_for_children = TRUE;
+#endif /* SIGPIPE */
+    }
+#endif /* !VMS */
+
+#ifdef SIGTSTP
+    /*
+     * Block Control-Z suspending if requested.  - FM
+     */
+    if (no_suspend)
+	(void) signal(SIGTSTP, SIG_IGN);
+#endif /* SIGTSTP */
+
+    /*
+     * Check for a valid HEAD request.  - FM
+     */
+    if (HEAD_request && LYCanDoHEAD(startfile) != TRUE) {
+	fprintf(stderr,
+		"The '-head' switch is for http HEAD requests and cannot be used for\n'%s'.\n",
+		startfile);
+	exit_immediately(EXIT_FAILURE);
+    }
+
+    /*
+     * Check for a valid MIME headers request.  - FM
+     */
+    if (keep_mime_headers && LYCanDoHEAD(startfile) != TRUE) {
+	fprintf(stderr,
+		"The '-mime_header' switch is for http URLs and cannot be used for\n'%s'.\n",
+		startfile);
+	exit_immediately(EXIT_FAILURE);
+    }
+
+    /*
+     * Check for a valid traversal request.  - FM
+     */
+    if (traversal && strncmp(startfile, "http", 4)) {
+	fprintf(stderr,
+		"The '-traversal' switch is for http URLs and cannot be used for\n'%s'.\n",
+		startfile);
+	exit_immediately(EXIT_FAILURE);
+    }
+
+    /*
+     * Finish setting up for an INTERACTIVE session.  Done here so that URL
+     * guessing in LYEnsureAbsoluteURL() can be interruptible (terminal is in
+     * raw mode, select() works).  -BL
+     */
+#ifdef USE_PRETTYSRC
+    if (!dump_output_immediately) {
+	HTMLSRC_init_caches(FALSE);	/* do it before terminal is initialized */
+#ifdef LY_FIND_LEAKS
+	atexit(html_src_clean_data);
+#endif
+    }
+#endif
+
+    if (!dump_output_immediately) {
+	setup(terminal);
+    }
+    /*
+     * If startfile is a file URL and the host is defaulted, force in
+     * "//localhost", and if it's not an absolute URL, make it one.  - FM
+     */
+    LYEnsureAbsoluteURL(&startfile, "STARTFILE", FALSE);
+
+    /*
+     * If homepage was specified and is a file URL with the host defaulted,
+     * force in "//localhost", and if it's not an absolute URL, make it one.  -
+     * FM
+     */
+    if (homepage) {
+	LYEnsureAbsoluteURL(&homepage, "HOMEPAGE", FALSE);
+    }
+
+    /*
+     * If we don't have a homepage specified, set it to startfile.  Otherwise,
+     * reset LynxHome.  - FM
+     */
+    if (isEmpty(homepage)) {
+	StrAllocCopy(homepage, startfile);
+    } else {
+	StrAllocCopy(LynxHome, homepage);
+    }
+
+    /*
+     * Set up the inside/outside domain restriction flags.  - FM
+     */
+    if (inlocaldomain()) {
+#if !defined(HAVE_UTMP) || defined(VMS)		/* not selective */
+	telnet_ok = (BOOL) (!no_inside_telnet && !no_outside_telnet && telnet_ok);
+#ifndef DISABLE_NEWS
+	news_ok = (BOOL) (!no_inside_news && !no_outside_news && news_ok);
+#endif
+	ftp_ok = (BOOL) (!no_inside_ftp && !no_outside_ftp && ftp_ok);
+	rlogin_ok = (BOOL) (!no_inside_rlogin && !no_outside_rlogin && rlogin_ok);
+#else
+	CTRACE((tfp, "LYMain: User in Local domain\n"));
+	telnet_ok = (BOOL) (!no_inside_telnet && telnet_ok);
+#ifndef DISABLE_NEWS
+	news_ok = (BOOL) (!no_inside_news && news_ok);
+#endif
+	ftp_ok = (BOOL) (!no_inside_ftp && ftp_ok);
+	rlogin_ok = (BOOL) (!no_inside_rlogin && rlogin_ok);
+#endif /* !HAVE_UTMP || VMS */
+    } else {
+	CTRACE((tfp, "LYMain: User in REMOTE domain\n"));
+	telnet_ok = (BOOL) (!no_outside_telnet && telnet_ok);
+#ifndef DISABLE_NEWS
+	news_ok = (BOOL) (!no_outside_news && news_ok);
+#endif
+	ftp_ok = (BOOL) (!no_outside_ftp && ftp_ok);
+	rlogin_ok = (BOOL) (!no_outside_rlogin && rlogin_ok);
+    }
+#ifdef DISABLE_FTP
+    ftp_ok = FALSE;
+#else
+    /* predefine some known broken ftp servers */
+    LYSetConfigValue(RC_BROKEN_FTP_RETR, "ProFTPD 1.2.5");
+    LYSetConfigValue(RC_BROKEN_FTP_RETR, "spftp/");
+    LYSetConfigValue(RC_BROKEN_FTP_EPSV, "(Version wu-2.6.2-12)");
+#endif
+
+    /*
+     * Make sure our bookmark default strings are all allocated and
+     * synchronized.  - FM
+     */
+    if (isEmpty(bookmark_page)) {
+	temp = NULL;
+	HTSprintf0(&temp, "lynx_bookmarks%s", HTML_SUFFIX);
+	set_default_bookmark_page(temp);
+	FREE(temp);
+    }
+    if (isEmpty(BookmarkPage)) {
+	set_default_bookmark_page(bookmark_page);
+    }
+#if defined(SYSLOG_REQUESTED_URLS)
+    LYOpenlog(syslog_txt);
+#endif
+
+    if (non_empty(x_display)) {
+	LYisConfiguredForX = TRUE;
+    }
+
+    /*
+     * Here's where we do all the work.
+     */
+    if (dump_output_immediately) {
+	/*
+	 * Finish setting up and start a NON-INTERACTIVE session.  - FM
+	 */
+	if (crawl && !number_links && !number_fields) {
+	    keypad_mode = NUMBERS_AS_ARROWS;
+	} else if (no_numbers) {
+	    keypad_mode = NUMBERS_AS_ARROWS;
+	} else if (!no_list) {
+	    if (!links_are_numbered()) {
+		if (number_fields)
+		    keypad_mode = LINKS_AND_FIELDS_ARE_NUMBERED;
+		else
+		    keypad_mode = LINKS_ARE_NUMBERED;
+	    }
+	}
+	if (dump_output_width > 0) {
+	    LYcols = dump_output_width;
+	}
+	/*
+	 * Normal argument processing puts non-options (URLs) into the Goto
+	 * history.  Use this to dump all of the pages listed on the command
+	 * line, or (if none are listed) via the startfile mechanism.
+	 * history.
+	 */
+#ifdef EXTENDED_STARTFILE_RECALL
+	HTAddGotoURL(startfile);
+	for (i = HTList_count(Goto_URLs) - 1; i >= 0; --i) {
+	    StrAllocCopy(startfile, (char *) HTList_objectAt(Goto_URLs, i));
+	    CTRACE((tfp, "dumping %d:%d %s\n",
+		    i + 1, HTList_count(Goto_URLs), startfile));
+	    status = mainloop();
+	    if (!no_list &&
+		!crawl)		/* For -crawl it has already been done! */
+		printlist(stdout, FALSE);
+	    if (i != 0)
+		printf("\n");
+	}
+#else
+	status = mainloop();
+	if (!no_list &&
+	    !crawl &&		/* For -crawl it has already been done! */
+	    links_are_numbered())
+	    printlist(stdout, FALSE);
+#endif
+#ifdef USE_PERSISTENT_COOKIES
+	/*
+	 * We want to save cookies picked up when in immediate dump mode.
+	 * Instead of calling cleanup() here, let's only call this one.  - BJP
+	 */
+	if (persistent_cookies)
+	    LYStoreCookies(LYCookieSaveFile);
+#endif /* USE_PERSISTENT_COOKIES */
+	exit_immediately(status);
+    } else {
+	/*
+	 * Start an INTERACTIVE session.  - FM
+	 */
+#ifdef USE_COLOR_STYLE
+	cache_tag_styles();
+#endif
+
+#ifndef NO_DUMP_WITH_BACKSPACES
+	if (with_backspaces) {
+	    /* we should warn about this somehow (nop for now) -VH */
+	    with_backspaces = FALSE;
+	}
+#endif
+
+#ifndef ALL_CHARSETS_IN_O_MENU_SCREEN
+	init_charset_subsets();
+#endif
+
+	ena_csi((BOOLEAN) (LYlowest_eightbit[current_char_set] > 155));
+#ifdef USE_SESSIONS
+	RestoreSession();
+#endif /* USE_SESSIONS */
+	status = mainloop();
+	LYCloseCloset(RECALL_URL);
+	LYCloseCloset(RECALL_MAIL);
+#if defined(PDCURSES) && defined(PDC_BUILD) && PDC_BUILD >= 2401
+	if (!isendwin()) {
+	    if ((saved_scrsize_x != 0) && (saved_scrsize_y != 0)) {
+		resize_term(saved_scrsize_y, saved_scrsize_x);
+	    }
+	}
+#endif
+	cleanup();
+	exit_immediately(status);
+    }
+
+    return (status);		/* though redundant, for compiler-warnings */
+}
+
+/*
+ * Called by HTAccessInit to register any protocols supported by lynx.
+ * Protocols added by lynx:
+ *    LYNXKEYMAP, lynxcgi, LYNXIMGMAP, LYNXCOOKIE, LYNXCACHE, LYNXMESSAGES
+ */
+#ifdef GLOBALREF_IS_MACRO
+extern GLOBALREF (HTProtocol, LYLynxKeymap);
+extern GLOBALREF (HTProtocol, LYLynxCGI);
+extern GLOBALREF (HTProtocol, LYLynxIMGmap);
+extern GLOBALREF (HTProtocol, LYLynxCookies);
+
+#ifdef USE_CACHEJAR
+extern GLOBALREF (HTProtocol, LYLynxCache);
+#endif
+extern GLOBALREF (HTProtocol, LYLynxStatusMessages);
+
+#else
+GLOBALREF HTProtocol LYLynxKeymap;
+GLOBALREF HTProtocol LYLynxCGI;
+GLOBALREF HTProtocol LYLynxIMGmap;
+GLOBALREF HTProtocol LYLynxCookies;
+
+#ifdef USE_CACHEJAR
+GLOBALREF HTProtocol LYLynxCache;
+#endif
+GLOBALREF HTProtocol LYLynxStatusMessages;
+#endif /* GLOBALREF_IS_MACRO */
+
+void LYRegisterLynxProtocols(void)
+{
+    HTRegisterProtocol(&LYLynxKeymap);
+    HTRegisterProtocol(&LYLynxCGI);
+    HTRegisterProtocol(&LYLynxIMGmap);
+    HTRegisterProtocol(&LYLynxCookies);
+#ifdef USE_CACHEJAR
+    HTRegisterProtocol(&LYLynxCache);
+#endif
+    HTRegisterProtocol(&LYLynxStatusMessages);
+}
+
+#ifndef NO_CONFIG_INFO
+/*
+ * Some stuff to reload lynx.cfg without restarting new lynx session, also load
+ * options menu items and command-line options to make things consistent.
+ *
+ * Called by user of interactive session by LYNXCFG://reload/ link.
+ *
+ * Warning:  experimental, more main() reorganization required.
+ *	*Known* exceptions: persistent cookies, cookie files.
+ *
+ *	Some aspects of COLOR (with slang?).
+ *	Viewer stuff, mailcap files
+ *	SUFFIX, mime.types files
+ *	RULESFILE/RULE
+ *
+ *	All work "somewhat", but not exactly as the first time.
+ */
+void reload_read_cfg(void)
+{
+    char *tempfile;
+    FILE *rcfp;
+
+    /*
+     * no_option_save is always set for -anonymous and -validate.  It is better
+     * to check for one or several specific restriction flags than for
+     * 'LYRestricted', which doesn't get set for individual restrictions or for
+     * -validate!  However, no_option_save may not be the appropriate one to
+     * check - in that case, a new no_something should be added that gets
+     * automatically set for -anonymous and -validate (and whether it applies
+     * for -anonymous can be made installer- configurable in the usual way at
+     * the bottom of userdefs.h).  - kw
+     *
+     */
+    if (no_option_save) {
+	/* current logic requires(?) that saving user preferences is
+	   possible.  Additional applicable restrictions are already
+	   checked by caller. - kw */
+	return;
+    }
+
+    /*
+     * Current user preferences are saved in a temporary file, to be read in
+     * again after lynx.cfg has been read.  This avoids accidental changing of
+     * the preferences file.  The regular preferences file doesn't even need to
+     * exist, and won't be created as a side effect of this function.  Honoring
+     * the no_option_save restriction may thus be unnecessarily restrictive,
+     * but the check is currently still left in place.  - kw
+     */
+    tempfile = typecallocn(char, LY_MAXPATH);
+    if (!tempfile) {
+	HTAlwaysAlert(NULL, NOT_ENOUGH_MEMORY);
+	return;
+    }
+    rcfp = LYOpenTemp(tempfile, ".rc", "w");
+    if (rcfp == NULL) {
+	FREE(tempfile);
+	HTAlwaysAlert(NULL, CANNOT_OPEN_TEMP);
+	return;
+    }
+    if (!save_rc(rcfp)) {
+	HTAlwaysAlert(NULL, OPTIONS_NOT_SAVED);
+	LYRemoveTemp(tempfile);
+	FREE(tempfile);
+	return;			/* can not write the very own file :( */
+    } {
+	/* set few safe flags: */
+#ifdef USE_PERSISTENT_COOKIES
+	BOOLEAN persistent_cookies_flag = persistent_cookies;
+	char *LYCookieFile_flag = NULL;
+	char *LYCookieSaveFile_flag = NULL;
+
+	if (persistent_cookies) {
+	    StrAllocCopy(LYCookieFile_flag, LYCookieFile);
+	    StrAllocCopy(LYCookieSaveFile_flag, LYCookieSaveFile);
+	}
+#endif
+
+#ifdef USE_CHARSET_CHOICE
+	custom_assumed_doc_charset = FALSE;
+	custom_display_charset = FALSE;
+	memset((char *) charset_subsets, 0, sizeof(charset_subset_t) * MAXCHARSETS);
+#endif
+
+#ifdef USE_PRETTYSRC
+	html_src_on_lynxcfg_reload();
+#endif
+	/* free downloaders, printers, environments, dired menu */
+	free_lynx_cfg();
+#ifdef USE_SOURCE_CACHE
+	source_cache_file_error = FALSE;	/* reset flag */
+#endif
+
+	/*
+	 * Process the configuration file.
+	 */
+	read_cfg(lynx_cfg_file, "main program", 1, (FILE *) 0);
+
+	/*
+	 * Process the temporary RC file.
+	 */
+	rcfp = fopen(tempfile, "r");
+	read_rc(rcfp);
+	LYRemoveTemp(tempfile);
+	FREE(tempfile);		/* done with it - kw */
+
+#ifdef USE_CHARSET_CHOICE
+	init_charset_subsets();
+#endif
+
+	/*
+	 * Initialize other things based on the configuration read.
+	 */
+	LYSetDisplayLines();
+	/* Not implemented yet here,
+	 * a major problem: file paths
+	 * like lynx_save_space, LYCookieFile etc.
+	 */
+#ifdef USE_PERSISTENT_COOKIES
+	/* restore old settings */
+	if (persistent_cookies != persistent_cookies_flag) {
+	    persistent_cookies = persistent_cookies_flag;
+	    HTAlert(gettext("persistent cookies state will be changed in next session only."));
+	}
+	if (persistent_cookies) {
+	    if (strcmp(LYCookieFile, LYCookieFile_flag)) {
+		StrAllocCopy(LYCookieFile, LYCookieFile_flag);
+		CTRACE((tfp,
+			"cookie file can be changed in next session only, restored.\n"));
+	    }
+	    if (strcmp(LYCookieSaveFile, LYCookieSaveFile_flag)) {
+		StrAllocCopy(LYCookieSaveFile, LYCookieSaveFile_flag);
+		CTRACE((tfp,
+			"cookie save file can be changed in next session only, restored.\n"));
+	    }
+	    FREE(LYCookieFile_flag);
+	    FREE(LYCookieSaveFile_flag);
+	}
+#endif
+
+    }
+}
+#endif /* !NO_CONFIG_INFO */
+
+static void force_dump_mode(void)
+{
+    dump_output_immediately = TRUE;
+    no_pause = TRUE;
+    LYcols = DFT_COLS;
+}
+
+/* There are different ways of setting arguments on the command line, and
+ * there are different types of arguments.  These include:
+ *
+ *   -set_some_variable		 ==> some_variable  = TRUE
+ *   -toggle_some_variable	 ==> some_variable = !some_variable
+ *   -some_variable=value	 ==> some_variable = value
+ *
+ * Others are complicated and require a function call.
+ */
+
+#define PARSE_SET(n,t,v,h) {n,    t, UNION_SET(v), h}
+#define PARSE_INT(n,t,v,h) {n,    t, UNION_INT(v), h}
+#define PARSE_STR(n,t,v,h) {n,    t, UNION_STR(v), h}
+#define PARSE_FUN(n,t,v,h) {n,    t, UNION_FUN(v), h}
+#define PARSE_NIL          {NULL, 0, UNION_DEF(0), NULL}
+
+typedef struct parse_args_type {
+    const char *name;
+    int type;
+
+#define TOGGLE_ARG		0x0010
+#define SET_ARG			0x0020
+#define UNSET_ARG		0x0030
+#define FUNCTION_ARG		0x0040
+#define LYSTRING_ARG		0x0050
+#define INT_ARG			0x0060
+#define STRING_ARG		0x0070
+#define TIME_ARG		0x0080
+#define ARG_TYPE_MASK		0x0FF0
+#define NEED_NEXT_ARG		0x1000
+
+#define NEED_INT_ARG		(NEED_NEXT_ARG | INT_ARG)
+#define NEED_TIME_ARG		(NEED_NEXT_ARG | TIME_ARG)
+#define NEED_LYSTRING_ARG	(NEED_NEXT_ARG | LYSTRING_ARG)
+#define NEED_STRING_ARG		(NEED_NEXT_ARG | STRING_ARG)
+#define NEED_FUNCTION_ARG	(NEED_NEXT_ARG | FUNCTION_ARG)
+
+    /* If the NEED_NEXT_ARG flags is set, and the option was not specified
+     * with an '=' character, then use the next argument in the argv list.
+     */
+
+      ParseData;
+    const char *help_string;
+} Config_Type;
+
+/* -auth, -pauth */
+static int parse_authentication(char *next_arg,
+				char **result)
+{
+    /*
+     * Authentication information for protected documents.
+     */
+    char *auth_info = 0;
+
+    if (next_arg != 0) {
+	StrAllocCopy(auth_info, next_arg);
+	memset(next_arg, ' ', strlen(next_arg));	/* Let's not show too much */
+    }
+
+    if (auth_info != 0) {
+	char *cp;
+
+	if ((cp = strchr(auth_info, ':')) != 0) {	/* Pw */
+	    *cp++ = '\0';	/* Terminate ID */
+	    HTUnEscape(cp);
+	    StrAllocCopy(result[1], cp);
+	}
+	if (*auth_info) {	/* Id */
+	    HTUnEscape(auth_info);
+	    StrAllocCopy(result[0], auth_info);
+	}
+	FREE(auth_info);
+    }
+    return 0;
+}
+
+/* -anonymous */
+static int anonymous_fun(char *next_arg GCC_UNUSED)
+{
+    if (!LYValidate && !LYRestricted)
+	parse_restrictions("default");
+    LYRestricted = TRUE;
+    return 0;
+}
+
+/* -assume_charset */
+static int assume_charset_fun(char *next_arg)
+{
+    UCLYhndl_for_unspec = safeUCGetLYhndl_byMIME(next_arg);
+    StrAllocCopy(UCAssume_MIMEcharset,
+		 LYCharSet_UC[UCLYhndl_for_unspec].MIMEname);
+/*	   this may be a memory for bogus typo -
+    StrAllocCopy(UCAssume_MIMEcharset, next_arg);
+    LYLowerCase(UCAssume_MIMEcharset);   */
+
+    return 0;
+}
+
+/* -assume_local_charset */
+static int assume_local_charset_fun(char *next_arg)
+{
+    UCLYhndl_HTFile_for_unspec = safeUCGetLYhndl_byMIME(next_arg);
+    return 0;
+}
+
+/* -assume_unrec_charset */
+static int assume_unrec_charset_fun(char *next_arg)
+{
+    UCLYhndl_for_unrec = safeUCGetLYhndl_byMIME(next_arg);
+    return 0;
+}
+
+/* -auth */
+static int auth_fun(char *next_arg)
+{
+    parse_authentication(next_arg, authentication_info);
+    return 0;
+}
+
+/* -base */
+static int base_fun(char *next_arg GCC_UNUSED)
+{
+    /*
+     * Treat -source equivalently to an interactive download with
+     * LYPrefixBaseToSource configured to TRUE, so that a BASE tag is prepended
+     * for text/html content types.  We normally treat the module-wide global
+     * LYPrefixBaseToSource flag as FALSE with -source, but force it TRUE,
+     * later, if LYPrependBase is set TRUE here.  - FM
+     */
+    LYPrependBase = TRUE;
+    if (HTOutputFormat == HTAtom_for("www/dump"))
+	HTOutputFormat = HTAtom_for("www/download");
+
+    return 0;
+}
+
+/* -cache */
+static int cache_fun(char *next_arg)
+{
+    if (next_arg != 0)
+	HTCacheSize = atoi(next_arg);
+    /*
+     * Limit size.
+     */
+    if (HTCacheSize < 2)
+	HTCacheSize = 2;
+
+    return 0;
+}
+
+/* -child */
+static int child_fun(char *next_arg GCC_UNUSED)
+{
+    child_lynx = TRUE;
+    no_disk_save = TRUE;
+    no_mail = TRUE;
+    return 0;
+}
+
+/* -child_relaxed */
+static int child_relaxed_fun(char *next_arg GCC_UNUSED)
+{
+    child_lynx = TRUE;
+    return 0;
+}
+
+#ifdef USE_SLANG
+/* -color */
+static int color_fun(char *next_arg GCC_UNUSED)
+{
+    Lynx_Color_Flags |= SL_LYNX_USE_COLOR;
+
+    if (LYShowColor != SHOW_COLOR_ALWAYS)
+	LYShowColor = SHOW_COLOR_ON;
+
+    return 0;
+}
+#endif
+
+#ifdef MISC_EXP
+/* -convert_to */
+static int convert_to_fun(char *next_arg)
+{
+    if (next_arg != 0) {
+	char *outformat = NULL;
+	char *cp1, *cp2, *cp4;
+	int chndl;
+
+	StrAllocCopy(outformat, next_arg);
+	/* not lowercased, to allow for experimentation - kw */
+	/*LYLowerCase(outformat); */
+	if ((cp1 = strchr(outformat, ';')) != NULL) {
+	    if ((cp2 = LYstrstr(cp1, "charset")) != NULL) {
+		cp2 += 7;
+		while (*cp2 == ' ' || *cp2 == '=' || *cp2 == '"')
+		    cp2++;
+		for (cp4 = cp2; (*cp4 != '\0' && *cp4 != '"' &&
+				 *cp4 != ';' &&
+				 !WHITE(*cp4)); cp4++) ;	/* do nothing */
+		*cp4 = '\0';
+		/* This is intentionally not the "safe" version,
+		   to allow for experimentation. */
+		chndl = UCGetLYhndl_byMIME(cp2);
+		if (chndl < 0)
+		    chndl = UCLYhndl_for_unrec;
+		if (chndl < 0) {
+		    fprintf(stderr,
+			    gettext("Lynx: ignoring unrecognized charset=%s\n"), cp2);
+		} else {
+		    current_char_set = chndl;
+		}
+		*cp1 = '\0';	/* truncate outformat */
+	    }
+	}
+	HTOutputFormat = HTAtom_for(outformat);
+	FREE(outformat);
+    } else {
+	HTOutputFormat = NULL;
+    }
+    return 0;
+}
+#endif
+
+/* -crawl */
+static int crawl_fun(char *next_arg GCC_UNUSED)
+{
+    crawl = TRUE;
+    LYcols = DFT_COLS;
+    return 0;
+}
+
+/* -display */
+static int display_fun(char *next_arg)
+{
+    if (next_arg != 0) {
+	LYsetXDisplay(next_arg);
+    }
+
+    return 0;
+}
+
+/* -display_charset */
+static int display_charset_fun(char *next_arg)
+{
+    int i = UCGetLYhndl_byMIME(next_arg);
+
+#ifdef CAN_AUTODETECT_DISPLAY_CHARSET
+    if (i < 0 && !strcasecomp(next_arg, "auto"))
+	i = auto_display_charset;
+#endif
+    if (i < 0) {		/* do nothing here: so fallback to lynx.cfg */
+	fprintf(stderr,
+		gettext("Lynx: ignoring unrecognized charset=%s\n"), next_arg);
+    } else
+	current_char_set = i;
+    return 0;
+}
+
+/* -dump */
+static int dump_output_fun(char *next_arg GCC_UNUSED)
+{
+    force_dump_mode();
+    return 0;
+}
+
+/* -editor */
+static int editor_fun(char *next_arg)
+{
+    if (next_arg != 0)
+	StrAllocCopy(editor, next_arg);
+    system_editor = TRUE;
+    return 0;
+}
+
+/* -error_file */
+static int error_file_fun(char *next_arg)
+{
+    /*
+     * Output return (success/failure) code of an HTTP transaction.
+     */
+    if (next_arg != 0)
+	http_error_file = next_arg;
+    return 0;
+}
+
+#if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)
+/* -exec */
+static int exec_fun(char *next_arg GCC_UNUSED)
+{
+#ifndef NEVER_ALLOW_REMOTE_EXEC
+    local_exec = TRUE;
+#else
+    local_exec_on_local_files = TRUE;
+#endif /* NEVER_ALLOW_REMOTE_EXEC */
+    return 0;
+}
+#endif
+
+/* -get_data */
+static int get_data_fun(char *next_arg GCC_UNUSED)
+{
+    /*
+     * User data for GET form.
+     */
+    char **get_data;
+    char *buf = NULL;
+
+    /*
+     * On Unix, conflicts with curses when interactive so let's force a dump.
+     * -CL
+     *
+     * On VMS, mods have been made in LYCurses.c to deal with potential
+     * conflicts, so don't force the dump here.  - FM
+     */
+#ifndef VMS
+    force_dump_mode();
+#endif /* VMS */
+
+    StrAllocCopy(form_get_data, "?");	/* Prime the pump */
+    get_data = &form_get_data;
+
+    /*
+     * Build GET data for later.  Stop reading when we see a line with "---" as
+     * its first three characters.
+     */
+    while (GetStdin(&buf, TRUE)) {
+	StrAllocCat(*get_data, buf);
+    }
+
+    CTRACE((tfp, "get_data:%s\n", *get_data));
+    CTRACE((tfp, "get_data:%s\n", form_get_data));
+    return 0;
+}
+
+/* -help */
+static int help_fun(char *next_arg GCC_UNUSED)
+{
+    print_help_and_exit(0);
+    return 0;
+}
+
+/* -hiddenlinks */
+static int hiddenlinks_fun(char *next_arg)
+{
+    /* *INDENT-OFF* */
+    static Config_Enum table[] = {
+	{ "merge",	HIDDENLINKS_MERGE },
+	{ "listonly",	HIDDENLINKS_SEPARATE },
+	{ "ignore",	HIDDENLINKS_IGNORE },
+	{ NULL,		-1 },
+    };
+    /* *INDENT-ON* */
+
+    if (next_arg != 0) {
+	if (!LYgetEnum(table, next_arg, &LYHiddenLinks))
+	    print_help_and_exit(-1);
+    } else {
+	LYHiddenLinks = HIDDENLINKS_MERGE;
+    }
+
+    return 0;
+}
+
+/* -homepage */
+static int homepage_fun(char *next_arg)
+{
+    if (next_arg != 0) {
+	StrAllocCopy(homepage, next_arg);
+	LYEscapeStartfile(&homepage);
+    }
+    return 0;
+}
+
+/* -mime_header */
+static int mime_header_fun(char *next_arg GCC_UNUSED)
+{
+    /*
+     * Include mime headers and force source dump.
+     */
+    keep_mime_headers = TRUE;
+    force_dump_mode();
+    HTOutputFormat = (LYPrependBase ?
+		      HTAtom_for("www/download") : HTAtom_for("www/dump"));
+    LYcols = MAX_COLS;
+    return 0;
+}
+
+#ifndef DISABLE_NEWS
+/* -newschunksize */
+static int newschunksize_fun(char *next_arg)
+{
+    if (next_arg != 0) {
+	HTNewsChunkSize = atoi(next_arg);
+	/*
+	 * If the new HTNewsChunkSize exceeds the maximum,
+	 * increase HTNewsMaxChunk to this size. - FM
+	 */
+	if (HTNewsChunkSize > HTNewsMaxChunk)
+	    HTNewsMaxChunk = HTNewsChunkSize;
+    }
+    return 0;
+}
+
+/* -newsmaxchunk */
+static int newsmaxchunk_fun(char *next_arg)
+{
+    if (next_arg) {
+	HTNewsMaxChunk = atoi(next_arg);
+	/*
+	 * If HTNewsChunkSize exceeds the new maximum,
+	 * reduce HTNewsChunkSize to this maximum. - FM
+	 */
+	if (HTNewsChunkSize > HTNewsMaxChunk)
+	    HTNewsChunkSize = HTNewsMaxChunk;
+    }
+    return 0;
+}
+#endif /* not DISABLE_NEWS */
+
+/* -nobold */
+static int nobold_fun(char *next_arg GCC_UNUSED)
+{
+    LYnoVideo(1);
+    return 0;
+}
+
+/* -nobrowse */
+static int nobrowse_fun(char *next_arg GCC_UNUSED)
+{
+    HTDirAccess = HT_DIR_FORBID;
+    return 0;
+}
+
+/* -nocolor */
+static int nocolor_fun(char *next_arg GCC_UNUSED)
+{
+    LYShowColor = SHOW_COLOR_NEVER;
+#ifdef USE_SLANG
+    Lynx_Color_Flags &= ~SL_LYNX_USE_COLOR;
+    Lynx_Color_Flags |= SL_LYNX_OVERRIDE_COLOR;
+#endif
+    return 0;
+}
+
+/* -nopause */
+static int nopause_fun(char *next_arg GCC_UNUSED)
+{
+    no_pause = TRUE;
+    return 0;
+}
+
+/* -nomore */
+static int nomore_fun(char *next_arg GCC_UNUSED)
+{
+    nomore = TRUE;
+    return 0;
+}
+
+/* -noreverse */
+static int noreverse_fun(char *next_arg GCC_UNUSED)
+{
+    LYnoVideo(2);
+    return 0;
+}
+
+/* -nounderline */
+static int nounderline_fun(char *next_arg GCC_UNUSED)
+{
+    LYnoVideo(4);
+    return 0;
+}
+
+#ifdef MISC_EXP
+/* -nozap */
+static int nozap_fun(char *next_arg)
+{
+    LYNoZapKey = 1;		/* everything but "initially" treated as "full" - kw */
+    if (next_arg != 0) {
+	if (strcasecomp(next_arg, "initially") == 0)
+	    LYNoZapKey = 2;
+
+    }
+    return 0;
+}
+#endif /* MISC_EXP */
+
+/* -pauth */
+static int pauth_fun(char *next_arg)
+{
+    parse_authentication(next_arg, proxyauth_info);
+    return 0;
+}
+
+/* -post_data */
+static int post_data_fun(char *next_arg GCC_UNUSED)
+{
+    /*
+     * User data for POST form.
+     */
+    char **post_data;
+    char *buf = NULL;
+
+    /*
+     * On Unix, conflicts with curses when interactive so let's force a dump.
+     * - CL
+     *
+     * On VMS, mods have been made in LYCurses.c to deal with potential
+     * conflicts, so don't force a dump here.  - FM
+     */
+#ifndef VMS
+    force_dump_mode();
+#endif /* VMS */
+
+    post_data = &form_post_data;
+
+    /*
+     * Build post data for later.  Stop reading when we see a line with "---"
+     * as its first three characters.
+     */
+    while (GetStdin(&buf, TRUE)) {
+	StrAllocCat(*post_data, buf);
+    }
+    return 0;
+}
+
+static const char *show_restriction(const char *name)
+{
+    const char *value = 0;
+
+    switch (find_restriction(name, -1)) {
+    case TRUE:
+	value = "on";
+	break;
+    case FALSE:
+	value = "off";
+	break;
+    default:
+	value = "?";
+	break;
+    }
+    return value;
+}
+
+/* -restrictions */
+static int restrictions_fun(char *next_arg)
+{
+    /* *INDENT-OFF* */
+    static const struct {
+	const char *name;
+	const char *help;
+    } table[] = {
+	{ "all", "restricts all options." },
+	{ "bookmark", "disallow changing the location of the bookmark file" },
+	{ "bookmark_exec", "disallow execution links via the bookmark file" },
+#if defined(DIRED_SUPPORT) && defined(OK_PERMIT)
+	{ "change_exec_perms", "\
+disallow changing the eXecute permission on files\n\
+(but still allow it for directories) when local file\n\
+management is enabled." },
+#endif /* DIRED_SUPPORT && OK_PERMIT */
+#ifdef SUPPORT_CHDIR
+	{ "chdir", "\
+disallow changing the working directory of lynx, e.g.,\n\
+to affect the behavior of download command" },
+#endif
+#if defined(HAVE_CONFIG_H) && !defined(NO_CONFIG_INFO)
+	{ "compileopts_info", "\
+disable info on options used to compile the binary" },
+#endif
+	{ "default", "\
+same as commandline option -anonymous.  Sets the\n\
+default service restrictions for anonymous users.  Set to\n\
+all restricted, except for: inside_telnet, outside_telnet,\n\
+inside_ftp, outside_ftp, inside_rlogin, outside_rlogin,\n\
+inside_news, outside_news, telnet_port, jump, mail, print,\n\
+exec, and goto.  The settings for these, as well as\n\
+additional goto restrictions for specific URL schemes\n\
+that are also applied, are derived from definitions\n\
+within userdefs.h." },
+#ifdef DIRED_SUPPORT
+	{ "dired_support", "disallow local file management" },
+#endif /* DIRED_SUPPORT */
+	{ "disk_save", "disallow saving to disk in the download and print menus" },
+	{ "dotfiles", "disallow access to, or creation of, hidden (dot) files" },
+	{ "download", "disallow some downloaders in the download menu" },
+	{ "editor", "disallow editing" },
+	{ "exec", "disable execution scripts" },
+	{ "exec_frozen", "disallow the user from changing the execution link option" },
+#ifdef USE_EXTERNALS
+	{ "externals", "disable passing URLs to some external programs" },
+#endif
+	{ "file_url", "\
+disallow using G)oto, served links or bookmarks for\n\
+file: URL's" },
+	{ "goto", "disable the 'g' (goto) command" },
+#if !defined(HAVE_UTMP) || defined(VMS) /* not selective */
+	{ "inside_ftp", "\
+disallow ftps coming from inside your\n\
+domain (utmp required for selectivity)" },
+	{ "inside_news", "\
+disallow USENET news reading and posting coming\n\
+from inside your domain (utmp required for selectivity)" },
+	{ "inside_rlogin", "\
+disallow rlogins coming from inside your\n\
+domain (utmp required for selectivity)" },
+	{ "inside_telnet", "\
+disallow telnets coming from inside your\n\
+domain (utmp required for selectivity)" },
+#else
+	{ "inside_ftp", "\
+disallow ftps coming from inside your domain" },
+	{ "inside_news", "\
+disallow USENET news reading and posting coming\n\
+from inside your domain" },
+	{ "inside_rlogin", "\
+disallow rlogins coming from inside your domain" },
+	{ "inside_telnet", "\
+disallow telnets coming from inside your domain" },
+#endif /* HAVE_UTMP || VMS */
+	{ "jump", "disable the 'j' (jump) command" },
+	{ "lynxcfg_info", "\
+disable viewing of lynx.cfg configuration file info" },
+#ifndef NO_CONFIG_INFO
+	{ "lynxcfg_xinfo", "\
+disable extended lynx.cfg viewing and reloading" },
+#endif
+	{ "lynxcgi", "\
+disallow execution of Lynx CGI URLs" },
+	{ "mail", "disallow mail" },
+	{ "multibook", "disallow multiple bookmark files" },
+	{ "news_post", "disallow USENET News posting." },
+	{ "option_save", "disallow saving options in .lynxrc" },
+#if !defined(HAVE_UTMP) || defined(VMS) /* not selective */
+	{ "outside_ftp", "\
+disallow ftps coming from outside your\n\
+domain (utmp required for selectivity)" },
+	{ "outside_news", "\
+disallow USENET news reading and posting coming\n\
+from outside your domain (utmp required for selectivity)" },
+	{ "outside_rlogin", "\
+disallow rlogins coming from outside your\n\
+domain (utmp required for selectivity)" },
+	{ "outside_telnet", "\
+disallow telnets coming from outside your\n\
+domain (utmp required for selectivity)" },
+#else
+	{ "outside_ftp", "\
+disallow ftp coming from outside your domain" },
+	{ "outside_news", "\
+disallow USENET news reading and posting coming\n\
+from outside your domain" },
+	{ "outside_rlogin", "\
+disallow rlogins coming from outside your domain" },
+	{ "outside_telnet", "\
+disallow telnets coming from outside your domain" },
+#endif /* !HAVE_UTMP || VMS */
+	{ "print", "disallow most print options" },
+	{ "shell", "\
+disallow shell escapes, and lynxexec, lynxprog or lynxcgi\n\
+G)oto's" },
+	{ "suspend", "disallow Control-Z suspends with escape to shell" },
+	{ "telnet_port", "disallow specifying a port in telnet G)oto's" },
+	{ "useragent", "disallow modifications of the User-Agent header" },
+    };
+    /* *INDENT-ON* */
+
+    static const char *Usage[] =
+    {
+	""
+	,"USAGE: lynx -restrictions=[option][,option][,option]"
+	,"List of Options:"
+	,"  ?                 when used alone, list restrictions in effect."
+
+    };
+    unsigned j, k, column = 0;
+    const char *name;
+    const char *value;
+    BOOLEAN found, first;
+
+    if (isEmpty(next_arg)) {
+	SetOutputMode(O_TEXT);
+	for (j = 0; j < TABLESIZE(Usage); j++) {
+	    printf("%s\n", Usage[j]);
+	}
+	for (j = 0; j < TABLESIZE(table); j++) {
+	    if (!strcmp(table[j].name, "all")
+		|| !strcmp(table[j].name, "default")) {
+		value = NULL;
+	    } else {
+		value = show_restriction(table[j].name);
+	    }
+	    print_help_strings(table[j].name, table[j].help, value, FALSE);
+	}
+	first = TRUE;
+	for (j = 0;; j++) {
+	    found = FALSE;
+	    if ((name = index_to_restriction(j)) == 0) {
+		break;
+	    }
+	    for (k = 0; k < TABLESIZE(table); k++) {
+		if (!strcmp(name, table[k].name)) {
+		    found = TRUE;
+		}
+	    }
+	    if (!found) {
+		if (first) {
+		    printf("Other restrictions (see the user's guide):\n");
+		}
+		value = show_restriction(table[j].name);
+		printf("%s%s (%s)", column ? ", " : "  ", name, value);
+		column += 5 + strlen(name) + strlen(value);
+		if (column > 50) {
+		    column = 0;
+		    printf("\n");
+		}
+		first = FALSE;
+	    }
+	}
+	if (column)
+	    printf("\n");
+	SetOutputMode(O_BINARY);
+	exit_immediately(EXIT_SUCCESS);
+    } else if (*next_arg == '?') {
+	SetOutputMode(O_TEXT);
+	print_restrictions_to_fd(stdout);
+	SetOutputMode(O_BINARY);
+	exit_immediately(EXIT_SUCCESS);
+    } else {
+	parse_restrictions(next_arg);
+    }
+    return 0;
+}
+
+/* -selective */
+static int selective_fun(char *next_arg GCC_UNUSED)
+{
+    HTDirAccess = HT_DIR_SELECTIVE;
+    return 0;
+}
+
+/* -source */
+static int source_fun(char *next_arg GCC_UNUSED)
+{
+    force_dump_mode();
+    HTOutputFormat = (LYPrependBase ?
+		      HTAtom_for("www/download") : HTAtom_for("www/dump"));
+    LYcols = MAX_COLS;
+    return 0;
+}
+
+/* -traversal */
+static int traversal_fun(char *next_arg GCC_UNUSED)
+{
+    traversal = TRUE;
+#ifdef USE_SLANG
+    LYcols = DFT_COLS;
+#else
+    LYcols = MAX_COLS;
+#endif /* USE_SLANG */
+
+    return 0;
+}
+
+/* -version */
+static int version_fun(char *next_arg GCC_UNUSED)
+{
+    char *result = NULL;
+
+    SetLocale();
+    SetOutputMode(O_TEXT);
+
+    HTSprintf0(&result, gettext("%s Version %s (%s)"),
+	       LYNX_NAME, LYNX_VERSION,
+	       LYVersionDate());
+
+    StrAllocCat(result, "\n");
+#ifdef USE_SSL
+    HTSprintf(&result, "libwww-FM %s,", HTLibraryVersion);
+    append_ssl_version(&result, " ");
+#else
+    HTSprintf(&result, "libwww-FM %s", HTLibraryVersion);
+#endif /* USE_SSL */
+
+#if defined(NCURSES) && defined(HAVE_CURSES_VERSION)
+    HTSprintf(&result, ", %s", curses_version());
+#if defined(WIDEC_CURSES)
+    HTSprintf(&result, "(wide)");
+#endif
+#elif defined(PDCURSES) && defined(PDC_BUILD)
+    HTSprintf(&result, ", pdcurses %.3f", PDC_BUILD * 0.001);
+#elif defined(USE_SLANG) && defined(SLANG_VERSION_STRING)
+    HTSprintf(&result, ", s-lang %s", SLANG_VERSION_STRING);
+#endif
+
+    printf("%s\n", result);
+    free(result);
+
+#ifndef __DATE__
+#define __DATE__ ""
+#endif
+#ifndef __TIME__
+#define __TIME__ ""
+#endif
+
+/*
+ * SYSTEM_NAME is set by the configure script.  Show build date/time for other
+ * systems, according to predefined compiler symbols.
+ */
+#ifdef SYSTEM_NAME
+    printf(gettext("Built on %s %s %s\n"), SYSTEM_NAME, __DATE__, __TIME__);
+#else
+#ifdef __CYGWIN__
+    printf("Compiled by CYGWIN (%s %s).\n", __DATE__, __TIME__);
+#else
+#ifdef __BORLANDC__
+    printf("Compiled by Borland C++ (%s %s).\n", __DATE__, __TIME__);
+#else
+#ifdef _MSC_VER
+    printf("Compiled by Microsoft Visual C++ (%s %s).\n", __DATE__, __TIME__);
+#else
+#ifdef __DJGPP__
+    printf("Compiled by DJGPP (%s %s).\n", __DATE__, __TIME__);
+#else
+    printf("Compiled at (%s %s).\n", __DATE__, __TIME__);
+#endif /* __DJGPP__ */
+#endif /* _MSC_VER */
+#endif /* __BORLANDC__ */
+#endif /* __CYGWIN__ */
+#endif
+
+    puts("");
+    puts(gettext("Copyrights held by the Lynx Developers Group,"));
+    puts(gettext("the University of Kansas, CERN, and other contributors."));
+    puts(gettext("Distributed under the GNU General Public License (Version 2)."));
+    puts(gettext("See http://lynx.isc.org/ and the online help for more information."));
+    puts("");
+#ifdef USE_SSL
+#if defined(OPENSSL_VERSION_TEXT) && !defined(LIBGNUTLS_VERSION)
+    puts("See http://www.openssl.org/ for information about OpenSSL.");
+#endif /* OPENSSL_VERSION_TEXT */
+    puts("");
+#endif /* USE_SSL */
+
+    SetOutputMode(O_BINARY);
+
+    exit_immediately(EXIT_SUCCESS);
+    /* NOT REACHED */
+    return 0;
+}
+
+/* -width */
+static int width_fun(char *next_arg)
+{
+    if (next_arg != 0) {
+	int w = atoi(next_arg);
+
+	if (w > 0)
+	    dump_output_width = ((w < MAX_COLS) ? w : MAX_COLS);
+    }
+
+    return 0;
+}
+
+#if defined(PDCURSES) && defined(PDC_BUILD) && PDC_BUILD >= 2401
+/* -scrsize */
+static int scrsize_fun(char *next_arg)
+{
+    if (next_arg != 0) {
+	char *cp;
+
+	if ((cp = strchr(next_arg, ',')) != 0) {
+	    *cp++ = '\0';	/* Terminate ID */
+	    scrsize_x = atoi(next_arg);
+	    scrsize_y = atoi(cp);
+	    if ((scrsize_x <= 1) || (scrsize_y <= 1)) {
+		scrsize_x = scrsize_y = 0;
+	    }
+	    if ((scrsize_x > 0) && (scrsize_x < 80)) {
+		scrsize_x = 80;
+	    }
+	    if ((scrsize_y > 0) && (scrsize_y < 4)) {
+		scrsize_y = 4;
+	    }
+	    CTRACE((tfp, "scrsize: x=%d, y=%d\n", scrsize_x, scrsize_y));
+	}
+    }
+    return 0;
+}
+#endif
+
+/* NOTE: This table is sorted by name to make the help message useful */
+/* *INDENT-OFF* */
+static Config_Type Arg_Table [] =
+{
+   PARSE_SET(
+      "accept_all_cookies", 4|SET_ARG,		LYAcceptAllCookies,
+      "\naccept cookies without prompting if Set-Cookie handling\nis on"
+   ),
+   PARSE_FUN(
+      "anonymous",	2|FUNCTION_ARG,		anonymous_fun,
+      "apply restrictions for anonymous account,\nsee also -restrictions"
+   ),
+   PARSE_FUN(
+      "assume_charset", 4|NEED_FUNCTION_ARG,	assume_charset_fun,
+      "=MIMEname\ncharset for documents that don't specify it"
+   ),
+   PARSE_FUN(
+      "assume_local_charset", 4|NEED_FUNCTION_ARG, assume_local_charset_fun,
+      "=MIMEname\ncharset assumed for local files"
+   ),
+   PARSE_FUN(
+      "assume_unrec_charset", 4|NEED_FUNCTION_ARG, assume_unrec_charset_fun,
+      "=MIMEname\nuse this instead of unrecognized charsets"
+   ),
+   PARSE_FUN(
+      "auth",		4|NEED_FUNCTION_ARG,	auth_fun,
+      "=id:pw\nauthentication information for protected documents"
+   ),
+   PARSE_FUN(
+      "base",		4|FUNCTION_ARG,		base_fun,
+      "prepend a request URL comment and BASE tag to text/html\n\
+outputs for -source dumps"
+   ),
+#ifndef DISABLE_BIBP
+   PARSE_STR(
+      "bibhost",	4|NEED_LYSTRING_ARG,	BibP_bibhost,
+      "=URL\nlocal bibp server (default http://bibhost/)"
+   ),
+#endif
+#ifdef USE_BLINK
+   PARSE_SET(
+      "blink",		4|SET_ARG,		term_blink_is_boldbg,
+      "enable bright background via the BLINK terminal attribute"
+   ),
+#endif
+   PARSE_SET(
+      "book",		4|SET_ARG,		bookmark_start,
+      "use the bookmark page as the startfile"
+   ),
+   PARSE_SET(
+      "buried_news",	4|TOGGLE_ARG,		scan_for_buried_news_references,
+      "toggles scanning of news articles for buried references"
+   ),
+   PARSE_FUN(
+      "cache",		4|NEED_FUNCTION_ARG,	cache_fun,
+      "=NUMBER\nNUMBER of documents cached in memory"
+   ),
+   PARSE_SET(
+      "case",		4|SET_ARG,		case_sensitive,
+      "enable case sensitive user searching"
+   ),
+   PARSE_SET(
+      "center",		4|TOGGLE_ARG,		no_table_center,
+      "toggle center alignment in HTML TABLE"
+   ),
+   PARSE_STR(
+      "cfg",		2|NEED_LYSTRING_ARG,	lynx_cfg_file,
+      "=FILENAME\nspecifies a lynx.cfg file other than the default"
+   ),
+   PARSE_FUN(
+      "child",		4|FUNCTION_ARG,		child_fun,
+      "exit on left-arrow in startfile, and disable save to disk"
+   ),
+   PARSE_FUN(
+      "child_relaxed",	4|FUNCTION_ARG,		child_relaxed_fun,
+      "exit on left-arrow in startfile (allows save to disk)"
+   ),
+#ifdef USE_CMD_LOGGING
+   PARSE_STR(
+      "cmd_log",	2|NEED_LYSTRING_ARG,	lynx_cmd_logfile,
+      "=FILENAME\nlog keystroke commands to the given file"
+   ),
+   PARSE_STR(
+      "cmd_script",	2|NEED_LYSTRING_ARG,	lynx_cmd_script,
+      "=FILENAME\nread keystroke commands from the given file\n(see -cmd_log)"
+   ),
+#endif
+#ifdef USE_SLANG
+   PARSE_FUN(
+      "color",		4|FUNCTION_ARG,		color_fun,
+      "force color mode on with standard bg colors"
+   ),
+#endif
+   PARSE_INT(
+      "connect_timeout", 4|NEED_INT_ARG,	connect_timeout,
+      "=N\nset the N-second connection timeout"
+   ),
+#ifdef MISC_EXP
+   PARSE_FUN(
+      "convert_to",	4|FUNCTION_ARG,		convert_to_fun,
+      "=FORMAT\nconvert input, FORMAT is in MIME type notation\n(experimental)"
+   ),
+#endif
+#ifdef USE_PERSISTENT_COOKIES
+   PARSE_STR(
+      "cookie_file",	4|LYSTRING_ARG,		LYCookieFile,
+      "=FILENAME\nspecifies a file to use to read cookies"
+   ),
+   PARSE_STR(
+      "cookie_save_file", 4|LYSTRING_ARG,	LYCookieSaveFile,
+      "=FILENAME\nspecifies a file to use to store cookies"
+   ),
+#endif /* USE_PERSISTENT_COOKIES */
+   PARSE_SET(
+      "cookies",	4|TOGGLE_ARG,		LYSetCookies,
+      "toggles handling of Set-Cookie headers"
+   ),
+#ifndef VMS
+   PARSE_SET(
+      "core",		4|TOGGLE_ARG,		LYNoCore,
+      "toggles forced core dumps on fatal errors"
+   ),
+#endif
+   PARSE_FUN(
+      "crawl",		4|FUNCTION_ARG,		crawl_fun,
+      "with -traversal, output each page to a file\n\
+with -dump, format output as with -traversal, but to stdout"
+   ),
+#ifdef USE_CURSES_PADS
+   PARSE_SET(
+      "curses_pads",	4|TOGGLE_ARG,		LYuseCursesPads,
+      "uses curses pad feature to support left/right shifting"
+   ),
+#endif
+#ifdef DISP_PARTIAL
+   PARSE_SET(
+      "debug_partial",	4|TOGGLE_ARG,		debug_display_partial,
+      "incremental display stages with MessageSecs delay"
+   ),
+#endif
+   PARSE_INT(
+      "delay",		4|NEED_TIME_ARG,	DebugSecs,
+      "=NNN\nset NNN-second delay at statusline message"
+   ),
+   PARSE_FUN(
+      "display",	4|NEED_FUNCTION_ARG,	display_fun,
+      "=DISPLAY\nset the display variable for X exec'ed programs"
+   ),
+   PARSE_FUN(
+      "display_charset", 4|NEED_FUNCTION_ARG,	display_charset_fun,
+      "=MIMEname\ncharset for the terminal output"
+   ),
+   PARSE_SET(
+      "dont_wrap_pre",	4|SET_ARG,		dont_wrap_pre,
+      "inhibit wrapping of text in <pre> when -dump'ing and\n\
+-crawl'ing, mark wrapped lines in interactive session"
+   ),
+   PARSE_FUN(
+      "dump",		4|FUNCTION_ARG,		dump_output_fun,
+      "dump the first file to stdout and exit"
+   ),
+   PARSE_FUN(
+      "editor",		4|NEED_FUNCTION_ARG,	editor_fun,
+      "=EDITOR\nenable edit mode with specified editor"
+   ),
+   PARSE_SET(
+      "emacskeys",	4|SET_ARG,		emacs_keys,
+      "enable emacs-like key movement"
+   ),
+   PARSE_SET(
+      "enable_scrollback", 4|TOGGLE_ARG,	enable_scrollback,
+      "\ntoggles compatibility with comm programs' scrollback\n\
+keys (may be incompatible with some curses packages)"
+   ),
+   PARSE_FUN(
+      "error_file",	4|NEED_FUNCTION_ARG,	error_file_fun,
+      "=FILE\nwrite the HTTP status code here"
+   ),
+#if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)
+#ifndef NEVER_ALLOW_REMOTE_EXEC
+   PARSE_FUN(
+      "exec",		4|FUNCTION_ARG,		exec_fun,
+      "enable local program execution"
+   ),
+#endif
+#endif /* EXEC_LINKS || EXEC_SCRIPTS */
+#ifdef VMS
+   PARSE_SET(
+      "fileversions",	4|SET_ARG,		HTVMSFileVersions,
+      "include all versions of files in local VMS directory\nlistings"
+   ),
+#endif
+#ifdef LY_FIND_LEAKS
+   PARSE_SET(
+      "find_leaks",	4|TOGGLE_ARG,		LYfind_leaks,
+      "toggles memory-leak checking"
+   ),
+#endif
+   PARSE_SET(
+      "force_empty_hrefless_a",	4|SET_ARG,	force_empty_hrefless_a,
+      "\nforce HREF-less 'A' elements to be empty (close them as\n\
+soon as they are seen)"
+   ),
+   PARSE_SET(
+      "force_html",	4|SET_ARG,		LYforce_HTML_mode,
+      "forces the first document to be interpreted as HTML"
+   ),
+   PARSE_SET(
+      "force_secure",	4|TOGGLE_ARG,		LYForceSSLCookiesSecure,
+      "toggles forcing of the secure flag for SSL cookies"
+   ),
+#if !defined(NO_OPTION_FORMS) && !defined(NO_OPTION_MENU)
+   PARSE_SET(
+      "forms_options",	4|TOGGLE_ARG,		LYUseFormsOptions,
+      "toggles forms-based vs old-style options menu"
+   ),
+#endif
+   PARSE_SET(
+      "from",		4|TOGGLE_ARG,		LYNoFromHeader,
+      "toggle transmission of From headers"
+   ),
+#ifndef DISABLE_FTP
+   PARSE_SET(
+      "ftp",		4|UNSET_ARG,		ftp_ok,
+      "disable ftp access"
+   ),
+#endif
+   PARSE_FUN(
+      "get_data",	2|FUNCTION_ARG,		get_data_fun,
+      "user data for get forms, read from stdin,\nterminated by '---' on a line"
+   ),
+   PARSE_SET(
+      "head",		4|SET_ARG,		HEAD_request,
+      "send a HEAD request"
+   ),
+   PARSE_FUN(
+      "help",		4|FUNCTION_ARG,		help_fun,
+      "print this usage message"
+   ),
+   PARSE_FUN(
+      "hiddenlinks",	4|NEED_FUNCTION_ARG,	hiddenlinks_fun,
+      "=[option]\nhidden links: options are merge, listonly, or ignore"
+   ),
+   PARSE_SET(
+      "historical",	4|TOGGLE_ARG,		historical_comments,
+      "toggles use of '>' or '-->' as terminator for comments"
+   ),
+   PARSE_FUN(
+      "homepage",	4|NEED_FUNCTION_ARG,	homepage_fun,
+      "=URL\nset homepage separate from start page"
+   ),
+   PARSE_SET(
+      "image_links",	4|TOGGLE_ARG,		clickable_images,
+      "toggles inclusion of links for all images"
+   ),
+   PARSE_STR(
+      "index",		4|NEED_LYSTRING_ARG,	indexfile,
+      "=URL\nset the default index file to URL"
+   ),
+   PARSE_SET(
+      "ismap",		4|TOGGLE_ARG,		LYNoISMAPifUSEMAP,
+      "toggles inclusion of ISMAP links when client-side\nMAPs are present"
+   ),
+#ifdef USE_JUSTIFY_ELTS
+   PARSE_SET(
+      "justify",	4|SET_ARG,		ok_justify,
+      "do justification of text"
+   ),
+#endif
+   PARSE_INT(
+      "link",		4|NEED_INT_ARG,		crawl_count,
+      "=NUMBER\nstarting count for lnk#.dat files produced by -crawl"
+   ),
+   PARSE_SET(
+      "listonly",	4|TOGGLE_ARG,		dump_links_only,
+      "with -dump, forces it to show only the list of links"
+   ),
+   PARSE_SET(
+      "localhost",	4|SET_ARG,		local_host_only,
+      "disable URLs that point to remote hosts"
+   ),
+#if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)
+   PARSE_SET(
+      "locexec",	4|SET_ARG,		local_exec_on_local_files,
+      "enable local program execution from local files only"
+   ),
+#endif /* EXEC_LINKS || EXEC_SCRIPTS */
+#if defined(USE_COLOR_STYLE)
+   PARSE_STR(
+      "lss",		2|NEED_LYSTRING_ARG,	lynx_lss_file2,
+      "=FILENAME\nspecifies a lynx.lss file other than the default"
+   ),
+#endif
+   PARSE_FUN(
+      "mime_header",	4|FUNCTION_ARG,		mime_header_fun,
+      "include mime headers and force source dump"
+   ),
+   PARSE_SET(
+      "minimal",	4|TOGGLE_ARG,		minimal_comments,
+      "toggles minimal versus valid comment parsing"
+   ),
+#ifdef EXP_NESTED_TABLES
+   PARSE_SET(
+      "nested_tables",	4|TOGGLE_ARG,		nested_tables,
+      "toggles nested-tables logic"
+   ),
+#endif
+#ifndef DISABLE_NEWS
+   PARSE_FUN(
+      "newschunksize",	4|NEED_FUNCTION_ARG,	newschunksize_fun,
+      "=NUMBER\nnumber of articles in chunked news listings"
+   ),
+   PARSE_FUN(
+      "newsmaxchunk",	4|NEED_FUNCTION_ARG,	newsmaxchunk_fun,
+      "=NUMBER\nmaximum news articles in listings before chunking"
+   ),
+#endif
+#if USE_BLAT_MAILER
+   PARSE_SET(
+      "noblat",		4|TOGGLE_ARG,		mail_is_blat,
+      "select mail tool (`BLAT' ==> `sendmail')"
+   ),
+#endif
+   PARSE_FUN(
+      "nobold",		4|FUNCTION_ARG,		nobold_fun,
+      "disable bold video-attribute"
+   ),
+   PARSE_FUN(
+      "nobrowse",	4|FUNCTION_ARG,		nobrowse_fun,
+      "disable directory browsing"
+   ),
+   PARSE_SET(
+      "nocc",		4|SET_ARG,		LYNoCc,
+      "disable Cc: prompts for self copies of mailings"
+   ),
+   PARSE_FUN(
+      "nocolor",	4|FUNCTION_ARG,		nocolor_fun,
+      "turn off color support"
+   ),
+#if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)
+   PARSE_SET(
+      "noexec",		4|UNSET_ARG,		local_exec,
+      "disable local program execution (DEFAULT)"
+   ),
+#endif /* EXEC_LINKS || EXEC_SCRIPTS */
+   PARSE_SET(
+      "nofilereferer",	4|SET_ARG,		no_filereferer,
+      "disable transmission of Referer headers for file URLs"
+   ),
+   PARSE_SET(
+      "nolist",		4|SET_ARG,		no_list,
+      "disable the link list feature in dumps"
+   ),
+   PARSE_SET(
+      "nolog",		4|UNSET_ARG,		error_logging,
+      "disable mailing of error messages to document owners"
+   ),
+   PARSE_SET(
+      "nomargins",	4|SET_ARG,		no_margins,
+      "disable the right/left margins in the default\nstyle-sheet"
+   ),
+   PARSE_FUN(
+      "nomore",		4|FUNCTION_ARG,		nomore_fun,
+      "disable -more- string in statusline messages"
+   ),
+#if defined(HAVE_SIGACTION) && defined(SIGWINCH)
+   PARSE_SET(
+      "nonrestarting_sigwinch", 4|SET_ARG,	LYNonRestartingSIGWINCH,
+      "\nmake window size change handler non-restarting"
+   ),
+#endif /* HAVE_SIGACTION */
+   PARSE_SET(
+      "nonumbers",	4|SET_ARG,		no_numbers,
+      "disable the link/form numbering feature in dumps"
+   ),
+   PARSE_FUN(
+      "nopause",	4|FUNCTION_ARG,		nopause_fun,
+      "disable forced pauses for statusline messages"
+   ),
+   PARSE_SET(
+      "noprint",	4|SET_ARG,		no_print,
+      "disable some print functions, like -restrictions=print"
+   ),
+   PARSE_SET(
+      "noredir",	4|SET_ARG,		no_url_redirection,
+      "don't follow Location: redirection"
+   ),
+   PARSE_SET(
+      "noreferer",	4|SET_ARG,		LYNoRefererHeader,
+      "disable transmission of Referer headers"
+   ),
+   PARSE_FUN(
+      "noreverse",	4|FUNCTION_ARG,		noreverse_fun,
+      "disable reverse video-attribute"
+   ),
+#ifdef SOCKS
+   PARSE_SET(
+      "nosocks",	2|UNSET_ARG,		socks_flag,
+      "don't use SOCKS proxy for this session"
+   ),
+#endif
+   PARSE_SET(
+      "nostatus",	4|SET_ARG,		no_statusline,
+      "disable the miscellaneous information messages"
+   ),
+   PARSE_SET(
+      "notitle",	4|SET_ARG,		no_title,
+      "disable the title at the top of each page"
+   ),
+   PARSE_FUN(
+      "nounderline",	4|FUNCTION_ARG,		nounderline_fun,
+      "disable underline video-attribute"
+   ),
+#ifdef MISC_EXP
+   PARSE_FUN(
+      "nozap",		4|FUNCTION_ARG,		nozap_fun,
+      "=DURATION (\"initially\" or \"full\") disable checks for 'z' key"
+   ),
+#endif
+   PARSE_SET(
+      "number_fields",	4|SET_ARG,		number_fields,
+      "force numbering of links as well as form input fields"
+   ),
+   PARSE_SET(
+      "number_links",	4|SET_ARG,		number_links,
+      "force numbering of links"
+   ),
+#ifdef DISP_PARTIAL
+   PARSE_SET(
+      "partial",	4|TOGGLE_ARG,		display_partial_flag,
+      "toggles display partial pages while downloading"
+   ),
+   PARSE_INT(
+      "partial_thres",	4|NEED_INT_ARG,		partial_threshold,
+      "[=NUMBER]\nnumber of lines to render before repainting display\n\
+with partial-display logic"
+   ),
+#endif
+#ifndef DISABLE_FTP
+   PARSE_SET(
+      "passive-ftp",	4|TOGGLE_ARG,		ftp_passive,
+      "toggles passive ftp connection"
+   ),
+#endif
+   PARSE_FUN(
+      "pauth",		4|NEED_FUNCTION_ARG,	pauth_fun,
+      "=id:pw\nauthentication information for protected proxy server"
+   ),
+   PARSE_SET(
+      "popup",		4|UNSET_ARG,		LYUseDefSelPop,
+      "toggles handling of single-choice SELECT options via\n\
+popup windows or as lists of radio buttons"
+   ),
+   PARSE_FUN(
+      "post_data",	2|FUNCTION_ARG,		post_data_fun,
+      "user data for post forms, read from stdin,\n\
+terminated by '---' on a line"
+   ),
+   PARSE_SET(
+      "preparsed",	4|SET_ARG,		LYPreparsedSource,
+      "show parsed text/html with -source and in source view\n\
+to visualize how lynx behaves with invalid HTML"
+   ),
+#ifdef USE_PRETTYSRC
+   PARSE_SET(
+      "prettysrc",	4|SET_ARG,		LYpsrc,
+      "do syntax highlighting and hyperlink handling in source\nview"
+   ),
+#endif
+   PARSE_SET(
+      "print",		4|UNSET_ARG,		no_print,
+      "enable print functions (DEFAULT), opposite of -noprint"
+   ),
+   PARSE_SET(
+      "pseudo_inlines", 4|TOGGLE_ARG,		pseudo_inline_alts,
+      "toggles pseudo-ALTs for inlines with no ALT string"
+   ),
+   PARSE_SET(
+      "raw",		4|UNSET_ARG,		LYUseDefaultRawMode,
+      "toggles default setting of 8-bit character translations\n\
+or CJK mode for the startup character set"
+   ),
+   PARSE_SET(
+      "realm",		4|SET_ARG,		check_realm,
+      "restricts access to URLs in the starting realm"
+   ),
+   PARSE_INT(
+      "read_timeout",	4|NEED_INT_ARG,		reading_timeout,
+      "=N\nset the N-second read-timeout"
+   ),
+   PARSE_SET(
+      "reload",		4|SET_ARG,		reloading,
+      "flushes the cache on a proxy server\n(only the first document affected)"
+   ),
+   PARSE_FUN(
+      "restrictions",	4|FUNCTION_ARG,		restrictions_fun,
+      "=[options]\nuse -restrictions to see list"
+   ),
+   PARSE_SET(
+      "resubmit_posts", 4|TOGGLE_ARG,		LYresubmit_posts,
+      "toggles forced resubmissions (no-cache) of forms with\n\
+method POST when the documents they returned are sought\n\
+with the PREV_DOC command or from the History List"
+   ),
+   PARSE_SET(
+      "rlogin",		4|UNSET_ARG,		rlogin_ok,
+      "disable rlogins"
+   ),
+#if defined(PDCURSES) && defined(PDC_BUILD) && PDC_BUILD >= 2401
+   PARSE_FUN(
+      "scrsize",	4|NEED_FUNCTION_ARG,	scrsize_fun,
+      "=width,height\nsize of window"
+   ),
+#endif
+#ifdef USE_SCROLLBAR
+   PARSE_SET(
+      "scrollbar",	4|TOGGLE_ARG,		LYShowScrollbar,
+      "toggles showing scrollbar"
+   ),
+   PARSE_SET(
+      "scrollbar_arrow", 4|TOGGLE_ARG,		LYsb_arrow,
+      "toggles showing arrows at ends of the scrollbar"
+   ),
+#endif
+   PARSE_FUN(
+      "selective",	4|FUNCTION_ARG,		selective_fun,
+      "require .www_browsable files to browse directories"
+   ),
+#ifdef USE_SESSIONS
+   PARSE_STR(
+      "session",	2|NEED_LYSTRING_ARG,	session_file,
+      "=FILENAME\nresumes from specified file on startup and\n\
+saves session to that file on exit"
+   ),
+   PARSE_STR(
+      "sessionin",	2|NEED_LYSTRING_ARG,	sessionin_file,
+      "=FILENAME\nresumes session from specified file"
+   ),
+   PARSE_STR(
+      "sessionout",	2|NEED_LYSTRING_ARG,	sessionout_file,
+      "=FILENAME\nsaves session to specified file"
+   ),
+#endif /* USE_SESSIONS */
+   PARSE_SET(
+      "short_url",	4|SET_ARG,		long_url_ok,
+      "enables examination of beginning and end of long URL in\nstatus line"
+   ),
+   PARSE_SET(
+      "show_cfg",	1|SET_ARG,		show_cfg,
+      "Show `LYNX.CFG' setting"
+   ),
+   PARSE_SET(
+      "show_cursor",	4|TOGGLE_ARG,		LYUseDefShoCur,
+      "toggles hiding of the cursor in the lower right corner"
+   ),
+#ifdef USE_READPROGRESS
+   PARSE_SET(
+      "show_rate",	4|TOGGLE_ARG,		LYShowTransferRate,
+      "toggles display of transfer rate"
+   ),
+#endif
+   PARSE_SET(
+      "soft_dquotes",	4|TOGGLE_ARG,		soft_dquotes,
+      "toggles emulation of the old Netscape and Mosaic\n\
+bug which treated '>' as a co-terminator for\ndouble-quotes and tags"
+   ),
+   PARSE_FUN(
+      "source",		4|FUNCTION_ARG,		source_fun,
+      "dump the source of the first file to stdout and exit"
+   ),
+   PARSE_SET(
+      "stack_dump",	4|SET_ARG,		stack_dump,
+      "disable SIGINT cleanup handler"
+   ),
+   PARSE_SET(
+      "startfile_ok",	4|SET_ARG,		startfile_ok,
+      "allow non-http startfile and homepage with -validate"
+   ),
+   PARSE_SET(
+      "stderr",		4|SET_ARG,		dump_to_stderr,
+      "write warning messages to standard error when -dump\nor -source is used"
+   ),
+   PARSE_SET(
+      "stdin",		4|SET_ARG,		startfile_stdin,
+      "read startfile from standard input"
+   ),
+#ifdef SYSLOG_REQUESTED_URLS
+   PARSE_STR(
+      "syslog",		4|NEED_LYSTRING_ARG,	syslog_txt,
+      "=text\ninformation for syslog call"
+   ),
+   PARSE_SET(
+      "syslog-urls",	4|SET_ARG,		syslog_requested_urls,
+      "log requested URLs with syslog"
+   ),
+#endif
+   PARSE_SET(
+      "tagsoup",	4|SET_ARG,		DTD_recovery,
+      "use TagSoup rather than SortaSGML parser"
+   ),
+   PARSE_SET(
+      "telnet",		4|UNSET_ARG,		telnet_ok,
+      "disable telnets"
+   ),
+   PARSE_STR(
+      "term",		4|NEED_STRING_ARG,	terminal,
+      "=TERM\nset terminal type to TERM"
+   ),
+#ifdef _WINDOWS
+   PARSE_INT(
+      "timeout",	4|INT_ARG,		lynx_timeout,
+      "=NUMBER\nset TCP/IP timeout"
+   ),
+#endif
+   PARSE_SET(
+      "tlog",		2|TOGGLE_ARG,		LYUseTraceLog,
+      "toggles use of a Lynx Trace Log for the current\nsession"
+   ),
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+   PARSE_SET(
+      "tna",		4|SET_ARG,		textfields_activation_option,
+      "turn on \"Textfields Need Activation\" mode"
+   ),
+#endif
+#ifndef NO_LYNX_TRACE
+   PARSE_SET(
+      "trace",		1|SET_ARG,		WWW_TraceFlag,
+      "turns on Lynx trace mode"
+   ),
+   PARSE_INT(
+      "trace_mask",	1|INT_ARG,		WWW_TraceMask,
+      "customize Lynx trace mode"
+   ),
+#endif
+   PARSE_FUN(
+      "traversal",	4|FUNCTION_ARG,		traversal_fun,
+      "traverse all http links derived from startfile"
+   ),
+   PARSE_SET(
+      "trim_input_fields", 2|SET_ARG,		LYtrimInputFields,
+      "\ntrim input text/textarea fields in forms"
+   ),
+   PARSE_SET(
+      "underline_links",4|TOGGLE_ARG,		LYUnderlineLinks,
+      "toggles use of underline/bold attribute for links"
+   ),
+   PARSE_SET(
+      "underscore",	4|TOGGLE_ARG,		use_underscore,
+      "toggles use of _underline_ format in dumps"
+   ),
+#if defined(USE_MOUSE)
+   PARSE_SET(
+      "use_mouse",	4|SET_ARG,		LYUseMouse,
+      "turn on mouse support"
+   ),
+#endif
+   PARSE_STR(
+      "useragent",	4|NEED_LYSTRING_ARG,	LYUserAgent,
+      "=Name\nset alternate Lynx User-Agent header"
+   ),
+   PARSE_SET(
+      "validate",	2|SET_ARG,		LYValidate,
+      "accept only http URLs (meant for validation)\n\
+implies more restrictions than -anonymous, but\n\
+goto is allowed for http and https"
+   ),
+   PARSE_SET(
+      "verbose",	4|TOGGLE_ARG,		verbose_img,
+      "toggles [LINK], [IMAGE] and [INLINE] comments\n\
+with filenames of these images"
+   ),
+   PARSE_FUN(
+      "version",	1|FUNCTION_ARG,		version_fun,
+      "print Lynx version information"
+   ),
+   PARSE_SET(
+      "vikeys",		4|SET_ARG,		vi_keys,
+      "enable vi-like key movement"
+   ),
+#ifdef __DJGPP__
+   PARSE_SET(
+      "wdebug",		4|TOGGLE_ARG,		watt_debug,
+      "enables Waterloo tcp/ip packet debug. Prints to watt\ndebugfile"
+  ),
+#endif /* __DJGPP__ */
+   PARSE_FUN(
+      "width",		4|NEED_FUNCTION_ARG,	width_fun,
+      "=NUMBER\nscreen width for formatting of dumps (default is 80)"
+   ),
+#ifndef NO_DUMP_WITH_BACKSPACES
+   PARSE_SET(
+      "with_backspaces", 4|SET_ARG,		with_backspaces,
+      "emit backspaces in output if -dumping or -crawling\n(like 'man' does)"
+   ),
+#endif
+   PARSE_SET(
+      "xhtml-parsing",	4|SET_ARG,		LYxhtml_parsing,
+      "enable XHTML 1.0 parsing"
+   ),
+   PARSE_NIL
+};
+/* *INDENT-ON* */
+
+static void print_help_strings(const char *name,
+			       const char *help,
+			       const char *value,
+			       BOOLEAN option)
+{
+    int pad;
+    int c;
+    int first;
+    int field_width = 20;
+
+    pad = field_width - (2 + option + (int) strlen(name));
+
+    fprintf(stdout, "  %s%s", option ? "-" : "", name);
+
+    if (*help != '=') {
+	pad--;
+	while (pad > 0) {
+	    fputc(' ', stdout);
+	    pad--;
+	}
+	fputc(' ', stdout);	/* at least one space */
+	first = 0;
+    } else {
+	first = pad;
+    }
+
+    if (strchr(help, '\n') == 0) {
+	fprintf(stdout, "%s", help);
+    } else {
+	while ((c = *help) != 0) {
+	    if (c == '\n') {
+		if ((pad = --first) < 0) {
+		    pad = field_width;
+		} else {
+		    c = ' ';
+		}
+		fputc(c, stdout);
+		while (pad--)
+		    fputc(' ', stdout);
+	    } else {
+		fputc(c, stdout);
+	    }
+	    help++;
+	    first--;
+	}
+    }
+    if (value)
+	printf(" (%s)", value);
+    fputc('\n', stdout);
+}
+
+static void print_help_and_exit(int exit_status)
+{
+    Config_Type *p;
+
+    if (pgm == NULL)
+	pgm = "lynx";
+
+    SetOutputMode(O_TEXT);
+
+    fprintf(stdout, gettext("USAGE: %s [options] [file]\n"), pgm);
+    fprintf(stdout, gettext("Options are:\n"));
+#ifdef VMS
+    print_help_strings("",
+		       "receive the arguments from stdin (enclose\n\
+in double-quotes (\"-\") on VMS)", NULL, TRUE);
+#else
+    print_help_strings("", "receive options and arguments from stdin", NULL, TRUE);
+#endif /* VMS */
+
+    for (p = Arg_Table; p->name != 0; p++) {
+	char temp[LINESIZE], *value = temp;
+	ParseUnionPtr q = ParseUnionOf(p);
+
+	switch (p->type & ARG_TYPE_MASK) {
+	case TOGGLE_ARG:
+	case SET_ARG:
+	    strcpy(temp, *(q->set_value) ? "on" : "off");
+	    break;
+	case UNSET_ARG:
+	    strcpy(temp, *(q->set_value) ? "off" : "on");
+	    break;
+	case INT_ARG:
+	    sprintf(temp, "%d", *(q->int_value));
+	    break;
+	case TIME_ARG:
+	    sprintf(temp, SECS_FMT, (double) Secs2SECS(*(q->int_value)));
+	    break;
+	case STRING_ARG:
+	    if ((value = *(q->str_value)) != 0
+		&& !*value)
+		value = 0;
+	    break;
+	default:
+	    value = 0;
+	    break;
+	}
+	print_help_strings(p->name, p->help_string, value, TRUE);
+    }
+
+    SetOutputMode(O_BINARY);
+
+    exit_immediately(exit_status);
+}
+
+/*
+ * This function performs a string comparison on two strings a and b.  a is
+ * assumed to be an ordinary null terminated string, but b may be terminated
+ * by an '=', '+' or '-' character.  If terminated by '=', *c will be pointed
+ * to the character following the '='.  If terminated by '+' or '-', *c will
+ * be pointed to that character.  (+/- added for toggle processing - BL.)
+ * If a and b match, it returns 1.  Otherwise 0 is returned.
+ */
+static int arg_eqs_parse(const char *a,
+			 char *b,
+			 char **c)
+{
+    int result = -1;
+
+    *c = NULL;
+    while (result < 0) {
+	if ((*a != *b)
+	    || (*a == 0)
+	    || (*b == 0)) {
+	    if (*a == 0) {
+		switch (*b) {
+		case '\t':	/* embedded blank when reading stdin */
+		case ' ':
+		    *c = LYSkipBlanks(b);
+		    result = 1;
+		    break;
+		case '=':
+		case ':':
+		    *c = b + 1;
+		    result = 1;
+		    break;
+		case '-':
+#if OPTNAME_ALLOW_DASHES
+		    if (isalpha(UCH(b[1]))) {
+			result = 0;
+			break;
+		    }
+#endif
+		    /* FALLTHRU */
+		case '+':
+		    *c = b;
+		    result = 1;
+		    break;
+		case 0:
+		    result = 1;
+		    break;
+		default:
+		    result = 0;
+		    break;
+		}
+	    } else {
+#if OPTNAME_ALLOW_DASHES
+		if (!(*a == '_' && *b == '-'))
+#endif
+		    result = 0;
+	    }
+	}
+	a++;
+	b++;
+    }
+    return result;
+}
+
+#define is_true(s)  (*s == '1' || *s == '+' || !strcasecomp(s, "on")  || !strcasecomp(s, "true"))
+#define is_false(s) (*s == '0' || *s == '-' || !strcasecomp(s, "off") || !strcasecomp(s, "false"))
+
+/*
+ * Parse an option.
+ *	argv[] points to the beginning of the unprocessed options.
+ *	mask is used to select certain options which must be processed
+ *		before others.
+ *	countp (if nonnull) points to an index into argv[], which is updated
+ *		to reflect option values which are also parsed.
+ */
+static BOOL parse_arg(char **argv,
+		      unsigned mask,
+		      int *countp)
+{
+    Config_Type *p;
+    char *arg_name;
+
+#if EXTENDED_STARTFILE_RECALL
+    static BOOLEAN no_options_further = FALSE;	/* set to TRUE after '--' argument */
+    static int nof_index = 0;	/* set the index of -- argument */
+#endif
+
+    arg_name = argv[0];
+    CTRACE((tfp, "parse_arg(arg_name=%s, mask=%u, count=%d)\n",
+	    arg_name, mask, countp ? *countp : -1));
+
+#if EXTENDED_STARTFILE_RECALL
+    if (mask == (unsigned) ((countp != 0) ? 0 : 1)) {
+	no_options_further = FALSE;
+	/* want to reset nonoption when beginning scan for --stdin */
+	if (nonoption != 0) {
+	    FREE(nonoption);
+	}
+    }
+#endif
+
+    /*
+     * Check for a command line startfile.  - FM
+     */
+    if (*arg_name != '-'
+#if EXTENDED_OPTION_LOGIC
+	|| (no_options_further == TRUE && nof_index < (*countp))
+#endif
+	) {
+#if EXTENDED_STARTFILE_RECALL
+	/*
+	 * On the last pass (mask==4), check for cases where we may want to
+	 * provide G)oto history for multiple startfiles.
+	 */
+	if (mask == 4) {
+	    if (nonoption != 0) {
+		LYEnsureAbsoluteURL(&nonoption, "NONOPTION", FALSE);
+		HTAddGotoURL(nonoption);
+		FREE(nonoption);
+	    }
+	    StrAllocCopy(nonoption, arg_name);
+	}
+#endif
+	StrAllocCopy(startfile, arg_name);
+	LYEscapeStartfile(&startfile);
+#ifdef _WINDOWS			/* 1998/01/14 (Wed) 20:11:17 */
+	HTUnEscape(startfile);
+	{
+	    char *q = startfile;
+
+	    while (*q++) {
+		if (*q == '|')
+		    *q = ':';
+	    }
+	}
+#endif
+	CTRACE((tfp, "parse_arg startfile:%s\n", startfile));
+	return (BOOL) (countp != 0);
+    }
+#if EXTENDED_OPTION_LOGIC
+    if (strcmp(arg_name, "--") == 0) {
+	no_options_further = TRUE;
+	nof_index = countp ? *countp : -1;
+	return TRUE;
+    }
+#endif
+
+    /* lose the first '-' character */
+    arg_name++;
+
+    /*
+     * Skip any lone "-" arguments, because we've loaded the stdin input into
+     * an HTList structure for special handling.  - FM
+     */
+    if (*arg_name == 0)
+	return TRUE;
+
+    /* allow GNU-style options with -- prefix */
+    if (*arg_name == '-')
+	++arg_name;
+
+    CTRACE((tfp, "parse_arg lookup(%s)\n", arg_name));
+
+    p = Arg_Table;
+    while (p->name != 0) {
+	ParseUnionPtr q = ParseUnionOf(p);
+	ParseFunc fun;
+	char *next_arg = NULL;
+	char *temp_ptr = NULL;
+
+	if ((p->name[0] != *arg_name)
+	    || (0 == arg_eqs_parse(p->name, arg_name, &next_arg))) {
+	    p++;
+	    continue;
+	}
+
+	if (p->type & NEED_NEXT_ARG) {
+	    if (next_arg == 0) {
+		next_arg = argv[1];
+		if ((countp != 0) && (next_arg != 0))
+		    (*countp)++;
+	    }
+	    CTRACE((tfp, "...arg:%s\n", NONNULL(next_arg)));
+	}
+
+	/* ignore option if it's not our turn */
+	if ((p->type & mask) == 0) {
+	    CTRACE((tfp, "...skip (mask %u/%d)\n", mask, p->type & 7));
+	    return FALSE;
+	}
+
+	switch (p->type & ARG_TYPE_MASK) {
+	case TOGGLE_ARG:	/* FALLTHRU */
+	case SET_ARG:		/* FALLTHRU */
+	case UNSET_ARG:
+	    if (q->set_value != 0) {
+		if (next_arg == 0) {
+		    switch (p->type & ARG_TYPE_MASK) {
+		    case TOGGLE_ARG:
+			*(q->set_value) = (BOOL) !(*(q->set_value));
+			break;
+		    case SET_ARG:
+			*(q->set_value) = TRUE;
+			break;
+		    case UNSET_ARG:
+			*(q->set_value) = FALSE;
+			break;
+		    }
+		} else if (is_true(next_arg)) {
+		    *(q->set_value) = TRUE;
+		} else if (is_false(next_arg)) {
+		    *(q->set_value) = FALSE;
+		}
+		/* deliberately ignore anything else - BL */
+	    }
+	    break;
+
+	case FUNCTION_ARG:
+	    fun = q->fun_value;
+	    if (0 != fun) {
+		if (-1 == (*fun) (next_arg)) {
+		}
+	    }
+	    break;
+
+	case LYSTRING_ARG:
+	    if ((q->str_value != 0) && (next_arg != 0))
+		StrAllocCopy(*(q->str_value), next_arg);
+	    break;
+
+	case INT_ARG:
+	    if ((q->int_value != 0) && (next_arg != 0))
+		*(q->int_value) = strtol(next_arg, &temp_ptr, 0);
+	    break;
+
+	case TIME_ARG:
+	    if ((q->int_value != 0) && (next_arg != 0)) {
+		float ival;
+
+		if (1 == LYscanFloat(next_arg, &ival)) {
+		    *(q->int_value) = (int) SECS2Secs(ival);
+		}
+	    }
+	    break;
+
+	case STRING_ARG:
+	    if ((q->str_value != 0) && (next_arg != 0))
+		*(q->str_value) = next_arg;
+	    break;
+	}
+
+	Old_DTD = DTD_recovery;	/* BOOL != int */
+	return TRUE;
+    }
+
+    if (pgm == 0)
+	pgm = "LYNX";
+
+    fprintf(stderr, gettext("%s: Invalid Option: %s\n"), pgm, argv[0]);
+    print_help_and_exit(-1);
+    return FALSE;
+}
+
+#ifndef VMS
+static void FatalProblem(int sig)
+{
+    /*
+     * Ignore further interrupts.  - mhc:  11/2/91
+     */
+#ifndef NOSIGHUP
+    (void) signal(SIGHUP, SIG_IGN);
+#endif /* NOSIGHUP */
+    (void) signal(SIGTERM, SIG_IGN);
+    (void) signal(SIGINT, SIG_IGN);
+#ifndef __linux__
+#ifdef SIGBUS
+    (void) signal(SIGBUS, SIG_IGN);
+#endif /* ! SIGBUS */
+#endif /* !__linux__ */
+    (void) signal(SIGSEGV, SIG_IGN);
+    (void) signal(SIGILL, SIG_IGN);
+
+    /*
+     * Flush all messages.  - FM
+     */
+    fflush(stderr);
+    fflush(stdout);
+
+    /*
+     * Deal with curses, if on, and clean up.  - FM
+     */
+    if (LYOutOfMemory && LYCursesON) {
+	LYSleepAlert();
+    }
+    cleanup_sig(0);
+#ifndef __linux__
+#ifdef SIGBUS
+    signal(SIGBUS, SIG_DFL);
+#endif /* SIGBUS */
+#endif /* !__linux__ */
+    signal(SIGSEGV, SIG_DFL);
+    signal(SIGILL, SIG_DFL);
+
+    /*
+     * Issue appropriate messages and abort or exit.  - FM
+     */
+    if (LYOutOfMemory == FALSE) {
+	fprintf(stderr, "\r\n\
+A Fatal error has occurred in %s Ver. %s\r\n", LYNX_NAME, LYNX_VERSION);
+
+	fprintf(stderr, "\r\n\
+Please notify your system administrator to confirm a bug, and\r\n\
+if confirmed, to notify the lynx-dev list.  Bug reports should\r\n\
+have concise descriptions of the command and/or URL which causes\r\n\
+the problem, the operating system name with version number, the\r\n\
+TCPIP implementation, and any other relevant information.\r\n");
+
+	if (!(sig == 0 && LYNoCore)) {
+	    fprintf(stderr, "\r\n\
+Do NOT mail the core file if one was generated.\r\n");
+	}
+	if (sig != 0) {
+	    fprintf(stderr, "\r\n\
+Lynx now exiting with signal:  %d\r\n\r\n", sig);
+#ifdef WIN_EX			/* 1998/08/09 (Sun) 09:58:25 */
+	    {
+		char *msg;
+
+		switch (sig) {
+		case SIGABRT:
+		    msg = "SIGABRT";
+		    break;
+		case SIGFPE:
+		    msg = "SIGFPE";
+		    break;
+		case SIGILL:
+		    msg = "SIGILL";
+		    break;
+		case SIGSEGV:
+		    msg = "SIGSEGV";
+		    break;
+		default:
+		    msg = "Not-def";
+		    break;
+		}
+		fprintf(stderr, "signal code = %s\n", msg);
+	    }
+#endif
+	}
+
+	/*
+	 * Exit and possibly dump core.
+	 */
+	if (LYNoCore) {
+	    exit_immediately(EXIT_FAILURE);
+	}
+	abort();
+
+    } else {
+	LYOutOfMemory = FALSE;
+	printf("\r\n%s\r\n\r\n", MEMORY_EXHAUSTED_ABORT);
+	fflush(stdout);
+
+	/*
+	 * Exit without dumping core.
+	 */
+	exit_immediately(EXIT_FAILURE);
+    }
+}
+#endif /* !VMS */
diff --git a/src/LYMainLoop.c b/src/LYMainLoop.c
new file mode 100644
index 00000000..585c1e12
--- /dev/null
+++ b/src/LYMainLoop.c
@@ -0,0 +1,7862 @@
+/*
+ * $LynxId: LYMainLoop.c,v 1.163 2010/04/29 22:11:50 tom Exp $
+ */
+#include <HTUtils.h>
+#include <HTAccess.h>
+#include <HTParse.h>
+#include <HTList.h>
+#include <HTML.h>
+#include <HTFTP.h>
+#include <HTFile.h>
+#include <HTTP.h>
+#include <HTAABrow.h>
+#include <HTNews.h>
+#include <LYCurses.h>
+#include <LYStyle.h>
+#include <LYGlobalDefs.h>
+#include <HTAlert.h>
+#include <LYUtils.h>
+#include <GridText.h>
+#include <LYStrings.h>
+#include <LYOptions.h>
+#include <LYSignal.h>
+#include <LYGetFile.h>
+#include <HTForms.h>
+#include <LYSearch.h>
+#include <LYClean.h>
+#include <LYHistory.h>
+#include <LYPrint.h>
+#include <LYMail.h>
+#include <LYEdit.h>
+#include <LYShowInfo.h>
+#include <LYBookmark.h>
+#include <LYKeymap.h>
+#include <LYJump.h>
+#include <LYDownload.h>
+#include <LYList.h>
+#include <LYMap.h>
+#include <LYTraversal.h>
+#include <LYCharSets.h>
+#include <LYCharUtils.h>
+#include <LYCookie.h>
+#include <LYMainLoop.h>
+#include <LYPrettySrc.h>
+
+#ifdef USE_SESSIONS
+#include <LYSession.h>
+#endif
+
+#ifdef KANJI_CODE_OVERRIDE
+#include <HTCJK.h>
+#endif
+
+#define LinkIsTextarea(linkNumber) \
+		(links[linkNumber].type == WWW_FORM_LINK_TYPE && \
+		 links[linkNumber].l_form->type == F_TEXTAREA_TYPE)
+
+#ifdef KANJI_CODE_OVERRIDE
+char *str_kcode(HTkcode code)
+{
+    char *p;
+    static char buff[8];
+
+    if (current_char_set == TRANSPARENT) {
+	p = "THRU";
+    } else if (!LYRawMode) {
+	p = "RAW";
+    } else {
+	switch (code) {
+	case NOKANJI:
+	    p = "AUTO";
+	    break;
+
+	case EUC:
+	    p = "EUC+";
+	    break;
+
+	case SJIS:
+	    p = "SJIS";
+	    break;
+
+	case JIS:
+	    p = " JIS";
+	    break;
+
+	default:
+	    p = " ???";
+	    break;
+	}
+    }
+
+    if (no_table_center) {
+	buff[0] = '!';
+	strcpy(buff + 1, p);
+    } else {
+	strcpy(buff, p);
+    }
+
+    return buff;
+}
+#endif
+
+#ifdef WIN_EX
+
+static char *str_sjis(char *to, char *from)
+{
+    if (!LYRawMode) {
+	strcpy(to, from);
+#ifdef KANJI_CODE_OVERRIDE
+    } else if (last_kcode == EUC) {
+	EUC_TO_SJIS(from, to);
+    } else if (last_kcode == SJIS) {
+	strcpy(to, from);
+#endif
+    } else {
+	TO_SJIS(from, to);
+    }
+    return to;
+}
+
+static void set_ws_title(char *str)
+{
+    SetConsoleTitle(str);
+}
+
+#endif /* WIN_EX */
+
+#if defined(USE_EXTERNALS) || defined(WIN_EX)
+#include <LYExtern.h>
+#endif
+
+#ifdef __EMX__
+#include <io.h>
+#endif
+
+#ifdef DIRED_SUPPORT
+#include <LYLocal.h>
+#include <LYUpload.h>
+#endif /* DIRED_SUPPORT */
+
+#include <LYexit.h>
+#include <LYLeaks.h>
+
+/* two constants: */
+HTLinkType *HTInternalLink = 0;
+HTAtom *WWW_SOURCE = 0;
+
+#ifndef DONT_TRACK_INTERNAL_LINKS
+#define NO_INTERNAL_OR_DIFFERENT(c,n) TRUE
+#define NONINTERNAL_OR_PHYS_DIFFERENT(p,n) (!curdoc.internal_link || \
+			   are_phys_different(p,n))
+#else /* TRACK_INTERNAL_LINKS */
+#define NO_INTERNAL_OR_DIFFERENT(c,n) are_different(c,n)
+#define NONINTERNAL_OR_PHYS_DIFFERENT(p,n) are_different(p,n)
+#endif /* TRACK_INTERNAL_LINKS */
+
+static void exit_immediately_with_error_message(int state, BOOLEAN first_file);
+static void status_link(char *curlink_name, BOOLEAN show_more, BOOLEAN show_indx);
+static void show_main_statusline(const LinkInfo curlink, int for_what);
+static void form_noviceline(int);
+static int are_different(DocInfo *doc1, DocInfo *doc2);
+
+#ifndef DONT_TRACK_INTERNAL_LINKS
+static int are_phys_different(DocInfo *doc1, DocInfo *doc2);
+#endif
+
+#define FASTTAB
+
+static int sametext(char *een,
+		    char *twee)
+{
+    if (een && twee)
+	return (strcmp(een, twee) == 0);
+    return TRUE;
+}
+
+HTList *Goto_URLs = NULL;	/* List of Goto URLs */
+
+char *LYRequestTitle = NULL;	/* newdoc.title in calls to getfile() */
+char *LYRequestReferer = NULL;	/* Referer, may be set in getfile() */
+
+static char prev_target[MAX_LINE];
+
+#ifdef DISP_PARTIAL
+BOOLEAN display_partial = FALSE;	/* could be enabled in HText_new() */
+int NumOfLines_partial = 0;	/* number of lines displayed in partial mode */
+#endif
+
+static int Newline = 0;
+static DocInfo newdoc;
+static DocInfo curdoc;
+static char *traversal_host = NULL;
+static char *traversal_link_to_add = NULL;
+static char *owner_address = NULL;	/* Holds the responsible owner's address     */
+static char *ownerS_address = NULL;	/* Holds owner's address during source fetch */
+
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+static BOOL textinput_activated = FALSE;
+
+#else
+#define textinput_activated TRUE	/* a current text input is always active */
+#endif
+#ifdef INACTIVE_INPUT_STYLE_VH
+BOOL textinput_redrawn = FALSE;
+
+    /*must be public since used in LYhighlight(..) */
+#endif
+
+#ifdef LY_FIND_LEAKS
+/*
+ * Function for freeing allocated mainloop() variables.  - FM
+ */
+static void free_mainloop_variables(void)
+{
+    LYFreeDocInfo(&newdoc);
+    LYFreeDocInfo(&curdoc);
+
+#ifdef USE_COLOR_STYLE
+    FREE(curdoc.style);
+    FREE(newdoc.style);
+#endif
+    FREE(traversal_host);
+    FREE(traversal_link_to_add);
+    FREE(owner_address);
+    FREE(ownerS_address);
+#ifdef DIRED_SUPPORT
+    clear_tags();
+    reset_dired_menu();
+#endif /* DIRED_SUPPORT */
+    FREE(WWW_Download_File);	/* LYGetFile.c/HTFWriter.c */
+    FREE(LYRequestReferer);
+
+    return;
+}
+#endif /* LY_FIND_LEAKS */
+
+#ifndef NO_LYNX_TRACE
+static void TracelogOpenFailed(void)
+{
+    WWW_TraceFlag = FALSE;
+    if (LYCursesON) {
+	HTUserMsg(TRACELOG_OPEN_FAILED);
+    } else {
+	fprintf(stderr, "%s\n", TRACELOG_OPEN_FAILED);
+	exit_immediately(EXIT_FAILURE);
+    }
+}
+
+static BOOLEAN LYReopenTracelog(BOOLEAN *trace_flag_ptr)
+{
+    CTRACE((tfp, "\nTurning off TRACE for fetch of log.\n"));
+    LYCloseTracelog();
+    if ((LYTraceLogFP = LYAppendToTxtFile(LYTraceLogPath)) == NULL) {
+	TracelogOpenFailed();
+	return FALSE;
+    }
+    if (TRACE) {
+	WWW_TraceFlag = FALSE;
+	*trace_flag_ptr = TRUE;
+    }
+    return TRUE;
+}
+
+static void turn_trace_back_on(BOOLEAN *trace_flag_ptr)
+{
+    if (*trace_flag_ptr == TRUE) {
+	WWW_TraceFlag = TRUE;
+	*trace_flag_ptr = FALSE;
+	fprintf(tfp, "Turning TRACE back on.\n\n");
+    }
+}
+#else
+#define LYReopenTracelog(flag) TRUE
+#define turn_trace_back_on(flag)	/*nothing */
+#endif /* NO_LYNX_TRACE */
+
+FILE *TraceFP(void)
+{
+#ifndef NO_LYNX_TRACE
+    if (LYTraceLogFP != 0) {
+	return LYTraceLogFP;
+    }
+#endif /* NO_LYNX_TRACE */
+    return stderr;
+}
+
+BOOLEAN LYOpenTraceLog(void)
+{
+#ifndef NO_LYNX_TRACE
+    if (TRACE && LYUseTraceLog && LYTraceLogFP == NULL) {
+	/*
+	 * If we can't open it for writing, give up.  Otherwise, on VMS close
+	 * it, delete it and any versions from previous sessions so they don't
+	 * accumulate, and open it again.  - FM
+	 */
+	if ((LYTraceLogFP = LYNewTxtFile(LYTraceLogPath)) == NULL) {
+	    TracelogOpenFailed();
+	    return FALSE;
+	}
+#ifdef VMS
+	LYCloseTracelog();
+	HTSYS_remove(LYTraceLogPath);
+	if ((LYTraceLogFP = LYNewTxtFile(LYTraceLogPath)) == NULL) {
+	    TracelogOpenFailed();
+	    return FALSE;
+	}
+#endif /* VMS */
+	fflush(stdout);
+	fflush(stderr);
+	fprintf(tfp, "\t\t%s (%s)\n\n", LYNX_TRACELOG_TITLE, LYNX_VERSION);
+	/*
+	 * If TRACE is on, indicate whether the anonymous restrictions are set. 
+	 * - FM, LP, kw
+	 *
+	 * This is only a summary for convenience - it doesn't take the case of
+	 * individual -restrictions= options into account.  - kw
+	 */
+	if (LYValidate) {
+	    if (LYRestricted && had_restrictions_default) {
+		CTRACE((tfp,
+			"Validate and some anonymous restrictions are set.\n"));
+	    } else if (had_restrictions_default) {
+		CTRACE((tfp,
+			"Validate restrictions set, restriction \"default\" was given.\n"));
+	    } else if (LYRestricted) {
+		CTRACE((tfp,
+			"Validate restrictions set, additional anonymous restrictions ignored.\n"));
+	    } else {
+		CTRACE((tfp, "Validate restrictions are set.\n"));
+	    }
+	    /* But none of the above can actually happen, since there should
+	     * never be a Trace Log with -validate.  If it appears in a log
+	     * file something went wrong! */
+	} else if (LYRestricted) {
+	    if (had_restrictions_all) {
+		CTRACE((tfp,
+			"Anonymous restrictions set, restriction \"all\" was given.\n"));
+	    } else {
+		CTRACE((tfp, "Anonymous restrictions are set.\n"));
+	    }
+	} else if (had_restrictions_all && had_restrictions_default) {
+	    CTRACE((tfp, "Restrictions \"all\" and \"default\" were given.\n"));
+	} else if (had_restrictions_default) {
+	    CTRACE((tfp, "Restriction \"default\" was given.\n"));
+	} else if (had_restrictions_all) {
+	    CTRACE((tfp, "\"all\" restrictions are set.\n"));
+	}
+    }
+#endif /* NO_LYNX_TRACE */
+    return TRUE;
+}
+
+void LYCloseTracelog(void)
+{
+#ifndef NO_LYNX_TRACE
+    if (LYTraceLogFP != 0) {
+	fflush(stdout);
+	fflush(stderr);
+	fclose(LYTraceLogFP);
+	LYTraceLogFP = 0;
+    }
+#endif /* NO_LYNX_TRACE */
+}
+
+void handle_LYK_TRACE_TOGGLE(void)
+{
+#ifndef NO_LYNX_TRACE
+    WWW_TraceFlag = (BOOLEAN) !WWW_TraceFlag;
+    if (LYOpenTraceLog())
+	HTUserMsg(WWW_TraceFlag ? TRACE_ON : TRACE_OFF);
+#else
+    HTUserMsg(TRACE_DISABLED);
+#endif /* NO_LYNX_TRACE */
+}
+
+void LYSetNewline(int value)
+{
+    Newline = value;
+}
+
+#define LYSetNewline(value)	Newline = value
+
+int LYGetNewline(void)
+{
+    return Newline;
+}
+
+#define LYGetNewline()		Newline
+
+void LYChgNewline(int adjust)
+{
+    LYSetNewline(Newline + adjust);
+}
+
+#define LYChgNewline(adjust)	Newline += (adjust)
+
+#ifdef USE_SOURCE_CACHE
+static BOOLEAN from_source_cache = FALSE;
+
+/*
+ * Like HTreparse_document(), but also set the flag.
+ */
+static BOOLEAN reparse_document(void)
+{
+    BOOLEAN result;
+
+    from_source_cache = TRUE;	/* set for LYMainLoop_pageDisplay() */
+    if ((result = HTreparse_document()) != FALSE) {
+	from_source_cache = TRUE;	/* set for mainloop refresh */
+    } else {
+	from_source_cache = FALSE;
+    }
+    return result;
+}
+#endif /* USE_SOURCE_CACHE */
+
+/*
+ * Prefer reparsing if we can, but reload if we must - to force regeneration
+ * of the display.
+ */
+static BOOLEAN reparse_or_reload(int *cmd)
+{
+#ifdef USE_SOURCE_CACHE
+    if (reparse_document()) {
+	return FALSE;
+    }
+#endif
+    *cmd = LYK_RELOAD;
+    return TRUE;
+}
+
+/*
+ * Functions for setting the current address
+ */
+static void set_address(DocInfo *doc, const char *address)
+{
+    StrAllocCopy(doc->address, address);
+}
+
+static void copy_address(DocInfo *dst, DocInfo *src)
+{
+    StrAllocCopy(dst->address, src->address);
+}
+
+static void free_address(DocInfo *doc)
+{
+    FREE(doc->address);
+}
+
+static void move_address(DocInfo *dst, DocInfo *src)
+{
+    copy_address(dst, src);
+    free_address(src);
+}
+
+#ifdef DISP_PARTIAL
+/*
+ * This is for traversal call from within partial mode in LYUtils.c
+ * and HTFormat.c  It simply calls HText_pageDisplay() but utilizes
+ * LYMainLoop.c static variables to manage proper newline position
+ * in case of #fragment
+ */
+BOOL LYMainLoop_pageDisplay(int line_num)
+{
+    const char *pound;
+    int prev_newline = LYGetNewline();
+
+    /*
+     * Override Newline with a new value if user scrolled the document while
+     * loading (in LYUtils.c).
+     */
+    LYSetNewline(line_num);
+
+#ifdef USE_SOURCE_CACHE
+    /*
+     * reparse_document() acts on 'curdoc' which always on top of the
+     * history stack: no need to resolve #fragment position since
+     * we already know it (curdoc.line).
+     * So bypass here. Sorry for possible confusion...
+     */
+    if (!from_source_cache)
+#endif
+	/*
+	 * If the requested URL has the #fragment, and we are not popped
+	 * from the history stack, and have not scrolled the document yet -
+	 * we should calculate correct newline position for the fragment.
+	 * (This is a bit suboptimal since HTFindPoundSelector() traverse
+	 * anchors list each time, so we have a quadratic complexity
+	 * and may load CPU in a worst case).
+	 */
+	if (display_partial
+	    && newdoc.line == 1 && line_num == 1 && prev_newline == 1
+	    && (pound = findPoundSelector(newdoc.address))
+	    && *pound && *(pound + 1)) {
+	    if (HTFindPoundSelector(pound + 1)) {
+		/* HTFindPoundSelector will initialize www_search_result */
+		LYSetNewline(www_search_result);
+	    } else {
+		LYSetNewline(prev_newline);	/* restore ??? */
+		return NO;	/* no repaint */
+	    }
+	}
+
+    HText_pageDisplay(LYGetNewline(), prev_target);
+    return YES;
+}
+#endif /* DISP_PARTIAL */
+
+static BOOL set_curdoc_link(int nextlink)
+{
+    BOOL result = FALSE;
+
+    if (curdoc.link != nextlink
+	&& nextlink >= 0
+	&& nextlink < nlinks) {
+	if (curdoc.link >= 0 && curdoc.link < nlinks) {
+	    LYhighlight(OFF, curdoc.link, prev_target);
+	    result = TRUE;
+	}
+	curdoc.link = nextlink;
+    }
+    return result;
+}
+
+/*
+ * Setup newdoc to jump to the given line.
+ *
+ * FIXME: prefer to also jump to the link given in a URL fragment, but the
+ * interface of getfile() does not provide that ability yet.
+ */
+static void goto_line(int nextline)
+{
+    int n;
+    int old_link = newdoc.link;
+
+    newdoc.link = 0;
+    for (n = 0; n < nlinks; ++n) {
+	if (nextline == links[n].anchor_line_num + 1) {
+	    CTRACE((tfp, "top_of_screen %d\n", HText_getTopOfScreen() + 1));
+	    CTRACE((tfp, "goto_line(%d) -> link %d -> %d\n", nextline,
+		    old_link, n));
+	    newdoc.link = n;
+	    break;
+	}
+    }
+}
+
+#ifdef USE_MOUSE
+static void set_curdoc_link_by_mouse(int nextlink)
+{
+    if (set_curdoc_link(nextlink)) {
+	LYhighlight(ON, nextlink, prev_target);
+	LYmsec_delay(20);
+    }
+}
+#else
+#define set_curdoc_link_by_mouse(nextlink) set_curdoc_link(nextlink)
+#endif
+
+static int do_change_link(void)
+{
+#ifdef USE_MOUSE
+    /* Is there a mouse-clicked link waiting? */
+    int mouse_tmp = get_mouse_link();
+
+    /* If yes, use it as the link */
+    if (mouse_tmp != -1) {
+	if (mouse_tmp < 0 || mouse_tmp >= nlinks) {
+	    char *msgtmp = NULL;
+
+	    HTSprintf0(&msgtmp,
+		       gettext("Internal error: Invalid mouse link %d!"),
+		       mouse_tmp);
+	    HTAlert(msgtmp);
+	    FREE(msgtmp);
+	    return (-1);	/* indicates unexpected error */
+	}
+	set_curdoc_link_by_mouse(mouse_tmp);
+    }
+#endif /* USE_MOUSE */
+    return (0);			/* indicates OK */
+}
+
+#ifdef DIRED_SUPPORT
+#define DIRED_UNCACHE_1 if (LYAutoUncacheDirLists < 1) /*nothing*/ ;\
+			else HTuncache_current_document()
+#define DIRED_UNCACHE_2 if (LYAutoUncacheDirLists < 2) /*nothing*/ ;\
+			else HTuncache_current_document()
+#endif /* DIRED_SUPPORT */
+
+static void do_check_goto_URL(char *user_input_buffer,
+			      char **old_user_input,
+			      BOOLEAN *force_load)
+{
+    static BOOLEAN always = TRUE;
+    /* *INDENT-OFF* */
+    static struct {
+	const char *name;
+	BOOLEAN *flag;
+    } table[] = {
+	{ STR_FILE_URL,		&no_file_url },
+	{ STR_FILE_URL,		&no_goto_file },
+	{ STR_LYNXEXEC,		&no_goto_lynxexec },
+	{ STR_LYNXPROG,		&no_goto_lynxprog },
+	{ STR_LYNXCGI,		&no_goto_lynxcgi },
+	{ STR_CSO_URL,		&no_goto_cso },
+	{ STR_FINGER_URL,	&no_goto_finger },
+	{ STR_FTP_URL,		&no_goto_ftp },
+	{ STR_GOPHER_URL,	&no_goto_gopher },
+	{ STR_HTTP_URL,		&no_goto_http },
+	{ STR_HTTPS_URL,	&no_goto_https },
+	{ STR_MAILTO_URL,	&no_goto_mailto },
+	{ STR_RLOGIN_URL,	&no_goto_rlogin },
+	{ STR_TELNET_URL,	&no_goto_telnet },
+	{ STR_TN3270_URL,	&no_goto_tn3270 },
+	{ STR_WAIS_URL,		&no_goto_wais },
+#ifndef DISABLE_BIBP
+	{ STR_BIBP_URL,		&no_goto_bibp },
+#endif
+#ifndef DISABLE_NEWS
+	{ STR_NEWS_URL,		&no_goto_news },
+	{ STR_NNTP_URL,		&no_goto_nntp },
+	{ STR_SNEWS_URL,	&no_goto_snews },
+#endif
+#ifdef EXEC_LINKS
+	{ STR_LYNXEXEC,		&local_exec_on_local_files },
+	{ STR_LYNXPROG,		&local_exec_on_local_files },
+#endif /* EXEC_LINKS */
+	{ STR_LYNXCFG,		&no_goto_configinfo },
+	{ STR_LYNXCFLAGS,	&no_goto_configinfo },
+	{ STR_LYNXCOOKIE,	&always },
+#ifdef USE_CACHEJAR
+	{ STR_LYNXCACHE,	&always },
+#endif
+	{ STR_LYNXDIRED,	&always },
+	{ STR_LYNXDOWNLOAD,	&always },
+	{ STR_LYNXOPTIONS,	&always },
+	{ STR_LYNXPRINT,	&always },
+    };
+    /* *INDENT-ON* */
+
+    unsigned n;
+    BOOLEAN found = FALSE;
+
+    /* allow going to anchors */
+    if (*user_input_buffer == '#') {
+	if (user_input_buffer[1] &&
+	    HTFindPoundSelector(user_input_buffer + 1)) {
+	    /* HTFindPoundSelector will initialize www_search_result,
+	       so we do nothing else. */
+	    HTAddGotoURL(user_input_buffer);
+	    trimPoundSelector(curdoc.address);
+	    StrAllocCat(curdoc.address, user_input_buffer);
+	}
+    } else {
+	/*
+	 * If it's not a URL then make it one.
+	 */
+	StrAllocCopy(*old_user_input, user_input_buffer);
+	LYEnsureAbsoluteURL(old_user_input, "", TRUE);
+	sprintf(user_input_buffer, "%.*s",
+		(int) (MAX_LINE - 1), *old_user_input);
+	FREE(*old_user_input);
+
+	for (n = 0; n < TABLESIZE(table); n++) {
+	    if (*(table[n].flag)
+		&& !strncmp(user_input_buffer, table[n].name, strlen(table[n].name))) {
+		found = TRUE;
+		HTUserMsg2(GOTO_XXXX_DISALLOWED, table[n].name);
+		break;
+	    }
+	}
+	if (found) {
+	    ;
+	} else if (LYValidate &&
+		   !isHTTP_URL(user_input_buffer) &&
+		   !isHTTPS_URL(user_input_buffer)) {
+	    HTUserMsg(GOTO_NON_HTTP_DISALLOWED);
+
+	} else {
+	    set_address(&newdoc, user_input_buffer);
+	    newdoc.isHEAD = FALSE;
+	    /*
+	     * Might be an anchor in the same doc from a POST form.  If so,
+	     * dont't free the content.  -- FM
+	     */
+	    if (are_different(&curdoc, &newdoc)) {
+		/*
+		 * Make a name for this new URL.
+		 */
+		StrAllocCopy(newdoc.title,
+			     gettext("A URL specified by the user"));
+		LYFreePostData(&newdoc);
+		FREE(newdoc.bookmark);
+		newdoc.safe = FALSE;
+		newdoc.internal_link = FALSE;
+		*force_load = TRUE;
+#ifdef DIRED_SUPPORT
+		if (lynx_edit_mode) {
+		    DIRED_UNCACHE_2;
+		}
+#endif /* DIRED_SUPPORT */
+	    }
+	    LYUserSpecifiedURL = TRUE;
+	    HTAddGotoURL(newdoc.address);
+	}
+    }
+}
+
+/* returns FALSE if user cancelled input or URL was invalid, TRUE otherwise */
+static BOOL do_check_recall(int ch,
+			    char *user_input_buffer,
+			    char **old_user_input,
+			    int URLTotal,
+			    int *URLNum,
+			    RecallType recall,
+			    BOOLEAN *FirstURLRecall)
+{
+    char *cp;
+    BOOL ret = FALSE;
+
+    if (*old_user_input == 0)
+	StrAllocCopy(*old_user_input, "");
+
+    for (;;) {
+#ifdef WIN_EX			/* 1998/10/11 (Sun) 10:41:05 */
+	int len = strlen(user_input_buffer);
+
+	if (len >= 3) {
+	    if (len < MAX_LINE - 1
+		&& LYIsHtmlSep(user_input_buffer[len - 3])
+		&& LYIsDosDrive(user_input_buffer + len - 2))
+		LYAddPathSep0(user_input_buffer);
+
+	} else if (len == 2 && user_input_buffer[1] == ':') {
+	    if (LYIsDosDrive(user_input_buffer)) {
+		LYAddPathSep0(user_input_buffer);
+	    } else {
+		HTUserMsg2(WWW_ILLEGAL_URL_MESSAGE, user_input_buffer);
+		LYstrncpy(user_input_buffer, *old_user_input, MAX_LINE - 1);
+		FREE(*old_user_input);
+		ret = FALSE;
+		break;
+	    }
+	}
+#endif
+	/*
+	 * Get rid of leading spaces (and any other spaces).
+	 */
+	LYTrimAllStartfile(user_input_buffer);
+	if (*user_input_buffer == '\0' &&
+	    !(recall && (ch == UPARROW || ch == DNARROW))) {
+	    LYstrncpy(user_input_buffer, *old_user_input, MAX_LINE - 1);
+	    FREE(*old_user_input);
+	    HTInfoMsg(CANCELLED);
+	    ret = FALSE;
+	    break;
+	}
+	if (recall && ch == UPARROW) {
+	    if (*FirstURLRecall) {
+		/*
+		 * Use last URL in the list.  - FM
+		 */
+		*FirstURLRecall = FALSE;
+		*URLNum = 0;
+	    } else {
+		/*
+		 * Go back to the previous URL in the list.  - FM
+		 */
+		*URLNum += 1;
+	    }
+	    if (*URLNum >= URLTotal)
+		/*
+		 * Roll around to the last URL in the list.  - FM
+		 */
+		*URLNum = 0;
+	    if ((cp = (char *) HTList_objectAt(Goto_URLs,
+					       *URLNum)) != NULL) {
+		LYstrncpy(user_input_buffer, cp, MAX_LINE - 1);
+		if (goto_buffer
+		    && **old_user_input
+		    && !strcmp(*old_user_input, user_input_buffer)) {
+		    _statusline(EDIT_CURRENT_GOTO);
+		} else if ((goto_buffer && URLTotal == 2) ||
+			   (!goto_buffer && URLTotal == 1)) {
+		    _statusline(EDIT_THE_PREV_GOTO);
+		} else {
+		    _statusline(EDIT_A_PREV_GOTO);
+		}
+		if ((ch = LYgetstr(user_input_buffer, VISIBLE,
+				   MAX_LINE,
+				   recall)) < 0) {
+		    /*
+		     * User cancelled the Goto via ^G.  Restore
+		     * user_input_buffer and break.  - FM
+		     */
+		    LYstrncpy(user_input_buffer, *old_user_input, MAX_LINE - 1);
+		    FREE(*old_user_input);
+		    HTInfoMsg(CANCELLED);
+		    ret = FALSE;
+		    break;
+		}
+		continue;
+	    }
+	} else if (recall && ch == DNARROW) {
+	    if (*FirstURLRecall) {
+		/*
+		 * Use the first URL in the list.  - FM
+		 */
+		*FirstURLRecall = FALSE;
+		*URLNum = URLTotal - 1;
+	    } else {
+		/*
+		 * Advance to the next URL in the list.  - FM
+		 */
+		*URLNum -= 1;
+	    }
+	    if (*URLNum < 0)
+		/*
+		 * Roll around to the first URL in the list.  - FM
+		 */
+		*URLNum = URLTotal - 1;
+	    if ((cp = (char *) HTList_objectAt(Goto_URLs, *URLNum)) != NULL) {
+		LYstrncpy(user_input_buffer, cp, MAX_LINE - 1);
+		if (goto_buffer && **old_user_input &&
+		    !strcmp(*old_user_input, user_input_buffer)) {
+		    _statusline(EDIT_CURRENT_GOTO);
+		} else if ((goto_buffer && URLTotal == 2) ||
+			   (!goto_buffer && URLTotal == 1)) {
+		    _statusline(EDIT_THE_PREV_GOTO);
+		} else {
+		    _statusline(EDIT_A_PREV_GOTO);
+		}
+		if ((ch = LYgetstr(user_input_buffer, VISIBLE,
+				   MAX_LINE,
+				   recall)) < 0) {
+		    /*
+		     * User cancelled the Goto via ^G.  Restore
+		     * user_input_buffer and break.  - FM
+		     */
+		    LYstrncpy(user_input_buffer, *old_user_input, MAX_LINE - 1);
+		    FREE(*old_user_input);
+		    HTInfoMsg(CANCELLED);
+		    ret = FALSE;
+		    break;
+		}
+		continue;
+	    }
+	} else {
+	    ret = TRUE;
+	    break;
+	}
+    }
+    return ret;
+}
+
+static void do_cleanup_after_delete(void)
+{
+    HTuncache_current_document();
+    move_address(&newdoc, &curdoc);
+    newdoc.line = curdoc.line;
+    if (curdoc.link == nlinks - 1) {
+	/*
+	 * We deleted the last link on the page.  - FM
+	 */
+	newdoc.link = curdoc.link - 1;
+    } else {
+	newdoc.link = curdoc.link;
+    }
+}
+
+static int find_link_near_col(int col,
+			      int delta)
+{
+    int i;
+
+    for (i = curdoc.link; delta > 0 ? (i < nlinks) : (i >= 0); i += delta) {
+	if ((links[i].ly - links[curdoc.link].ly) * delta > 0) {
+	    int cy = links[i].ly, best = -1, dist = 1000000;
+
+	    while ((delta > 0 ? (i < nlinks) : (i >= 0)) && cy == links[i].ly) {
+		int cx = links[i].lx;
+		const char *text = LYGetHiliteStr(i, 0);
+
+		if (text != NULL)
+		    cx += (int) strlen(text) / 2;
+		cx -= col;
+		if (cx < 0)
+		    cx = -cx;
+		if (cx < dist) {
+		    dist = cx;
+		    best = i;
+		}
+		i += delta;
+	    }
+	    return (best);
+	}
+    }
+    return (-1);
+}
+
+/*
+ * This is a special feature to traverse every http link derived from startfile
+ * and check for errors or create crawl output files.  Only URL's that begin
+ * with "traversal_host" are searched - this keeps the search from crossing to
+ * other servers (a feature, not a bug!).
+ */
+static int DoTraversal(int c,
+		       BOOLEAN *crawl_ok)
+{
+    BOOLEAN rlink_rejected = FALSE;
+    BOOLEAN rlink_exists;
+    BOOLEAN rlink_allowed;
+
+    rlink_exists = (BOOL) (nlinks > 0 &&
+			   links[curdoc.link].type != WWW_FORM_LINK_TYPE &&
+			   links[curdoc.link].lname != NULL);
+
+    if (rlink_exists) {
+	rlink_rejected = lookup_reject(links[curdoc.link].lname);
+	if (!rlink_rejected &&
+	    traversal_host &&
+	    links[curdoc.link].lname) {
+	    if (!isLYNXIMGMAP(links[curdoc.link].lname)) {
+		rlink_allowed = (BOOL) !strncmp(traversal_host,
+						links[curdoc.link].lname,
+						strlen(traversal_host));
+	    } else {
+		rlink_allowed = (BOOL) !strncmp(traversal_host,
+						links[curdoc.link].lname + LEN_LYNXIMGMAP,
+						strlen(traversal_host));
+	    }
+	} else {
+	    rlink_allowed = FALSE;
+	}
+    } else {
+	rlink_allowed = FALSE;
+    }
+    if (rlink_exists && rlink_allowed) {
+	if (lookup_link(links[curdoc.link].lname)) {
+	    if (more_links ||
+		(curdoc.link > -1 && curdoc.link < nlinks - 1)) {
+		c = DNARROW;
+	    } else {
+		if (STREQ(curdoc.title, "Entry into main screen") ||
+		    (nhist <= 0)) {
+		    if (!dump_output_immediately) {
+			cleanup();
+			exit_immediately(EXIT_FAILURE);
+		    }
+		    c = -1;
+		} else {
+		    c = LTARROW;
+		}
+	    }
+	} else {
+	    StrAllocCopy(traversal_link_to_add,
+			 links[curdoc.link].lname);
+	    if (!isLYNXIMGMAP(traversal_link_to_add))
+		*crawl_ok = TRUE;
+	    c = RTARROW;
+	}
+    } else {			/* no good right link, so only down and left arrow ok */
+	if (rlink_exists /* && !rlink_rejected */ )
+	    /* uncomment in previous line to avoid duplicates - kw */
+	    add_to_reject_list(links[curdoc.link].lname);
+	if (more_links ||
+	    (curdoc.link > -1 && curdoc.link < nlinks - 1)) {
+	    c = DNARROW;
+	} else {
+	    /*
+	     * curdoc.title doesn't always work, so bail out if the history
+	     * list is empty.
+	     */
+	    if (STREQ(curdoc.title, "Entry into main screen") ||
+		(nhist <= 0)) {
+		if (!dump_output_immediately) {
+		    cleanup();
+		    exit_immediately(EXIT_FAILURE);
+		}
+		c = -1;
+	    } else {
+		c = LTARROW;
+	    }
+	}
+    }
+    CTRACE((tfp, "DoTraversal(%d:%d) -> %s\n",
+	    nlinks > 0 ? curdoc.link : 0,
+	    nlinks,
+	    LYKeycodeToString(c, FALSE)));
+    return c;
+}
+
+#ifndef DONT_TRACK_INTERNAL_LINKS
+static BOOLEAN check_history(void)
+{
+    const char *base;
+
+    if (!curdoc.post_data)
+	/*
+	 * Normal case - List Page is not associated with post data.  - kw
+	 */
+	return TRUE;
+
+    if (nhist > 0
+	&& !LYresubmit_posts
+	&& HDOC(nhist - 1).post_data
+	&& BINEQ(curdoc.post_data, HDOC(nhist - 1).post_data)
+	&& (base = HText_getContentBase()) != 0) {
+	char *text = !isLYNXIMGMAP(HDOC(nhist - 1).address)
+	? HDOC(nhist - 1).address
+	: HDOC(nhist - 1).address + LEN_LYNXIMGMAP;
+
+	if (!strncmp(base, text, strlen(base))) {
+	    /*
+	     * Normal case - as best as we can check, the document at the top
+	     * of the history stack seems to be the document the List Page is
+	     * about (or a LYNXIMGMAP derived from it), and LYresubmit_posts is
+	     * not set, so don't prompt here.  If we actually have to repeat a
+	     * POST because, against expectations, the underlying document
+	     * isn't cached any more, HTAccess will prompt for confirmation,
+	     * unless we had LYK_NOCACHE -kw
+	     */
+	    return TRUE;
+	}
+    }
+    return FALSE;
+}
+#endif
+
+static int handle_LYK_ACTIVATE(int *c,
+			       int cmd GCC_UNUSED,
+			       BOOLEAN *try_internal GCC_UNUSED,
+			       BOOLEAN *refresh_screen,
+			       BOOLEAN *force_load,
+			       int real_cmd)
+{
+    if (do_change_link() == -1) {
+	LYforce_no_cache = FALSE;
+	reloading = FALSE;
+	return 1;		/* mouse stuff was confused, ignore - kw */
+    }
+    if (nlinks > 0) {
+	if (links[curdoc.link].type == WWW_FORM_LINK_TYPE) {
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+	    if (real_cmd == LYK_ACTIVATE && textfields_need_activation &&
+		F_TEXTLIKE(links[curdoc.link].l_form->type)) {
+
+		textinput_activated = TRUE;
+		show_main_statusline(links[curdoc.link], FOR_INPUT);
+		textfields_need_activation = textfields_activation_option;
+
+		return 0;
+	    }
+#endif
+	    /*
+	     * Don't try to submit forms with bad actions.  - FM
+	     */
+	    if (links[curdoc.link].l_form->type == F_SUBMIT_TYPE ||
+		links[curdoc.link].l_form->type == F_IMAGE_SUBMIT_TYPE ||
+		links[curdoc.link].l_form->type ==
+		F_TEXT_SUBMIT_TYPE) {
+		/*
+		 * Do nothing if it's disabled.  - FM
+		 */
+		if (links[curdoc.link].l_form->disabled == YES) {
+		    HTOutputFormat = WWW_PRESENT;
+		    LYforce_no_cache = FALSE;
+		    reloading = FALSE;
+		    return 0;
+		}
+		/*
+		 * Make sure we have an action.  - FM
+		 */
+		if (!links[curdoc.link].l_form->submit_action ||
+		    *links[curdoc.link].l_form->submit_action
+		    == '\0') {
+		    HTUserMsg(NO_FORM_ACTION);
+		    HTOutputFormat = WWW_PRESENT;
+		    LYforce_no_cache = FALSE;
+		    reloading = FALSE;
+		    return 0;
+		}
+		/*
+		 * Check for no_mail if the form action is a mailto URL.  - FM
+		 */
+		if (links[curdoc.link].l_form->submit_method
+		    == URL_MAIL_METHOD && no_mail) {
+		    HTAlert(FORM_MAILTO_DISALLOWED);
+		    HTOutputFormat = WWW_PRESENT;
+		    LYforce_no_cache = FALSE;
+		    reloading = FALSE;
+		    return 0;
+		}
+		/*
+		 * Make sure this isn't a spoof in an account with restrictions
+		 * on file URLs.  - FM
+		 */
+		if (no_file_url &&
+		    isFILE_URL(links[curdoc.link].l_form->submit_action)) {
+		    HTAlert(FILE_ACTIONS_DISALLOWED);
+		    HTOutputFormat = WWW_PRESENT;
+		    LYforce_no_cache = FALSE;
+		    reloading = FALSE;
+		    return 0;
+		}
+		/*
+		 * Make sure this isn't a spoof attempt via an internal URL.  -
+		 * FM
+		 */
+		if (isLYNXCOOKIE(links[curdoc.link].l_form->submit_action) ||
+#ifdef USE_CACHEJAR
+		    isLYNXCACHE(links[curdoc.link].l_form->submit_action) ||
+#endif
+#ifdef DIRED_SUPPORT
+#ifdef OK_PERMIT
+		    (isLYNXDIRED(links[curdoc.link].l_form->submit_action) &&
+		     (no_dired_support ||
+		      strncasecomp((links[curdoc.link].l_form->submit_action
+				    + 10),
+				   "//PERMIT_LOCATION", 17) ||
+		      !LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS))) ||
+#else
+		    isLYNXDIRED(links[curdoc.link].l_form->submit_action) ||
+#endif /* OK_PERMIT */
+#endif /* DIRED_SUPPORT */
+		    isLYNXDOWNLOAD(links[curdoc.link].l_form->submit_action) ||
+		    isLYNXHIST(links[curdoc.link].l_form->submit_action) ||
+		    isLYNXKEYMAP(links[curdoc.link].l_form->submit_action) ||
+		    isLYNXIMGMAP(links[curdoc.link].l_form->submit_action) ||
+		    isLYNXPRINT(links[curdoc.link].l_form->submit_action) ||
+		    isLYNXEXEC(links[curdoc.link].l_form->submit_action) ||
+		    isLYNXPROG(links[curdoc.link].l_form->submit_action)) {
+
+		    HTAlert(SPECIAL_ACTION_DISALLOWED);
+		    CTRACE((tfp, "LYMainLoop: Rejected '%s'\n",
+			    links[curdoc.link].l_form->submit_action));
+		    HTOutputFormat = WWW_PRESENT;
+		    LYforce_no_cache = FALSE;
+		    reloading = FALSE;
+		    return 0;
+		}
+#ifdef NOTDEFINED		/* We're disabling form inputs instead of using this. - FM */
+		/*
+		 * Check for enctype and let user know we don't yet support
+		 * multipart/form-data - FM
+		 */
+		if (links[curdoc.link].l_form->submit_enctype) {
+		    if (!strcmp(links[curdoc.link].l_form->submit_enctype,
+				"multipart/form-data")) {
+			HTAlert(gettext("Enctype multipart/form-data not yet supported!  Cannot submit."));
+			HTOutputFormat = WWW_PRESENT;
+			LYforce_no_cache = FALSE;
+			reloading = FALSE;
+			return 0;
+		    }
+		}
+#endif /* NOTDEFINED */
+		if (check_realm) {
+		    LYPermitURL = TRUE;
+		}
+		if (no_filereferer == TRUE && isFILE_URL(curdoc.address)) {
+		    LYNoRefererForThis = TRUE;
+		}
+		if (links[curdoc.link].l_form->submit_method != URL_MAIL_METHOD) {
+		    StrAllocCopy(newdoc.title,
+				 LYGetHiliteStr(curdoc.link, 0));
+		}
+	    }
+
+	    /*
+	     * Normally we don't get here for text input fields, but it can
+	     * happen as a result of mouse positioning.  In that case the
+	     * statusline will not have updated info, so update it now.  - kw
+	     */
+	    if (F_TEXTLIKE(links[curdoc.link].l_form->type)) {
+		show_formlink_statusline(links[curdoc.link].l_form,
+					 (real_cmd == LYK_NOCACHE ||
+					  real_cmd == LYK_DOWNLOAD ||
+					  real_cmd == LYK_HEAD ||
+					  (real_cmd == LYK_SUBMIT &&
+					   !textinput_activated)) ?
+					 FOR_PANEL : FOR_INPUT);
+		if (user_mode == NOVICE_MODE &&
+		    textinput_activated &&
+		    (real_cmd == LYK_ACTIVATE || real_cmd == LYK_SUBMIT)) {
+		    form_noviceline(links[curdoc.link].l_form->disabled);
+		}
+	    }
+
+	    *c = change_form_link(curdoc.link,
+				  &newdoc, refresh_screen,
+				  FALSE,
+				  (BOOLEAN) (real_cmd == LYK_SUBMIT ||
+					     real_cmd == LYK_NOCACHE ||
+					     real_cmd == LYK_DOWNLOAD ||
+					     real_cmd == LYK_HEAD));
+	    if (*c != LKC_DONE || *refresh_screen) {
+		/*
+		 * Cannot have been a submit field for which newdoc was filled
+		 * in.  - kw
+		 */
+		if ((links[curdoc.link].l_form->type == F_SUBMIT_TYPE ||
+		     links[curdoc.link].l_form->type == F_IMAGE_SUBMIT_TYPE ||
+		     links[curdoc.link].l_form->type == F_TEXT_SUBMIT_TYPE) &&
+		    links[curdoc.link].l_form->submit_method
+		    != URL_MAIL_METHOD) {
+		    /*
+		     * Try to undo change of newdoc.title done above.
+		     */
+		    if (HText_getTitle()) {
+			StrAllocCopy(newdoc.title, HText_getTitle());
+		    } else if (curdoc.title) {
+			StrAllocCopy(newdoc.title, curdoc.title);
+		    }
+		}
+	    } else {
+		if (HTOutputFormat == HTAtom_for("www/download") &&
+		    newdoc.post_data != NULL &&
+		    newdoc.safe == FALSE) {
+
+		    if ((HText_POSTReplyLoaded(&newdoc) == TRUE) &&
+			HTConfirm(CONFIRM_POST_RESUBMISSION) == FALSE) {
+			HTInfoMsg(CANCELLED);
+			HTOutputFormat = WWW_PRESENT;
+			LYforce_no_cache = FALSE;
+			copy_address(&newdoc, &curdoc);
+			StrAllocCopy(newdoc.title, curdoc.title);
+			BStrCopy(newdoc.post_data, curdoc.post_data);
+			StrAllocCopy(newdoc.post_content_type,
+				     curdoc.post_content_type);
+			StrAllocCopy(newdoc.bookmark, curdoc.bookmark);
+			newdoc.isHEAD = curdoc.isHEAD;
+			newdoc.safe = curdoc.safe;
+			newdoc.internal_link = curdoc.internal_link;
+			return 0;
+		    }
+		}
+		/*
+		 * Moved here from earlier to only apply when it should. 
+		 * Anyway, why should realm checking be overridden for form
+		 * submissions, this seems to be an unnecessary loophole??  But
+		 * that's the way it was, maybe there is some reason.  However,
+		 * at least make sure this doesn't weaken restrictions implied
+		 * by -validate!
+		 * - kw 1999-05-25
+		 */
+		if (check_realm && !LYValidate) {
+		    LYPermitURL = TRUE;
+		}
+	    }
+	    if (*c == LKC_DONE) {
+		*c = DO_NOTHING;
+	    } else if (*c == 23) {
+		*c = DO_NOTHING;
+		*refresh_screen = TRUE;
+	    } else {
+		/* Avoid getting stuck with repeatedly calling
+		 * handle_LYK_ACTIVATE(), instead of calling change_form_link()
+		 * directly from mainloop(), for text input fields.  - kw
+		 */
+		switch (LKC_TO_C(*c)) {
+		case '\n':
+		case '\r':
+		default:
+		    if ((real_cmd == LYK_ACTIVATE || real_cmd == LYK_SUBMIT) &&
+			F_TEXTLIKE(links[curdoc.link].l_form->type) &&
+			textinput_activated) {
+			return 3;
+		    }
+		    break;
+		}
+	    }
+	    return 2;
+	} else {
+	    /*
+	     * Not a forms link.
+	     *
+	     * Make sure this isn't a spoof in an account with restrictions on
+	     * file URLs.  - FM
+	     */
+	    if (no_file_url && isFILE_URL(links[curdoc.link].lname)) {
+		if (!isFILE_URL(curdoc.address) &&
+		    !((isLYNXKEYMAP(curdoc.address) ||
+#ifndef USE_CACHEJAR
+		       isLYNXCOOKIE(curdoc.address)) &&
+#else
+		       isLYNXCOOKIE(curdoc.address) ||
+		       isLYNXCACHE(curdoc.address)) &&
+#endif
+		      !strncmp(links[curdoc.link].lname,
+			       helpfilepath,
+			       strlen(helpfilepath)))) {
+		    HTAlert(FILE_SERVED_LINKS_DISALLOWED);
+		    reloading = FALSE;
+		    return 0;
+		} else if (curdoc.bookmark != NULL) {
+		    HTAlert(FILE_BOOKMARKS_DISALLOWED);
+		    reloading = FALSE;
+		    return 0;
+		}
+	    }
+	    /*
+	     * Make sure this isn't a spoof attempt via an internal URL in a
+	     * non-internal document.  - FM
+	     */
+	    if ((isLYNXCOOKIE(links[curdoc.link].lname) &&
+		 (strcmp(NonNull(curdoc.title), COOKIE_JAR_TITLE) ||
+		  !isLYNXCOOKIE(curdoc.address))) ||
+#ifdef USE_CACHEJAR
+		(isLYNXCACHE(links[curdoc.link].lname) &&
+		 (strcmp(NonNull(curdoc.title), CACHE_JAR_TITLE) ||
+		  !isLYNXCACHE(curdoc.address))) ||
+#endif
+#ifdef DIRED_SUPPORT
+		(isLYNXDIRED(links[curdoc.link].lname) &&
+		 !LYIsUIPage(curdoc.address, UIP_DIRED_MENU) &&
+		 !LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS) &&
+#ifdef OK_INSTALL
+		 !LYIsUIPage(curdoc.address, UIP_INSTALL) &&
+#endif /* OK_INSTALL */
+		 !LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS)) ||
+#endif /* DIRED_SUPPORT */
+		(isLYNXDOWNLOAD(links[curdoc.link].lname) &&
+		 !LYIsUIPage(curdoc.address, UIP_DOWNLOAD_OPTIONS)) ||
+		(isLYNXHIST(links[curdoc.link].lname) &&
+		 !LYIsUIPage(curdoc.address, UIP_HISTORY) &&
+		 !LYIsUIPage(curdoc.address, UIP_LIST_PAGE) &&
+		 !LYIsUIPage(curdoc.address, UIP_ADDRLIST_PAGE)) ||
+		(isLYNXPRINT(links[curdoc.link].lname) &&
+		 !LYIsUIPage(curdoc.address, UIP_PRINT_OPTIONS))) {
+		HTAlert(SPECIAL_VIA_EXTERNAL_DISALLOWED);
+		HTOutputFormat = WWW_PRESENT;
+		LYforce_no_cache = FALSE;
+		reloading = FALSE;
+		return 0;
+	    }
+#ifdef USE_EXTERNALS
+	    if (run_external(links[curdoc.link].lname, TRUE)) {
+		*refresh_screen = TRUE;
+		return 0;
+	    }
+#endif /* USE_EXTERNALS */
+
+	    /*
+	     * Follow a normal link or anchor.
+	     */
+	    set_address(&newdoc, links[curdoc.link].lname);
+	    StrAllocCopy(newdoc.title, LYGetHiliteStr(curdoc.link, 0));
+#ifndef DONT_TRACK_INTERNAL_LINKS
+	    /*
+	     * For internal links, retain POST content if present.  If we are
+	     * on the List Page, prevent pushing it on the history stack. 
+	     * Otherwise set try_internal to signal that the top of the loop
+	     * should attempt to reposition directly, without calling getfile. 
+	     * - kw
+	     */
+	    /*
+	     * Might be an internal link anchor in the same doc.  If so, take
+	     * the try_internal shortcut if we didn't fall through from
+	     * LYK_NOCACHE.  - kw
+	     */
+	    newdoc.internal_link =
+		(links[curdoc.link].type == WWW_INTERN_LINK_TYPE);
+	    if (newdoc.internal_link) {
+		/*
+		 * Special case of List Page document with an internal link
+		 * indication, which may really stand for an internal link
+		 * within the document the List Page is about.  - kw
+		 */
+		if (LYIsListpageTitle(NonNull(curdoc.title)) &&
+		    (LYIsUIPage(curdoc.address, UIP_LIST_PAGE) ||
+		     LYIsUIPage(curdoc.address, UIP_ADDRLIST_PAGE))) {
+		    if (check_history()) {
+			LYinternal_flag = TRUE;
+		    } else {
+			HTLastConfirmCancelled();	/* reset flag */
+			if (!confirm_post_resub(newdoc.address,
+						newdoc.title,
+						((LYresubmit_posts &&
+						  HText_POSTReplyLoaded(&newdoc))
+						 ? 1
+						 : 2),
+						2)) {
+			    if (HTLastConfirmCancelled() ||
+				(LYresubmit_posts &&
+				 cmd != LYK_NOCACHE &&
+				 !HText_POSTReplyLoaded(&newdoc))) {
+				/* cancel the whole thing */
+				LYforce_no_cache = FALSE;
+				reloading = FALSE;
+				copy_address(&newdoc, &curdoc);
+				StrAllocCopy(newdoc.title, curdoc.title);
+				newdoc.internal_link = curdoc.internal_link;
+				HTInfoMsg(CANCELLED);
+				return 1;
+			    } else if (LYresubmit_posts &&
+				       cmd != LYK_NOCACHE) {
+				/* If LYresubmit_posts is set, and the
+				   answer was No, and the key wasn't
+				   NOCACHE, and we have a cached copy,
+				   then use it. - kw */
+				LYforce_no_cache = FALSE;
+			    } else {
+				/* if No, but not ^C or ^G, drop
+				 * the post data.  Maybe the link
+				 * wasn't meant to be internal after
+				 * all, here we can recover from that
+				 * assumption. - kw */
+				LYFreePostData(&newdoc);
+				newdoc.internal_link = FALSE;
+				HTAlert(DISCARDING_POST_DATA);
+			    }
+			}
+		    }
+		    /*
+		     * Don't push the List Page if we follow an internal link
+		     * given by it.  - kw
+		     */
+		    free_address(&curdoc);
+		} else if (cmd != LYK_NOCACHE) {
+		    *try_internal = TRUE;
+		}
+		if (!(LYresubmit_posts && newdoc.post_data))
+		    LYinternal_flag = TRUE;
+		/* We still set force_load so that history pushing
+		 * etc. will be done.  - kw
+		 */
+		*force_load = TRUE;
+		return 1;
+	    } else {
+		/*
+		 * Free POST content if not an internal link.  - kw
+		 */
+		LYFreePostData(&newdoc);
+	    }
+#endif /* TRACK_INTERNAL_LINKS */
+	    /*
+	     * Might be an anchor in the same doc from a POST form.  If so,
+	     * don't free the content.  -- FM
+	     */
+	    if (are_different(&curdoc, &newdoc)) {
+		LYFreePostData(&newdoc);
+		FREE(newdoc.bookmark);
+		if (isLYNXMESSAGES(newdoc.address))
+		    LYforce_no_cache = TRUE;
+	    }
+	    if (!no_jump && lynxjumpfile && curdoc.address &&
+		!strcmp(lynxjumpfile, curdoc.address)) {
+		LYJumpFileURL = TRUE;
+		LYUserSpecifiedURL = TRUE;
+	    } else if ((curdoc.title &&
+			(LYIsUIPage(curdoc.address, UIP_HISTORY) ||
+			 !strcmp(curdoc.title, HISTORY_PAGE_TITLE))) ||
+		       curdoc.bookmark != NULL ||
+		       (lynxjumpfile &&
+			!strcmp(lynxjumpfile, curdoc.address))) {
+		LYUserSpecifiedURL = TRUE;
+	    } else if (no_filereferer == TRUE && isFILE_URL(curdoc.address)) {
+		LYNoRefererForThis = TRUE;
+	    }
+	    newdoc.link = 0;
+	    *force_load = TRUE;	/* force MainLoop to reload */
+#ifdef USE_PRETTYSRC
+	    psrc_view = FALSE;	/* we get here if link is not internal */
+#endif
+
+#if defined(DIRED_SUPPORT) && !defined(__DJGPP__)
+	    if (lynx_edit_mode) {
+		DIRED_UNCACHE_2;
+		/*
+		 * Unescaping any slash chars in the URL, but avoid double
+		 * unescaping and too-early unescaping of other chars.  - KW
+		 */
+		HTUnEscapeSome(newdoc.address, "/");
+		/* avoid stripping final slash for root dir - kw */
+		if (strcasecomp(newdoc.address, "file://localhost/"))
+		    strip_trailing_slash(newdoc.address);
+	    }
+#endif /* DIRED_SUPPORT  && !__DJGPP__ */
+	    if (isLYNXCOOKIE(curdoc.address)
+#ifdef USE_CACHEJAR
+		|| isLYNXCACHE(curdoc.address)
+#endif
+		) {
+		HTuncache_current_document();
+	    }
+	}
+    }
+    return 0;
+}
+
+#ifdef EXP_ADDRLIST_PAGE
+static BOOLEAN handle_LYK_ADDRLIST(int *cmd)
+{
+    /*
+     * Don't do if already viewing list addresses page.
+     */
+    if (LYIsUIPage(curdoc.address, UIP_ADDRLIST_PAGE)) {
+	/*
+	 * Already viewing list page, so get out.
+	 */
+	*cmd = LYK_PREV_DOC;
+	return TRUE;
+    }
+
+    /*
+     * Print address list page to file.
+     */
+    if (showlist(&newdoc, FALSE) < 0)
+	return FALSE;
+    StrAllocCopy(newdoc.title, ADDRLIST_PAGE_TITLE);
+    /*
+     * showlist will set newdoc's other fields.  It may leave post_data intact
+     * so the list can be used to follow internal links in the current document
+     * even if it is a POST response.  - kw
+     */
+
+    if (LYValidate || check_realm) {
+	LYPermitURL = TRUE;
+	StrAllocCopy(lynxlistfile, newdoc.address);
+    }
+    return FALSE;
+}
+#endif /* EXP_ADDRLIST_PAGE */
+
+static void handle_LYK_ADD_BOOKMARK(BOOLEAN *refresh_screen,
+				    int *old_c,
+				    int real_c)
+{
+    int c;
+
+    if (LYValidate) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(BOOKMARKS_DISABLED);
+	}
+	return;
+    }
+
+    if (!LYIsUIPage(curdoc.address, UIP_HISTORY) &&
+	!LYIsUIPage(curdoc.address, UIP_SHOWINFO) &&
+	!LYIsUIPage(curdoc.address, UIP_PRINT_OPTIONS) &&
+#ifdef DIRED_SUPPORT
+	!LYIsUIPage(curdoc.address, UIP_DIRED_MENU) &&
+	!LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS) &&
+	!LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS) &&
+#endif /* DIRED_SUPPORT */
+	!LYIsUIPage(curdoc.address, UIP_DOWNLOAD_OPTIONS) &&
+	!isLYNXCOOKIE(curdoc.address) &&
+#ifdef USE_CACHEJAR
+	!isLYNXCACHE(curdoc.address) &&
+#endif
+	!LYIsUIPage(curdoc.address, UIP_OPTIONS_MENU) &&
+	((nlinks <= 0) ||
+	 (links[curdoc.link].lname != NULL &&
+	  !isLYNXHIST(links[curdoc.link].lname) &&
+	  !isLYNXPRINT(links[curdoc.link].lname) &&
+	  !isLYNXDIRED(links[curdoc.link].lname) &&
+	  !isLYNXDOWNLOAD(links[curdoc.link].lname) &&
+	  !isLYNXCOOKIE(links[curdoc.link].lname) &&
+#ifdef USE_CACHEJAR
+	  !isLYNXCACHE(links[curdoc.link].lname) &&
+#endif
+	  !isLYNXPRINT(links[curdoc.link].lname)))) {
+	if (nlinks > 0) {
+	    if (curdoc.post_data == NULL &&
+		curdoc.bookmark == NULL &&
+		!LYIsUIPage(curdoc.address, UIP_LIST_PAGE) &&
+		!LYIsUIPage(curdoc.address, UIP_ADDRLIST_PAGE) &&
+		!LYIsUIPage(curdoc.address, UIP_VLINKS)) {
+		/*
+		 * The document doesn't have POST content, and is not a
+		 * bookmark file, nor is the list or visited links page, so we
+		 * can save either that or the link.  - FM
+		 */
+		_statusline(BOOK_D_L_OR_CANCEL);
+		if ((c = LYgetch_single()) == 'D') {
+		    save_bookmark_link(curdoc.address, curdoc.title);
+		    *refresh_screen = TRUE;	/* MultiBookmark support */
+		    goto check_add_bookmark_to_self;
+		}
+	    } else {
+		if (LYMultiBookmarks == MBM_OFF &&
+		    curdoc.bookmark != NULL &&
+		    strstr(curdoc.address,
+			   (*bookmark_page == '.'
+			    ? (bookmark_page + 1)
+			    : bookmark_page)) != NULL) {
+		    /*
+		     * If multiple bookmarks are disabled, offer the L)ink or
+		     * C)ancel, but with wording which indicates that the link
+		     * already exists in this bookmark file.  - FM
+		     */
+		    _statusline(MULTIBOOKMARKS_SELF);
+		} else if (curdoc.post_data != NULL &&
+			   links[curdoc.link].type == WWW_INTERN_LINK_TYPE) {
+		    /*
+		     * Internal link, and document has POST content.
+		     */
+		    HTUserMsg(NOBOOK_POST_FORM);
+		    return;
+		} else {
+		    /*
+		     * Only offer the link in a document with POST content, or
+		     * if the current document is a bookmark file and multiple
+		     * bookmarks are enabled.  - FM
+		     */
+		    _statusline(BOOK_L_OR_CANCEL);
+		}
+		c = LYgetch_single();
+	    }
+	    if (c == 'L') {
+		if (curdoc.post_data != NULL &&
+		    links[curdoc.link].type == WWW_INTERN_LINK_TYPE) {
+		    /*
+		     * Internal link, and document has POST content.
+		     */
+		    HTUserMsg(NOBOOK_POST_FORM);
+		    return;
+		}
+		/*
+		 * User does want to save the link.  - FM
+		 */
+		if (links[curdoc.link].type != WWW_FORM_LINK_TYPE) {
+		    save_bookmark_link(links[curdoc.link].lname,
+				       LYGetHiliteStr(curdoc.link, 0));
+		    *refresh_screen = TRUE;	/* MultiBookmark support */
+		} else {
+		    HTUserMsg(NOBOOK_FORM_FIELD);
+		    return;
+		}
+	    } else {
+		return;
+	    }
+	} else if (curdoc.post_data != NULL) {
+	    /*
+	     * No links, and document has POST content.  - FM
+	     */
+	    HTUserMsg(NOBOOK_POST_FORM);
+	    return;
+	} else if (curdoc.bookmark != NULL) {
+	    /*
+	     * It's a bookmark file from which all of the links were deleted. 
+	     * - FM
+	     */
+	    HTUserMsg(BOOKMARKS_NOLINKS);
+	    return;
+	} else {
+	    _statusline(BOOK_D_OR_CANCEL);
+	    if (LYgetch_single() == 'D') {
+		save_bookmark_link(curdoc.address, curdoc.title);
+		*refresh_screen = TRUE;		/* MultiBookmark support */
+	    } else {
+		return;
+	    }
+	}
+      check_add_bookmark_to_self:
+	if (curdoc.bookmark && BookmarkPage &&
+	    !strcmp(curdoc.bookmark, BookmarkPage)) {
+	    HTuncache_current_document();
+	    move_address(&newdoc, &curdoc);
+	    StrAllocCopy(newdoc.bookmark, curdoc.bookmark);
+	    newdoc.line = curdoc.line;
+	    newdoc.link = curdoc.link;
+	    newdoc.internal_link = FALSE;
+	}
+    } else {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(NOBOOK_HSML);
+	}
+    }
+}
+
+static void handle_LYK_CLEAR_AUTH(int *old_c,
+				  int real_c)
+{
+    if (*old_c != real_c) {
+	*old_c = real_c;
+	if (HTConfirm(CLEAR_ALL_AUTH_INFO)) {
+	    FREE(authentication_info[0]);
+	    FREE(authentication_info[1]);
+	    FREE(proxyauth_info[0]);
+	    FREE(proxyauth_info[1]);
+	    HTClearHTTPAuthInfo();
+#ifndef DISABLE_NEWS
+	    HTClearNNTPAuthInfo();
+#endif
+#ifndef DISABLE_FTP
+	    HTClearFTPPassword();
+#endif
+	    HTUserMsg(AUTH_INFO_CLEARED);
+	} else {
+	    HTUserMsg(CANCELLED);
+	}
+    }
+}
+
+static int handle_LYK_COMMAND(char *user_input_buffer)
+{
+    LYKeymapCode ch;
+    Kcmd *mp;
+    char *src, *tmp;
+
+    *user_input_buffer = 0;
+    _statusline(": ");
+    if (LYgetstr(user_input_buffer, VISIBLE, MAX_LINE, RECALL_CMD) >= 0) {
+	src = LYSkipBlanks(user_input_buffer);
+	tmp = LYSkipNonBlanks(src);
+	*tmp = 0;
+	ch = ((mp = LYStringToKcmd(src)) != 0) ? mp->code : LYK_UNKNOWN;
+	CTRACE((tfp, "LYK_COMMAND(%s.%s) = %d\n", src, tmp, (int) ch));
+	if (ch == 0) {
+	    return *src ? -1 : 0;
+	}
+	/* FIXME: reuse the rest of the buffer for parameters */
+	return ch;
+    }
+    return 0;
+}
+
+static void handle_LYK_COMMENT(BOOLEAN *refresh_screen,
+			       char **owner_address_p,
+			       int *old_c,
+			       int real_c)
+{
+    int c;
+
+    if (!*owner_address_p &&
+	strncasecomp(curdoc.address, "http", 4)) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(NO_OWNER);
+	}
+    } else if (no_mail) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(MAIL_DISALLOWED);
+	}
+    } else {
+	if (HTConfirmDefault(CONFIRM_COMMENT, NO)) {
+	    if (!*owner_address_p) {
+		/*
+		 * No owner defined, so make a guess and and offer it to the
+		 * user.  - FM
+		 */
+		char *address = NULL;
+		char *temp = HTParse(curdoc.address, "", PARSE_PATH);
+		char *cp;
+
+		if (temp != NULL) {
+		    HTUnEscape(temp);
+		    if (LYIsTilde(*temp) && strlen(temp) > 1) {
+			/*
+			 * It's a ~user URL so guess user@host.  - FM
+			 */
+			if ((cp = strchr((temp + 1), '/')) != NULL)
+			    *cp = '\0';
+			StrAllocCopy(address, STR_MAILTO_URL);
+			StrAllocCat(address, (temp + 1));
+			StrAllocCat(address, "@");
+		    }
+		    FREE(temp);
+		}
+		if (address == NULL)
+		    /*
+		     * Wasn't a ~user URL so guess WebMaster@host.  - FM
+		     */
+		    StrAllocCopy(address, "mailto:WebMaster@");
+		temp = HTParse(curdoc.address, "", PARSE_HOST);
+		StrAllocCat(address, temp);
+		HTSprintf0(&temp, NO_OWNER_USE, address);
+		c = HTConfirmDefault(temp, NO);
+		FREE(temp);
+		if (c == YES) {
+		    StrAllocCopy(*owner_address_p, address);
+		    FREE(address);
+		} else {
+		    FREE(address);
+		    return;
+		}
+	    }
+	    if (is_url(*owner_address_p) != MAILTO_URL_TYPE) {
+		/*
+		 * The address is a URL.  Just follow the link.
+		 */
+		set_address(&newdoc, *owner_address_p);
+		newdoc.internal_link = FALSE;
+	    } else {
+		/*
+		 * The owner_address is a mailto:  URL.
+		 */
+		const char *kp = HText_getRevTitle();
+		const char *id = HText_getMessageID();
+		char *tmptitle = NULL;
+
+		if (!kp && HTMainAnchor) {
+		    kp = HTAnchor_subject(HTMainAnchor);
+		    if (non_empty(kp)) {
+			if (strncasecomp(kp, "Re: ", 4)) {
+			    StrAllocCopy(tmptitle, "Re: ");
+			    StrAllocCat(tmptitle, kp);
+			    kp = tmptitle;
+			}
+		    }
+		}
+
+		if (strchr(*owner_address_p, ':') != NULL)
+		    /*
+		     * Send a reply.  The address is after the colon.
+		     */
+		    reply_by_mail(strchr(*owner_address_p, ':') + 1,
+				  curdoc.address,
+				  NonNull(kp), id);
+		else
+		    reply_by_mail(*owner_address_p, curdoc.address,
+				  NonNull(kp), id);
+
+		FREE(tmptitle);
+		*refresh_screen = TRUE;		/* to force a showpage */
+	    }
+	}
+    }
+}
+
+#ifdef USE_CACHEJAR
+static BOOLEAN handle_LYK_CACHE_JAR(int *cmd)
+{
+    /*
+     * Don't do this if already viewing cache jar.
+     */
+    if (!isLYNXCACHE(curdoc.address)) {
+	set_address(&newdoc, STR_LYNXCACHE "/");
+	LYFreePostData(&newdoc);
+	FREE(newdoc.bookmark);
+	newdoc.isHEAD = FALSE;
+	newdoc.safe = FALSE;
+	newdoc.internal_link = FALSE;
+	LYforce_no_cache = TRUE;
+	if (LYValidate || check_realm) {
+	    LYPermitURL = TRUE;
+	}
+    } else {
+	/*
+	 * If already in the cache jar, get out.
+	 */
+	*cmd = LYK_PREV_DOC;
+	return TRUE;
+    }
+    return FALSE;
+}
+#endif /* USE_CACHEJAR */
+
+static BOOLEAN handle_LYK_COOKIE_JAR(int *cmd)
+{
+    /*
+     * Don't do if already viewing the cookie jar.
+     */
+    if (!isLYNXCOOKIE(curdoc.address)) {
+	set_address(&newdoc, "LYNXCOOKIE:/");
+	LYFreePostData(&newdoc);
+	FREE(newdoc.bookmark);
+	newdoc.isHEAD = FALSE;
+	newdoc.safe = FALSE;
+	newdoc.internal_link = FALSE;
+	LYforce_no_cache = TRUE;
+	if (LYValidate || check_realm) {
+	    LYPermitURL = TRUE;
+	}
+    } else {
+	/*
+	 * If already in the cookie jar, get out.
+	 */
+	*cmd = LYK_PREV_DOC;
+	return TRUE;
+    }
+    return FALSE;
+}
+
+#if defined(DIRED_SUPPORT)
+static void handle_LYK_CREATE(void)
+{
+    if (lynx_edit_mode && !no_dired_support) {
+	if (local_create(&curdoc) > 0) {
+	    DIRED_UNCACHE_1;
+	    move_address(&newdoc, &curdoc);
+	    LYFreePostData(&newdoc);
+	    FREE(newdoc.bookmark);
+	    newdoc.isHEAD = FALSE;
+	    newdoc.safe = FALSE;
+	    newdoc.line = curdoc.line;
+	    newdoc.link = curdoc.link > -1 ? curdoc.link : 0;
+	    LYclear();
+	}
+    }
+}
+#endif /* DIRED_SUPPORT */
+
+static void handle_LYK_DEL_BOOKMARK(BOOLEAN *refresh_screen,
+				    int *old_c,
+				    int real_c)
+{
+    if (curdoc.bookmark != NULL) {
+	if (HTConfirmDefault(CONFIRM_BOOKMARK_DELETE, NO) != YES)
+	    return;
+	remove_bookmark_link(links[curdoc.link].anchor_number - 1,
+			     curdoc.bookmark);
+    } else {			/* behave like REFRESH for backward compatibility */
+	*refresh_screen = TRUE;
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    lynx_force_repaint();
+	}
+	return;
+    }
+    do_cleanup_after_delete();
+}
+
+#if defined(DIRED_SUPPORT) || defined(VMS)
+static void handle_LYK_DIRED_MENU(BOOLEAN *refresh_screen,
+				  int *old_c GCC_UNUSED,
+				  int real_c GCC_UNUSED)
+{
+#ifdef VMS
+    char *cp, *temp = 0;
+    const char *test = HTGetProgramPath(ppCSWING);
+
+    /*
+     * Check if the CSwing Directory/File Manager is available.  Will be
+     * disabled if CSWING path is NULL, zero-length, or "none" (case
+     * insensitive), if no_file_url was set via the file_url restriction, if
+     * no_goto_file was set for the anonymous account, or if HTDirAccess was
+     * set to HT_DIR_FORBID or HT_DIR_SELECTIVE via the -nobrowse or -selective
+     * switches.  - FM
+     */
+    if (isEmpty(test) ||
+	!strcasecomp(test, "none") ||
+	no_file_url || no_goto_file ||
+	HTDirAccess == HT_DIR_FORBID ||
+	HTDirAccess == HT_DIR_SELECTIVE) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(DFM_NOT_AVAILABLE);
+	}
+	return;
+    }
+
+    /*
+     * If we are viewing a local directory listing or a local file which is not
+     * temporary, invoke CSwing with the URL's directory converted to VMS path
+     * specs and passed as the argument, so we start up CSwing positioned on
+     * that node of the directory tree.  Otherwise, pass the current default
+     * directory as the argument.  - FM
+     */
+    if (LYisLocalFile(curdoc.address) &&
+	strncasecomp(curdoc.address,
+		     lynx_temp_space, strlen(lynx_temp_space))) {
+	/*
+	 * We are viewing a local directory or a local file which is not
+	 * temporary.  - FM
+	 */
+	struct stat stat_info;
+
+	cp = HTParse(curdoc.address, "", PARSE_PATH | PARSE_PUNCTUATION);
+	HTUnEscape(cp);
+	if (HTStat(cp, &stat_info) == -1) {
+	    CTRACE((tfp, "mainloop: Can't stat %s\n", cp));
+	    FREE(cp);
+	    HTSprintf0(&temp, "%s []", HTGetProgramPath(ppCSWING));
+	    *refresh_screen = TRUE;	/* redisplay */
+	} else {
+	    char *VMSdir = NULL;
+
+	    if (S_ISDIR(stat_info.st_mode)) {
+		/*
+		 * We're viewing a local directory.  Make that the CSwing
+		 * argument.  - FM
+		 */
+		LYAddPathSep(&cp);
+		StrAllocCopy(VMSdir, HTVMS_name("", cp));
+		FREE(cp);
+	    } else {
+		/*
+		 * We're viewing a local file.  Make its directory the CSwing
+		 * argument.  - FM
+		 */
+		StrAllocCopy(VMSdir, HTVMS_name("", cp));
+		FREE(cp);
+		if ((cp = strrchr(VMSdir, ']')) != NULL) {
+		    *(cp + 1) = '\0';
+		    cp == NULL;
+		} else if ((cp = strrchr(VMSdir, ':')) != NULL) {
+		    *(cp + 1) = '\0';
+		    cp == NULL;
+		}
+	    }
+	    HTSprintf0(&temp, "%s %s", HTGetProgramPath(ppCSWING), VMSdir);
+	    FREE(VMSdir);
+	    /*
+	     * Uncache the current document in case we change, move, or delete
+	     * it during the CSwing session.  - FM
+	     */
+	    /* could use DIRED_UNCACHE_1 but it's currently only defined
+	       for dired - kw */
+	    HTuncache_current_document();
+	    move_address(&newdoc, &curdoc);
+	    StrAllocCopy(newdoc.title, NonNull(curdoc.title));
+	    StrAllocCopy(newdoc.bookmark, curdoc.bookmark);
+	    newdoc.line = curdoc.line;
+	    newdoc.link = curdoc.link;
+	}
+    } else {
+	/*
+	 * We're not viewing a local directory or file.  Pass CSwing the
+	 * current default directory as an argument and don't uncache the
+	 * current document.  - FM
+	 */
+	HTSprintf0(&temp, "%s []", HTGetProgramPath(ppCSWING));
+	*refresh_screen = TRUE;	/* redisplay */
+    }
+    stop_curses();
+    LYSystem(temp);
+    start_curses();
+    FREE(temp);
+#else
+    /*
+     * Don't do if not allowed or already viewing the menu.
+     */
+    if (lynx_edit_mode && !no_dired_support &&
+	!LYIsUIPage(curdoc.address, UIP_DIRED_MENU) &&
+	strcmp(NonNull(curdoc.title), DIRED_MENU_TITLE)) {
+	dired_options(&curdoc, &newdoc.address);
+	*refresh_screen = TRUE;	/* redisplay */
+    }
+#endif /* VMS */
+}
+#endif /* defined(DIRED_SUPPORT) || defined(VMS) */
+
+static int handle_LYK_DOWNLOAD(int *cmd,
+			       int *old_c,
+			       int real_c)
+{
+
+    /*
+     * Don't do if both download and disk_save are restricted.
+     */
+    if (LYValidate ||
+	(no_download && !override_no_download && no_disk_save)) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(DOWNLOAD_DISABLED);
+	}
+	return 0;
+    }
+
+    /*
+     * Don't do if already viewing download options page.
+     */
+    if (LYIsUIPage(curdoc.address, UIP_DOWNLOAD_OPTIONS))
+	return 0;
+
+    if (do_change_link() == -1)
+	return 1;		/* mouse stuff was confused, ignore - kw */
+    if (nlinks > 0) {
+	if (links[curdoc.link].type == WWW_FORM_LINK_TYPE) {
+	    if (links[curdoc.link].l_form->type == F_SUBMIT_TYPE ||
+		links[curdoc.link].l_form->type == F_IMAGE_SUBMIT_TYPE ||
+		links[curdoc.link].l_form->type == F_TEXT_SUBMIT_TYPE) {
+		if (links[curdoc.link].l_form->submit_method ==
+		    URL_MAIL_METHOD) {
+		    if (*old_c != real_c) {
+			*old_c = real_c;
+			HTUserMsg(NO_DOWNLOAD_MAILTO_ACTION);
+		    }
+		    return 0;
+		}
+		if (isLYNXOPTIONS(links[curdoc.link].l_form->submit_action)) {
+		    if (*old_c != real_c) {
+			*old_c = real_c;
+			HTUserMsg(NO_DOWNLOAD_SPECIAL);
+		    }
+		    return 0;
+		}
+		HTOutputFormat = HTAtom_for("www/download");
+		LYforce_no_cache = TRUE;
+		*cmd = LYK_ACTIVATE;
+		return 2;
+	    }
+	    if (*old_c != real_c) {
+		*old_c = real_c;
+		HTUserMsg(NO_DOWNLOAD_INPUT);
+	    }
+
+	} else if (isLYNXCOOKIE(curdoc.address)) {
+	    if (*old_c != real_c) {
+		*old_c = real_c;
+		HTUserMsg(NO_DOWNLOAD_COOKIES);
+	    }
+	} else if (LYIsUIPage(curdoc.address, UIP_PRINT_OPTIONS)) {
+	    if (*old_c != real_c) {
+		*old_c = real_c;
+		HTUserMsg(NO_DOWNLOAD_PRINT_OP);
+	    }
+#ifdef DIRED_SUPPORT
+	} else if (LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS)) {
+	    if (*old_c != real_c) {
+		*old_c = real_c;
+		HTUserMsg(NO_DOWNLOAD_UPLOAD_OP);
+	    }
+
+	} else if (LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS)) {
+	    if (*old_c != real_c) {
+		*old_c = real_c;
+		HTUserMsg(NO_DOWNLOAD_PERMIT_OP);
+	    }
+
+	} else if (lynx_edit_mode && !no_dired_support &&
+		   !strstr(links[curdoc.link].lname, "/SugFile=")) {
+	    /*
+	     * Don't bother making a /tmp copy of the local file.
+	     */
+	    static DocInfo temp;
+
+	    copy_address(&temp, &newdoc);
+	    set_address(&newdoc, links[curdoc.link].lname);
+	    if (LYdownload_options(&newdoc.address,
+				   links[curdoc.link].lname) < 0)
+		copy_address(&newdoc, &temp);
+	    else
+		newdoc.internal_link = FALSE;
+	    LYFreeDocInfo(&temp);
+#endif /* DIRED_SUPPORT */
+
+	} else if (LYIsUIPage(curdoc.address, UIP_HISTORY) &&
+		   isLYNXHIST(links[curdoc.link].lname)) {
+	    int number = atoi(links[curdoc.link].lname + LEN_LYNXHIST);
+
+	    if (number >= nhist || number < 0) {
+		HTUserMsg(NO_DOWNLOAD_SPECIAL);
+		return 0;
+	    }
+	    if ((HDOC(number).post_data != NULL &&
+		 HDOC(number).safe != TRUE) &&
+		HTConfirm(CONFIRM_POST_RESUBMISSION) == FALSE) {
+		HTInfoMsg(CANCELLED);
+		return 0;
+	    }
+	    /*
+	     * OK, we download from history page, restore URL from stack.
+	     */
+	    copy_address(&newdoc, &HDOC(number));
+	    StrAllocCopy(newdoc.title, LYGetHiliteStr(curdoc.link, 0));
+	    StrAllocCopy(newdoc.bookmark, HDOC(number).bookmark);
+	    LYFreePostData(&newdoc);
+	    if (HDOC(number).post_data)
+		BStrCopy(newdoc.post_data,
+			 HDOC(number).post_data);
+	    if (HDOC(number).post_content_type)
+		StrAllocCopy(newdoc.post_content_type,
+			     HDOC(number).post_content_type);
+	    newdoc.isHEAD = HDOC(number).isHEAD;
+	    newdoc.safe = HDOC(number).safe;
+	    newdoc.internal_link = FALSE;
+	    newdoc.link = (user_mode == NOVICE_MODE) ? 1 : 0;
+	    HTOutputFormat = HTAtom_for("www/download");
+	    LYUserSpecifiedURL = TRUE;
+	    /*
+	     * Force the document to be reloaded.
+	     */
+	    LYforce_no_cache = TRUE;
+
+	} else if (!strncmp(links[curdoc.link].lname, "data:", 5)) {
+	    if (*old_c != real_c) {
+		*old_c = real_c;
+		HTAlert(UNSUPPORTED_DATA_URL);
+	    }
+
+	} else if (isLYNXCOOKIE(links[curdoc.link].lname) ||
+#ifdef USE_CACHEJAR
+		   isLYNXCACHE(links[curdoc.link].lname) ||
+#endif
+		   isLYNXDIRED(links[curdoc.link].lname) ||
+		   isLYNXDOWNLOAD(links[curdoc.link].lname) ||
+		   isLYNXPRINT(links[curdoc.link].lname) ||
+		   isLYNXOPTIONS(links[curdoc.link].lname) ||
+		   isLYNXHIST(links[curdoc.link].lname) ||
+	    /* handled above if valid - kw */
+/* @@@ should next two be downloadable? - kw */
+		   isLYNXHIST(links[curdoc.link].lname) ||
+		   isLYNXCFLAGS(links[curdoc.link].lname) ||
+		   isLYNXEXEC(links[curdoc.link].lname) ||
+		   isLYNXPROG(links[curdoc.link].lname)) {
+	    HTUserMsg(NO_DOWNLOAD_SPECIAL);
+
+	} else if (isMAILTO_URL(links[curdoc.link].lname)) {
+	    HTUserMsg(NO_DOWNLOAD_MAILTO_LINK);
+
+	    /*
+	     * From here on we could have a remote host, so check if that's
+	     * allowed.
+	     *
+	     * We copy all these checks from getfile() to LYK_DOWNLOAD here
+	     * because LYNXDOWNLOAD:// will NOT be pushing the previous
+	     * document into the history stack so preserve getfile() from
+	     * returning a wrong status (NULLFILE).
+	     */
+	} else if (local_host_only &&
+		   !(LYisLocalHost(links[curdoc.link].lname) ||
+		     LYisLocalAlias(links[curdoc.link].lname))) {
+	    HTUserMsg(ACCESS_ONLY_LOCALHOST);
+	} else {		/* Not a forms, options or history link */
+	    /*
+	     * Follow a normal link or anchor.  Note that if it's an anchor
+	     * within the same document, entire document will be downloaded.
+	     */
+	    set_address(&newdoc, links[curdoc.link].lname);
+	    StrAllocCopy(newdoc.title, LYGetHiliteStr(curdoc.link, 0));
+#ifndef DONT_TRACK_INTERNAL_LINKS
+	    /*
+	     * Might be an internal link in the same doc from a POST form.  If
+	     * so, don't free the content.  - kw
+	     */
+	    if (links[curdoc.link].type != WWW_INTERN_LINK_TYPE)
+#else
+	    /*
+	     * Might be an anchor in the same doc from a POST form.  If so,
+	     * don't free the content.  -- FM
+	     */
+	    if (are_different(&curdoc, &newdoc))
+#endif /* TRACK_INTERNAL_LINKS */
+	    {
+		LYFreePostData(&newdoc);
+		FREE(newdoc.bookmark);
+		newdoc.isHEAD = FALSE;
+		newdoc.safe = FALSE;
+	    }
+	    newdoc.internal_link = FALSE;
+	    newdoc.link = (user_mode == NOVICE_MODE) ? 1 : 0;
+	    HTOutputFormat = HTAtom_for("www/download");
+	    /*
+	     * Force the document to be reloaded.
+	     */
+	    LYforce_no_cache = TRUE;
+	}
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTUserMsg(NO_DOWNLOAD_CHOICE);
+    }
+    return 0;
+}
+
+static void handle_LYK_DOWN_xxx(int *old_c,
+				int real_c,
+				int scroll_by)
+{
+    int i;
+
+    if (more_text) {
+	LYChgNewline(scroll_by);
+	if (nlinks > 0 && curdoc.link > -1 &&
+	    links[curdoc.link].ly > scroll_by) {
+	    newdoc.link = curdoc.link;
+	    for (i = 0; links[i].ly <= scroll_by; i++)
+		--newdoc.link;
+	}
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTInfoMsg(ALREADY_AT_END);
+    }
+}
+
+static void handle_LYK_DOWN_HALF(int *old_c,
+				 int real_c)
+{
+    handle_LYK_DOWN_xxx(old_c, real_c, display_lines / 2);
+}
+
+static void handle_LYK_DOWN_LINK(int *follow_col,
+				 int *old_c,
+				 int real_c)
+{
+    if (curdoc.link < (nlinks - 1)) {	/* more links? */
+	int newlink;
+
+	if (*follow_col == -1) {
+	    const char *text = LYGetHiliteStr(curdoc.link, 0);
+
+	    *follow_col = links[curdoc.link].lx;
+
+	    if (text != NULL)
+		*follow_col += (int) strlen(text) / 2;
+	}
+
+	newlink = find_link_near_col(*follow_col, 1);
+	if (newlink > -1) {
+	    set_curdoc_link(newlink);
+	} else if (more_text) {	/* next page */
+	    LYChgNewline(display_lines);
+	} else if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(NO_LINKS_BELOW);
+	    return;
+	}
+    } else if (more_text) {	/* next page */
+	LYChgNewline(display_lines);
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTInfoMsg(ALREADY_AT_END);
+    }
+}
+
+static void handle_LYK_DOWN_TWO(int *old_c,
+				int real_c)
+{
+    handle_LYK_DOWN_xxx(old_c, real_c, 2);
+}
+
+static int handle_LYK_DWIMEDIT(int *cmd,
+			       int *old_c,
+			       int real_c)
+{
+#ifdef TEXTAREA_AUTOEXTEDIT
+    /*
+     * If we're in a forms TEXTAREA, invoke the editor on *its* contents,
+     * rather than attempting to edit the html source document.  KED
+     */
+    if (nlinks > 0 &&
+	LinkIsTextarea(curdoc.link)) {
+	*cmd = LYK_EDIT_TEXTAREA;
+	return 2;
+    }
+
+    /*
+     * If we're in a forms TEXT type, tell user the request is bogus (though in
+     * reality, without this trap, if the document with the TEXT field is
+     * local, the editor *would* be invoked on the source .html file; eg, the
+     * o(ptions) form tempfile).
+     *
+     * [This is done to avoid possible user confusion, due to auto invocation
+     * of the editor on the TEXTAREA's contents via the above if() statement.]
+     */
+    if (nlinks > 0 &&
+	links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
+	links[curdoc.link].l_form->type == F_TEXT_TYPE) {
+	HTUserMsg(CANNOT_EDIT_FIELD);
+	return 1;
+    }
+
+    if (no_editor) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(ANYEDIT_DISABLED);
+	}
+	return 1;
+    }
+#endif /* TEXTAREA_AUTOEXTEDIT */
+    return 0;
+}
+
+static int handle_LYK_ECGOTO(int *ch,
+			     char *user_input_buffer,
+			     char **old_user_input,
+			     int *old_c,
+			     int real_c)
+{
+    if (no_goto && !LYValidate) {
+	/*
+	 * Go to not allowed.  - FM
+	 */
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(GOTO_DISALLOWED);
+	}
+	return 0;
+    }
+#ifdef DIRED_SUPPORT
+    if (LYIsUIPage(curdoc.address, UIP_DIRED_MENU) ||
+	LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS) ||
+	LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS)) {
+	/*
+	 * Disallow editing of File Management URLs.  - FM
+	 */
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(EDIT_FM_MENU_URLS_DISALLOWED);
+	}
+	return 0;
+    }
+#endif /* DIRED_SUPPORT */
+
+    /*
+     * Save the current user_input_buffer string, and load the current
+     * document's address.
+     */
+    StrAllocCopy(*old_user_input, user_input_buffer);
+    LYstrncpy(user_input_buffer,
+	      curdoc.address,
+	      (MAX_LINE - 1));
+
+    /*
+     * Warn the user if the current document has POST data associated with it. 
+     * - FM
+     */
+    if (curdoc.post_data)
+	HTAlert(CURRENT_DOC_HAS_POST_DATA);
+
+    /*
+     * Offer the current document's URL for editing.  - FM
+     */
+    _statusline(EDIT_CURDOC_URL);
+    if (((*ch = LYgetstr(user_input_buffer, VISIBLE,
+			 MAX_LINE, RECALL_URL)) >= 0) &&
+	user_input_buffer[0] != '\0' &&
+	strcmp(user_input_buffer, curdoc.address)) {
+	LYTrimAllStartfile(user_input_buffer);
+	if (user_input_buffer[0] != '\0') {
+	    return 2;
+	}
+    }
+    /*
+     * User cancelled via ^G, a full deletion, or not modifying the URL.  - FM
+     */
+    HTInfoMsg(CANCELLED);
+    LYstrncpy(user_input_buffer, *old_user_input, MAX_LINE - 1);
+    FREE(*old_user_input);
+    return 0;
+}
+
+static void handle_LYK_EDIT(int *old_c,
+			    int real_c)
+{
+#ifdef DIRED_SUPPORT
+    char *cp;
+    char *tp = NULL;
+    struct stat dir_info;
+#endif /* DIRED_SUPPORT */
+
+    if (no_editor) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(EDIT_DISABLED);
+	}
+    }
+#ifdef DIRED_SUPPORT
+    /*
+     * Allow the user to edit the link rather than curdoc in edit mode.
+     */
+    else if (lynx_edit_mode &&
+	     non_empty(editor) && !no_dired_support) {
+	if (nlinks > 0) {
+	    cp = links[curdoc.link].lname;
+	    if (is_url(cp) == FILE_URL_TYPE) {
+		cp = HTfullURL_toFile(cp);
+		StrAllocCopy(tp, cp);
+		FREE(cp);
+
+		if (stat(tp, &dir_info) == -1) {
+		    HTAlert(NO_STATUS);
+		} else {
+		    if (S_ISREG(dir_info.st_mode)) {
+			StrAllocCopy(tp, links[curdoc.link].lname);
+			HTUnEscapeSome(tp, "/");
+			if (edit_current_file(tp, curdoc.link, -1)) {
+			    DIRED_UNCACHE_1;
+			    move_address(&newdoc, &curdoc);
+#ifdef NO_SEEK_OLD_POSITION
+			    /*
+			     * Go to top of file.
+			     */
+			    newdoc.line = 1;
+			    newdoc.link = 0;
+#else
+			    /*
+			     * Seek old position, which probably changed.
+			     */
+			    newdoc.line = curdoc.line;
+			    newdoc.link = curdoc.link;
+#endif /* NO_SEEK_OLD_POSITION */
+			    LYclear();	/* clear the screen */
+			}
+		    }
+		}
+		FREE(tp);
+	    }
+	}
+    }
+#endif /* DIRED_SUPPORT */
+    else if (non_empty(editor)) {
+	if (edit_current_file(newdoc.address, curdoc.link, LYGetNewline())) {
+	    HTuncache_current_document();
+	    LYforce_no_cache = TRUE;	/*force reload of document */
+	    free_address(&curdoc);	/* so it doesn't get pushed */
+#ifdef NO_SEEK_OLD_POSITION
+	    /*
+	     * Go to top of file.
+	     */
+	    newdoc.line = 1;
+	    newdoc.link = 0;
+#else
+	    /*
+	     * Seek old position, which probably changed.
+	     */
+	    newdoc.line = curdoc.line;
+	    newdoc.link = curdoc.link;
+#endif /* NO_SEEK_OLD_POSITION */
+	    LYclear();		/* clear the screen */
+	}
+
+    } else {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(NO_EDITOR);
+	}
+    }
+}
+
+static void handle_LYK_DWIMHELP(const char **cshelpfile)
+{
+    /*
+     * Currently a help file different from the main 'helpfile' is shown only
+     * if current link is a text input form field.  - kw
+     */
+    if (curdoc.link >= 0 && curdoc.link < nlinks &&
+	links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
+	!links[curdoc.link].l_form->disabled &&
+	F_TEXTLIKE(links[curdoc.link].l_form->type)) {
+	*cshelpfile = LYLineeditHelpURL();
+    }
+}
+
+static void handle_LYK_EDIT_TEXTAREA(BOOLEAN *refresh_screen,
+				     int *old_c,
+				     int real_c)
+{
+    if (no_editor) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(ANYEDIT_DISABLED);
+	}
+    } else if (isEmpty(editor)) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(NO_EDITOR);
+	}
+    }
+    /*
+     * See if the current link is in a form TEXTAREA.
+     */
+    else if (LinkIsTextarea(curdoc.link)) {
+
+	/* stop screen */
+	stop_curses();
+
+	(void) HText_ExtEditForm(&links[curdoc.link]);
+
+	/*
+	 * TODO:
+	 * Move cursor "n" lines from the current line to position it on the
+	 * 1st trailing blank line in the now edited TEXTAREA.  If the target
+	 * line/ anchor requires us to scroll up/down, position the target in
+	 * the approximate center of the screen.
+	 */
+
+	/* curdoc.link += n; */
+	/* works, except for page crossing, */
+	/* damnit; why is nothing ever easy */
+
+	/* start screen */
+	start_curses();
+	*refresh_screen = TRUE;
+
+    } else {
+
+	HTInfoMsg(NOT_IN_TEXTAREA_NOEDIT);
+    }
+}
+
+static int handle_LYK_ELGOTO(int *ch,
+			     char *user_input_buffer,
+			     char **old_user_input,
+			     int *old_c,
+			     int real_c)
+{
+    if (no_goto && !LYValidate) {
+	/*
+	 * Go to not allowed.  - FM
+	 */
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(GOTO_DISALLOWED);
+	}
+	return 0;
+    }
+    if (!(nlinks > 0 && curdoc.link > -1) ||
+	(links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
+	 links[curdoc.link].l_form->type != F_SUBMIT_TYPE &&
+	 links[curdoc.link].l_form->type != F_IMAGE_SUBMIT_TYPE &&
+	 links[curdoc.link].l_form->type != F_TEXT_SUBMIT_TYPE)) {
+	/*
+	 * No links on page, or not a normal link or form submit button.  - FM
+	 */
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(NOT_ON_SUBMIT_OR_LINK);
+	}
+	return 0;
+    }
+    if ((links[curdoc.link].type == WWW_FORM_LINK_TYPE) &&
+	(!links[curdoc.link].l_form->submit_action ||
+	 *links[curdoc.link].l_form->submit_action == '\0')) {
+	/*
+	 * Form submit button with no ACTION defined.  - FM
+	 */
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(NO_FORM_ACTION);
+	}
+	return 0;
+    }
+#ifdef DIRED_SUPPORT
+    if (isLYNXDIRED(links[curdoc.link].lname) ||
+	LYIsUIPage(curdoc.address, UIP_DIRED_MENU) ||
+	LYIsUIPage(curdoc.address, UIP_PERMIT_OPTIONS) ||
+	LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS)) {
+	/*
+	 * Disallow editing of File Management URLs.  - FM
+	 */
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(EDIT_FM_MENU_URLS_DISALLOWED);
+	}
+	return 0;
+    }
+#endif /* DIRED_SUPPORT */
+
+    /*
+     * Save the current user_input_buffer string, and load the current link's
+     * address.  - FM
+     */
+    StrAllocCopy(*old_user_input, user_input_buffer);
+    LYstrncpy(user_input_buffer,
+	      ((links[curdoc.link].type == WWW_FORM_LINK_TYPE)
+	       ?
+	       links[curdoc.link].l_form->submit_action : links[curdoc.link].lname),
+	      (MAX_LINE - 1));
+
+    /*
+     * Offer the current link's URL for editing.  - FM
+     */
+    _statusline(EDIT_CURLINK_URL);
+    if (((*ch = LYgetstr(user_input_buffer, VISIBLE,
+			 MAX_LINE, RECALL_URL)) >= 0) &&
+	user_input_buffer[0] != '\0' &&
+	strcmp(user_input_buffer,
+	       ((links[curdoc.link].type == WWW_FORM_LINK_TYPE)
+		? links[curdoc.link].l_form->submit_action
+		: links[curdoc.link].lname))) {
+	LYTrimAllStartfile(user_input_buffer);
+	if (user_input_buffer[0] != '\0') {
+	    return 2;
+	}
+    }
+    /*
+     * User cancelled via ^G, a full deletion, or not modifying the URL.  - FM
+     */
+    HTInfoMsg(CANCELLED);
+    LYstrncpy(user_input_buffer, *old_user_input, MAX_LINE - 1);
+    FREE(*old_user_input);
+    return 0;
+}
+
+#ifdef USE_EXTERNALS
+static void handle_LYK_EXTERN_LINK(BOOLEAN *refresh_screen)
+{
+    if ((nlinks > 0) && (links[curdoc.link].lname != NULL)) {
+	run_external(links[curdoc.link].lname, FALSE);
+	*refresh_screen = TRUE;
+    }
+}
+
+static void handle_LYK_EXTERN_PAGE(BOOLEAN *refresh_screen)
+{
+    if (curdoc.address != NULL) {
+	run_external(curdoc.address, FALSE);
+	*refresh_screen = TRUE;
+    }
+}
+#endif
+
+static BOOLEAN handle_LYK_FASTBACKW_LINK(int *cmd,
+					 int *old_c,
+					 int real_c)
+{
+    int samepage = 0, nextlink = curdoc.link;
+    int res;
+    BOOLEAN code = FALSE;
+
+    if (nlinks > 1) {
+
+	/*
+	 * If in textarea, move to first link or textarea group before it if
+	 * there is one on this screen.  - kw
+	 */
+	if (LinkIsTextarea(curdoc.link)) {
+	    int thisgroup = links[curdoc.link].l_form->number;
+	    char *thisname = links[curdoc.link].l_form->name;
+
+	    if (curdoc.link > 0 &&
+		!(LinkIsTextarea(0) &&
+		  links[0].l_form->number == thisgroup &&
+		  sametext(links[0].l_form->name, thisname))) {
+		do
+		    nextlink--;
+		while
+		    (LinkIsTextarea(nextlink) &&
+		     links[nextlink].l_form->number == thisgroup &&
+		     sametext(links[nextlink].l_form->name, thisname));
+		samepage = 1;
+
+	    } else if (!more_text && LYGetNewline() == 1 &&
+		       (LinkIsTextarea(0) &&
+			links[0].l_form->number == thisgroup &&
+			sametext(links[0].l_form->name, thisname)) &&
+		       !(LinkIsTextarea(nlinks - 1) &&
+			 links[nlinks - 1].l_form->number == thisgroup &&
+			 sametext(links[nlinks - 1].l_form->name, thisname))) {
+		nextlink = nlinks - 1;
+		samepage = 1;
+
+	    } else if (!more_text && LYGetNewline() == 1 && curdoc.link > 0) {
+		nextlink = 0;
+		samepage = 1;
+	    }
+	} else if (curdoc.link > 0) {
+	    nextlink--;
+	    samepage = 1;
+	} else if (!more_text && LYGetNewline() == 1) {
+	    nextlink = nlinks - 1;
+	    samepage = 1;
+	}
+    }
+
+    if (samepage) {
+	/*
+	 * If the link as determined so far is part of a group of textarea
+	 * fields, try to use the first of them that's on the screen instead. 
+	 * - kw
+	 */
+	if (nextlink > 0 &&
+	    LinkIsTextarea(nextlink)) {
+	    int thisgroup = links[nextlink].l_form->number;
+	    char *thisname = links[nextlink].l_form->name;
+
+	    if (LinkIsTextarea(0) &&
+		links[0].l_form->number == thisgroup &&
+		sametext(links[0].l_form->name, thisname)) {
+		nextlink = 0;
+	    } else
+		while
+		    (nextlink > 1 &&
+		     LinkIsTextarea(nextlink - 1) &&
+		     links[nextlink - 1].l_form->number == thisgroup &&
+		     sametext(links[nextlink - 1].l_form->name, thisname)) {
+		    nextlink--;
+		}
+	}
+	set_curdoc_link(nextlink);
+
+    } else if (LYGetNewline() > 1 &&	/* need a previous page */
+	       (res = HTGetLinkOrFieldStart(curdoc.link,
+					    &Newline, &newdoc.link,
+					    -1, TRUE)) != NO) {
+	if (res == LINK_DO_ARROWUP) {
+	    /*
+	     * It says we should use the normal PREV_LINK mechanism, so we'll
+	     * do that.  - kw
+	     */
+	    if (nlinks > 0)
+		curdoc.link = 0;
+	    *cmd = LYK_PREV_LINK;
+	    code = TRUE;
+	} else {
+	    LYChgNewline(1);	/* our line counting starts with 1 not 0 */
+	}
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTInfoMsg(NO_LINKS_ABOVE);
+    }
+    return code;
+}
+
+static void handle_LYK_FASTFORW_LINK(int *old_c,
+				     int real_c)
+{
+    int samepage = 0, nextlink = curdoc.link;
+
+    if (nlinks > 1) {
+
+	/*
+	 * If in textarea, move to first link or field after it if there is one
+	 * on this screen.  - kw
+	 */
+	if (LinkIsTextarea(curdoc.link)) {
+	    int thisgroup = links[curdoc.link].l_form->number;
+	    char *thisname = links[curdoc.link].l_form->name;
+
+	    if (curdoc.link < nlinks - 1 &&
+		!(LinkIsTextarea(nlinks - 1) &&
+		  links[nlinks - 1].l_form->number == thisgroup &&
+		  sametext(links[nlinks - 1].l_form->name, thisname))) {
+		do
+		    nextlink++;
+		while
+		    (LinkIsTextarea(nextlink) &&
+		     links[nextlink].l_form->number == thisgroup &&
+		     sametext(links[nextlink].l_form->name, thisname));
+		samepage = 1;
+	    } else if (!more_text && LYGetNewline() == 1 && curdoc.link > 0) {
+		nextlink = 0;
+		samepage = 1;
+	    }
+	} else if (curdoc.link < nlinks - 1) {
+	    nextlink++;
+	    samepage = 1;
+	} else if (!more_text && LYGetNewline() == 1 && curdoc.link > 0) {
+	    nextlink = 0;
+	    samepage = 1;
+	}
+    }
+
+    if (samepage) {
+	set_curdoc_link(nextlink);
+    } else if (!more_text && LYGetNewline() == 1 && curdoc.link == nlinks - 1) {
+	/*
+	 * At the bottom of list and there is only one page.  Move to the top
+	 * link on the page.
+	 */
+	set_curdoc_link(0);
+
+    } else if (more_text &&	/* need a later page */
+	       HTGetLinkOrFieldStart(curdoc.link,
+				     &Newline, &newdoc.link,
+				     1, TRUE) != NO) {
+	LYChgNewline(1);	/* our line counting starts with 1 not 0 */
+	/* nothing more to do here */
+
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTInfoMsg(NO_LINKS_BELOW);
+    }
+    return;
+}
+
+static void handle_LYK_FIRST_LINK(void)
+{
+    int i = curdoc.link;
+
+    for (;;) {
+	if (--i < 0
+	    || links[i].ly != links[curdoc.link].ly) {
+	    set_curdoc_link(i + 1);
+	    break;
+	}
+    }
+}
+
+static BOOLEAN handle_LYK_GOTO(int *ch,
+			       char *user_input_buffer,
+			       char **old_user_input,
+			       RecallType * recall,
+			       int *URLTotal,
+			       int *URLNum,
+			       BOOLEAN *FirstURLRecall,
+			       int *old_c,
+			       int real_c)
+{
+
+    if (no_goto && !LYValidate) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(GOTO_DISALLOWED);
+	}
+	return FALSE;
+    }
+
+    StrAllocCopy(*old_user_input, user_input_buffer);
+    if (!goto_buffer)
+	*user_input_buffer = '\0';
+
+    *URLTotal = (Goto_URLs ? HTList_count(Goto_URLs) : 0);
+    if (goto_buffer && *user_input_buffer) {
+	*recall = ((*URLTotal > 1) ? RECALL_URL : NORECALL);
+	*URLNum = 0;
+	*FirstURLRecall = FALSE;
+    } else {
+	*recall = ((*URLTotal >= 1) ? RECALL_URL : NORECALL);
+	*URLNum = *URLTotal;
+	*FirstURLRecall = TRUE;
+    }
+
+    /*
+     * Ask the user.
+     */
+    _statusline(URL_TO_OPEN);
+    if ((*ch = LYgetstr(user_input_buffer, VISIBLE,
+			MAX_LINE, *recall)) < 0) {
+	/*
+	 * User cancelled the Goto via ^G.  Restore user_input_buffer and
+	 * break.  - FM
+	 */
+	LYstrncpy(user_input_buffer, *old_user_input, MAX_LINE - 1);
+	FREE(*old_user_input);
+	HTInfoMsg(CANCELLED);
+	return FALSE;
+    }
+    return TRUE;
+}
+
+static void handle_LYK_GROW_TEXTAREA(BOOLEAN *refresh_screen)
+{
+    /*
+     * See if the current link is in a form TEXTAREA.
+     */
+    if (LinkIsTextarea(curdoc.link)) {
+
+	HText_ExpandTextarea(&links[curdoc.link], TEXTAREA_EXPAND_SIZE);
+
+	*refresh_screen = TRUE;
+
+    } else {
+
+	HTInfoMsg(NOT_IN_TEXTAREA);
+    }
+}
+
+static BOOLEAN handle_LYK_HEAD(int *cmd)
+{
+    int c;
+
+    if (nlinks > 0 &&
+	(links[curdoc.link].type != WWW_FORM_LINK_TYPE ||
+	 links[curdoc.link].l_form->type == F_SUBMIT_TYPE ||
+	 links[curdoc.link].l_form->type == F_IMAGE_SUBMIT_TYPE ||
+	 links[curdoc.link].l_form->type == F_TEXT_SUBMIT_TYPE)) {
+	/*
+	 * We have links, and the current link is a normal link or a form's
+	 * submit button.  - FM
+	 */
+	_statusline(HEAD_D_L_OR_CANCEL);
+	c = LYgetch_single();
+	if (c == 'D') {
+	    char *scheme = !isLYNXIMGMAP(curdoc.address)
+	    ? curdoc.address
+	    : curdoc.address + LEN_LYNXIMGMAP;
+
+	    if (LYCanDoHEAD(scheme) != TRUE) {
+		HTUserMsg(DOC_NOT_HTTP_URL);
+	    } else {
+		/*
+		 * Check if this is a reply from a POST, and if so, seek
+		 * confirmation if the safe element is not set.  - FM
+		 */
+		if ((curdoc.post_data != NULL &&
+		     curdoc.safe != TRUE) &&
+		    HTConfirm(CONFIRM_POST_DOC_HEAD) == FALSE) {
+		    HTInfoMsg(CANCELLED);
+		} else {
+		    HEAD_request = TRUE;
+		    LYforce_no_cache = TRUE;
+		    StrAllocCopy(newdoc.title, curdoc.title);
+		    if (HTLoadedDocumentIsHEAD()) {
+			HText_setNoCache(HTMainText);
+			free_address(&curdoc);
+		    } else {
+			StrAllocCat(newdoc.title, " - HEAD");
+		    }
+		}
+	    }
+	} else if (c == 'L') {
+	    if (links[curdoc.link].type != WWW_FORM_LINK_TYPE &&
+		strncmp(links[curdoc.link].lname, "http", 4) &&
+		strncmp(links[curdoc.link].lname,
+			"LYNXIMGMAP:http", 15) &&
+		LYCanDoHEAD(links[curdoc.link].lname) != TRUE &&
+		(links[curdoc.link].type != WWW_INTERN_LINK_TYPE ||
+		 !curdoc.address ||
+		 strncmp(curdoc.address, "http", 4))) {
+		HTUserMsg(LINK_NOT_HTTP_URL);
+	    } else if (links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
+		       links[curdoc.link].l_form->disabled) {
+		HTUserMsg(FORM_ACTION_DISABLED);
+	    } else if (links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
+		       links[curdoc.link].l_form->submit_action != 0 &&
+		       !isLYNXCGI(links[curdoc.link].l_form->submit_action) &&
+		       strncmp(links[curdoc.link].l_form->submit_action,
+			       "http", 4)) {
+		HTUserMsg(FORM_ACTION_NOT_HTTP_URL);
+	    } else if (links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
+		       links[curdoc.link].l_form->submit_method ==
+		       URL_POST_METHOD &&
+		       HTConfirm(CONFIRM_POST_LINK_HEAD) == FALSE) {
+		HTInfoMsg(CANCELLED);
+	    } else {
+		HEAD_request = TRUE;
+		LYforce_no_cache = TRUE;
+		*cmd = LYK_ACTIVATE;
+		return TRUE;
+	    }
+	}
+    } else {
+	/*
+	 * We can offer only this document for a HEAD request.  Check if this
+	 * is a reply from a POST, and if so, seek confirmation if the safe
+	 * element is not set.  - FM
+	 */
+	if ((curdoc.post_data != NULL &&
+	     curdoc.safe != TRUE) &&
+	    HTConfirm(CONFIRM_POST_DOC_HEAD) == FALSE) {
+	    HTInfoMsg(CANCELLED);
+	} else {
+	    if (nlinks > 0) {
+		/*
+		 * The current link is a non-submittable form link, so prompt
+		 * the user to make it clear that the HEAD request would be for
+		 * the current document, not the form link.  - FM
+		 */
+		_statusline(HEAD_D_OR_CANCEL);
+		c = LYgetch_single();
+	    } else {
+		/*
+		 * No links, so we can just assume that the user wants a HEAD
+		 * request for the current document.  - FM
+		 */
+		c = 'D';
+	    }
+	    if (c == 'D') {
+		char *scheme = !isLYNXIMGMAP(curdoc.address)
+		? curdoc.address
+		: curdoc.address + LEN_LYNXIMGMAP;
+
+		/*
+		 * The user didn't cancel, so check if a HEAD request is
+		 * appropriate for the current document.  - FM
+		 */
+		if (LYCanDoHEAD(scheme) != TRUE) {
+		    HTUserMsg(DOC_NOT_HTTP_URL);
+		} else {
+		    HEAD_request = TRUE;
+		    LYforce_no_cache = TRUE;
+		    StrAllocCopy(newdoc.title, curdoc.title);
+		    if (HTLoadedDocumentIsHEAD()) {
+			HText_setNoCache(HTMainText);
+			free_address(&curdoc);
+		    } else {
+			StrAllocCat(newdoc.title, " - HEAD");
+		    }
+		}
+	    }
+	}
+    }
+    return FALSE;
+}
+
+static void handle_LYK_HELP(const char **cshelpfile)
+{
+    char *my_value = NULL;
+
+    if (*cshelpfile == NULL)
+	*cshelpfile = helpfile;
+    StrAllocCopy(my_value, *cshelpfile);
+    LYEnsureAbsoluteURL(&my_value, *cshelpfile, FALSE);
+    if (!STREQ(curdoc.address, my_value)) {
+	/*
+	 * Set the filename.
+	 */
+	set_address(&newdoc, my_value);
+	/*
+	 * Make a name for this help file.
+	 */
+	StrAllocCopy(newdoc.title, gettext("Help Screen"));
+	LYFreePostData(&newdoc);
+	FREE(newdoc.bookmark);
+	newdoc.isHEAD = FALSE;
+	newdoc.safe = FALSE;
+	newdoc.internal_link = FALSE;
+    }
+    FREE(my_value);
+    *cshelpfile = NULL;		/* reset pointer - kw */
+}
+
+static void handle_LYK_HISTORICAL(void)
+{
+#ifdef USE_SOURCE_CACHE
+    if (!HTcan_reparse_document()) {
+#endif
+	/*
+	 * Check if this is a reply from a POST, and if so, seek confirmation
+	 * of reload if the safe element is not set.  - FM
+	 */
+	if ((curdoc.post_data != NULL &&
+	     curdoc.safe != TRUE) &&
+	    confirm_post_resub(curdoc.address, NULL, 0, 0) == FALSE) {
+	    HTInfoMsg(WILL_NOT_RELOAD_DOC);
+	} else {
+	    HText_setNoCache(HTMainText);
+	    move_address(&newdoc, &curdoc);
+	    newdoc.line = curdoc.line;
+	    newdoc.link = curdoc.link;
+	}
+#ifdef USE_SOURCE_CACHE
+    }				/* end if no bypass */
+#endif
+    historical_comments = (BOOLEAN) !historical_comments;
+    if (minimal_comments) {
+	HTAlert(historical_comments ?
+		HISTORICAL_ON_MINIMAL_OFF : HISTORICAL_OFF_MINIMAL_ON);
+    } else {
+	HTAlert(historical_comments ?
+		HISTORICAL_ON_VALID_OFF : HISTORICAL_OFF_VALID_ON);
+    }
+#ifdef USE_SOURCE_CACHE
+    (void) reparse_document();
+#endif
+    return;
+}
+
+static BOOLEAN handle_LYK_HISTORY(BOOLEAN ForcePush)
+{
+    if (curdoc.title && !LYIsUIPage(curdoc.address, UIP_HISTORY)) {
+	/*
+	 * Don't do this if already viewing history page.
+	 *
+	 * Push the current file so that the history list contains the current
+	 * file for printing purposes.  Pop the file afterwards to prevent
+	 * multiple copies.
+	 */
+	if (TRACE && !LYUseTraceLog && LYCursesON) {
+	    LYHideCursor();	/* make sure cursor is down */
+#ifdef USE_SLANG
+	    LYaddstr("\n");
+#endif /* USE_SLANG */
+	    LYrefresh();
+	}
+	LYpush(&curdoc, ForcePush);
+
+	/*
+	 * Print history options to file.
+	 */
+	if (showhistory(&newdoc.address) < 0) {
+	    LYpop(&curdoc);
+	    return TRUE;
+	}
+	LYRegisterUIPage(newdoc.address, UIP_HISTORY);
+	StrAllocCopy(newdoc.title, HISTORY_PAGE_TITLE);
+	LYFreePostData(&newdoc);
+	FREE(newdoc.bookmark);
+	newdoc.isHEAD = FALSE;
+	newdoc.safe = FALSE;
+	newdoc.internal_link = FALSE;
+	newdoc.link = 1;	/*@@@ bypass "recent statusline messages" link */
+	free_address(&curdoc);	/* so it doesn't get pushed */
+
+	if (LYValidate || check_realm) {
+	    LYPermitURL = TRUE;
+	}
+	return TRUE;
+    }				/* end if strncmp */
+    return FALSE;
+}
+
+static BOOLEAN handle_LYK_IMAGE_TOGGLE(int *cmd)
+{
+    clickable_images = (BOOLEAN) !clickable_images;
+
+    HTUserMsg(clickable_images ?
+	      CLICKABLE_IMAGES_ON : CLICKABLE_IMAGES_OFF);
+    return reparse_or_reload(cmd);
+}
+
+static void handle_LYK_INDEX(int *old_c,
+			     int real_c)
+{
+    /*
+     * Make sure we are not in the index already.
+     */
+    if (!STREQ(curdoc.address, indexfile)) {
+
+	if (indexfile[0] == '\0') {	/* no defined index */
+	    if (*old_c != real_c) {
+		*old_c = real_c;
+		HTUserMsg(NO_INDEX_FILE);
+	    }
+
+	} else {
+#ifdef KANJI_CODE_OVERRIDE
+	    if (HTCJK == JAPANESE) {
+		last_kcode = NOKANJI;	/* AUTO */
+	    }
+#endif
+	    set_address(&newdoc, indexfile);
+	    StrAllocCopy(newdoc.title, gettext("System Index"));	/* name it */
+	    LYFreePostData(&newdoc);
+	    FREE(newdoc.bookmark);
+	    newdoc.isHEAD = FALSE;
+	    newdoc.safe = FALSE;
+	    newdoc.internal_link = FALSE;
+	}			/* end else */
+    }				/* end if */
+}
+
+static void handle_LYK_INDEX_SEARCH(BOOLEAN *force_load,
+				    BOOLEAN ForcePush,
+				    int *old_c,
+				    int real_c)
+{
+    if (is_www_index) {
+	/*
+	 * Perform a database search.
+	 *
+	 * do_www_search will try to go out and get the document.  If it
+	 * returns TRUE, a new document was returned and is named in the
+	 * newdoc.address.
+	 */
+	newdoc.isHEAD = FALSE;
+	newdoc.safe = FALSE;
+	if (do_www_search(&newdoc) == NORMAL) {
+	    /*
+	     * Yah, the search succeeded.
+	     */
+	    if (TRACE && !LYUseTraceLog && LYCursesON) {
+		/*
+		 * Make sure cursor is down.
+		 */
+		LYHideCursor();
+#ifdef USE_SLANG
+		LYaddstr("\n");
+#endif /* USE_SLANG */
+		LYrefresh();
+	    }
+	    LYpush(&curdoc, ForcePush);
+	    /*
+	     * Make the curdoc.address the newdoc.address so that getfile
+	     * doesn't try to get the newdoc.address.  Since we have already
+	     * gotten it.
+	     */
+	    copy_address(&curdoc, &newdoc);
+	    BStrCopy(newdoc.post_data, curdoc.post_data);
+	    StrAllocCopy(newdoc.post_content_type, curdoc.post_content_type);
+	    newdoc.internal_link = FALSE;
+	    curdoc.line = -1;
+	    LYSetNewline(0);
+	} else if (use_this_url_instead != NULL) {
+	    /*
+	     * Got back a redirecting URL.  Check it out.
+	     */
+	    HTUserMsg2(WWW_USING_MESSAGE, use_this_url_instead);
+
+	    /*
+	     * Make a name for this URL.
+	     */
+	    StrAllocCopy(newdoc.title,
+			 "A URL specified by redirection");
+	    set_address(&newdoc, use_this_url_instead);
+	    LYFreePostData(&newdoc);
+	    FREE(newdoc.bookmark);
+	    newdoc.isHEAD = FALSE;
+	    newdoc.safe = FALSE;
+	    newdoc.internal_link = FALSE;
+	    FREE(use_this_url_instead);
+	    *force_load = TRUE;
+	} else {
+	    /*
+	     * Yuk, the search failed.  Restore the old file.
+	     */
+	    copy_address(&newdoc, &curdoc);
+	    BStrCopy(newdoc.post_data, curdoc.post_data);
+	    StrAllocCopy(newdoc.post_content_type,
+			 curdoc.post_content_type);
+	    StrAllocCopy(newdoc.bookmark, curdoc.bookmark);
+	    newdoc.isHEAD = curdoc.isHEAD;
+	    newdoc.safe = curdoc.safe;
+	    newdoc.internal_link = curdoc.internal_link;
+	}
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTUserMsg(NOT_ISINDEX);
+    }
+}
+
+static BOOLEAN handle_LYK_INFO(int *cmd)
+{
+    /*
+     * Don't do if already viewing info page.
+     */
+    if (!LYIsUIPage(curdoc.address, UIP_SHOWINFO)) {
+	if (do_change_link() != -1
+	    && LYShowInfo(&curdoc, &newdoc, owner_address) >= 0) {
+	    LYRegisterUIPage(newdoc.address, UIP_SHOWINFO);
+	    StrAllocCopy(newdoc.title, SHOWINFO_TITLE);
+	    LYFreePostData(&newdoc);
+	    FREE(newdoc.bookmark);
+	    newdoc.isHEAD = FALSE;
+	    newdoc.safe = FALSE;
+	    newdoc.internal_link = FALSE;
+	    LYforce_no_cache = TRUE;
+	    if (LYValidate || check_realm)
+		LYPermitURL = TRUE;
+	}
+    } else {
+	/*
+	 * If already in info page, get out.
+	 */
+	*cmd = LYK_PREV_DOC;
+	return TRUE;
+    }
+    return FALSE;
+}
+
+static BOOLEAN handle_LYK_INLINE_TOGGLE(int *cmd)
+{
+    pseudo_inline_alts = (BOOLEAN) !pseudo_inline_alts;
+
+    HTUserMsg(pseudo_inline_alts ?
+	      PSEUDO_INLINE_ALTS_ON : PSEUDO_INLINE_ALTS_OFF);
+    return reparse_or_reload(cmd);
+}
+
+static void handle_LYK_INSERT_FILE(BOOLEAN *refresh_screen,
+				   int *old_c,
+				   int real_c)
+{
+    /*
+     * See if the current link is in a form TEXTAREA.
+     */
+    if (LinkIsTextarea(curdoc.link)) {
+
+	/*
+	 * Reject attempts to use this for gaining access to local files when
+	 * such access is restricted:  if no_file_url was set via the file_url
+	 * restriction, if no_goto_file was set for the anonymous account, or
+	 * if HTDirAccess was set to HT_DIR_FORBID or HT_DIR_SELECTIVE via the
+	 * -nobrowse or -selective switches, it is assumed that inserting files
+	 * or checking for existence of files needs to be denied.  - kw
+	 */
+	if (no_file_url || no_goto_file ||
+	    HTDirAccess == HT_DIR_FORBID ||
+	    HTDirAccess == HT_DIR_SELECTIVE) {
+	    if (*old_c != real_c) {
+		*old_c = real_c;
+		if (no_goto_file)
+		    HTUserMsg2(GOTO_XXXX_DISALLOWED, STR_FILE_URL);
+		else
+		    HTUserMsg(NOAUTH_TO_ACCESS_FILES);
+		HTInfoMsg(FILE_INSERT_CANCELLED);
+	    }
+	    return;
+	}
+
+	(void) HText_InsertFile(&links[curdoc.link]);
+
+	/*
+	 * TODO:
+	 * Move cursor "n" lines from the current line to position it on the
+	 * 1st line following the text that was inserted.  If the target
+	 * line/anchor requires us to scroll up/down, position the target in
+	 * the approximate center of the screen.
+	 *
+	 * [Current behavior leaves cursor on the same line relative to the
+	 * start of the TEXTAREA that it was on before the insertion.  This is
+	 * the same behavior that occurs with (my) editor, so this TODO will
+	 * stay unimplemented.]
+	 */
+
+	*refresh_screen = TRUE;
+
+    } else {
+
+	HTInfoMsg(NOT_IN_TEXTAREA);
+    }
+}
+
+#if defined(DIRED_SUPPORT) && defined(OK_INSTALL)
+static void handle_LYK_INSTALL(void)
+{
+    if (lynx_edit_mode && nlinks > 0 && !no_dired_support)
+	local_install(NULL, links[curdoc.link].lname, &newdoc.address);
+}
+#endif
+
+static BOOLEAN handle_LYK_JUMP(int c,
+			       char *user_input_buffer,
+			       char **old_user_input GCC_UNUSED,
+			       RecallType * recall GCC_UNUSED,
+			       BOOLEAN *FirstURLRecall GCC_UNUSED,
+			       int *URLNum GCC_UNUSED,
+			       int *URLTotal GCC_UNUSED,
+			       int *ch GCC_UNUSED,
+			       int *old_c,
+			       int real_c)
+{
+    char *ret;
+
+    if (no_jump || JThead == NULL) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    if (no_jump)
+		HTUserMsg(JUMP_DISALLOWED);
+	    else
+		HTUserMsg(NO_JUMPFILE);
+	}
+    } else {
+	LYJumpFileURL = TRUE;
+	if ((ret = LYJump(c)) != NULL) {
+#ifdef PERMIT_GOTO_FROM_JUMP
+	    if (!strncasecomp(ret, "Go ", 3)) {
+		LYJumpFileURL = FALSE;
+		StrAllocCopy(*old_user_input, user_input_buffer);
+		*URLTotal = (Goto_URLs ? HTList_count(Goto_URLs) : 0);
+		*recall = ((*URLTotal >= 1) ? RECALL_URL : NORECALL);
+		*URLNum = *URLTotal;
+		*FirstURLRecall = TRUE;
+		if (!strcasecomp(ret, "Go :")) {
+		    if (recall) {
+			*ch = UPARROW;
+			return TRUE;
+		    }
+		    FREE(*old_user_input);
+		    HTUserMsg(NO_RANDOM_URLS_YET);
+		    return FALSE;
+		}
+		ret = HTParse((ret + 3), startfile, PARSE_ALL);
+		LYstrncpy(user_input_buffer, ret, MAX_LINE - 1);
+		FREE(ret);
+		return TRUE;
+	    }
+#endif /* PERMIT_GOTO_FROM_JUMP */
+	    ret = HTParse(ret, startfile, PARSE_ALL);
+	    if (!LYTrimStartfile(ret)) {
+		LYRemoveBlanks(user_input_buffer);
+	    }
+	    set_address(&newdoc, ret);
+	    StrAllocCopy(lynxjumpfile, ret);
+	    LYFreePostData(&newdoc);
+	    FREE(newdoc.bookmark);
+	    newdoc.isHEAD = FALSE;
+	    newdoc.safe = FALSE;
+	    newdoc.internal_link = FALSE;
+	    FREE(ret);
+	    LYUserSpecifiedURL = TRUE;
+	} else {
+	    LYJumpFileURL = FALSE;
+	}
+    }
+    return FALSE;
+}
+
+static void handle_LYK_KEYMAP(BOOLEAN *vi_keys_flag,
+			      BOOLEAN *emacs_keys_flag,
+			      int *old_c,
+			      int real_c)
+{
+    if (*old_c != real_c) {
+	*old_c = real_c;
+	set_address(&newdoc, STR_LYNXKEYMAP);
+	StrAllocCopy(newdoc.title, CURRENT_KEYMAP_TITLE);
+	LYFreePostData(&newdoc);
+	FREE(newdoc.bookmark);
+	newdoc.isHEAD = FALSE;
+	newdoc.safe = FALSE;
+	newdoc.internal_link = FALSE;
+	/*
+	 * If vi_keys changed, the keymap did too, so force no cache, and reset
+	 * the flag.  - FM
+	 */
+	if (*vi_keys_flag != vi_keys ||
+	    *emacs_keys_flag != emacs_keys) {
+	    LYforce_no_cache = TRUE;
+	    *vi_keys_flag = vi_keys;
+	    *emacs_keys_flag = emacs_keys;
+	}
+#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE)
+	/*
+	 * Remember whether we are in dired menu so we can display the right
+	 * keymap.
+	 */
+	if (!no_dired_support) {
+	    prev_lynx_edit_mode = lynx_edit_mode;
+	}
+#endif /* DIRED_SUPPORT && OK_OVERRIDE */
+	LYforce_no_cache = TRUE;
+    }
+}
+
+static void handle_LYK_LAST_LINK(void)
+{
+    int i = curdoc.link;
+
+    for (;;) {
+	if (++i >= nlinks
+	    || links[i].ly != links[curdoc.link].ly) {
+	    set_curdoc_link(i - 1);
+	    break;
+	}
+    }
+}
+
+static void handle_LYK_LEFT_LINK(void)
+{
+    if (curdoc.link > 0 &&
+	links[curdoc.link].ly == links[curdoc.link - 1].ly) {
+	set_curdoc_link(curdoc.link - 1);
+    }
+}
+
+static BOOLEAN handle_LYK_LIST(int *cmd)
+{
+    /*
+     * Don't do if already viewing list page.
+     */
+    if (!strcmp(NonNull(curdoc.title), LIST_PAGE_TITLE) &&
+	LYIsUIPage(curdoc.address, UIP_LIST_PAGE)) {
+	/*
+	 * Already viewing list page, so get out.
+	 */
+	*cmd = LYK_PREV_DOC;
+	return TRUE;
+    }
+
+    /*
+     * Print list page to file.
+     */
+    if (showlist(&newdoc, TRUE) < 0)
+	return FALSE;
+    StrAllocCopy(newdoc.title, LIST_PAGE_TITLE);
+    /*
+     * showlist will set newdoc's other fields.  It may leave post_data intact
+     * so the list can be used to follow internal links in the current document
+     * even if it is a POST response.  - kw
+     */
+
+    if (LYValidate || check_realm) {
+	LYPermitURL = TRUE;
+	StrAllocCopy(lynxlistfile, newdoc.address);
+    }
+    return FALSE;
+}
+
+static void handle_LYK_MAIN_MENU(int *old_c,
+				 int real_c)
+{
+    /*
+     * If its already the homepage then don't reload it.
+     */
+    if (!STREQ(curdoc.address, homepage)) {
+
+	if (HTConfirmDefault(CONFIRM_MAIN_SCREEN, NO) == YES) {
+	    set_address(&newdoc, homepage);
+	    StrAllocCopy(newdoc.title, gettext("Entry into main screen"));
+	    LYFreePostData(&newdoc);
+	    FREE(newdoc.bookmark);
+	    newdoc.isHEAD = FALSE;
+	    newdoc.safe = FALSE;
+	    newdoc.internal_link = FALSE;
+	    LYhighlight(OFF, curdoc.link, prev_target);
+#ifdef DIRED_SUPPORT
+	    if (lynx_edit_mode) {
+		DIRED_UNCACHE_2;
+	    }
+#endif /* DIRED_SUPPORT */
+	}
+    } else {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(IN_MAIN_SCREEN);
+	}
+    }
+}
+
+static void handle_LYK_MINIMAL(void)
+{
+    if (!historical_comments) {
+#ifdef USE_SOURCE_CACHE
+	if (!HTcan_reparse_document()) {
+#endif
+	    /*
+	     * Check if this is a reply from a POST, and if so, seek
+	     * confirmation of reload if the safe element is not set.  - FM
+	     */
+	    if ((curdoc.post_data != NULL &&
+		 curdoc.safe != TRUE) &&
+		confirm_post_resub(curdoc.address, NULL, 0, 0) == FALSE) {
+		HTInfoMsg(WILL_NOT_RELOAD_DOC);
+	    } else {
+		HText_setNoCache(HTMainText);
+		move_address(&newdoc, &curdoc);
+		newdoc.line = curdoc.line;
+		newdoc.link = curdoc.link;
+	    }
+#ifdef USE_SOURCE_CACHE
+	}			/* end if no bypass */
+#endif
+    }
+    minimal_comments = (BOOLEAN) !minimal_comments;
+    if (!historical_comments) {
+	HTAlert(minimal_comments ?
+		MINIMAL_ON_IN_EFFECT : MINIMAL_OFF_VALID_ON);
+    } else {
+	HTAlert(minimal_comments ?
+		MINIMAL_ON_BUT_HISTORICAL : MINIMAL_OFF_HISTORICAL_ON);
+    }
+#ifdef USE_SOURCE_CACHE
+    (void) reparse_document();
+#endif
+    return;
+}
+
+#if defined(DIRED_SUPPORT)
+static void handle_LYK_MODIFY(BOOLEAN *refresh_screen)
+{
+    if (lynx_edit_mode && nlinks > 0 && !no_dired_support) {
+	int ret;
+
+	ret = local_modify(&curdoc, &newdoc.address);
+	if (ret == PERMIT_FORM_RESULT) {	/* Permit form thrown up */
+	    *refresh_screen = TRUE;
+	} else if (ret) {
+	    DIRED_UNCACHE_1;
+	    move_address(&newdoc, &curdoc);
+	    LYFreePostData(&newdoc);
+	    FREE(newdoc.bookmark);
+	    newdoc.isHEAD = FALSE;
+	    newdoc.safe = FALSE;
+	    newdoc.internal_link = FALSE;
+	    newdoc.line = curdoc.line;
+	    newdoc.link = curdoc.link;
+	    LYclear();
+	}
+    }
+}
+#endif /* DIRED_SUPPORT */
+
+#ifdef EXP_NESTED_TABLES
+static BOOLEAN handle_LYK_NESTED_TABLES(int *cmd)
+{
+    nested_tables = (BOOLEAN) !nested_tables;
+    HTUserMsg(nested_tables ? NESTED_TABLES_ON : NESTED_TABLES_OFF);
+    return reparse_or_reload(cmd);
+}
+#endif
+
+static BOOLEAN handle_LYK_OPTIONS(int *cmd,
+				  BOOLEAN *refresh_screen)
+{
+#ifndef NO_OPTION_MENU
+    if (!LYUseFormsOptions) {
+	BOOLEAN LYUseDefaultRawMode_flag = LYUseDefaultRawMode;
+	BOOLEAN LYSelectPopups_flag = LYSelectPopups;
+	BOOLEAN verbose_img_flag = verbose_img;
+	BOOLEAN keypad_mode_flag = (BOOL) keypad_mode;
+	BOOLEAN show_dotfiles_flag = show_dotfiles;
+	BOOLEAN user_mode_flag = (BOOL) user_mode;
+	int CurrentAssumeCharSet_flag = UCLYhndl_for_unspec;
+	int CurrentCharSet_flag = current_char_set;
+	int HTfileSortMethod_flag = HTfileSortMethod;
+	char *CurrentUserAgent = NULL;
+	char *CurrentNegoLanguage = NULL;
+	char *CurrentNegoCharset = NULL;
+
+	StrAllocCopy(CurrentUserAgent, NonNull(LYUserAgent));
+	StrAllocCopy(CurrentNegoLanguage, NonNull(language));
+	StrAllocCopy(CurrentNegoCharset, NonNull(pref_charset));
+
+	LYoptions(); /** do the old-style options stuff **/
+
+	if (keypad_mode_flag != keypad_mode ||
+	    (user_mode_flag != user_mode &&
+	     (user_mode_flag == NOVICE_MODE ||
+	      user_mode == NOVICE_MODE)) ||
+	    (((HTfileSortMethod_flag != HTfileSortMethod) ||
+	      (show_dotfiles_flag != show_dotfiles)) &&
+	     (isFILE_URL(curdoc.address) ||
+	      isFTP_URL(curdoc.address))) ||
+	    CurrentCharSet_flag != current_char_set ||
+	    CurrentAssumeCharSet_flag != UCLYhndl_for_unspec ||
+	    verbose_img_flag != verbose_img ||
+	    LYUseDefaultRawMode_flag != LYUseDefaultRawMode ||
+	    LYSelectPopups_flag != LYSelectPopups ||
+	    ((strcmp(CurrentUserAgent, NonNull(LYUserAgent)) ||
+	      strcmp(CurrentNegoLanguage, NonNull(language)) ||
+	      strcmp(CurrentNegoCharset, NonNull(pref_charset))) &&
+	     (!strncmp(curdoc.address, "http", 4) ||
+	      isLYNXCGI(curdoc.address)))) {
+
+	    BOOLEAN canreparse_post = FALSE;
+
+	    /*
+	     * Check if this is a reply from a POST, and if so, seek
+	     * confirmation of reload if the safe element is not set.  - FM
+	     */
+	    if ((curdoc.post_data != NULL &&
+		 curdoc.safe != TRUE) &&
+#ifdef USE_SOURCE_CACHE
+		(!(canreparse_post = HTcan_reparse_document())) &&
+#endif
+		confirm_post_resub(curdoc.address, curdoc.title,
+				   2, 1) == FALSE) {
+		HTInfoMsg(WILL_NOT_RELOAD_DOC);
+	    } else {
+		copy_address(&newdoc, &curdoc);
+		if (((strcmp(CurrentUserAgent, NonNull(LYUserAgent)) ||
+		      strcmp(CurrentNegoLanguage, NonNull(language)) ||
+		      strcmp(CurrentNegoCharset, NonNull(pref_charset))) &&
+		     (strncmp(curdoc.address, "http", 4) == 0 ||
+		      !isLYNXCGI(curdoc.address) == 0))) {
+		    /*
+		     * An option has changed which may influence content
+		     * negotiation, and the resource is from a http or https or
+		     * lynxcgi URL (the only protocols which currently do
+		     * anything with this information).  Set reloading = TRUE
+		     * so that proxy caches will be flushed, which is necessary
+		     * until the time when all proxies understand HTTP 1.1
+		     * Vary:  and all Servers properly use it...  Treat like
+		     * case LYK_RELOAD (see comments there).  - KW
+		     */
+		    reloading = TRUE;
+		}
+		if (HTisDocumentSource()) {
+		    srcmode_for_next_retrieval(1);
+		}
+#ifdef USE_SOURCE_CACHE
+		if (reloading == FALSE) {
+		    /* one more attempt to be smart enough: */
+		    if (reparse_document()) {
+			FREE(CurrentUserAgent);
+			FREE(CurrentNegoLanguage);
+			FREE(CurrentNegoCharset);
+			return FALSE;
+		    }
+		}
+#endif
+		if (canreparse_post &&
+		    confirm_post_resub(curdoc.address, curdoc.title,
+				       2, 1) == FALSE) {
+		    if (HTisDocumentSource()) {
+			srcmode_for_next_retrieval(0);
+		    }
+		    FREE(CurrentUserAgent);
+		    FREE(CurrentNegoLanguage);
+		    FREE(CurrentNegoCharset);
+		    return FALSE;
+		}
+
+		HEAD_request = HTLoadedDocumentIsHEAD();
+		HText_setNoCache(HTMainText);
+		newdoc.line = curdoc.line;
+		newdoc.link = curdoc.link;
+		LYforce_no_cache = TRUE;
+		free_address(&curdoc);	/* So it doesn't get pushed. */
+	    }
+	}
+	FREE(CurrentUserAgent);
+	FREE(CurrentNegoLanguage);
+	FREE(CurrentNegoCharset);
+	*refresh_screen = TRUE;	/* to repaint screen */
+	return FALSE;
+    }				/* end if !LYUseFormsOptions */
+#endif /* !NO_OPTION_MENU */
+#ifndef NO_OPTION_FORMS
+    /*
+     * Generally stolen from LYK_COOKIE_JAR.  Options menu handling is
+     * done in postoptions(), called from getfile() currently.
+     *
+     * postoptions() is also responsible for reloading the document
+     * before the 'options menu' but only when (a few) important
+     * options were changed.
+     *
+     * It is critical that post_data is freed here since the
+     * submission of changed options is done via the same protocol as
+     * LYNXOPTIONS:
+     */
+    /*
+     * Don't do if already viewing options page.
+     */
+    if (!LYIsUIPage(curdoc.address, UIP_OPTIONS_MENU)) {
+
+	set_address(&newdoc, LYNXOPTIONS_PAGE("/"));
+	LYFreePostData(&newdoc);
+	FREE(newdoc.bookmark);
+	newdoc.isHEAD = FALSE;
+	newdoc.safe = FALSE;
+	newdoc.internal_link = FALSE;
+	LYforce_no_cache = TRUE;
+	/* change to 'if (check_realm && !LYValidate)' and
+	   make change near top of getfile to forbid
+	   using forms options menu with -validate:  - kw */
+	if (LYValidate || check_realm) {
+	    LYPermitURL = TRUE;
+	}
+    } else {
+	/*
+	 * If already in the options menu, get out.
+	 */
+	*cmd = LYK_PREV_DOC;
+	return TRUE;
+    }
+#endif /* !NO_OPTION_FORMS */
+    return FALSE;
+}
+
+static void handle_NEXT_DOC(void)
+{
+    if (LYhist_next(&curdoc, &newdoc)) {
+	free_address(&curdoc);	/* avoid push */
+	return;
+    }
+    HTUserMsg(gettext("No next document present"));
+}
+
+static void handle_LYK_NEXT_LINK(int c,
+				 int *old_c,
+				 int real_c)
+{
+    if (curdoc.link < nlinks - 1) {	/* next link */
+	LYhighlight(OFF, curdoc.link, prev_target);
+#ifdef FASTTAB
+	/*
+	 * Move to different textarea if TAB in textarea.
+	 */
+	if (LinkIsTextarea(curdoc.link) &&
+	    c == '\t') {
+	    int thisgroup = links[curdoc.link].l_form->number;
+	    char *thisname = links[curdoc.link].l_form->name;
+
+	    do
+		curdoc.link++;
+	    while ((curdoc.link < nlinks - 1) &&
+		   LinkIsTextarea(curdoc.link) &&
+		   links[curdoc.link].l_form->number == thisgroup &&
+		   sametext(links[curdoc.link].l_form->name, thisname));
+	} else {
+	    curdoc.link++;
+	}
+#else
+	curdoc.link++;
+#endif /* FASTTAB */
+	/*
+	 * At the bottom of list and there is only one page.  Move to the top
+	 * link on the page.
+	 */
+    } else if (!more_text && LYGetNewline() == 1 && curdoc.link == nlinks - 1) {
+	set_curdoc_link(0);
+
+    } else if (more_text) {	/* next page */
+	LYChgNewline(display_lines);
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTInfoMsg(ALREADY_AT_END);
+    }
+}
+
+static void handle_LYK_NEXT_PAGE(int *old_c,
+				 int real_c)
+{
+    if (more_text) {
+	LYChgNewline(display_lines);
+    } else if (curdoc.link < nlinks - 1) {
+	set_curdoc_link(nlinks - 1);
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTInfoMsg(ALREADY_AT_END);
+    }
+}
+
+static BOOLEAN handle_LYK_NOCACHE(int *old_c,
+				  int real_c)
+{
+    if (nlinks > 0) {
+	if (links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
+	    links[curdoc.link].l_form->type != F_SUBMIT_TYPE &&
+	    links[curdoc.link].l_form->type != F_IMAGE_SUBMIT_TYPE &&
+	    links[curdoc.link].l_form->type != F_TEXT_SUBMIT_TYPE) {
+	    if (*old_c != real_c) {
+		*old_c = real_c;
+		HTUserMsg(NOT_ON_SUBMIT_OR_LINK);
+	    }
+	    return FALSE;
+	} else {
+	    LYforce_no_cache = TRUE;
+	    reloading = TRUE;
+	}
+    }
+    return TRUE;
+}
+
+static void handle_LYK_PREV_LINK(int *arrowup,
+				 int *old_c,
+				 int real_c)
+{
+    if (curdoc.link > 0) {	/* previous link */
+	set_curdoc_link(curdoc.link - 1);
+
+    } else if (!more_text &&
+	       curdoc.link == 0 && LYGetNewline() == 1) {	/* at the top of list */
+	/*
+	 * If there is only one page of data and the user goes off the top,
+	 * just move the cursor to last link on the page.
+	 */
+	set_curdoc_link(nlinks - 1);
+
+    } else if (curdoc.line > 1) {	/* previous page */
+	/*
+	 * Go back to the previous page.
+	 */
+	int scrollamount = (LYGetNewline() > display_lines
+			    ? display_lines
+			    : LYGetNewline() - 1);
+
+	LYChgNewline(-scrollamount);
+	if (scrollamount < display_lines &&
+	    nlinks > 0 && curdoc.link == 0 &&
+	    links[0].ly - 1 + scrollamount <= display_lines) {
+	    newdoc.link = HText_LinksInLines(HTMainText,
+					     1,
+					     scrollamount) - 1;
+	} else {
+	    *arrowup = TRUE;
+	}
+
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTInfoMsg(ALREADY_AT_BEGIN);
+    }
+}
+
+#define nhist_1 (nhist - 1)	/* workaround for indent */
+
+static int handle_PREV_DOC(int *cmd,
+			   int *old_c,
+			   int real_c)
+{
+    if (nhist > 0) {		/* if there is anything to go back to */
+	/*
+	 * Check if the previous document is a reply from a POST, and if so,
+	 * seek confirmation of resubmission if the safe element is not set and
+	 * the document is not still in the cache or LYresubmit_posts is set. 
+	 * If not confirmed and it is not the startfile, pop it so we go to the
+	 * yet previous document, until we're OK or reach the startfile.  If we
+	 * reach the startfile and its not OK or we don't get confirmation,
+	 * cancel.  - FM
+	 */
+	DocAddress WWWDoc;
+	HTParentAnchor *tmpanchor;
+	BOOLEAN conf = FALSE, first = TRUE;
+
+	HTLastConfirmCancelled();	/* reset flag */
+	while (nhist > 0) {
+	    conf = FALSE;
+	    if (HDOC(nhist_1).post_data == NULL) {
+		break;
+	    }
+	    WWWDoc.address = HDOC(nhist_1).address;
+	    WWWDoc.post_data = HDOC(nhist_1).post_data;
+	    WWWDoc.post_content_type =
+		HDOC(nhist_1).post_content_type;
+	    WWWDoc.bookmark = HDOC(nhist_1).bookmark;
+	    WWWDoc.isHEAD = HDOC(nhist_1).isHEAD;
+	    WWWDoc.safe = HDOC(nhist_1).safe;
+	    tmpanchor = HTAnchor_findAddress(&WWWDoc);
+	    if (HTAnchor_safe(tmpanchor)) {
+		break;
+	    }
+	    if ((HTAnchor_document(tmpanchor) == NULL &&
+		 (isLYNXIMGMAP(WWWDoc.address) ||
+		  (conf = confirm_post_resub(WWWDoc.address,
+					     HDOC(nhist_1).title,
+					     0, 0))
+		  == FALSE)) ||
+		((LYresubmit_posts && !conf &&
+		  (NONINTERNAL_OR_PHYS_DIFFERENT((DocInfo *) &history[(nhist_1)],
+						 &curdoc) ||
+		   NONINTERNAL_OR_PHYS_DIFFERENT((DocInfo *) &history[(nhist_1)],
+						 &newdoc))) &&
+		 !confirm_post_resub(WWWDoc.address,
+				     HDOC(nhist_1).title,
+				     2, 2))) {
+		if (HTLastConfirmCancelled()) {
+		    if (!first && curdoc.internal_link)
+			free_address(&curdoc);
+		    *cmd = LYK_DO_NOTHING;
+		    return 2;
+		}
+		if (nhist == 1) {
+		    HTInfoMsg(CANCELLED);
+		    *old_c = 0;
+		    *cmd = LYK_DO_NOTHING;
+		    return 2;
+		} else {
+		    HTUserMsg2(WWW_SKIP_MESSAGE, WWWDoc.address);
+		    do {	/* Should be LYhist_prev when _next supports */
+			LYpop(&curdoc);		/* skipping of forms */
+		    } while (nhist > 1
+			     && !are_different((DocInfo *) &history[nhist_1],
+					       &curdoc));
+		    first = FALSE;	/* have popped at least one */
+		    continue;
+		}
+	    } else {
+		/*
+		 * Break from loop; if user just confirmed to load again
+		 * because document wasn't in cache, set LYforce_no_cache to
+		 * avoid unnecessary repeat question down the road.  - kw
+		 */
+		if (conf)
+		    LYforce_no_cache = TRUE;
+		break;
+	    }
+	}
+
+	if (!first)
+	    curdoc.internal_link = FALSE;
+
+	/*
+	 * Set newdoc.address to empty to pop a file.
+	 */
+	LYhist_prev_register(&curdoc);	/* Why not call _prev instead of zeroing address?  */
+	free_address(&newdoc);
+#ifdef DIRED_SUPPORT
+	if (lynx_edit_mode) {
+	    DIRED_UNCACHE_2;
+	}
+#endif /* DIRED_SUPPORT */
+    } else if (child_lynx == TRUE) {
+	return (1);		/* exit on left arrow in main screen */
+
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTUserMsg(ALREADY_AT_FIRST);
+    }
+    return 0;
+}
+
+static void handle_LYK_PREV_PAGE(int *old_c,
+				 int real_c)
+{
+    if (LYGetNewline() > 1) {
+	LYChgNewline(-display_lines);
+    } else if (curdoc.link > 0) {
+	set_curdoc_link(0);
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTInfoMsg(ALREADY_AT_BEGIN);
+    }
+}
+
+static void handle_LYK_PRINT(BOOLEAN *ForcePush,
+			     int *old_c,
+			     int real_c)
+{
+    if (LYValidate) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(PRINT_DISABLED);
+	}
+	return;
+    }
+
+    /*
+     * Don't do if already viewing print options page.
+     */
+    if (!LYIsUIPage(curdoc.address, UIP_PRINT_OPTIONS)
+	&& print_options(&newdoc.address,
+			 curdoc.address, HText_getNumOfLines()) >= 0) {
+	LYRegisterUIPage(newdoc.address, UIP_PRINT_OPTIONS);
+	StrAllocCopy(newdoc.title, PRINT_OPTIONS_TITLE);
+	LYFreePostData(&newdoc);
+	FREE(newdoc.bookmark);
+	newdoc.isHEAD = FALSE;
+	newdoc.safe = FALSE;
+	*ForcePush = TRUE;	/* see LYpush() and print_options() */
+	if (check_realm)
+	    LYPermitURL = TRUE;
+    }
+}
+
+static BOOLEAN handle_LYK_QUIT(void)
+{
+    int c;
+
+    if (LYQuitDefaultYes == TRUE) {
+	c = HTConfirmDefault(REALLY_QUIT, YES);
+    } else {
+	c = HTConfirmDefault(REALLY_QUIT, NO);
+    }
+    if (LYQuitDefaultYes == TRUE) {
+	if (c != NO) {
+	    return (TRUE);
+	} else {
+	    HTInfoMsg(NO_CANCEL);
+	}
+    } else if (c == YES) {
+	return (TRUE);
+    } else {
+	HTInfoMsg(NO_CANCEL);
+    }
+    return FALSE;
+}
+
+static BOOLEAN handle_LYK_RAW_TOGGLE(int *cmd)
+{
+    if (HTLoadedDocumentCharset()) {
+	HTUserMsg(gettext("charset for this document specified explicitly, sorry..."));
+	return FALSE;
+    } else {
+	LYUseDefaultRawMode = (BOOL) !LYUseDefaultRawMode;
+	HTUserMsg(LYRawMode ? RAWMODE_OFF : RAWMODE_ON);
+	HTMLSetCharacterHandling(current_char_set);
+	return reparse_or_reload(cmd);
+    }
+}
+
+static void handle_LYK_RELOAD(int real_cmd)
+{
+    /*
+     * Check if this is a reply from a POST, and if so,
+     * seek confirmation if the safe element is not set.  - FM
+     */
+    if ((curdoc.post_data != NULL &&
+	 curdoc.safe != TRUE) &&
+	HTConfirm(CONFIRM_POST_RESUBMISSION) == FALSE) {
+	HTInfoMsg(CANCELLED);
+	return;
+    }
+
+    /*
+     * Check to see if should reload source, or load html
+     */
+
+    if (HTisDocumentSource()) {
+	if ((forced_UCLYhdnl = HTMainText_Get_UCLYhndl()) >= 0)
+	    force_old_UCLYhndl_on_reload = TRUE;
+	srcmode_for_next_retrieval(1);
+    }
+
+    HEAD_request = HTLoadedDocumentIsHEAD();
+    HText_setNoCache(HTMainText);
+    /*
+     * Do assume the reloaded document will be the same.  - FM
+     *
+     * (I don't remember all the reasons why we couldn't assume this.  As the
+     * problems show up, we'll try to fix them, or add warnings.  - FM)
+     */
+    newdoc.line = curdoc.line;
+    newdoc.link = curdoc.link;
+    free_address(&curdoc);	/* so it doesn't get pushed */
+#ifdef VMS
+    lynx_force_repaint();
+#endif /* VMS */
+    /*
+     * Reload should force a cache refresh on a proxy.  -- Ari L. 
+     * <luotonen@dxcern.cern.ch>
+     *
+     * -- but only if this was really a reload requested by the user, not if we
+     * jumped here to handle reloading for INLINE_TOGGLE, IMAGE_TOGGLE,
+     * RAW_TOGGLE, etc.  - KW
+     */
+    if (real_cmd == LYK_RELOAD)
+	reloading = TRUE;
+
+    return;
+}
+
+#ifdef DIRED_SUPPORT
+static void handle_LYK_REMOVE(BOOLEAN *refresh_screen)
+{
+    if (lynx_edit_mode && nlinks > 0 && !no_dired_support) {
+	int linkno = curdoc.link;	/* may be changed in local_remove - kw */
+
+	local_remove(&curdoc);
+	if (LYAutoUncacheDirLists >= 1)
+	    do_cleanup_after_delete();
+	else if (curdoc.link != linkno)
+	    *refresh_screen = TRUE;
+    }
+}
+#endif /* DIRED_SUPPORT */
+
+static void handle_LYK_RIGHT_LINK(void)
+{
+    if (curdoc.link < nlinks - 1 &&
+	links[curdoc.link].ly == links[curdoc.link + 1].ly) {
+	set_curdoc_link(curdoc.link + 1);
+    }
+}
+
+static void handle_LYK_SHELL(BOOLEAN *refresh_screen,
+			     int *old_c,
+			     int real_c)
+{
+    if (!no_shell) {
+	stop_curses();
+	printf("%s\r\n", SPAWNING_MSG);
+#if defined(__CYGWIN__)
+	/* handling "exec $SHELL" does not work if $SHELL is null */
+	if (LYGetEnv("SHELL") == NULL) {
+	    Cygwin_Shell();
+	} else
+#endif
+	{
+	    static char *shell = NULL;
+
+	    if (shell == 0)
+		StrAllocCopy(shell, LYSysShell());
+	    LYSystem(shell);
+	}
+	start_curses();
+	*refresh_screen = TRUE;	/* for an HText_pageDisplay() */
+    } else {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(SPAWNING_DISABLED);
+	}
+    }
+}
+
+static void handle_LYK_SOFT_DQUOTES(void)
+{
+#ifdef USE_SOURCE_CACHE
+    if (!HTcan_reparse_document()) {
+#endif
+	/*
+	 * Check if this is a reply from a POST, and if so, seek confirmation
+	 * of reload if the safe element is not set.  - FM
+	 */
+	if ((curdoc.post_data != NULL &&
+	     curdoc.safe != TRUE) &&
+	    confirm_post_resub(curdoc.address, NULL, 1, 1) == FALSE) {
+	    HTInfoMsg(WILL_NOT_RELOAD_DOC);
+	} else {
+	    HText_setNoCache(HTMainText);
+	    move_address(&newdoc, &curdoc);
+	    newdoc.line = curdoc.line;
+	    newdoc.link = curdoc.link;
+	}
+#ifdef USE_SOURCE_CACHE
+    }				/* end if no bypass */
+#endif
+    soft_dquotes = (BOOLEAN) !soft_dquotes;
+    HTUserMsg(soft_dquotes ?
+	      SOFT_DOUBLE_QUOTE_ON : SOFT_DOUBLE_QUOTE_OFF);
+#ifdef USE_SOURCE_CACHE
+    (void) reparse_document();
+#endif
+    return;
+}
+
+#define GetAnchorNumber(link) \
+			((nlinks > 0 && link >= 0) \
+    			 ? links[link].anchor_number \
+			 : -1)
+#define GetAnchorLineNo(link) \
+			((nlinks > 0 && link >= 0) \
+    			 ? links[link].anchor_line_num \
+			 : -1)
+
+/*
+ * Adjust the top-of-screen line number for the new document if the redisplayed
+ * screen would not show the given link-number.
+ */
+#ifdef USE_SOURCE_CACHE
+static int wrap_reparse_document(void)
+{
+    int result;
+    int anchor_number = GetAnchorNumber(curdoc.link);
+    int old_line_num = HText_getAbsLineNumber(HTMainText, anchor_number);
+    int old_from_top = old_line_num - LYGetNewline() + 1;
+
+    /* get the offset for the current anchor */
+    int old_offset = ((nlinks > 0 && curdoc.link >= 0)
+		      ? links[curdoc.link].sgml_offset
+		      : -1);
+
+    CTRACE((tfp, "original anchor %d, topline %d, link %d, offset %d\n",
+	    anchor_number, old_line_num, curdoc.link, old_offset));
+
+    /* reparse the document (producing a new anchor list) */
+    result = reparse_document();
+
+    /* readjust top-line and link-number */
+    if (result && old_offset >= 0) {
+	int new_anchor = HText_closestAnchor(HTMainText, old_offset);
+	int new_lineno = HText_getAbsLineNumber(HTMainText, new_anchor);
+	int top_lineno;
+
+	CTRACE((tfp, "old anchor %d -> new anchor %d\n", anchor_number, new_anchor));
+
+	if (new_lineno - old_from_top < 0)
+	    old_from_top = new_lineno;
+
+	/* Newline and newdoc.line are 1-based,
+	 * but 0-based lines are simpler to work with.
+	 */
+	top_lineno = HText_getPreferredTopLine(HTMainText, new_lineno -
+					       old_from_top) + 1;
+	CTRACE((tfp, "preferred top %d\n", top_lineno));
+
+	if (top_lineno != LYGetNewline()) {
+	    LYSetNewline(top_lineno);
+	    newdoc.link = HText_anchorRelativeTo(HTMainText, top_lineno - 1, new_anchor);
+	    curdoc.link = newdoc.link;
+	    CTRACE((tfp,
+		    "adjusted anchor %d, topline %d, link %d, offset %d\n",
+		    new_anchor,
+		    top_lineno,
+		    curdoc.link,
+		    HText_locateAnchor(HTMainText, new_anchor)));
+	} else {
+	    newdoc.link = curdoc.link;
+	}
+    }
+    return result;
+}
+#endif /* USE_SOURCE_CACHE */
+
+static void handle_LYK_SOURCE(char **ownerS_address_p)
+{
+#ifdef USE_SOURCE_CACHE
+    BOOLEAN canreparse_post = FALSE;
+#endif
+
+    /*
+     * Check if this is a reply from a POST, and if so,
+     * seek confirmation if the safe element is not set.  - FM
+     */
+    if ((curdoc.post_data != NULL &&
+	 curdoc.safe != TRUE) &&
+#ifdef USE_SOURCE_CACHE
+	(!(canreparse_post = HTcan_reparse_document())) &&
+#endif
+	(curdoc.isHEAD ? HTConfirm(CONFIRM_POST_RESUBMISSION) :
+	 confirm_post_resub(curdoc.address, curdoc.title, 1, 1)) == FALSE) {
+	HTInfoMsg(CANCELLED);
+	return;
+    }
+
+    if (HTisDocumentSource()) {
+	srcmode_for_next_retrieval(-1);
+    } else {
+	if (HText_getOwner())
+	    StrAllocCopy(*ownerS_address_p, HText_getOwner());
+	LYUCPushAssumed(HTMainAnchor);
+	srcmode_for_next_retrieval(1);
+    }
+
+#ifdef USE_SOURCE_CACHE
+    if (wrap_reparse_document()) {
+	/*
+	 * These normally get cleaned up after getfile() returns;
+	 * since we're not calling getfile(), we have to clean them
+	 * up ourselves.  -dsb
+	 */
+	HTOutputFormat = WWW_PRESENT;
+#ifdef USE_PRETTYSRC
+	if (psrc_view)
+	    HTMark_asSource();
+	psrc_view = FALSE;
+#endif
+	FREE(*ownerS_address_p);	/* not used with source_cache */
+	LYUCPopAssumed();	/* probably a right place here */
+	HTMLSetCharacterHandling(current_char_set);	/* restore now */
+
+	return;
+    } else if (canreparse_post) {
+	srcmode_for_next_retrieval(0);
+	LYUCPopAssumed();	/* probably a right place here */
+	return;
+    }
+#endif
+
+    if (curdoc.title)
+	StrAllocCopy(newdoc.title, curdoc.title);
+
+    free_address(&curdoc);	/* so it doesn't get pushed */
+    LYforce_no_cache = TRUE;
+}
+
+static void handle_LYK_SWITCH_DTD(void)
+{
+#ifdef USE_SOURCE_CACHE
+    BOOLEAN canreparse = FALSE;
+
+    if (!(canreparse = HTcan_reparse_document())) {
+#endif
+	/*
+	 * Check if this is a reply from a POST, and if so,
+	 * seek confirmation of reload if the safe element
+	 * is not set.  - FM, kw
+	 */
+	if ((curdoc.post_data != NULL &&
+	     curdoc.safe != TRUE) &&
+	    confirm_post_resub(curdoc.address, NULL, 1, 1) == FALSE) {
+	    HTInfoMsg(WILL_NOT_RELOAD_DOC);
+	} else {
+	    /*
+	     * If currently viewing preparsed source, switching to the other
+	     * DTD parsing may show source differences, so stay in source view
+	     * - kw
+	     */
+
+	    /* NOTE: this conditional can be considered incorrect -
+	       current behaviour - when viewing source and
+	       LYPreparsedSource==TRUE, pressing ^V will toggle parser mode
+	       AND switch back from the source view to presentation view.-HV
+	     */
+	    if (HTisDocumentSource() && LYPreparsedSource) {
+		srcmode_for_next_retrieval(1);
+	    }
+	    HText_setNoCache(HTMainText);
+	    move_address(&newdoc, &curdoc);
+	    newdoc.line = curdoc.line;
+	    newdoc.link = curdoc.link;
+	}
+#ifdef USE_SOURCE_CACHE
+    }				/* end if no bypass */
+#endif
+    Old_DTD = !Old_DTD;
+    HTSwitchDTD(!Old_DTD);
+    HTUserMsg(Old_DTD ? USING_DTD_0 : USING_DTD_1);
+#ifdef USE_SOURCE_CACHE
+    if (canreparse) {
+	if (HTisDocumentSource() && LYPreparsedSource) {
+	    srcmode_for_next_retrieval(1);
+	}
+	if (!reparse_document()) {
+	    srcmode_for_next_retrieval(0);
+	}
+    }
+#endif
+    return;
+}
+
+#ifdef DIRED_SUPPORT
+static void handle_LYK_TAG_LINK(void)
+{
+    if (lynx_edit_mode && nlinks > 0 && !no_dired_support) {
+	if (!strcmp(LYGetHiliteStr(curdoc.link, 0), ".."))
+	    return;		/* Never tag the parent directory */
+	if (dir_list_style == MIXED_STYLE) {
+	    if (!strcmp(LYGetHiliteStr(curdoc.link, 0), "../"))
+		return;
+	} else if (!strncmp(LYGetHiliteStr(curdoc.link, 0), "Up to ", 6))
+	    return;
+	{
+	    /*
+	     * HTList-based management of tag list, see LYLocal.c - KW
+	     */
+	    HTList *t1 = tagged;
+	    char *tagname = NULL;
+	    BOOLEAN found = FALSE;
+
+	    while ((tagname = (char *) HTList_nextObject(t1)) != NULL) {
+		if (!strcmp(links[curdoc.link].lname, tagname)) {
+		    found = TRUE;
+		    HTList_removeObject(tagged, tagname);
+		    FREE(tagname);
+		    tagflag(OFF, curdoc.link);
+		    break;
+		}
+	    }
+	    if (!found) {
+		if (tagged == NULL)
+		    tagged = HTList_new();
+		tagname = NULL;
+		StrAllocCopy(tagname, links[curdoc.link].lname);
+		HTList_addObject(tagged, tagname);
+		tagflag(ON, curdoc.link);
+	    }
+	}
+	if (curdoc.link < nlinks - 1) {
+	    set_curdoc_link(curdoc.link + 1);
+	} else if (!more_text && LYGetNewline() == 1 && curdoc.link == nlinks
+		   - 1) {
+	    set_curdoc_link(0);
+	} else if (more_text) {	/* next page */
+	    LYChgNewline(display_lines);
+	}
+    }
+}
+#endif /* DIRED_SUPPORT */
+
+static void handle_LYK_TOGGLE_HELP(void)
+{
+    if (user_mode == NOVICE_MODE) {
+	toggle_novice_line();
+	noviceline(more_text);
+    }
+}
+
+static void handle_LYK_TOOLBAR(BOOLEAN *try_internal,
+			       BOOLEAN *force_load,
+			       int *old_c,
+			       int real_c)
+{
+    char *cp;
+    char *toolbar = NULL;
+
+    if (!HText_hasToolbar(HTMainText)) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(NO_TOOLBAR);
+	}
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	cp = trimPoundSelector(curdoc.address);
+	HTSprintf0(&toolbar, "%s#%s", curdoc.address, LYToolbarName);
+	restorePoundSelector(cp);
+	set_address(&newdoc, toolbar);
+	FREE(toolbar);
+	*try_internal = TRUE;
+	*force_load = TRUE;	/* force MainLoop to reload */
+    }
+}
+
+static void handle_LYK_TRACE_LOG(BOOLEAN *trace_flag_ptr)
+{
+#ifndef NO_LYNX_TRACE
+    /*
+     * Check whether we've started a TRACE log in this session.  - FM
+     */
+    if (LYTraceLogFP == NULL) {
+	HTUserMsg(NO_TRACELOG_STARTED);
+	return;
+    }
+
+    /*
+     * Don't do if already viewing the TRACE log.  - FM
+     */
+    if (LYIsUIPage(curdoc.address, UIP_TRACELOG))
+	return;
+
+    /*
+     * If TRACE mode is on, turn it off during this fetch of the TRACE log, so
+     * we don't enter stuff about this fetch, and set a flag for turning it
+     * back on when we return to this loop.  Note that we'll miss any messages
+     * about memory exhaustion if it should occur.  It seems unlikely that
+     * anything else bad might happen, but if it does, we'll miss messages
+     * about that too.  We also fflush(), close, and open it again, to make
+     * sure all stderr messages thus far will be in the log.  - FM
+     */
+    if (!LYReopenTracelog(trace_flag_ptr))
+	return;
+
+    LYLocalFileToURL(&(newdoc.address), LYTraceLogPath);
+    LYRegisterUIPage(newdoc.address, UIP_TRACELOG);
+    StrAllocCopy(newdoc.title, LYNX_TRACELOG_TITLE);
+    LYFreePostData(&newdoc);
+    FREE(newdoc.bookmark);
+    newdoc.isHEAD = FALSE;
+    newdoc.safe = FALSE;
+    newdoc.internal_link = FALSE;
+    if (LYValidate || check_realm) {
+	LYPermitURL = TRUE;
+    }
+    LYforce_no_cache = TRUE;
+#else
+    HTUserMsg(TRACE_DISABLED);
+#endif /* NO_LYNX_TRACE */
+}
+
+#ifdef DIRED_SUPPORT
+static void handle_LYK_UPLOAD(void)
+{
+    /*
+     * Don't do if already viewing upload options page.
+     */
+    if (LYIsUIPage(curdoc.address, UIP_UPLOAD_OPTIONS))
+	return;
+
+    if (lynx_edit_mode && !no_dired_support) {
+	LYUpload_options(&(newdoc.address), curdoc.address);
+	StrAllocCopy(newdoc.title, UPLOAD_OPTIONS_TITLE);
+	LYFreePostData(&newdoc);
+	FREE(newdoc.bookmark);
+	newdoc.isHEAD = FALSE;
+	newdoc.safe = FALSE;
+	newdoc.internal_link = FALSE;
+	/*
+	 * Uncache the current listing so that it will be updated to included
+	 * the uploaded file if placed in the current directory.  - FM
+	 */
+	DIRED_UNCACHE_1;
+    }
+}
+#endif /* DIRED_SUPPORT */
+
+static void handle_LYK_UP_xxx(int *arrowup,
+			      int *old_c,
+			      int real_c,
+			      int scroll_by)
+{
+    if (LYGetNewline() > 1) {
+	if (LYGetNewline() - scroll_by < 1)
+	    scroll_by = LYGetNewline() - 1;
+	LYChgNewline(-scroll_by);
+	if (nlinks > 0 && curdoc.link > -1) {
+	    if (links[curdoc.link].ly + scroll_by <= display_lines) {
+		newdoc.link = curdoc.link +
+		    HText_LinksInLines(HTMainText,
+				       LYGetNewline(),
+				       scroll_by);
+	    } else {
+		*arrowup = TRUE;
+	    }
+	}
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTInfoMsg(ALREADY_AT_BEGIN);
+    }
+}
+
+static void handle_LYK_UP_HALF(int *arrowup,
+			       int *old_c,
+			       int real_c)
+{
+    handle_LYK_UP_xxx(arrowup, old_c, real_c, display_lines / 2);
+}
+
+static void handle_LYK_UP_LINK(int *follow_col,
+			       int *arrowup,
+			       int *old_c,
+			       int real_c)
+{
+    if (curdoc.link > 0 &&
+	(links[0].ly != links[curdoc.link].ly ||
+	 !HText_LinksInLines(HTMainText, 1, LYGetNewline() - 1))) {
+	/* more links before this on screen, and first of them on
+	   a different line or no previous links before this screen? */
+	int newlink;
+
+	if (*follow_col == -1) {
+	    const char *text = LYGetHiliteStr(curdoc.link, 0);
+
+	    *follow_col = links[curdoc.link].lx;
+
+	    if (text != NULL)
+		*follow_col += (int) strlen(text) / 2;
+	}
+
+	newlink = find_link_near_col(*follow_col, -1);
+	if (newlink > -1) {
+	    set_curdoc_link(newlink);
+	} else if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(NO_LINKS_ABOVE);
+	}
+
+    } else if (curdoc.line > 1 && LYGetNewline() > 1) {		/* previous page */
+	int scrollamount = (LYGetNewline() > display_lines
+			    ? display_lines
+			    : LYGetNewline() - 1);
+
+	LYChgNewline(-scrollamount);
+	if (scrollamount < display_lines &&
+	    nlinks > 0 && curdoc.link > -1 &&
+	    links[0].ly - 1 + scrollamount <= display_lines) {
+	    newdoc.link = HText_LinksInLines(HTMainText,
+					     1,
+					     scrollamount) - 1;
+	} else {
+	    *arrowup = TRUE;
+	}
+
+    } else if (*old_c != real_c) {
+	*old_c = real_c;
+	HTInfoMsg(ALREADY_AT_BEGIN);
+    }
+}
+
+static void handle_LYK_UP_TWO(int *arrowup,
+			      int *old_c,
+			      int real_c)
+{
+    handle_LYK_UP_xxx(arrowup, old_c, real_c, 2);
+}
+
+static void handle_LYK_VIEW_BOOKMARK(BOOLEAN *refresh_screen,
+				     int *old_c,
+				     int real_c)
+{
+    const char *cp;
+
+    if (LYValidate) {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    HTUserMsg(BOOKMARKS_DISABLED);
+	}
+	return;
+    }
+
+    /*
+     * See if a bookmark exists.  If it does replace newdoc.address with its
+     * name.
+     */
+    if ((cp = get_bookmark_filename(&newdoc.address)) != NULL) {
+	if (*cp == '\0' || !strcmp(cp, " ") ||
+	    !strcmp(curdoc.address, newdoc.address)) {
+	    if (LYMultiBookmarks != MBM_OFF)
+		*refresh_screen = TRUE;
+	    return;
+	}
+#ifdef KANJI_CODE_OVERRIDE
+	if (HTCJK == JAPANESE) {
+	    last_kcode = NOKANJI;	/* AUTO */
+	}
+#endif
+	LYforce_no_cache = TRUE;	/*force the document to be reloaded */
+	StrAllocCopy(newdoc.title, BOOKMARK_TITLE);
+	StrAllocCopy(newdoc.bookmark, BookmarkPage);
+	LYFreePostData(&newdoc);
+	newdoc.isHEAD = FALSE;
+	newdoc.safe = FALSE;
+	newdoc.internal_link = FALSE;
+    } else {
+	if (*old_c != real_c) {
+	    *old_c = real_c;
+	    LYMBM_statusline(BOOKMARKS_NOT_OPEN);
+	    LYSleepAlert();
+	    if (LYMultiBookmarks != MBM_OFF) {
+		*refresh_screen = TRUE;
+	    }
+	}
+    }
+}
+
+static BOOLEAN handle_LYK_VLINKS(int *cmd,
+				 BOOLEAN *newdoc_link_is_absolute)
+{
+    int c;
+
+    if (LYIsUIPage(curdoc.address, UIP_VLINKS)) {
+	/*
+	 * Already viewing visited links page, so get out.
+	 */
+	*cmd = LYK_PREV_DOC;
+	return TRUE;
+    }
+
+    /*
+     * Print visited links page to file.
+     */
+    c = LYShowVisitedLinks(&newdoc.address);
+    if (c < 0) {
+	HTUserMsg(VISITED_LINKS_EMPTY);
+	return FALSE;
+    }
+    StrAllocCopy(newdoc.title, VISITED_LINKS_TITLE);
+    LYFreePostData(&newdoc);
+    FREE(newdoc.bookmark);
+    newdoc.isHEAD = FALSE;
+    newdoc.safe = FALSE;
+    newdoc.internal_link = FALSE;
+    if (c > 0) {
+	/* Select a correct link. */
+	*newdoc_link_is_absolute = TRUE;
+	newdoc.link = c - 1;
+    }
+    if (LYValidate || check_realm) {
+	LYPermitURL = TRUE;
+	StrAllocCopy(lynxlinksfile, newdoc.address);
+    }
+    return FALSE;
+}
+
+void handle_LYK_WHEREIS(int cmd,
+			BOOLEAN *refresh_screen)
+{
+    BOOLEAN have_target_onscreen = (BOOLEAN) (*prev_target != '\0' &&
+					      HText_pageHasPrevTarget());
+    BOOL found;
+    int oldcur = curdoc.link;	/* temporarily remember */
+    char *remember_old_target = NULL;
+
+    if (have_target_onscreen)
+	StrAllocCopy(remember_old_target, prev_target);
+    else
+	StrAllocCopy(remember_old_target, "");
+
+    if (cmd == LYK_WHEREIS) {
+	/*
+	 * Reset prev_target to force prompting for a new search string and to
+	 * turn off highlighting if no search string is entered by the user.
+	 */
+	*prev_target = '\0';
+    }
+    found = textsearch(&curdoc, prev_target, sizeof(prev_target) - 1,
+		       (cmd == LYK_WHEREIS)
+		       ? 0
+		       : ((cmd == LYK_NEXT)
+			  ? 1
+			  : -1));
+
+    /*
+     * Force a redraw to ensure highlighting of hits even when found on the
+     * same page, or clearing of highlighting if the default search string was
+     * erased without replacement.  - FM
+     */
+    /*
+     * Well let's try to avoid it at least in a few cases
+     * where it is not needed. - kw
+     */
+    if (www_search_result >= 0 && www_search_result != curdoc.line) {
+	*refresh_screen = TRUE;	/* doesn't really matter */
+    } else if (!found) {
+	*refresh_screen = have_target_onscreen;
+    } else if (!have_target_onscreen && found) {
+	*refresh_screen = TRUE;
+    } else if (www_search_result == curdoc.line &&
+	       curdoc.link == oldcur &&
+	       curdoc.link >= 0 && nlinks > 0 &&
+	       links[curdoc.link].ly >= (display_lines / 3)) {
+	*refresh_screen = TRUE;
+    } else if ((case_sensitive && 0 != strcmp(prev_target,
+					      remember_old_target)) ||
+	       (!case_sensitive && 0 != strcasecomp8(prev_target,
+						     remember_old_target))) {
+	*refresh_screen = TRUE;
+    }
+    FREE(remember_old_target);
+}
+
+/*
+ * Get a number from the user and follow that link number.
+ */
+static void handle_LYK_digit(int c,
+			     BOOLEAN *force_load,
+			     char *user_input_buffer,
+			     int *old_c,
+			     int real_c,
+			     BOOLEAN *try_internal GCC_UNUSED)
+{
+    int lindx = ((nlinks > 0) ? curdoc.link : 0);
+    int number;
+    char *temp = NULL;
+
+    /* pass cur line num for use in follow_link_number()
+     * Note: Current line may not equal links[cur].line
+     */
+    number = curdoc.line;
+    switch (follow_link_number(c, lindx, &newdoc, &number)) {
+    case DO_LINK_STUFF:
+	/*
+	 * Follow a normal link.
+	 */
+	set_address(&newdoc, links[lindx].lname);
+	StrAllocCopy(newdoc.title, LYGetHiliteStr(lindx, 0));
+#ifndef DONT_TRACK_INTERNAL_LINKS
+	/*
+	 * For internal links, retain POST content if present.  If we are on
+	 * the List Page, prevent pushing it on the history stack.  Otherwise
+	 * set try_internal to signal that the top of the loop should attempt
+	 * to reposition directly, without calling getfile.  - kw
+	 */
+	if (links[lindx].type == WWW_INTERN_LINK_TYPE) {
+	    LYinternal_flag = TRUE;
+	    newdoc.internal_link = TRUE;
+	    if (LYIsListpageTitle(NonNull(curdoc.title)) &&
+		(LYIsUIPage(curdoc.address, UIP_LIST_PAGE) ||
+		 LYIsUIPage(curdoc.address, UIP_ADDRLIST_PAGE))) {
+		if (check_history()) {
+		    LYinternal_flag = TRUE;
+		} else {
+		    HTLastConfirmCancelled();	/* reset flag */
+		    if (!confirm_post_resub(newdoc.address,
+					    newdoc.title,
+					    ((LYresubmit_posts &&
+					      HText_POSTReplyLoaded(&newdoc))
+					     ? 1
+					     : 2),
+					    2)) {
+			if (HTLastConfirmCancelled() ||
+			    (LYresubmit_posts &&
+			     !HText_POSTReplyLoaded(&newdoc))) {
+			    /* cancel the whole thing */
+			    LYforce_no_cache = FALSE;
+			    reloading = FALSE;
+			    copy_address(&newdoc, &curdoc);
+			    StrAllocCopy(newdoc.title, curdoc.title);
+			    newdoc.internal_link = curdoc.internal_link;
+			    HTInfoMsg(CANCELLED);
+			    if (nlinks > 0)
+				HText_pageDisplay(curdoc.line, prev_target);
+			    break;
+			} else if (LYresubmit_posts) {
+			    /* If LYresubmit_posts is set, and the
+			       answer was No, and we have a cached
+			       copy, then use it. - kw */
+			    LYforce_no_cache = FALSE;
+			} else {
+			    /* if No, but not ^C or ^G, drop
+			     * the post data.  Maybe the link
+			     * wasn't meant to be internal after
+			     * all, here we can recover from that
+			     * assumption. - kw */
+			    LYFreePostData(&newdoc);
+			    newdoc.internal_link = FALSE;
+			    HTAlert(DISCARDING_POST_DATA);
+			}
+		    }
+		}
+		/*
+		 * Don't push the List Page if we follow an internal link given
+		 * by it.  - kw
+		 */
+		free_address(&curdoc);
+	    } else
+		*try_internal = TRUE;
+	    if (!(LYresubmit_posts && newdoc.post_data))
+		LYinternal_flag = TRUE;
+	    *force_load = TRUE;
+	    break;
+	} else {
+	    /*
+	     * Free POST content if not an internal link.  - kw
+	     */
+	    LYFreePostData(&newdoc);
+	}
+#endif /* DONT_TRACK_INTERNAL_LINKS */
+	/*
+	 * Might be an anchor in the same doc from a POST form.  If so, don't
+	 * free the content.  -- FM
+	 */
+	if (are_different(&curdoc, &newdoc)) {
+	    LYFreePostData(&newdoc);
+	    FREE(newdoc.bookmark);
+	    newdoc.isHEAD = FALSE;
+	    newdoc.safe = FALSE;
+	    if (isLYNXMESSAGES(newdoc.address))
+		LYforce_no_cache = TRUE;
+	}
+	newdoc.internal_link = FALSE;
+	*force_load = TRUE;	/* force MainLoop to reload */
+	break;
+
+    case DO_GOTOLINK_STUFF:
+	/*
+	 * Position on a normal link, don't follow it.  - KW
+	 */
+	LYSetNewline(newdoc.line);
+	newdoc.line = 1;
+	if (LYGetNewline() == curdoc.line) {
+	    /*
+	     * It's a link in the current page.  - FM
+	     */
+	    if (nlinks > 0 && curdoc.link > -1) {
+		if (curdoc.link == newdoc.link) {
+		    /*
+		     * It's the current link, and presumably reflects a typo in
+		     * the statusline entry, so issue a statusline message for
+		     * the typo-prone users (like me 8-).  - FM
+		     */
+		    StrAllocCopy(temp, user_input_buffer);
+		    sprintf(user_input_buffer,
+			    LINK_ALREADY_CURRENT, number);
+		    HTUserMsg(user_input_buffer);
+		    LYstrncpy(user_input_buffer, temp, MAX_LINE - 1);
+		    FREE(temp);
+		} else {
+		    /*
+		     * It's a different link on this page,
+		     */
+		    set_curdoc_link(newdoc.link);
+		    newdoc.link = 0;
+		}
+	    }
+	}
+	break;			/* nothing more to do */
+
+    case DO_GOTOPAGE_STUFF:
+	/*
+	 * Position on a page in this document.  - FM
+	 */
+	LYSetNewline(newdoc.line);
+	newdoc.line = 1;
+	if (LYGetNewline() == curdoc.line) {
+	    /*
+	     * It's the current page, so issue a statusline message for the
+	     * typo-prone users (like me 8-).  - FM
+	     */
+	    if (LYGetNewline() <= 1) {
+		HTInfoMsg(ALREADY_AT_BEGIN);
+	    } else if (!more_text) {
+		HTInfoMsg(ALREADY_AT_END);
+	    } else {
+		StrAllocCopy(temp, user_input_buffer);
+		sprintf(user_input_buffer,
+			ALREADY_AT_PAGE, number);
+		HTUserMsg(user_input_buffer);
+		LYstrncpy(user_input_buffer, temp, MAX_LINE - 1);
+		FREE(temp);
+	    }
+	}
+	break;
+
+    case PRINT_ERROR:
+	*old_c = real_c;
+	HTUserMsg(BAD_LINK_NUM_ENTERED);
+	break;
+    }
+    return;
+}
+
+#ifdef SUPPORT_CHDIR
+
+/* original implementation by VH */
+void handle_LYK_CHDIR(void)
+{
+    static char buf[LY_MAXPATH];
+    char *p = NULL;
+
+    if (no_chdir) {
+	HTUserMsg(CHDIR_DISABLED);
+	return;
+    }
+
+    _statusline(gettext("cd to:"));
+    /* some people may prefer automatic clearing of the previous user input,
+       here, to do this, just uncomment next line - VH */
+    /* buf[0]='\0'; */
+    if (LYgetstr(buf, VISIBLE, sizeof(buf) - 1, NORECALL) < 0 || !*buf) {
+	HTInfoMsg(CANCELLED);
+	return;
+    }
+
+    if (LYIsTilde(*buf) && (LYIsPathSep(buf[1]) || buf[1] == '\0')) {
+	HTSprintf0(&p, "%s%s", Home_Dir(), buf + 1);
+    } else {
+	StrAllocCopy(p, buf);
+    }
+
+    CTRACE((tfp, "changing directory to '%s'\n", p));
+    if (chdir(p)) {
+	switch (errno) {
+	case EACCES:
+	    HTInfoMsg(COULD_NOT_ACCESS_DIR);
+	    break;
+	case ENOENT:
+	    HTInfoMsg(gettext("No such directory"));
+	    break;
+	case ENOTDIR:
+	    HTInfoMsg(gettext("A component of path is not a directory"));
+	    break;
+	default:
+	    HTInfoMsg(gettext("failed to change directory"));
+	    break;
+	}
+    } else {
+#ifdef DIRED_SUPPORT
+	/*if in dired, load content of other directory */
+	if (!no_dired_support
+	    && (lynx_edit_mode || (LYIsUIPage(curdoc.address, UIP_DIRED_MENU)))) {
+	    char buf2[LY_MAXPATH];
+	    char *addr = NULL;
+
+	    Current_Dir(buf2);
+	    LYLocalFileToURL(&addr, buf2);
+
+	    newdoc.address = addr;
+	    newdoc.isHEAD = FALSE;
+	    StrAllocCopy(newdoc.title, gettext("A URL specified by the user"));
+	    LYFreePostData(&newdoc);
+	    FREE(newdoc.bookmark);
+	    newdoc.safe = FALSE;
+	    newdoc.internal_link = FALSE;
+	    /**force_load = TRUE;*/
+	    if (lynx_edit_mode) {
+		DIRED_UNCACHE_2;
+	    }
+	} else
+#endif
+	    HTInfoMsg(OPERATION_DONE);
+    }
+    FREE(p);
+}
+#endif
+
+#ifdef USE_CURSES_PADS
+/*
+ * Having jumps larger than this is counter-productive.  Indeed, it is natural
+ * to expect that when the relevant text appears, one would "overshoot" and
+ * would scroll 3-4 extra full screens.  When going back, the "accumulation"
+ * logic would again start moving in full screens, so one would overshoot
+ * again, etc.
+ *
+ * Going back, one can fix it in 28 keypresses. The relevant text will appear
+ * on the screen soon enough for the key-repeat to become not that important,
+ * and we are still moving in smaller steps than when we overshot.  Since key
+ * repeat is not important, even if we overshoot again, it is going to be by 30
+ * steps, which is easy to fix by reversing the direction again.
+ */
+static int repeat_to_delta(int n)
+{
+    int threshold = LYcols / 3;
+
+    while (threshold > 0) {
+	if (n >= threshold) {
+	    n = threshold;
+	    break;
+	}
+	threshold = (threshold * 2) / 3;
+    }
+    return n;
+}
+
+static void handle_LYK_SHIFT_LEFT(BOOLEAN *flag, int count)
+{
+    if (!LYwideLines) {
+	HTAlert(SHIFT_VS_LINEWRAP);
+	return;
+    }
+    if (LYshiftWin > 0) {
+	LYshiftWin -= repeat_to_delta(count);
+	*flag = TRUE;
+    }
+    if (LYshiftWin < 0)
+	LYshiftWin = 0;
+}
+
+static void handle_LYK_SHIFT_RIGHT(BOOLEAN *flag, int count)
+{
+    if (!LYwideLines) {
+	HTAlert(SHIFT_VS_LINEWRAP);
+	return;
+    }
+    LYshiftWin += repeat_to_delta(count);
+    *flag = TRUE;
+}
+
+static BOOLEAN handle_LYK_LINEWRAP_TOGGLE(int *cmd,
+					  BOOLEAN *flag)
+{
+    static const char *choices[] =
+    {
+	"Try to fit screen width",
+	"No line wrap in columns",
+	"Wrap columns at screen width",
+	"Wrap columns at 3/4 screen width",
+	"Wrap columns at 2/3 screen width",
+	"Wrap columns at 1/2 screen width",
+	"Wrap columns at 1/3 screen width",
+	"Wrap columns at 1/4 screen width",
+	NULL
+    };
+    static int wrap[] =
+    {
+	0,
+	0,
+	12,			/* In units of 1/12 */
+	9,
+	8,
+	6,
+	4,
+	3
+    };
+    int c;
+
+    if (LYwin == stdscr)
+	return FALSE;
+
+    /* Somehow the mouse is over the number instead of being over the
+       name, so we decrease x. */
+    c = LYChoosePopup(!LYwideLines, LYlines / 2 - 2, LYcolLimit / 2 - 6,
+		      choices, TABLESIZE(choices) - 1, FALSE, TRUE);
+    /*
+     * LYhandlePopupList() wasn't really meant to be used outside of old-style
+     * Options menu processing.  One result of mis-using it here is that we
+     * have to deal with side-effects regarding SIGINT signal handler and the
+     * term_options global variable.  - kw
+     */
+    if (term_options)
+	return FALSE;
+    LYwideLines = c;
+    LYtableCols = wrap[c];
+
+    if (LYwideLines == 0)
+	LYshiftWin = 0;
+    *flag = TRUE;
+    HTUserMsg(LYwideLines ? LINEWRAP_OFF : LINEWRAP_ON);
+    return reparse_or_reload(cmd);
+}
+#endif
+
+/*
+ * Here's where we do all the work.
+ * mainloop is basically just a big switch dependent on the users input.  I
+ * have tried to offload most of the work done here to procedures to make it
+ * more modular, but this procedure still does a lot of variable manipulation. 
+ * This needs some work to make it neater.  - Lou Moutilli
+ *					(memoir from the original Lynx - FM)
+ */
+
+int mainloop(void)
+{
+#if defined(WIN_EX)		/* 1997/10/08 (Wed) 14:52:06 */
+    char sjis_buff[MAX_LINE];
+    char temp_buff[sizeof(sjis_buff) * 4];
+#endif
+    int c = 0;
+    int real_c = 0;
+    int old_c = 0;
+    int pending_form_c = -1;
+    int cmd = LYK_DO_NOTHING, real_cmd = LYK_DO_NOTHING;
+    int getresult;
+    int arrowup = FALSE, show_help = FALSE;
+    char user_input_buffer[MAX_LINE];
+    const char *cshelpfile = NULL;
+    BOOLEAN first_file = TRUE;
+    BOOLEAN popped_doc = FALSE;
+    BOOLEAN refresh_screen = FALSE;
+    BOOLEAN force_load = FALSE;
+    BOOLEAN try_internal = FALSE;
+    BOOLEAN crawl_ok = FALSE;
+    BOOLEAN vi_keys_flag = vi_keys;
+    BOOLEAN emacs_keys_flag = emacs_keys;
+    BOOLEAN trace_mode_flag = FALSE;
+    BOOLEAN forced_HTML_mode = LYforce_HTML_mode;
+    char cfile[128];
+    FILE *cfp;
+    char *cp;
+    int ch = 0;
+    RecallType recall = NORECALL;
+    int URLTotal = 0;
+    int URLNum;
+    BOOLEAN FirstURLRecall = TRUE;
+    char *temp = NULL;
+    BOOLEAN ForcePush = FALSE;
+    BOOLEAN override_LYresubmit_posts = FALSE;
+    BOOLEAN newdoc_link_is_absolute = FALSE;
+    BOOLEAN curlink_is_editable;
+    BOOLEAN use_last_tfpos;
+    unsigned int len;
+    int i;
+    int follow_col = -1, key_count = 0, last_key = 0;
+    int tmpNewline;
+    DocInfo tmpDocInfo;
+
+    /* "internal" means "within the same document, with certainty".  It includes a
+     * space so it cannot conflict with any (valid) "TYPE" attributes on A
+     * elements.  [According to which DTD, anyway??] - kw
+     */
+    HTInternalLink = HTAtom_for("internal link");	/* init, used as const */
+
+#ifndef WWW_SOURCE
+    WWW_SOURCE = HTAtom_for("www/source");	/* init, used as const */
+#endif
+
+    /*
+     * curdoc.address contains the name of the file that is currently open.
+     * newdoc.address contains the name of the file that will soon be
+     *                opened if it exits.
+     * prev_target    contains the last search string the user searched for.
+     * newdoc.title   contains the link name that the user last chose to get
+     *                into the current link (file).
+     */
+    /* initialize some variables */
+    newdoc.address = NULL;
+    newdoc.title = NULL;
+    newdoc.post_data = NULL;
+    newdoc.post_content_type = NULL;
+    newdoc.bookmark = NULL;
+    newdoc.internal_link = FALSE;
+    curdoc.address = NULL;
+    curdoc.title = NULL;
+    curdoc.post_data = NULL;
+    curdoc.post_content_type = NULL;
+    curdoc.bookmark = NULL;
+    curdoc.internal_link = FALSE;
+#ifdef USE_COLOR_STYLE
+    curdoc.style = NULL;
+    newdoc.style = NULL;
+#endif
+#ifndef USE_SESSIONS
+    nhist = 0;
+#endif
+    user_input_buffer[(sizeof(user_input_buffer) - 1)] = '\0';
+    *prev_target = '\0';
+    *user_input_buffer = '\0';
+#ifdef LY_FIND_LEAKS
+    atexit(free_mainloop_variables);
+#endif
+  initialize:
+    set_address(&newdoc, startfile);
+    StrAllocCopy(startrealm, startfile);
+    StrAllocCopy(newdoc.title, gettext("Entry into main screen"));
+    newdoc.isHEAD = FALSE;
+    newdoc.safe = FALSE;
+    newdoc.line = 1;
+    newdoc.link = 0;
+
+#ifdef USE_SLANG
+    if (TRACE && LYCursesON) {
+	LYaddstr("\n");
+	LYrefresh();
+    }
+#endif /* USE_SLANG */
+    CTRACE((tfp, "Entering mainloop, startfile=%s\n", startfile));
+
+    if (form_post_data) {
+	BStrCopy0(newdoc.post_data, form_post_data);
+	StrAllocCopy(newdoc.post_content_type,
+		     "application/x-www-form-urlencoded");
+    } else if (form_get_data) {
+	StrAllocCat(newdoc.address, form_get_data);
+    }
+
+    if (bookmark_start) {
+	if (LYValidate) {
+	    HTAlert(BOOKMARKS_DISABLED);
+	    bookmark_start = FALSE;
+	    goto initialize;
+	} else if (traversal) {
+	    HTAlert(BOOKMARKS_NOT_TRAVERSED);
+	    traversal = FALSE;
+	    crawl = FALSE;
+	    bookmark_start = FALSE;
+	    goto initialize;
+	} else {
+	    const char *cp1;
+
+	    /*
+	     * See if a bookmark page exists.  If it does, replace
+	     * newdoc.address with its name
+	     */
+	    if ((cp1 = get_bookmark_filename(&newdoc.address)) != NULL &&
+		*cp1 != '\0' && strcmp(cp1, " ")) {
+		StrAllocCopy(newdoc.title, BOOKMARK_TITLE);
+		StrAllocCopy(newdoc.bookmark, BookmarkPage);
+		StrAllocCopy(startrealm, newdoc.address);
+		LYFreePostData(&newdoc);
+		newdoc.isHEAD = FALSE;
+		newdoc.safe = FALSE;
+		CTRACE((tfp, "Using bookmarks=%s\n", newdoc.address));
+	    } else {
+		HTUserMsg(BOOKMARKS_NOT_OPEN);
+		bookmark_start = FALSE;
+		goto initialize;
+	    }
+	}
+    }
+
+    FREE(form_post_data);
+    FREE(form_get_data);
+
+    LYSetDisplayLines();
+
+    while (TRUE) {
+#ifdef USE_COLOR_STYLE
+	if (curdoc.style != NULL)
+	    force_load = TRUE;
+#endif
+	/*
+	 * If newdoc.address is different from curdoc.address then we need to
+	 * go out and find and load newdoc.address.
+	 */
+	if (LYforce_no_cache || force_load ||
+	    are_different(&curdoc, &newdoc)) {
+
+	    force_load = FALSE;	/* done */
+	    if (TRACE && LYCursesON) {
+		LYHideCursor();	/* make sure cursor is down */
+#ifdef USE_SLANG
+		LYaddstr("\n");
+#endif /* USE_SLANG */
+		LYrefresh();
+	    }
+	  try_again:
+	    /*
+	     * Push the old file onto the history stack if we have a current
+	     * doc and a new address.  - FM
+	     */
+	    if (curdoc.address && newdoc.address) {
+		/*
+		 * Don't actually push if this is a LYNXDOWNLOAD URL, because
+		 * that returns NORMAL even if it fails due to a spoof attempt
+		 * or file access problem, and we set the newdoc structure
+		 * elements to the curdoc structure elements under case NORMAL. 
+		 * - FM
+		 */
+		if (!isLYNXDOWNLOAD(newdoc.address)) {
+		    LYpush(&curdoc, ForcePush);
+		}
+	    } else if (!newdoc.address) {
+		/*
+		 * If newdoc.address is empty then pop a file and load it.  -
+		 * FM
+		 */
+		LYhist_prev(&newdoc);
+		popped_doc = TRUE;
+
+#ifndef DONT_TRACK_INTERNAL_LINKS
+		/*
+		 * If curdoc had been reached via an internal
+		 * (fragment) link from what we now have just
+		 * popped into newdoc, then override non-caching in
+		 * all cases. - kw
+		 */
+		if (curdoc.internal_link &&
+		    !are_phys_different(&curdoc, &newdoc)) {
+		    LYinternal_flag = TRUE;
+		    LYoverride_no_cache = TRUE;
+		    LYforce_no_cache = FALSE;
+		    try_internal = TRUE;
+		} else
+#endif /* TRACK_INTERNAL_LINKS */
+		{
+		    /*
+		     * Force a no_cache override unless it's a bookmark file,
+		     * or it has POST content and LYresubmit_posts is set
+		     * without safe also set, and we are not going to another
+		     * position in the current document or restoring the
+		     * previous document due to a NOT_FOUND or NULLFILE return
+		     * value from getfile().  - FM
+		     */
+		    if ((newdoc.bookmark != NULL) ||
+			(newdoc.post_data != NULL &&
+			 !newdoc.safe &&
+			 LYresubmit_posts &&
+			 !override_LYresubmit_posts &&
+			 NO_INTERNAL_OR_DIFFERENT(&curdoc, &newdoc))) {
+			LYoverride_no_cache = FALSE;
+		    } else {
+			LYoverride_no_cache = TRUE;
+		    }
+		}
+	    }
+	    override_LYresubmit_posts = FALSE;
+
+	    if (HEAD_request) {
+		/*
+		 * Make SURE this is an appropriate request.  - FM
+		 */
+		if (newdoc.address) {
+		    if (LYCanDoHEAD(newdoc.address) == TRUE) {
+			newdoc.isHEAD = TRUE;
+		    } else if (isLYNXIMGMAP(newdoc.address)) {
+			if (LYCanDoHEAD(newdoc.address + LEN_LYNXIMGMAP) == TRUE) {
+			    StrAllocCopy(temp, newdoc.address + LEN_LYNXIMGMAP);
+			    free_address(&newdoc);
+			    newdoc.address = temp;
+			    newdoc.isHEAD = TRUE;
+			    temp = NULL;
+			}
+		    }
+		}
+		try_internal = FALSE;
+		HEAD_request = FALSE;
+	    }
+
+	    /*
+	     * If we're getting the TRACE log and it's not new, check whether
+	     * its HText structure has been dumped, and if so, fflush() and
+	     * fclose() it to ensure it's fully updated, and then fopen() it
+	     * again.  - FM
+	     */
+	    if (LYUseTraceLog == TRUE &&
+		trace_mode_flag == FALSE &&
+		LYTraceLogFP != NULL &&
+		LYIsUIPage(newdoc.address, UIP_TRACELOG)) {
+		DocAddress WWWDoc;
+		HTParentAnchor *tmpanchor;
+
+		WWWDoc.address = newdoc.address;
+		WWWDoc.post_data = newdoc.post_data;
+		WWWDoc.post_content_type = newdoc.post_content_type;
+		WWWDoc.bookmark = newdoc.bookmark;
+		WWWDoc.isHEAD = newdoc.isHEAD;
+		WWWDoc.safe = newdoc.safe;
+		tmpanchor = HTAnchor_findAddress(&WWWDoc);
+		if ((HText *) HTAnchor_document(tmpanchor) == NULL) {
+		    if (!LYReopenTracelog(&trace_mode_flag)) {
+			old_c = 0;
+			cmd = LYK_PREV_DOC;
+			goto new_cmd;
+		    }
+		}
+	    }
+
+	    LYRequestTitle = newdoc.title;
+	    if (newdoc.bookmark)
+		LYforce_HTML_mode = TRUE;
+	    if (LYValidate &&
+		startfile_ok &&
+		newdoc.address && startfile && homepage &&
+		(!strcmp(newdoc.address, startfile) ||
+		 !strcmp(newdoc.address, homepage))) {
+		LYPermitURL = TRUE;
+	    }
+
+	    /* reset these two variables here before getfile()
+	     * so they will be available in partial mode
+	     * (was previously implemented in case NORMAL).
+	     */
+	    *prev_target = '\0';	/* Reset for new coming document */
+	    LYSetNewline(newdoc.line);	/* set for LYGetNewline() */
+
+#ifdef USE_PRETTYSRC
+	    psrc_first_tag = TRUE;
+#endif
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+	    textfields_need_activation = textfields_activation_option;
+#endif
+	    FREE(LYRequestReferer);
+	    /*
+	     * Don't send Referer if we have to load a document again that we
+	     * got from the history stack.  We don't know any more how we
+	     * originally got to that page.  Using a Referer based on the
+	     * current HTMainText could only be right by coincidence.  - kw
+	     * 1999-11-01
+	     */
+	    if (popped_doc)
+		LYNoRefererForThis = TRUE;
+
+#ifndef DONT_TRACK_INTERNAL_LINKS
+	    if (try_internal) {
+		if (newdoc.address &&
+		    isLYNXIMGMAP(newdoc.address)) {
+		    try_internal = FALSE;
+		} else if (curdoc.address &&
+			   isLYNXIMGMAP(curdoc.address)) {
+		    try_internal = FALSE;
+		}
+	    }
+	    if (try_internal) {
+		char *hashp = findPoundSelector(newdoc.address);
+
+		if (hashp) {
+		    HTFindPoundSelector(hashp + 1);
+		}
+		getresult = (HTMainText != NULL) ? NORMAL : NOT_FOUND;
+		try_internal = FALSE;	/* done */
+		/* fix up newdoc.address which may have been fragment-only */
+		if (getresult == NORMAL && (!hashp || hashp == newdoc.address)) {
+		    if (!hashp) {
+			set_address(&newdoc, HTLoadedDocumentURL());
+		    } else {
+			StrAllocCopy(temp, HTLoadedDocumentURL());
+			StrAllocCat(temp, hashp);	/* append fragment */
+			set_address(&newdoc, temp);
+			FREE(temp);
+		    }
+		}
+	    } else {
+		if (newdoc.internal_link && newdoc.address &&
+		    *newdoc.address == '#' && nhist > 0) {
+		    char *cp0;
+
+		    if (isLYNXIMGMAP(HDOC(nhist_1).address))
+			cp0 = HDOC(nhist_1).address + LEN_LYNXIMGMAP;
+		    else
+			cp0 = HDOC(nhist_1).address;
+		    StrAllocCopy(temp, cp0);
+		    (void) trimPoundSelector(temp);
+		    StrAllocCat(temp, newdoc.address);
+		    free_address(&newdoc);
+		    newdoc.address = temp;
+		    temp = NULL;
+		}
+		tmpDocInfo = newdoc;
+		tmpNewline = -1;
+		getresult = getfile(&newdoc, &tmpNewline);
+		if (!reloading && !popped_doc && (tmpNewline >= 0)) {
+		    LYSetNewline(tmpNewline);
+		} else {
+		    newdoc.link = tmpDocInfo.link;
+		}
+	    }
+#else /* TRACK_INTERNAL_LINKS */
+	    tmpDocInfo = newdoc;
+	    tmpNewline = -1;
+	    getresult = getfile(&newdoc, &tmpNewline);
+	    if (!reloading && !popped_doc && (tmpNewline >= 0)) {
+		LYSetNewline(tmpNewline);
+	    } else {
+		newdoc.link = tmpDocInfo.link;
+	    }
+#endif /* TRACK_INTERNAL_LINKS */
+
+#ifdef INACTIVE_INPUT_STYLE_VH
+	    textinput_redrawn = FALSE;	/* for sure */
+#endif
+
+	    switch (getresult) {
+
+	    case NOT_FOUND:
+		/*
+		 * OK!  can't find the file, so it must not be around now.  Do
+		 * any error logging, if appropriate.
+		 */
+		LYoverride_no_cache = FALSE;	/* Was TRUE if popped. - FM */
+		LYinternal_flag = FALSE;	/* Reset to default. - kw */
+		turn_trace_back_on(&trace_mode_flag);
+		if (!first_file && !LYCancelledFetch) {
+		    /*
+		     * Do error mail sending and/or traversal stuff.  Note that
+		     * the links[] elements may not be valid at this point, if
+		     * we did call HTuncache_current_document!  This should not
+		     * have happened for traversal, but for sending error mail
+		     * check that HTMainText exists for this reason.  - kw
+		     */
+		    if (error_logging && nhist > 0 && !popped_doc &&
+			!LYUserSpecifiedURL &&
+			HTMainText &&
+			nlinks > 0 && curdoc.link < nlinks &&
+			!isLYNXHIST(NonNull(newdoc.address)) &&
+#ifdef USE_CACHEJAR
+			!isLYNXCACHE(NonNull(newdoc.address)) &&
+#endif
+			!isLYNXCOOKIE(NonNull(newdoc.address))) {
+			char *mail_owner = NULL;
+
+			if (owner_address && isMAILTO_URL(owner_address)) {
+			    mail_owner = owner_address + LEN_MAILTO_URL;
+			}
+			/*
+			 * Email a bad link message to the owner of the
+			 * document, or to ALERTMAIL if defined, but NOT to
+			 * lynx-dev (it is rejected in mailmsg).  - FM, kw
+			 */
+#ifndef ALERTMAIL
+			if (mail_owner)
+#endif
+			    mailmsg(curdoc.link,
+				    mail_owner,
+				    HDOC(nhist_1).address,
+				    HDOC(nhist_1).title);
+		    }
+		    if (traversal) {
+			FILE *ofp;
+
+			if ((ofp = LYAppendToTxtFile(TRAVERSE_ERRORS)) == NULL) {
+			    if ((ofp = LYNewTxtFile(TRAVERSE_ERRORS)) == NULL) {
+				perror(NOOPEN_TRAV_ERR_FILE);
+				exit_immediately(EXIT_FAILURE);
+			    }
+			}
+			if (nhist > 0) {
+			    fprintf(ofp,
+				    "%s %s\tin %s\n",
+				    popped_doc ?
+				    newdoc.address : links[curdoc.link].lname,
+				    links[curdoc.link].target,
+				    HDOC(nhist_1).address);
+			} else {
+			    fprintf(ofp,
+				    "%s %s\t\n",
+				    popped_doc ?
+				    newdoc.address : links[curdoc.link].lname,
+				    links[curdoc.link].target);
+			}
+			LYCloseOutput(ofp);
+		    }
+		}
+
+		/*
+		 * Fall through to do the NULL stuff and reload the old file,
+		 * unless the first file wasn't found or has gone missing.
+		 */
+		if (!nhist) {
+		    /*
+		     * If nhist = 0 then it must be the first file.
+		     */
+		    exit_immediately_with_error_message(NOT_FOUND, first_file);
+		    return (EXIT_FAILURE);
+		}
+		/* FALLTHRU */
+
+	    case NULLFILE:
+		/*
+		 * Not supposed to return any file.
+		 */
+		LYoverride_no_cache = FALSE;	/* Was TRUE if popped. - FM */
+		popped_doc = FALSE;	/* Was TRUE if popped. - FM */
+		LYinternal_flag = FALSE;	/* Reset to default. - kw */
+		turn_trace_back_on(&trace_mode_flag);
+		free_address(&newdoc);	/* to pop last doc */
+		FREE(newdoc.bookmark);
+		LYJumpFileURL = FALSE;
+		reloading = FALSE;
+		LYPermitURL = FALSE;
+		LYCancelledFetch = FALSE;
+		ForcePush = FALSE;
+		LYforce_HTML_mode = FALSE;
+		force_old_UCLYhndl_on_reload = FALSE;
+		if (traversal) {
+		    crawl_ok = FALSE;
+		    if (traversal_link_to_add) {
+			/*
+			 * It's a binary file, or the fetch attempt failed. 
+			 * Add it to TRAVERSE_REJECT_FILE so we don't try again
+			 * in this run.
+			 */
+			if (!lookup_reject(traversal_link_to_add)) {
+			    add_to_reject_list(traversal_link_to_add);
+			}
+			FREE(traversal_link_to_add);
+		    }
+		}
+		/*
+		 * Make sure the first file was found and has not gone missing.
+		 */
+		if (!nhist) {
+		    /*
+		     * If nhist = 0 then it must be the first file.
+		     */
+		    if (first_file && homepage &&
+			!LYSameFilename(homepage, startfile)) {
+			/*
+			 * Couldn't return to the first file but there is a
+			 * homepage we can use instead.  Useful for when the
+			 * first URL causes a program to be invoked.  - GL
+			 *
+			 * But first make sure homepage is different from
+			 * startfile (above), then make it the same (below) so
+			 * we don't enter an infinite getfile() loop on on
+			 * failures to find the files.  - FM
+			 */
+			set_address(&newdoc, homepage);
+			LYFreePostData(&newdoc);
+			FREE(newdoc.bookmark);
+			StrAllocCopy(startfile, homepage);
+			newdoc.isHEAD = FALSE;
+			newdoc.safe = FALSE;
+			newdoc.internal_link = FALSE;
+			goto try_again;
+		    } else {
+			exit_immediately_with_error_message(NULLFILE, first_file);
+			return (EXIT_FAILURE);
+		    }
+		}
+
+		/*
+		 * If we're going to pop from history because getfile didn't
+		 * succeed, reset LYforce_no_cache first.  This would have been
+		 * done in HTAccess.c if the request got that far, but the URL
+		 * may have been handled or rejected in getfile without taking
+		 * care of that.  - kw
+		 */
+		LYforce_no_cache = FALSE;
+		/*
+		 * Retrieval of a newdoc just failed, and just going to
+		 * try_again would pop the next doc from history and try to get
+		 * it without further questions.  This may not be the right
+		 * thing to do if we have POST data, so fake a PREV_DOC key if
+		 * it seems that some prompting should be done.  This doesn't
+		 * affect the traversal logic, since with traversal POST data
+		 * can never occur.  - kw
+		 */
+		if (HDOC(nhist - 1).post_data &&
+		    !HDOC(nhist - 1).safe) {
+		    if (HText_POSTReplyLoaded((DocInfo *) &history[(nhist_1)])) {
+			override_LYresubmit_posts = TRUE;
+			goto try_again;
+		    }
+		    /* Set newdoc fields, just in case the PREV_DOC gets
+		     * cancelled.  - kw
+		     */
+		    if (!curdoc.address) {
+			set_address(&newdoc, HTLoadedDocumentURL());
+			StrAllocCopy(newdoc.title, HTLoadedDocumentTitle());
+			if (HTMainAnchor
+			    && HTMainAnchor->post_data) {
+			    BStrCopy(newdoc.post_data,
+				     HTMainAnchor->post_data);
+			    StrAllocCopy(newdoc.post_content_type,
+					 HTMainAnchor->post_content_type);
+			} else {
+			    BStrFree(newdoc.post_data);
+			}
+			newdoc.isHEAD = HTLoadedDocumentIsHEAD();
+			newdoc.safe = HTLoadedDocumentIsSafe();
+			newdoc.internal_link = FALSE;
+		    } else {
+			copy_address(&newdoc, &curdoc);
+			StrAllocCopy(newdoc.title, curdoc.title);
+			BStrCopy(newdoc.post_data, curdoc.post_data);
+			StrAllocCopy(newdoc.post_content_type,
+				     curdoc.post_content_type);
+			newdoc.isHEAD = curdoc.isHEAD;
+			newdoc.safe = curdoc.safe;
+			newdoc.internal_link = curdoc.internal_link;
+			newdoc.line = curdoc.line;
+			newdoc.link = curdoc.link;
+		    }
+		    cmd = LYK_PREV_DOC;
+		    goto new_cmd;
+		}
+		override_LYresubmit_posts = TRUE;
+		goto try_again;
+
+	    case NORMAL:
+		/*
+		 * Marvelously, we got the document!
+		 */
+		LYoverride_no_cache = FALSE;	/* Was TRUE if popped. - FM */
+		LYinternal_flag = FALSE;	/* Reset to default. - kw */
+		turn_trace_back_on(&trace_mode_flag);
+
+		/*
+		 * If it's the first file and we're interactive, check whether
+		 * it's a bookmark file which was not accessed via the -book
+		 * switch.  - FM
+		 */
+		if (((first_file == TRUE) &&
+		     (dump_output_immediately == FALSE) &&
+		     isEmpty(newdoc.bookmark)) &&
+		    ((LYisLocalFile(newdoc.address) == TRUE) &&
+		     !(strcmp(NonNull(HText_getTitle()),
+			      BOOKMARK_TITLE))) &&
+		    (temp = HTParse(newdoc.address, "",
+				    PARSE_PATH + PARSE_PUNCTUATION)) != NULL) {
+		    const char *name = wwwName(Home_Dir());
+
+		    len = strlen(name);
+#ifdef VMS
+		    if (!strncasecomp(temp, name, len) &&
+			strlen(temp) > len)
+#else
+		    if (!strncmp(temp, name, len) &&
+			strlen(temp) > len)
+#endif /* VMS */
+		    {
+			/*
+			 * We're interactive and this might be a bookmark file
+			 * entered as a startfile rather than invoked via
+			 * -book.  Check if it's in our bookmark file list, and
+			 * if so, reload if with the relevant bookmark elements
+			 * set.  - FM
+			 */
+			cp = NULL;
+			if (temp[len] == '/') {
+			    if (strchr(&temp[(len + 1)], '/')) {
+				HTSprintf0(&cp, ".%s", &temp[len]);
+			    } else {
+				StrAllocCopy(cp, &temp[(len + 1)]);
+			    }
+			} else {
+			    StrAllocCopy(cp, &temp[len]);
+			}
+			for (i = 0; i <= MBM_V_MAXFILES; i++) {
+			    if (MBM_A_subbookmark[i] &&
+				LYSameFilename(cp, MBM_A_subbookmark[i])) {
+				StrAllocCopy(BookmarkPage,
+					     MBM_A_subbookmark[i]);
+				break;
+			    }
+			}
+			FREE(cp);
+			if (i <= MBM_V_MAXFILES) {
+			    FREE(temp);
+			    if (LYValidate) {
+				HTAlert(BOOKMARKS_DISABLED);
+				return (EXIT_FAILURE);
+			    }
+			    if ((temp = HTParse(newdoc.address, "",
+						PARSE_ACCESS + PARSE_HOST + PARSE_PUNCTUATION))) {
+				set_address(&newdoc, temp);
+				HTuncache_current_document();
+				free_address(&curdoc);
+				StrAllocCat(newdoc.address,
+					    wwwName(Home_Dir()));
+				StrAllocCat(newdoc.address, "/");
+				StrAllocCat(newdoc.address,
+					    (strncmp(BookmarkPage, "./", 2) ?
+					     BookmarkPage :
+					     (BookmarkPage + 2)));
+				StrAllocCopy(newdoc.title, BOOKMARK_TITLE);
+				StrAllocCopy(newdoc.bookmark, BookmarkPage);
+#ifdef USE_COLOR_STYLE
+				if (curdoc.style)
+				    StrAllocCopy(newdoc.style, curdoc.style);
+#endif
+				StrAllocCopy(startrealm, newdoc.address);
+				LYFreePostData(&newdoc);
+				newdoc.isHEAD = FALSE;
+				newdoc.safe = FALSE;
+				FREE(temp);
+				if (!strcmp(homepage, startfile))
+				    StrAllocCopy(homepage, newdoc.address);
+				StrAllocCopy(startfile, newdoc.address);
+				CTRACE((tfp, "Reloading as bookmarks=%s\n",
+					newdoc.address));
+				goto try_again;
+			    }
+			}
+		    }
+		    cp = NULL;
+		}
+		FREE(temp);
+
+		if (traversal) {
+		    /*
+		     * During traversal build up lists of all links traversed. 
+		     * Traversal mode is a special feature for traversing http
+		     * links in the web.
+		     */
+		    if (traversal_link_to_add) {
+			/*
+			 * Add the address we sought to TRAVERSE_FILE.
+			 */
+			if (!lookup_link(traversal_link_to_add))
+			    add_to_table(traversal_link_to_add);
+			FREE(traversal_link_to_add);
+		    }
+		    if (curdoc.address && curdoc.title &&
+			!isLYNXIMGMAP(curdoc.address))
+			/*
+			 * Add the address we got to TRAVERSE_FOUND_FILE.
+			 */
+			add_to_traverse_list(curdoc.address, curdoc.title);
+		}
+
+		/*
+		 * If this was a LYNXDOWNLOAD, we still have curdoc, not a
+		 * newdoc, so reset the address, title and positioning
+		 * elements.  - FM
+		 */
+		if (newdoc.address && curdoc.address &&
+		    isLYNXDOWNLOAD(newdoc.address)) {
+		    copy_address(&newdoc, &curdoc);
+		    StrAllocCopy(newdoc.title, NonNull(curdoc.title));
+		    StrAllocCopy(newdoc.bookmark, curdoc.bookmark);
+		    newdoc.line = curdoc.line;
+		    newdoc.link = curdoc.link;
+		    newdoc.internal_link = FALSE;	/* can't be true. - kw */
+		}
+
+		/*
+		 * Set Newline to the saved line.  It contains the line the
+		 * user was on if s/he has been in the file before, or it is 1
+		 * if this is a new file.
+		 *
+		 * We already set Newline before getfile() and probably update
+		 * it explicitly if popping from the history stack via LYpop()
+		 * or LYpop_num() within getfile() cycle.
+		 *
+		 * In partial mode, Newline was probably updated in
+		 * LYMainLoop_pageDisplay() if user scrolled the document while
+		 * loading.  Incremental loading stage already closed in
+		 * HT*Copy().
+		 */
+#ifdef DISP_PARTIAL
+		/* Newline = newdoc.line; */
+		display_partial = FALSE;	/* for sure, LYNXfoo:/ may be a problem */
+#else
+		/* Should not be needed either if we remove "DISP_PARTIAL" from
+		 * LYHistory.c, but lets leave it as an important comment for
+		 * now.
+		 */
+		/* Newline = newdoc.line; */
+#endif
+
+		/*
+		 * If we are going to a target line or the first page of a
+		 * popped document, override any www_search line result.
+		 */
+		if (LYGetNewline() > 1 || popped_doc == TRUE)
+		    www_search_result = -1;
+
+		/*
+		 * Make sure curdoc.line will not be equal to Newline, so we
+		 * get a redraw.
+		 */
+		curdoc.line = -1;
+		break;
+	    }			/* end switch */
+
+	    if (TRACE) {
+		if (!LYTraceLogFP || trace_mode_flag) {
+		    LYSleepAlert();	/* allow me to look at the results */
+		}
+	    }
+
+	    /*
+	     * Set the files the same.
+	     */
+	    copy_address(&curdoc, &newdoc);
+	    BStrCopy(curdoc.post_data, newdoc.post_data);
+	    StrAllocCopy(curdoc.post_content_type, newdoc.post_content_type);
+	    StrAllocCopy(curdoc.bookmark, newdoc.bookmark);
+#ifdef USE_COLOR_STYLE
+	    StrAllocCopy(curdoc.style, HText_getStyle());
+	    if (curdoc.style != NULL)
+		style_readFromFile(curdoc.style);
+#endif
+	    curdoc.isHEAD = newdoc.isHEAD;
+	    curdoc.internal_link = newdoc.internal_link;
+
+	    /*
+	     * Set the remaining document elements and add to the visited links
+	     * list.  - FM
+	     */
+	    if (ownerS_address != NULL) {
+#ifndef USE_PRETTYSRC
+		if (HTOutputFormat == WWW_SOURCE && !HText_getOwner())
+#else
+		if ((LYpsrc ? psrc_view : HTOutputFormat == WWW_SOURCE)
+		    && !HText_getOwner())
+#endif
+		    HText_setMainTextOwner(ownerS_address);
+		FREE(ownerS_address);
+	    }
+	    if (HText_getTitle()) {
+		StrAllocCopy(curdoc.title, HText_getTitle());
+	    } else if (!dump_output_immediately) {
+		StrAllocCopy(curdoc.title, newdoc.title);
+	    }
+	    StrAllocCopy(owner_address, HText_getOwner());
+	    curdoc.safe = HTLoadedDocumentIsSafe();
+	    if (!dump_output_immediately) {
+		LYAddVisitedLink(&curdoc);
+	    }
+
+	    /*
+	     * Reset WWW present mode so that if we were getting the source, we
+	     * get rendered HTML from now on.
+	     */
+	    HTOutputFormat = WWW_PRESENT;
+#ifdef USE_PRETTYSRC
+	    psrc_view = FALSE;
+#endif
+
+	    HTMLSetCharacterHandling(current_char_set);		/* restore, for sure? */
+
+	    /*
+	     * Reset all of the other relevant flags.  - FM
+	     */
+	    LYUserSpecifiedURL = FALSE;		/* only set for goto's and jumps's */
+	    LYJumpFileURL = FALSE;	/* only set for jump's */
+	    LYNoRefererForThis = FALSE;		/* always reset on return here */
+	    reloading = FALSE;	/* set for RELOAD and NOCACHE keys */
+	    HEAD_request = FALSE;	/* only set for HEAD requests */
+	    LYPermitURL = FALSE;	/* only for LYValidate or check_realm */
+	    ForcePush = FALSE;	/* only set for some PRINT requests. */
+	    LYforce_HTML_mode = FALSE;
+	    force_old_UCLYhndl_on_reload = FALSE;
+	    popped_doc = FALSE;
+	    pending_form_c = -1;
+
+	}
+	/* end if (LYforce_no_cache || force_load || are_different(...)) */
+	if (dump_output_immediately) {
+	    if (crawl) {
+		print_crawl_to_fd(stdout, curdoc.address, curdoc.title);
+	    } else if (!dump_links_only) {
+		print_wwwfile_to_fd(stdout, FALSE, FALSE);
+	    }
+	    return ((dump_server_status >= 400) ? EXIT_FAILURE : EXIT_SUCCESS);
+	}
+
+	/*
+	 * If the recent_sizechange variable is set to TRUE then the window
+	 * size changed recently.
+	 */
+	if (recent_sizechange) {
+	    /*
+	     * First we need to make sure the display library - curses, slang,
+	     * whatever - gets notified about the change, and gets a chance to
+	     * update external structures appropriately.  Hopefully the
+	     * stop_curses()/start_curses() sequence achieves this, at least if
+	     * the display library has a way to get the new screen size from
+	     * the OS.
+	     *
+	     * However, at least for ncurses, the update of the internal
+	     * structures will come still too late - the changed screen size is
+	     * detected in doupdate(), which would only be called (indirectly
+	     * through the HText_pageDisplay below) after the WINDOW structures
+	     * are already filled based on the old size.  So we notify the
+	     * ncurses library directly here.  - kw
+	     */
+#if defined(NCURSES) && defined(HAVE_RESIZETERM) && defined(HAVE_WRESIZE)
+	    resizeterm(LYlines, LYcols);
+	    wresize(LYwin, LYlines, LYcols);
+#else
+#if 0				/* defined(PDCURSES) && defined(HAVE_XCURSES) */
+	    resize_term(LYlines, LYcols);
+	    if (LYwin != 0)
+		LYwin = resize_window(LYwin, LYlines, LYcols);
+	    refresh();
+#else
+	    stop_curses();
+	    start_curses();
+	    LYclear();
+#endif
+#endif
+	    refresh_screen = TRUE;	/* to force a redraw */
+	    if (HTMainText)	/* to REALLY force it... - kw */
+		HText_setStale(HTMainText);
+	    recent_sizechange = FALSE;
+
+	    LYSetDisplayLines();
+	}
+
+	if (www_search_result != -1) {
+	    /*
+	     * This was a WWW search, set the line to the result of the search.
+	     */
+	    LYSetNewline(www_search_result);
+	    www_search_result = -1;	/* reset */
+	}
+
+	if (first_file == TRUE) {
+	    /*
+	     * We can never again have the first file.
+	     */
+	    first_file = FALSE;
+
+	    /*
+	     * Set the startrealm, and deal as best we can with preserving
+	     * forced HTML mode for a local startfile.  - FM
+	     */
+	    temp = HTParse(curdoc.address, "",
+			   PARSE_ACCESS + PARSE_HOST + PARSE_PUNCTUATION);
+	    if (isEmpty(temp)) {
+		StrAllocCopy(startrealm, NO_NOTHING);
+	    } else {
+		StrAllocCopy(startrealm, temp);
+		FREE(temp);
+		if (!(temp = HTParse(curdoc.address, "",
+				     PARSE_PATH + PARSE_PUNCTUATION))) {
+		    LYAddHtmlSep(&startrealm);
+		} else {
+		    if (forced_HTML_mode &&
+			!dump_output_immediately &&
+			!curdoc.bookmark &&
+			isFILE_URL(curdoc.address) &&
+			strlen(temp) > 1) {
+			/*
+			 * We forced HTML for a local startfile which is not a
+			 * bookmark file and has a path of at least two
+			 * letters.  If it doesn't have a suffix mapped to
+			 * text/html, we'll set the entire path (including the
+			 * lead slash) as a "suffix" mapped to text/html to
+			 * ensure it is always treated as an HTML source file. 
+			 * We are counting on a tail match to this full path
+			 * for some other URL fetched during the session having
+			 * too low a probability to worry about, but it could
+			 * happen.  - FM
+			 */
+			HTAtom *encoding;
+
+			if (HTFileFormat(temp, &encoding, NULL) != WWW_HTML) {
+			    HTSetSuffix(temp, "text/html", "8bit", 1.0);
+			}
+		    }
+		    if ((cp = strrchr(temp, '/')) != NULL) {
+			*(cp + 1) = '\0';
+			StrAllocCat(startrealm, temp);
+		    }
+		}
+	    }
+	    FREE(temp);
+	    CTRACE((tfp, "Starting realm is '%s'\n\n", startrealm));
+	    if (traversal) {
+		/*
+		 * Set up the crawl output stuff.
+		 */
+		if (curdoc.address && !lookup_link(curdoc.address)) {
+		    if (!isLYNXIMGMAP(curdoc.address))
+			crawl_ok = TRUE;
+		    add_to_table(curdoc.address);
+		}
+		/*
+		 * Set up the traversal_host comparison string.
+		 */
+		if (strncmp((curdoc.address ? curdoc.address : "NULL"),
+			    "http", 4)) {
+		    StrAllocCopy(traversal_host, NO_NOTHING);
+		} else if (check_realm) {
+		    StrAllocCopy(traversal_host, startrealm);
+		} else {
+		    temp = HTParse(curdoc.address, "",
+				   PARSE_ACCESS + PARSE_HOST + PARSE_PUNCTUATION);
+		    if (isEmpty(temp)) {
+			StrAllocCopy(traversal_host, NO_NOTHING);
+		    } else {
+			StrAllocCopy(traversal_host, temp);
+			LYAddHtmlSep(&traversal_host);
+		    }
+		    FREE(temp);
+		}
+		CTRACE((tfp, "Traversal host is '%s'\n\n", traversal_host));
+	    }
+	    if (startfile) {
+		/*
+		 * If homepage was not equated to startfile, make the homepage
+		 * URL the first goto entry.  - FM
+		 */
+		if (homepage && strcmp(startfile, homepage))
+		    HTAddGotoURL(homepage);
+		/*
+		 * If we are not starting up with startfile (e.g., had -book),
+		 * or if we are using the startfile and it has no POST content,
+		 * make the startfile URL a goto entry.  - FM
+		 */
+		if (strcmp(startfile, newdoc.address) ||
+		    newdoc.post_data == NULL)
+		    HTAddGotoURL(startfile);
+	    }
+	    if (TRACE) {
+		refresh_screen = TRUE;
+		if (!LYTraceLogFP || trace_mode_flag) {
+		    LYSleepAlert();
+		}
+	    }
+	}
+#ifdef USE_SOURCE_CACHE
+	/*
+	 * If the parse settings have changed since this HText was
+	 * generated, we need to reparse and redraw it.  -dsb
+	 *
+	 * Should be configured to avoid shock for experienced lynx users.
+	 * Currently enabled for cached sources only.
+	 */
+	if (HTdocument_settings_changed()) {
+	    if (HTcan_reparse_document()) {
+		HTInfoMsg(gettext("Reparsing document under current settings..."));
+		reparse_document();
+	    } else {
+		/*
+		 * Urk.  I have no idea how to recover from a failure here.
+		 * At a guess, I'll try reloading.  -dsb
+		 */
+		/*  currently disabled ***
+		   HTUserMsg(gettext("Reparsing document under current settings..."));
+		   cmd = LYK_RELOAD;
+		   goto new_cmd;
+		 */
+	    }
+	}
+
+	if (from_source_cache) {
+	    from_source_cache = FALSE;	/* reset */
+	    curdoc.line = -1;	/* so curdoc.line != Newline, see below */
+	}
+#endif
+
+	/*
+	 * If the curdoc.line is different than Newline then there must have
+	 * been a change since last update.  Run HText_pageDisplay() to create
+	 * a fresh screen of text output.
+	 *
+	 * If we got new HTMainText go this way.  All display_partial calls
+	 * ends here for final redraw.
+	 */
+	if (curdoc.line != LYGetNewline()) {
+#ifdef INACTIVE_INPUT_STYLE_VH
+	    textinput_redrawn = FALSE;
+#endif
+
+	    refresh_screen = FALSE;
+
+	    HText_pageDisplay(LYGetNewline(), prev_target);
+
+#ifdef DIRED_SUPPORT
+	    if (lynx_edit_mode && nlinks > 0 && !HTList_isEmpty(tagged))
+		showtags(tagged);
+#endif /* DIRED_SUPPORT */
+
+	    /*
+	     * Check if there is more info below this page.
+	     */
+	    more_text = HText_canScrollDown();
+
+	    if (newdoc.link < 0)
+		goto_line(LYGetNewline());
+	    LYSetNewline(HText_getTopOfScreen() + 1);
+	    curdoc.line = LYGetNewline();
+
+	    if (curdoc.title == NULL) {
+		/*
+		 * If we don't yet have a title, try to get it, or set to that
+		 * for newdoc.title.  - FM
+		 */
+		if (HText_getTitle()) {
+		    StrAllocCopy(curdoc.title, HText_getTitle());
+		} else {
+		    StrAllocCopy(curdoc.title, newdoc.title);
+		}
+	    }
+
+	    /*
+	     * If the request is to highlight a link which is counted from the
+	     * start of document, correct the link number:
+	     */
+	    if (newdoc_link_is_absolute) {
+		newdoc_link_is_absolute = FALSE;
+		if (curdoc.line > 1)
+		    newdoc.link -= HText_LinksInLines(HTMainText, 1,
+						      curdoc.line - 1);
+	    }
+
+	    if (arrowup) {
+		/*
+		 * arrowup is set if we just came up from a page below.
+		 */
+		curdoc.link = nlinks - 1;
+		arrowup = FALSE;
+	    } else {
+		curdoc.link = newdoc.link;
+		if (curdoc.link >= nlinks) {
+		    curdoc.link = nlinks - 1;
+		} else if (curdoc.link < 0 && nlinks > 0) {
+		    /*
+		     * We may have popped a doc (possibly in local_dired) which
+		     * didn't have any links when it was pushed, but does have
+		     * links now (e.g., a file was created).  Code below
+		     * assumes that curdoc.link is valid and that
+		     * (curdoc.link==-1) only occurs if (nlinks==0) is true.  -
+		     * KW
+		     */
+		    curdoc.link = 0;
+		}
+	    }
+
+	    show_help = FALSE;	/* reset */
+	    newdoc.line = 1;
+	    newdoc.link = 0;
+	    curdoc.line = LYGetNewline();	/* set */
+	} else if (newdoc.link < 0) {
+	    newdoc.link = 0;	/* ...just in case getfile set this */
+	}
+
+	/*
+	 * Refresh the screen if necessary.
+	 */
+	if (refresh_screen) {
+#if defined(FANCY_CURSES) || defined (USE_SLANG)
+	    if (enable_scrollback) {
+		LYclear();
+	    } else {
+		LYerase();
+	    }
+#else
+	    LYclear();
+#endif /* FANCY_CURSES || USE_SLANG */
+	    HText_pageDisplay(LYGetNewline(), prev_target);
+
+#ifdef DIRED_SUPPORT
+	    if (lynx_edit_mode && nlinks > 0 && !HTList_isEmpty(tagged))
+		showtags(tagged);
+#endif /* DIRED_SUPPORT */
+
+	    /*
+	     * Check if there is more info below this page.
+	     */
+	    more_text = HText_canScrollDown();
+
+	    /*
+	     * Adjust curdoc.link as above; nlinks may have changed, if the
+	     * refresh_screen flag was set as a result of a size change.  Code
+	     * below assumes that curdoc.link is valid and that
+	     * (curdoc.link==-1) only occurs if (nlinks==0) is true.  - kw
+	     */
+	    if (curdoc.link >= nlinks) {
+		curdoc.link = nlinks - 1;
+	    } else if (curdoc.link < 0 && nlinks > 0) {
+		curdoc.link = 0;
+	    }
+
+	    if (user_mode == NOVICE_MODE)
+		noviceline(more_text);	/* print help message */
+	    refresh_screen = FALSE;
+
+	}
+
+	curlink_is_editable = (BOOLEAN)
+	    (nlinks > 0 &&
+	     (links[curdoc.link].type == WWW_FORM_LINK_TYPE) &&
+	     F_TEXTLIKE(links[curdoc.link].l_form->type));
+
+	use_last_tfpos = (BOOLEAN)
+	    (curlink_is_editable &&
+	     (real_cmd == LYK_LPOS_PREV_LINK ||
+	      real_cmd == LYK_LPOS_NEXT_LINK));
+
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+	if (!textfields_need_activation)
+	    textinput_activated = TRUE;
+#endif
+
+#if defined(WIN_EX)		/* 1997/10/08 (Wed) 14:52:06 */
+	if (nlinks > 0) {
+	    char *p = "LYNX (unknown link type)";
+
+	    /* Show the URL & kanji code . */
+	    if (strlen(links[curdoc.link].lname) == 0) {
+
+		if (links[curdoc.link].type == WWW_FORM_LINK_TYPE) {
+
+		    switch (links[curdoc.link].l_form->type) {
+		    case F_TEXT_SUBMIT_TYPE:
+		    case F_SUBMIT_TYPE:
+		    case F_IMAGE_SUBMIT_TYPE:
+			p = "[SUBMIT]";
+			break;
+		    case F_PASSWORD_TYPE:
+			p = "Password";
+			break;
+		    case F_OPTION_LIST_TYPE:
+			p = "Option list";
+			break;
+		    case F_CHECKBOX_TYPE:
+			p = "Check box";
+			break;
+		    case F_RADIO_TYPE:
+			p = "[Radio]";
+			break;
+		    case F_RESET_TYPE:
+			p = "[Reset]";
+			break;
+		    case F_TEXT_TYPE:
+			p = "Text input";
+			break;
+		    case F_TEXTAREA_TYPE:
+			p = "Text input lines";
+			break;
+		    default:
+			break;
+		    }
+		    set_ws_title(p);
+		}
+	    } else {
+		if (user_mode == ADVANCED_MODE) {
+		    p = curdoc.title;
+		} else {
+		    p = links[curdoc.link].lname;
+		}
+
+		if (strlen(p) < (sizeof(sjis_buff) / 10)) {
+		    strcpy(temp_buff, p);
+		    if (strchr(temp_buff, '%')) {
+			HTUnEscape(temp_buff);
+		    }
+		    str_sjis(sjis_buff, temp_buff);
+		    set_ws_title(LYElideString(sjis_buff, 10));
+		}
+	    }
+	} else {
+	    if (strlen(curdoc.address) < sizeof(temp_buff) - 1) {
+		if (user_mode == ADVANCED_MODE) {
+		    str_sjis(temp_buff, curdoc.title);
+		} else {
+		    strcpy(temp_buff, curdoc.address);
+		}
+		set_ws_title(HTUnEscape(temp_buff));
+	    }
+	}
+#endif /* WIN_EX */
+
+	/*
+	 * Report unread or new mail, if appropriate.
+	 */
+	if (check_mail && !no_mail)
+	    LYCheckMail();
+
+	/*
+	 * If help is not on the screen, then put a message on the screen to
+	 * tell the user other misc info.
+	 */
+	if (!show_help) {
+	    show_main_statusline(links[curdoc.link],
+				 (curlink_is_editable && textinput_activated) ?
+				 FOR_INPUT : FOR_PANEL);
+	} else {
+	    show_help = FALSE;
+	}
+
+	if (nlinks > 0) {
+	    /*
+	     * Highlight current link, unless it is an active text input field.
+	     */
+	    if (!curlink_is_editable) {
+		LYhighlight(ON, curdoc.link, prev_target);
+#ifndef INACTIVE_INPUT_STYLE_VH
+	    } else if (!textinput_activated) {
+		LYhighlight(ON, curdoc.link, prev_target);
+#endif
+	    }
+	}
+
+	if (traversal) {
+	    /*
+	     * Don't go interactively into forms, or accept keystrokes from the
+	     * user
+	     */
+	    if (crawl && crawl_ok) {
+		crawl_ok = FALSE;
+#ifdef FNAMES_8_3
+		sprintf(cfile, "lnk%05d.dat", crawl_count);
+#else
+		sprintf(cfile, "lnk%08d.dat", crawl_count);
+#endif /* FNAMES_8_3 */
+		crawl_count = crawl_count + 1;
+		if ((cfp = LYNewTxtFile(cfile)) != NULL) {
+		    print_crawl_to_fd(cfp, curdoc.address, curdoc.title);
+		    LYCloseOutput(cfp);
+		} else {
+		    if (!dump_output_immediately)
+			cleanup();
+		    fprintf(
+#ifdef UNIX
+			       (dump_output_immediately
+				? stderr
+				: stdout),
+#else
+			       stdout,
+#endif
+			       gettext("Fatal error - could not open output file %s\n"), cfile);
+		    if (!dump_output_immediately) {
+			exit_immediately(EXIT_FAILURE);
+		    }
+		    return (EXIT_FAILURE);
+		}
+	    }
+	} else {
+	    /*
+	     * Normal, non-traversal handling.
+	     */
+	    if (curlink_is_editable &&
+		(textinput_activated || pending_form_c != -1)) {
+		if (pending_form_c != -1) {
+		    real_c = pending_form_c;
+		    pending_form_c = -1;
+		} else {
+		    /*
+		     * Replace novice lines if in NOVICE_MODE.
+		     */
+		    if (user_mode == NOVICE_MODE) {
+			form_noviceline(links[curdoc.link].l_form->disabled);
+		    }
+		    real_c = change_form_link(curdoc.link,
+					      &newdoc, &refresh_screen,
+					      use_last_tfpos, FALSE);
+		}
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+		if (textfields_need_activation)
+		    textinput_activated = FALSE;
+#ifdef INACTIVE_INPUT_STYLE_VH
+		textinput_redrawn = FALSE;
+#endif
+#endif
+
+		c = (real_c == LKC_DONE) ? DO_NOTHING : LKC_TO_C(real_c);
+		if (c != DO_NOTHING &&
+		    peek_mouse_link() != -1 && peek_mouse_link() != -2)
+		    old_c = 0;
+		if (peek_mouse_link() >= 0 &&
+		    LKC_TO_LAC(keymap, real_c) != LYK_CHANGE_LINK) {
+		    do_change_link();
+		    if ((c == '\n' || c == '\r') &&
+			links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
+			F_TEXTLIKE(links[curdoc.link].l_form->type) &&
+			!textfields_need_activation) {
+			c = DO_NOTHING;
+		    }
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+		} else if (LinkIsTextarea(curdoc.link)
+			   && textfields_need_activation
+			   && !links[curdoc.link].l_form->disabled
+			   && peek_mouse_link() < 0 &&
+			   (((LKC_TO_LAC(keymap, real_c) == LYK_NEXT_LINK ||
+#ifdef TEXTAREA_AUTOGROW
+			      LKC_TO_LAC(keymap, real_c) == LYK_ACTIVATE ||
+#endif
+			      LKC_TO_LAC(keymap, real_c) == LYK_LPOS_NEXT_LINK ||
+			      LKC_TO_LAC(keymap, real_c) == LYK_DOWN_LINK) &&
+			     ((curdoc.link < nlinks - 1 &&
+			       LinkIsTextarea(curdoc.link + 1)
+			       && (links[curdoc.link].l_form->number ==
+				   links[curdoc.link + 1].l_form->number)
+			       && strcmp(links[curdoc.link].l_form->name,
+					 links[curdoc.link + 1].l_form->name)
+			       == 0) ||
+			      (curdoc.link == nlinks - 1 && more_text &&
+			       HText_TAHasMoreLines(curdoc.link, 1)))) ||
+			    ((LKC_TO_LAC(keymap, real_c) == LYK_PREV_LINK ||
+			      LKC_TO_LAC(keymap, real_c) == LYK_LPOS_PREV_LINK ||
+			      LKC_TO_LAC(keymap, real_c) == LYK_UP_LINK) &&
+			     ((curdoc.link > 0 &&
+			       LinkIsTextarea(curdoc.link - 1)
+			       && (links[curdoc.link].l_form->number ==
+				   links[curdoc.link - 1].l_form->number) &&
+			       strcmp(links[curdoc.link].l_form->name,
+				      links[curdoc.link - 1].l_form->name) == 0)
+			      || (curdoc.link == 0 && curdoc.line > 1 &&
+				  HText_TAHasMoreLines(curdoc.link, -1)))))) {
+		    textinput_activated = TRUE;
+#ifdef TEXTAREA_AUTOGROW
+		    if ((c == '\n' || c == '\r') &&
+			LKC_TO_LAC(keymap, real_c) == LYK_ACTIVATE)
+			c = LAC_TO_LKC0(LYK_NEXT_LINK);
+#endif /* TEXTAREA_AUTOGROW */
+#endif /* TEXTFIELDS_MAY_NEED_ACTIVATION */
+		} else
+		    switch (c) {
+		    case '\n':
+		    case '\r':
+#ifdef TEXTAREA_AUTOGROW
+			/*
+			 * If on the bottom line of a TEXTAREA, and the user
+			 * hit the ENTER key, we add a new line/anchor
+			 * automatically, positioning the cursor on it.
+			 *
+			 * If at the bottom of the screen, we effectively
+			 * perform an LYK_DOWN_HALF-like operation, then move
+			 * down to the new line we just added.  --KED 02/14/99
+			 *
+			 * [There is some redundancy and non-standard
+			 * indentation in the monster-if() below.  This is
+			 * intentional ...  to try and improve the
+			 * "readability" (such as it is).  Caveat emptor to
+			 * anyone trying to change it.]
+			 */
+			if (LinkIsTextarea(curdoc.link)
+			    && ((curdoc.link == nlinks - 1 &&
+				 !(more_text &&
+				   HText_TAHasMoreLines(curdoc.link, 1)))
+				||
+				((curdoc.link < nlinks - 1) &&
+				 !LinkIsTextarea(curdoc.link + 1))
+				||
+				((curdoc.link < nlinks - 1) &&
+				 (LinkIsTextarea(curdoc.link + 1)
+				  && ((links[curdoc.link].l_form->number !=
+				       links[curdoc.link + 1].l_form->number) ||
+				      (strcmp(links[curdoc.link].l_form->name,
+					      links[curdoc.link + 1].l_form->name)
+				       != 0)))))) {
+
+			    HText_ExpandTextarea(&links[curdoc.link], 1);
+
+			    if (links[curdoc.link].ly < display_lines) {
+				refresh_screen = TRUE;
+			    } else {
+				LYChgNewline(display_lines / 2);
+				if (nlinks > 0 && curdoc.link > -1 &&
+				    links[curdoc.link].ly > display_lines / 2) {
+				    newdoc.link = curdoc.link;
+				    for (i = 0;
+					 links[i].ly <= (display_lines / 2);
+					 i++)
+					--newdoc.link;
+				    newdoc.link++;
+				}
+			    }
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+			    if (textfields_need_activation) {
+				textinput_activated = TRUE;
+				textfields_need_activation = textfields_activation_option;
+#ifdef INACTIVE_INPUT_STYLE_VH
+				textinput_redrawn = TRUE;
+#endif
+			    };
+#endif
+
+			}
+#endif /* TEXTAREA_AUTOGROW */
+
+			/*
+			 * Make return in input field (if it was returned by
+			 * change_form_link) act as LYK_NEXT_LINK, independent
+			 * of what key (if any) is mapped to LYK_NEXT_LINK.  -
+			 * kw
+			 */
+			c = LAC_TO_LKC0(LYK_NEXT_LINK);
+			break;
+		    default:
+
+			if (old_c != c && old_c != real_c && c != real_c)
+			    real_c = c;
+		    }
+	    } else {
+#if defined(TEXTFIELDS_MAY_NEED_ACTIVATION) && defined(INACTIVE_INPUT_STYLE_VH)
+		if (curlink_is_editable && !textinput_redrawn) {
+		    /*draw the text entry, but don't activate it */
+		    textinput_redrawn = TRUE;
+		    change_form_link_ex(curdoc.link,
+					&newdoc, &refresh_screen,
+					use_last_tfpos, FALSE, TRUE);
+		    if (LYShowCursor) {
+			LYmove(links[curdoc.link].ly,
+			       ((links[curdoc.link].lx > 0) ?
+				(links[curdoc.link].lx - 1) : 0));
+		    } else {
+			LYHideCursor();
+		    }
+		}
+#endif /* TEXTFIELDS_MAY_NEED_ACTIVATION && INACTIVE_INPUT_STYLE_VH */
+		/*
+		 * Get a keystroke from the user.  Save the last keystroke to
+		 * avoid redundant error reporting.
+		 */
+		real_c = c = LYgetch();		/* get user input */
+
+		if (c != last_key)
+		    key_count = 0;
+		key_count++;
+		last_key = c;
+#ifndef VMS
+		if (c == 3) {	/* ^C */
+		    /*
+		     * This shouldn't happen.  We'll try to deal with whatever
+		     * bug caused it.  - FM
+		     */
+		    signal(SIGINT, cleanup_sig);
+		    old_c = 0;
+		    cmd = LYK_QUIT;
+		    goto new_cmd;
+		}
+#endif /* !VMS */
+		if (LKC_HAS_ESC_MOD(c) && EditBinding(c) != LYE_FORM_PASS) {
+		    /*
+		     * If ESC + <key> was read (and not recognized as a
+		     * terminal escape sequence for another key), ignore the
+		     * ESC modifier and act on <key> only if the line editor
+		     * binding would have passed the same ESC-modified
+		     * lynxkeycode back to us if it had been pressed in a text
+		     * input field.  Otherwise set interesting part so that it
+		     * will map to 0, to prevent that ESC + <key> acts like
+		     * <key>, which might be unexpected.  - kw
+		     */
+		    c = (c & ~LKC_MASK) | LAC_TO_LKC(0);
+		}
+		if (old_c != real_c) {
+		    old_c = 0;
+		}
+	    }
+	}
+
+#ifdef VMS
+	if (HadVMSInterrupt) {
+	    HadVMSInterrupt = FALSE;
+	    c = DO_NOTHING;
+	}
+#else
+	if (recent_sizechange) {
+	    if (c <= 0)
+		c = DO_NOTHING;
+	}
+#endif /* VMS */
+
+      new_keyboard_input:
+	/*
+	 * A goto point for new input without going back through the getch()
+	 * loop.
+	 */
+	if (traversal) {
+	    if ((c = DoTraversal(c, &crawl_ok)) < 0)
+		return (EXIT_FAILURE);
+	}
+	/* traversal */
+#ifdef WIN_EX
+	if (c == DO_NOTHING)
+	    cmd = LYK_DO_NOTHING;
+	else
+#endif
+	    cmd = LKC_TO_LAC(keymap, c);	/* adds 1 to map EOF to 0 */
+
+#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE)
+	if (lynx_edit_mode && !no_dired_support && LKC_TO_LAC(key_override, c))
+	    cmd = LKC_TO_LAC(key_override, c);
+#endif /* DIRED_SUPPORT && OK_OVERRIDE */
+
+	real_cmd = cmd;
+
+	/*
+	 * A goto point for new input without going back through the getch()
+	 * loop.
+	 */
+      new_cmd:
+
+	force_old_UCLYhndl_on_reload = FALSE;
+	CTRACE_FLUSH(tfp);
+
+	if (cmd != LYK_UP_LINK && cmd != LYK_DOWN_LINK)
+	    follow_col = -1;
+
+	switch (cmd) {
+	case -1:
+	    HTUserMsg(COMMAND_UNKNOWN);
+	    break;
+	case 0:		/* unmapped character */
+	default:
+	    if (curdoc.link >= 0 && curdoc.link < nlinks &&
+		links[curdoc.link].type == WWW_FORM_LINK_TYPE &&
+		F_TEXTLIKE(links[curdoc.link].l_form->type)) {
+
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+		if (textfields_need_activation) {
+		    show_main_statusline(links[curdoc.link], FOR_PANEL);
+#ifdef INACTIVE_INPUT_STYLE_VH
+		    textinput_redrawn = FALSE;
+#endif
+		} else
+#endif
+		    show_main_statusline(links[curdoc.link], FOR_INPUT);
+	    } else if (more_text) {
+		HTInfoMsg(MOREHELP);
+	    } else {
+		HTInfoMsg(HELP);
+	    }
+	    show_help = TRUE;
+
+	    if (TRACE) {
+		sprintf(cfile, "%d", c);
+		LYaddstr(cfile);	/* show the user input */
+		cfile[0] = '\0';
+	    }
+	    break;
+
+	case LYK_COMMAND:
+	    cmd = handle_LYK_COMMAND(user_input_buffer);
+	    goto new_cmd;
+
+	case LYK_INTERRUPT:
+	    /*
+	     * No network transmission to interrupt - 'til we multithread.
+	     */
+	    break;
+
+	case LYK_F_LINK_NUM:
+	    c = '\0';
+	    /* FALLTHRU */
+	case LYK_1:		/* FALLTHRU */
+	case LYK_2:		/* FALLTHRU */
+	case LYK_3:		/* FALLTHRU */
+	case LYK_4:		/* FALLTHRU */
+	case LYK_5:		/* FALLTHRU */
+	case LYK_6:		/* FALLTHRU */
+	case LYK_7:		/* FALLTHRU */
+	case LYK_8:		/* FALLTHRU */
+	case LYK_9:
+	    handle_LYK_digit(c, &force_load, user_input_buffer,
+			     &old_c, real_c, &try_internal);
+	    break;
+
+	case LYK_SOURCE:	/* toggle view source mode */
+	    handle_LYK_SOURCE(&ownerS_address);
+	    break;
+
+	case LYK_CHG_CENTER:	/* ^Q */
+
+	    if (no_table_center) {
+		no_table_center = FALSE;
+		HTInfoMsg(gettext("TABLE center enable."));
+	    } else {
+		no_table_center = TRUE;
+		HTInfoMsg(gettext("TABLE center disable."));
+	    }
+	    /* FALLTHRU */
+
+	case LYK_RELOAD:	/* control-R to reload and refresh */
+	    handle_LYK_RELOAD(real_cmd);
+	    break;
+
+	case LYK_HISTORICAL:	/* toggle 'historical' comments parsing */
+	    handle_LYK_HISTORICAL();
+	    break;
+
+	case LYK_MINIMAL:	/* toggle 'minimal' comments parsing */
+	    handle_LYK_MINIMAL();
+	    break;
+
+	case LYK_SOFT_DQUOTES:
+	    handle_LYK_SOFT_DQUOTES();
+	    break;
+
+	case LYK_SWITCH_DTD:
+	    handle_LYK_SWITCH_DTD();
+	    break;
+
+	case LYK_QUIT:		/* quit */
+	    if (handle_LYK_QUIT())
+		return (EXIT_SUCCESS);
+	    break;
+
+	case LYK_ABORT:	/* don't ask the user about quitting */
+	    return (EXIT_SUCCESS);
+
+	case LYK_NEXT_PAGE:	/* next page */
+	    handle_LYK_NEXT_PAGE(&old_c, real_c);
+	    break;
+
+	case LYK_PREV_PAGE:	/* page up */
+	    handle_LYK_PREV_PAGE(&old_c, real_c);
+	    break;
+
+	case LYK_UP_TWO:
+	    handle_LYK_UP_TWO(&arrowup, &old_c, real_c);
+	    break;
+
+	case LYK_DOWN_TWO:
+	    handle_LYK_DOWN_TWO(&old_c, real_c);
+	    break;
+
+	case LYK_UP_HALF:
+	    handle_LYK_UP_HALF(&arrowup, &old_c, real_c);
+	    break;
+
+	case LYK_DOWN_HALF:
+	    handle_LYK_DOWN_HALF(&old_c, real_c);
+	    break;
+
+#ifdef CAN_CUT_AND_PASTE
+	case LYK_TO_CLIPBOARD:	/* ^S */
+	    {
+		char *s;
+		int ch2;
+
+		/* The logic resembles one of ADD_BOOKMARK */
+		if (nlinks > 0 && links[curdoc.link].lname
+		    && links[curdoc.link].type != WWW_FORM_LINK_TYPE) {
+		    /* Makes sense to copy a link */
+		    _statusline("Copy D)ocument's or L)ink's URL to clipboard or C)ancel?");
+		    ch2 = LYgetch_single();
+		    if (ch2 == 'D')
+			s = curdoc.address;
+		    else if (ch2 == 'C')
+			break;
+		    else
+			s = links[curdoc.link].lname;
+		} else
+		    s = curdoc.address;
+		if (isEmpty(s))
+		    HTInfoMsg(gettext("Current URL is empty."));
+		if (put_clip(s))
+		    HTInfoMsg(gettext("Copy to clipboard failed."));
+		else if (s == curdoc.address)
+		    HTInfoMsg(gettext("Document URL put to clipboard."));
+		else
+		    HTInfoMsg(gettext("Link URL put to clipboard."));
+	    }
+	    break;
+
+	case LYK_PASTE_URL:
+	    if (no_goto && !LYValidate) {	/*  Go to not allowed. - FM */
+		HTUserMsg(GOTO_DISALLOWED);
+	    } else {
+		unsigned char *s = (unsigned char *) get_clip_grab(), *e, *t;
+		char *buf;
+		int len2;
+
+		if (!s)
+		    break;
+		len2 = (int) strlen((const char *) s);
+		e = s + len2;
+		while (s < e && strchr(" \t\n\r", *s))
+		    s++;
+		while (s < e && strchr(" \t\n\r", e[-1]))
+		    e--;
+		if (s[0] == '<' && e > s && e[-1] == '>') {
+		    s++;
+		    e--;
+		    if (!strncasecomp((const char *) s, "URL:", 4))
+			s += 4;
+		}
+		if (s >= e) {
+		    HTInfoMsg(gettext("No URL in the clipboard."));
+		    break;
+		}
+		len = (unsigned) (e - s + 1);
+		if (len < MAX_LINE)
+		    len = MAX_LINE;	/* Required for do_check_goto_URL() */
+		buf = (char *) malloc(len);
+		strncpy(buf, (const char *) s, (unsigned) (e - s));
+		buf[e - s] = '\0';
+		t = (unsigned char *) buf;
+
+		while (s < e) {
+		    if (strchr(" \t\n\r", *s)) {
+			int nl2 = 0;	/* Keep whitespace without NL - file names! */
+			unsigned char *s1 = s;
+
+			while (strchr(" \t\n\r", *s)) {
+			    if (!nl2 && *s == '\n')
+				nl2 = 1;
+			    s++;
+			}
+			if (!nl2) {
+			    while (s1 < s) {
+				if (*s1 != '\r' && *s1 != '\r')
+				    *t = *s1;
+				t++, s1++;
+			    }
+			}
+		    } else
+			*t++ = *s++;
+		}
+		*t = '\0';
+		get_clip_release();
+		do_check_goto_URL(buf, &temp, &force_load);
+		free(buf);
+	    }
+	    break;
+#endif
+
+#ifdef KANJI_CODE_OVERRIDE
+	case LYK_CHG_KCODE:
+	    if (LYRawMode && (HTCJK == JAPANESE)) {
+		switch (last_kcode) {
+		case NOKANJI:
+		    last_kcode = SJIS;
+		    break;
+		case SJIS:
+		    last_kcode = EUC;
+		    break;
+		case EUC:
+		    last_kcode = NOKANJI;
+		    break;
+		default:
+		    break;
+		}
+	    }
+	    LYmove(0, 0);
+	    lynx_start_title_color();
+	    LYaddstr(str_kcode(last_kcode));
+	    lynx_stop_title_color();
+
+	    break;
+#endif
+
+	case LYK_REFRESH:
+	    refresh_screen = TRUE;
+	    lynx_force_repaint();
+	    break;
+
+	case LYK_HOME:
+	    if (curdoc.line > 1) {
+		LYSetNewline(1);
+	    } else {
+		cmd = LYK_PREV_PAGE;
+		goto new_cmd;
+	    }
+	    break;
+
+	case LYK_END:
+	    i = HText_getNumOfLines() - display_lines + 2;
+	    if (i >= 1 && LYGetNewline() != i) {
+		LYSetNewline(i);	/* go to end of file */
+		arrowup = TRUE;	/* position on last link */
+	    } else {
+		cmd = LYK_NEXT_PAGE;
+		goto new_cmd;
+	    }
+	    break;
+
+	case LYK_FIRST_LINK:
+	    handle_LYK_FIRST_LINK();
+	    break;
+
+	case LYK_LAST_LINK:
+	    handle_LYK_LAST_LINK();
+	    break;
+
+	case LYK_PREV_LINK:
+	case LYK_LPOS_PREV_LINK:
+	    handle_LYK_PREV_LINK(&arrowup, &old_c, real_c);
+	    break;
+
+	case LYK_NEXT_LINK:
+	case LYK_LPOS_NEXT_LINK:
+	    handle_LYK_NEXT_LINK(c, &old_c, real_c);
+	    break;
+
+	case LYK_FASTFORW_LINK:
+	    handle_LYK_FASTFORW_LINK(&old_c, real_c);
+	    break;
+
+	case LYK_FASTBACKW_LINK:
+	    if (handle_LYK_FASTBACKW_LINK(&cmd, &old_c, real_c))
+		goto new_cmd;
+	    break;
+
+	case LYK_UP_LINK:
+	    handle_LYK_UP_LINK(&follow_col, &arrowup, &old_c, real_c);
+	    break;
+
+	case LYK_DOWN_LINK:
+	    handle_LYK_DOWN_LINK(&follow_col, &old_c, real_c);
+	    break;
+
+	case LYK_CHANGE_LINK:
+	    do_change_link();
+#if defined(TEXTFIELDS_MAY_NEED_ACTIVATION) && defined(INACTIVE_INPUT_STYLE_VH)
+	    if (textfields_need_activation)
+		textinput_redrawn = FALSE;
+#endif /* TEXTFIELDS_MAY_NEED_ACTIVATION && INACTIVE_INPUT_STYLE_VH */
+	    break;
+
+	case LYK_RIGHT_LINK:
+	    handle_LYK_RIGHT_LINK();
+	    break;
+
+	case LYK_LEFT_LINK:
+	    handle_LYK_LEFT_LINK();
+	    break;
+
+	case LYK_COOKIE_JAR:	/* show the cookie jar */
+	    if (handle_LYK_COOKIE_JAR(&cmd))
+		goto new_cmd;
+	    break;
+
+#ifdef USE_CACHEJAR
+	case LYK_CACHE_JAR:	/* show the cache jar */
+	    if (handle_LYK_CACHE_JAR(&cmd))
+		goto new_cmd;
+	    break;
+#endif
+
+	case LYK_HISTORY:	/* show the history page */
+	    if (handle_LYK_HISTORY(ForcePush))
+		break;
+
+	    /* FALLTHRU */
+	case LYK_PREV_DOC:	/* back up a level */
+	    switch (handle_PREV_DOC(&cmd, &old_c, real_c)) {
+	    case 1:
+		return (EXIT_SUCCESS);
+	    case 2:
+		goto new_cmd;
+	    }
+	    break;
+
+	case LYK_NEXT_DOC:	/* undo back up a level */
+	    handle_NEXT_DOC();
+	    break;
+
+	case LYK_NOCACHE:	/* Force submission of form or link with no-cache */
+	    if (!handle_LYK_NOCACHE(&old_c, real_c))
+		break;
+
+	    /* FALLTHRU */
+	case LYK_ACTIVATE:	/* follow a link */
+	case LYK_SUBMIT:	/* follow a link, submit TEXT_SUBMIT input */
+	    switch (handle_LYK_ACTIVATE(&c,
+					cmd,
+					&try_internal,
+					&refresh_screen,
+					&force_load,
+					real_cmd)) {
+	    case 1:
+		continue;
+	    case 2:
+		goto new_keyboard_input;
+	    case 3:
+		pending_form_c = c;
+		break;
+	    }
+	    break;
+
+	case LYK_ELGOTO:	/* edit URL of current link and go to it  */
+	    if (handle_LYK_ELGOTO(&ch, user_input_buffer, &temp, &old_c, real_c))
+		do_check_goto_URL(user_input_buffer, &temp, &force_load);
+	    break;
+
+	case LYK_ECGOTO:	/* edit current URL and go to to it     */
+	    if (handle_LYK_ECGOTO(&ch, user_input_buffer, &temp, &old_c, real_c))
+		do_check_goto_URL(user_input_buffer, &temp, &force_load);
+	    break;
+
+	case LYK_GOTO:		/* 'g' to goto a random URL  */
+	    if (handle_LYK_GOTO(&ch, user_input_buffer, &temp, &recall,
+				&URLTotal, &URLNum, &FirstURLRecall, &old_c,
+				real_c)) {
+		if (do_check_recall(ch, user_input_buffer, &temp, URLTotal,
+				    &URLNum, recall, &FirstURLRecall))
+		    do_check_goto_URL(user_input_buffer, &temp, &force_load);
+	    }
+	    break;
+
+	case LYK_DWIMHELP:	/* show context-dependent help file */
+	    handle_LYK_DWIMHELP(&cshelpfile);
+	    /* FALLTHRU */
+
+	case LYK_HELP:		/* show help file */
+	    handle_LYK_HELP(&cshelpfile);
+	    break;
+
+	case LYK_INDEX:	/* index file */
+	    handle_LYK_INDEX(&old_c, real_c);
+	    break;
+
+	case LYK_MAIN_MENU:	/* return to main screen */
+	    handle_LYK_MAIN_MENU(&old_c, real_c);
+	    break;
+
+#ifdef EXP_NESTED_TABLES
+	case LYK_NESTED_TABLES:
+	    if (handle_LYK_NESTED_TABLES(&cmd))
+		goto new_cmd;
+	    break;
+#endif
+	case LYK_OPTIONS:	/* options screen */
+	    if (handle_LYK_OPTIONS(&cmd, &refresh_screen))
+		goto new_cmd;
+	    break;
+
+	case LYK_INDEX_SEARCH:	/* search for a user string */
+	    handle_LYK_INDEX_SEARCH(&force_load, ForcePush, &old_c, real_c);
+	    break;
+
+	case LYK_WHEREIS:	/* search within the document */
+	case LYK_NEXT:		/* find the next occurrence in the document */
+	case LYK_PREV:		/* find the previous occurrence in the document */
+	    handle_LYK_WHEREIS(cmd, &refresh_screen);
+	    break;
+
+	case LYK_COMMENT:	/* reply by mail */
+	    handle_LYK_COMMENT(&refresh_screen, &owner_address, &old_c, real_c);
+	    break;
+
+#ifdef DIRED_SUPPORT
+	case LYK_TAG_LINK:	/* tag or untag the current link */
+	    handle_LYK_TAG_LINK();
+	    break;
+
+	case LYK_MODIFY:	/* rename a file or directory */
+	    handle_LYK_MODIFY(&refresh_screen);
+	    break;
+
+	case LYK_CREATE:	/* create a new file or directory */
+	    handle_LYK_CREATE();
+	    break;
+#endif /* DIRED_SUPPORT */
+
+	case LYK_DWIMEDIT:	/* context-dependent edit */
+	    switch (handle_LYK_DWIMEDIT(&cmd, &old_c, real_c)) {
+	    case 1:
+		continue;
+	    case 2:
+		goto new_cmd;
+	    }
+	    /* FALLTHRU */
+
+	case LYK_EDIT:		/* edit */
+	    handle_LYK_EDIT(&old_c, real_c);
+	    break;
+
+	case LYK_DEL_BOOKMARK:	/* remove a bookmark file link */
+	    handle_LYK_DEL_BOOKMARK(&refresh_screen, &old_c, real_c);
+	    break;
+
+#ifdef DIRED_SUPPORT
+	case LYK_REMOVE:	/* remove files and directories */
+	    handle_LYK_REMOVE(&refresh_screen);
+	    break;
+#endif /* DIRED_SUPPORT */
+
+#if defined(DIRED_SUPPORT) && defined(OK_INSTALL)
+	case LYK_INSTALL:	/* install a file into system area */
+	    handle_LYK_INSTALL();
+	    break;
+#endif /* DIRED_SUPPORT && OK_INSTALL */
+
+	case LYK_INFO:		/* show document info */
+	    if (handle_LYK_INFO(&cmd))
+		goto new_cmd;
+	    break;
+
+	case LYK_EDIT_TEXTAREA:	/* use external editor on a TEXTAREA - KED */
+	    handle_LYK_EDIT_TEXTAREA(&refresh_screen, &old_c, real_c);
+	    break;
+
+	case LYK_GROW_TEXTAREA:	/* add new lines to bottom of TEXTAREA - KED */
+	    handle_LYK_GROW_TEXTAREA(&refresh_screen);
+	    break;
+
+	case LYK_INSERT_FILE:	/* insert file in TEXTAREA, above cursor - KED */
+	    handle_LYK_INSERT_FILE(&refresh_screen, &old_c, real_c);
+	    break;
+
+	case LYK_PRINT:	/* print the file */
+	    handle_LYK_PRINT(&ForcePush, &old_c, real_c);
+	    break;
+
+	case LYK_LIST:		/* list links in the current document */
+	    if (handle_LYK_LIST(&cmd))
+		goto new_cmd;
+	    break;
+
+#ifdef EXP_ADDRLIST_PAGE
+	case LYK_ADDRLIST:	/* always list URL's (only) */
+	    if (handle_LYK_ADDRLIST(&cmd))
+		goto new_cmd;
+	    break;
+#endif /* EXP_ADDRLIST_PAGE */
+
+	case LYK_VLINKS:	/* list links visited during the current session */
+	    if (handle_LYK_VLINKS(&cmd, &newdoc_link_is_absolute))
+		goto new_cmd;
+	    break;
+
+	case LYK_TOOLBAR:	/* go to Toolbar or Banner in current document */
+	    handle_LYK_TOOLBAR(&try_internal, &force_load, &old_c, real_c);
+	    break;
+
+#if defined(DIRED_SUPPORT) || defined(VMS)
+	case LYK_DIRED_MENU:	/* provide full file management menu */
+	    handle_LYK_DIRED_MENU(&refresh_screen, &old_c, real_c);
+	    break;
+#endif /* DIRED_SUPPORT || VMS */
+
+#ifdef USE_EXTERNALS
+	case LYK_EXTERN_LINK:	/* use external program on url */
+	    handle_LYK_EXTERN_LINK(&refresh_screen);
+	    break;
+	case LYK_EXTERN_PAGE:	/* use external program on current page */
+	    handle_LYK_EXTERN_PAGE(&refresh_screen);
+	    break;
+#endif /* USE_EXTERNALS */
+
+	case LYK_ADD_BOOKMARK:	/* add link to bookmark file */
+	    handle_LYK_ADD_BOOKMARK(&refresh_screen, &old_c, real_c);
+	    break;
+
+	case LYK_VIEW_BOOKMARK:	/* v to view home page */
+	    handle_LYK_VIEW_BOOKMARK(&refresh_screen, &old_c, real_c);
+	    break;
+
+	case LYK_SHELL:	/* (!) shell escape */
+	    handle_LYK_SHELL(&refresh_screen, &old_c, real_c);
+	    break;
+
+	case LYK_DOWNLOAD:
+	    switch (handle_LYK_DOWNLOAD(&cmd, &old_c, real_c)) {
+	    case 1:
+		continue;
+	    case 2:
+		goto new_cmd;
+	    }
+	    break;
+
+#ifdef DIRED_SUPPORT
+	case LYK_UPLOAD:
+	    handle_LYK_UPLOAD();
+	    break;
+#endif /* DIRED_SUPPORT */
+
+	case LYK_TRACE_TOGGLE:	/*  Toggle TRACE mode. */
+	    handle_LYK_TRACE_TOGGLE();
+	    break;
+
+	case LYK_TRACE_LOG:	/*  View TRACE log. */
+	    handle_LYK_TRACE_LOG(&trace_mode_flag);
+	    break;
+
+	case LYK_IMAGE_TOGGLE:
+	    if (handle_LYK_IMAGE_TOGGLE(&cmd))
+		goto new_cmd;
+	    break;
+
+	case LYK_INLINE_TOGGLE:
+	    if (handle_LYK_INLINE_TOGGLE(&cmd))
+		goto new_cmd;
+	    break;
+
+	case LYK_RAW_TOGGLE:
+	    if (handle_LYK_RAW_TOGGLE(&cmd))
+		goto new_cmd;
+	    break;
+
+	case LYK_HEAD:
+	    if (handle_LYK_HEAD(&cmd))
+		goto new_cmd;
+	    break;
+
+	case LYK_TOGGLE_HELP:
+	    handle_LYK_TOGGLE_HELP();
+	    break;
+
+	case LYK_KEYMAP:
+	    handle_LYK_KEYMAP(&vi_keys_flag, &emacs_keys_flag, &old_c, real_c);
+	    break;
+
+	case LYK_JUMP:
+	    if (handle_LYK_JUMP(c, user_input_buffer, &temp, &recall,
+				&FirstURLRecall, &URLNum, &URLTotal, &ch,
+				&old_c, real_c)) {
+		if (do_check_recall(ch, user_input_buffer, &temp, URLTotal,
+				    &URLNum, recall, &FirstURLRecall))
+		    do_check_goto_URL(user_input_buffer, &temp, &force_load);
+	    }
+	    break;
+
+	case LYK_CLEAR_AUTH:
+	    handle_LYK_CLEAR_AUTH(&old_c, real_c);
+	    break;
+
+	case LYK_DO_NOTHING:	/* pretty self explanatory */
+	    break;
+#ifdef SUPPORT_CHDIR
+	case LYK_CHDIR:
+	    handle_LYK_CHDIR();
+	    break;
+#endif
+#ifdef USE_CURSES_PADS
+	case LYK_SHIFT_LEFT:
+	    handle_LYK_SHIFT_LEFT(&refresh_screen, key_count);
+	    break;
+	case LYK_SHIFT_RIGHT:
+	    handle_LYK_SHIFT_RIGHT(&refresh_screen, key_count);
+	    break;
+	case LYK_LINEWRAP_TOGGLE:
+	    if (handle_LYK_LINEWRAP_TOGGLE(&cmd, &refresh_screen))
+		goto new_cmd;
+	    break;
+#endif
+	}			/* end of BIG switch */
+    }
+}
+
+static int are_different(DocInfo *doc1, DocInfo *doc2)
+{
+    char *cp1, *cp2;
+
+    /*
+     * Do we have two addresses?
+     */
+    if (!doc1->address || !doc2->address)
+	return (TRUE);
+
+    /*
+     * Do they differ in the type of request?
+     */
+    if (doc1->isHEAD != doc2->isHEAD)
+	return (TRUE);
+
+    /*
+     * See if the addresses are different, making sure we're not tripped up by
+     * multiple anchors in the the same document from a POST form.  -- FM
+     */
+    cp1 = trimPoundSelector(doc1->address);
+    cp2 = trimPoundSelector(doc2->address);
+    /*
+     * Are the base addresses different?
+     */
+    if (strcmp(doc1->address, doc2->address)) {
+	restorePoundSelector(cp1);
+	restorePoundSelector(cp2);
+	return (TRUE);
+    }
+    restorePoundSelector(cp1);
+    restorePoundSelector(cp2);
+
+    /*
+     * Do the docs have different contents?
+     */
+    if (doc1->post_data) {
+	if (doc2->post_data) {
+	    if (!BINEQ(doc1->post_data, doc2->post_data))
+		return (TRUE);
+	} else
+	    return (TRUE);
+    } else if (doc2->post_data)
+	return (TRUE);
+
+    /*
+     * We'll assume the two documents in fact are the same.
+     */
+    return (FALSE);
+}
+
+/* This determines whether two docs are _physically_ different,
+ * meaning they are "from different files". - kw
+ */
+#ifndef DONT_TRACK_INTERNAL_LINKS
+static int are_phys_different(DocInfo *doc1, DocInfo *doc2)
+{
+    char *cp1, *cp2, *ap1 = doc1->address, *ap2 = doc2->address;
+
+    /*
+     * Do we have two addresses?
+     */
+    if (!doc1->address || !doc2->address)
+	return (TRUE);
+
+    /*
+     * Do they differ in the type of request?
+     */
+    if (doc1->isHEAD != doc2->isHEAD)
+	return (TRUE);
+
+    /*
+     * Skip over possible LYNXIMGMAP parts. - kw
+     */
+    if (isLYNXIMGMAP(doc1->address))
+	ap1 += LEN_LYNXIMGMAP;
+    if (isLYNXIMGMAP(doc2->address))
+	ap2 += LEN_LYNXIMGMAP;
+    /*
+     * If there isn't any real URL in doc2->address, but maybe just
+     * a fragment, doc2 is assumed to be an internal reference in
+     * the same physical document, so return FALSE. - kw
+     */
+    if (*ap2 == '\0' || *ap2 == '#')
+	return (FALSE);
+
+    /*
+     * See if the addresses are different, making sure we're not tripped up by
+     * multiple anchors in the the same document from a POST form.  -- FM
+     */
+    cp1 = trimPoundSelector(doc1->address);
+    cp2 = trimPoundSelector(doc2->address);
+    /*
+     * Are the base addresses different?
+     */
+    if (strcmp(ap1, ap2)) {
+	restorePoundSelector(cp1);
+	restorePoundSelector(cp2);
+	return (TRUE);
+    }
+    restorePoundSelector(cp1);
+    restorePoundSelector(cp2);
+
+    /*
+     * Do the docs have different contents?
+     */
+    if (doc1->post_data) {
+	if (doc2->post_data) {
+	    if (!BINEQ(doc1->post_data, doc2->post_data))
+		return (TRUE);
+	} else
+	    return (TRUE);
+    } else if (doc2->post_data)
+	return (TRUE);
+
+    /*
+     * We'll assume the two documents in fact are the same.
+     */
+    return (FALSE);
+}
+#endif
+
+/*
+ * Utility for freeing the list of goto URLs.  - FM
+ */
+#ifdef LY_FIND_LEAKS
+static void HTGotoURLs_free(void)
+{
+    LYFreeStringList(Goto_URLs);
+    Goto_URLs = NULL;
+}
+#endif
+
+/*
+ * Utility for listing Goto URLs, making any repeated URLs the most current in
+ * the list.  - FM
+ */
+void HTAddGotoURL(char *url)
+{
+    char *copy = NULL;
+    char *old;
+    HTList *cur;
+
+    if (isEmpty(url))
+	return;
+
+    CTRACE((tfp, "HTAddGotoURL %s\n", url));
+    StrAllocCopy(copy, url);
+
+    if (!Goto_URLs) {
+	Goto_URLs = HTList_new();
+#ifdef LY_FIND_LEAKS
+	atexit(HTGotoURLs_free);
+#endif
+	HTList_addObject(Goto_URLs, copy);
+	return;
+    }
+
+    cur = Goto_URLs;
+    while (NULL != (old = (char *) HTList_nextObject(cur))) {
+	if (!strcmp(old, copy)) {
+	    HTList_removeObject(Goto_URLs, old);
+	    FREE(old);
+	    break;
+	}
+    }
+    HTList_addObject(Goto_URLs, copy);
+
+    return;
+}
+
+/*
+ * When help is not on the screen, put a message on the screen to tell the user
+ * other misc info.
+ */
+static void show_main_statusline(const LinkInfo curlink,
+				 int for_what)
+{
+    /*
+     * Make sure form novice lines are replaced.
+     */
+    if (user_mode == NOVICE_MODE && for_what != FOR_INPUT) {
+	noviceline(more_text);
+    }
+
+    if (HTisDocumentSource()) {
+	/*
+	 * Currently displaying HTML source.
+	 */
+	_statusline(SOURCE_HELP);
+
+	/*
+	 * If we are in forms mode then explicitly tell the user what each kind
+	 * of link is.
+	 */
+#ifdef INDICATE_FORMS_MODE_FOR_ALL_LINKS_ON_PAGE
+    } else if (lynx_mode == FORMS_LYNX_MODE && nlinks > 0) {
+#else
+#ifdef NORMAL_NON_FORM_LINK_STATUSLINES_FOR_ALL_USER_MODES
+    } else if (lynx_mode == FORMS_LYNX_MODE && nlinks > 0 &&
+	       !(curlink.type & WWW_LINK_TYPE)) {
+#else
+    } else if (lynx_mode == FORMS_LYNX_MODE && nlinks > 0 &&
+	       !(user_mode == ADVANCED_MODE &&
+		 (curlink.type & WWW_LINK_TYPE))) {
+#endif /* NORMAL_NON_FORM_LINK_STATUSLINES_FOR_ALL_USER_MODES */
+#endif /* INDICATE_FORMS_MODE_FOR_ALL_LINKS_ON_PAGE */
+	if (curlink.type == WWW_FORM_LINK_TYPE) {
+	    show_formlink_statusline(curlink.l_form, for_what);
+	} else {
+	    statusline(NORMAL_LINK_MESSAGE);
+	}
+
+	/*
+	 * Let them know if it's an index -- very rare.
+	 */
+	if (is_www_index) {
+	    const char *indx = gettext("-index-");
+
+	    LYmove(LYlines - 1, LYcolLimit - (int) strlen(indx));
+	    lynx_start_reverse();
+	    LYaddstr(indx);
+	    lynx_stop_reverse();
+	}
+
+    } else if (user_mode == ADVANCED_MODE && nlinks > 0) {
+	/*
+	 * Show the URL or, for some internal links, the fragment
+	 */
+	char *cp = NULL;
+
+	if (curlink.type == WWW_INTERN_LINK_TYPE &&
+	    !isLYNXIMGMAP(curlink.lname)) {
+	    cp = findPoundSelector(curlink.lname);
+	}
+	if (!cp)
+	    cp = curlink.lname;
+	status_link(cp, more_text, is_www_index);
+    } else if (is_www_index && more_text) {
+	char buf[128];
+
+	sprintf(buf, WWW_INDEX_MORE_MESSAGE, key_for_func(LYK_INDEX_SEARCH));
+	_statusline(buf);
+    } else if (is_www_index) {
+	char buf[128];
+
+	sprintf(buf, WWW_INDEX_MESSAGE, key_for_func(LYK_INDEX_SEARCH));
+	_statusline(buf);
+    } else if (more_text) {
+	if (user_mode == NOVICE_MODE)
+	    _statusline(MORE);
+	else
+	    _statusline(MOREHELP);
+    } else {
+	_statusline(HELP);
+    }
+
+    /* turn off cursor since now it's probably on statusline -HV */
+    /* But not if LYShowCursor is on.  -show_cursor may be used as a
+     * workaround to avoid putting the cursor in the last position, for
+     * curses implementations or terminals that cannot deal with that
+     * correctly. - kw */
+    if (!LYShowCursor) {
+	LYHideCursor();
+    }
+}
+
+/*
+ * Public function for redrawing the statusline appropriate for the selected
+ * link.  It should only be called at times when curdoc.link, nlinks, and the
+ * links[] array are valid.  - kw
+ */
+void repaint_main_statusline(int for_what)
+{
+    if (curdoc.link >= 0 && curdoc.link < nlinks)
+	show_main_statusline(links[curdoc.link], for_what);
+}
+
+static void form_noviceline(int disabled)
+{
+    LYmove(LYlines - 2, 0);
+    LYclrtoeol();
+    if (!disabled) {
+	LYaddstr(FORM_NOVICELINE_ONE);
+    }
+    LYmove(LYlines - 1, 0);
+    LYclrtoeol();
+    if (disabled)
+	return;
+    if (EditBinding(FROMASCII('\025')) == LYE_ERASE) {
+	LYaddstr(FORM_NOVICELINE_TWO);
+    } else if (EditBinding(FROMASCII('\025')) == LYE_DELBL) {
+	LYaddstr(FORM_NOVICELINE_TWO_DELBL);
+    } else {
+	char *temp = NULL;
+	char *erasekey = fmt_keys(LYKeyForEditAction(LYE_ERASE), -1);
+
+	if (erasekey) {
+	    HTSprintf0(&temp, FORM_NOVICELINE_TWO_VAR, erasekey);
+	} else {
+	    erasekey = fmt_keys(LYKeyForEditAction(LYE_DELBL), -1);
+	    if (erasekey)
+		HTSprintf0(&temp,
+			   FORM_NOVICELINE_TWO_DELBL_VAR, erasekey);
+	}
+	if (temp) {
+	    LYaddstr(temp);
+	    FREE(temp);
+	}
+	FREE(erasekey);
+    }
+}
+
+static void exit_immediately_with_error_message(int state,
+						BOOLEAN first_file)
+{
+    char *buf = 0;
+    char *buf2 = 0;
+
+    if (first_file) {
+	/* print statusline messages as a hint, if any */
+	LYstatusline_messages_on_exit(&buf2);
+    }
+
+    if (state == NOT_FOUND) {
+	HTSprintf0(&buf, "%s\n%s %s\n",
+		   NonNull(buf2),
+		   gettext("lynx: Can't access startfile"),
+	/*
+	 * hack: if we fail in HTAccess.c
+	 * avoid duplicating URL, oh.
+	 */
+		   (buf2 && strstr(buf2, gettext("Can't Access"))) ?
+		   "" : startfile);
+    }
+
+    if (state == NULLFILE) {
+	HTSprintf0(&buf, "%s\n%s\n%s\n",
+		   NonNull(buf2),
+		   gettext("lynx: Start file could not be found or is not text/html or text/plain"),
+		   gettext("      Exiting..."));
+    }
+
+    FREE(buf2);
+
+    if (!dump_output_immediately)
+	cleanup();
+
+#ifdef UNIX
+    if (dump_output_immediately) {
+	fputs(buf, stderr);
+    } else
+#endif /* UNIX */
+    {
+	SetOutputMode(O_TEXT);
+	fputs(buf, stdout);
+	SetOutputMode(O_BINARY);
+    }
+
+    FREE(buf);
+
+    if (!dump_output_immediately) {
+	exit_immediately(EXIT_FAILURE);
+    }
+    /* else: return(EXIT_FAILURE) in mainloop */
+}
+
+static void status_link(char *curlink_name,
+			BOOLEAN show_more,
+			BOOLEAN show_indx)
+{
+#define MAX_STATUS (LYcolLimit - 1)
+#define MIN_STATUS 0
+    char format[MAX_LINE];
+    int prefix = 0;
+    int length;
+
+    *format = 0;
+    if (show_more && !nomore) {
+	sprintf(format, "%.*s ",
+		(int) (sizeof(format) - 2),
+		gettext("-more-"));
+	prefix = (int) strlen(format);
+    }
+    if (show_indx) {
+	sprintf(format + prefix, "%.*s ",
+		((int) sizeof(format) - prefix - 2),
+		gettext("-index-"));
+    }
+    prefix = (int) strlen(format);
+    length = (int) strlen(curlink_name);
+
+    if (prefix > MAX_STATUS || prefix >= MAX_LINE - 1) {
+	_user_message("%s", format);	/* no room for url */
+    } else {
+	sprintf(format + prefix, "%%.%ds", MAX_STATUS - prefix);
+
+	if ((length + prefix > MAX_STATUS) && long_url_ok) {
+	    char *buf = NULL;
+	    int cut_from_pos;
+	    int cut_to_pos;
+	    int n;
+
+	    StrAllocCopy(buf, curlink_name);
+	    /*
+	     * Scan to find the final leaf of the URL.  Ignore trailing '/'.
+	     */
+	    for (cut_to_pos = length - 2;
+		 (cut_to_pos > 0) && (buf[cut_to_pos] != '/');
+		 cut_to_pos--) ;
+	    /*
+	     * Jump back to the next leaf to remove.
+	     */
+	    for (cut_from_pos = cut_to_pos - 4;
+		 (cut_from_pos > 0) && ((buf[cut_from_pos] != '/')
+					|| ((prefix + cut_from_pos
+					     + 4
+					     + (length - cut_to_pos)) >= MAX_STATUS));
+		 cut_from_pos--) ;
+	    /*
+	     * Replace some leaves to '...', if possible, and put the final
+	     * leaf at the end.  We assume that one can recognize the link from
+	     * at least MIN_STATUS characters.
+	     */
+	    if (cut_from_pos > MIN_STATUS) {
+		for (n = 1; n <= 3; n++)
+		    buf[cut_from_pos + n] = '.';
+		for (n = 0; cut_to_pos + n <= length; n++)
+		    buf[cut_from_pos + 4 + n] = buf[cut_to_pos + n];
+	    }
+	    _user_message(format, buf);
+	    CTRACE((tfp, "lastline = %s\n", buf));	/* don't forget to erase me */
+	    FREE(buf);
+	} else {		/* show (possibly truncated) url */
+	    _user_message(format, curlink_name);
+	}
+    }
+}
+
+const char *LYDownLoadAddress(void)
+{
+    return NonNull(newdoc.address);
+}
diff --git a/src/LYMainLoop.h b/src/LYMainLoop.h
new file mode 100644
index 00000000..bd2926a3
--- /dev/null
+++ b/src/LYMainLoop.h
@@ -0,0 +1,34 @@
+#ifndef LYMAINLOOP_H
+#define LYMAINLOOP_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef DISP_PARTIAL
+    extern BOOL LYMainLoop_pageDisplay(int line_num);
+#endif
+
+    extern BOOLEAN LYOpenTraceLog(void);
+    extern const char *LYDownLoadAddress(void);
+    extern int LYGetNewline(void);
+    extern int mainloop(void);
+    extern void HTAddGotoURL(char *url);
+    extern void LYChgNewline(int adjust);
+    extern void LYCloseTracelog(void);
+    extern void LYSetNewline(int value);
+    extern void handle_LYK_TRACE_TOGGLE(void);
+    extern void handle_LYK_WHEREIS(int cmd, BOOLEAN *refresh_screen);
+    extern void repaint_main_statusline(int for_what);
+
+#ifdef SUPPORT_CHDIR
+    extern void handle_LYK_CHDIR(void);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYMAINLOOP_H */
diff --git a/src/LYMap.c b/src/LYMap.c
new file mode 100644
index 00000000..edca37eb
--- /dev/null
+++ b/src/LYMap.c
@@ -0,0 +1,658 @@
+/*
+ * $LynxId: LYMap.c,v 1.37 2009/01/01 22:30:15 tom Exp $
+ *			Lynx Client-side Image MAP Support	       LYMap.c
+ *			==================================
+ *
+ *	Author: FM	Foteos Macrides (macrides@sci.wfbr.edu)
+ *
+ */
+
+#include <HTUtils.h>
+#include <HTTP.h>
+#include <HTAnchor.h>
+#include <HTAccess.h>
+#include <HTFormat.h>
+#include <HTParse.h>
+#include <HTAlert.h>
+#include <LYUtils.h>
+#include <LYMap.h>
+#include <GridText.h>
+#include <LYGlobalDefs.h>
+#include <LYKeymap.h>
+#include <LYCharUtils.h>
+#include <LYCharSets.h>
+#include <LYStrings.h>
+
+#ifdef DIRED_SUPPORT
+#include <LYUpload.h>
+#include <LYLocal.h>
+#endif
+
+#include <LYexit.h>
+#include <LYLeaks.h>
+
+#define NO_MAP_TITLE "[USEMAP]"
+
+typedef struct _LYMapElement {
+    char *address;
+    char *title;
+#ifndef DONT_TRACK_INTERNAL_LINKS
+    BOOL intern_flag;
+#endif
+} LYMapElement;
+
+typedef struct _LYImageMap {
+    char *address;
+    char *title;
+    HTList *elements;
+} LYImageMap;
+
+struct _HTStream {
+    HTStreamClass *isa;
+};
+
+static HTList *LynxMaps = NULL;
+
+BOOL LYMapsOnly = FALSE;
+
+/*
+ * Utility for freeing a list of MAPs.
+ */
+void ImageMapList_free(HTList *theList)
+{
+    LYImageMap *map;
+    LYMapElement *element;
+    HTList *cur = theList;
+    HTList *current;
+
+    if (!cur)
+	return;
+
+    while (NULL != (map = (LYImageMap *) HTList_nextObject(cur))) {
+	FREE(map->address);
+	FREE(map->title);
+	if (map->elements) {
+	    current = map->elements;
+	    while (NULL !=
+		   (element = (LYMapElement *) HTList_nextObject(current))) {
+		FREE(element->address);
+		FREE(element->title);
+		FREE(element);
+	    }
+	    HTList_delete(map->elements);
+	    map->elements = NULL;
+	}
+	FREE(map);
+    }
+    HTList_delete(theList);
+    return;
+}
+
+#ifdef LY_FIND_LEAKS
+/*
+ * Utility for freeing the global list of MAPs.  - kw
+ */
+static void LYLynxMaps_free(void)
+{
+    ImageMapList_free(LynxMaps);
+    LynxMaps = NULL;
+    return;
+}
+#endif /* LY_FIND_LEAKS */
+
+/*
+ * We keep two kinds of lists:
+ * - A global list (LynxMaps) shared by MAPs from all documents that
+ *   do not have POST data.
+ * - For each response to a POST which contains MAPs, a list specific
+ *   to this combination of URL and post_data.  It is kept in the
+ *   HTParentAnchor structure and is freed when the document is removed
+ *   from memory, in the course of normal removal of anchors.
+ *   MAPs from POST responses can only be accessed via internal links,
+ *   i.e., from within the same document (with the same post_data).
+ *   The notion of "same document" is extended, so that LYNXIMGMAP:
+ *   and List Page screens are logically part of the document on which
+ *   they are based. - kw
+ *
+ * If DONT_TRACK_INTERNAL_LINKS is defined, only the global list will be used
+ * for all MAPs.
+ *
+ */
+
+/*
+ * Utility for creating an LYImageMap list, if it doesn't exist already, adding
+ * LYImageMap entry structures if needed, and removing any LYMapElements in a
+ * pre-existing LYImageMap entry so that it will have only those from AREA tags
+ * for the current analysis of MAP element content.  - FM
+ */
+BOOL LYAddImageMap(char *address,
+		   char *title,
+		   HTParentAnchor *node_anchor)
+{
+    LYImageMap *tmp = NULL;
+    LYImageMap *old = NULL;
+    HTList *cur = NULL;
+    HTList *theList = NULL;
+    HTList *curele = NULL;
+    LYMapElement *ele = NULL;
+
+    if (isEmpty(address))
+	return FALSE;
+    if (!(node_anchor && node_anchor->address))
+	return FALSE;
+
+    /*
+     * Set theList to either the global LynxMaps list or, if we are associated
+     * with post data, the specific list.  The list is created if it doesn't
+     * already exist.  - kw
+     */
+#ifndef DONT_TRACK_INTERNAL_LINKS
+    if (node_anchor->post_data) {
+	/*
+	 * We are handling a MAP element found while parsing node_anchor's
+	 * stream of data, and node_anchor has post_data associated and should
+	 * therefore represent a POST response, so use the specific list.  - kw
+	 */
+	theList = node_anchor->imaps;
+	if (!theList) {
+	    theList = node_anchor->imaps = HTList_new();
+	}
+    } else
+#endif
+    {
+	if (!LynxMaps) {
+	    LynxMaps = HTList_new();
+#ifdef LY_FIND_LEAKS
+	    atexit(LYLynxMaps_free);
+#endif
+	}
+	theList = LynxMaps;
+    }
+
+    if (theList) {
+	cur = theList;
+	while (NULL != (old = (LYImageMap *) HTList_nextObject(cur))) {
+	    if (old->address == 0)	/* shouldn't happen */
+		continue;
+	    if (!strcmp(old->address, address)) {
+		FREE(old->address);
+		FREE(old->title);
+		if (old->elements) {
+		    curele = old->elements;
+		    while (NULL !=
+			   (ele = (LYMapElement *) HTList_nextObject(curele))) {
+			FREE(ele->address);
+			FREE(ele->title);
+			FREE(ele);
+		    }
+		    HTList_delete(old->elements);
+		    old->elements = NULL;
+		}
+		break;
+	    }
+	}
+    }
+
+    tmp = (old != NULL) ?
+	old : typecalloc(LYImageMap);
+    if (tmp == NULL) {
+	outofmem(__FILE__, "LYAddImageMap");
+	return FALSE;
+    }
+    StrAllocCopy(tmp->address, address);
+    if (non_empty(title))
+	StrAllocCopy(tmp->title, title);
+    if (tmp != old)
+	HTList_addObject(theList, tmp);
+    return TRUE;
+}
+
+/*
+ * Utility for adding LYMapElement's to LYImageMap's
+ * in the appropriate list. - FM
+ */
+BOOL LYAddMapElement(char *map,
+		     char *address,
+		     char *title,
+		     HTParentAnchor *node_anchor,
+		     BOOL intern_flag GCC_UNUSED)
+{
+    LYMapElement *tmp = NULL;
+    LYImageMap *theMap = NULL;
+    HTList *theList = NULL;
+    HTList *cur = NULL;
+
+    if (isEmpty(map) || isEmpty(address))
+	return FALSE;
+    if (!(node_anchor && node_anchor->address))
+	return FALSE;
+
+    /*
+     * Set theList to either the global LynxMaps list or, if we are associated
+     * with post data, the specific list.  The list should already exist, since
+     * this function is only called if the AREA tag we are handling was within
+     * a MAP element in node_anchor's stream of data, so that LYAddImageMap has
+     * been called.  - kw
+     */
+#ifndef DONT_TRACK_INTERNAL_LINKS
+    if (node_anchor->post_data) {
+	/*
+	 * We are handling an AREA tag found while parsing node_anchor's stream
+	 * of data, and node_anchor has post_data associated and should
+	 * therefore represent a POST response, so use the specific list.  - kw
+	 */
+	theList = node_anchor->imaps;
+	if (!theList) {
+	    return FALSE;
+	}
+    } else
+#endif
+    {
+	if (!LynxMaps)
+	    LYAddImageMap(map, NULL, node_anchor);
+	theList = LynxMaps;
+    }
+
+    cur = theList;
+    while (NULL != (theMap = (LYImageMap *) HTList_nextObject(cur))) {
+	if (!strcmp(theMap->address, map)) {
+	    break;
+	}
+    }
+    if (!theMap)
+	return FALSE;
+    if (!theMap->elements)
+	theMap->elements = HTList_new();
+    cur = theMap->elements;
+    while (NULL != (tmp = (LYMapElement *) HTList_nextObject(cur))) {
+	if (!strcmp(tmp->address, address)) {
+	    FREE(tmp->address);
+	    FREE(tmp->title);
+	    HTList_removeObject(theMap->elements, tmp);
+	    FREE(tmp);
+	    break;
+	}
+    }
+
+    tmp = typecalloc(LYMapElement);
+    if (tmp == NULL) {
+	perror("Out of memory in LYAddMapElement");
+	return FALSE;
+    }
+    StrAllocCopy(tmp->address, address);
+    if (non_empty(title))
+	StrAllocCopy(tmp->title, title);
+    else
+	StrAllocCopy(tmp->title, address);
+#ifndef DONT_TRACK_INTERNAL_LINKS
+    tmp->intern_flag = intern_flag;
+#endif
+    HTList_appendObject(theMap->elements, tmp);
+
+    CTRACE((tfp,
+	    "LYAddMapElement\n\tmap     %s\n\taddress %s\n\ttitle   %s)\n",
+	    NonNull(map), NonNull(address), NonNull(title)));
+
+    return TRUE;
+}
+
+/*
+ * Utility for checking whether an LYImageMap entry with a given address
+ * already exists in the LynxMaps structure.  - FM
+ */
+BOOL LYHaveImageMap(char *address)
+{
+    LYImageMap *Map;
+    HTList *cur = LynxMaps;
+
+    if (!(cur && non_empty(address)))
+	return FALSE;
+
+    while (NULL != (Map = (LYImageMap *) HTList_nextObject(cur))) {
+	if (!strcmp(Map->address, address)) {
+	    return TRUE;
+	}
+    }
+
+    return FALSE;
+}
+
+/*
+ * Fills in a DocAddress structure for getting the HTParentAnchor of the
+ * underlying resource.  ALso returns a pointer to that anchor in
+ * *punderlying if we are dealing with POST data.  - kw
+ *
+ * address  is the address of the underlying resource, i.e., the one
+ *	    containing the MAP element, the MAP's name appended as
+ *	    fragment is ignored.
+ * anAnchor is the LYNXIMGMAP: anchor; if it is associated with POST
+ *	    data, we want the specific list, otherwise the global list.
+ */
+static void fill_DocAddress(DocAddress *wwwdoc,
+			    const char *address,
+			    HTParentAnchor *anAnchor,
+			    HTParentAnchor **punderlying)
+{
+    char *doc_address = NULL;
+    HTParentAnchor *underlying;
+
+    StrAllocCopy(doc_address, address);
+    if (anAnchor && anAnchor->post_data) {
+	wwwdoc->address = doc_address;
+	wwwdoc->post_data = anAnchor->post_data;
+	wwwdoc->post_content_type = anAnchor->post_content_type;
+	wwwdoc->bookmark = NULL;
+	wwwdoc->isHEAD = FALSE;
+	wwwdoc->safe = FALSE;
+	underlying = HTAnchor_findAddress(wwwdoc);
+	if (underlying->safe)
+	    wwwdoc->safe = TRUE;
+	if (punderlying)
+	    *punderlying = underlying;
+    } else {
+	wwwdoc->address = doc_address;
+	wwwdoc->post_data = NULL;
+	wwwdoc->post_content_type = NULL;
+	wwwdoc->bookmark = NULL;
+	wwwdoc->isHEAD = FALSE;
+	wwwdoc->safe = FALSE;
+	if (punderlying)
+	    *punderlying = NULL;
+    }
+}
+
+/*
+ * Get the appropriate list for creating a LYNXIMGMAP:  pseudo- document: 
+ * either the global list (LynxMaps), or the specific list if a List Page for a
+ * POST response is requested.  Also fill in the DocAddress structure etc.  by
+ * calling fill_DocAddress().
+ *
+ * address is the address of the underlying resource, i.e., the one
+ *	   containing the MAP element, the MAP's name appended as
+ *	   fragment is ignored.
+ * anchor  is the LYNXIMGMAP: anchor for which LYLoadIMGmap() is
+ *	   requested; if it is associated with POST data, we want the
+ *	   specific list for this combination of address+post_data.
+ *
+ * if DONT_TRACK_INTERNAL_LINKS is defined, the Anchor passed to
+ * LYLoadIMGmap() will never have post_data, so that the global list
+ * will be used. - kw
+ */
+static HTList *get_the_list(DocAddress *wwwdoc,
+			    const char *address,
+			    HTParentAnchor *anchor,
+			    HTParentAnchor **punderlying)
+{
+    if (anchor && anchor->post_data) {
+	fill_DocAddress(wwwdoc, address, anchor, punderlying);
+	if (non_empty(punderlying))
+	    return (*punderlying)->imaps;
+	return anchor->imaps;
+    } else {
+	fill_DocAddress(wwwdoc, address, NULL, punderlying);
+	return LynxMaps;
+    }
+}
+
+/*	LYLoadIMGmap - F.Macrides (macrides@sci.wfeb.edu)
+ *	------------
+ *	Create a text/html stream with a list of links
+ *	for HyperText References in AREAs of a MAP.
+ */
+
+static int LYLoadIMGmap(const char *arg,
+			HTParentAnchor *anAnchor,
+			HTFormat format_out,
+			HTStream *sink)
+{
+    HTFormat format_in = WWW_HTML;
+    HTStream *target = NULL;
+    char *buf = NULL;
+    LYMapElement *tmp = NULL;
+    LYImageMap *theMap = NULL;
+    char *MapTitle = NULL;
+    char *MapAddress = NULL;
+    HTList *theList;
+    HTList *cur = NULL;
+    const char *address = NULL;
+    char *cp = NULL;
+    DocAddress WWWDoc;
+    HTParentAnchor *underlying;
+    BOOL old_cache_setting = LYforce_no_cache;
+    BOOL old_reloading = reloading;
+    HTFormat old_format_out = HTOutputFormat;
+
+    if (isLYNXIMGMAP(arg)) {
+	address = (arg + LEN_LYNXIMGMAP);
+    }
+    if (!(address && strchr(address, ':'))) {
+	HTAlert(MISDIRECTED_MAP_REQUEST);
+	return (HT_NOT_LOADED);
+    }
+
+    theList = get_the_list(&WWWDoc, address, anAnchor, &underlying);
+    if (WWWDoc.safe)
+	anAnchor->safe = TRUE;
+
+    if (!theList) {
+	if (anAnchor->post_data && !WWWDoc.safe &&
+	    ((underlying && underlying->document && !LYforce_no_cache) ||
+	     HTConfirm(CONFIRM_POST_RESUBMISSION) != TRUE)) {
+	    HTAlert(FAILED_MAP_POST_REQUEST);
+	    return (HT_NOT_LOADED);
+	}
+	LYforce_no_cache = TRUE;
+	reloading = TRUE;
+	HTOutputFormat = WWW_PRESENT;
+	LYMapsOnly = TRUE;
+	if (!HTLoadAbsolute(&WWWDoc)) {
+	    LYforce_no_cache = old_cache_setting;
+	    reloading = old_reloading;
+	    HTOutputFormat = old_format_out;
+	    LYMapsOnly = FALSE;
+	    HTAlert(MAP_NOT_ACCESSIBLE);
+	    return (HT_NOT_LOADED);
+	}
+	LYforce_no_cache = old_cache_setting;
+	reloading = old_reloading;
+	HTOutputFormat = old_format_out;
+	LYMapsOnly = FALSE;
+	theList = get_the_list(&WWWDoc, address, anAnchor, &underlying);
+    }
+
+    if (!theList) {
+	HTAlert(MAPS_NOT_AVAILABLE);
+	return (HT_NOT_LOADED);
+    }
+
+    cur = theList;
+    while (NULL != (theMap = (LYImageMap *) HTList_nextObject(cur))) {
+	if (!strcmp(theMap->address, address)) {
+	    break;
+	}
+    }
+    if (theMap && HTList_count(theMap->elements) == 0) {
+	/*
+	 * We found a MAP without any usable AREA.  Fake a redirection to the
+	 * address with fragment.  We do this even for post data (internal link
+	 * within a document with post data) if it will not result in an
+	 * unwanted network request.  - kw
+	 */
+	if (!anAnchor->post_data) {
+	    StrAllocCopy(redirecting_url, address);
+	    return (HT_REDIRECTING);
+	} else if (WWWDoc.safe ||
+		   (underlying->document && !anAnchor->document &&
+		    (LYinternal_flag || LYoverride_no_cache))) {
+	    StrAllocCopy(redirecting_url, address);
+	    redirect_post_content = TRUE;
+	    return (HT_REDIRECTING);
+	}
+    }
+    if (!(theMap && theMap->elements)) {
+	if (anAnchor->post_data && !WWWDoc.safe &&
+	    ((underlying && underlying->document && !LYforce_no_cache) ||
+	     HTConfirm(CONFIRM_POST_RESUBMISSION) != TRUE)) {
+	    HTAlert(FAILED_MAP_POST_REQUEST);
+	    return (HT_NOT_LOADED);
+	}
+	LYforce_no_cache = TRUE;
+	reloading = TRUE;
+	HTOutputFormat = WWW_PRESENT;
+	LYMapsOnly = TRUE;
+	if (!HTLoadAbsolute(&WWWDoc)) {
+	    LYforce_no_cache = old_cache_setting;
+	    reloading = old_reloading;
+	    HTOutputFormat = old_format_out;
+	    LYMapsOnly = FALSE;
+	    HTAlert(MAP_NOT_ACCESSIBLE);
+	    return (HT_NOT_LOADED);
+	}
+	LYforce_no_cache = old_cache_setting;
+	reloading = old_reloading;
+	HTOutputFormat = old_format_out;
+	LYMapsOnly = FALSE;
+	cur = get_the_list(&WWWDoc, address, anAnchor, &underlying);
+	while (NULL != (theMap = (LYImageMap *) HTList_nextObject(cur))) {
+	    if (!strcmp(theMap->address, address)) {
+		break;
+	    }
+	}
+	if (!(theMap && theMap->elements)) {
+	    HTAlert(MAP_NOT_AVAILABLE);
+	    return (HT_NOT_LOADED);
+	}
+    }
+#ifdef DONT_TRACK_INTERNAL_LINKS
+    anAnchor->no_cache = TRUE;
+#endif
+
+    target = HTStreamStack(format_in,
+			   format_out,
+			   sink, anAnchor);
+
+    if (!target || target == NULL) {
+	HTSprintf0(&buf, CANNOT_CONVERT_I_TO_O,
+		   HTAtom_name(format_in), HTAtom_name(format_out));
+	HTAlert(buf);
+	FREE(buf);
+	return (HT_NOT_LOADED);
+    }
+
+    if (non_empty(theMap->title)) {
+	StrAllocCopy(MapTitle, theMap->title);
+    } else if (non_empty(anAnchor->title)) {
+	StrAllocCopy(MapTitle, anAnchor->title);
+    } else if (non_empty(LYRequestTitle) &&
+	       strcasecomp(LYRequestTitle, NO_MAP_TITLE)) {
+	StrAllocCopy(MapTitle, LYRequestTitle);
+    } else if ((cp = strchr(address, '#')) != NULL) {
+	StrAllocCopy(MapTitle, (cp + 1));
+    }
+    if (isEmpty(MapTitle)) {
+	StrAllocCopy(MapTitle, NO_MAP_TITLE);
+    } else {
+	LYEntify(&MapTitle, TRUE);
+    }
+
+#define PUTS(buf)    (*target->isa->put_block)(target, buf, (int) strlen(buf))
+
+    HTSprintf0(&buf, "<html>\n<head>\n");
+    PUTS(buf);
+    HTSprintf0(&buf, "<META %s content=\"text/html;charset=%s\">\n",
+	       "http-equiv=\"content-type\"",
+	       LYCharSet_UC[current_char_set].MIMEname);
+    PUTS(buf);
+    /*
+     * This page is a list of titles and anchors for them.  Since titles
+     * already passed SGML/HTML stage they are converted to current_char_set. 
+     * That is why we insist on META charset for this page.
+     */
+    HTSprintf0(&buf, "<title>%s</title>\n", MapTitle);
+    PUTS(buf);
+    HTSprintf0(&buf, "</head>\n<body>\n");
+    PUTS(buf);
+
+    HTSprintf0(&buf, "<h1><em>%s</em></h1>\n", MapTitle);
+    PUTS(buf);
+
+    StrAllocCopy(MapAddress, address);
+    LYEntify(&MapAddress, FALSE);
+    HTSprintf0(&buf, "<h2><em>MAP:</em>&nbsp;%s</h2>\n", MapAddress);
+    PUTS(buf);
+
+    HTSprintf0(&buf, "<%s compact>\n", ((keypad_mode == NUMBERS_AS_ARROWS) ?
+					"ol" : "ul"));
+    PUTS(buf);
+    cur = theMap->elements;
+    while (NULL != (tmp = (LYMapElement *) HTList_nextObject(cur))) {
+	StrAllocCopy(MapAddress, tmp->address);
+	LYEntify(&MapAddress, FALSE);
+	PUTS("<li><a href=\"");
+	PUTS(MapAddress);
+	PUTS("\"");
+#ifndef DONT_TRACK_INTERNAL_LINKS
+	if (tmp->intern_flag)
+	    PUTS(" TYPE=\"internal link\"");
+#endif
+	PUTS("\n>");
+	LYformTitle(&MapTitle, tmp->title);
+	LYEntify(&MapTitle, TRUE);
+	PUTS(MapTitle);
+	PUTS("</a>\n");
+    }
+    HTSprintf0(&buf, "</%s>\n</body>\n</html>\n",
+	       ((keypad_mode == NUMBERS_AS_ARROWS)
+		? "ol"
+		: "ul"));
+    PUTS(buf);
+
+    (*target->isa->_free) (target);
+    FREE(MapAddress);
+    FREE(MapTitle);
+    FREE(buf);
+    return (HT_LOADED);
+}
+
+void LYPrintImgMaps(FILE *fp)
+{
+    const char *only = HTLoadedDocumentURL();
+    unsigned only_len = strlen(only);
+    HTList *outer = LynxMaps;
+    HTList *inner;
+    LYImageMap *map;
+    LYMapElement *elt;
+    int count;
+
+    if (HTList_count(outer) > 0) {
+	while (NULL != (map = (LYImageMap *) HTList_nextObject(outer))) {
+	    if (only_len != 0) {
+		if (strncmp(only, map->address, only_len)
+		    || (map->address[only_len] != '\0'
+			&& map->address[only_len] != '#')) {
+		    continue;
+		}
+	    }
+	    fprintf(fp, "\n%s\n", isEmpty(map->title) ? NO_MAP_TITLE : map->title);
+	    fprintf(fp, "%s\n", map->address);
+	    inner = map->elements;
+	    count = 0;
+	    while (NULL != (elt = (LYMapElement *) HTList_nextObject(inner))) {
+		fprintf(fp, "%4d. %s", ++count, elt->address);
+#ifndef DONT_TRACK_INTERNAL_LINKS
+		if (elt->intern_flag)
+		    fprintf(fp, " TYPE=\"internal link\"");
+#endif
+		fprintf(fp, "\n");
+	    }
+	}
+    }
+}
+
+#ifdef GLOBALDEF_IS_MACRO
+#define _LYIMGMAP_C_GLOBALDEF_1_INIT { "LYNXIMGMAP", LYLoadIMGmap, 0}
+GLOBALDEF(HTProtocol, LYLynxIMGmap, _LYIMGMAP_C_GLOBALDEF_1_INIT);
+#else
+GLOBALDEF HTProtocol LYLynxIMGmap =
+{"LYNXIMGMAP", LYLoadIMGmap, 0};
+#endif /* GLOBALDEF_IS_MACRO */
diff --git a/src/LYMap.h b/src/LYMap.h
new file mode 100644
index 00000000..28a60178
--- /dev/null
+++ b/src/LYMap.h
@@ -0,0 +1,28 @@
+#ifndef LYMAP_H
+#define LYMAP_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#include <HTList.h>
+#include <HTAnchor.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    extern BOOL LYMapsOnly;
+
+    extern void ImageMapList_free(HTList *list);
+    extern void LYPrintImgMaps(FILE *fp);
+    extern BOOL LYAddImageMap(char *address, char *title,
+			      HTParentAnchor *node_anchor);
+    extern BOOL LYAddMapElement(char *map, char *address, char *title,
+				HTParentAnchor *node_anchor,
+				BOOL intern_flag);
+    extern BOOL LYHaveImageMap(char *address);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYMAP_H */
diff --git a/src/LYNews.c b/src/LYNews.c
new file mode 100644
index 00000000..afebe0ee
--- /dev/null
+++ b/src/LYNews.c
@@ -0,0 +1,509 @@
+/*
+ * $LynxId: LYNews.c,v 1.54 2009/01/01 23:09:57 tom Exp $
+ */
+#include <HTUtils.h>
+#ifndef DISABLE_NEWS
+#include <HTParse.h>
+#include <HTAccess.h>
+#include <HTCJK.h>
+#include <HTAlert.h>
+#include <LYCurses.h>
+#include <LYSignal.h>
+#include <LYStructs.h>
+#include <LYUtils.h>
+#include <LYClean.h>
+#include <LYStrings.h>
+#include <LYHistory.h>
+#include <GridText.h>
+#include <LYCharSets.h>
+#include <LYNews.h>
+#include <LYEdit.h>
+
+#include <LYGlobalDefs.h>
+
+#include <LYLeaks.h>
+
+/*
+ *  Global variable for async i/o.
+ */
+BOOLEAN term_message = FALSE;
+static void terminate_message(int sig);
+
+static BOOLEAN message_has_content(const char *filename,
+				   BOOLEAN *nonspaces)
+{
+    FILE *fp;
+    char *buffer = NULL;
+    BOOLEAN in_headers = TRUE;
+
+    *nonspaces = FALSE;
+
+    if (!filename || (fp = fopen(filename, "r")) == NULL) {
+	CTRACE((tfp, "Failed to open file %s for reading!\n",
+		NONNULL(filename)));
+	return FALSE;
+    }
+    while (LYSafeGets(&buffer, fp) != NULL) {
+	char *cp = buffer;
+	char firstnonblank = '\0';
+
+	LYTrimNewline(cp);
+	for (; *cp; cp++) {
+	    if (!firstnonblank && isgraph(UCH(*cp))) {
+		firstnonblank = *cp;
+	    } else if (!isspace(UCH(*cp))) {
+		*nonspaces = TRUE;
+	    }
+	}
+	if (firstnonblank && firstnonblank != '>') {
+	    if (!in_headers) {
+		LYCloseInput(fp);
+		FREE(buffer);
+		return TRUE;
+	    }
+	}
+	if (!firstnonblank) {
+	    in_headers = FALSE;
+	}
+    }
+    FREE(buffer);
+    LYCloseInput(fp);
+    return FALSE;
+}
+
+/*
+ *  This function is called from HTLoadNews() to have the user
+ *  create a file with news headers and a body for posting of
+ *  a new message (based on a newspost://nntp_host/newsgroups
+ *  or snewspost://secure_nntp_host/newsgroups URL), or to post
+ *  a followup (based on a newsreply://nntp_host/newsgroups or
+ *  snewsreply://secure_nntp_host/newsgroups URL). The group
+ *  or comma-separated list of newsgroups is passed without
+ *  a lead slash, and followup is TRUE for newsreply or
+ *  snewsreply URLs.  - FM
+ */
+char *LYNewsPost(char *newsgroups,
+		 BOOLEAN followup)
+{
+    char user_input[MAX_LINE];
+    char CJKinput[MAX_LINE];
+    char *cp = NULL;
+    const char *kp = NULL;
+    int c = 0;			/* user input */
+    int len;
+    FILE *fd = NULL;
+    char my_tempfile[LY_MAXPATH];
+    FILE *fc = NULL;
+    char CJKfile[LY_MAXPATH];
+    char *postfile = NULL;
+    char *NewsGroups = NULL;
+    char *References = NULL;
+    char *org = NULL;
+    FILE *fp = NULL;
+    BOOLEAN nonempty = FALSE;
+    BOOLEAN nonspaces = FALSE;
+
+    /*
+     * Make sure a non-zero length newspost, newsreply, snewspost or snewsreply
+     * path was sent to us.  - FM
+     */
+    if (isEmpty(newsgroups))
+	return (postfile);
+
+    /*
+     * Return immediately if we do get called, maybe by some quirk of HTNews.c,
+     * when we shouldn't.  - kw
+     */
+    if (no_newspost)
+	return (postfile);
+
+    /*
+     * Open a temporary file for the headers and message body.  - FM
+     */
+#ifdef __DJGPP__
+    if ((fd = LYOpenTemp(my_tempfile, HTML_SUFFIX, BIN_W)) == NULL)
+#else
+    if ((fd = LYOpenTemp(my_tempfile, HTML_SUFFIX, "w")) == NULL)
+#endif /* __DJGPP__ */
+    {
+	HTAlert(CANNOT_OPEN_TEMP);
+	return (postfile);
+    }
+
+    /*
+     * If we're using a Japanese display character set, open a temporary file
+     * for a conversion to JIS.  - FM
+     */
+    CJKfile[0] = '\0';
+    if (current_char_set == UCGetLYhndl_byMIME("euc-jp") ||
+	current_char_set == UCGetLYhndl_byMIME("shift_jis")) {
+	if ((fc = LYOpenTemp(CJKfile, HTML_SUFFIX, "w")) == NULL) {
+	    HTAlert(CANNOT_OPEN_TEMP);
+	    LYRemoveTemp(my_tempfile);
+	    return (postfile);
+	}
+    }
+
+    /*
+     * The newsgroups could be a comma-seperated list.  It need not have
+     * spaces, but deal with any that may also have been hex escaped.  - FM
+     */
+    StrAllocCopy(NewsGroups, newsgroups);
+    if ((cp = strstr(NewsGroups, ";ref="))) {
+	*cp = '\0';
+	cp += 5;
+	if (*cp == '<') {
+	    StrAllocCopy(References, cp);
+	} else {
+	    StrAllocCopy(References, "<");
+	    StrAllocCat(References, cp);
+	    StrAllocCat(References, ">");
+	}
+	HTUnEscape(References);
+	if (!((cp = strchr(References, '@')) && cp > References + 1 &&
+	      isalnum(UCH(cp[1])))) {
+	    FREE(References);
+	}
+    }
+    HTUnEscape(NewsGroups);
+    if (!*NewsGroups) {
+	LYCloseTempFP(fd);	/* Close the temp file. */
+	goto cleanup;
+    }
+
+    /*
+     * Allow ^C to cancel the posting, i.e., don't let SIGINTs exit Lynx.
+     */
+    signal(SIGINT, terminate_message);
+    term_message = FALSE;
+
+    /*
+     * Show the list of newsgroups.  - FM
+     */
+    LYclear();
+    LYmove(2, 0);
+    scrollok(LYwin, TRUE);	/* Enable scrolling. */
+    LYaddstr(gettext("You will be posting to:"));
+    LYaddstr("\n\t");
+    LYaddstr(NewsGroups);
+    LYaddch('\n');
+
+    /*
+     * Get the mail address for the From header, offering personal_mail_address
+     * as default.
+     */
+    LYaddstr(gettext("\n\n Please provide your mail address for the From: header\n"));
+    sprintf(user_input, "From: %.*s", (int) sizeof(user_input) - 8,
+	    NonNull(personal_mail_address));
+    if (LYgetstr(user_input, VISIBLE,
+		 sizeof(user_input), NORECALL) < 0 ||
+	term_message) {
+	HTInfoMsg(NEWS_POST_CANCELLED);
+	LYCloseTempFP(fd);	/* Close the temp file. */
+	scrollok(LYwin, FALSE);	/* Stop scrolling.      */
+	goto cleanup;
+    }
+    fprintf(fd, "%s\n", user_input);
+
+    /*
+     * Get the Subject header, offering the current document's title as the
+     * default if this is a followup rather than a new post.  - FM
+     */
+    LYaddstr(gettext("\n\n Please provide or edit the Subject: header\n"));
+    strcpy(user_input, "Subject: ");
+    if ((followup == TRUE && nhist > 0) &&
+	(kp = HText_getTitle()) != NULL) {
+	/*
+	 * Add the default subject.
+	 */
+	kp = LYSkipCBlanks(kp);
+#ifdef CJK_EX			/* 1998/05/15 (Fri) 09:10:38 */
+	if (HTCJK == JAPANESE) {
+	    CJKinput[0] = '\0';
+	    switch (kanji_code) {
+	    case EUC:
+		TO_EUC((const unsigned char *) kp, (unsigned char *) CJKinput);
+		kp = CJKinput;
+		break;
+	    case SJIS:
+		TO_SJIS((const unsigned char *) kp, (unsigned char *) CJKinput);
+		kp = CJKinput;
+		break;
+	    default:
+		break;
+	    }
+	}
+#endif
+	if (strncasecomp(kp, "Re:", 3)) {
+	    strcat(user_input, "Re: ");
+	}
+	len = (int) strlen(user_input);
+	LYstrncpy(user_input + len, kp, (int) sizeof(user_input) - len - 1);
+    }
+    cp = NULL;
+    if (LYgetstr(user_input, VISIBLE,
+		 sizeof(user_input), NORECALL) < 0 ||
+	term_message) {
+	HTInfoMsg(NEWS_POST_CANCELLED);
+	LYCloseTempFP(fd);	/* Close the temp file. */
+	scrollok(LYwin, FALSE);	/* Stop scrolling.      */
+	goto cleanup;
+    }
+    fprintf(fd, "%s\n", user_input);
+
+    /*
+     * Add Organization:  header.
+     */
+    StrAllocCopy(cp, "Organization: ");
+    if ((org = LYGetEnv("ORGANIZATION")) != NULL) {
+	StrAllocCat(cp, org);
+    } else if ((org = LYGetEnv("NEWS_ORGANIZATION")) != NULL) {
+	StrAllocCat(cp, org);
+    }
+#ifdef UNIX
+    else if ((fp = fopen("/etc/organization", TXT_R)) != NULL) {
+	char *buffer = 0;
+
+	if (LYSafeGets(&buffer, fp) != NULL) {
+	    if (user_input[0] != '\0') {
+		LYTrimNewline(buffer);
+		StrAllocCat(cp, buffer);
+	    }
+	}
+	FREE(buffer);
+	LYCloseInput(fp);
+    }
+#else
+#ifdef _WINDOWS			/* 1998/05/14 (Thu) 17:47:01 */
+    else {
+	char *p, fname[LY_MAXPATH];
+
+	strcpy(fname, LynxSigFile);
+	p = strrchr(fname, '/');
+	if (p != 0 && (p - fname) < sizeof(fname) - 15) {
+	    strcpy(p + 1, "LYNX_ETC.TXT");
+	    if ((fp = fopen(fname, TXT_R)) != NULL) {
+		if (fgets(user_input, sizeof(user_input), fp) != NULL) {
+		    if ((org = strchr(user_input, '\n')) != NULL) {
+			*org = '\0';
+		    }
+		    if (user_input[0] != '\0') {
+			StrAllocCat(cp, user_input);
+		    }
+		}
+		LYCloseInput(fp);
+	    }
+	}
+    }
+#endif /* _WINDOWS */
+#endif /* !UNIX */
+    LYstrncpy(user_input, cp, (sizeof(user_input) - 16));
+    FREE(cp);
+    LYaddstr(gettext("\n\n Please provide or edit the Organization: header\n"));
+    if (LYgetstr(user_input, VISIBLE,
+		 sizeof(user_input), NORECALL) < 0 ||
+	term_message) {
+	HTInfoMsg(NEWS_POST_CANCELLED);
+	LYCloseTempFP(fd);	/* Close the temp file. */
+	scrollok(LYwin, FALSE);	/* Stop scrolling.      */
+	goto cleanup;
+    }
+    fprintf(fd, "%s\n", user_input);
+
+    if (References) {
+	fprintf(fd, "References: %s\n", References);
+    }
+    /*
+     * Add Newsgroups Summary and Keywords headers.
+     */
+    fprintf(fd, "Newsgroups: %s\nSummary: \nKeywords: \n\n", NewsGroups);
+
+    /*
+     * Have the user create the message body.
+     */
+    if (!no_editor && non_empty(editor)) {
+
+	if (followup && nhist > 0) {
+	    /*
+	     * Ask if the user wants to include the original message.
+	     */
+	    if (term_message) {
+		_statusline(INC_ORIG_MSG_PROMPT);
+	    } else if (HTConfirm(INC_ORIG_MSG_PROMPT) == YES) {
+		/*
+		 * The 'TRUE' will add the reply ">" in front of every line. 
+		 * We're assuming that if the display character set is Japanese
+		 * and the document did not have a CJK charset, any non-EUC or
+		 * non-SJIS 8-bit characters in it where converted to 7-bit
+		 * equivalents.  - FM
+		 */
+		print_wwwfile_to_fd(fd, FALSE, TRUE);
+	    }
+	}
+	LYCloseTempFP(fd);	/* Close the temp file. */
+	scrollok(LYwin, FALSE);	/* Stop scrolling.      */
+	if (term_message || LYCharIsINTERRUPT(c))
+	    goto cleanup;
+
+	/*
+	 * Spawn the user's editor on the news file.
+	 */
+	edit_temporary_file(my_tempfile, "", SPAWNING_EDITOR_FOR_NEWS);
+
+	nonempty = message_has_content(my_tempfile, &nonspaces);
+
+    } else {
+	/*
+	 * Use the built in line editior.
+	 */
+	LYaddstr(gettext("\n\n Please enter your message below."));
+	LYaddstr(gettext("\n When you are done, press enter and put a single period (.)"));
+	LYaddstr(gettext("\n on a line and press enter again."));
+	LYaddstr("\n\n");
+	LYrefresh();
+	*user_input = '\0';
+	if (LYgetstr(user_input, VISIBLE,
+		     sizeof(user_input), NORECALL) < 0 ||
+	    term_message) {
+	    HTInfoMsg(NEWS_POST_CANCELLED);
+	    LYCloseTempFP(fd);	/* Close the temp file. */
+	    scrollok(LYwin, FALSE);	/* Stop scrolling.      */
+	    goto cleanup;
+	}
+	while (!STREQ(user_input, ".") && !term_message) {
+	    LYaddch('\n');
+	    fprintf(fd, "%s\n", user_input);
+	    if (!nonempty && strlen(user_input))
+		nonempty = TRUE;
+	    *user_input = '\0';
+	    if (LYgetstr(user_input, VISIBLE,
+			 sizeof(user_input), NORECALL) < 0) {
+		HTInfoMsg(NEWS_POST_CANCELLED);
+		LYCloseTempFP(fd);	/* Close the temp file. */
+		scrollok(LYwin, FALSE);		/* Stop scrolling.      */
+		goto cleanup;
+	    }
+	}
+	fprintf(fd, "\n");
+	LYCloseTempFP(fd);	/* Close the temp file. */
+	scrollok(LYwin, FALSE);	/* Stop scrolling.      */
+    }
+
+    if (nonempty) {
+	/*
+	 * Confirm whether to post, and if so, whether to append the sig file. 
+	 * - FM
+	 */
+	LYStatusLine = (LYlines - 1);
+	c = HTConfirm(POST_MSG_PROMPT);
+	LYStatusLine = -1;
+	if (c != YES) {
+	    LYclear();		/* clear the screen */
+	    goto cleanup;
+	}
+    } else {
+	HTAlert(gettext("Message has no original text!"));
+	if (!nonspaces
+	    || HTConfirmDefault(POST_MSG_PROMPT, NO) != YES)
+	    goto cleanup;
+    }
+    if ((LynxSigFile != NULL) && (fp = fopen(LynxSigFile, TXT_R)) != NULL) {
+	char *msg = NULL;
+
+	HTSprintf0(&msg, APPEND_SIG_FILE, LynxSigFile);
+
+	LYStatusLine = (LYlines - 1);
+	if (term_message) {
+	    _user_message(APPEND_SIG_FILE, LynxSigFile);
+	} else if (HTConfirm(msg) == YES) {
+	    if ((fd = LYAppendToTxtFile(my_tempfile)) != NULL) {
+		char *buffer = NULL;
+
+		fputs("-- \n", fd);
+		while (LYSafeGets(&buffer, fp) != NULL) {
+		    fputs(buffer, fd);
+		}
+		LYCloseOutput(fd);
+	    }
+	}
+	LYCloseInput(fp);
+	FREE(msg);
+	LYStatusLine = -1;
+    }
+    LYclear();			/* clear the screen */
+
+    /*
+     * If we are using a Japanese display character set, convert the contents
+     * of the temp file to JIS (nothing should change if it does not, in fact,
+     * contain EUC or SJIS di-bytes).  Otherwise, use the temp file as is.  -
+     * FM
+     */
+    if (CJKfile[0] != '\0') {
+	if ((fd = fopen(my_tempfile, TXT_R)) != NULL) {
+	    char *buffer = NULL;
+
+	    while (LYSafeGets(&buffer, fd) != NULL) {
+		TO_JIS((unsigned char *) buffer,
+		       (unsigned char *) CJKinput);
+		fputs(CJKinput, fc);
+	    }
+	    LYCloseTempFP(fc);
+	    StrAllocCopy(postfile, CJKfile);
+	    LYCloseInput(fd);
+	    LYRemoveTemp(my_tempfile);
+	    strcpy(my_tempfile, CJKfile);
+	    CJKfile[0] = '\0';
+	} else {
+	    StrAllocCopy(postfile, my_tempfile);
+	}
+    } else {
+	StrAllocCopy(postfile, my_tempfile);
+    }
+    if (!followup) {
+	/*
+	 * If it's not a followup, the current document most likely is the
+	 * group listing, so force a to have the article show up in the list
+	 * after the posting.  Note, that if it's a followup via a link in a
+	 * news article, the user must do a reload manually on returning to the
+	 * group listing.  - FM
+	 */
+	LYforce_no_cache = TRUE;
+    }
+    LYStatusLine = (LYlines - 1);
+    HTUserMsg(POSTING_TO_NEWS);
+    LYStatusLine = -1;
+
+    /*
+     * Come here to cleanup and exit.
+     */
+  cleanup:
+#ifndef VMS
+    signal(SIGINT, cleanup_sig);
+#endif /* !VMS */
+    term_message = FALSE;
+    if (!postfile)
+	LYRemoveTemp(my_tempfile);
+    LYRemoveTemp(CJKfile);
+    FREE(NewsGroups);
+    FREE(References);
+
+    return (postfile);
+}
+
+static void terminate_message(int sig GCC_UNUSED)
+{
+    term_message = TRUE;
+    /*
+     * Reassert the AST.
+     */
+    signal(SIGINT, terminate_message);
+#ifdef VMS
+    /*
+     * Refresh the screen to get rid of the "interrupt" message.
+     */
+    lynx_force_repaint();
+    LYrefresh();
+#endif /* VMS */
+}
+
+#endif /* not DISABLE_NEWS */
diff --git a/src/LYNews.h b/src/LYNews.h
new file mode 100644
index 00000000..780f8fa6
--- /dev/null
+++ b/src/LYNews.h
@@ -0,0 +1,18 @@
+#ifndef LYNEWSPOST_H
+#define LYNEWSPOST_H
+
+#ifndef LYSTRUCTS_H
+#include <LYStructs.h>
+#endif /* LYSTRUCTS_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    extern BOOLEAN term_message;
+
+    extern char *LYNewsPost(char *newsgroups, BOOLEAN followup);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYNEWSPOST_H */
diff --git a/src/LYOptions.c b/src/LYOptions.c
new file mode 100644
index 00000000..0196cd1a
--- /dev/null
+++ b/src/LYOptions.c
@@ -0,0 +1,4028 @@
+/* $LynxId: LYOptions.c,v 1.136 2010/04/30 00:01:19 tom Exp $ */
+#include <HTUtils.h>
+#include <HTFTP.h>
+#include <HTTP.h>		/* 'reloading' flag */
+#include <HTML.h>
+#include <LYCurses.h>
+#include <LYUtils.h>
+#include <LYStrings.h>
+#include <LYGlobalDefs.h>
+#include <LYHistory.h>
+#include <LYOptions.h>
+#include <LYSignal.h>
+#include <LYClean.h>
+#include <LYCharSets.h>
+#include <UCMap.h>
+#include <UCAux.h>
+#include <LYKeymap.h>
+#include <LYrcFile.h>
+#include <HTAlert.h>
+#include <LYBookmark.h>
+#include <GridText.h>
+#include <LYGetFile.h>
+#include <LYReadCFG.h>
+#include <LYPrettySrc.h>
+#include <HTFile.h>
+#include <LYCharUtils.h>
+
+#include <LYLeaks.h>
+
+BOOLEAN term_options;
+
+#define TOP_LINK  "/"
+#define MBM_LINK  "//MBM_MENU"
+
+#define MARGIN_STR (no_margins ? "" : "&nbsp;&nbsp;")
+#define MARGIN_LEN (no_margins ?  0 : 2)
+
+static void terminate_options(int sig);
+
+#define COL_OPTION_VALUES 36	/* display column where option values start */
+
+#if defined(USE_SLANG) || defined(COLOR_CURSES)
+static BOOLEAN can_do_colors = FALSE;
+#endif
+
+static int LYChosenShowColor = SHOW_COLOR_UNKNOWN;	/* whether to show and save */
+
+BOOLEAN LYCheckUserAgent(void)
+{
+    if (non_empty(LYUserAgent)) {
+	if (strstr(LYUserAgent, "Lynx") == 0
+	    && strstr(LYUserAgent, "lynx") == 0
+	    && strstr(LYUserAgent, "L_y_n_x") == 0
+	    && strstr(LYUserAgent, "l_y_n_x") == 0) {
+	    return FALSE;
+	}
+    }
+    return TRUE;
+}
+
+static void validate_x_display(void)
+{
+    char *cp;
+
+    if ((cp = LYgetXDisplay()) != NULL) {
+	StrAllocCopy(x_display, cp);
+    } else {
+	FREE(x_display);
+    }
+}
+
+static void summarize_x_display(char *display_option)
+{
+    if ((x_display == NULL && *display_option == '\0') ||
+	(x_display != NULL && !strcmp(x_display, display_option))) {
+	if (x_display == NULL && LYisConfiguredForX == TRUE) {
+	    _statusline(VALUE_ACCEPTED_WARNING_X);
+	} else if (x_display != NULL && LYisConfiguredForX == FALSE) {
+	    _statusline(VALUE_ACCEPTED_WARNING_NONX);
+	} else {
+	    _statusline(VALUE_ACCEPTED);
+	}
+    } else {
+	if (*display_option) {
+	    _statusline(FAILED_TO_SET_DISPLAY);
+	} else {
+	    _statusline(FAILED_CLEAR_SET_DISPLAY);
+	}
+    }
+}
+
+static void SetupChosenShowColor(void)
+{
+#if defined(USE_SLANG) || defined(COLOR_CURSES)
+    can_do_colors = TRUE;
+#if defined(COLOR_CURSES)
+    if (LYCursesON)		/* could crash if called before initialization */
+	can_do_colors = (has_colors()
+			 ? TRUE
+			 : FALSE);
+#endif
+    if (!no_option_save) {
+	if (LYChosenShowColor == SHOW_COLOR_UNKNOWN) {
+	    switch (LYrcShowColor) {
+	    case SHOW_COLOR_NEVER:
+		LYChosenShowColor =
+		    (LYShowColor >= SHOW_COLOR_ON) ?
+		    SHOW_COLOR_ON : SHOW_COLOR_NEVER;
+		break;
+	    case SHOW_COLOR_ALWAYS:
+		if (!can_do_colors)
+		    LYChosenShowColor = SHOW_COLOR_ALWAYS;
+		else
+		    LYChosenShowColor =
+			(LYShowColor >= SHOW_COLOR_ON) ?
+			SHOW_COLOR_ALWAYS : SHOW_COLOR_OFF;
+		break;
+	    default:
+		LYChosenShowColor =
+		    (LYShowColor >= SHOW_COLOR_ON) ?
+		    SHOW_COLOR_ON : SHOW_COLOR_OFF;
+	    }
+	}
+    }
+#endif /* USE_SLANG || COLOR_CURSES */
+}
+
+#ifndef NO_OPTION_MENU
+static int boolean_choice(int status,
+			  int line,
+			  int column,
+			  const char **choices);
+
+#define LYChooseBoolean(status, line, column, choices) \
+	(BOOLEAN) boolean_choice(status, line, column, (const char **)choices)
+
+#define LYChooseEnum(status, line, column, choices) \
+	boolean_choice(status, line, column, (const char **)choices)
+
+#define MAXCHOICES 10
+
+/*
+ * Values for the options menu.  - FM
+ *
+ * L_foo values are the Y coordinates for the menu item.
+ * B_foo values are the X coordinates for the item's prompt string.
+ * C_foo values are the X coordinates for the item's value string.
+ */
+#define L_EDITOR	 2
+#define L_DISPLAY	 3
+
+#define L_HOME		 4
+#define C_MULTI		24
+#define B_BOOK		34
+#define C_DEFAULT	50
+
+#define L_FTPSTYPE	 5
+#define L_MAIL_ADDRESS	 6
+#define L_SSEARCH	 7
+#define L_LANGUAGE	 8
+#define L_PREF_CHARSET	 9
+#define L_ASSUME_CHARSET (L_PREF_CHARSET + 1)
+#define L_CHARSET	10
+#define L_RAWMODE	11
+
+#define L_COLOR		L_RAWMODE
+#define B_COLOR		44
+#define C_COLOR		62
+
+#define L_BOOL_A	12
+#define B_VIKEYS	5
+#define C_VIKEYS	15
+#define B_EMACSKEYS	22
+#define C_EMACSKEYS	36
+#define B_SHOW_DOTFILES	44
+#define C_SHOW_DOTFILES	62
+
+#define L_BOOL_B	13
+#define B_SELECT_POPUPS	5
+#define C_SELECT_POPUPS	36
+#define B_SHOW_CURSOR	44
+#define C_SHOW_CURSOR	62
+
+#define L_KEYPAD	14
+#define L_LINEED	15
+#define L_LAYOUT	16
+
+#ifdef DIRED_SUPPORT
+#define L_DIRED		17
+#define L_USER_MODE	18
+#define L_USER_AGENT	19
+#define L_EXEC		20
+#else
+#define L_USER_MODE	17
+#define L_USER_AGENT	18
+#define L_EXEC		19
+#endif /* DIRED_SUPPORT */
+
+#define L_VERBOSE_IMAGES L_USER_MODE
+#define B_VERBOSE_IMAGES 50
+#define C_VERBOSE_IMAGES (B_VERBOSE_IMAGES + 21)
+
+/* a kludge to add assume_charset only in ADVANCED mode... */
+#define L_Bool_A     (use_assume_charset ? L_BOOL_A     + 1 : L_BOOL_A)
+#define L_Bool_B     (use_assume_charset ? L_BOOL_B     + 1 : L_BOOL_B)
+#define L_Exec       (use_assume_charset ? L_EXEC       + 1 : L_EXEC)
+#define L_Rawmode    (use_assume_charset ? L_RAWMODE    + 1 : L_RAWMODE)
+#define L_Charset    (use_assume_charset ? L_CHARSET    + 1 : L_CHARSET)
+#define L_Color      (use_assume_charset ? L_COLOR      + 1 : L_COLOR)
+#define L_Keypad     (use_assume_charset ? L_KEYPAD     + 1 : L_KEYPAD)
+#define L_Lineed     (use_assume_charset ? L_LINEED     + 1 : L_LINEED)
+#define L_Layout     (use_assume_charset ? L_LAYOUT     + 1 : L_LAYOUT)
+#define L_Dired      (use_assume_charset ? L_DIRED      + 1 : L_DIRED)
+#define L_User_Mode  (use_assume_charset ? L_USER_MODE  + 1 : L_USER_MODE)
+#define L_User_Agent (use_assume_charset ? L_USER_AGENT + 1 : L_USER_AGENT)
+
+#define LPAREN '('
+#define RPAREN ')'
+
+static int add_it(char *text, int len)
+{
+    if (len) {
+	text[len] = '\0';
+	LYaddstr(text);
+    }
+    return 0;
+}
+
+/*
+ * addlbl() is used instead of plain LYaddstr() in old-style options menu
+ * to show hot keys in bold.
+ */
+static void addlbl(const char *text)
+{
+    char actual[80];
+    int s, d;
+    BOOL b = FALSE;
+
+    for (s = d = 0; text[s]; s++) {
+	actual[d++] = text[s];
+	if (text[s] == LPAREN) {
+	    d = add_it(actual, d - 1);
+	    lynx_start_bold();
+	    b = TRUE;
+	    actual[d++] = text[s];
+	} else if (text[s] == RPAREN) {
+	    d = add_it(actual, d);
+	    lynx_stop_bold();
+	    b = FALSE;
+	}
+    }
+    add_it(actual, d);
+    if (b)
+	lynx_stop_bold();
+}
+
+#if !defined(VMS) || defined(USE_SLANG)
+#define HANDLE_LYOPTIONS \
+		    if (term_options) { \
+			term_options = FALSE; \
+		    } else { \
+			AddValueAccepted = TRUE; \
+		    } \
+		    goto draw_options
+#else
+#define HANDLE_LYOPTIONS \
+		    term_options = FALSE; \
+		    if (use_assume_charset != old_use_assume_charset) \
+			goto draw_options
+#endif /* !VMS || USE_SLANG */
+
+void LYoptions(void)
+{
+#define ShowBool(value) LYaddstr((value) ? "ON " : "OFF")
+    static const char *bool_choices[] =
+    {
+	"OFF",
+	"ON",
+	NULL
+    };
+    static const char *caseless_choices[] =
+    {
+	"CASE INSENSITIVE",
+	"CASE SENSITIVE",
+	NULL
+    };
+
+#ifdef DIRED_SUPPORT
+    static const char *dirList_choices[] =
+    {
+	"Directories first",
+	"Files first",
+	"Mixed style",
+	NULL
+    };
+#endif
+
+#if defined(ENABLE_OPTS_CHANGE_EXEC) && (defined(EXEC_LINKS) || defined(EXEC_SCRIPTS))
+    static const char *exec_choices[] =
+    {
+	"ALWAYS OFF",
+	"FOR LOCAL FILES ONLY",
+#ifndef NEVER_ALLOW_REMOTE_EXEC
+	"ALWAYS ON",
+#endif				/* !NEVER_ALLOW_REMOTE_EXEC */
+	NULL
+    };
+#endif
+    static const char *fileSort_choices[] =
+    {
+	"By Filename",
+	"By Type",
+	"By Size",
+	"By Date",
+	NULL
+    };
+    static const char *keypad_choices[] =
+    {
+	"Numbers act as arrows",
+	"Links are numbered",
+	"Links and form fields are numbered",
+	NULL
+    };
+    static const char *mbm_choices[] =
+    {
+	"OFF     ",
+	"STANDARD",
+	"ADVANCED",
+	NULL
+    };
+    static const char *userMode_choices[] =
+    {
+	"Novice",
+	"Intermediate",
+	"Advanced",
+	NULL
+    };
+
+#if defined(ENABLE_OPTS_CHANGE_EXEC) && (defined(EXEC_LINKS) || defined(EXEC_SCRIPTS))
+    int itmp;
+#endif /* ENABLE_OPTS_CHANGE_EXEC */
+    int response, ch;
+
+    /*
+     * If the user changes the display we need memory to put it in.
+     */
+    char display_option[MAX_LINE];
+    char *choices[MAXCHOICES];
+    int CurrentCharSet = current_char_set;
+    int CurrentAssumeCharSet = UCLYhndl_for_unspec;
+    int CurrentShowColor = LYShowColor;
+    BOOLEAN CurrentRawMode = LYRawMode;
+    BOOLEAN AddValueAccepted = FALSE;
+    char *cp = NULL;
+    BOOL use_assume_charset;
+
+#if defined(VMS) || defined(USE_SLANG)
+    BOOL old_use_assume_charset;
+#endif
+
+#ifdef DIRED_SUPPORT
+#ifdef ENABLE_OPTS_CHANGE_EXEC
+    if (LYlines < 24) {
+	HTAlert(OPTION_SCREEN_NEEDS_24);
+	return;
+    }
+#else
+    if (LYlines < 23) {
+	HTAlert(OPTION_SCREEN_NEEDS_23);
+	return;
+    }
+#endif /* ENABLE_OPTS_CHANGE_EXEC */
+#else
+#ifdef ENABLE_OPTS_CHANGE_EXEC
+    if (LYlines < 23) {
+	HTAlert(OPTION_SCREEN_NEEDS_23);
+	return;
+    }
+#else
+    if (LYlines < 22) {
+	HTAlert(OPTION_SCREEN_NEEDS_22);
+	return;
+    }
+#endif /* ENABLE_OPTS_CHANGE_EXEC */
+#endif /* DIRED_SUPPORT */
+
+    term_options = FALSE;
+    LYStatusLine = (LYlines - 1);	/* screen is otherwise too crowded */
+    signal(SIGINT, terminate_options);
+    if (no_option_save) {
+	if (LYShowColor == SHOW_COLOR_NEVER) {
+	    LYShowColor = SHOW_COLOR_OFF;
+	} else if (LYShowColor == SHOW_COLOR_ALWAYS) {
+	    LYShowColor = SHOW_COLOR_ON;
+	}
+#if defined(USE_SLANG) || defined(COLOR_CURSES)
+    } else {
+	SetupChosenShowColor();
+#endif /* USE_SLANG || COLOR_CURSES */
+    }
+
+    use_assume_charset = (BOOLEAN) (user_mode == ADVANCED_MODE);
+
+  draw_options:
+
+#if defined(VMS) || defined(USE_SLANG)
+    old_use_assume_charset = use_assume_charset;
+#endif
+    /*
+     * NOTE that printw() should be avoided for strings that might have
+     * non-ASCII or multibyte/CJK characters.  - FM
+     */
+#if defined(FANCY_CURSES) || defined (USE_SLANG)
+    if (enable_scrollback) {
+	LYclear();
+    } else {
+	LYerase();
+    }
+#else
+    LYclear();
+#endif /* FANCY_CURSES || USE_SLANG */
+    LYmove(0, 5);
+
+    lynx_start_h1_color();
+    LYaddstr("         Options Menu (");
+    LYaddstr(LYNX_NAME);
+    LYaddstr(" Version ");
+    LYaddstr(LYNX_VERSION);
+    LYaddch(')');
+    lynx_stop_h1_color();
+    LYmove(L_EDITOR, 5);
+    addlbl("(E)ditor                     : ");
+    LYaddstr(non_empty(editor) ? editor : "NONE");
+
+    LYmove(L_DISPLAY, 5);
+    addlbl("(D)ISPLAY variable           : ");
+    LYaddstr(non_empty(x_display) ? x_display : "NONE");
+
+    LYmove(L_HOME, 5);
+    addlbl("mu(L)ti-bookmarks: ");
+    LYaddstr(mbm_choices[LYMultiBookmarks]);
+    LYmove(L_HOME, B_BOOK);
+    if (LYMultiBookmarks != MBM_OFF) {
+	addlbl("review/edit (B)ookmarks files");
+    } else {
+	addlbl("(B)ookmark file: ");
+	LYaddstr(non_empty(bookmark_page) ? bookmark_page : "NONE");
+    }
+
+    LYmove(L_FTPSTYPE, 5);
+    addlbl("(F)TP sort criteria          : ");
+    LYaddstr((HTfileSortMethod == FILE_BY_NAME ? "By Filename" :
+	      (HTfileSortMethod == FILE_BY_SIZE ? "By Size    " :
+	       (HTfileSortMethod == FILE_BY_TYPE ? "By Type    " :
+		"By Date    "))));
+
+    LYmove(L_MAIL_ADDRESS, 5);
+    addlbl("(P)ersonal mail address      : ");
+    LYaddstr(non_empty(personal_mail_address) ?
+	     personal_mail_address : "NONE");
+
+    LYmove(L_SSEARCH, 5);
+    addlbl("(S)earching type             : ");
+    LYaddstr(case_sensitive ? "CASE SENSITIVE  " : "CASE INSENSITIVE");
+
+    LYmove(L_Charset, 5);
+    addlbl("display (C)haracter set      : ");
+    LYaddstr(LYchar_set_names[current_char_set]);
+
+    LYmove(L_LANGUAGE, 5);
+    addlbl("preferred document lan(G)uage: ");
+    LYaddstr(non_empty(language) ? language : "NONE");
+
+    LYmove(L_PREF_CHARSET, 5);
+    addlbl("preferred document c(H)arset : ");
+    LYaddstr(non_empty(pref_charset) ? pref_charset : "NONE");
+
+    if (use_assume_charset) {
+	LYmove(L_ASSUME_CHARSET, 5);
+	addlbl("(^A)ssume charset if unknown : ");
+	if (UCAssume_MIMEcharset)
+	    LYaddstr(UCAssume_MIMEcharset);
+	else
+	    LYaddstr((UCLYhndl_for_unspec >= 0) ?
+		     LYCharSet_UC[UCLYhndl_for_unspec].MIMEname
+		     : "NONE");
+    }
+
+    LYmove(L_Rawmode, 5);
+    addlbl("Raw 8-bit or CJK m(O)de      : ");
+    ShowBool(LYRawMode);
+
+#if defined(USE_SLANG) || defined(COLOR_CURSES)
+    LYmove(L_Color, B_COLOR);
+    addlbl("show color (&)  : ");
+    if (no_option_save) {
+	ShowBool(LYShowColor == SHOW_COLOR_OFF);
+    } else {
+	switch (LYChosenShowColor) {
+	case SHOW_COLOR_NEVER:
+	    LYaddstr("NEVER     ");
+	    break;
+	case SHOW_COLOR_OFF:
+	    LYaddstr("OFF");
+	    break;
+	case SHOW_COLOR_ON:
+	    LYaddstr("ON ");
+	    break;
+	case SHOW_COLOR_ALWAYS:
+#if defined(COLOR_CURSES)
+	    if (!has_colors())
+		LYaddstr("Always try");
+	    else
+#endif
+		LYaddstr("ALWAYS    ");
+	}
+    }
+#endif /* USE_SLANG || COLOR_CURSES */
+
+    LYmove(L_Bool_A, B_VIKEYS);
+    addlbl("(V)I keys: ");
+    ShowBool(vi_keys);
+
+    LYmove(L_Bool_A, B_EMACSKEYS);
+    addlbl("e(M)acs keys: ");
+    ShowBool(emacs_keys);
+
+    LYmove(L_Bool_A, B_SHOW_DOTFILES);
+    addlbl("sho(W) dot files: ");
+    ShowBool(!no_dotfiles && show_dotfiles);
+
+    LYmove(L_Bool_B, B_SELECT_POPUPS);
+    addlbl("popups for selec(T) fields   : ");
+    ShowBool(LYSelectPopups);
+
+    LYmove(L_Bool_B, B_SHOW_CURSOR);
+    addlbl("show cursor (@) : ");
+    ShowBool(LYShowCursor);
+
+    LYmove(L_Keypad, 5);
+    addlbl("(K)eypad mode                : ");
+    LYaddstr((fields_are_numbered() && links_are_numbered())
+	     ? "Links and form fields are numbered"
+	     : (links_are_numbered()
+		? "Links are numbered                "
+		: (fields_are_numbered()
+		   ? "Form fields are numbered          "
+		   : "Numbers act as arrows             ")));
+
+    LYmove(L_Lineed, 5);
+    addlbl("li(N)e edit style            : ");
+    LYaddstr(LYLineeditNames[current_lineedit]);
+
+#ifdef EXP_KEYBOARD_LAYOUT
+    LYmove(L_Layout, 5);
+    addlbl("Ke(Y)board layout            : ");
+    LYaddstr(LYKbLayoutNames[current_layout]);
+#endif
+
+#ifdef DIRED_SUPPORT
+    LYmove(L_Dired, 5);
+    addlbl("l(I)st directory style       : ");
+    LYaddstr((dir_list_style == FILES_FIRST) ? "Files first      " :
+	     ((dir_list_style == MIXED_STYLE) ? "Mixed style      " :
+	      "Directories first"));
+#endif /* DIRED_SUPPORT */
+
+    LYmove(L_User_Mode, 5);
+    addlbl("(U)ser mode                  : ");
+    LYaddstr((user_mode == NOVICE_MODE) ? "Novice      " :
+	     ((user_mode == INTERMEDIATE_MODE) ? "Intermediate" :
+	      "Advanced    "));
+
+    addlbl("  verbose images (!) : ");
+    ShowBool(verbose_img);
+
+    LYmove(L_User_Agent, 5);
+    addlbl("user (A)gent                 : ");
+    LYaddstr(non_empty(LYUserAgent) ? LYUserAgent : "NONE");
+
+#if defined(ENABLE_OPTS_CHANGE_EXEC) && (defined(EXEC_LINKS) || defined(EXEC_SCRIPTS))
+    LYmove(L_Exec, 5);
+    addlbl("local e(X)ecution links      : ");
+#ifndef NEVER_ALLOW_REMOTE_EXEC
+    LYaddstr(local_exec ? "ALWAYS ON           " :
+	     (local_exec_on_local_files ? "FOR LOCAL FILES ONLY" :
+	      "ALWAYS OFF          "));
+#else
+    LYaddstr(local_exec_on_local_files ? "FOR LOCAL FILES ONLY" :
+	     "ALWAYS OFF          ");
+#endif /* !NEVER_ALLOW_REMOTE_EXEC */
+#endif /* ENABLE_OPTS_CHANGE_EXEC */
+
+    LYmove(LYlines - 3, 2);
+    LYaddstr(SELECT_SEGMENT);
+    lynx_start_bold();
+    LYaddstr(CAP_LETT_SEGMENT);
+    lynx_stop_bold();
+    LYaddstr(OF_OPT_LINE_SEGMENT);
+    if (!no_option_save) {
+	LYaddstr(" '");
+	lynx_start_bold();
+	LYaddstr(">");
+	lynx_stop_bold();
+	LYaddstr("'");
+	LYaddstr(TO_SAVE_SEGMENT);
+    }
+    LYaddstr(OR_SEGMENT);
+    LYaddstr("'");
+    lynx_start_bold();
+    LYaddstr("r");
+    lynx_stop_bold();
+    LYaddstr("'");
+    LYaddstr(TO_RETURN_SEGMENT);
+
+    response = 0;
+    while (response != 'R' &&
+	   !LYisNonAlnumKeyname(response, LYK_PREV_DOC) &&
+	   response != '>' && !term_options &&
+	   !LYCharIsINTERRUPT_NO_letter(response)) {
+	if (AddValueAccepted == TRUE) {
+	    _statusline(VALUE_ACCEPTED);
+	    AddValueAccepted = FALSE;
+	}
+	LYmove((LYlines - 2), 0);
+	lynx_start_prompt_color();
+	LYaddstr(COMMAND_PROMPT);
+	lynx_stop_prompt_color();
+
+	LYrefresh();
+	response = LYgetch_single();
+	if (term_options || LYCharIsINTERRUPT_NO_letter(response))
+	    response = 'R';
+	if (LYisNonAlnumKeyname(response, LYK_REFRESH)) {
+	    lynx_force_repaint();
+	    goto draw_options;
+	}
+	switch (response) {
+	case 'E':		/* Change the editor. */
+	    if (no_editor) {
+		_statusline(EDIT_DISABLED);
+	    } else if (system_editor) {
+		_statusline(EDITOR_LOCKED);
+	    } else {
+		if (non_empty(editor))
+		    LYstrncpy(display_option, editor, sizeof(display_option) - 1);
+		else {		/* clear the NONE */
+		    LYmove(L_EDITOR, COL_OPTION_VALUES);
+		    LYaddstr("    ");
+		    *display_option = '\0';
+		}
+		_statusline(ACCEPT_DATA);
+		LYmove(L_EDITOR, COL_OPTION_VALUES);
+		lynx_start_bold();
+		ch = LYgetstr(display_option, VISIBLE,
+			      sizeof(display_option), NORECALL);
+		lynx_stop_bold();
+		LYmove(L_EDITOR, COL_OPTION_VALUES);
+		if (term_options || ch == -1) {
+		    LYaddstr(non_empty(editor) ?
+			     editor : "NONE");
+		} else if (*display_option == '\0') {
+		    FREE(editor);
+		    LYaddstr("NONE");
+		} else {
+		    StrAllocCopy(editor, display_option);
+		    LYaddstr(display_option);
+		}
+		LYclrtoeol();
+		if (ch == -1) {
+		    HTInfoMsg(CANCELLED);
+		    HTInfoMsg("");
+		} else {
+		    _statusline(VALUE_ACCEPTED);
+		}
+	    }
+	    response = ' ';
+	    break;
+
+	case 'D':		/* Change the display. */
+	    if (non_empty(x_display)) {
+		LYstrncpy(display_option, x_display, sizeof(display_option) - 1);
+	    } else {		/* clear the NONE */
+		LYmove(L_DISPLAY, COL_OPTION_VALUES);
+		LYaddstr("    ");
+		*display_option = '\0';
+	    }
+	    _statusline(ACCEPT_DATA);
+	    LYmove(L_DISPLAY, COL_OPTION_VALUES);
+	    lynx_start_bold();
+	    ch = LYgetstr(display_option, VISIBLE,
+			  sizeof(display_option), NORECALL);
+	    lynx_stop_bold();
+	    LYmove(L_DISPLAY, COL_OPTION_VALUES);
+
+#ifdef VMS
+#define CompareEnvVars(a,b) strcasecomp(a, b)
+#else
+#define CompareEnvVars(a,b) strcmp(a, b)
+#endif /* VMS */
+
+	    if ((term_options || ch == -1) ||
+		(x_display != NULL &&
+		 !CompareEnvVars(x_display, display_option))) {
+		/*
+		 * Cancelled, or a non-NULL display string wasn't changed.  -
+		 * FM
+		 */
+		LYaddstr(non_empty(x_display) ? x_display : "NONE");
+		LYclrtoeol();
+		if (ch == -1) {
+		    HTInfoMsg(CANCELLED);
+		    HTInfoMsg("");
+		} else {
+		    _statusline(VALUE_ACCEPTED);
+		}
+		response = ' ';
+		break;
+	    } else if (*display_option == '\0') {
+		if ((x_display == NULL) ||
+		    (x_display != NULL && *x_display == '\0')) {
+		    /*
+		     * NULL or zero-length display string wasn't changed.  - FM
+		     */
+		    LYaddstr("NONE");
+		    LYclrtoeol();
+		    _statusline(VALUE_ACCEPTED);
+		    response = ' ';
+		    break;
+		}
+	    }
+	    /*
+	     * Set the new DISPLAY variable.  - FM
+	     */
+	    LYsetXDisplay(display_option);
+	    validate_x_display();
+	    cp = NULL;
+	    LYaddstr(x_display ? x_display : "NONE");
+	    LYclrtoeol();
+	    summarize_x_display(display_option);
+	    response = ' ';
+	    break;
+
+	case 'L':		/* Change multibookmarks option. */
+	    if (LYMBMBlocked) {
+		_statusline(MULTIBOOKMARKS_DISALLOWED);
+		response = ' ';
+		break;
+	    }
+	    if (!LYSelectPopups) {
+		LYMultiBookmarks = LYChooseEnum(LYMultiBookmarks,
+						L_HOME, C_MULTI,
+						mbm_choices);
+	    } else {
+		LYMultiBookmarks = LYChoosePopup(LYMultiBookmarks,
+						 L_HOME, (C_MULTI - 1),
+						 mbm_choices,
+						 3, FALSE, FALSE);
+	    }
+#if defined(VMS) || defined(USE_SLANG)
+	    if (LYSelectPopups) {
+		LYmove(L_HOME, C_MULTI);
+		LYclrtoeol();
+		LYaddstr(mbm_choices[LYMultiBookmarks]);
+	    }
+#endif /* VMS || USE_SLANG */
+#if !defined(VMS) && !defined(USE_SLANG)
+	    if (!LYSelectPopups)
+#endif /* !VMS && !USE_SLANG */
+	    {
+		LYmove(L_HOME, B_BOOK);
+		LYclrtoeol();
+		if (LYMultiBookmarks != MBM_OFF) {
+		    LYaddstr(gettext("review/edit B)ookmarks files"));
+		} else {
+		    LYaddstr(gettext("B)ookmark file: "));
+		    LYaddstr(non_empty(bookmark_page) ?
+			     bookmark_page : "NONE");
+		}
+	    }
+	    response = ' ';
+	    if (LYSelectPopups) {
+		HANDLE_LYOPTIONS;
+	    }
+	    break;
+
+	case 'B':		/* Change the bookmark page location. */
+	    /*
+	     * Anonymous users should not be allowed to change the bookmark
+	     * page.
+	     */
+	    if (!no_bookmark) {
+		if (LYMultiBookmarks != MBM_OFF) {
+		    edit_bookmarks();
+		    signal(SIGINT, terminate_options);
+		    goto draw_options;
+		}
+		if (non_empty(bookmark_page)) {
+		    LYstrncpy(display_option,
+			      bookmark_page,
+			      sizeof(display_option) - 1);
+		} else {	/* clear the NONE */
+		    LYmove(L_HOME, C_DEFAULT);
+		    LYclrtoeol();
+		    *display_option = '\0';
+		}
+		_statusline(ACCEPT_DATA);
+		LYmove(L_HOME, C_DEFAULT);
+		lynx_start_bold();
+		ch = LYgetstr(display_option, VISIBLE,
+			      sizeof(display_option), NORECALL);
+		lynx_stop_bold();
+		LYmove(L_HOME, C_DEFAULT);
+		if (term_options ||
+		    ch == -1 || *display_option == '\0') {
+		    LYaddstr(non_empty(bookmark_page) ?
+			     bookmark_page : "NONE");
+		} else if (!LYPathOffHomeOK(display_option,
+					    sizeof(display_option))) {
+		    LYaddstr(non_empty(bookmark_page) ?
+			     bookmark_page : "NONE");
+		    LYclrtoeol();
+		    _statusline(USE_PATH_OFF_HOME);
+		    response = ' ';
+		    break;
+		} else {
+		    StrAllocCopy(bookmark_page, display_option);
+		    StrAllocCopy(MBM_A_subbookmark[0], bookmark_page);
+		    LYaddstr(bookmark_page);
+		}
+		LYclrtoeol();
+		if (ch == -1) {
+		    HTInfoMsg(CANCELLED);
+		    HTInfoMsg("");
+		} else {
+		    _statusline(VALUE_ACCEPTED);
+		}
+	    } else {		/* anonymous */
+		_statusline(BOOKMARK_CHANGE_DISALLOWED);
+	    }
+	    response = ' ';
+	    break;
+
+	case 'F':		/* Change ftp directory sorting. */
+	    if (!LYSelectPopups) {
+		HTfileSortMethod = LYChooseEnum(HTfileSortMethod,
+						L_FTPSTYPE, -1,
+						fileSort_choices);
+	    } else {
+		HTfileSortMethod = LYChoosePopup(HTfileSortMethod,
+						 L_FTPSTYPE, -1,
+						 fileSort_choices,
+						 4, FALSE, FALSE);
+#if defined(VMS) || defined(USE_SLANG)
+		LYmove(L_FTPSTYPE, COL_OPTION_VALUES);
+		LYclrtoeol();
+		LYaddstr(fileSort_choices[HTfileSortMethod]);
+#endif /* VMS || USE_SLANG */
+	    }
+	    response = ' ';
+	    if (LYSelectPopups) {
+		HANDLE_LYOPTIONS;
+	    }
+	    break;
+
+	case 'P':		/* Change personal mail address for From headers. */
+	    if (non_empty(personal_mail_address)) {
+		LYstrncpy(display_option,
+			  personal_mail_address,
+			  sizeof(display_option) - 1);
+	    } else {		/* clear the NONE */
+		LYmove(L_MAIL_ADDRESS, COL_OPTION_VALUES);
+		LYaddstr("    ");
+		*display_option = '\0';
+	    }
+	    _statusline(ACCEPT_DATA);
+	    LYmove(L_MAIL_ADDRESS, COL_OPTION_VALUES);
+	    lynx_start_bold();
+	    ch = LYgetstr(display_option, VISIBLE,
+			  sizeof(display_option), NORECALL);
+	    lynx_stop_bold();
+	    LYmove(L_MAIL_ADDRESS, COL_OPTION_VALUES);
+	    if (term_options || ch == -1) {
+		LYaddstr((personal_mail_address &&
+			  *personal_mail_address) ?
+			 personal_mail_address : "NONE");
+	    } else if (*display_option == '\0') {
+		FREE(personal_mail_address);
+		LYaddstr("NONE");
+	    } else {
+		StrAllocCopy(personal_mail_address, display_option);
+		LYaddstr(display_option);
+	    }
+	    LYclrtoeol();
+	    if (ch == -1) {
+		HTInfoMsg(CANCELLED);
+		HTInfoMsg("");
+	    } else {
+		_statusline(VALUE_ACCEPTED);
+	    }
+	    response = ' ';
+	    break;
+
+	case 'S':		/* Change case sensitivity for searches. */
+	    case_sensitive = LYChooseBoolean(case_sensitive,
+					     L_SSEARCH, -1,
+					     caseless_choices);
+	    response = ' ';
+	    break;
+
+	case '\001':		/* Change assume_charset setting. */
+	    if (use_assume_charset) {
+		int i, curval;
+		const char **assume_list;
+		assume_list = typecallocn(const char *, (unsigned)
+					    (LYNumCharsets + 1));
+
+		if (!assume_list) {
+		    outofmem(__FILE__, "options");
+		}
+		for (i = 0; i < LYNumCharsets; i++) {
+		    assume_list[i] = LYCharSet_UC[i].MIMEname;
+		}
+		curval = UCLYhndl_for_unspec;
+		if (curval == current_char_set && UCAssume_MIMEcharset) {
+		    curval = UCGetLYhndl_byMIME(UCAssume_MIMEcharset);
+		}
+		if (curval < 0)
+		    curval = LYRawMode ? current_char_set : 0;
+		if (!LYSelectPopups) {
+#ifndef ALL_CHARSETS_IN_O_MENU_SCREEN
+		    UCLYhndl_for_unspec =
+			assumed_doc_charset_map[(LYChooseEnum(charset_subsets[curval].assumed_idx,
+							      L_ASSUME_CHARSET, -1,
+							      assumed_charset_choices)
+						 ? 1
+						 : 0)];
+#else
+		    UCLYhndl_for_unspec =
+			LYChooseEnum(curval,
+				     L_ASSUME_CHARSET, -1,
+				     assume_list);
+#endif
+		} else {
+#ifndef ALL_CHARSETS_IN_O_MENU_SCREEN
+		    UCLYhndl_for_unspec =
+			assumed_doc_charset_map[(LYChoosePopup(charset_subsets[curval].assumed_idx,
+							       L_ASSUME_CHARSET, -1,
+							       assumed_charset_choices,
+							       0,
+							       FALSE,
+							       FALSE)
+						 ? 1
+						 : 0)];
+#else
+		    UCLYhndl_for_unspec =
+			LYChoosePopup(curval,
+				      L_ASSUME_CHARSET, -1,
+				      assume_list,
+				      0, FALSE, FALSE);
+#endif
+#if defined(VMS) || defined(USE_SLANG)
+		    LYmove(L_ASSUME_CHARSET, COL_OPTION_VALUES);
+		    LYclrtoeol();
+		    if (UCLYhndl_for_unspec >= 0)
+			LYaddstr(LYCharSet_UC[UCLYhndl_for_unspec].MIMEname);
+#endif /* VMS || USE_SLANG */
+		}
+
+		/*
+		 * Set the raw 8-bit or CJK mode defaults and character set if
+		 * changed.  - FM
+		 */
+		if (CurrentAssumeCharSet != UCLYhndl_for_unspec ||
+		    UCLYhndl_for_unspec != curval) {
+		    if (UCLYhndl_for_unspec != CurrentAssumeCharSet) {
+			StrAllocCopy(UCAssume_MIMEcharset,
+				     LYCharSet_UC[UCLYhndl_for_unspec].MIMEname);
+		    }
+		    if (HTCJK != JAPANESE)
+			LYRawMode = (BOOLEAN) (UCLYhndl_for_unspec == current_char_set);
+		    HTMLSetUseDefaultRawMode(current_char_set, LYRawMode);
+		    HTMLSetCharacterHandling(current_char_set);
+		    CurrentAssumeCharSet = UCLYhndl_for_unspec;
+		    CurrentRawMode = LYRawMode;
+#if !defined(VMS) && !defined(USE_SLANG)
+		    if (!LYSelectPopups)
+#endif /* !VMS && !USE_SLANG */
+		    {
+			LYmove(L_Rawmode, COL_OPTION_VALUES);
+			LYclrtoeol();
+			ShowBool(LYRawMode);
+		    }
+		}
+		FREE(assume_list);
+		response = ' ';
+		if (LYSelectPopups) {
+		    HANDLE_LYOPTIONS;
+		}
+	    } else {
+		_statusline(NEED_ADVANCED_USER_MODE);
+		AddValueAccepted = FALSE;
+	    }
+	    break;
+
+	case 'C':		/* Change display charset setting. */
+	    if (!LYSelectPopups) {
+#ifndef ALL_CHARSETS_IN_O_MENU_SCREEN
+		displayed_display_charset_idx = LYChooseEnum(displayed_display_charset_idx,
+							     L_Charset, -1,
+							     display_charset_choices);
+		current_char_set = display_charset_map[displayed_display_charset_idx];
+#else
+		current_char_set = LYChooseEnum(current_char_set,
+						L_Charset, -1,
+						LYchar_set_names);
+#endif
+	    } else {
+#ifndef ALL_CHARSETS_IN_O_MENU_SCREEN
+		displayed_display_charset_idx = LYChoosePopup(displayed_display_charset_idx,
+							      L_Charset, -1,
+							      display_charset_choices,
+							      0, FALSE, FALSE);
+		current_char_set = display_charset_map[displayed_display_charset_idx];
+#else
+		current_char_set = LYChoosePopup(current_char_set,
+						 L_Charset, -1,
+						 LYchar_set_names,
+						 0, FALSE, FALSE);
+#endif
+
+#if defined(VMS) || defined(USE_SLANG)
+		LYmove(L_Charset, COL_OPTION_VALUES);
+		LYclrtoeol();
+		LYaddstr(LYchar_set_names[current_char_set]);
+#endif /* VMS || USE_SLANG */
+	    }
+	    /*
+	     * Set the raw 8-bit or CJK mode defaults and character set if
+	     * changed.  - FM
+	     */
+	    if (CurrentCharSet != current_char_set) {
+		LYUseDefaultRawMode = TRUE;
+		HTMLUseCharacterSet(current_char_set);
+		CurrentCharSet = current_char_set;
+		CurrentRawMode = LYRawMode;
+#if !defined(VMS) && !defined(USE_SLANG)
+		if (!LYSelectPopups)
+#endif /* !VMS && !USE_SLANG */
+		{
+		    LYmove(L_Rawmode, COL_OPTION_VALUES);
+		    LYclrtoeol();
+		    ShowBool(LYRawMode);
+		}
+#ifdef CAN_SWITCH_DISPLAY_CHARSET
+		/* Deduce whether the user wants autoswitch: */
+		switch_display_charsets =
+		    (current_char_set == auto_display_charset
+		     || current_char_set == auto_other_display_charset);
+#endif
+	    }
+	    response = ' ';
+	    if (LYSelectPopups) {
+		HANDLE_LYOPTIONS;
+	    }
+	    break;
+
+	case 'O':		/* Change raw mode setting. */
+	    LYRawMode = LYChooseBoolean(LYRawMode, L_Rawmode, -1, bool_choices);
+	    /*
+	     * Set the LYUseDefaultRawMode value and character handling if
+	     * LYRawMode was changed.  - FM
+	     */
+	    if (CurrentRawMode != LYRawMode) {
+		HTMLSetUseDefaultRawMode(current_char_set, LYRawMode);
+		HTMLSetCharacterHandling(current_char_set);
+		CurrentRawMode = LYRawMode;
+	    }
+	    response = ' ';
+	    break;
+
+	case 'G':		/* Change language preference. */
+	    if (non_empty(language)) {
+		LYstrncpy(display_option, language, sizeof(display_option) - 1);
+	    } else {		/* clear the NONE */
+		LYmove(L_LANGUAGE, COL_OPTION_VALUES);
+		LYaddstr("    ");
+		*display_option = '\0';
+	    }
+	    _statusline(ACCEPT_DATA);
+	    LYmove(L_LANGUAGE, COL_OPTION_VALUES);
+	    lynx_start_bold();
+	    ch = LYgetstr(display_option, VISIBLE,
+			  sizeof(display_option), NORECALL);
+	    lynx_stop_bold();
+	    LYmove(L_LANGUAGE, COL_OPTION_VALUES);
+	    if (term_options || ch == -1) {
+		LYaddstr(non_empty(language) ?
+			 language : "NONE");
+	    } else if (*display_option == '\0') {
+		FREE(language);
+		LYaddstr("NONE");
+	    } else {
+		StrAllocCopy(language, display_option);
+		LYaddstr(display_option);
+	    }
+	    LYclrtoeol();
+	    if (ch == -1) {
+		HTInfoMsg(CANCELLED);
+		HTInfoMsg("");
+	    } else {
+		_statusline(VALUE_ACCEPTED);
+	    }
+	    response = ' ';
+	    break;
+
+	case 'H':		/* Change charset preference. */
+	    if (non_empty(pref_charset)) {
+		LYstrncpy(display_option,
+			  pref_charset,
+			  sizeof(display_option) - 1);
+	    } else {		/* clear the NONE */
+		LYmove(L_PREF_CHARSET, COL_OPTION_VALUES);
+		LYaddstr("    ");
+		*display_option = '\0';
+	    }
+	    _statusline(ACCEPT_DATA);
+	    LYmove(L_PREF_CHARSET, COL_OPTION_VALUES);
+	    lynx_start_bold();
+	    ch = LYgetstr(display_option, VISIBLE,
+			  sizeof(display_option), NORECALL);
+	    lynx_stop_bold();
+	    LYmove(L_PREF_CHARSET, COL_OPTION_VALUES);
+	    if (term_options || ch == -1) {
+		LYaddstr(non_empty(pref_charset) ?
+			 pref_charset : "NONE");
+	    } else if (*display_option == '\0') {
+		FREE(pref_charset);
+		LYaddstr("NONE");
+	    } else {
+		StrAllocCopy(pref_charset, display_option);
+		LYaddstr(display_option);
+	    }
+	    LYclrtoeol();
+	    if (ch == -1) {
+		HTInfoMsg(CANCELLED);
+		HTInfoMsg("");
+	    } else {
+		_statusline(VALUE_ACCEPTED);
+	    }
+	    response = ' ';
+	    break;
+
+	case 'V':		/* Change VI keys setting. */
+	    vi_keys = LYChooseBoolean(vi_keys,
+				      L_Bool_A, C_VIKEYS,
+				      bool_choices);
+	    if (vi_keys) {
+		set_vi_keys();
+	    } else {
+		reset_vi_keys();
+	    }
+	    response = ' ';
+	    break;
+
+	case 'M':		/* Change emacs keys setting. */
+	    emacs_keys = LYChooseBoolean(emacs_keys,
+					 L_Bool_A, C_EMACSKEYS,
+					 bool_choices);
+	    if (emacs_keys) {
+		set_emacs_keys();
+	    } else {
+		reset_emacs_keys();
+	    }
+	    response = ' ';
+	    break;
+
+	case 'W':		/* Change show dotfiles setting. */
+	    if (no_dotfiles) {
+		_statusline(DOTFILE_ACCESS_DISABLED);
+	    } else {
+		show_dotfiles = LYChooseBoolean(show_dotfiles,
+						L_Bool_A,
+						C_SHOW_DOTFILES,
+						bool_choices);
+	    }
+	    response = ' ';
+	    break;
+
+	case 'T':		/* Change select popups setting. */
+	    LYSelectPopups = LYChooseBoolean(LYSelectPopups,
+					     L_Bool_B,
+					     C_SELECT_POPUPS,
+					     bool_choices);
+	    response = ' ';
+	    break;
+
+#if defined(USE_SLANG) || defined(COLOR_CURSES)
+	case '&':		/* Change show color setting. */
+	    if (no_option_save) {
+#if defined(COLOR_CURSES)
+		if (!has_colors()) {
+		    char *terminal = LYGetEnv("TERM");
+
+		    if (terminal)
+			HTUserMsg2(COLOR_TOGGLE_DISABLED_FOR_TERM,
+				   terminal);
+		    else
+			HTUserMsg(COLOR_TOGGLE_DISABLED);
+		    break;
+		}
+#endif
+		LYShowColor = LYChooseEnum((LYShowColor - 1),
+					   L_Color,
+					   C_COLOR,
+					   bool_choices);
+		if (LYShowColor == 0) {
+		    LYShowColor = SHOW_COLOR_OFF;
+		} else {
+		    LYShowColor = SHOW_COLOR_ON;
+		}
+	    } else {		/* !no_option_save */
+		BOOLEAN again = FALSE;
+		int chosen;
+
+		/*
+		 * Copy strings into choice array.
+		 */
+		choices[0] = NULL;
+		StrAllocCopy(choices[0], "NEVER     ");
+		choices[1] = NULL;
+		StrAllocCopy(choices[1], "OFF       ");
+		choices[2] = NULL;
+		StrAllocCopy(choices[2], "ON        ");
+		choices[3] = NULL;
+#if defined(COLOR_CURSES)
+		if (!has_colors())
+		    StrAllocCopy(choices[3], "Always try");
+		else
+#endif
+		    StrAllocCopy(choices[3], "ALWAYS    ");
+		choices[4] = NULL;
+		do {
+		    if (!LYSelectPopups) {
+			chosen = LYChooseEnum(LYChosenShowColor,
+					      L_Color,
+					      C_COLOR,
+					      choices);
+		    } else {
+			chosen = LYChoosePopup(LYChosenShowColor,
+					       L_Color,
+					       C_COLOR,
+					       choices, 4, FALSE, FALSE);
+		    }
+#if defined(COLOR_CURSES)
+		    again = (BOOLEAN) (chosen == SHOW_COLOR_ON && !has_colors());
+		    if (again) {
+			char *terminal = LYGetEnv("TERM");
+
+			if (terminal)
+			    HTUserMsg2(COLOR_TOGGLE_DISABLED_FOR_TERM,
+				       terminal);
+			else
+			    HTUserMsg(COLOR_TOGGLE_DISABLED);
+		    }
+#endif
+		} while (again);
+		LYChosenShowColor = chosen;
+#if defined(VMS)
+		if (LYSelectPopups) {
+		    LYmove(L_Color, C_COLOR);
+		    LYclrtoeol();
+		    LYaddstr(choices[LYChosenShowColor]);
+		}
+#endif /* VMS */
+#if defined(COLOR_CURSES)
+		if (has_colors())
+#endif
+		    LYShowColor = chosen;
+		FREE(choices[0]);
+		FREE(choices[1]);
+		FREE(choices[2]);
+		FREE(choices[3]);
+	    }
+	    if (CurrentShowColor != LYShowColor) {
+		lynx_force_repaint();
+	    }
+	    CurrentShowColor = LYShowColor;
+#ifdef USE_SLANG
+	    SLtt_Use_Ansi_Colors = (LYShowColor > SHOW_COLOR_OFF ? TRUE : FALSE);
+#endif
+	    response = ' ';
+	    if (LYSelectPopups && !no_option_save) {
+		HANDLE_LYOPTIONS;
+	    }
+	    break;
+#endif /* USE_SLANG or COLOR_CURSES */
+
+	case '@':		/* Change show cursor setting. */
+	    LYShowCursor = LYChooseBoolean(LYShowCursor,
+					   L_Bool_B,
+					   C_SHOW_CURSOR,
+					   bool_choices);
+	    response = ' ';
+	    break;
+
+	case 'K':		/* Change keypad mode. */
+	    if (!LYSelectPopups) {
+		keypad_mode = LYChooseEnum(keypad_mode,
+					   L_Keypad, -1,
+					   keypad_choices);
+	    } else {
+		keypad_mode = LYChoosePopup(keypad_mode,
+					    L_Keypad, -1,
+					    keypad_choices,
+					    3, FALSE, FALSE);
+#if defined(VMS) || defined(USE_SLANG)
+		LYmove(L_Keypad, COL_OPTION_VALUES);
+		LYclrtoeol();
+		LYaddstr(keypad_choices[keypad_mode]);
+#endif /* VMS || USE_SLANG */
+	    }
+	    if (keypad_mode == NUMBERS_AS_ARROWS) {
+		set_numbers_as_arrows();
+	    } else {
+		reset_numbers_as_arrows();
+	    }
+	    response = ' ';
+	    if (LYSelectPopups) {
+		HANDLE_LYOPTIONS;
+	    }
+	    break;
+
+	case 'N':		/* Change line editor key bindings. */
+	    if (!LYSelectPopups) {
+		current_lineedit = LYChooseEnum(current_lineedit,
+						L_Lineed, -1,
+						LYLineeditNames);
+	    } else {
+		current_lineedit = LYChoosePopup(current_lineedit,
+						 L_Lineed, -1,
+						 LYLineeditNames,
+						 0, FALSE, FALSE);
+#if defined(VMS) || defined(USE_SLANG)
+		LYmove(L_Lineed, COL_OPTION_VALUES);
+		LYclrtoeol();
+		LYaddstr(LYLineeditNames[current_lineedit]);
+#endif /* VMS || USE_SLANG */
+	    }
+	    response = ' ';
+	    if (LYSelectPopups) {
+		HANDLE_LYOPTIONS;
+	    }
+	    break;
+
+#ifdef EXP_KEYBOARD_LAYOUT
+	case 'Y':		/* Change keyboard layout */
+	    if (!LYSelectPopups) {
+		current_layout = LYChooseEnum(current_layout,
+					      L_Layout, -1,
+					      LYKbLayoutNames);
+	    } else {
+		current_layout = LYChoosePopup(current_layout,
+					       L_Layout, -1,
+					       LYKbLayoutNames,
+					       0, FALSE, FALSE);
+#if defined(VMS) || defined(USE_SLANG)
+		LYmove(L_Layout, COL_OPTION_VALUES);
+		LYclrtoeol();
+		LYaddstr(LYKbLayoutNames[current_layout]);
+#endif /* VMS || USE_SLANG */
+	    }
+	    response = ' ';
+	    if (LYSelectPopups) {
+		HANDLE_LYOPTIONS;
+	    }
+	    break;
+#endif /* EXP_KEYBOARD_LAYOUT */
+
+#ifdef DIRED_SUPPORT
+	case 'I':		/* Change local directory sorting. */
+	    if (!LYSelectPopups) {
+		dir_list_style = LYChooseEnum(dir_list_style,
+					      L_Dired, -1,
+					      dirList_choices);
+	    } else {
+		dir_list_style = LYChoosePopup(dir_list_style,
+					       L_Dired, -1,
+					       dirList_choices,
+					       3, FALSE, FALSE);
+#if defined(VMS) || defined(USE_SLANG)
+		LYmove(L_Dired, COL_OPTION_VALUES);
+		LYclrtoeol();
+		LYaddstr(dirList_choices[dir_list_style]);
+#endif /* VMS || USE_SLANG */
+	    }
+	    response = ' ';
+	    if (LYSelectPopups) {
+		HANDLE_LYOPTIONS;
+	    }
+	    break;
+#endif /* DIRED_SUPPORT */
+
+	case 'U':		/* Change user mode. */
+	    if (!LYSelectPopups) {
+		user_mode = LYChooseEnum(user_mode,
+					 L_User_Mode, -1,
+					 userMode_choices);
+		use_assume_charset = (BOOLEAN) (user_mode >= 2);
+	    } else {
+		user_mode = LYChoosePopup(user_mode,
+					  L_User_Mode, -1,
+					  userMode_choices,
+					  3, FALSE, FALSE);
+		use_assume_charset = (BOOLEAN) (user_mode >= 2);
+#if defined(VMS) || defined(USE_SLANG)
+		if (use_assume_charset == old_use_assume_charset) {
+		    LYmove(L_User_Mode, COL_OPTION_VALUES);
+		    LYclrtoeol();
+		    LYaddstr(userMode_choices[user_mode]);
+		}
+#endif /* VMS || USE_SLANG */
+	    }
+	    LYSetDisplayLines();
+	    response = ' ';
+	    if (LYSelectPopups) {
+		HANDLE_LYOPTIONS;
+	    }
+	    break;
+
+	case '!':
+	    if (!LYSelectPopups) {
+		verbose_img = LYChooseBoolean(verbose_img,
+					      L_VERBOSE_IMAGES,
+					      C_VERBOSE_IMAGES,
+					      bool_choices);
+	    } else {
+		verbose_img = (BOOLEAN) LYChoosePopup(verbose_img,
+						      L_VERBOSE_IMAGES,
+						      C_VERBOSE_IMAGES,
+						      bool_choices,
+						      2, FALSE, FALSE);
+	    }
+	    response = ' ';
+	    if (LYSelectPopups) {
+		HANDLE_LYOPTIONS;
+	    }
+	    break;
+
+	case 'A':		/* Change user agent string. */
+	    if (!no_useragent) {
+		if (non_empty(LYUserAgent)) {
+		    LYstrncpy(display_option,
+			      LYUserAgent,
+			      sizeof(display_option) - 1);
+		} else {	/* clear the NONE */
+		    LYmove(L_HOME, COL_OPTION_VALUES);
+		    LYaddstr("    ");
+		    *display_option = '\0';
+		}
+		_statusline(ACCEPT_DATA_OR_DEFAULT);
+		LYmove(L_User_Agent, COL_OPTION_VALUES);
+		lynx_start_bold();
+		ch = LYgetstr(display_option, VISIBLE,
+			      sizeof(display_option), NORECALL);
+		lynx_stop_bold();
+		LYmove(L_User_Agent, COL_OPTION_VALUES);
+		if (term_options || ch == -1) {
+		    LYaddstr((LYUserAgent &&
+			      *LYUserAgent) ?
+			     LYUserAgent : "NONE");
+		} else if (*display_option == '\0') {
+		    StrAllocCopy(LYUserAgent, LYUserAgentDefault);
+		    LYaddstr((LYUserAgent &&
+			      *LYUserAgent) ?
+			     LYUserAgent : "NONE");
+		} else {
+		    StrAllocCopy(LYUserAgent, display_option);
+		    LYaddstr(display_option);
+		}
+		LYclrtoeol();
+		if (ch == -1) {
+		    HTInfoMsg(CANCELLED);
+		    HTInfoMsg("");
+		} else if (!LYCheckUserAgent()) {
+		    _statusline(UA_PLEASE_USE_LYNX);
+		} else {
+		    _statusline(VALUE_ACCEPTED);
+		}
+	    } else {		/* disallowed */
+		_statusline(UA_CHANGE_DISABLED);
+	    }
+	    response = ' ';
+	    break;
+
+#if defined(ENABLE_OPTS_CHANGE_EXEC) && (defined(EXEC_LINKS) || defined(EXEC_SCRIPTS))
+	case 'X':		/* Change local exec restriction. */
+	    if (exec_frozen && !LYSelectPopups) {
+		_statusline(CHANGE_OF_SETTING_DISALLOWED);
+		response = ' ';
+		break;
+	    }
+#ifndef NEVER_ALLOW_REMOTE_EXEC
+	    if (local_exec) {
+		itmp = 2;
+	    } else
+#endif /* !NEVER_ALLOW_REMOTE_EXEC */
+	    {
+		if (local_exec_on_local_files) {
+		    itmp = 1;
+		} else {
+		    itmp = 0;
+		}
+	    }
+	    if (!LYSelectPopups) {
+		itmp = LYChooseEnum(itmp,
+				    L_Exec, -1,
+				    exec_choices);
+	    } else {
+		itmp = LYChoosePopup(itmp,
+				     L_Exec, -1,
+				     exec_choices,
+				     0, (exec_frozen ? TRUE : FALSE),
+				     FALSE);
+#if defined(VMS) || defined(USE_SLANG)
+		LYmove(L_Exec, COL_OPTION_VALUES);
+		LYclrtoeol();
+		LYaddstr(exec_choices[itmp]);
+#endif /* VMS || USE_SLANG */
+	    }
+	    if (!exec_frozen) {
+		switch (itmp) {
+		case 0:
+		    local_exec = FALSE;
+		    local_exec_on_local_files = FALSE;
+		    break;
+		case 1:
+		    local_exec = FALSE;
+		    local_exec_on_local_files = TRUE;
+		    break;
+#ifndef NEVER_ALLOW_REMOTE_EXEC
+		case 2:
+		    local_exec = TRUE;
+		    local_exec_on_local_files = FALSE;
+		    break;
+#endif /* !NEVER_ALLOW_REMOTE_EXEC */
+		}		/* end switch */
+	    }
+	    response = ' ';
+	    if (LYSelectPopups) {
+		HANDLE_LYOPTIONS;
+	    }
+	    break;
+#endif /* ENABLE_OPTS_CHANGE_EXEC */
+
+	case '>':		/* Save current options to RC file. */
+	    if (!no_option_save) {
+		HTInfoMsg(SAVING_OPTIONS);
+		LYrcShowColor = LYChosenShowColor;
+		if (save_rc(NULL)) {
+		    HTInfoMsg(OPTIONS_SAVED);
+		} else {
+		    HTAlert(OPTIONS_NOT_SAVED);
+		}
+	    } else {
+		HTInfoMsg(R_TO_RETURN_TO_LYNX);
+		/*
+		 * Change response so that we don't exit the options menu.
+		 */
+		response = ' ';
+	    }
+	    break;
+
+	case 'R':		/* Return to document (quit options menu). */
+	    break;
+
+	default:
+	    if (!no_option_save) {
+		HTInfoMsg(SAVE_OR_R_TO_RETURN_TO_LYNX);
+	    } else {
+		HTInfoMsg(R_TO_RETURN_TO_LYNX);
+	    }
+	}			/* end switch */
+    }				/* end while */
+
+    term_options = FALSE;
+    LYStatusLine = -1;		/* let user_mode have some of the screen */
+    signal(SIGINT, cleanup_sig);
+}
+
+static int widest_choice(const char **choices)
+{
+    int n, width = 0;
+
+    for (n = 0; choices[n] != NULL; ++n) {
+	int len = (int) strlen(choices[n]);
+
+	if (width < len)
+	    width = len;
+    }
+    return width;
+}
+
+static void show_choice(const char *choice,
+			int width)
+{
+    int len = (int) strlen(choice);
+
+    LYaddstr(choice);
+    while (len++ < width)
+	LYaddch(' ');
+}
+
+/*
+ * Take a status code, prompt the user for a new status, and return it.
+ */
+static int boolean_choice(int cur_choice,
+			  int line,
+			  int column,
+			  const char **choices)
+{
+    int response = 0;
+    int cmd = 0;
+    int number = 0;
+    int col = (column >= 0 ? column : COL_OPTION_VALUES);
+    int orig_choice = cur_choice;
+    int width = widest_choice(choices);
+
+    /*
+     * Get the number of choices and then make number zero-based.
+     */
+    for (number = 0; choices[number] != NULL; number++) ;	/* empty loop body */
+    number--;
+
+    /*
+     * Update the statusline.
+     */
+    _statusline(ANY_KEY_CHANGE_RET_ACCEPT);
+
+    /*
+     * Highlight the current choice.
+     */
+    LYmove(line, col);
+    lynx_start_reverse();
+    show_choice(choices[cur_choice], width);
+    if (LYShowCursor)
+	LYmove(line, (col - 1));
+    LYrefresh();
+
+    /*
+     * Get the keyboard entry, and leave the cursor at the choice, to indicate
+     * that it can be changed, until the user accepts the current choice.
+     */
+    term_options = FALSE;
+    while (1) {
+	LYmove(line, col);
+	if (term_options == FALSE) {
+	    response = LYgetch_single();
+	}
+	if (term_options || LYCharIsINTERRUPT_NO_letter(response)) {
+	    /*
+	     * Control-C or Control-G.
+	     */
+	    response = '\n';
+	    term_options = TRUE;
+	    cur_choice = orig_choice;
+	}
+#ifdef VMS
+	if (HadVMSInterrupt) {
+	    HadVMSInterrupt = FALSE;
+	    response = '\n';
+	    term_options = TRUE;
+	    cur_choice = orig_choice;
+	}
+#endif /* VMS */
+	if ((response != '\n' && response != '\r') &&
+	    (cmd = LKC_TO_LAC(keymap, response)) != LYK_ACTIVATE) {
+	    switch (cmd) {
+	    case LYK_HOME:
+		cur_choice = 0;
+		break;
+
+	    case LYK_END:
+		cur_choice = number;
+		break;
+
+	    case LYK_REFRESH:
+		lynx_force_repaint();
+		LYrefresh();
+		break;
+
+	    case LYK_QUIT:
+	    case LYK_ABORT:
+	    case LYK_PREV_DOC:
+		cur_choice = orig_choice;
+		term_options = TRUE;
+		break;
+
+	    case LYK_PREV_PAGE:
+	    case LYK_UP_HALF:
+	    case LYK_UP_TWO:
+	    case LYK_PREV_LINK:
+	    case LYK_LPOS_PREV_LINK:
+	    case LYK_FASTBACKW_LINK:
+	    case LYK_UP_LINK:
+	    case LYK_LEFT_LINK:
+		if (cur_choice == 0)
+		    cur_choice = number;	/* go back to end */
+		else
+		    cur_choice--;
+		break;
+
+	    case LYK_1:
+	    case LYK_2:
+	    case LYK_3:
+	    case LYK_4:
+	    case LYK_5:
+	    case LYK_6:
+	    case LYK_7:
+	    case LYK_8:
+	    case LYK_9:
+		if ((cmd - LYK_1 + 1) <= number) {
+		    cur_choice = cmd - LYK_1 + 1;
+		    break;
+		}		/* else fall through! */
+	    default:
+		if (cur_choice == number)
+		    cur_choice = 0;	/* go over the top and around */
+		else
+		    cur_choice++;
+	    }			/* end of switch */
+	    show_choice(choices[cur_choice], width);
+	    if (LYShowCursor)
+		LYmove(line, (col - 1));
+	    LYrefresh();
+	} else {
+	    /*
+	     * Unhighlight choice.
+	     */
+	    LYmove(line, col);
+	    lynx_stop_reverse();
+	    show_choice(choices[cur_choice], width);
+
+	    if (term_options) {
+		term_options = FALSE;
+		HTInfoMsg(CANCELLED);
+		HTInfoMsg("");
+	    } else {
+		_statusline(VALUE_ACCEPTED);
+	    }
+	    return cur_choice;
+	}
+    }
+}
+#endif /* !NO_OPTION_MENU */
+
+static void terminate_options(int sig GCC_UNUSED)
+{
+    term_options = TRUE;
+    /*
+     * Reassert the AST.
+     */
+    signal(SIGINT, terminate_options);
+#ifdef VMS
+    /*
+     * Refresh the screen to get rid of the "interrupt" message.
+     */
+    if (!dump_output_immediately) {
+	lynx_force_repaint();
+	LYrefresh();
+    }
+#endif /* VMS */
+}
+
+/*
+ * Multi-Bookmark On-Line editing support.  - FMG & FM
+ */
+void edit_bookmarks(void)
+{
+    int response = 0, def_response = 0;
+    int MBM_current = 1;
+
+#define MULTI_OFFSET 8
+    int a;			/* misc counter */
+    char MBM_tmp_line[LY_MAXPATH];	/* buffer for LYgetstr */
+
+    /*
+     * We need (MBM_V_MAXFILES + MULTI_OFFSET) lines to display the whole list
+     * at once.  Otherwise break it up into two segments.  We know it won't be
+     * less than that because 'o'ptions needs 23-24 at LEAST.
+     */
+    term_options = FALSE;
+    signal(SIGINT, terminate_options);
+
+  draw_bookmark_list:
+    /*
+     * Display menu of bookmarks.  NOTE that we avoid printw()'s to increase
+     * the chances that any non-ASCII or multibyte/CJK characters will be
+     * handled properly.  - FM
+     */
+#if defined(FANCY_CURSES) || defined (USE_SLANG)
+    if (enable_scrollback) {
+	LYclear();
+    } else {
+	LYerase();
+    }
+#else
+    LYclear();
+#endif /* FANCY_CURSES || USE_SLANG */
+    LYmove(0, 5);
+    lynx_start_h1_color();
+    if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET)) {
+	char *ehead_buffer = 0;
+
+	HTSprintf0(&ehead_buffer, MULTIBOOKMARKS_EHEAD_MASK, MBM_current);
+	LYaddstr(ehead_buffer);
+	FREE(ehead_buffer);
+    } else {
+	LYaddstr(MULTIBOOKMARKS_EHEAD);
+    }
+    lynx_stop_h1_color();
+
+    if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET)) {
+	for (a = ((MBM_V_MAXFILES / 2 + 1) * (MBM_current - 1));
+	     a <= (MBM_current * MBM_V_MAXFILES / 2); a++) {
+	    LYmove((3 + a) - ((MBM_V_MAXFILES / 2 + 1) * (MBM_current - 1)), 5);
+	    LYaddch(UCH(LYindex2MBM(a)));
+	    LYaddstr(" : ");
+	    if (MBM_A_subdescript[a])
+		LYaddstr(MBM_A_subdescript[a]);
+	    LYmove((3 + a) - ((MBM_V_MAXFILES / 2 + 1) * (MBM_current - 1)), 35);
+	    LYaddstr("| ");
+	    if (MBM_A_subbookmark[a]) {
+		LYaddstr(MBM_A_subbookmark[a]);
+	    }
+	}
+    } else {
+	for (a = 0; a <= MBM_V_MAXFILES; a++) {
+	    LYmove(3 + a, 5);
+	    LYaddch(UCH(LYindex2MBM(a)));
+	    LYaddstr(" : ");
+	    if (MBM_A_subdescript[a])
+		LYaddstr(MBM_A_subdescript[a]);
+	    LYmove(3 + a, 35);
+	    LYaddstr("| ");
+	    if (MBM_A_subbookmark[a]) {
+		LYaddstr(MBM_A_subbookmark[a]);
+	    }
+	}
+    }
+
+    /*
+     * Only needed when we have 2 screens.
+     */
+    if (LYlines < MBM_V_MAXFILES + MULTI_OFFSET) {
+	LYmove((LYlines - 4), 0);
+	LYaddstr("'");
+	lynx_start_bold();
+	LYaddstr("[");
+	lynx_stop_bold();
+	LYaddstr("' ");
+	LYaddstr(PREVIOUS);
+	LYaddstr(", '");
+	lynx_start_bold();
+	LYaddstr("]");
+	lynx_stop_bold();
+	LYaddstr("' ");
+	LYaddstr(NEXT_SCREEN);
+    }
+
+    LYmove((LYlines - 3), 0);
+    if (!no_option_save) {
+	LYaddstr("'");
+	lynx_start_bold();
+	LYaddstr(">");
+	lynx_stop_bold();
+	LYaddstr("'");
+	LYaddstr(TO_SAVE_SEGMENT);
+    }
+    LYaddstr(OR_SEGMENT);
+    LYaddstr("'");
+    lynx_start_bold();
+    LYaddstr("^G");
+    lynx_stop_bold();
+    LYaddstr("'");
+    LYaddstr(TO_RETURN_SEGMENT);
+
+    while (!term_options &&
+	   !LYisNonAlnumKeyname(response, LYK_PREV_DOC) &&
+	   !LYCharIsINTERRUPT_NO_letter(response) && response != '>') {
+
+	LYmove((LYlines - 2), 0);
+	lynx_start_prompt_color();
+	LYaddstr(MULTIBOOKMARKS_LETTER);
+	lynx_stop_prompt_color();
+
+	LYrefresh();
+	response = (def_response ? def_response : LYgetch_single());
+	def_response = 0;
+
+	/*
+	 * Check for a cancel.
+	 */
+	if (term_options || LYCharIsINTERRUPT_NO_letter(response) ||
+	    LYisNonAlnumKeyname(response, LYK_PREV_DOC))
+	    continue;
+
+	/*
+	 * Check for a save.
+	 */
+	if (response == '>') {
+	    if (!no_option_save) {
+		HTInfoMsg(SAVING_OPTIONS);
+		if (save_rc(NULL))
+		    HTInfoMsg(OPTIONS_SAVED);
+		else
+		    HTAlert(OPTIONS_NOT_SAVED);
+	    } else {
+		HTInfoMsg(R_TO_RETURN_TO_LYNX);
+		/*
+		 * Change response so that we don't exit the options menu.
+		 */
+		response = ' ';
+	    }
+	    continue;
+	}
+
+	/*
+	 * Check for a refresh.
+	 */
+	if (LYisNonAlnumKeyname(response, LYK_REFRESH)) {
+	    lynx_force_repaint();
+	    continue;
+	}
+
+	/*
+	 * Move between the screens - if we can't show it all at once.
+	 */
+	if ((response == ']' ||
+	     LYisNonAlnumKeyname(response, LYK_NEXT_PAGE)) &&
+	    LYlines < (MBM_V_MAXFILES + MULTI_OFFSET)) {
+	    MBM_current++;
+	    if (MBM_current >= 3)
+		MBM_current = 1;
+	    goto draw_bookmark_list;
+	}
+	if ((response == '[' ||
+	     LYisNonAlnumKeyname(response, LYK_PREV_PAGE)) &&
+	    LYlines < (MBM_V_MAXFILES + MULTI_OFFSET)) {
+	    MBM_current--;
+	    if (MBM_current <= 0)
+		MBM_current = 2;
+	    goto draw_bookmark_list;
+	}
+
+	/*
+	 * Instead of using 26 case statements, we set up a scan through the
+	 * letters and edit the lines that way.
+	 */
+	for (a = 0; a <= MBM_V_MAXFILES; a++) {
+	    if (LYMBM2index(response) == a) {
+		if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET)) {
+		    if (MBM_current == 1 && a > (MBM_V_MAXFILES / 2)) {
+			MBM_current = 2;
+			def_response = response;
+			goto draw_bookmark_list;
+		    }
+		    if (MBM_current == 2 && a < (MBM_V_MAXFILES / 2)) {
+			MBM_current = 1;
+			def_response = response;
+			goto draw_bookmark_list;
+		    }
+		}
+		_statusline(ACCEPT_DATA);
+
+		if (a > 0) {
+		    lynx_start_bold();
+		    if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET))
+			LYmove((3 + a)
+			       - ((MBM_V_MAXFILES / 2 + 1) * (MBM_current - 1)),
+			       9);
+		    else
+			LYmove((3 + a), 9);
+		    LYstrncpy(MBM_tmp_line,
+			      (!MBM_A_subdescript[a] ?
+			       "" : MBM_A_subdescript[a]),
+			      sizeof(MBM_tmp_line) - 1);
+		    (void) LYgetstr(MBM_tmp_line, VISIBLE,
+				    sizeof(MBM_tmp_line), NORECALL);
+		    lynx_stop_bold();
+
+		    if (strlen(MBM_tmp_line) < 1) {
+			FREE(MBM_A_subdescript[a]);
+		    } else {
+			StrAllocCopy(MBM_A_subdescript[a], MBM_tmp_line);
+		    }
+		    if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET))
+			LYmove((3 + a)
+			       - ((MBM_V_MAXFILES / 2 + 1)
+				  * (MBM_current - 1)),
+			       5);
+		    else
+			LYmove((3 + a), 5);
+		    LYaddch(UCH(LYindex2MBM(a)));
+		    LYaddstr(" : ");
+		    if (MBM_A_subdescript[a])
+			LYaddstr(MBM_A_subdescript[a]);
+		    LYclrtoeol();
+		    LYrefresh();
+		}
+
+		if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET))
+		    LYmove((3 + a)
+			   - ((MBM_V_MAXFILES / 2 + 1)
+			      * (MBM_current - 1)),
+			   35);
+		else
+		    LYmove((3 + a), 35);
+		LYaddstr("| ");
+
+		lynx_start_bold();
+		LYstrncpy(MBM_tmp_line,
+			  NonNull(MBM_A_subbookmark[a]),
+			  sizeof(MBM_tmp_line) - 1);
+		(void) LYgetstr(MBM_tmp_line, VISIBLE,
+				sizeof(MBM_tmp_line), NORECALL);
+		lynx_stop_bold();
+
+		if (*MBM_tmp_line == '\0') {
+		    if (a == 0)
+			StrAllocCopy(MBM_A_subbookmark[a], bookmark_page);
+		    else
+			FREE(MBM_A_subbookmark[a]);
+		} else if (!LYPathOffHomeOK(MBM_tmp_line,
+					    sizeof(MBM_tmp_line))) {
+		    LYMBM_statusline(USE_PATH_OFF_HOME);
+		    LYSleepAlert();
+		} else {
+		    StrAllocCopy(MBM_A_subbookmark[a], MBM_tmp_line);
+		    if (a == 0) {
+			StrAllocCopy(bookmark_page, MBM_A_subbookmark[a]);
+		    }
+		}
+		if (LYlines < (MBM_V_MAXFILES + MULTI_OFFSET))
+		    LYmove((3 + a)
+			   - ((MBM_V_MAXFILES / 2 + 1)
+			      * (MBM_current - 1)),
+			   35);
+		else
+		    LYmove((3 + a), 35);
+		LYaddstr("| ");
+		if (MBM_A_subbookmark[a])
+		    LYaddstr(MBM_A_subbookmark[a]);
+		LYclrtoeol();
+		LYmove(LYlines - 1, 0);
+		LYclrtoeol();
+		break;
+	    }
+	}			/* end for */
+    }				/* end while */
+
+    term_options = FALSE;
+    signal(SIGINT, cleanup_sig);
+}
+
+#if defined(USE_CURSES_PADS) || !defined(NO_OPTION_MENU) || (defined(USE_MOUSE) && (defined(NCURSES) || defined(PDCURSES)))
+
+/*
+ * This function offers the choices for values of an option via a popup window
+ * which functions like that for selection of options in a form.  - FM
+ *
+ * Also used for mouse popups with ncurses; this is indicated by for_mouse.
+ */
+int popup_choice(int cur_choice,
+		 int line,
+		 int column,
+		 const char **choices,
+		 int i_length,
+		 int disabled,
+		 BOOLEAN for_mouse)
+{
+    if (column < 0)
+	column = (COL_OPTION_VALUES - 1);
+
+    term_options = FALSE;
+    cur_choice = LYhandlePopupList(cur_choice,
+				   line,
+				   column,
+				   (const char **) choices,
+				   -1,
+				   i_length,
+				   disabled,
+				   for_mouse);
+    switch (cur_choice) {
+    case LYK_QUIT:
+    case LYK_ABORT:
+    case LYK_PREV_DOC:
+	term_options = TRUE;
+	if (!for_mouse) {
+	    HTUserMsg(CANCELLED);
+	}
+	break;
+    }
+
+    if (disabled || term_options) {
+	_statusline("");
+    } else if (!for_mouse) {
+	_statusline(VALUE_ACCEPTED);
+    }
+    return (cur_choice);
+}
+
+#endif /* !NO_OPTION_MENU */
+#ifndef NO_OPTION_FORMS
+
+/*
+ * I'm paranoid about mistyping strings.  Also, this way they get combined
+ * so we don't have to worry about the intelligence of the compiler.
+ * We don't need to burn memory like it's cheap.  We're better than that.
+ */
+#define SELECTED(flag) (flag) ? selected_string : ""
+#define DISABLED(flag) (flag) ? disabled_string : ""
+
+typedef struct {
+    int value;
+    const char *LongName;
+    const char *HtmlName;
+} OptValues;
+
+typedef struct {
+    char *tag;
+    char *value;
+} PostPair;
+
+static const char selected_string[] = "selected";
+static const char disabled_string[] = "disabled";
+static const char on_string[] = N_("ON");
+static const char off_string[] = N_("OFF");
+static const char never_string[] = N_("NEVER");
+static const char always_string[] = N_("ALWAYS");
+static OptValues bool_values[] =
+{
+    {FALSE, N_("OFF"), "OFF"},
+    {TRUE, N_("ON"), "ON"},
+    {0, 0, 0}
+};
+
+static const char *secure_string = "secure";
+static char *secure_value = NULL;
+static const char *save_options_string = "save_options";
+
+/*
+ * Personal Preferences
+ */
+static const char *cookies_string = RC_SET_COOKIES;
+static const char *cookies_ignore_all_string = N_("ignore");
+static const char *cookies_up_to_user_string = N_("ask user");
+static const char *cookies_accept_all_string = N_("accept all");
+static const char *x_display_string = RC_DISPLAY;
+static const char *editor_string = RC_FILE_EDITOR;
+static const char *emacs_keys_string = RC_EMACS_KEYS;
+
+#if defined(ENABLE_OPTS_CHANGE_EXEC) && (defined(EXEC_LINKS) || defined(EXEC_SCRIPTS))
+#define EXEC_ALWAYS 2
+#define EXEC_LOCAL  1
+#define EXEC_NEVER  0
+static const char *exec_links_string = RC_RUN_ALL_EXECUTION_LINKS;
+static OptValues exec_links_values[] =
+{
+    {EXEC_NEVER, N_("ALWAYS OFF"), "ALWAYS OFF"},
+    {EXEC_LOCAL, N_("FOR LOCAL FILES ONLY"), "FOR LOCAL FILES ONLY"},
+#ifndef NEVER_ALLOW_REMOTE_EXEC
+    {EXEC_ALWAYS, N_("ALWAYS ON"), "ALWAYS ON"},
+#endif
+    {0, 0, 0}
+};
+#endif /* ENABLE_OPTS_CHANGE_EXEC */
+
+#ifdef EXP_KEYBOARD_LAYOUT
+static const char *kblayout_string = RC_KBLAYOUT;
+#endif
+static const char *keypad_mode_string = RC_KEYPAD_MODE;
+static OptValues keypad_mode_values[] =
+{
+    {NUMBERS_AS_ARROWS, N_("Numbers act as arrows"),
+     "number_arrows"},
+    {LINKS_ARE_NUMBERED, N_("Links are numbered"),
+     "links_numbered"},
+    {LINKS_AND_FIELDS_ARE_NUMBERED,
+     N_("Links and form fields are numbered"),
+     "links_and_forms"},
+    {FIELDS_ARE_NUMBERED,
+     N_("Form fields are numbered"),
+     "forms_numbered"},
+    {0, 0, 0}
+};
+static const char *lineedit_mode_string = RC_LINEEDIT_MODE;
+static const char *mail_address_string = RC_PERSONAL_MAIL_ADDRESS;
+static const char *search_type_string = RC_CASE_SENSITIVE_SEARCHING;
+
+#ifndef DISABLE_FTP
+static const char *anonftp_password_string = RC_ANONFTP_PASSWORD;
+#endif
+
+static OptValues search_type_values[] =
+{
+    {FALSE, N_("Case insensitive"), "case_insensitive"},
+    {TRUE, N_("Case sensitive"), "case_sensitive"},
+    {0, 0, 0}
+};
+
+#if defined(USE_SLANG) || defined(COLOR_CURSES)
+static const char *show_color_string = RC_SHOW_COLOR;
+static OptValues show_color_values[] =
+{
+    {SHOW_COLOR_NEVER, never_string, never_string},
+    {SHOW_COLOR_OFF, off_string, off_string},
+    {SHOW_COLOR_ON, on_string, on_string},
+    {SHOW_COLOR_ALWAYS, always_string, always_string},
+    {0, 0, 0}
+};
+#endif
+
+static const char *show_cursor_string = RC_SHOW_CURSOR;
+
+static const char *underline_links_string = RC_UNDERLINE_LINKS;
+
+#ifdef USE_SCROLLBAR
+static const char *show_scrollbar_string = RC_SCROLLBAR;
+#endif
+
+static const char prompt_dft_string[] = N_("prompt normally");
+static const char prompt_yes_string[] = N_("force yes-response");
+static const char prompt_no_string[] = N_("force no-response");
+static OptValues prompt_values[] =
+{
+    {FORCE_PROMPT_DFT, prompt_dft_string, prompt_dft_string},
+    {FORCE_PROMPT_YES, prompt_yes_string, prompt_yes_string},
+    {FORCE_PROMPT_NO, prompt_no_string, prompt_no_string},
+    {0, 0, 0}
+};
+
+static const char *cookie_prompt_string = RC_FORCE_COOKIE_PROMPT;
+
+#ifdef USE_SSL
+static const char *ssl_prompt_string = RC_FORCE_SSL_PROMPT;
+#endif
+
+static const char *user_mode_string = RC_USER_MODE;
+static OptValues user_mode_values[] =
+{
+    {NOVICE_MODE, N_("Novice"), "Novice"},
+    {INTERMEDIATE_MODE, N_("Intermediate"), "Intermediate"},
+    {ADVANCED_MODE, N_("Advanced"), "Advanced"},
+    {0, 0, 0}
+};
+
+static const char *vi_keys_string = RC_VI_KEYS;
+
+static const char *visited_links_string = RC_VISITED_LINKS;
+static OptValues visited_links_values[] =
+{
+    {VISITED_LINKS_AS_FIRST_V, N_("By First Visit"), "first_visited"},
+    {VISITED_LINKS_AS_FIRST_V | VISITED_LINKS_REVERSE,
+     N_("By First Visit Reversed"), "first_visited_reversed"},
+    {VISITED_LINKS_AS_TREE, N_("As Visit Tree"), "visit_tree"},
+    {VISITED_LINKS_AS_LATEST, N_("By Last Visit"), "last_visited"},
+    {VISITED_LINKS_AS_LATEST | VISITED_LINKS_REVERSE,
+     N_("By Last Visit Reversed"), "last_visited_reversed"},
+    {0, 0, 0}
+};
+
+/*
+ * Document Layout
+ */
+static const char *DTD_recovery_string = RC_TAGSOUP;
+static OptValues DTD_type_values[] =
+{
+	/* Old_DTD variable */
+    {TRUE, N_("relaxed (TagSoup mode)"), "tagsoup"},
+    {FALSE, N_("strict (SortaSGML mode)"), "sortasgml"},
+    {0, 0, 0}
+};
+
+static const char *bad_html_string = RC_BAD_HTML;
+static OptValues bad_html_values[] =
+{
+    {BAD_HTML_IGNORE, N_("Ignore"), "ignore"},
+    {BAD_HTML_TRACE, N_("Add to trace-file"), "trace"},
+    {BAD_HTML_MESSAGE, N_("Add to LYNXMESSAGES"), "message"},
+    {BAD_HTML_WARN, N_("Warn, point to trace-file"), "warn"},
+    {0, 0, 0}
+};
+
+static const char *select_popups_string = RC_SELECT_POPUPS;
+static const char *images_string = "images";
+static const char *images_ignore_all_string = N_("ignore");
+static const char *images_use_label_string = N_("as labels");
+static const char *images_use_links_string = N_("as links");
+
+static const char *verbose_images_string = RC_VERBOSE_IMAGES;
+static OptValues verbose_images_type_values[] =
+{
+	/* verbose_img variable */
+    {FALSE, N_("OFF"), "OFF"},
+    {TRUE, N_("show filename"), "ON"},
+    {0, 0, 0}
+};
+
+/*
+ * Bookmark Options
+ */
+static const char *mbm_string = RC_MULTI_BOOKMARK;
+static OptValues mbm_values[] =
+{
+    {MBM_OFF, N_("OFF"), "OFF"},
+    {MBM_STANDARD, N_("STANDARD"), "STANDARD"},
+    {MBM_ADVANCED, N_("ADVANCED"), "ADVANCED"},
+    {0, 0, 0}
+};
+
+static const char *single_bookmark_string = RC_BOOKMARK_FILE;
+
+#ifdef USE_SESSIONS
+static const char *auto_session_string = RC_AUTO_SESSION;
+static const char *single_session_string = RC_SESSION_FILE;
+#endif
+
+/*
+ * Character Set Options
+ */
+static const char *assume_char_set_string = RC_ASSUME_CHARSET;
+static const char *display_char_set_string = RC_CHARACTER_SET;
+static const char *raw_mode_string = RC_RAW_MODE;
+
+#ifdef USE_LOCALE_CHARSET
+static const char *locale_charset_string = RC_LOCALE_CHARSET;
+#endif
+
+/*
+ * File Management Options
+ */
+static const char *show_dotfiles_string = RC_SHOW_DOTFILES;
+static const char *no_pause_string = RC_NO_PAUSE;
+
+#ifdef DIRED_SUPPORT
+static const char *dired_list_string = RC_DIR_LIST_STYLE;
+static OptValues dired_list_values[] =
+{
+    {DIRS_FIRST, N_("Directories first"), "dired_dir"},
+    {FILES_FIRST, N_("Files first"), "dired_files"},
+    {MIXED_STYLE, N_("Mixed style"), "dired_mixed"},
+    {0, 0, 0}
+};
+
+#ifdef LONG_LIST
+static const char *dired_sort_string = RC_DIR_LIST_ORDER;
+static OptValues dired_sort_values[] =
+{
+    {ORDER_BY_NAME, N_("By Name"), "dired_by_name"},
+    {ORDER_BY_TYPE, N_("By Type"), "dired_by_type"},
+    {ORDER_BY_SIZE, N_("By Size"), "dired_by_size"},
+    {ORDER_BY_DATE, N_("By Date"), "dired_by_date"},
+    {ORDER_BY_MODE, N_("By Mode"), "dired_by_mode"},
+#ifndef NO_GROUPS
+    {ORDER_BY_USER, N_("By User"), "dired_by_user"},
+    {ORDER_BY_GROUP, N_("By Group"), "dired_by_group"},
+#endif
+    {0, 0, 0}
+};
+#endif /* LONG_LIST */
+#endif /* DIRED_SUPPORT */
+
+#ifndef DISABLE_FTP
+static const char *passive_ftp_string = RC_FTP_PASSIVE;
+
+static const char *ftp_sort_string = RC_FILE_SORTING_METHOD;
+static OptValues ftp_sort_values[] =
+{
+    {FILE_BY_NAME, N_("By Name"), "ftp_by_name"},
+    {FILE_BY_TYPE, N_("By Type"), "ftp_by_type"},
+    {FILE_BY_SIZE, N_("By Size"), "ftp_by_size"},
+    {FILE_BY_DATE, N_("By Date"), "ftp_by_date"},
+    {0, 0, 0}
+};
+#endif
+
+#ifdef USE_READPROGRESS
+static const char *show_rate_string = RC_SHOW_KB_RATE;
+static OptValues rate_values[] =
+{
+    {rateOFF, N_("Do not show rate"), "rate_off"},
+    {rateBYTES, N_("Show %s/sec rate"), "rate_bytes"},
+    {rateKB, N_("Show %s/sec rate"), "rate_kb"},
+#ifdef USE_READPROGRESS
+    {rateEtaBYTES, N_("Show %s/sec, ETA"), "rate_eta_bytes"},
+    {rateEtaKB, N_("Show %s/sec, ETA"), "rate_eta_kb"},
+#endif
+#ifdef USE_PROGRESSBAR
+    {rateBAR, N_("Show progressbar"), "rate_bar"},
+#endif
+    {0, 0, 0}
+};
+#endif /* USE_READPROGRESS */
+
+/*
+ * Presentation (MIME) types used in "Accept".
+ */
+static const char *preferred_media_string = RC_PREFERRED_MEDIA_TYPES;
+static OptValues media_values[] =
+{
+    {mediaOpt1, N_("Accept lynx's internal types"), "media_opt1"},
+    {mediaOpt2, N_("Also accept lynx.cfg's types"), "media_opt2"},
+    {mediaOpt3, N_("Also accept user's types"), "media_opt3"},
+    {mediaOpt4, N_("Also accept system's types"), "media_opt4"},
+    {mediaALL, N_("Accept all types"), "media_all"},
+    {0, 0, 0}
+};
+
+static const char *preferred_encoding_string = RC_PREFERRED_ENCODING;
+static OptValues encoding_values[] =
+{
+    {encodingNONE, N_("None"), "encoding_none"},
+#if defined(USE_ZLIB) || defined(GZIP_PATH)
+    {encodingGZIP, N_("gzip"), "encoding_gzip"},
+    {encodingDEFLATE, N_("deflate"), "encoding_deflate"},
+#endif
+#if defined(USE_ZLIB) || defined(COMPRESS_PATH)
+    {encodingCOMPRESS, N_("compress"), "encoding_compress"},
+#endif
+#if defined(USE_BZLIB) || defined(BZIP2_PATH)
+    {encodingBZIP2, N_("bzip2"), "encoding_bzip2"},
+#endif
+    {encodingALL, N_("All"), "encoding_all"},
+    {0, 0, 0}
+};
+
+/*
+ * Headers transferred to remote server
+ */
+static const char *preferred_doc_char_string = RC_PREFERRED_CHARSET;
+static const char *preferred_doc_lang_string = RC_PREFERRED_LANGUAGE;
+static const char *send_user_agent_string = RC_SEND_USERAGENT;
+static const char *user_agent_string = RC_USERAGENT;
+
+#define PutHeader(fp, Name) \
+	fprintf(fp, "\n%s<em>%s</em>\n", MARGIN_STR, LYEntifyTitle(&buffer, Name));
+
+#define PutCheckBox(fp, Name, Value, disable) \
+	fprintf(fp,\
+	"<input type=\"checkbox\" name=\"%s\" %s %s>\n",\
+		Name, Value ? "checked" : "", disable_all?disabled_string:disable)
+
+#define PutTextInput(fp, Name, Value, Size, disable) \
+	fprintf(fp,\
+	"<input size=%d type=\"text\" name=\"%s\" value=\"%s\" %s>\n",\
+		(int) Size, Name, Value, disable_all?disabled_string:disable)
+
+#define PutOption(fp, flag, html, name) \
+	fprintf(fp,"<option value=\"%s\" %s>%s\n", html, SELECTED(flag), gettext(name))
+
+#define BeginSelect(fp, text) \
+	fprintf(fp,"<select name=\"%s\" %s>\n", text, disable_all?disabled_string:"")
+
+#define MaybeSelect(fp, flag, text) \
+	fprintf(fp,"<select name=\"%s\" %s>\n", text, disable_all?disabled_string:DISABLED(flag))
+
+#define EndSelect(fp)\
+	fprintf(fp,"</select>\n")
+
+static void PutOptValues(FILE *fp, int value,
+			 OptValues * table)
+{
+    while (table->LongName != 0) {
+	if (table->HtmlName) {
+	    PutOption(fp,
+		      value == table->value,
+		      table->HtmlName,
+		      table->LongName);
+	}
+	table++;
+    }
+}
+
+static BOOLEAN GetOptValues(OptValues * table, char *value,
+			    int *result)
+{
+    while (table->LongName != 0) {
+	if (table->HtmlName && !strcmp(value, table->HtmlName)) {
+	    *result = table->value;
+	    return TRUE;
+	}
+	table++;
+    }
+    return FALSE;
+}
+
+/*
+ * Break cgi line into array of pairs of pointers.  Don't bother trying to
+ * be efficient.  We're not called all that often.
+ * We come in with a string looking like:
+ * tag1=value1&tag2=value2&...&tagN=valueN
+ * We leave with an array of post_pairs.  The last element in the array
+ * will have a tag pointing to NULL.
+ * Not pretty, but works.  Hey, if strings can be null terminate arrays...
+ */
+static PostPair *break_data(bstring *data)
+{
+    char *p;
+    PostPair *q = NULL;
+    int count = 0;
+
+    if (isBEmpty(data))
+	return NULL;
+
+    p = BStrData(data);
+    CTRACE((tfp, "break_data %s\n", p));
+
+    q = typecalloc(PostPair);
+    if (q == NULL)
+	outofmem(__FILE__, "break_data(calloc)");
+
+    assert(q != NULL);
+
+    do {
+	/*
+	 * First, break up on '&', sliding 'p' on down the line.
+	 */
+	q[count].value = LYstrsep(&p, "&");
+	/*
+	 * Then break up on '=', sliding value down, and setting tag.
+	 */
+	q[count].tag = LYstrsep(&(q[count].value), "=");
+
+	/*
+	 * Clean them up a bit, in case user entered a funky string.
+	 */
+	HTUnEscape(q[count].tag);
+
+	/* In the value field we have '+' instead of ' '. So do a simple
+	 * find&replace on the value field before UnEscaping() - SKY
+	 */
+	{
+	    size_t i, len;
+
+	    len = strlen(q[count].value);
+	    for (i = 0; i < len; i++) {
+		if (q[count].value[i] == '+') {
+#ifdef UNIX
+		    /*
+		     * Allow for special case of options which begin with a "+" on
+		     * Unix - TD
+		     */
+		    if (i > 0
+			&& q[count].value[i + 1] == '+'
+			&& isalnum(UCH(q[count].value[i + 2]))) {
+			q[count].value[i++] = ' ';
+			i++;
+			continue;
+		    }
+#endif
+		    q[count].value[i] = ' ';
+		}
+	    }
+	}
+	HTUnEscape(q[count].value);
+	CTRACE((tfp, "...item[%d] tag=%s, value=%s\n",
+		count, q[count].tag, q[count].value));
+
+	count++;
+	/*
+	 * Like I said, screw efficiency.  Sides, realloc is fast on
+	 * Linux ;->
+	 */
+	q = typeRealloc(PostPair, q, (unsigned) (count + 1));
+	if (q == NULL)
+	    outofmem(__FILE__, "break_data(realloc)");
+
+	assert(q != NULL);
+
+	q[count].tag = NULL;
+    } while (p != NULL && p[0] != '\0');
+    return q;
+}
+
+static BOOL isLynxOptionsPage(const char *address, const char *portion)
+{
+    BOOL result = FALSE;
+
+    if (!strncasecomp(address, STR_LYNXOPTIONS, LEN_LYNXOPTIONS)) {
+	unsigned len = strlen(portion);
+
+	address += LEN_LYNXOPTIONS;
+	if (!strncasecomp(address, portion, (int) len)
+	    && (address[len] == '\0' || LYIsHtmlSep(address[len]))) {
+	    result = TRUE;
+	}
+    }
+    return result;
+}
+
+static int gen_options(char **newfile);
+
+/*
+ * Handle options from the pseudo-post.  I think we really only need
+ * post_data here, but bring along everything just in case.  It's only a
+ * pointer.  MRC
+ *
+ * Options are processed in order according to gen_options(), we should not
+ * depend on it and add boolean flags where the order is essential (save,
+ * character sets...)
+ *
+ * Security:  some options are disabled in gen_options() under certain
+ * conditions.  We *should* duplicate the same conditions here in postoptions()
+ * to prevent user with a limited access from editing HTML options code
+ * manually (e.g., doing 'e'dit in 'o'ptions) and submit it to access the
+ * restricted items.  Prevent spoofing attempts from index overrun. - LP
+ *
+ * Exit status: NULLFILE (reload) or NORMAL (use HText cache).
+ *
+ * On exit, got the document which was current before the Options menu:
+ *
+ *   (from cache) nothing changed or no visual effect supposed:
+ *             editor name, e-mail, etc.
+ *
+ *   (reload locally) to see the effect of certain changes:
+ *             display_char_set, assume_charset, etc.
+ *             (use 'need_reload' flag where necessary).
+ *
+ *   (reload from remote server and uncache on a proxy)
+ *             few options changes should be transferred to remote server:
+ *             preferred language, fake browser name, etc.
+ *             (use 'need_end_reload' flag).
+ */
+
+int postoptions(DocInfo *newdoc)
+{
+    PostPair *data = 0;
+    DocAddress WWWDoc;		/* need on exit */
+    int i;
+    int code = 0;
+    BOOLEAN save_all = FALSE;
+    int display_char_set_old = current_char_set;
+    int old_media_value = LYAcceptMedia;
+    BOOLEAN raw_mode_old = LYRawMode;
+    BOOLEAN assume_char_set_changed = FALSE;
+    BOOLEAN need_reload = FALSE;
+    BOOLEAN need_end_reload = FALSE;
+
+#if defined(USE_SLANG) || defined(COLOR_CURSES)
+    int CurrentShowColor = LYShowColor;
+#endif
+
+    /*-------------------------------------------------
+     * kludge a link from mbm_menu, the URL was:
+     * "<a href=\"" LYNXOPTIONS_PAGE(MBM_MENU) "\">Goto multi-bookmark menu</a>\n"
+     *--------------------------------------------------*/
+
+    if (isLynxOptionsPage(newdoc->address, MBM_LINK)) {
+	FREE(newdoc->post_data);
+	if (no_bookmark) {
+	    HTAlert(BOOKMARK_CHANGE_DISALLOWED);	/* anonymous */
+	    return (NULLFILE);
+	} else if (dump_output_immediately) {
+	    return (NOT_FOUND);
+	} else {
+	    edit_bookmarks();
+	    return (NULLFILE);
+	}
+    } else if (!isLynxOptionsPage(newdoc->address, "/")) {
+	HTAlert(RANDOM_URL_DISALLOWED);
+	return NULLFILE;
+    }
+
+    data = break_data(newdoc->post_data);
+
+    if (!data) {
+	int status;
+
+	/*-------------------------------------------------
+	 * kludge gen_options() call:
+	 *--------------------------------------------------*/
+	status = gen_options(&newdoc->address);
+	if (status != NORMAL) {
+	    HTAlwaysAlert("Unexpected way of accessing", newdoc->address);
+	    FREE(newdoc->address);
+	    return (status);
+	}
+
+	/* exit to getfile() cycle */
+	WWWDoc.address = newdoc->address;
+	WWWDoc.post_data = newdoc->post_data;
+	WWWDoc.post_content_type = newdoc->post_content_type;
+	WWWDoc.bookmark = newdoc->bookmark;
+	WWWDoc.isHEAD = newdoc->isHEAD;
+	WWWDoc.safe = newdoc->safe;
+
+	if (!HTLoadAbsolute(&WWWDoc))
+	    return (NOT_FOUND);
+	LYRegisterUIPage(newdoc->address, UIP_OPTIONS_MENU);
+#ifdef DIRED_SUPPORT
+	lynx_edit_mode = FALSE;
+#endif /* DIRED_SUPPORT */
+	return (NORMAL);
+    }
+
+    if (!LYIsUIPage3(HTLoadedDocumentURL(), UIP_OPTIONS_MENU, 0) &&
+	!LYIsUIPage3(HTLoadedDocumentURL(), UIP_VLINKS, 0)) {
+	char *buf = NULL;
+
+	/*  We may have been spoofed? */
+	HTSprintf0(&buf,
+		   gettext("Use %s to invoke the Options menu!"),
+		   key_for_func_ext(LYK_OPTIONS, FOR_PANEL));
+	HTAlert(buf);
+	FREE(buf);
+	FREE(data);
+	return (NOT_FOUND);
+    }
+
+    /*
+     * Checkbox will be missing from data if unchecked.
+     */
+    LYSendUserAgent = FALSE;
+
+    for (i = 0; data[i].tag != NULL; i++) {
+	/*
+	 * This isn't really for security, but rather for avoiding that the
+	 * user may revisit an older instance from the history stack and submit
+	 * stuff which accidentally undoes changes that had been done from a
+	 * newer instance.  - kw
+	 */
+	if (!strcmp(data[i].tag, secure_string)) {
+	    if (!secure_value || strcmp(data[i].value, secure_value)) {
+		char *buf = NULL;
+
+		/*
+		 * We probably came from an older instance of the Options
+		 * page that had been on the history stack. - kw
+		 */
+		HTSprintf0(&buf,
+			   gettext("Use %s to invoke the Options menu!"),
+			   key_for_func_ext(LYK_OPTIONS, FOR_PANEL));
+		HTAlert(buf);
+		FREE(data);
+		return (NULLFILE);
+	    }
+	    FREE(secure_value);
+	}
+
+	/* Save options */
+	if (!strcmp(data[i].tag, save_options_string) && (!no_option_save)) {
+	    save_all = TRUE;
+	}
+
+	/* Cookies: SELECT */
+	if (!strcmp(data[i].tag, cookies_string)) {
+	    if (!strcmp(data[i].value, cookies_ignore_all_string)) {
+		LYSetCookies = FALSE;
+	    } else if (!strcmp(data[i].value, cookies_up_to_user_string)) {
+		LYSetCookies = TRUE;
+		LYAcceptAllCookies = FALSE;
+	    } else if (!strcmp(data[i].value, cookies_accept_all_string)) {
+		LYSetCookies = TRUE;
+		LYAcceptAllCookies = TRUE;
+	    }
+	}
+
+	/* X Display: INPUT */
+	if (!strcmp(data[i].tag, x_display_string)) {
+	    LYsetXDisplay(data[i].value);
+	    validate_x_display();
+	    summarize_x_display(data[i].value);
+	}
+
+	/* Editor: INPUT */
+	if (!strcmp(data[i].tag, editor_string)) {
+	    FREE(editor);
+	    StrAllocCopy(editor, data[i].value);
+	}
+
+	/* Emacs keys: ON/OFF */
+	if (!strcmp(data[i].tag, emacs_keys_string)
+	    && GetOptValues(bool_values, data[i].value, &code)) {
+	    if ((emacs_keys = (BOOLEAN) code) != FALSE) {
+		set_emacs_keys();
+	    } else {
+		reset_emacs_keys();
+	    }
+	}
+
+	/* Execution links: SELECT */
+#if defined(ENABLE_OPTS_CHANGE_EXEC) && (defined(EXEC_LINKS) || defined(EXEC_SCRIPTS))
+	if (!strcmp(data[i].tag, exec_links_string)
+	    && GetOptValues(exec_links_values, data[i].value, &code)) {
+#ifndef NEVER_ALLOW_REMOTE_EXEC
+	    local_exec = (BOOLEAN) (code == EXEC_ALWAYS);
+#endif /* !NEVER_ALLOW_REMOTE_EXEC */
+	    local_exec_on_local_files = (BOOLEAN) (code == EXEC_LOCAL);
+	}
+#endif /* ENABLE_OPTS_CHANGE_EXEC */
+
+	/* Keypad Mode: SELECT */
+	if (!strcmp(data[i].tag, keypad_mode_string)) {
+	    int newval = 0;
+
+	    if (GetOptValues(keypad_mode_values, data[i].value, &newval)
+		&& keypad_mode != newval) {
+		keypad_mode = newval;
+		need_reload = TRUE;
+		if (keypad_mode == NUMBERS_AS_ARROWS) {
+		    set_numbers_as_arrows();
+		} else {
+		    reset_numbers_as_arrows();
+		}
+	    }
+	}
+
+	/* Line edit style: SELECT */
+	if (!strcmp(data[i].tag, lineedit_mode_string)) {
+	    int newval = atoi(data[i].value);
+	    int j;
+
+	    /* prevent spoofing attempt */
+	    for (j = 0; LYLineeditNames[j]; j++) {
+		if (j == newval)
+		    current_lineedit = newval;
+	    }
+	}
+#ifdef EXP_KEYBOARD_LAYOUT
+	/* Keyboard layout: SELECT */
+	if (!strcmp(data[i].tag, kblayout_string)) {
+	    int newval = atoi(data[i].value);
+	    int j;
+
+	    /* prevent spoofing attempt */
+	    for (j = 0; LYKbLayoutNames[j]; j++) {
+		if (j == newval)
+		    current_layout = newval;
+	    }
+	}
+#endif /* EXP_KEYBOARD_LAYOUT */
+
+	/* Mail Address: INPUT */
+	if (!strcmp(data[i].tag, mail_address_string)) {
+	    FREE(personal_mail_address);
+	    StrAllocCopy(personal_mail_address, data[i].value);
+	}
+
+	/* Anonymous FTP Password: INPUT */
+#ifndef DISABLE_FTP
+	if (!strcmp(data[i].tag, anonftp_password_string)) {
+	    FREE(anonftp_password);
+	    StrAllocCopy(anonftp_password, data[i].value);
+	}
+#endif
+
+	/* Search Type: SELECT */
+	if (!strcmp(data[i].tag, search_type_string)
+	    && GetOptValues(search_type_values, data[i].value, &code)) {
+	    case_sensitive = (BOOLEAN) code;
+	}
+
+	/* HTML error tolerance: SELECT */
+	if (!strcmp(data[i].tag, DTD_recovery_string)
+	    && GetOptValues(DTD_type_values, data[i].value, &code)) {
+	    if (Old_DTD != code) {
+		Old_DTD = code;
+		HTSwitchDTD(!Old_DTD);
+		need_reload = TRUE;
+	    }
+	}
+
+	/* Bad HTML warnings: SELECT */
+	if (!strcmp(data[i].tag, bad_html_string)
+	    && GetOptValues(bad_html_values, data[i].value, &code)) {
+	    cfg_bad_html = code;
+	}
+
+	/* Select Popups: ON/OFF */
+	if (!strcmp(data[i].tag, select_popups_string)
+	    && GetOptValues(bool_values, data[i].value, &code)) {
+	    LYSelectPopups = (BOOLEAN) code;
+	}
+#if defined(USE_SLANG) || defined(COLOR_CURSES)
+	/* Show Color: SELECT */
+	if (!strcmp(data[i].tag, show_color_string)
+	    && GetOptValues(show_color_values, data[i].value,
+			    &LYChosenShowColor)) {
+	    if (can_do_colors)
+		LYShowColor = LYChosenShowColor;
+	    if (CurrentShowColor != LYShowColor) {
+		lynx_force_repaint();
+	    }
+	    CurrentShowColor = LYShowColor;
+#ifdef USE_SLANG
+	    SLtt_Use_Ansi_Colors = (LYShowColor > SHOW_COLOR_OFF ? TRUE : FALSE);
+#endif
+	}
+#endif /* USE_SLANG || COLOR_CURSES */
+
+	/* Show Cursor: ON/OFF */
+	if (!strcmp(data[i].tag, show_cursor_string)
+	    && GetOptValues(bool_values, data[i].value, &code)) {
+	    LYShowCursor = (BOOLEAN) code;
+	}
+
+	/* Underline links: ON/OFF */
+	if (!strcmp(data[i].tag, underline_links_string)
+	    && GetOptValues(bool_values, data[i].value, &code)) {
+	    LYUnderlineLinks = (BOOLEAN) code;
+	}
+#ifdef USE_SCROLLBAR
+	/* Show Scrollbar: ON/OFF */
+	if (!strcmp(data[i].tag, show_scrollbar_string)
+	    && GetOptValues(bool_values, data[i].value, &code)) {
+	    LYShowScrollbar = (BOOLEAN) code;
+	    need_reload = TRUE;
+	}
+#endif
+
+	/* Cookie Prompting: SELECT */
+	if (!strcmp(data[i].tag, cookie_prompt_string))
+	    GetOptValues(prompt_values, data[i].value, &cookie_noprompt);
+
+#ifdef USE_SSL
+	/* SSL Prompting: SELECT */
+	if (!strcmp(data[i].tag, ssl_prompt_string))
+	    GetOptValues(prompt_values, data[i].value, &ssl_noprompt);
+#endif
+
+	/* User Mode: SELECT */
+	if (!strcmp(data[i].tag, user_mode_string)
+	    && GetOptValues(user_mode_values, data[i].value, &user_mode)) {
+	    LYSetDisplayLines();
+	}
+
+	/* Type of visited pages page: SELECT */
+	if (!strcmp(data[i].tag, visited_links_string))
+	    GetOptValues(visited_links_values, data[i].value, &Visited_Links_As);
+
+	/* Show Images: SELECT */
+	if (!strcmp(data[i].tag, images_string)) {
+	    if (!strcmp(data[i].value, images_ignore_all_string)
+		&& !(pseudo_inline_alts == FALSE && clickable_images == FALSE)) {
+		pseudo_inline_alts = FALSE;
+		clickable_images = FALSE;
+		need_reload = TRUE;
+	    } else if (!strcmp(data[i].value, images_use_label_string)
+		       && !(pseudo_inline_alts == TRUE && clickable_images == FALSE)) {
+		pseudo_inline_alts = TRUE;
+		clickable_images = FALSE;
+		need_reload = TRUE;
+	    } else if (!strcmp(data[i].value, images_use_links_string)
+		       && !(clickable_images == TRUE)) {
+		clickable_images = TRUE;
+		need_reload = TRUE;
+	    }
+	}
+
+	/* Verbose Images: ON/OFF */
+	if (!strcmp(data[i].tag, verbose_images_string)
+	    && GetOptValues(verbose_images_type_values, data[i].value, &code)) {
+	    if (verbose_img != code) {
+		verbose_img = (BOOLEAN) code;
+		need_reload = TRUE;
+	    }
+	}
+
+	/* VI Keys: ON/OFF */
+	if (!strcmp(data[i].tag, vi_keys_string)
+	    && GetOptValues(bool_values, data[i].value, &code)) {
+	    if ((vi_keys = (BOOLEAN) code) != FALSE) {
+		set_vi_keys();
+	    } else {
+		reset_vi_keys();
+	    }
+	}
+
+	/* Bookmarks File Menu: SELECT */
+	if (!strcmp(data[i].tag, mbm_string) && (!LYMBMBlocked)) {
+	    GetOptValues(mbm_values, data[i].value, &LYMultiBookmarks);
+	}
+
+	/* Default Bookmarks filename: INPUT */
+	if (!strcmp(data[i].tag, single_bookmark_string) && (!no_bookmark)) {
+	    if (strcmp(data[i].value, "")) {
+		FREE(bookmark_page);
+		StrAllocCopy(bookmark_page, data[i].value);
+	    }
+	}
+#ifdef USE_SESSIONS
+	/* Auto Session: ON/OFF */
+	if (!strcmp(data[i].tag, auto_session_string)
+	    && GetOptValues(bool_values, data[i].value, &code)) {
+	    LYAutoSession = (BOOLEAN) code;
+	}
+
+	/* Default Session filename: INPUT */
+	if (!strcmp(data[i].tag, single_session_string)) {
+	    if (strcmp(data[i].value, "")) {
+		FREE(LYSessionFile);
+		StrAllocCopy(LYSessionFile, data[i].value);
+	    }
+	}
+#endif
+
+	/* Assume Character Set: SELECT */
+	if (!strcmp(data[i].tag, assume_char_set_string)) {
+	    int newval = UCGetLYhndl_byMIME(data[i].value);
+
+	    if (newval >= 0
+		&& ((raw_mode_old &&
+		     newval != safeUCGetLYhndl_byMIME(UCAssume_MIMEcharset))
+		    || (!raw_mode_old &&
+			newval != UCLYhndl_for_unspec)
+		)) {
+
+		UCLYhndl_for_unspec = newval;
+		StrAllocCopy(UCAssume_MIMEcharset, data[i].value);
+		assume_char_set_changed = TRUE;
+	    }
+	}
+#ifdef USE_LOCALE_CHARSET
+	/* Use locale-based character set: ON/OFF */
+	if (!strcmp(data[i].tag, locale_charset_string)
+	    && GetOptValues(bool_values, data[i].value, &code)) {
+	    LYLocaleCharset = (BOOLEAN) code;
+	}
+#endif
+
+	/* Display Character Set: SELECT */
+	if (!strcmp(data[i].tag, display_char_set_string)) {
+	    int newval = atoi(data[i].value);
+	    int j;
+
+	    /* prevent spoofing attempt */
+	    for (j = 0; LYchar_set_names[j]; j++) {
+		if (j == newval)
+		    current_char_set = newval;
+	    }
+	}
+
+	/* Raw Mode: ON/OFF */
+	if (!strcmp(data[i].tag, raw_mode_string)
+	    && GetOptValues(bool_values, data[i].value, &code)) {
+	    LYRawMode = (BOOLEAN) code;
+	}
+#ifndef DISABLE_FTP
+	/*
+	 * passive ftp: ON/OFF
+	 */
+	if (!strcmp(data[i].tag, passive_ftp_string)) {
+	    ftp_passive = (BOOLEAN) code;
+	}
+
+	/*
+	 * ftp sort: SELECT
+	 */
+	if (!strcmp(data[i].tag, ftp_sort_string)) {
+	    GetOptValues(ftp_sort_values, data[i].value, &HTfileSortMethod);
+	}
+#endif /* DISABLE_FTP */
+
+#ifdef DIRED_SUPPORT
+	/* Local Directory Style: SELECT */
+	if (!strcmp(data[i].tag, dired_list_string)) {
+	    GetOptValues(dired_list_values, data[i].value, &dir_list_style);
+	}
+#ifdef LONG_LIST
+	/* Local Directory Order: SELECT */
+	if (!strcmp(data[i].tag, dired_sort_string)) {
+	    GetOptValues(dired_sort_values, data[i].value, &dir_list_order);
+	}
+#endif /* LONG_LIST */
+#endif /* DIRED_SUPPORT */
+
+	/* Show dot files: ON/OFF */
+	if (!strcmp(data[i].tag, show_dotfiles_string) && (!no_dotfiles)
+	    && GetOptValues(bool_values, data[i].value, &code)) {
+	    show_dotfiles = (BOOLEAN) code;
+	}
+
+	/* Pause when showing messages: ON/OFF */
+	if (!strcmp(data[i].tag, no_pause_string)
+	    && GetOptValues(bool_values, data[i].value, &code)) {
+	    no_pause = (BOOLEAN) !code;
+	}
+#ifdef USE_READPROGRESS
+	/* Show Transfer Rate: enumerated value */
+	if (!strcmp(data[i].tag, show_rate_string)
+	    && GetOptValues(rate_values, data[i].value, &code)) {
+	    LYTransferRate = code;
+	}
+#endif /* USE_READPROGRESS */
+
+	/* Preferred Media Type: SELECT */
+	if (!strcmp(data[i].tag, preferred_media_string)) {
+	    GetOptValues(media_values, data[i].value, &LYAcceptMedia);
+	}
+
+	/* Preferred Encoding: SELECT */
+	if (!strcmp(data[i].tag, preferred_encoding_string)) {
+	    GetOptValues(encoding_values, data[i].value, &LYAcceptEncoding);
+	}
+
+	/* Preferred Document Character Set: INPUT */
+	if (!strcmp(data[i].tag, preferred_doc_char_string)) {
+	    if (strcmp(pref_charset, data[i].value)) {
+		FREE(pref_charset);
+		StrAllocCopy(pref_charset, data[i].value);
+		need_end_reload = TRUE;
+	    }
+	}
+
+	/* Preferred Document Language: INPUT */
+	if (!strcmp(data[i].tag, preferred_doc_lang_string)) {
+	    if (strcmp(language, data[i].value)) {
+		FREE(language);
+		StrAllocCopy(language, data[i].value);
+		need_end_reload = TRUE;
+	    }
+	}
+
+	/* Send User Agent: INPUT */
+	if (!strcmp(data[i].tag, send_user_agent_string)) {
+	    LYSendUserAgent = !strcasecomp(data[i].value, "ON");
+	}
+
+	/* User Agent: INPUT */
+	if (!strcmp(data[i].tag, user_agent_string) && (!no_useragent)) {
+	    if (strcmp(LYUserAgent, data[i].value)) {
+		need_end_reload = TRUE;
+		FREE(LYUserAgent);
+		/* ignore Copyright warning ? */
+		StrAllocCopy(LYUserAgent,
+			     *(data[i].value)
+			     ? data[i].value
+			     : LYUserAgentDefault);
+		if (!LYCheckUserAgent()) {
+		    HTAlert(UA_PLEASE_USE_LYNX);
+		}
+	    }
+	}
+    }				/* end of loop */
+
+    /*
+     * Process the flags:
+     */
+#ifdef USE_LOCALE_CHARSET
+    LYFindLocaleCharset();
+#endif
+
+    if (old_media_value != LYAcceptMedia)
+	HTFilterPresentations();
+
+    if (display_char_set_old != current_char_set ||
+	raw_mode_old != LYRawMode ||
+	assume_char_set_changed) {
+	/*
+	 * charset settings: the order is essential here.
+	 */
+	if (display_char_set_old != current_char_set) {
+	    /*
+	     * Set the LYUseDefaultRawMode value and character handling if
+	     * LYRawMode was changed.  - FM
+	     */
+	    LYUseDefaultRawMode = TRUE;
+	    HTMLUseCharacterSet(current_char_set);
+#ifdef CAN_SWITCH_DISPLAY_CHARSET
+	    /* Deduce whether the user wants autoswitch: */
+	    switch_display_charsets =
+		(current_char_set == auto_display_charset
+		 || current_char_set == auto_other_display_charset);
+#endif
+	}
+	if (assume_char_set_changed && HTCJK != JAPANESE) {
+	    LYRawMode = (BOOLEAN) (UCLYhndl_for_unspec == current_char_set);
+	}
+	if (raw_mode_old != LYRawMode || assume_char_set_changed) {
+	    /*
+	     * Set the raw 8-bit or CJK mode defaults and character set if
+	     * changed.  - FM
+	     */
+	    HTMLSetUseDefaultRawMode(current_char_set, LYRawMode);
+	    HTMLSetCharacterHandling(current_char_set);
+	}
+	need_reload = TRUE;
+    }
+    /* end of charset settings */
+    /*
+     * FIXME: Golly gee, we need to write all of this out now, don't we?
+     */
+    BStrFree(newdoc->post_data);
+    FREE(data);
+    if (save_all) {
+	HTInfoMsg(SAVING_OPTIONS);
+	LYrcShowColor = LYChosenShowColor;
+	if (save_rc(NULL)) {
+	    HTInfoMsg(OPTIONS_SAVED);
+	} else {
+	    HTAlert(OPTIONS_NOT_SAVED);
+	}
+    }
+
+    /*
+     * Exit:  working around the previous document.  Being out of
+     * mainloop()/getfile() cycle, do things manually.
+     */
+    CTRACE((tfp, "\nLYOptions.c/postoptions(): exiting...\n"));
+    CTRACE((tfp, "                            need_reload = %s\n",
+	    need_reload ? "TRUE" : "FALSE"));
+    CTRACE((tfp, "                            need_end_reload = %s\n",
+	    need_end_reload ? "TRUE" : "FALSE"));
+
+    /*  Options menu was pushed before postoptions(), so pop-up. */
+    LYpop(newdoc);
+    WWWDoc.address = newdoc->address;
+    WWWDoc.post_data = newdoc->post_data;
+    WWWDoc.post_content_type = newdoc->post_content_type;
+    WWWDoc.bookmark = newdoc->bookmark;
+    WWWDoc.isHEAD = newdoc->isHEAD;
+    WWWDoc.safe = newdoc->safe;
+    LYforce_no_cache = FALSE;	/* ! */
+    LYoverride_no_cache = TRUE;	/* ! */
+    /*
+     * Working out of getfile() cycle we reset *no_cache manually here so
+     * HTLoadAbsolute() will return "Document already in memory":  it was
+     * forced reloading Options Menu again without this (overhead).
+     *
+     * Probably *no_cache was set in a wrong position because of
+     * the internal page...
+     */
+    if (!HTLoadAbsolute(&WWWDoc))
+	return (NOT_FOUND);
+
+    HTuncache_current_document();	/* will never use again */
+
+    /*
+     * Return to previous doc, not to options menu!  Reload the document we had
+     * before the options menu but uncache only when necessary (Hurrah, user!):
+     */
+    LYpop(newdoc);
+    WWWDoc.address = newdoc->address;
+    WWWDoc.post_data = newdoc->post_data;
+    WWWDoc.post_content_type = newdoc->post_content_type;
+    WWWDoc.bookmark = newdoc->bookmark;
+    WWWDoc.isHEAD = newdoc->isHEAD;
+    WWWDoc.safe = newdoc->safe;
+    LYforce_no_cache = FALSE;	/* see below */
+    LYoverride_no_cache = TRUE;	/* see below */
+    /*
+     * Re-setting of *no_cache is probably not required here but this is a
+     * guarantee against _double_ reloading over the net in case prev document
+     * has its own "no cache" attribute and options menu set "need_reload"
+     * also.  Force this HTLoadAbsolute() to return "Document already in
+     * memory".
+     */
+    if (!HTLoadAbsolute(&WWWDoc))
+	return (NOT_FOUND);
+
+    /*
+     * Now most interesting part: reload document when necessary.
+     * **********************************************************
+     */
+
+    reloading = FALSE;		/* set manually */
+    /* force end-to-end reload from remote server if change LYUserAgent or
+     * language or pref_charset (marked by need_end_reload flag above), from
+     * old-style LYK_OPTIONS (mainloop):
+     */
+    if ((need_end_reload == TRUE &&
+	 (strncmp(newdoc->address, "http", 4) == 0 ||
+	  isLYNXCGI(newdoc->address) == 0))) {
+	/*
+	 * An option has changed which may influence content negotiation, and
+	 * the resource is from a http or https or lynxcgi URL (the only
+	 * protocols which currently do anything with this information).  Set
+	 * reloading = TRUE so that proxy caches will be flushed, which is
+	 * necessary until the time when all proxies understand HTTP 1.1 Vary: 
+	 * and all Servers properly use it...  Treat like case LYK_RELOAD (see
+	 * comments there).  - KW
+	 */
+	reloading = TRUE;	/* global flag */
+	need_reload = TRUE;	/* this was probably already TRUE, don't worry */
+    }
+
+    if (need_reload == FALSE) {
+	/*  no uncache, already loaded */
+	CTRACE((tfp, "LYOptions.c/postoptions(): now really exit.\n\n"));
+	return (NORMAL);
+    } else {
+	/*  update HText cache */
+
+	/*
+	 * see LYK_RELOAD & LYK_OPTIONS in mainloop for details...
+	 */
+	if (HTisDocumentSource()) {
+	    srcmode_for_next_retrieval(1);
+	}
+#ifdef USE_SOURCE_CACHE
+	if (reloading == FALSE) {
+	    /* one more attempt to be smart enough: */
+	    if (HTcan_reparse_document()) {
+		if (!HTreparse_document())
+		    srcmode_for_next_retrieval(0);
+		CTRACE((tfp, "LYOptions.c/postoptions(): now really exit.\n\n"));
+		return (NORMAL);
+	    }
+	}
+#endif
+	if (newdoc->post_data != NULL && !newdoc->safe &&
+	    confirm_post_resub(newdoc->address, newdoc->title, 2, 1) == FALSE) {
+	    HTInfoMsg(WILL_NOT_RELOAD_DOC);
+	    if (HTisDocumentSource()) {
+		srcmode_for_next_retrieval(0);
+	    }
+	    return (NORMAL);
+	}
+
+	HEAD_request = HTLoadedDocumentIsHEAD();
+	/*  uncache and load again */
+	HTuncache_current_document();
+	LYpush(newdoc, FALSE);
+	CTRACE((tfp, "LYOptions.c/postoptions(): now really exit.\n\n"));
+	return (NULLFILE);
+    }
+
+    /******** Done! **************************************************/
+}
+
+static char *NewSecureValue(void)
+{
+    static char oops[] = "?";
+
+    FREE(secure_value);
+    if ((secure_value = typeMallocn(char, 80)) != 0) {
+#if defined(RAND_MAX)
+	long key = lynx_rand();
+
+#else
+	long key = (long) secure_value + (long) time(0);
+#endif
+	sprintf(secure_value, "%ld", key);
+	return secure_value;
+    }
+    return oops;
+}
+
+#define LABEL_LEN 33
+
+/*
+ * Note: the 'value' we are passing here is a local copy of the "same" string
+ * as is used in LYrcFile.c to index the savable options.
+ */
+static void PutLabel(FILE *fp, const char *name,
+		     const char *value)
+{
+    int have = (int) strlen(name);
+    int want = LABEL_LEN;
+    int need = LYstrExtent(name, have, want);
+    char *buffer = NULL;
+
+    fprintf(fp, "%s%s", MARGIN_STR, LYEntifyTitle(&buffer, name));
+
+    if (will_save_rc(value) && !no_option_save) {
+	while (need++ < want)
+	    fprintf(fp, "&nbsp;");
+    } else {
+	want -= 3;
+	if (need < want) {
+	    fprintf(fp, "&nbsp;");
+	    ++need;
+	}
+	fprintf(fp, "(!)");
+	while (need++ < want) {
+	    fprintf(fp, "&nbsp;");
+	}
+    }
+    fprintf(fp, ": ");
+    FREE(buffer);
+}
+
+/*
+ * For given a list of the .lynxrc names for boolean flags that make up a
+ * composite setting, check if any are not writable for the .lynxrc file.  If
+ * so, return that name, so the subsequence will_save_rc() check in PutLabel()
+ * will flag the composite as not-saved.
+ */
+static const char *check_if_write_lynxrc(const char **table)
+{
+    int n;
+    const char *result = NULL;
+
+    for (n = 0; table[n] != 0; ++n) {
+	result = table[n];
+	if (!will_save_rc(result))
+	    break;
+    }
+    return result;
+}
+
+/*
+ * The options menu treats "Cookies" as a single enumeration, but it is read
+ * from lynx.cfg (and perhaps .lynxrc) as a set of booleans.  Check if any are
+ * not writable to .lynxrc, so we can show the user. 
+ */
+static const char *will_save_cookies(void)
+{
+    static const char *table[] =
+    {
+	RC_SET_COOKIES,		/* LYSetCookies */
+	RC_ACCEPT_ALL_COOKIES,	/* LYAcceptAllCookies */
+	NULL
+    };
+
+    return check_if_write_lynxrc(table);
+}
+
+/*
+ * The options menu treats "Show images" as a single enumeration, but it is
+ * read from lynx.cfg (and perhaps .lynxrc) as a set of booleans.  Check if any
+ * are not writable to .lynxrc, so we can show the user. 
+ */
+static const char *will_save_images(void)
+{
+    static const char *table[] =
+    {
+	RC_MAKE_PSEUDO_ALTS_FOR_INLINES,	/* pseudo_inline_alts */
+	RC_MAKE_LINKS_FOR_ALL_IMAGES,	/* clickable_images */
+	NULL
+    };
+
+    return check_if_write_lynxrc(table);
+}
+
+/*
+ * The visited-links menu is used from the visited-links page as well as the
+ * options page.
+ */
+void LYMenuVisitedLinks(FILE *fp0, int disable_all)
+{
+    BeginSelect(fp0, visited_links_string);
+    PutOptValues(fp0, Visited_Links_As, visited_links_values);
+    EndSelect(fp0);
+}
+
+/*
+ * Okay, someone wants to change options.  So, let's gen up a form for them
+ * and pass it around.  Gor, this is ugly.  Be a lot easier in Bourne with
+ * "here" documents.  :->
+ * Basic Strategy:  For each option, throw up the appropriate type of
+ * control, giving defaults as appropriate.  If nothing else, we're
+ * probably going to test every control there is.  MRC
+ *
+ * This function is synchronized with postoptions().  Read the comments in
+ * postoptions() header if you change something in gen_options().
+ */
+static int gen_options(char **newfile)
+{
+    static char tempfile[LY_MAXPATH] = "\0";
+
+    int i;
+    char *buffer = NULL;
+    BOOLEAN disable_all = FALSE;
+    FILE *fp0;
+    size_t cset_len = 0;
+    size_t text_len = (size_t) ((LYcolLimit > 45)
+				? LYcolLimit - (LABEL_LEN + 2 + MARGIN_LEN)
+				: 7);	/* cf: PutLabel */
+
+    if ((fp0 = InternalPageFP(tempfile, TRUE)) == 0)
+	return (NOT_FOUND);
+
+    LYLocalFileToURL(newfile, tempfile);
+
+    /* This should not be needed if we regenerate the temp file every time with
+     * a new name, which just happened above in the case
+     * LYReuseTempfiles==FALSE.  Even for LYReuseTempfiles=TRUE, code at the
+     * end of postoptions() may remove an older cached version from memory if
+     * that version of the page was left by submitting changes.  - kw
+     * 1999-11-27
+     * If access to the actual file via getfile() later fails (maybe because of
+     * some restrictions), mainloop may leave this flag on after popping the
+     * previous doc which is then unnecessarily reloaded.  But I changed
+     * mainloop to reset the flag.  - kw 1999-05-24
+     */
+    LYforce_no_cache = TRUE;
+
+    /*
+     * Without LYUseFormsOptions set we should maybe not even get here.
+     * However, it's possible we do; disable the form in that case. - kw
+     */
+#ifndef NO_OPTION_MENU
+    if (!LYUseFormsOptions)
+	disable_all = TRUE;
+#endif
+
+    BeginInternalPage(fp0, OPTIONS_TITLE, NULL);	/* help link below */
+
+    /*
+     * I do C, not HTML.  Feel free to pretty this up.
+     */
+    fprintf(fp0, "<form action=\"%s\" method=\"post\">\n", STR_LYNXOPTIONS);
+    /*
+     * use following with some sort of one shot secret key akin to NCSA
+     * (or was it CUTE?) telnet one shot password to allow ftp to self.
+     * to prevent spoofing.
+     */
+    fprintf(fp0, "<input name=\"%s\" type=\"hidden\" value=\"%s\">\n",
+	    secure_string, NewSecureValue());
+
+    /*
+     * visible text begins here
+     */
+
+    /* Submit/Reset/Help */
+    fprintf(fp0, "<p align=center>\n");
+    if (!disable_all) {
+	fprintf(fp0,
+		"<input type=\"submit\" value=\"%s\"> - \n",
+		LYEntifyValue(&buffer, ACCEPT_CHANGES));
+	fprintf(fp0,
+		"<input type=\"reset\" value=\"%s\"> - \n",
+		LYEntifyValue(&buffer, RESET_CHANGES));
+	fprintf(fp0,
+		"%s - \n",
+		LYEntifyTitle(&buffer, CANCEL_CHANGES));
+    }
+    fprintf(fp0, "<a href=\"%s%s\">%s</a>\n",
+	    helpfilepath, LYEntifyTitle(&buffer, OPTIONS_HELP), TO_HELP);
+
+    /* Save options */
+    if (!no_option_save) {
+	if (!disable_all) {
+	    fprintf(fp0, "<p align=center>%s: ", LYEntifyTitle(&buffer, SAVE_OPTIONS));
+	    fprintf(fp0, "<input type=\"checkbox\" name=\"%s\">\n",
+		    save_options_string);
+	}
+	fprintf(fp0, "<br>%s\n",
+		LYEntifyTitle(&buffer,
+			      gettext("(options marked with (!) will not be saved)")));
+    }
+
+    /*
+     * preformatted text follows
+     */
+    fprintf(fp0, "<pre>\n");
+
+    PutHeader(fp0, gettext("General Preferences"));
+    /*****************************************************************/
+
+    /* User Mode: SELECT */
+    PutLabel(fp0, gettext("User mode"), user_mode_string);
+    BeginSelect(fp0, user_mode_string);
+    PutOptValues(fp0, user_mode, user_mode_values);
+    EndSelect(fp0);
+
+    /* Editor: INPUT */
+    PutLabel(fp0, gettext("Editor"), editor_string);
+    PutTextInput(fp0, editor_string, NonNull(editor), text_len,
+		 DISABLED(no_editor || system_editor));
+
+    /* Search Type: SELECT */
+    PutLabel(fp0, gettext("Type of Search"), search_type_string);
+    BeginSelect(fp0, search_type_string);
+    PutOptValues(fp0, case_sensitive, search_type_values);
+    EndSelect(fp0);
+
+    PutHeader(fp0, gettext("Security and Privacy"));
+    /*****************************************************************/
+
+    /* Cookies: SELECT */
+    PutLabel(fp0, gettext("Cookies"), will_save_cookies());
+    BeginSelect(fp0, cookies_string);
+    PutOption(fp0, !LYSetCookies,
+	      cookies_ignore_all_string,
+	      cookies_ignore_all_string);
+    PutOption(fp0, LYSetCookies && !LYAcceptAllCookies,
+	      cookies_up_to_user_string,
+	      cookies_up_to_user_string);
+    PutOption(fp0, LYSetCookies && LYAcceptAllCookies,
+	      cookies_accept_all_string,
+	      cookies_accept_all_string);
+    EndSelect(fp0);
+
+    /* Cookie Prompting: SELECT */
+    PutLabel(fp0, gettext("Invalid-Cookie Prompting"), cookie_prompt_string);
+    BeginSelect(fp0, cookie_prompt_string);
+    PutOptValues(fp0, cookie_noprompt, prompt_values);
+    EndSelect(fp0);
+
+#ifdef USE_SSL
+    /* SSL Prompting: SELECT */
+    PutLabel(fp0, gettext("SSL Prompting"), ssl_prompt_string);
+    BeginSelect(fp0, ssl_prompt_string);
+    PutOptValues(fp0, ssl_noprompt, prompt_values);
+    EndSelect(fp0);
+#endif
+
+    PutHeader(fp0, gettext("Keyboard Input"));
+    /*****************************************************************/
+
+    /* Keypad Mode: SELECT */
+    PutLabel(fp0, gettext("Keypad mode"), keypad_mode_string);
+    BeginSelect(fp0, keypad_mode_string);
+    PutOptValues(fp0, keypad_mode, keypad_mode_values);
+    EndSelect(fp0);
+
+    /* Emacs keys: ON/OFF */
+    PutLabel(fp0, gettext("Emacs keys"), emacs_keys_string);
+    BeginSelect(fp0, emacs_keys_string);
+    PutOptValues(fp0, emacs_keys, bool_values);
+    EndSelect(fp0);
+
+    /* VI Keys: ON/OFF */
+    PutLabel(fp0, gettext("VI keys"), vi_keys_string);
+    BeginSelect(fp0, vi_keys_string);
+    PutOptValues(fp0, vi_keys, bool_values);
+    EndSelect(fp0);
+
+    /* Line edit style: SELECT */
+    if (LYLineeditNames[1]) {	/* well, at least 2 line edit styles available */
+	PutLabel(fp0, gettext("Line edit style"), lineedit_mode_string);
+	BeginSelect(fp0, lineedit_mode_string);
+	for (i = 0; LYLineeditNames[i]; i++) {
+	    char temp[16];
+
+	    sprintf(temp, "%d", i);
+	    PutOption(fp0, i == current_lineedit, temp, LYLineeditNames[i]);
+	}
+	EndSelect(fp0);
+    }
+#ifdef EXP_KEYBOARD_LAYOUT
+    /* Keyboard layout: SELECT */
+    PutLabel(fp0, gettext("Keyboard layout"), kblayout_string);
+    BeginSelect(fp0, kblayout_string);
+    for (i = 0; LYKbLayoutNames[i]; i++) {
+	char temp[16];
+
+	sprintf(temp, "%d", i);
+	PutOption(fp0, i == current_layout, temp, LYKbLayoutNames[i]);
+    }
+    EndSelect(fp0);
+#endif /* EXP_KEYBOARD_LAYOUT */
+
+    /*
+     * Display and Character Set
+     */
+    PutHeader(fp0, gettext("Display and Character Set"));
+    /*****************************************************************/
+
+#ifdef USE_LOCALE_CHARSET
+    /* Use locale-based character set: ON/OFF */
+    PutLabel(fp0, gettext("Use locale-based character set"), locale_charset_string);
+    BeginSelect(fp0, locale_charset_string);
+    PutOptValues(fp0, LYLocaleCharset, bool_values);
+    EndSelect(fp0);
+#else
+#define LYLocaleCharset FALSE
+#endif
+
+    /* Display Character Set: SELECT */
+    PutLabel(fp0, gettext("Display character set"), display_char_set_string);
+    MaybeSelect(fp0, LYLocaleCharset, display_char_set_string);
+    for (i = 0; LYchar_set_names[i]; i++) {
+	char temp[10];
+	size_t len = strlen(LYchar_set_names[i]);
+
+	if (len > cset_len)
+	    cset_len = len;
+	sprintf(temp, "%d", i);
+#ifdef USE_CHARSET_CHOICE
+	if (!charset_subsets[i].hide_display)
+#endif
+	    PutOption(fp0, i == current_char_set, temp, LYchar_set_names[i]);
+    }
+    EndSelect(fp0);
+
+    /* Assume Character Set: SELECT */
+    {
+	int curval;
+
+	curval = UCLYhndl_for_unspec;
+
+	/*
+	 * FIXME: If bogus value in lynx.cfg, then in old way, that is the
+	 * string that was displayed.  Now, user will never see that.  Good
+	 * or bad?  I don't know.  MRC
+	 */
+	if (curval == current_char_set) {
+	    /* ok, LYRawMode, so use UCAssume_MIMEcharset */
+	    curval = safeUCGetLYhndl_byMIME(UCAssume_MIMEcharset);
+	}
+	PutLabel(fp0, gettext("Assumed document character set"), assume_char_set_string);
+	BeginSelect(fp0, assume_char_set_string);
+	for (i = 0; i < LYNumCharsets; i++) {
+#ifdef USE_CHARSET_CHOICE
+	    if (!charset_subsets[i].hide_assumed)
+#endif
+		PutOption(fp0, i == curval,
+			  LYCharSet_UC[i].MIMEname,
+			  LYCharSet_UC[i].MIMEname);
+	}
+	EndSelect(fp0);
+    }
+
+    /* Raw Mode: ON/OFF */
+    if (LYHaveCJKCharacterSet) {
+	/*
+	 * Since CJK people hardly mixed with other world
+	 * we split the header to make it more readable:
+	 * "CJK mode" for CJK display charsets, and "Raw 8-bit" for others.
+	 */
+	PutLabel(fp0, gettext("CJK mode"), raw_mode_string);
+    } else {
+	PutLabel(fp0, gettext("Raw 8-bit"), raw_mode_string);
+    }
+
+    BeginSelect(fp0, raw_mode_string);
+    PutOptValues(fp0, LYRawMode, bool_values);
+    EndSelect(fp0);
+
+    /* X Display: INPUT */
+    PutLabel(fp0, gettext("X Display"), x_display_string);
+    PutTextInput(fp0, x_display_string, NonNull(x_display), text_len, "");
+
+    /*
+     * Document Appearance
+     */
+    PutHeader(fp0, gettext("Document Appearance"));
+    /*****************************************************************/
+
+    /* Show Color: SELECT */
+#if defined(USE_SLANG) || defined(COLOR_CURSES)
+    SetupChosenShowColor();
+    PutLabel(fp0, gettext("Show color"), show_color_string);
+    if (no_option_save) {
+	MaybeSelect(fp0, !can_do_colors, show_color_string);
+	if (LYShowColor == SHOW_COLOR_NEVER) {
+	    LYShowColor = SHOW_COLOR_OFF;
+	} else if (LYShowColor == SHOW_COLOR_ALWAYS) {
+	    LYShowColor = SHOW_COLOR_ON;
+	}
+	PutOptValues(fp0, LYShowColor - SHOW_COLOR_OFF, bool_values);
+    } else {
+	BeginSelect(fp0, show_color_string);
+	if (can_do_colors) {
+	    show_color_values[2].HtmlName = on_string;
+	    show_color_values[3].LongName = always_string;
+	} else {
+	    show_color_values[2].HtmlName = NULL;	/* suppress "ON" - kw */
+	    show_color_values[3].LongName = "Always try";
+	}
+	PutOptValues(fp0, LYChosenShowColor, show_color_values);
+    }
+    EndSelect(fp0);
+#endif /* USE_SLANG || COLOR_CURSES */
+
+    /* Show cursor: ON/OFF */
+    PutLabel(fp0, gettext("Show cursor"), show_cursor_string);
+    BeginSelect(fp0, show_cursor_string);
+    PutOptValues(fp0, LYShowCursor, bool_values);
+    EndSelect(fp0);
+
+    /* Underline links: ON/OFF */
+    PutLabel(fp0, gettext("Underline links"), underline_links_string);
+    BeginSelect(fp0, underline_links_string);
+    PutOptValues(fp0, LYUnderlineLinks, bool_values);
+    EndSelect(fp0);
+
+#ifdef USE_SCROLLBAR
+    /* Show scrollbar: ON/OFF */
+    PutLabel(fp0, gettext("Show scrollbar"), show_scrollbar_string);
+    BeginSelect(fp0, show_scrollbar_string);
+    PutOptValues(fp0, LYShowScrollbar, bool_values);
+    EndSelect(fp0);
+#endif
+
+    /* Select Popups: ON/OFF */
+    PutLabel(fp0, gettext("Popups for select fields"), select_popups_string);
+    BeginSelect(fp0, select_popups_string);
+    PutOptValues(fp0, LYSelectPopups, bool_values);
+    EndSelect(fp0);
+
+    /* HTML error recovery: SELECT */
+    PutLabel(fp0, gettext("HTML error recovery"), DTD_recovery_string);
+    BeginSelect(fp0, DTD_recovery_string);
+    PutOptValues(fp0, Old_DTD, DTD_type_values);
+    EndSelect(fp0);
+
+    /* Bad HTML messages: SELECT */
+    PutLabel(fp0, gettext("Bad HTML messages"), bad_html_string);
+    BeginSelect(fp0, bad_html_string);
+    PutOptValues(fp0, cfg_bad_html, bad_html_values);
+    EndSelect(fp0);
+
+    /* Show Images: SELECT */
+    PutLabel(fp0, gettext("Show images"), will_save_images());
+    BeginSelect(fp0, images_string);
+    PutOption(fp0, !pseudo_inline_alts && !clickable_images,
+	      images_ignore_all_string,
+	      images_ignore_all_string);
+    PutOption(fp0, pseudo_inline_alts && !clickable_images,
+	      images_use_label_string,
+	      images_use_label_string);
+    PutOption(fp0, clickable_images,
+	      images_use_links_string,
+	      images_use_links_string);
+    EndSelect(fp0);
+
+    /* Verbose Images: ON/OFF */
+    PutLabel(fp0, gettext("Verbose images"), verbose_images_string);
+    BeginSelect(fp0, verbose_images_string);
+    PutOptValues(fp0, verbose_img, verbose_images_type_values);
+    EndSelect(fp0);
+
+    /*
+     * Headers Transferred to Remote Servers
+     */
+    PutHeader(fp0, gettext("Headers Transferred to Remote Servers"));
+    /*****************************************************************/
+
+    /* Mail Address: INPUT */
+    PutLabel(fp0, gettext("Personal mail address"), mail_address_string);
+    PutTextInput(fp0, mail_address_string,
+		 NonNull(personal_mail_address), text_len, "");
+
+    /* Anonymous FTP Address: INPUT */
+#ifndef DISABLE_FTP
+    PutLabel(fp0, gettext("Password for anonymous ftp"), mail_address_string);
+    PutTextInput(fp0, anonftp_password_string,
+		 NonNull(anonftp_password), text_len, "");
+#endif
+
+    /* Preferred media type: SELECT */
+    PutLabel(fp0, gettext("Preferred media type"), preferred_media_string);
+    BeginSelect(fp0, preferred_media_string);
+    PutOptValues(fp0, LYAcceptMedia, media_values);
+    EndSelect(fp0);
+
+    /* Preferred encoding: SELECT */
+    PutLabel(fp0, gettext("Preferred encoding"), preferred_encoding_string);
+    BeginSelect(fp0, preferred_encoding_string);
+    PutOptValues(fp0, LYAcceptEncoding, encoding_values);
+    EndSelect(fp0);
+
+    /* Preferred Document Character Set: INPUT */
+    PutLabel(fp0, gettext("Preferred document character set"), preferred_doc_char_string);
+    PutTextInput(fp0, preferred_doc_char_string,
+		 NonNull(pref_charset), cset_len + 2, "");
+
+    /* Preferred Document Language: INPUT */
+    PutLabel(fp0, gettext("Preferred document language"), preferred_doc_lang_string);
+    PutTextInput(fp0, preferred_doc_lang_string,
+		 NonNull(language), cset_len + 2, "");
+
+    /* User Agent: INPUT */
+    if (!no_useragent) {
+	PutLabel(fp0, gettext("Send User-Agent header"), send_user_agent_string);
+	PutCheckBox(fp0, send_user_agent_string, LYSendUserAgent, "");
+	PutLabel(fp0, gettext("User-Agent header"), user_agent_string);
+	PutTextInput(fp0, user_agent_string,
+		     NonNull(LYUserAgent), text_len, "");
+    }
+
+    /*
+     * Listing and Accessing Files
+     */
+    PutHeader(fp0, gettext("Listing and Accessing Files"));
+    /*****************************************************************/
+
+#ifndef DISABLE_FTP
+    /* FTP sort: SELECT */
+    PutLabel(fp0, gettext("Use Passive FTP"), passive_ftp_string);
+    BeginSelect(fp0, passive_ftp_string);
+    PutOptValues(fp0, ftp_passive, bool_values);
+    EndSelect(fp0);
+
+    /* FTP sort: SELECT */
+    PutLabel(fp0, gettext("FTP sort criteria"), ftp_sort_string);
+    BeginSelect(fp0, ftp_sort_string);
+    PutOptValues(fp0, HTfileSortMethod, ftp_sort_values);
+    EndSelect(fp0);
+#endif /* DISABLE_FTP */
+
+#ifdef DIRED_SUPPORT
+    /* Local Directory Sort: SELECT */
+    PutLabel(fp0, gettext("Local directory sort criteria"), dired_list_string);
+    BeginSelect(fp0, dired_list_string);
+    PutOptValues(fp0, dir_list_style, dired_list_values);
+    EndSelect(fp0);
+#ifdef LONG_LIST
+    /* Local Directory Order: SELECT */
+    PutLabel(fp0, gettext("Local directory sort order"), dired_sort_string);
+    BeginSelect(fp0, dired_sort_string);
+    PutOptValues(fp0, dir_list_order, dired_sort_values);
+    EndSelect(fp0);
+#endif /* LONG_LIST */
+#endif /* DIRED_SUPPORT */
+
+    /* Show dot files: ON/OFF */
+    if (!no_dotfiles) {
+	PutLabel(fp0, gettext("Show dot files"), show_dotfiles_string);
+	BeginSelect(fp0, show_dotfiles_string);
+	PutOptValues(fp0, show_dotfiles, bool_values);
+	EndSelect(fp0);
+    }
+
+    /* Execution links: SELECT */
+#if defined(ENABLE_OPTS_CHANGE_EXEC) && (defined(EXEC_LINKS) || defined(EXEC_SCRIPTS))
+    PutLabel(fp0, gettext("Execution links"), exec_links_string);
+    BeginSelect(fp0, exec_links_string);
+#ifndef NEVER_ALLOW_REMOTE_EXEC
+    PutOptValues(fp0, local_exec
+		 ? EXEC_ALWAYS
+		 : (local_exec_on_local_files
+		    ? EXEC_LOCAL
+		    : EXEC_NEVER),
+		 exec_links_values);
+#else
+    PutOptValues(fp0, local_exec_on_local_files
+		 ? EXEC_LOCAL
+		 : EXEC_NEVER,
+		 exec_links_values);
+#endif /* !NEVER_ALLOW_REMOTE_EXEC */
+    EndSelect(fp0);
+#endif /* ENABLE_OPTS_CHANGE_EXEC */
+
+    PutLabel(fp0, gettext("Pause when showing message"), no_pause_string);
+    BeginSelect(fp0, no_pause_string);
+    PutOptValues(fp0, !no_pause, bool_values);
+    EndSelect(fp0);
+
+#ifdef USE_READPROGRESS
+    /* Show transfer rate: SELECT */
+    PutLabel(fp0, gettext("Show transfer rate"), show_rate_string);
+    BeginSelect(fp0, show_rate_string);
+    for (i = 0; rate_values[i].LongName != 0; ++i) {
+	char *message = NULL;
+
+	HTSprintf0(&message,
+		   rate_values[i].LongName,
+		   HTProgressUnits(rate_values[i].value));
+	PutOption(fp0,
+		  LYTransferRate == rate_values[i].value,
+		  rate_values[i].HtmlName,
+		  message);
+	FREE(message);
+    }
+    EndSelect(fp0);
+#endif /* USE_READPROGRESS */
+
+    /*
+     * Special Files and Screens
+     */
+    PutHeader(fp0, gettext("Special Files and Screens"));
+    /*****************************************************************/
+
+    /* Multi-Bookmark Mode: SELECT */
+    if (!LYMBMBlocked) {
+	PutLabel(fp0, gettext("Multi-bookmarks"), mbm_string);
+	BeginSelect(fp0, mbm_string);
+	PutOptValues(fp0, LYMultiBookmarks, mbm_values);
+	EndSelect(fp0);
+    }
+
+    /* Bookmarks File Menu: LINK/INPUT */
+    if (LYMultiBookmarks) {
+	PutLabel(fp0, gettext("Review/edit Bookmarks files"), mbm_string);
+	fprintf(fp0, "<a href=\"%s\">%s</a>\n",
+		LYNXOPTIONS_PAGE(MBM_LINK),
+		LYEntifyTitle(&buffer, gettext("Goto multi-bookmark menu")));
+    } else {
+	PutLabel(fp0, gettext("Bookmarks file"), single_bookmark_string);
+	PutTextInput(fp0, single_bookmark_string,
+		     NonNull(bookmark_page), text_len, "");
+    }
+
+#ifdef USE_SESSIONS
+    /* Auto Session: ON/OFF */
+    PutLabel(fp0, gettext("Auto Session"), auto_session_string);
+    BeginSelect(fp0, auto_session_string);
+    PutOptValues(fp0, LYAutoSession, bool_values);
+    EndSelect(fp0);
+
+    /* Session File Menu: INPUT */
+    PutLabel(fp0, gettext("Session file"), single_session_string);
+    PutTextInput(fp0, single_session_string,
+		 NonNull(LYSessionFile), text_len, "");
+#endif
+
+    /* Visited Pages: SELECT */
+    PutLabel(fp0, gettext("Visited Pages"), visited_links_string);
+    LYMenuVisitedLinks(fp0, disable_all);
+
+    if (!no_lynxcfg_info) {
+	fprintf(fp0, "\n  %s<a href=\"%s\">lynx.cfg</a>.\n",
+		LYEntifyTitle(&buffer, gettext("View the file ")),
+		STR_LYNXCFG);
+    }
+
+    fprintf(fp0, "\n</pre>\n");
+
+    /* Submit/Reset */
+    if (!disable_all) {
+	fprintf(fp0, "<p align=center>\n");
+	fprintf(fp0,
+		"<input type=\"submit\" value=\"%s\"> - \n",
+		LYEntifyValue(&buffer, ACCEPT_CHANGES));
+	fprintf(fp0,
+		"<input type=\"reset\" value=\"%s\"> - \n",
+		LYEntifyValue(&buffer, RESET_CHANGES));
+	fprintf(fp0, "%s\n", LYEntifyTitle(&buffer, CANCEL_CHANGES));
+    }
+
+    /*
+     * close HTML
+     */
+    fprintf(fp0, "</form>\n");
+    EndInternalPage(fp0);
+
+    FREE(buffer);
+
+    LYCloseTempFP(fp0);
+    return (NORMAL);
+}
+#endif /* !NO_OPTION_FORMS */
diff --git a/src/LYOptions.h b/src/LYOptions.h
new file mode 100644
index 00000000..2e402613
--- /dev/null
+++ b/src/LYOptions.h
@@ -0,0 +1,37 @@
+#ifndef LYOPTIONS_H
+#define LYOPTIONS_H
+
+#include <LYStructs.h>
+#include <LYStrings.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    extern BOOLEAN term_options;	/* for LYgetstr() */
+
+    extern BOOLEAN LYCheckUserAgent(void);
+    extern void edit_bookmarks(void);
+    extern int popup_choice(int cur_choice,
+			    int line,
+			    int column,
+			    const char **choices,
+			    int length,
+			    int disabled,
+			    BOOLEAN mouse);
+
+#define LYChoosePopup(cur, line, column, choices, length, disabled, mouse) \
+	popup_choice(cur, line, column, (const char **)choices, length, disabled, mouse)
+
+#ifndef NO_OPTION_FORMS
+    extern void LYMenuVisitedLinks(FILE *fp0, int disable_all);
+    extern int postoptions(DocInfo *newdoc);
+#endif				/* !NO_OPTION_FORMS */
+
+#ifndef NO_OPTION_MENU
+    extern void LYoptions(void);
+#endif				/* !NO_OPTION_MENU */
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYOPTIONS_H */
diff --git a/src/LYPrettySrc.c b/src/LYPrettySrc.c
new file mode 100644
index 00000000..a89288f0
--- /dev/null
+++ b/src/LYPrettySrc.c
@@ -0,0 +1,431 @@
+/*
+ * $LynxId: LYPrettySrc.c,v 1.21 2010/04/30 09:26:14 tom Exp $
+ *
+ * HTML source syntax highlighting
+ * by Vlad Harchev <hvv@hippo.ru>
+ * March 1999
+ */
+#include <HTUtils.h>
+#include <LYHash.h>
+#include <LYPrettySrc.h>
+#include <LYStrings.h>
+#include <LYLeaks.h>
+
+ /* This file creates too many "leak detected" entries in Lynx.leaks. */
+#define NO_MEMORY_TRACKING
+#include <LYLeaks.h>
+
+#ifdef USE_PRETTYSRC
+BOOL psrc_convert_string = FALSE;
+BOOL psrc_view = FALSE;		/* this is read by SGML_put_character - TRUE
+
+				   when viewing pretty source */
+BOOLEAN LYpsrc = FALSE;		/* this tells what will be shown on '\':
+
+				   if TRUE, then pretty source, normal source view otherwise. Toggled by
+				   -prettysrc commandline option.  */
+BOOL sgml_in_psrc_was_initialized;
+BOOL psrc_nested_call;
+BOOL psrc_first_tag;
+BOOL mark_htext_as_source = FALSE;
+
+  /* tagspecs from lynx.cfg are read here. After .lss file is read (is with lss
+     support), the style cache and markup are created before entering the
+     mainloop. */
+BOOLEAN psrcview_no_anchor_numbering = FALSE;
+static const char *HTL_tagspecs_defaults[HTL_num_lexemes] =
+{
+ /* these values are defaults. They are also listed in comments of distibution's
+    lynx.cfg. */
+#ifdef USE_COLOR_STYLE
+    "span.htmlsrc_comment:!span",
+    "span.htmlsrc_tag:!span",
+    "span.htmlsrc_attrib:!span",
+    "span.htmlsrc_attrval:!span",
+    "span.htmlsrc_abracket:!span",
+    "span.htmlsrc_entity:!span",
+    "span.htmlsrc_href:!span",
+    "span.htmlsrc_entire:!span",
+    "span.htmlsrc_badseq:!span",
+    "span.htmlsrc_badtag:!span",
+    "span.htmlsrc_badattr:!span",
+    "span.htmlsrc_sgmlspecial:!span"
+#else
+    "b:!b",			/* comment */
+    "b:!b",			/* tag     */
+    "b:!b",			/* attrib  */
+    ":",			/* attrval */
+    "b:!b",			/* abracket */
+    "b:!b",			/* entity  */
+    ":",			/* href    */
+    ":",			/* entire  */
+    "b:!b",			/* badseq  */
+    ":",			/* badtag  */
+    ":",			/* badattr */
+    "b:!b"			/* sgmlspec */
+#endif
+};
+
+char *HTL_tagspecs[HTL_num_lexemes];
+
+ /* these are pointers since tagspec can be empty (the pointer will be NULL
+    in that case) */
+HT_tagspec *lexeme_start[HTL_num_lexemes];
+HT_tagspec *lexeme_end[HTL_num_lexemes];
+
+int tagname_transform = 2;
+int attrname_transform = 2;
+
+static int html_src_tag_index(char *tagname)
+{
+    HTTag *tag = SGMLFindTag(&HTML_dtd, tagname);
+
+    return (tag && tag != &HTTag_unrecognized) ? tag - HTML_dtd.tags : -1;
+}
+
+typedef enum {
+    HTSRC_CK_normal,
+    HTSRC_CK_seen_excl,
+    HTSRC_CK_after_tagname,
+    HTSRC_CK_seen_dot
+} html_src_check_state;
+
+static void append_close_tag(char *tagname,
+			     HT_tagspec ** head,
+			     HT_tagspec ** tail)
+{
+    int idx, nattr;
+    HTTag *tag;
+    HT_tagspec *subj;
+
+    idx = html_src_tag_index(tagname);
+    tag = HTML_dtd.tags + idx;
+    nattr = tag->number_of_attributes;
+
+    if (idx == -1) {
+	fprintf(stderr,
+		"internal error: previous check didn't find bad HTML tag %s", tagname);
+	exit_immediately(EXIT_FAILURE);
+    }
+
+    subj = typecalloc(HT_tagspec);
+    subj->element = (HTMLElement) idx;
+    subj->present = typecallocn(BOOL, (unsigned) nattr);
+    subj->value = typecallocn(char *, (unsigned) nattr);
+
+    subj->start = FALSE;
+#ifdef USE_COLOR_STYLE
+    subj->class_name = NULL;
+#endif
+
+    if (!*head) {
+	*head = subj;
+	*tail = subj;
+    } else {
+	(*tail)->next = subj;
+	*tail = subj;
+    }
+}
+
+/* this will allocate node, initialize all members, and node
+   append to the list, possibly modifying head and modifying tail */
+static void append_open_tag(char *tagname,
+			    char *classname GCC_UNUSED,
+			    HT_tagspec ** head,
+			    HT_tagspec ** tail)
+{
+    HT_tagspec *subj;
+
+#ifdef USE_COLOR_STYLE
+    int hcode;
+#endif
+
+    append_close_tag(tagname, head, tail);	/* initialize common members */
+    subj = *tail;
+    subj->start = TRUE;
+
+#ifdef USE_COLOR_STYLE
+    hcode = hash_code_lowercase_on_fly(tagname);
+    if (non_empty(classname)) {
+
+#  if 0
+	/*
+	 * we don't provide a classname as attribute of that tag, since for
+	 * plain formatting tags they are not used directly for anything except
+	 * style - and we provide style value directly.
+	 */
+	HTTag *tag = HTML_dtd.tags + subj->element;
+	int class_attr_idx = 0;
+	int n = tag->number_of_attributes;
+	attr *attrs = tag->attributes;
+
+/*.... *//* this is not implemented though it's easy */
+#  endif
+
+	hcode = hash_code_aggregate_char('.', hcode);
+	hcode = hash_code_aggregate_lower_str(classname, hcode);
+	StrAllocCopy(subj->class_name, classname);
+    } else {
+	StrAllocCopy(subj->class_name, "");
+    }
+    subj->style = hcode;
+#endif
+}
+
+#define isLeadP(p) ((isalpha(UCH(*p)) || *p == '_'))
+#define isNextP(p) ((isalnum(UCH(*p)) || *p == '_'))
+
+#define FMT_AT " at column %d:\n\t%s\n"
+#define TXT_AT (int) (1 + p - ts), ts
+
+/* returns FALSE if incorrect */
+int html_src_parse_tagspec(char *ts,
+			   HTlexeme lexeme,
+			   BOOL checkonly,
+			   BOOL isstart)
+{
+    BOOL stop = FALSE;
+    BOOL code = FALSE;
+    char *p = ts;
+    char *tagstart = 0;
+    char *tagend = 0;
+    char *classstart;
+    char *classend;
+    char save, save1;
+    char after_excl = FALSE;
+    html_src_check_state state = HTSRC_CK_normal;
+    HT_tagspec *head = NULL;
+    HT_tagspec *tail = NULL;
+    HT_tagspec **slot = (isstart ? lexeme_start : lexeme_end) + lexeme;
+
+    while (!stop) {
+	switch (state) {
+	case HTSRC_CK_normal:
+	case HTSRC_CK_seen_excl:
+	    switch (*p) {
+	    case '\0':
+		stop = TRUE;
+		code = TRUE;
+		break;
+	    case ' ':
+	    case '\t':
+		break;
+	    case '!':
+		if (state == HTSRC_CK_seen_excl) {
+		    CTRACE2(TRACE_CFG,
+			    (tfp, "second '!'" FMT_AT,
+			     TXT_AT));
+		    stop = TRUE;
+		    break;
+		}
+		state = HTSRC_CK_seen_excl;
+		after_excl = TRUE;
+		break;
+	    default:
+		if (!isLeadP(p)) {
+		    CTRACE2(TRACE_CFG,
+			    (tfp, "no name starting" FMT_AT,
+			     TXT_AT));
+		    stop = TRUE;
+		    break;
+		}
+		tagstart = p;
+		while (*p && isNextP(p))
+		    ++p;
+		tagend = p--;
+		state = HTSRC_CK_after_tagname;
+	    }
+	    break;
+	case HTSRC_CK_after_tagname:
+	    switch (*p) {
+	    case '\0':
+		stop = TRUE;
+		code = TRUE;
+		/* FALLTHRU */
+	    case ' ':
+		/* FALLTHRU */
+	    case '\t':
+		save = *tagend;
+
+		*tagend = '\0';
+		classstart = 0;
+		if (checkonly) {
+		    int idx = html_src_tag_index(tagstart);
+
+		    CTRACE2(TRACE_CFG,
+			    (tfp, "tag index(%s) = %d\n",
+			     tagstart, idx));
+
+		    *tagend = save;
+		    if (idx == -1) {
+			stop = TRUE;
+			break;
+		    }
+		} else {
+		    if (after_excl)
+			append_close_tag(tagstart, &head, &tail);
+		    else
+			append_open_tag(tagstart, NULL, &head, &tail);
+		}
+		state = HTSRC_CK_normal;
+		after_excl = FALSE;
+		break;
+	    case '.':
+		if (after_excl) {
+		    CTRACE2(TRACE_CFG,
+			    (tfp, "dot after '!'" FMT_AT,
+			     TXT_AT));
+		    stop = TRUE;
+		    break;
+		}
+		state = HTSRC_CK_seen_dot;
+		break;
+	    default:
+		CTRACE2(TRACE_CFG,
+			(tfp, "unexpected char '%c' after tagname" FMT_AT,
+			 *p, TXT_AT));
+		stop = TRUE;
+		break;
+	    }
+	    break;
+	case HTSRC_CK_seen_dot:
+	    switch (*p) {
+	    case ' ':
+	    case '\t':
+		break;
+	    case '\0':
+		CTRACE2(TRACE_CFG,
+			(tfp, "expected text after dot" FMT_AT,
+			 TXT_AT));
+		stop = TRUE;
+		break;
+	    default:
+		if (!isLeadP(p)) {
+		    CTRACE2(TRACE_CFG,
+			    (tfp, "no name starting" FMT_AT,
+			     TXT_AT));
+		    stop = TRUE;
+		    break;
+		}
+		classstart = p;
+		while (*p && isNextP(p))
+		    ++p;
+		classend = p--;
+		save = *classend;
+		*classend = '\0';
+		save1 = *tagend;
+		*tagend = '\0';
+		if (checkonly) {
+		    int idx = html_src_tag_index(tagstart);
+
+		    *tagend = save1;
+		    *classend = save;
+		    if (idx == -1)
+			return FALSE;
+		} else {
+		    append_open_tag(tagstart, classstart, &head, &tail);
+		}
+		state = HTSRC_CK_normal;
+		after_excl = FALSE;
+		break;
+	    }			/* of switch(*p) */
+	    break;
+	}			/* of switch */
+	++p;
+    }
+
+    if (code && !checkonly)
+	*slot = head;
+
+    return code;
+}
+
+/*this will clean the data associated with lexeme 'l' */
+void html_src_clean_item(HTlexeme l)
+{
+    int i;
+
+    if (HTL_tagspecs[l])
+	FREE(HTL_tagspecs[l]);
+    for (i = 0; i < 2; ++i) {
+	HT_tagspec *cur;
+	HT_tagspec **pts = (i ? lexeme_start : lexeme_end) + l;
+	HT_tagspec *ts = *pts;
+
+	*pts = NULL;
+	while (ts) {
+	    FREE(ts->present);
+	    FREE(ts->value);
+#ifdef USE_COLOR_STYLE
+	    if (ts->start) {
+		FREE(ts->class_name);
+	    }
+#endif
+	    cur = ts;
+	    ts = ts->next;
+	    FREE(cur);
+	}
+    }
+}
+
+/*this will be registered with atexit*/
+void html_src_clean_data(void)
+{
+    int i;
+
+    for (i = 0; i < HTL_num_lexemes; ++i)
+	html_src_clean_item((HTlexeme) i);
+}
+
+void html_src_on_lynxcfg_reload(void)
+{
+    html_src_clean_data();
+    HTMLSRC_init_caches(TRUE);
+}
+
+static void failed_init(const char *tag, int lexeme)
+{
+    fprintf(stderr,
+	    gettext("parse-error while caching %s tagspec of lexeme %d\n"),
+	    tag, lexeme);
+    fprintf(stderr,
+	    gettext("Use -trace -trace-mask=8 to see details in log.\n"));
+    exit_immediately(EXIT_FAILURE);
+}
+
+void HTMLSRC_init_caches(BOOL dont_exit)
+{
+    int i;
+    char *p;
+    char buf[1000];
+
+    CTRACE2(TRACE_CFG, (tfp, "HTMLSRC_init_caches(%d tagspecs)\n", HTL_num_lexemes));
+    for (i = 0; i < HTL_num_lexemes; ++i) {
+	/*we assume that HT_tagspecs was NULLs at when program started */
+	LYstrncpy(buf,
+		  HTL_tagspecs[i]
+		  ? HTL_tagspecs[i]
+		  : HTL_tagspecs_defaults[i],
+		  sizeof(buf) - 1);
+	StrAllocCopy(HTL_tagspecs[i], buf);
+
+	CTRACE2(TRACE_CFG, (tfp, "parsing lexeme %d: %s\n", i + 1, buf));
+
+	if ((p = strchr(buf, ':')) != 0)
+	    *p = '\0';
+	if (!html_src_parse_tagspec(buf,
+				    (HTlexeme) i,
+				    FALSE,
+				    TRUE) && !dont_exit) {
+	    failed_init("1st", i);
+	}
+	if (!html_src_parse_tagspec(p ? p + 1 : NULL,
+				    (HTlexeme) i,
+				    FALSE,
+				    FALSE) && !dont_exit) {
+	    failed_init("2nd", i);
+	}
+    }
+}
+
+#endif /* ifdef USE_PRETTYSRC */
diff --git a/src/LYPrettySrc.h b/src/LYPrettySrc.h
new file mode 100644
index 00000000..0031eb7c
--- /dev/null
+++ b/src/LYPrettySrc.h
@@ -0,0 +1,92 @@
+/*
+ * $LynxId: LYPrettySrc.h,v 1.10 2009/03/11 00:40:00 tom Exp $
+ */
+#ifndef LYPrettySrc_H
+#define LYPrettySrc_H
+
+#ifdef USE_PRETTYSRC
+
+#include <HTMLDTD.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    extern BOOL psrc_convert_string;
+
+    /*whether HTML_put_string should convert string passed with 
+       TRANSLATE_AND_UNESCAPE_TO_STD */
+    extern BOOL psrc_view;
+    extern BOOLEAN LYpsrc;
+
+/*
+ * This is used for tracking down whether the SGML engine was initialized
+ * ==TRUE if yes.  It's value is meaningful if psrc_view = TRUE
+ */
+    extern BOOL sgml_in_psrc_was_initialized;
+
+    extern BOOL psrc_nested_call;	/* this is used when distinguishing whether 
+
+					   the current call is nested or not in HTML.c HTML_{start,end}_element.
+					   It ==FALSE if psrc_view==FALSE || sgml_in_psrc_was_initialized==TRUE */
+
+    extern BOOL psrc_first_tag;	/* this is also used in HTML.c to trigger the 
+
+				   1st tag to preform special.
+				 */
+
+    extern BOOL mark_htext_as_source;
+
+/* here is a list of lexeme codes. */
+    typedef enum {
+	HTL_comm = 0,
+	HTL_tag,
+	HTL_attrib,
+	HTL_attrval,
+	HTL_abracket,
+	HTL_entity,
+	HTL_href,
+	HTL_entire,
+	HTL_badseq,
+	HTL_badtag,
+	HTL_badattr,
+	HTL_sgmlspecial,
+	HTL_num_lexemes
+    } HTlexeme;
+
+    typedef struct _HT_tagspec {
+	struct _HT_tagspec *next;	/* 0 at the last */
+#ifdef USE_COLOR_STYLE
+	int style;		/* precalculated value of the style */
+	char *class_name;
+#endif
+	/* these will be passed to HTML_start_element */
+	HTMLElement element;
+	BOOL *present;
+	char **value;
+
+	BOOL start;		/* if true, then this starts element, otherwise - ends */
+    } HT_tagspec;
+
+    extern char *HTL_tagspecs[HTL_num_lexemes];
+    extern HT_tagspec *lexeme_start[HTL_num_lexemes];
+    extern HT_tagspec *lexeme_end[HTL_num_lexemes];
+
+    extern int html_src_parse_tagspec(char *ts, HTlexeme lexeme,
+				      BOOL checkonly, BOOL isstart);
+    extern void HTMLSRC_init_caches(BOOL dont_exit);
+    extern void html_src_clean_item(HTlexeme l);
+    extern void html_src_clean_data(void);
+    extern void html_src_on_lynxcfg_reload(void);
+
+/* these 2 vars tell what kind of transform should be appiled to tag names
+  and attribute names. 0 - lowercase, 1 - as is, 2 uppercase. */
+    extern int tagname_transform;
+    extern int attrname_transform;
+
+    extern BOOLEAN psrcview_no_anchor_numbering;
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* ifdef USE_PRETTYSRC */
+#endif				/* LYPrettySrc_H */
diff --git a/src/LYPrint.c b/src/LYPrint.c
new file mode 100644
index 00000000..ba71a6d5
--- /dev/null
+++ b/src/LYPrint.c
@@ -0,0 +1,1458 @@
+/*
+ * $LynxId: LYPrint.c,v 1.87 2010/04/29 23:46:07 tom Exp $
+ */
+#include <HTUtils.h>
+#include <HTAccess.h>
+#include <HTList.h>
+#include <HTAlert.h>
+#include <HTFile.h>
+#include <LYCurses.h>
+#include <GridText.h>
+#include <LYUtils.h>
+#include <LYPrint.h>
+#include <LYGlobalDefs.h>
+#include <LYSignal.h>
+#include <LYStrings.h>
+#include <LYClean.h>
+#include <LYGetFile.h>
+#include <LYHistory.h>
+#include <LYList.h>
+#include <LYCharSets.h>		/* To get current charset for mail header. */
+
+#include <LYLeaks.h>
+
+#define CancelPrint(msg) HTInfoMsg(msg); goto done
+#define CannotPrint(msg) HTAlert(msg); goto done
+
+/*
+ * printfile prints out the current file minus the links and targets to a
+ * variety of places
+ */
+
+/* it parses an incoming link that looks like
+ *
+ * LYNXPRINT://LOCAL_FILE/lines=##
+ * LYNXPRINT://MAIL_FILE/lines=##
+ * LYNXPRINT://TO_SCREEN/lines=##
+ * LYNXPRINT://LPANSI/lines=##
+ * LYNXPRINT://PRINTER/lines=##/number=#
+ */
+
+#define TO_FILE   1
+#define TO_SCREEN 2
+/*
+ * "lpansi.c"
+ * Original author: Gary Day (gday@comp.uark.edu), 11/30/93
+ * Current version: 2.1 by Noel Hunter (noel@wfu.edu), 10/20/94
+ *
+ * Basic structure based on print -- format files for printing from
+ * _Practical_C_Programming by Steve Oualline, O'Reilly & Associates
+ *
+ * adapted from the README for lpansi.c v2.1, dated 10/20/1994:
+ *		    Print to ANSI printer on local terminal
+ *     The VT100 standard defines printer on and off escape sequences,
+ *     esc[5i is printer on, and esc[4i is printer off.
+ *
+ * incorporate the idea of "lpansi" directly into LYPrint.c - HN
+ */
+#define LPANSI	  3
+#define MAIL	  4
+#define PRINTER   5
+
+#if USE_VMS_MAILER
+static int remove_quotes(char *string);
+#endif /* USE_VMS_MAILER */
+
+static char *subject_translate8bit(char *source);
+
+#define LYNX_PRINT_TITLE   0
+#define LYNX_PRINT_URL     1
+#define LYNX_PRINT_DATE    2
+#define LYNX_PRINT_LASTMOD 3
+
+#define MAX_PUTENV 4
+
+static void set_environ(int name,
+			const char *value,
+			const char *no_value)
+{
+    static const char *names[MAX_PUTENV] =
+    {
+	"LYNX_PRINT_TITLE",
+	"LYNX_PRINT_URL",
+	"LYNX_PRINT_DATE",
+	"LYNX_PRINT_LASTMOD",
+    };
+    static char *pointers[MAX_PUTENV];
+    char *envbuffer = 0;
+
+#ifdef VMS
+#define SET_ENVIRON(name, value, no_value) set_environ(name, value, no_value)
+    char temp[80];
+
+    StrAllocCopy(envbuffer, value);
+    if (isEmpty(envbuffer))
+	StrAllocCopy(envbuffer, no_value);
+    Define_VMSLogical(strcpy(temp, names[name]), envbuffer);
+    FREE(envbuffer);
+#else
+#define SET_ENVIRON(name, value, no_value) set_environ(name, value, "")
+    /*
+     * Once we've given a string to 'putenv()', we must not free it until we
+     * give it a string to replace it.
+     */
+    StrAllocCopy(envbuffer, names[name]);
+    StrAllocCat(envbuffer, "=");
+    StrAllocCat(envbuffer, value ? value : no_value);
+    putenv(envbuffer);
+    FREE(pointers[name]);
+    pointers[name] = envbuffer;
+#endif
+}
+
+static char *suggested_filename(DocInfo *newdoc)
+{
+    char *sug_filename = 0;
+    int rootlen;
+
+    /*
+     * Load the suggested filename string.  - FM
+     */
+    if (HText_getSugFname() != 0)
+	StrAllocCopy(sug_filename, HText_getSugFname());	/* must be freed */
+    else
+	StrAllocCopy(sug_filename, newdoc->address);	/* must be freed */
+    /*
+     * Strip suffix for compressed-files, if present.
+     */
+    if (HTCompressFileType(sug_filename, ".", &rootlen) != cftNone)
+	sug_filename[rootlen] = '\0';
+
+    CTRACE((tfp, "suggest %s\n", sug_filename));
+    return sug_filename;
+}
+
+static void SetupFilename(char *filename,
+			  const char *sug_filename)
+{
+    HTFormat format;
+    HTAtom *encoding;
+    char *cp;
+
+    LYstrncpy(filename, sug_filename, LY_MAXPATH - 1);	/* add suggestion info */
+    /* make the sug_filename conform to system specs */
+    change_sug_filename(filename);
+    if (!(HTisDocumentSource())
+	&& (cp = strrchr(filename, '.')) != NULL
+	&& (cp - filename) < (LY_MAXPATH - (int) (sizeof(TEXT_SUFFIX) + 1))) {
+	format = HTFileFormat(filename, &encoding, NULL);
+	CTRACE((tfp, "... format %s\n", format->name));
+	if (!strcasecomp(format->name, "text/html") ||
+	    !IsUnityEnc(encoding)) {
+	    strcpy(cp, TEXT_SUFFIX);
+	}
+    }
+    CTRACE((tfp, "... result %s\n", filename));
+}
+
+#define FN_INIT 0
+#define FN_READ 1
+#define FN_DONE 2
+#define FN_QUIT 3
+
+#define PRINT_FLAG   0
+#define GENERIC_FLAG 1
+
+static int RecallFilename(char *filename,
+			  BOOLEAN *first,
+			  int *now,
+			  int *total,
+			  int flag)
+{
+    int ch;
+    char *cp;
+    RecallType recall;
+
+    /*
+     * Set up the sug_filenames recall buffer.
+     */
+    if (*now < 0) {
+	*total = (sug_filenames ? HTList_count(sug_filenames) : 0);
+	*now = *total;
+    }
+    recall = ((*total >= 1) ? RECALL_URL : NORECALL);
+
+    if ((ch = LYgetstr(filename, VISIBLE, LY_MAXPATH, recall)) < 0 ||
+	*filename == '\0' || ch == UPARROW || ch == DNARROW) {
+	if (recall && ch == UPARROW) {
+	    if (*first) {
+		*first = FALSE;
+		/*
+		 * Use the last Fname in the list.  - FM
+		 */
+		*now = 0;
+	    } else {
+		/*
+		 * Go back to the previous Fname in the list.  - FM
+		 */
+		*now += 1;
+	    }
+	    if (*now >= *total) {
+		/*
+		 * Reset the *first flag, and use sug_file or a blank.  -
+		 * FM
+		 */
+		*first = TRUE;
+		*now = *total;
+		_statusline(FILENAME_PROMPT);
+		return FN_INIT;
+	    } else if ((cp = (char *) HTList_objectAt(sug_filenames,
+						      *now)) != NULL) {
+		LYstrncpy(filename, cp, LY_MAXPATH - 1);
+		if (*total == 1) {
+		    _statusline(EDIT_THE_PREV_FILENAME);
+		} else {
+		    _statusline(EDIT_A_PREV_FILENAME);
+		}
+		return FN_READ;
+	    }
+	} else if (recall && ch == DNARROW) {
+	    if (*first) {
+		*first = FALSE;
+		/*
+		 * Use the first Fname in the list. - FM
+		 */
+		*now = *total - 1;
+	    } else {
+		/*
+		 * Advance to the next Fname in the list. - FM
+		 */
+		*now -= 1;
+	    }
+	    if (*now < 0) {
+		/*
+		 * Set the *first flag, and use sug_file or a blank.  - FM
+		 */
+		*first = TRUE;
+		*now = *total;
+		_statusline(FILENAME_PROMPT);
+		return FN_INIT;
+	    } else if ((cp = (char *) HTList_objectAt(sug_filenames,
+						      *now)) != NULL) {
+		LYstrncpy(filename, cp, LY_MAXPATH - 1);
+		if (*total == 1) {
+		    _statusline(EDIT_THE_PREV_FILENAME);
+		} else {
+		    _statusline(EDIT_A_PREV_FILENAME);
+		}
+		return FN_READ;
+	    }
+	}
+
+	/*
+	 * Operation cancelled.
+	 */
+	if (flag == PRINT_FLAG)
+	    HTInfoMsg(SAVE_REQUEST_CANCELLED);
+	else if (flag == GENERIC_FLAG)
+	    return FN_QUIT;
+
+	return FN_QUIT;
+    }
+    return FN_DONE;
+}
+
+static BOOLEAN confirm_by_pages(const char *prompt,
+				int lines_in_file,
+				int lines_per_page)
+{
+    int pages = lines_in_file / (lines_per_page + 1);
+    int c;
+
+    /* count fractional pages ! */
+    if ((lines_in_file % (LYlines + 1)) > 0)
+	pages++;
+
+    if (pages > 4) {
+	char *msg = 0;
+
+	HTSprintf0(&msg, prompt, pages);
+	c = HTConfirmDefault(msg, YES);
+	FREE(msg);
+
+	if (c == YES) {
+	    LYaddstr("   Ok...");
+	} else {
+	    HTInfoMsg(PRINT_REQUEST_CANCELLED);
+	    return FALSE;
+	}
+    }
+    return TRUE;
+}
+
+static void send_file_to_file(DocInfo *newdoc,
+			      char *content_base,
+			      char *sug_filename)
+{
+    BOOLEAN FirstRecall = TRUE;
+    BOOLEAN use_cte;
+    const char *disp_charset;
+    FILE *outfile_fp;
+    char buffer[LY_MAXPATH];
+    char filename[LY_MAXPATH];
+    int FnameNum = -1;
+    int FnameTotal;
+    int c = 0;
+
+    _statusline(FILENAME_PROMPT);
+  retry:
+    SetupFilename(filename, sug_filename);
+    if (lynx_save_space
+	&& (strlen(lynx_save_space) + strlen(filename)) < sizeof(filename)) {
+	strcpy(buffer, lynx_save_space);
+	strcat(buffer, filename);
+	strcpy(filename, buffer);
+    }
+  check_recall:
+    switch (RecallFilename(filename, &FirstRecall, &FnameNum,
+			   &FnameTotal, PRINT_FLAG)) {
+    case FN_INIT:
+	goto retry;
+    case FN_READ:
+	goto check_recall;
+    case FN_QUIT:
+	goto done;
+    default:
+	break;
+    }
+
+    if (!LYValidateFilename(buffer, filename)) {
+	CancelPrint(SAVE_REQUEST_CANCELLED);
+    }
+
+    /*
+     * See if it already exists.
+     */
+    switch (LYValidateOutput(buffer)) {
+    case 'Y':
+	break;
+    case 'N':
+	_statusline(NEW_FILENAME_PROMPT);
+	FirstRecall = TRUE;
+	FnameNum = FnameTotal;
+	goto retry;
+    default:
+	goto done;
+    }
+
+    /*
+     * See if we can write to it.
+     */
+    CTRACE((tfp, "LYPrint: filename is %s, action is `%c'\n", buffer, c));
+
+#ifdef HAVE_POPEN
+    if (*buffer == '|') {
+	if (no_shell) {
+	    HTUserMsg(SPAWNING_DISABLED);
+	    FirstRecall = TRUE;
+	    FnameNum = FnameTotal;
+	    goto retry;
+	} else if ((outfile_fp = popen(buffer + 1, "w")) == NULL) {
+	    CTRACE((tfp, "LYPrint: errno is %d\n", errno));
+	    HTAlert(CANNOT_WRITE_TO_FILE);
+	    _statusline(NEW_FILENAME_PROMPT);
+	    FirstRecall = TRUE;
+	    FnameNum = FnameTotal;
+	    goto retry;
+	}
+    } else
+#endif
+	if ((outfile_fp = (TOUPPER(c) == 'A'
+			   ? LYAppendToTxtFile(buffer)
+			   : LYNewTxtFile(buffer))) == NULL) {
+	CTRACE((tfp, "LYPrint: errno is %d\n", errno));
+	HTAlert(CANNOT_WRITE_TO_FILE);
+	_statusline(NEW_FILENAME_PROMPT);
+	FirstRecall = TRUE;
+	FnameNum = FnameTotal;
+	goto retry;
+    }
+
+    if (LYPrependBaseToSource && HTisDocumentSource()) {
+	/*
+	 * Added the document's base as a BASE tag to the top of the file.  May
+	 * create technically invalid HTML, but will help get any partial or
+	 * relative URLs resolved properly if no BASE tag is present to replace
+	 * it.  - FM
+	 *
+	 * Add timestamp (last reload).
+	 */
+
+	fprintf(outfile_fp,
+		"<!-- X-URL: %s -->\n", newdoc->address);
+	if (HText_getDate() != NULL) {
+	    fprintf(outfile_fp,
+		    "<!-- Date: %s -->\n", HText_getDate());
+	    if (HText_getLastModified() != NULL
+		&& strcmp(HText_getLastModified(), HText_getDate())
+		&& strcmp(HText_getLastModified(),
+			  "Thu, 01 Jan 1970 00:00:01 GMT")) {
+		fprintf(outfile_fp,
+			"<!-- Last-Modified: %s -->\n", HText_getLastModified());
+	    }
+	}
+
+	fprintf(outfile_fp,
+		"<BASE HREF=\"%s\">\n", content_base);
+    }
+
+    if (LYPrependCharsetToSource && HTisDocumentSource()) {
+	/*
+	 * Added the document's charset as a META CHARSET tag to the top of the
+	 * file.  May create technically invalid HTML, but will help to resolve
+	 * properly the document converted via chartrans:  printed document
+	 * correspond to a display charset and we *should* override both
+	 * assume_local_charset and original document's META CHARSET (if any).
+	 *
+	 * Currently, if several META CHARSETs are found Lynx uses the first
+	 * only, and it is opposite to BASE where the original BASE in the
+	 * <HEAD> overrides ones from the top.
+	 *
+	 * As in print-to-email we write charset only if the document has 8-bit
+	 * characters, and we have no CJK or an unofficial "x-" charset.
+	 */
+	use_cte = HTLoadedDocumentEightbit();
+	disp_charset = LYCharSet_UC[current_char_set].MIMEname;
+	if (!use_cte || LYHaveCJKCharacterSet ||
+	    strncasecomp(disp_charset, "x-", 2) == 0) {
+	} else {
+	    fprintf(outfile_fp,
+		    "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=%s\">\n\n",
+		    disp_charset);
+	}
+    }
+
+    print_wwwfile_to_fd(outfile_fp, FALSE, FALSE);	/* FILE */
+    if (keypad_mode)
+	printlist(outfile_fp, FALSE);
+
+#ifdef HAVE_POPEN
+    if (LYIsPipeCommand(buffer))
+	pclose(outfile_fp);
+    else
+#endif
+	LYCloseOutput(outfile_fp);
+
+#ifdef VMS
+    if (0 == strncasecomp(buffer, "sys$disk:", 9)) {
+	if (0 == strncmp((buffer + 9), "[]", 2)) {
+	    HTAddSugFilename(buffer + 11);
+	} else {
+	    HTAddSugFilename(buffer + 9);
+	}
+    } else {
+	HTAddSugFilename(buffer);
+    }
+#else
+    HTAddSugFilename(buffer);
+#endif /* VMS */
+
+  done:
+    return;
+}
+
+static void send_file_to_mail(DocInfo *newdoc,
+			      char *content_base,
+			      char *content_location)
+{
+    static BOOLEAN first_mail_preparsed = TRUE;
+
+#if USE_VMS_MAILER
+    BOOLEAN isPMDF = LYMailPMDF();
+    FILE *hfd;
+    char hdrfile[LY_MAXPATH];
+#endif
+    BOOL use_mime;
+
+#if !CAN_PIPE_TO_MAILER
+    char my_temp[LY_MAXPATH];
+#endif
+
+    BOOL use_cte;
+    BOOL use_type;
+    const char *disp_charset;
+    FILE *outfile_fp;
+    char *buffer = NULL;
+    char *subject = NULL;
+    char user_response[LINESIZE];
+
+    if (!LYSystemMail())
+	return;
+
+    if (LYPreparsedSource && first_mail_preparsed &&
+	HTisDocumentSource()) {
+	if (HTConfirmDefault(CONFIRM_MAIL_SOURCE_PREPARSED, NO) == YES) {
+	    LYaddstr("   Ok...");
+	    first_mail_preparsed = FALSE;
+	} else {
+	    CancelPrint(MAIL_REQUEST_CANCELLED);
+	}
+    }
+
+    _statusline(MAIL_ADDRESS_PROMPT);
+    LYstrncpy(user_response, personal_mail_address, sizeof(user_response) - 1);
+    if (LYgetstr(user_response,
+		 VISIBLE,
+		 sizeof(user_response),
+		 RECALL_MAIL) < 0 ||
+	*user_response == '\0') {
+	CancelPrint(MAIL_REQUEST_CANCELLED);
+    }
+
+    /*
+     * Determine which mail headers should be sent.  Use Content-Type and
+     * MIME-Version headers only if needed.  We need them if we are mailing
+     * HTML source, or if we have 8-bit characters and will be sending
+     * Content-Transfer-Encoding to indicate this.  We will append a charset
+     * parameter to the Content-Type if we do not have an "x-" charset, and we
+     * will include the Content-Transfer-Encoding only if we are appending the
+     * charset parameter, because indicating an 8-bit transfer without also
+     * indicating the charset can cause problems with many mailers.  - FM & KW
+     */
+    disp_charset = LYCharSet_UC[current_char_set].MIMEname;
+    use_cte = HTLoadedDocumentEightbit();
+    if (!(use_cte && strncasecomp(disp_charset, "x-", 2))) {
+	disp_charset = NULL;
+#if USE_VMS_MAILER
+	use_cte = FALSE;
+#endif
+    }
+#if USE_VMS_MAILER
+    use_type = (BOOL) (disp_charset || HTisDocumentSource());
+#endif
+
+    /*
+     * Use newdoc->title as a subject instead of sug_filename:  MORE readable
+     * and 8-bit letters shouldn't be a problem - LP
+     */
+    /* change_sug_filename(sug_filename); */
+    subject = subject_translate8bit(newdoc->title);
+
+    if (newdoc->isHEAD) {
+	/*
+	 * Special case for mailing HEAD responce:  this is rather technical
+	 * information, show URL.
+	 */
+	FREE(subject);
+	StrAllocCopy(subject, "HEAD  ");
+	StrAllocCat(subject, newdoc->address);
+    }
+#if USE_VMS_MAILER
+    if (strchr(user_response, '@') && !strchr(user_response, ':') &&
+	!strchr(user_response, '%') && !strchr(user_response, '"')) {
+	char *temp = 0;
+
+	HTSprintf0(&temp, mail_adrs, user_response);
+	LYstrncpy(user_response, temp, sizeof(user_response) - 1);
+	FREE(temp);
+    }
+
+    outfile_fp = LYOpenTemp(my_temp,
+			    (HTisDocumentSource())
+			    ? HTML_SUFFIX
+			    : TEXT_SUFFIX,
+			    "w");
+    if (outfile_fp == NULL) {
+	CannotPrint(UNABLE_TO_OPEN_TEMPFILE);
+    }
+
+    if (isPMDF) {
+	if ((hfd = LYOpenTemp(hdrfile, TEXT_SUFFIX, "w")) == NULL) {
+	    CannotPrint(UNABLE_TO_OPEN_TEMPFILE);
+	}
+	if (use_type) {
+	    fprintf(hfd, "Mime-Version: 1.0\n");
+	    if (use_cte) {
+		fprintf(hfd, "Content-Transfer-Encoding: 8bit\n");
+	    }
+	}
+	if (HTisDocumentSource()) {
+	    /*
+	     * Add Content-Type, Content-Location, and Content-Base headers for
+	     * HTML source.  - FM
+	     */
+	    fprintf(hfd, "Content-Type: text/html");
+	    if (disp_charset != NULL) {
+		fprintf(hfd, "; charset=%s\n", disp_charset);
+	    } else {
+		fprintf(hfd, "\n");
+	    }
+	    fprintf(hfd, "Content-Base: %s\n", content_base);
+	    fprintf(hfd, "Content-Location: %s\n", content_location);
+	} else {
+	    /*
+	     * Add Content-Type:  text/plain if we have 8-bit characters and a
+	     * valid charset for non-source documents.  - FM
+	     */
+	    if (disp_charset != NULL) {
+		fprintf(hfd,
+			"Content-Type: text/plain; charset=%s\n",
+			disp_charset);
+	    }
+	}
+	/*
+	 * X-URL header.  - FM
+	 */
+	fprintf(hfd, "X-URL: %s\n", newdoc->address);
+	/*
+	 * For PMDF, put the subject in the header file and close it.  - FM
+	 */
+	fprintf(hfd, "Subject: %.70s\n\n", subject);
+	LYCloseTempFP(hfd);
+    }
+
+    /*
+     * Write the contents to a temp file.
+     */
+    if (LYPrependBaseToSource && HTisDocumentSource()) {
+	/*
+	 * Added the document's base as a BASE tag to the top of the message
+	 * body.  May create technically invalid HTML, but will help get any
+	 * partial or relative URLs resolved properly if no BASE tag is present
+	 * to replace it.  - FM
+	 */
+	fprintf(outfile_fp,
+		"<!-- X-URL: %s -->\n<BASE HREF=\"%s\">\n\n",
+		newdoc->address, content_base);
+    } else if (!isPMDF) {
+	fprintf(outfile_fp, "X-URL: %s\n\n", newdoc->address);
+    }
+    print_wwwfile_to_fd(outfile_fp, TRUE, FALSE);	/* MAIL */
+    if (keypad_mode)
+	printlist(outfile_fp, FALSE);
+    LYCloseTempFP(outfile_fp);
+
+    buffer = NULL;
+    if (isPMDF) {
+	/*
+	 * Now set up the command.  - FM
+	 */
+	HTSprintf0(&buffer,
+		   "%s %s %s,%s %s",
+		   system_mail,
+		   system_mail_flags,
+		   hdrfile,
+		   my_temp,
+		   user_response);
+    } else {
+	/*
+	 * For "generic" VMS MAIL, include the subject in the command.  - FM
+	 */
+	remove_quotes(subject);
+	HTSprintf0(&buffer,
+		   "%s %s/subject=\"%.70s\" %s %s",
+		   system_mail,
+		   system_mail_flags,
+		   subject,
+		   my_temp,
+		   user_response);
+    }
+
+    stop_curses();
+    SetOutputMode(O_TEXT);
+    printf(MAILING_FILE);
+    LYSystem(buffer);
+    LYSleepAlert();
+    start_curses();
+    SetOutputMode(O_BINARY);
+
+    if (isPMDF)
+	LYRemoveTemp(hdrfile);
+    LYRemoveTemp(my_temp);
+#else /* !VMS (Unix or DOS) */
+
+#if CAN_PIPE_TO_MAILER
+    outfile_fp = LYPipeToMailer();
+#else
+    outfile_fp = LYOpenTemp(my_temp, TEXT_SUFFIX, "w");
+#endif
+    if (outfile_fp == NULL) {
+	CannotPrint(MAIL_REQUEST_FAILED);
+    }
+
+    /*
+     * Determine which mail headers should be sent.  Use Content-Type and
+     * MIME-Version headers only if needed.  We need them if we are mailing
+     * HTML source, or if we have 8-bit characters and will be sending
+     * Content-Transfer-Encoding to indicate this.
+     *
+     * Send Content-Transfer-Encoding only if the document has 8-bit
+     * characters.  Send a charset parameter only if the document has 8-bit
+     * characters and we seem to have a valid charset.  - kw
+     */
+    use_cte = HTLoadedDocumentEightbit();
+    disp_charset = LYCharSet_UC[current_char_set].MIMEname;
+    /*
+     * Don't send a charset if we have a CJK character set selected, since it
+     * may not be appropriate for mail...  Also don't use an unofficial "x-"
+     * charset.  - kw
+     */
+    if (!use_cte || LYHaveCJKCharacterSet ||
+	strncasecomp(disp_charset, "x-", 2) == 0) {
+	disp_charset = NULL;
+    }
+#ifdef NOTDEFINED
+    /* Enable this if indicating an 8-bit transfer without also indicating the
+     * charset causes problems.  - kw */
+    if (use_cte && !disp_charset)
+	use_cte = FALSE;
+#endif /* NOTDEFINED */
+    use_type = (BOOL) (disp_charset || HTisDocumentSource());
+    use_mime = (BOOL) (use_cte || use_type);
+
+    if (use_mime) {
+	fprintf(outfile_fp, "Mime-Version: 1.0\n");
+	if (use_cte) {
+	    fprintf(outfile_fp, "Content-Transfer-Encoding: 8bit\n");
+	}
+    }
+
+    if (HTisDocumentSource()) {
+	/*
+	 * Add Content-Type, Content-Location, and Content-Base headers for
+	 * HTML source.  - FM
+	 */
+	fprintf(outfile_fp, "Content-Type: text/html");
+	if (disp_charset != NULL) {
+	    fprintf(outfile_fp, "; charset=%s\n", disp_charset);
+	} else {
+	    fprintf(outfile_fp, "\n");
+	}
+    } else {
+	/*
+	 * Add Content-Type:  text/plain if we have 8-bit characters and a
+	 * valid charset for non-source documents.  - KW
+	 */
+	if (disp_charset != NULL) {
+	    fprintf(outfile_fp,
+		    "Content-Type: text/plain; charset=%s\n",
+		    disp_charset);
+	}
+    }
+    /*
+     * If we are using MIME headers, add content-base and content-location if
+     * we have them.  This will always be the case if the document is source.
+     * - kw
+     */
+    if (use_mime) {
+	if (content_base)
+	    fprintf(outfile_fp, "Content-Base: %s\n", content_base);
+	if (content_location)
+	    fprintf(outfile_fp, "Content-Location: %s\n", content_location);
+    }
+
+    /*
+     * Add the To, Subject, and X-URL headers.  - FM
+     */
+    fprintf(outfile_fp, "To: %s\nSubject: %s\n", user_response, subject);
+    fprintf(outfile_fp, "X-URL: %s\n\n", newdoc->address);
+
+    if (LYPrependBaseToSource && HTisDocumentSource()) {
+	/*
+	 * Added the document's base as a BASE tag to the top of the message
+	 * body.  May create technically invalid HTML, but will help get any
+	 * partial or relative URLs resolved properly if no BASE tag is present
+	 * to replace it.  - FM
+	 */
+	fprintf(outfile_fp,
+		"<!-- X-URL: %s -->\n<BASE HREF=\"%s\">\n\n",
+		newdoc->address, content_base);
+    }
+    print_wwwfile_to_fd(outfile_fp, TRUE, FALSE);	/* MAIL */
+    if (keypad_mode)
+	printlist(outfile_fp, FALSE);
+
+#if CAN_PIPE_TO_MAILER
+    pclose(outfile_fp);
+#else
+    LYCloseOutput(outfile_fp);
+    LYSendMailFile(user_response,
+		   my_temp,
+		   subject,
+		   "",
+		   "");
+    LYRemoveTemp(my_temp);	/* Delete the tmpfile. */
+#endif /* CAN_PIPE_TO_MAILER */
+#endif /* USE_VMS_MAILER */
+
+  done:			/* send_file_to_mail() */
+    FREE(buffer);
+    FREE(subject);
+    return;
+}
+
+static void send_file_to_printer(DocInfo *newdoc,
+				 char *content_base,
+				 char *sug_filename,
+				 int printer_number)
+{
+    BOOLEAN FirstRecall = TRUE;
+    FILE *outfile_fp;
+    char *the_command = 0;
+    char my_file[LY_MAXPATH];
+    char my_temp[LY_MAXPATH];
+    int FnameTotal, FnameNum = -1;
+    lynx_list_item_type *cur_printer;
+
+    outfile_fp = LYOpenTemp(my_temp,
+			    (HTisDocumentSource())
+			    ? HTML_SUFFIX
+			    : TEXT_SUFFIX,
+			    "w");
+    if (outfile_fp == NULL) {
+	CannotPrint(FILE_ALLOC_FAILED);
+    }
+
+    if (LYPrependBaseToSource && HTisDocumentSource()) {
+	/*
+	 * Added the document's base as a BASE tag to the top of the file.  May
+	 * create technically invalid HTML, but will help get any partial or
+	 * relative URLs resolved properly if no BASE tag is present to replace
+	 * it.  - FM
+	 */
+	fprintf(outfile_fp,
+		"<!-- X-URL: %s -->\n<BASE HREF=\"%s\">\n\n",
+		newdoc->address, content_base);
+    }
+    print_wwwfile_to_fd(outfile_fp, FALSE, FALSE);	/* PRINTER */
+    if (keypad_mode)
+	printlist(outfile_fp, FALSE);
+
+    LYCloseTempFP(outfile_fp);
+
+    /* find the right printer number */
+    {
+	int count = 0;
+
+	for (cur_printer = printers;
+	     count < printer_number;
+	     count++, cur_printer = cur_printer->next) ;	/* null body */
+    }
+
+    /*
+     * Commands have the form "command %s [%s] [etc]" where %s is the filename
+     * and the second optional %s is the suggested filename.
+     */
+    if (cur_printer->command == NULL) {
+	CannotPrint(PRINTER_MISCONF_ERROR);
+    }
+
+    /*
+     * Check for two '%s' and ask for the second filename argument if there
+     * is.
+     */
+    if (HTCountCommandArgs(cur_printer->command) >= 2) {
+	_statusline(FILENAME_PROMPT);
+      again:
+	SetupFilename(my_file, sug_filename);
+      check_again:
+	switch (RecallFilename(my_file, &FirstRecall, &FnameNum,
+			       &FnameTotal, PRINT_FLAG)) {
+	case FN_INIT:
+	    goto again;
+	case FN_READ:
+	    goto check_again;
+	case FN_QUIT:
+	    goto done;
+	default:
+	    break;
+	}
+
+	if (no_dotfiles || !show_dotfiles) {
+	    if (*LYPathLeaf(my_file) == '.') {
+		HTAlert(FILENAME_CANNOT_BE_DOT);
+		_statusline(NEW_FILENAME_PROMPT);
+		FirstRecall = TRUE;
+		FnameNum = FnameTotal;
+		goto again;
+	    }
+	}
+	/*
+	 * Cancel if the user entered "/dev/null" on Unix, or an "nl:" path
+	 * on VMS.  - FM
+	 */
+	if (LYIsNullDevice(my_file)) {
+	    CancelPrint(PRINT_REQUEST_CANCELLED);
+	}
+	HTAddSugFilename(my_file);
+    }
+#ifdef SH_EX			/* 1999/01/04 (Mon) 09:37:03 */
+    else {
+	my_file[0] = '\0';
+    }
+
+    HTAddParam(&the_command, cur_printer->command, 1, my_temp);
+    if (my_file[0]) {
+	HTAddParam(&the_command, cur_printer->command, 2, my_file);
+	HTEndParam(&the_command, cur_printer->command, 3);
+    } else {
+	HTEndParam(&the_command, cur_printer->command, 2);
+    }
+#else
+    HTAddParam(&the_command, cur_printer->command, 1, my_temp);
+    HTAddParam(&the_command, cur_printer->command, 2, my_file);
+    HTEndParam(&the_command, cur_printer->command, 2);
+#endif
+
+    /*
+     * Move the cursor to the top of the screen so that output from system'd
+     * commands don't scroll up the screen.
+     */
+    LYmove(1, 1);
+
+    stop_curses();
+    CTRACE((tfp, "command: %s\n", the_command));
+    SetOutputMode(O_TEXT);
+    printf(PRINTING_FILE);
+    /*
+     * Set various bits of document information as environment variables, for
+     * use by external print scripts/etc.  On UNIX, We assume there are values,
+     * and leave NULL value checking up to the external PRINTER:  cmd/script -
+     * KED
+     */
+    SET_ENVIRON(LYNX_PRINT_TITLE, HText_getTitle(), "No Title");
+    SET_ENVIRON(LYNX_PRINT_URL, newdoc->address, "No URL");
+    SET_ENVIRON(LYNX_PRINT_DATE, HText_getDate(), "No Date");
+    SET_ENVIRON(LYNX_PRINT_LASTMOD, HText_getLastModified(), "No LastMod");
+
+    LYSystem(the_command);
+    FREE(the_command);
+    LYRemoveTemp(my_temp);
+
+    /*
+     * Remove the various LYNX_PRINT_xxxx logicals.  - KED
+     * [could use unsetenv(), but it's not portable]
+     */
+    SET_ENVIRON(LYNX_PRINT_TITLE, "", "");
+    SET_ENVIRON(LYNX_PRINT_URL, "", "");
+    SET_ENVIRON(LYNX_PRINT_DATE, "", "");
+    SET_ENVIRON(LYNX_PRINT_LASTMOD, "", "");
+
+    fflush(stdout);
+#ifndef VMS
+    signal(SIGINT, cleanup_sig);
+#endif /* !VMS */
+#ifdef SH_EX
+    fprintf(stdout, gettext(" Print job complete.\n"));
+    fflush(stdout);
+#endif
+    SetOutputMode(O_BINARY);
+    LYSleepMsg();
+    start_curses();
+
+  done:			/* send_file_to_printer() */
+    return;
+}
+
+static void send_file_to_screen(DocInfo *newdoc,
+				char *content_base,
+				BOOLEAN Lpansi)
+{
+    FILE *outfile_fp;
+    char prompt[80];
+
+    if (Lpansi) {
+	_statusline(CHECK_PRINTER);
+    } else {
+	_statusline(PRESS_RETURN_TO_BEGIN);
+    }
+
+    *prompt = '\0';
+    if (LYgetstr(prompt, VISIBLE, sizeof(prompt), NORECALL) < 0) {
+	CancelPrint(PRINT_REQUEST_CANCELLED);
+    }
+
+    outfile_fp = stdout;
+
+    stop_curses();
+    SetOutputMode(O_TEXT);
+
+#ifndef VMS
+    signal(SIGINT, SIG_IGN);
+#endif /* !VMS */
+
+    if (LYPrependBaseToSource && HTisDocumentSource()) {
+	/*
+	 * Added the document's base as a BASE tag to the top of the file.  May
+	 * create technically invalid HTML, but will help get any partial or
+	 * relative URLs resolved properly if no BASE tag is present to replace
+	 * it.  - FM
+	 */
+	fprintf(outfile_fp,
+		"<!-- X-URL: %s -->\n<BASE HREF=\"%s\">\n\n",
+		newdoc->address, content_base);
+    }
+    if (Lpansi)
+	printf("\033[5i");
+    print_wwwfile_to_fd(outfile_fp, FALSE, FALSE);	/* SCREEN */
+    if (keypad_mode)
+	printlist(outfile_fp, FALSE);
+
+#ifdef VMS
+    if (HadVMSInterrupt) {
+	HadVMSInterrupt = FALSE;
+	start_curses();
+	CancelPrint(PRINT_REQUEST_CANCELLED);
+    }
+#endif /* VMS */
+    if (Lpansi) {
+	printf("\n\014");	/* Form feed */
+	printf("\033[4i");
+	fflush(stdout);		/* refresh to screen */
+    } else {
+	fprintf(stdout, "\n\n%s", PRESS_RETURN_TO_FINISH);
+	fflush(stdout);		/* refresh to screen */
+	(void) LYgetch();	/* grab some user input to pause */
+#ifdef VMS
+	HadVMSInterrupt = FALSE;
+#endif /* VMS */
+    }
+#ifdef SH_EX
+    fprintf(stdout, "\n");
+#endif
+    SetOutputMode(O_BINARY);
+    start_curses();
+
+  done:			/* send_file_to_screen() */
+    return;
+}
+
+int printfile(DocInfo *newdoc)
+{
+    BOOLEAN Lpansi = FALSE;
+    DocAddress WWWDoc;
+    char *content_base = NULL;
+    char *content_location = NULL;
+    char *cp = NULL;
+    char *link_info = NULL;
+    char *sug_filename = NULL;
+    int lines_in_file = 0;
+    int pagelen = 0;
+    int printer_number = 0;
+    int type = 0;
+
+    /*
+     * Extract useful info from URL.
+     */
+    StrAllocCopy(link_info, newdoc->address + 12);
+
+    /*
+     * Reload the file we want to print into memory.
+     */
+    LYpop(newdoc);
+    WWWDoc.address = newdoc->address;
+    WWWDoc.post_data = newdoc->post_data;
+    WWWDoc.post_content_type = newdoc->post_content_type;
+    WWWDoc.bookmark = newdoc->bookmark;
+    WWWDoc.isHEAD = newdoc->isHEAD;
+    WWWDoc.safe = newdoc->safe;
+    if (!HTLoadAbsolute(&WWWDoc))
+	return (NOT_FOUND);
+
+    /*
+     * If we have an explicit content-base, we may use it even if not in source
+     * mode.  - kw
+     */
+    if (HText_getContentBase()) {
+	StrAllocCopy(content_base, HText_getContentBase());
+	LYRemoveBlanks(content_base);
+	if (isEmpty(content_base)) {
+	    FREE(content_base);
+	}
+    }
+    /*
+     * If document is source, load the content_base and content_location
+     * strings.  - FM
+     */
+    if (HTisDocumentSource()) {
+	if (HText_getContentLocation()) {
+	    StrAllocCopy(content_location, HText_getContentLocation());
+	    LYRemoveBlanks(content_location);
+	    if (isEmpty(content_location)) {
+		FREE(content_location);
+	    }
+	}
+	if (!content_base) {
+	    if ((content_location) && is_url(content_location)) {
+		StrAllocCopy(content_base, content_location);
+	    } else {
+		StrAllocCopy(content_base, newdoc->address);
+	    }
+	}
+	if (!content_location) {
+	    StrAllocCopy(content_location, newdoc->address);
+	}
+    }
+
+    sug_filename = suggested_filename(newdoc);
+
+    /*
+     * Get the number of lines in the file.
+     */
+    if ((cp = strstr(link_info, "lines=")) != NULL) {
+	/*
+	 * Terminate prev string here.
+	 */
+	*cp = '\0';
+	/*
+	 * Number of characters in "lines=".
+	 */
+	cp += 6;
+
+	lines_in_file = atoi(cp);
+    }
+
+    /*
+     * Determine the type.
+     */
+    if (strstr(link_info, "LOCAL_FILE")) {
+	type = TO_FILE;
+    } else if (strstr(link_info, "TO_SCREEN")) {
+	type = TO_SCREEN;
+    } else if (strstr(link_info, "LPANSI")) {
+	Lpansi = TRUE;
+	type = TO_SCREEN;
+    } else if (strstr(link_info, "MAIL_FILE")) {
+	type = MAIL;
+    } else if (strstr(link_info, "PRINTER")) {
+	type = PRINTER;
+
+	if ((cp = strstr(link_info, "number=")) != NULL) {
+	    /* number of characters in "number=" */
+	    cp += 7;
+	    printer_number = atoi(cp);
+	}
+	if ((cp = strstr(link_info, "pagelen=")) != NULL) {
+	    /* number of characters in "pagelen=" */
+	    cp += 8;
+	    pagelen = atoi(cp);
+	} else {
+	    /* default to 66 lines */
+	    pagelen = 66;
+	}
+    }
+
+    /*
+     * Act on the request.  - FM
+     */
+    switch (type) {
+
+    case TO_FILE:
+	send_file_to_file(newdoc, content_base, sug_filename);
+	break;
+
+    case MAIL:
+	send_file_to_mail(newdoc, content_base, content_location);
+	break;
+
+    case TO_SCREEN:
+	if (confirm_by_pages(CONFIRM_LONG_SCREEN_PRINT, lines_in_file, LYlines))
+	    send_file_to_screen(newdoc, content_base, Lpansi);
+	break;
+
+    case PRINTER:
+	if (confirm_by_pages(CONFIRM_LONG_PAGE_PRINT, lines_in_file, pagelen))
+	    send_file_to_printer(newdoc, content_base, sug_filename, printer_number);
+	break;
+
+    }				/* end switch */
+
+    FREE(link_info);
+    FREE(sug_filename);
+    FREE(content_base);
+    FREE(content_location);
+    return (NORMAL);
+}
+
+#if USE_VMS_MAILER
+static int remove_quotes(char *string)
+{
+    int i;
+
+    for (i = 0; string[i] != '\0'; i++)
+	if (string[i] == '"')
+	    string[i] = ' ';
+	else if (string[i] == '&')
+	    string[i] = ' ';
+	else if (string[i] == '|')
+	    string[i] = ' ';
+
+    return (0);
+}
+#endif /* USE_VMS_MAILER */
+
+/*
+ * Mail subject may have 8-bit characters and they are in display charset.
+ * There is no stable practice for 8-bit subject encodings:  MIME defines
+ * "quoted-printable" which holds charset info but most mailers still don't
+ * support it.  On the other hand many mailers send open 8-bit subjects without
+ * charset info and use local assumption for certain countries.  Besides that,
+ * obsolete SMTP software is not 8bit clean but still in use, it strips the
+ * characters in 128-160 range from subjects which may be a fault outside
+ * iso-8859-XX.
+ *
+ * We translate subject to "outgoing_mail_charset" (defined in lynx.cfg) it may
+ * correspond to US-ASCII as the safest value or any other lynx character
+ * handler, -1 for no translation (so display charset).
+ *
+ * Always returns a new allocated string which has to be freed.
+ */
+#include <LYCharUtils.h>
+static char *subject_translate8bit(char *source)
+{
+    char *target = NULL;
+
+    int charset_in, charset_out;
+
+    int i = outgoing_mail_charset;	/* from lynx.cfg, -1 by default */
+
+    StrAllocCopy(target, source);
+    if (i < 0
+	|| i == current_char_set
+	|| LYCharSet_UC[current_char_set].enc == UCT_ENC_CJK
+	|| LYCharSet_UC[i].enc == UCT_ENC_CJK) {
+	return (target);	/* OK */
+    } else {
+	charset_out = i;
+	charset_in = current_char_set;
+    }
+
+    LYUCTranslateBackHeaderText(&target, charset_in, charset_out, YES);
+
+    return (target);
+}
+
+/*
+ * print_options writes out the current printer choices to a file
+ * so that the user can select printers in the same way that
+ * they select all other links
+ * printer links look like
+ *
+ * LYNXPRINT://LOCAL_FILE/lines=#	     print to a local file
+ * LYNXPRINT://TO_SCREEN/lines=#	     print to the screen
+ * LYNXPRINT://LPANSI/lines=#		     print to the local terminal
+ * LYNXPRINT://MAIL_FILE/lines=#	     mail the file
+ * LYNXPRINT://PRINTER/lines=#/number=#      print to printer number #
+ */
+int print_options(char **newfile,
+		  const char *printed_url,
+		  int lines_in_file)
+{
+    static char my_temp[LY_MAXPATH] = "\0";
+    char *buffer = 0;
+    int count;
+    int pages;
+    FILE *fp0;
+    lynx_list_item_type *cur_printer;
+
+    if ((fp0 = InternalPageFP(my_temp, TRUE)) == 0)
+	return (-1);
+
+    LYLocalFileToURL(newfile, my_temp);
+
+    BeginInternalPage(fp0, PRINT_OPTIONS_TITLE, PRINT_OPTIONS_HELP);
+
+    fprintf(fp0, "<pre>\n");
+
+    /*  pages = lines_in_file/66 + 1; */
+    pages = (lines_in_file + 65) / 66;
+    HTSprintf0(&buffer,
+	       "   <em>%s</em> %s\n   <em>%s</em> %d\n   <em>%s</em> %d %s %s\n",
+	       gettext("Document:"), printed_url,
+	       gettext("Number of lines:"), lines_in_file,
+	       gettext("Number of pages:"), pages,
+	       (pages > 1 ? gettext("pages") : gettext("page")),
+	       gettext("(approximately)"));
+    fputs(buffer, fp0);
+    FREE(buffer);
+
+    if (no_print || no_disk_save || no_mail)
+	fprintf(fp0,
+		"   <em>%s</em>\n",
+		gettext("Some print functions have been disabled!"));
+
+    fprintf(fp0, "\n%s\n",
+	    (user_mode == NOVICE_MODE)
+	    ? gettext("Standard print options:")
+	    : gettext("Print options:"));
+
+    if (no_disk_save == FALSE && no_print == FALSE) {
+	fprintf(fp0,
+		"   <a href=\"%s//LOCAL_FILE/lines=%d\">%s</a>\n",
+		STR_LYNXPRINT,
+		lines_in_file,
+		gettext("Save to a local file"));
+    } else {
+	fprintf(fp0, "   <em>%s</em>\n", gettext("Save to disk disabled"));
+    }
+    if (no_mail == FALSE && local_host_only == FALSE)
+	fprintf(fp0,
+		"   <a href=\"%s//MAIL_FILE/lines=%d\">%s</a>\n",
+		STR_LYNXPRINT,
+		lines_in_file,
+		gettext("Mail the file"));
+
+#if defined(UNIX) || defined(VMS)
+    fprintf(fp0,
+	    "   <a href=\"%s//TO_SCREEN/lines=%d\">%s</a>\n",
+	    STR_LYNXPRINT,
+	    lines_in_file,
+	    gettext("Print to the screen"));
+    fprintf(fp0,
+	    "   <a href=\"%s//LPANSI/lines=%d\">%s</a>\n",
+	    STR_LYNXPRINT,
+	    lines_in_file,
+	    gettext("Print out on a printer attached to your vt100 terminal"));
+#endif
+
+    if (user_mode == NOVICE_MODE)
+	fprintf(fp0, "\n%s\n", gettext("Local additions:"));
+
+    for (count = 0, cur_printer = printers; cur_printer != NULL;
+	 cur_printer = cur_printer->next, count++)
+	if (no_print == FALSE || cur_printer->always_enabled) {
+	    fprintf(fp0,
+		    "   <a href=\"%s//PRINTER/number=%d/pagelen=%d/lines=%d\">",
+		    STR_LYNXPRINT,
+		    count, cur_printer->pagelen, lines_in_file);
+	    fprintf(fp0, "%s", (cur_printer->name ?
+				cur_printer->name : "No Name Given"));
+	    fprintf(fp0, "</a>\n");
+	}
+    fprintf(fp0, "</pre>\n");
+    EndInternalPage(fp0);
+    LYCloseTempFP(fp0);
+
+    LYforce_no_cache = TRUE;
+    return (0);
+}
+
+/*
+ * General purpose filename getter.
+ *
+ * Returns a pointer to an absolute filename string, if the input filename
+ * exists, and is readable.  Returns NULL if the input was cancelled (^G, or CR
+ * on empty input).
+ *
+ * The pointer to the filename string needs to be free()'d by the caller (when
+ * non-NULL).
+ *
+ * --KED 02/21/99
+ */
+char *GetFileName(void)
+{
+    struct stat stat_info;
+
+    char fbuf[LY_MAXPATH];
+    char tbuf[LY_MAXPATH];
+    char *fn;
+
+    BOOLEAN FirstRecall = TRUE;
+    int FnameNum = -1;
+    int FnameTotal;
+
+    _statusline(FILENAME_PROMPT);
+
+  retry:
+    /*
+     * No initial filename.
+     */
+    SetupFilename(fbuf, "");
+
+  check_recall:
+    /*
+     * Go get a filename (it would be nice to do TAB == filename-completion as
+     * the name is entered, but we'll save doing that for another time.
+     */
+    switch (RecallFilename(fbuf, &FirstRecall, &FnameNum,
+			   &FnameTotal, GENERIC_FLAG)) {
+    case FN_INIT:
+	goto retry;
+    case FN_READ:
+	goto check_recall;
+    case FN_QUIT:
+	goto quit;
+    default:
+	break;
+    }
+
+    /*
+     * Add raw input form to list ...  we may want to reuse/edit it on a
+     * subsequent call, etc.
+     */
+#ifdef VMS
+    if (0 == strncasecomp(fbuf, "sys$disk:", 9)) {
+	if (0 == strncmp((fbuf + 9), "[]", 2)) {
+	    HTAddSugFilename(fbuf + 11);
+	} else {
+	    HTAddSugFilename(fbuf + 9);
+	}
+    } else {
+	HTAddSugFilename(fbuf);
+    }
+#else
+    HTAddSugFilename(fbuf);
+#endif /* VMS */
+
+    /*
+     * Expand tilde's, make filename absolute, etc.
+     */
+    if (!LYValidateFilename(tbuf, fbuf))
+	goto quit;
+
+    /*
+     * Check for file existence; readability.
+     */
+    if ((stat(tbuf, &stat_info) < 0) ||
+	(!(S_ISREG(stat_info.st_mode)
+#ifdef S_IFLNK
+	   || S_ISLNK(stat_info.st_mode)
+#endif /* S_IFLNK */
+	 ))) {
+	HTInfoMsg(FILE_DOES_NOT_EXIST);
+	_statusline(FILE_DOES_NOT_EXIST_RE);
+	FirstRecall = TRUE;
+	FnameNum = FnameTotal;
+	goto retry;
+    }
+
+    if (!LYCanReadFile(tbuf)) {
+	HTInfoMsg(FILE_NOT_READABLE);
+	_statusline(FILE_NOT_READABLE_RE);
+	FirstRecall = TRUE;
+	FnameNum = FnameTotal;
+	goto retry;
+    }
+
+    /*
+     * We have a valid filename, and readable file.  Return it to the caller.
+     *
+     * The returned pointer should be free()'d by the caller.
+     *
+     * [For some silly reason, if we use StrAllocCopy() here, we get an
+     * "invalid pointer" reported in the Lynx.leaks file (if compiled with
+     * --enable-find-leaks turned on.  Dumb.]
+     */
+    if ((fn = typecallocn(char, strlen(tbuf) + 1)) == NULL)
+	  outofmem(__FILE__, "GetFileName");
+
+    assert(fn != NULL);
+
+    return (strcpy(fn, tbuf));
+
+  quit:
+    /*
+     * The user cancelled the input (^G, or CR on empty input field).
+     */
+    return (NULL);
+}
diff --git a/src/LYPrint.h b/src/LYPrint.h
new file mode 100644
index 00000000..645b8749
--- /dev/null
+++ b/src/LYPrint.h
@@ -0,0 +1,20 @@
+#ifndef LYPRINT_H
+#define LYPRINT_H
+
+#ifndef LYSTRUCTS_H
+#include <LYStructs.h>
+#endif /* LYSTRUCTS_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    extern int printfile(DocInfo *newdoc);
+    extern int print_options(char **newfile,
+			     const char *printed_url,
+			     int lines_in_file);
+    extern char *GetFileName(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYPRINT_H */
diff --git a/src/LYReadCFG.c b/src/LYReadCFG.c
new file mode 100644
index 00000000..f0bd524b
--- /dev/null
+++ b/src/LYReadCFG.c
@@ -0,0 +1,2501 @@
+/*
+ * $LynxId: LYReadCFG.c,v 1.148 2010/05/02 22:22:56 tom Exp $
+ */
+#ifndef NO_RULES
+#include <HTRules.h>
+#else
+#include <HTUtils.h>
+#endif
+#include <HTTP.h>		/* 'reloading' flag */
+#include <HTFile.h>
+#include <HTInit.h>
+#include <UCMap.h>
+
+#include <LYUtils.h>
+#include <GridText.h>
+#include <LYStrings.h>
+#include <LYStructs.h>
+#include <LYGlobalDefs.h>
+#include <LYCharSets.h>
+#include <LYCharUtils.h>
+#include <LYKeymap.h>
+#include <LYJump.h>
+#include <LYGetFile.h>
+#include <LYCgi.h>
+#include <LYCurses.h>
+#include <LYBookmark.h>
+#include <LYCookie.h>
+#include <LYReadCFG.h>
+#include <HTAlert.h>
+#include <LYHistory.h>
+#include <LYPrettySrc.h>
+#include <LYrcFile.h>
+
+#ifdef DIRED_SUPPORT
+#include <LYLocal.h>
+#endif /* DIRED_SUPPORT */
+
+#include <LYexit.h>
+#include <LYLeaks.h>
+
+#ifndef DISABLE_NEWS
+#include <HTNews.h>
+#endif
+
+BOOLEAN have_read_cfg = FALSE;
+BOOLEAN LYUseNoviceLineTwo = TRUE;
+
+/*
+ * Translate a TRUE/FALSE field in a string buffer.
+ */
+static BOOL is_true(char *string)
+{
+    if (!strcasecomp(string, "TRUE") || !strcasecomp(string, "ON"))
+	return (TRUE);
+    else
+	return (FALSE);
+}
+
+/*
+ * Find an unescaped colon in a string buffer.
+ */
+static char *find_colon(char *buffer)
+{
+    char ch, *buf = buffer;
+
+    if (buf == NULL)
+	return NULL;
+
+    while ((ch = *buf) != 0) {
+	if (ch == ':')
+	    return buf;
+	if (ch == '\\') {
+	    buf++;
+	    if (*buf == 0)
+		break;
+	}
+	buf++;
+    }
+    return NULL;
+}
+
+static void free_item_list(lynx_list_item_type **ptr)
+{
+    lynx_list_item_type *cur = *ptr;
+    lynx_list_item_type *next;
+
+    while (cur) {
+	next = cur->next;
+	FREE(cur->name);
+	FREE(cur->command);
+	FREE(cur);
+	cur = next;
+    }
+    *ptr = NULL;
+}
+
+/*
+ * Function for freeing the DOWNLOADER and UPLOADER menus list.  - FM
+ */
+static void free_all_item_lists(void)
+{
+    free_item_list(&printers);
+    free_item_list(&downloaders);
+#ifdef DIRED_SUPPORT
+    free_item_list(&uploaders);
+#endif /* DIRED_SUPPORT */
+
+#ifdef USE_EXTERNALS
+    free_item_list(&externals);
+#endif /* USE_EXTERNALS */
+
+    return;
+}
+
+/*
+ * Process string buffer fields for DOWNLOADER or UPLOADER
+ *                               or PRINTERS   or EXTERNALS menus
+ */
+static void add_item_to_list(char *buffer,
+			     lynx_list_item_type **list_ptr,
+			     int special)
+{
+    char *colon, *next_colon, *last_colon;
+    lynx_list_item_type *cur_item, *prev_item;
+
+    /*
+     * Check if the XWINDOWS or NON_XWINDOWS keyword is present in the last
+     * field, and act properly when found depending if external environment
+     * $DISPLAY variable is set.
+     */
+    if ((last_colon = strrchr(buffer, ':')) != NULL && *(last_colon - 1) != '\\') {
+	*last_colon++ = '\0';
+	/*
+	 * If last_colon equals XWINDOWS then only continue
+	 * if there is a $DISPLAY variable
+	 */
+	if (!strcasecomp(last_colon, "XWINDOWS")) {
+	    if (LYgetXDisplay() == NULL)
+		return;
+	}
+	/*
+	 * If last_colon equals NON_XWINDOWS then only continue
+	 * if there is no $DISPLAY variable
+	 */
+	else if (!strcasecomp(last_colon, "NON_XWINDOWS")) {
+	    if (LYgetXDisplay() != NULL)
+		return;
+	}
+    }
+
+    /*
+     * Make a linked list
+     */
+    if (*list_ptr == NULL) {
+	/*
+	 * First item.
+	 */
+	cur_item = typecalloc(lynx_list_item_type);
+
+	if (cur_item == NULL)
+	    outofmem(__FILE__, "read_cfg");
+
+	assert(cur_item != NULL);
+
+	*list_ptr = cur_item;
+#ifdef LY_FIND_LEAKS
+	atexit(free_all_item_lists);
+#endif
+    } else {
+	/*
+	 * Find the last item.
+	 */
+	for (prev_item = *list_ptr;
+	     prev_item->next != NULL;
+	     prev_item = prev_item->next) ;	/* null body */
+	cur_item = typecalloc(lynx_list_item_type);
+
+	if (cur_item == NULL)
+	    outofmem(__FILE__, "read_cfg");
+	else
+	    prev_item->next = cur_item;
+
+	assert(cur_item != NULL);
+    }
+    cur_item->next = NULL;
+    cur_item->name = NULL;
+    cur_item->command = NULL;
+    cur_item->always_enabled = FALSE;
+    cur_item->override_primary_action = FALSE;
+    cur_item->pagelen = 66;
+
+    /*
+     * Find first unescaped colon and process fields
+     */
+    if ((colon = find_colon(buffer)) != NULL) {
+	/*
+	 * Process name field
+	 */
+	cur_item->name = typecallocn(char, (unsigned) (colon - buffer + 1));
+
+	if (cur_item->name == NULL)
+	    outofmem(__FILE__, "read_cfg");
+	LYstrncpy(cur_item->name, buffer, (int) (colon - buffer));
+	remove_backslashes(cur_item->name);
+
+	/*
+	 * Find end of command string and beginning of TRUE/FALSE option field. 
+	 * If we do not find a colon that ends the command string, leave the
+	 * always_enabled option flag as FALSE.  In any case, we want the
+	 * command string.
+	 */
+	if ((next_colon = find_colon(colon + 1)) == NULL) {
+	    next_colon = colon + strlen(colon);
+	}
+	if (next_colon - (colon + 1) > 0) {
+	    cur_item->command = typecallocn(char, (unsigned) (next_colon - colon));
+
+	    if (cur_item->command == NULL)
+		outofmem(__FILE__, "read_cfg");
+	    LYstrncpy(cur_item->command,
+		      colon + 1,
+		      (int) (next_colon - (colon + 1)));
+	    remove_backslashes(cur_item->command);
+	}
+	if (*next_colon++) {
+	    colon = next_colon;
+	    if ((next_colon = strchr(colon, ':')) != 0)
+		*next_colon++ = '\0';
+	    cur_item->always_enabled = is_true(colon);
+	    if (next_colon) {
+		if (special) {
+		    cur_item->pagelen = atoi(next_colon);
+		} else {
+		    cur_item->override_primary_action = is_true(next_colon);
+		}
+	    }
+	}
+    }
+}
+
+lynx_list_item_type *find_item_by_number(lynx_list_item_type *list_ptr,
+					 char *number)
+{
+    int value = atoi(number);
+
+    while (value-- >= 0 && list_ptr != 0) {
+	list_ptr = list_ptr->next;
+    }
+    return list_ptr;
+}
+
+int match_item_by_name(lynx_list_item_type *ptr, char *name,
+		       BOOLEAN only_overriders)
+{
+    return
+	(ptr->command != 0
+	 && !strncasecomp(ptr->name, name, (int) strlen(ptr->name))
+	 && (only_overriders ? ptr->override_primary_action : 1));
+}
+
+#if defined(USE_COLOR_STYLE) || defined(USE_COLOR_TABLE)
+
+#ifndef COLOR_WHITE
+#define COLOR_WHITE 7
+#endif
+
+#ifndef COLOR_BLACK
+#define COLOR_BLACK 0
+#endif
+
+#ifdef USE_DEFAULT_COLORS
+int default_fg = DEFAULT_COLOR;
+int default_bg = DEFAULT_COLOR;
+
+#else
+int default_fg = COLOR_WHITE;
+int default_bg = COLOR_BLACK;
+#endif
+
+static const char *Color_Strings[16] =
+{
+    "black",
+    "red",
+    "green",
+    "brown",
+    "blue",
+    "magenta",
+    "cyan",
+    "lightgray",
+    "gray",
+    "brightred",
+    "brightgreen",
+    "yellow",
+    "brightblue",
+    "brightmagenta",
+    "brightcyan",
+    "white"
+};
+
+#if defined(PDCURSES) && !defined(XCURSES)
+/*
+ * PDCurses (and possibly some other implementations) use a non-ANSI set of
+ * codes for colors.
+ */
+static int ColorCode(int color)
+{
+    /* *INDENT-OFF* */
+    static int map[] =
+    {
+	0,  4,  2,  6,  1,  5,  3,  7,
+	8, 12, 10, 14,  9, 13, 11, 15
+    };
+    /* *INDENT-ON* */
+
+    return map[color];
+}
+#else
+#define ColorCode(color) (color)
+#endif
+
+BOOL default_color_reset = FALSE;
+
+/*
+ * Validator for COLOR fields.
+ */
+int check_color(const char *color,
+		int the_default)
+{
+    int i;
+
+    CTRACE2(TRACE_STYLE, (tfp, "check_color(%s,%d)\n", color, the_default));
+    if (!strcasecomp(color, "default")) {
+#ifdef USE_DEFAULT_COLORS
+	if (LYuse_default_colors && !default_color_reset)
+	    the_default = DEFAULT_COLOR;
+#endif /* USE_DEFAULT_COLORS */
+	CTRACE2(TRACE_STYLE, (tfp, "=> default %d\n", the_default));
+	return the_default;
+    }
+    if (!strcasecomp(color, "nocolor"))
+	return NO_COLOR;
+
+    for (i = 0; i < 16; i++) {
+	if (!strcasecomp(color, Color_Strings[i])) {
+	    int c = ColorCode(i);
+
+	    CTRACE2(TRACE_STYLE, (tfp, "=> %d\n", c));
+	    return c;
+	}
+    }
+    CTRACE2(TRACE_STYLE, (tfp, "=> ERR_COLOR\n"));
+    return ERR_COLOR;
+}
+
+const char *lookup_color(int code)
+{
+    unsigned n;
+
+    for (n = 0; n < 16; n++) {
+	if ((int) ColorCode(n) == code)
+	    return Color_Strings[n];
+    }
+    return "default";
+}
+#endif /* USE_COLOR_STYLE || USE_COLOR_TABLE */
+
+#if defined(USE_COLOR_TABLE) || defined(EXP_ASSUMED_COLOR)
+
+/*
+ * Exit routine for failed COLOR parsing.
+ */
+static void exit_with_color_syntax(char *error_line)
+{
+    unsigned int i;
+
+    fprintf(stderr, gettext("\
+Syntax Error parsing COLOR in configuration file:\n\
+The line must be of the form:\n\
+COLOR:INTEGER:FOREGROUND:BACKGROUND\n\
+\n\
+Here FOREGROUND and BACKGROUND must be one of:\n\
+The special strings 'nocolor' or 'default', or\n")
+	);
+    for (i = 0; i < 16; i += 4) {
+	fprintf(stderr, "%16s %16s %16s %16s\n",
+		Color_Strings[i], Color_Strings[i + 1],
+		Color_Strings[i + 2], Color_Strings[i + 3]);
+    }
+    fprintf(stderr, "%s\nCOLOR:%s\n", gettext("Offending line:"), error_line);
+    exit_immediately(EXIT_FAILURE);
+}
+#endif /* defined(USE_COLOR_TABLE) || defined(EXP_ASSUMED_COLOR) */
+
+#if defined(USE_COLOR_TABLE)
+/*
+ * Process string buffer fields for COLOR setting.
+ */
+static void parse_color(char *buffer)
+{
+    int color;
+    char *fg, *bg;
+    char *temp = 0;
+
+    StrAllocCopy(temp, buffer);	/* save a copy, for error messages */
+
+    /*
+     * We are expecting a line of the form:
+     *    INTEGER:FOREGROUND:BACKGROUND
+     */
+    color = atoi(buffer);
+    if (NULL == (fg = find_colon(buffer)))
+	exit_with_color_syntax(temp);
+
+    assert(fg != NULL);
+
+    *fg++ = '\0';
+
+    if (NULL == (bg = find_colon(fg)))
+	exit_with_color_syntax(temp);
+
+    assert(bg != NULL);
+
+    *bg++ = '\0';
+
+#if defined(USE_SLANG)
+    if ((check_color(fg, default_fg) == ERR_COLOR) ||
+	(check_color(bg, default_bg) == ERR_COLOR))
+	exit_with_color_syntax(temp);
+
+    SLtt_set_color(color, NULL, fg, bg);
+#else
+    if (lynx_chg_color(color,
+		       check_color(fg, default_fg),
+		       check_color(bg, default_bg)) < 0)
+	exit_with_color_syntax(temp);
+#endif
+    FREE(temp);
+}
+#endif /* USE_COLOR_TABLE */
+/* *INDENT-OFF* */
+#ifdef USE_SOURCE_CACHE
+static Config_Enum tbl_source_cache[] = {
+    { "FILE",	SOURCE_CACHE_FILE },
+    { "MEMORY",	SOURCE_CACHE_MEMORY },
+    { "NONE",	SOURCE_CACHE_NONE },
+    { NULL,		-1 },
+};
+
+static Config_Enum tbl_abort_source_cache[] = {
+    { "KEEP",	SOURCE_CACHE_FOR_ABORTED_KEEP },
+    { "DROP",	SOURCE_CACHE_FOR_ABORTED_DROP },
+    { NULL,		-1 },
+};
+#endif
+/* *INDENT-ON* */
+
+#define PARSE_ADD(n,v)   {n, CONF_ADD_ITEM,    UNION_ADD(v), 0}
+#define PARSE_SET(n,v)   {n, CONF_BOOL,        UNION_SET(v), 0}
+#define PARSE_ENU(n,v,t) {n, CONF_ENUM,        UNION_INT(v), t}
+#define PARSE_INT(n,v)   {n, CONF_INT,         UNION_INT(v), 0}
+#define PARSE_TIM(n,v)   {n, CONF_TIME,        UNION_INT(v), 0}
+#define PARSE_STR(n,v)   {n, CONF_STR,         UNION_STR(v), 0}
+#define PARSE_PRG(n,v)   {n, CONF_PRG,         UNION_DEF(v), 0}
+#define PARSE_Env(n,v)   {n, CONF_ENV,         UNION_ENV(v), 0}
+#define PARSE_ENV(n,v)   {n, CONF_ENV2,        UNION_ENV(v), 0}
+#define PARSE_FUN(n,v)   {n, CONF_FUN,         UNION_FUN(v), 0}
+#define PARSE_REQ(n,v)   {n, CONF_INCLUDE,     UNION_FUN(v), 0}
+#define PARSE_LST(n,v)   {n, CONF_ADD_STRING,  UNION_LST(v), 0}
+#define PARSE_DEF(n,v)   {n, CONF_ADD_TRUSTED, UNION_DEF(v), 0}
+#define PARSE_NIL        {NULL, CONF_NIL,      UNION_DEF(0), 0}
+
+typedef enum {
+    CONF_NIL = 0
+    ,CONF_BOOL			/* BOOLEAN type */
+    ,CONF_FUN
+    ,CONF_TIME
+    ,CONF_ENUM
+    ,CONF_INT
+    ,CONF_STR
+    ,CONF_PRG
+    ,CONF_ENV			/* from environment variable */
+    ,CONF_ENV2			/* from environment VARIABLE */
+    ,CONF_INCLUDE		/* include file-- handle special */
+    ,CONF_ADD_ITEM
+    ,CONF_ADD_STRING
+    ,CONF_ADD_TRUSTED
+} Conf_Types;
+
+typedef struct {
+    const char *name;
+    Conf_Types type;
+      ParseData;
+    Config_Enum *table;
+} Config_Type;
+
+static int assume_charset_fun(char *value)
+{
+    UCLYhndl_for_unspec = safeUCGetLYhndl_byMIME(value);
+    StrAllocCopy(UCAssume_MIMEcharset,
+		 LYCharSet_UC[UCLYhndl_for_unspec].MIMEname);
+/*    this may be a memory for bogus typo -
+    StrAllocCopy(UCAssume_MIMEcharset, value);
+    LYLowerCase(UCAssume_MIMEcharset);    */
+
+    return 0;
+}
+
+static int assume_local_charset_fun(char *value)
+{
+    UCLYhndl_HTFile_for_unspec = safeUCGetLYhndl_byMIME(value);
+    return 0;
+}
+
+static int assume_unrec_charset_fun(char *value)
+{
+    UCLYhndl_for_unrec = safeUCGetLYhndl_byMIME(value);
+    return 0;
+}
+
+static int character_set_fun(char *value)
+{
+    int i = UCGetLYhndl_byAnyName(value);	/* by MIME or full name */
+
+    if (i < 0) {
+#ifdef CAN_AUTODETECT_DISPLAY_CHARSET
+	if (auto_display_charset >= 0
+	    && (!strnicmp(value, "AutoDetect ", 11)
+		|| !strnicmp(value, "AutoDetect-2 ", 13)))
+	    current_char_set = auto_display_charset;
+#endif
+	/* do nothing here: so fallback to userdefs.h */
+    } else
+	current_char_set = i;
+
+    return 0;
+}
+
+static int outgoing_mail_charset_fun(char *value)
+{
+    outgoing_mail_charset = UCGetLYhndl_byMIME(value);
+    /* -1 if NULL or not recognized value: no translation (compatibility) */
+
+    return 0;
+}
+
+#ifdef EXP_ASSUMED_COLOR
+/*
+ * Process string buffer fields for ASSUMED_COLOR setting.
+ */
+static int assumed_color_fun(char *buffer)
+{
+    char *fg = buffer, *bg;
+    char *temp = 0;
+
+    if (LYuse_default_colors) {
+	StrAllocCopy(temp, buffer);	/* save a copy, for error messages */
+
+	/*
+	 * We are expecting a line of the form:
+	 *    FOREGROUND:BACKGROUND
+	 */
+	if (NULL == (bg = find_colon(fg)))
+	    exit_with_color_syntax(temp);
+
+	assert(bg != NULL);
+
+	*bg++ = '\0';
+
+	default_fg = check_color(fg, default_fg);
+	default_bg = check_color(bg, default_bg);
+
+	if (default_fg == ERR_COLOR
+	    || default_bg == ERR_COLOR)
+	    exit_with_color_syntax(temp);
+#ifdef USE_SLANG
+	/*
+	 * Sorry - the order of initialization of slang precludes setting the
+	 * default colors from the lynx.cfg file, since slang is already
+	 * initialized before the file is read, and there is no interface
+	 * defined for setting it from the application (that's one of the
+	 * problems with using environment variables rather than a programmable
+	 * interface) -TD
+	 */
+#endif
+	FREE(temp);
+    } else {
+	CTRACE((tfp, "...ignored since DEFAULT_COLORS:off\n"));
+    }
+    return 0;
+}
+#endif /* EXP_ASSUMED_COLOR */
+
+#ifdef USE_COLOR_TABLE
+static int color_fun(char *value)
+{
+    parse_color(value);
+    return 0;
+}
+#endif
+
+#ifdef USE_DEFAULT_COLORS
+static int default_colors_fun(char *value)
+{
+    LYuse_default_colors = is_true(value);
+    if (LYuse_default_colors) {
+	default_fg = DEFAULT_COLOR;
+	default_bg = DEFAULT_COLOR;
+    } else {
+	default_color_reset = TRUE;
+	if (default_fg == DEFAULT_COLOR ||
+	    default_bg == DEFAULT_COLOR) {
+	    default_fg = COLOR_WHITE;
+	    default_bg = COLOR_BLACK;
+	    lynx_setup_colors();
+	}
+    }
+    return 0;
+}
+#endif
+
+static int default_bookmark_file_fun(char *value)
+{
+    set_default_bookmark_page(value);
+    return 0;
+}
+
+static int default_cache_size_fun(char *value)
+{
+    HTCacheSize = atoi(value);
+    if (HTCacheSize < 2)
+	HTCacheSize = 2;
+    return 0;
+}
+
+static int default_editor_fun(char *value)
+{
+    if (!system_editor)
+	StrAllocCopy(editor, value);
+    return 0;
+}
+
+static int numbers_as_arrows_fun(char *value)
+{
+    if (is_true(value))
+	keypad_mode = NUMBERS_AS_ARROWS;
+    else
+	keypad_mode = LINKS_ARE_NUMBERED;
+
+    return 0;
+}
+
+#ifdef DIRED_SUPPORT
+static int dired_menu_fun(char *value)
+{
+    add_menu_item(value);
+    return 0;
+}
+#endif
+
+static int jumpfile_fun(char *value)
+{
+    char *buffer = NULL;
+
+    HTSprintf0(&buffer, "JUMPFILE:%s", value);
+    if (!LYJumpInit(buffer))
+	CTRACE((tfp, "Failed to register %s\n", buffer));
+    FREE(buffer);
+
+    return 0;
+}
+
+#ifdef EXP_KEYBOARD_LAYOUT
+static int keyboard_layout_fun(char *key)
+{
+    if (!LYSetKbLayout(key))
+	CTRACE((tfp, "Failed to set keyboard layout %s\n", key));
+    return 0;
+}
+#endif /* EXP_KEYBOARD_LAYOUT */
+
+static int keymap_fun(char *key)
+{
+    char *func, *efunc;
+
+    if ((func = strchr(key, ':')) != NULL) {
+	*func++ = '\0';
+	efunc = strchr(func, ':');
+	/* Allow comments on the ends of key remapping lines. - DT */
+	/* Allow third field for line-editor action. - kw */
+	if (efunc == func) {	/* have 3rd field, but 2nd field empty */
+	    func = NULL;
+	} else if (efunc && strncasecomp(efunc + 1, "DIRED", 5) == 0) {
+	    if (!remap(key, strtok(func, " \t\n:#"), TRUE)) {
+		fprintf(stderr,
+			gettext("key remapping of %s to %s for %s failed\n"),
+			key, func, efunc + 1);
+	    } else if (func && !strcmp("TOGGLE_HELP", func)) {
+		LYUseNoviceLineTwo = FALSE;
+	    }
+	    return 0;
+	} else if (!remap(key, strtok(func, " \t\n:#"), FALSE)) {
+	    fprintf(stderr, gettext("key remapping of %s to %s failed\n"),
+		    key, func);
+	} else {
+	    if (func && !strcmp("TOGGLE_HELP", func))
+		LYUseNoviceLineTwo = FALSE;
+	}
+	if (efunc) {
+	    efunc++;
+	    if (efunc == strtok((func ? NULL : efunc), " \t\n:#") && *efunc) {
+		BOOLEAN success = FALSE;
+		int lkc = lkcstring_to_lkc(key);
+		int lec = -1;
+		int select_edi = 0;
+		char *sselect_edi = strtok(NULL, " \t\n:#");
+		char **endp = &sselect_edi;
+
+		if (sselect_edi) {
+		    if (*sselect_edi)
+			select_edi = strtol(sselect_edi, endp, 10);
+		    if (**endp != '\0') {
+			fprintf(stderr,
+				gettext("invalid line-editor selection %s for key %s, selecting all\n"),
+				sselect_edi, key);
+			select_edi = 0;
+		    }
+		}
+		/*
+		 * PASS!  tries to enter the key into the LYLineEditors
+		 * bindings in a different way from PASS, namely as binding
+		 * that maps to the specific lynx actioncode (rather than to
+		 * LYE_FORM_PASS).  That only works for lynx keycodes with
+		 * modifier bit set, and we have no documented/official way to
+		 * specify this in the KEYMAP directive, although it can be
+		 * made to work e.g. by specifying a hex value that has the
+		 * modifier bit set.  But knowledge about the bit pattern of
+		 * modifiers should remain in internal matter subject to
+		 * change...  At any rate, if PASS!  fails try it the same way
+		 * as for PASS.  - kw
+		 */
+		if (!success && strcasecomp(efunc, "PASS!") == 0) {
+		    if (func) {
+			lec = LYE_FORM_LAC | lacname_to_lac(func);
+			success = (BOOL) LYRemapEditBinding(lkc, lec, select_edi);
+		    }
+		    if (!success)
+			fprintf(stderr,
+				gettext("setting of line-editor binding for key %s (0x%x) to 0x%x for %s failed\n"),
+				key, lkc, lec, efunc);
+		    else
+			return 0;
+		}
+		if (!success) {
+		    lec = lecname_to_lec(efunc);
+		    success = (BOOL) LYRemapEditBinding(lkc, lec, select_edi);
+		}
+		if (!success) {
+		    if (lec != -1) {
+			fprintf(stderr,
+				gettext("setting of line-editor binding for key %s (0x%x) to 0x%x for %s failed\n"),
+				key, lkc, lec, efunc);
+		    } else {
+			fprintf(stderr,
+				gettext("setting of line-editor binding for key %s (0x%x) for %s failed\n"),
+				key, lkc, efunc);
+		    }
+		}
+	    }
+	}
+    }
+    return 0;
+}
+
+static int localhost_alias_fun(char *value)
+{
+    LYAddLocalhostAlias(value);
+    return 0;
+}
+
+#ifdef LYNXCGI_LINKS
+static int lynxcgi_environment_fun(char *value)
+{
+    add_lynxcgi_environment(value);
+    return 0;
+}
+#endif
+
+static int lynx_sig_file_fun(char *value)
+{
+    char temp[LY_MAXPATH];
+
+    LYstrncpy(temp, value, sizeof(temp) - 1);
+    if (LYPathOffHomeOK(temp, sizeof(temp))) {
+	StrAllocCopy(LynxSigFile, temp);
+	LYAddPathToHome(temp, sizeof(temp), LynxSigFile);
+	StrAllocCopy(LynxSigFile, temp);
+	CTRACE((tfp, "LYNX_SIG_FILE set to '%s'\n", LynxSigFile));
+    } else {
+	CTRACE((tfp, "LYNX_SIG_FILE '%s' is bad. Ignoring.\n", LYNX_SIG_FILE));
+    }
+    return 0;
+}
+
+#ifndef DISABLE_NEWS
+static int news_chunk_size_fun(char *value)
+{
+    HTNewsChunkSize = atoi(value);
+    /*
+     * If the new HTNewsChunkSize exceeds the maximum,
+     * increase HTNewsMaxChunk to this size. - FM
+     */
+    if (HTNewsChunkSize > HTNewsMaxChunk)
+	HTNewsMaxChunk = HTNewsChunkSize;
+    return 0;
+}
+
+static int news_max_chunk_fun(char *value)
+{
+    HTNewsMaxChunk = atoi(value);
+    /*
+     * If HTNewsChunkSize exceeds the new maximum,
+     * reduce HTNewsChunkSize to this maximum. - FM
+     */
+    if (HTNewsChunkSize > HTNewsMaxChunk)
+	HTNewsChunkSize = HTNewsMaxChunk;
+    return 0;
+}
+
+static int news_posting_fun(char *value)
+{
+    LYNewsPosting = is_true(value);
+    no_newspost = (BOOL) (LYNewsPosting == FALSE);
+    return 0;
+}
+#endif /* DISABLE_NEWS */
+
+#ifndef NO_RULES
+static int cern_rulesfile_fun(char *value)
+{
+    char *rulesfile1 = NULL;
+    char *rulesfile2 = NULL;
+
+    if (HTLoadRules(value) >= 0) {
+	return 0;
+    }
+    StrAllocCopy(rulesfile1, value);
+    LYTrimLeading(value);
+    LYTrimTrailing(value);
+
+    StrAllocCopy(rulesfile2, value);
+    LYTildeExpand(&rulesfile2, FALSE);
+
+    if (strcmp(rulesfile1, rulesfile2) &&
+	HTLoadRules(rulesfile2) >= 0) {
+	FREE(rulesfile1);
+	FREE(rulesfile2);
+	return 0;
+    }
+    fprintf(stderr,
+	    gettext("Lynx: cannot start, CERN rules file %s is not available\n"),
+	    non_empty(rulesfile2) ? rulesfile2 : gettext("(no name)"));
+    exit_immediately(EXIT_FAILURE);
+    return 0;			/* though redundant, for compiler-warnings */
+}
+#endif /* NO_RULES */
+
+static int referer_with_query_fun(char *value)
+{
+    if (!strncasecomp(value, "SEND", 4))
+	LYRefererWithQuery = 'S';
+    else if (!strncasecomp(value, "PARTIAL", 7))
+	LYRefererWithQuery = 'P';
+    else
+	LYRefererWithQuery = 'D';
+    return 0;
+}
+
+static int status_buffer_size_fun(char *value)
+{
+    status_buf_size = atoi(value);
+    if (status_buf_size < 2)
+	status_buf_size = 2;
+    return 0;
+}
+
+static int suffix_fun(char *value)
+{
+    char *mime_type, *p, *parsed;
+    const char *encoding = NULL;
+    char *sq = NULL;
+    char *description = NULL;
+    double q = 1.0;
+
+    if ((strlen(value) < 3)
+	|| (NULL == (mime_type = strchr(value, ':')))) {
+	CTRACE((tfp, "Invalid SUFFIX:%s ignored.\n", value));
+	return 0;
+    }
+
+    *mime_type++ = '\0';
+    if (*mime_type) {
+	if ((parsed = strchr(mime_type, ':')) != NULL) {
+	    *parsed++ = '\0';
+	    if ((sq = strchr(parsed, ':')) != NULL) {
+		*sq++ = '\0';
+		if ((description = strchr(sq, ':')) != NULL) {
+		    *description++ = '\0';
+		    if ((p = strchr(sq, ':')) != NULL)
+			*p = '\0';
+		    LYTrimTail(description);
+		}
+		LYRemoveBlanks(sq);
+		if (!*sq)
+		    sq = NULL;
+	    }
+	    LYRemoveBlanks(parsed);
+	    LYLowerCase(parsed);
+	    if (!*parsed)
+		parsed = NULL;
+	}
+	encoding = parsed;
+    }
+
+    LYRemoveBlanks(mime_type);
+    /*
+     * mime-type is not converted to lowercase on input, to make it possible to
+     * reproduce the equivalent of some of the HTInit.c defaults that use mixed
+     * case, although that is not recomended.  - kw
+     */
+    if (!*mime_type) {		/* that's ok now, with an encoding!  */
+	CTRACE((tfp, "SUFFIX:%s without MIME type for %s\n", value,
+		encoding ? encoding : "what?"));
+	mime_type = NULL;	/* that's ok now, with an encoding!  */
+	if (!encoding)
+	    return 0;
+    }
+
+    if (!encoding) {
+	if (strstr(mime_type, "tex") != NULL ||
+	    strstr(mime_type, "postscript") != NULL ||
+	    strstr(mime_type, "sh") != NULL ||
+	    strstr(mime_type, "troff") != NULL ||
+	    strstr(mime_type, "rtf") != NULL)
+	    encoding = "8bit";
+	else
+	    encoding = "binary";
+    }
+    if (!sq) {
+	q = 1.0;
+    } else {
+	double df = strtod(sq, &p);
+
+	if (p == sq && df <= 0.0) {
+	    CTRACE((tfp, "Invalid q=%s for SUFFIX:%s, using -1.0\n",
+		    sq, value));
+	    q = -1.0;
+	} else {
+	    q = df;
+	}
+    }
+    HTSetSuffix5(value, mime_type, encoding, description, q);
+
+    return 0;
+}
+
+static int suffix_order_fun(char *value)
+{
+    char *p = value;
+    char *optn;
+    BOOLEAN want_file_init_now = FALSE;
+
+    LYUseBuiltinSuffixes = TRUE;
+    while ((optn = HTNextTok(&p, ", ", "", NULL)) != NULL) {
+	if (!strcasecomp(optn, "NO_BUILTIN")) {
+	    LYUseBuiltinSuffixes = FALSE;
+	} else if (!strcasecomp(optn, "PRECEDENCE_HERE")) {
+	    want_file_init_now = TRUE;
+	} else if (!strcasecomp(optn, "PRECEDENCE_OTHER")) {
+	    want_file_init_now = FALSE;
+	} else {
+	    CTRACE((tfp, "Invalid SUFFIX_ORDER:%s\n", optn));
+	    break;
+	}
+    }
+
+    if (want_file_init_now && !FileInitAlreadyDone) {
+	HTFileInit();
+	FileInitAlreadyDone = TRUE;
+    }
+    return 0;
+}
+
+static int system_editor_fun(char *value)
+{
+    StrAllocCopy(editor, value);
+    system_editor = TRUE;
+    return 0;
+}
+
+#define SetViewer(mime_type, viewer) \
+    HTSetPresentation(mime_type, viewer, 0, 1.0, 3.0, 0.0, 0, mediaCFG)
+
+static int viewer_fun(char *value)
+{
+    char *mime_type;
+    char *viewer;
+    char *environment;
+
+    mime_type = value;
+
+    if ((strlen(value) < 3)
+	|| (NULL == (viewer = strchr(mime_type, ':'))))
+	return 0;
+
+    *viewer++ = '\0';
+
+    LYRemoveBlanks(mime_type);
+    LYLowerCase(mime_type);
+
+    environment = strrchr(viewer, ':');
+    if ((environment != NULL) &&
+	(strlen(viewer) > 1) && *(environment - 1) != '\\') {
+	*environment++ = '\0';
+	remove_backslashes(viewer);
+	/*
+	 * If environment equals xwindows then only assign the presentation if
+	 * there is a $DISPLAY variable.
+	 */
+	if (!strcasecomp(environment, "XWINDOWS")) {
+	    if (LYgetXDisplay() != NULL)
+		SetViewer(mime_type, viewer);
+	} else if (!strcasecomp(environment, "NON_XWINDOWS")) {
+	    if (LYgetXDisplay() == NULL)
+		SetViewer(mime_type, viewer);
+	} else {
+	    SetViewer(mime_type, viewer);
+	}
+    } else {
+	remove_backslashes(viewer);
+	SetViewer(mime_type, viewer);
+    }
+
+    return 0;
+}
+
+static int nonrest_sigwinch_fun(char *value)
+{
+    if (!strncasecomp(value, "XWINDOWS", 8)) {
+	LYNonRestartingSIGWINCH = (BOOL) (LYgetXDisplay() != NULL);
+    } else {
+	LYNonRestartingSIGWINCH = is_true(value);
+    }
+    return 0;
+}
+
+#ifdef USE_CHARSET_CHOICE
+static void matched_charset_choice(BOOL display_charset,
+				   int i)
+{
+    int j;
+
+    if (display_charset && !custom_display_charset) {
+	for (custom_display_charset = TRUE, j = 0; j < LYNumCharsets; ++j)
+	    charset_subsets[j].hide_display = TRUE;
+    } else if (!display_charset && !custom_assumed_doc_charset) {
+	for (custom_assumed_doc_charset = TRUE, j = 0; j < LYNumCharsets; ++j)
+	    charset_subsets[j].hide_assumed = TRUE;
+    }
+    if (display_charset)
+	charset_subsets[i].hide_display = FALSE;
+    else
+	charset_subsets[i].hide_assumed = FALSE;
+}
+
+static int parse_charset_choice(char *p,
+				BOOL display_charset)	/*if FALSE, then assumed doc charset */
+{
+    int len, i;
+    int matches = 0;
+
+    /*only one charset choice is allowed per line! */
+    LYTrimHead(p);
+    LYTrimTail(p);
+    CTRACE((tfp, "parsing charset choice for %s:\"%s\"",
+	    (display_charset ? "display charset" : "assumed doc charset"), p));
+    len = (int) strlen(p);
+    if (!len) {
+	CTRACE((tfp, " - EMPTY STRING\n"));
+	return 1;
+    }
+    if (*p == '*' && len == 1) {
+	if (display_charset)
+	    for (custom_display_charset = TRUE, i = 0; i < LYNumCharsets; ++i)
+		charset_subsets[i].hide_display = FALSE;
+	else
+	    for (custom_assumed_doc_charset = TRUE, i = 0; i < LYNumCharsets; ++i)
+		charset_subsets[i].hide_assumed = FALSE;
+	CTRACE((tfp, " - all unhidden\n"));
+	return 0;
+    }
+    if (p[len - 1] == '*') {
+	--len;
+	for (i = 0; i < LYNumCharsets; ++i) {
+	    if ((!strncasecomp(p, LYchar_set_names[i], len)) ||
+		(!strncasecomp(p, LYCharSet_UC[i].MIMEname, len))) {
+		++matches;
+		matched_charset_choice(display_charset, i);
+	    }
+	}
+	CTRACE((tfp, " - %d matches\n", matches));
+	return 0;
+    } else {
+	for (i = 0; i < LYNumCharsets; ++i) {
+	    if ((!strcasecomp(p, LYchar_set_names[i])) ||
+		(!strcasecomp(p, LYCharSet_UC[i].MIMEname))) {
+		matched_charset_choice(display_charset, i);
+		++matches;
+		CTRACE((tfp, " - OK, %d matches\n", matches));
+		return 0;
+	    }
+	}
+	CTRACE((tfp, " - NOT recognised\n"));
+	return 1;
+    }
+}
+
+static int parse_display_charset_choice(char *p)
+{
+    return parse_charset_choice(p, 1);
+}
+
+static int parse_assumed_doc_charset_choice(char *p)
+{
+    return parse_charset_choice(p, 0);
+}
+
+#endif /* USE_CHARSET_CHOICE */
+
+#ifdef USE_PRETTYSRC
+static void html_src_bad_syntax(char *value,
+				char *option_name)
+{
+    char *buf = 0;
+
+    HTSprintf0(&buf, "HTMLSRC_%s", option_name);
+    LYUpperCase(buf);
+    fprintf(stderr, "Bad syntax in TAGSPEC %s:%s\n", buf, value);
+    exit_immediately(EXIT_FAILURE);
+}
+
+static int parse_html_src_spec(HTlexeme lexeme_code, char *value,
+			       char *option_name)
+{
+    /* Now checking the value for being correct.  Since HTML_dtd is not
+     * initialized completely (member tags points to non-initiailized data), we
+     * use tags_old.  If the syntax is incorrect, then lynx will exit with error
+     * message.
+     */
+    char *ts2;
+
+    if (isEmpty(value))
+	return 0;		/* silently ignoring */
+
+#define BS() html_src_bad_syntax(value,option_name)
+
+    ts2 = strchr(value, ':');
+    if (!ts2)
+	BS();
+
+    assert(ts2 != NULL);
+
+    *ts2 = '\0';
+
+    CTRACE2(TRACE_CFG, (tfp,
+			"LYReadCFG - parsing tagspec '%s:%s' for option '%s'\n",
+			value, ts2, option_name));
+    html_src_clean_item(lexeme_code);
+    if (!html_src_parse_tagspec(value, lexeme_code, TRUE, TRUE)
+	|| !html_src_parse_tagspec(ts2, lexeme_code, TRUE, TRUE)) {
+	*ts2 = ':';
+	BS();
+    }
+
+    *ts2 = ':';
+    StrAllocCopy(HTL_tagspecs[lexeme_code], value);
+#undef BS
+    return 0;
+}
+
+static int psrcspec_fun(char *s)
+{
+    char *e;
+    /* *INDENT-OFF* */
+    static Config_Enum lexemnames[] =
+    {
+	{ "comm",	HTL_comm	},
+	{ "tag",	HTL_tag		},
+	{ "attrib",	HTL_attrib	},
+	{ "attrval",	HTL_attrval	},
+	{ "abracket",	HTL_abracket	},
+	{ "entity",	HTL_entity	},
+	{ "href",	HTL_href	},
+	{ "entire",	HTL_entire	},
+	{ "badseq",	HTL_badseq	},
+	{ "badtag",	HTL_badtag	},
+	{ "badattr",	HTL_badattr	},
+	{ "sgmlspecial", HTL_sgmlspecial },
+	{ NULL,		-1		}
+    };
+    /* *INDENT-ON* */
+
+    int found;
+
+    e = strchr(s, ':');
+    if (!e) {
+	CTRACE((tfp,
+		"bad format of PRETTYSRC_SPEC setting value, ignored %s\n",
+		s));
+	return 0;
+    }
+    *e = '\0';
+    if (!LYgetEnum(lexemnames, s, &found)) {
+	CTRACE((tfp,
+		"bad format of PRETTYSRC_SPEC setting value, ignored %s:%s\n",
+		s, e + 1));
+	return 0;
+    }
+    parse_html_src_spec((HTlexeme) found, e + 1, s);
+    return 0;
+}
+
+static int read_htmlsrc_attrname_xform(char *str)
+{
+    int val;
+
+    if (1 == sscanf(str, "%d", &val)) {
+	if (val < 0 || val > 2) {
+	    CTRACE((tfp,
+		    "bad value for htmlsrc_attrname_xform (ignored - must be one of 0,1,2): %d\n",
+		    val));
+	} else
+	    attrname_transform = val;
+    } else {
+	CTRACE((tfp, "bad value for htmlsrc_attrname_xform (ignored): %s\n",
+		str));
+    }
+    return 0;
+}
+
+static int read_htmlsrc_tagname_xform(char *str)
+{
+    int val;
+
+    if (1 == sscanf(str, "%d", &val)) {
+	if (val < 0 || val > 2) {
+	    CTRACE((tfp,
+		    "bad value for htmlsrc_tagname_xform (ignored - must be one of 0,1,2): %d\n",
+		    val));
+	} else
+	    tagname_transform = val;
+    } else {
+	CTRACE((tfp, "bad value for htmlsrc_tagname_xform (ignored): %s\n",
+		str));
+    }
+    return 0;
+}
+#endif
+
+#ifdef USE_SESSIONS
+static int session_limit_fun(char *value)
+{
+    session_limit = (short) atoi(value);
+    if (session_limit < 1)
+	session_limit = 1;
+    else if (session_limit > MAX_SESSIONS)
+	session_limit = MAX_SESSIONS;
+    return 0;
+}
+#endif /* USE_SESSIONS */
+
+#if defined(PDCURSES) && defined(PDC_BUILD) && PDC_BUILD >= 2401
+static int screen_size_fun(char *value)
+{
+    char *cp;
+
+    if ((cp = strchr(value, ',')) != 0) {
+	*cp++ = '\0';		/* Terminate ID */
+	scrsize_x = atoi(value);
+	scrsize_y = atoi(cp);
+	if ((scrsize_x <= 1) || (scrsize_y <= 1)) {
+	    scrsize_x = scrsize_y = 0;
+	}
+	if ((scrsize_x > 0) && (scrsize_x < 80)) {
+	    scrsize_x = 80;
+	}
+	if ((scrsize_y > 0) && (scrsize_y < 4)) {
+	    scrsize_y = 4;
+	}
+	CTRACE((tfp, "scrsize: x=%d, y=%d\n", scrsize_x, scrsize_y));
+    }
+    return 0;
+}
+#endif
+
+/* This table is searched ignoring case */
+/* *INDENT-OFF* */
+static Config_Type Config_Table [] =
+{
+     PARSE_SET(RC_ACCEPT_ALL_COOKIES,   LYAcceptAllCookies),
+     PARSE_TIM(RC_ALERTSECS,            AlertSecs),
+     PARSE_SET(RC_ALWAYS_RESUBMIT_POSTS, LYresubmit_posts),
+#ifdef EXEC_LINKS
+     PARSE_DEF(RC_ALWAYS_TRUSTED_EXEC,  ALWAYS_EXEC_PATH),
+#endif
+     PARSE_FUN(RC_ASSUME_CHARSET,       assume_charset_fun),
+     PARSE_FUN(RC_ASSUME_LOCAL_CHARSET, assume_local_charset_fun),
+     PARSE_FUN(RC_ASSUME_UNREC_CHARSET, assume_unrec_charset_fun),
+#ifdef EXP_ASSUMED_COLOR
+     PARSE_FUN(RC_ASSUMED_COLOR,        assumed_color_fun),
+#endif
+#ifdef USE_CHARSET_CHOICE
+     PARSE_FUN(RC_ASSUMED_DOC_CHARSET_CHOICE, parse_assumed_doc_charset_choice),
+#endif
+#ifdef DIRED_SUPPORT
+     PARSE_INT(RC_AUTO_UNCACHE_DIRLISTS, LYAutoUncacheDirLists),
+#endif
+#ifndef DISABLE_BIBP
+     PARSE_STR(RC_BIBP_BIBHOST,         BibP_bibhost),
+     PARSE_STR(RC_BIBP_GLOBALSERVER,    BibP_globalserver),
+#endif
+     PARSE_SET(RC_BLOCK_MULTI_BOOKMARKS, LYMBMBlocked),
+     PARSE_SET(RC_BOLD_H1,              bold_H1),
+     PARSE_SET(RC_BOLD_HEADERS,         bold_headers),
+     PARSE_SET(RC_BOLD_NAME_ANCHORS,    bold_name_anchors),
+#ifndef DISABLE_FTP
+     PARSE_LST(RC_BROKEN_FTP_EPSV,      broken_ftp_epsv),
+     PARSE_LST(RC_BROKEN_FTP_RETR,      broken_ftp_retr),
+#endif
+     PARSE_PRG(RC_BZIP2_PATH,           ppBZIP2),
+     PARSE_SET(RC_CASE_SENSITIVE_ALWAYS_ON, case_sensitive),
+     PARSE_FUN(RC_CHARACTER_SET,        character_set_fun),
+#ifdef CAN_SWITCH_DISPLAY_CHARSET
+     PARSE_STR(RC_CHARSET_SWITCH_RULES, charset_switch_rules),
+     PARSE_STR(RC_CHARSETS_DIRECTORY,   charsets_directory),
+#endif
+     PARSE_SET(RC_CHECKMAIL,            check_mail),
+     PARSE_PRG(RC_CHMOD_PATH,           ppCHMOD),
+     PARSE_SET(RC_COLLAPSE_BR_TAGS,     LYCollapseBRs),
+#ifdef USE_COLOR_TABLE
+     PARSE_FUN(RC_COLOR,                color_fun),
+#endif
+#ifdef USE_COLOR_STYLE
+     PARSE_STR(RC_COLOR_STYLE,          lynx_lss_file),
+#endif
+     PARSE_PRG(RC_COMPRESS_PATH,        ppCOMPRESS),
+     PARSE_PRG(RC_COPY_PATH,            ppCOPY),
+     PARSE_INT(RC_CONNECT_TIMEOUT,      connect_timeout),
+     PARSE_STR(RC_COOKIE_ACCEPT_DOMAINS, LYCookieSAcceptDomains),
+#ifdef USE_PERSISTENT_COOKIES
+     PARSE_STR(RC_COOKIE_FILE,          LYCookieFile),
+#endif /* USE_PERSISTENT_COOKIES */
+     PARSE_STR(RC_COOKIE_LOOSE_INVALID_DOMAINS, LYCookieSLooseCheckDomains),
+     PARSE_STR(RC_COOKIE_QUERY_INVALID_DOMAINS, LYCookieSQueryCheckDomains),
+     PARSE_STR(RC_COOKIE_REJECT_DOMAINS, LYCookieSRejectDomains),
+#ifdef USE_PERSISTENT_COOKIES
+     PARSE_STR(RC_COOKIE_SAVE_FILE,     LYCookieSaveFile),
+#endif /* USE_PERSISTENT_COOKIES */
+     PARSE_STR(RC_COOKIE_STRICT_INVALID_DOMAIN, LYCookieSStrictCheckDomains),
+     PARSE_Env(RC_CSO_PROXY,            0),
+#ifdef VMS
+     PARSE_PRG(RC_CSWING_PATH,          ppCSWING),
+#endif
+     PARSE_TIM(RC_DELAYSECS,            DebugSecs),
+     PARSE_FUN(RC_DEFAULT_BOOKMARK_FILE, default_bookmark_file_fun),
+     PARSE_FUN(RC_DEFAULT_CACHE_SIZE,   default_cache_size_fun),
+#ifdef USE_DEFAULT_COLORS
+     PARSE_FUN(RC_DEFAULT_COLORS,       default_colors_fun),
+#endif
+     PARSE_FUN(RC_DEFAULT_EDITOR,       default_editor_fun),
+     PARSE_STR(RC_DEFAULT_INDEX_FILE,   indexfile),
+     PARSE_ENU(RC_DEFAULT_KEYPAD_MODE,  keypad_mode, tbl_keypad_mode),
+     PARSE_FUN(RC_DEFAULT_KEYPAD_MODE_NUMARO, numbers_as_arrows_fun),
+     PARSE_ENU(RC_DEFAULT_USER_MODE,    user_mode, tbl_user_mode),
+#if defined(VMS) && defined(VAXC) && !defined(__DECC)
+     PARSE_INT(RC_DEFAULT_VIRTUAL_MEMORY_SIZE, HTVirtualMemorySize),
+#endif
+#ifdef DIRED_SUPPORT
+     PARSE_FUN(RC_DIRED_MENU,           dired_menu_fun),
+#endif
+#ifdef USE_CHARSET_CHOICE
+     PARSE_FUN(RC_DISPLAY_CHARSET_CHOICE, parse_display_charset_choice),
+#endif
+     PARSE_ADD(RC_DOWNLOADER,           downloaders),
+     PARSE_SET(RC_EMACS_KEYS_ALWAYS_ON, emacs_keys),
+     PARSE_FUN(RC_ENABLE_LYNXRC,        enable_lynxrc),
+     PARSE_SET(RC_ENABLE_SCROLLBACK,    enable_scrollback),
+#ifdef USE_EXTERNALS
+     PARSE_ADD(RC_EXTERNAL,             externals),
+#endif
+     PARSE_Env(RC_FINGER_PROXY,         0),
+#if defined(_WINDOWS)	/* 1998/10/05 (Mon) 17:34:15 */
+     PARSE_SET(RC_FOCUS_WINDOW,         focus_window),
+#endif
+     PARSE_SET(RC_FORCE_8BIT_TOUPPER,   UCForce8bitTOUPPER),
+     PARSE_ENU(RC_FORCE_COOKIE_PROMPT,  cookie_noprompt, tbl_force_prompt),
+     PARSE_SET(RC_FORCE_EMPTY_HREFLESS_A, force_empty_hrefless_a),
+     PARSE_SET(RC_FORCE_SSL_COOKIES_SECURE, LYForceSSLCookiesSecure),
+#ifdef USE_SSL
+     PARSE_ENU(RC_FORCE_SSL_PROMPT,     ssl_noprompt, tbl_force_prompt),
+#endif
+#if !defined(NO_OPTION_FORMS) && !defined(NO_OPTION_MENU)
+     PARSE_SET(RC_FORMS_OPTIONS,        LYUseFormsOptions),
+#endif
+     PARSE_STR(RC_FTP_FORMAT,           ftp_format),
+#ifndef DISABLE_FTP
+     PARSE_SET(RC_FTP_PASSIVE,          ftp_passive),
+#endif
+     PARSE_Env(RC_FTP_PROXY,            0),
+     PARSE_STR(RC_GLOBAL_EXTENSION_MAP, global_extension_map),
+     PARSE_STR(RC_GLOBAL_MAILCAP,       global_type_map),
+     PARSE_Env(RC_GOPHER_PROXY,         0),
+     PARSE_SET(RC_GOTOBUFFER,           goto_buffer),
+     PARSE_PRG(RC_GZIP_PATH,            ppGZIP),
+     PARSE_STR(RC_HELPFILE,             helpfile),
+#ifdef MARK_HIDDEN_LINKS
+     PARSE_STR(RC_HIDDEN_LINK_MARKER,   hidden_link_marker),
+#endif
+     PARSE_SET(RC_HISTORICAL_COMMENTS,  historical_comments),
+#ifdef USE_PRETTYSRC
+     PARSE_FUN(RC_HTMLSRC_ATTRNAME_XFORM, read_htmlsrc_attrname_xform),
+     PARSE_FUN(RC_HTMLSRC_TAGNAME_XFORM, read_htmlsrc_tagname_xform),
+#endif
+     PARSE_Env(RC_HTTP_PROXY,           0),
+     PARSE_Env(RC_HTTPS_PROXY,          0),
+     PARSE_REQ(RC_INCLUDE,              0),
+     PARSE_PRG(RC_INFLATE_PATH,         ppINFLATE),
+     PARSE_TIM(RC_INFOSECS,             InfoSecs),
+     PARSE_PRG(RC_INSTALL_PATH,         ppINSTALL),
+     PARSE_STR(RC_JUMP_PROMPT,          jumpprompt),
+     PARSE_SET(RC_JUMPBUFFER,           jump_buffer),
+     PARSE_FUN(RC_JUMPFILE,             jumpfile_fun),
+#ifdef USE_JUSTIFY_ELTS
+     PARSE_SET(RC_JUSTIFY,              ok_justify),
+     PARSE_INT(RC_JUSTIFY_MAX_VOID_PERCENT, justify_max_void_percent),
+#endif
+#ifdef EXP_KEYBOARD_LAYOUT
+     PARSE_FUN(RC_KEYBOARD_LAYOUT,      keyboard_layout_fun),
+#endif
+     PARSE_FUN(RC_KEYMAP,               keymap_fun),
+     PARSE_SET(RC_LEFTARROW_IN_TEXTFLD_PROMPT, textfield_prompt_at_left_edge),
+#ifndef VMS
+     PARSE_STR(RC_LIST_FORMAT,          list_format),
+#endif
+#ifndef DISABLE_NEWS
+     PARSE_SET(RC_LIST_NEWS_DATES,      LYListNewsDates),
+     PARSE_SET(RC_LIST_NEWS_NUMBERS,    LYListNewsNumbers),
+#endif
+#ifdef USE_LOCALE_CHARSET
+     PARSE_SET(RC_LOCALE_CHARSET,       LYLocaleCharset),
+#endif
+     PARSE_STR(RC_LOCAL_DOMAIN,         LYLocalDomain),
+     PARSE_FUN(RC_LOCALHOST_ALIAS,      localhost_alias_fun),
+#if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)
+     PARSE_SET(RC_LOCAL_EXECUTION_LINKS_ALWAYS, local_exec),
+     PARSE_SET(RC_LOCAL_EXECUTION_LINKS_LOCAL, local_exec_on_local_files),
+#endif
+     PARSE_STR(RC_LYNX_HOST_NAME,       LYHostName),
+     PARSE_FUN(RC_LYNX_SIG_FILE,        lynx_sig_file_fun),
+#ifdef LYNXCGI_LINKS
+#ifndef VMS
+     PARSE_STR(RC_LYNXCGI_DOCUMENT_ROOT, LYCgiDocumentRoot),
+#endif
+     PARSE_FUN(RC_LYNXCGI_ENVIRONMENT,  lynxcgi_environment_fun),
+#endif
+#if USE_VMS_MAILER
+     PARSE_STR(RC_MAIL_ADRS,            mail_adrs),
+#endif
+     PARSE_SET(RC_MAIL_SYSTEM_ERROR_LOGGING, error_logging),
+     PARSE_SET(RC_MAKE_LINKS_FOR_ALL_IMAGES, clickable_images),
+     PARSE_SET(RC_MAKE_PSEUDO_ALTS_FOR_INLINES, pseudo_inline_alts),
+     PARSE_INT(RC_MAX_COOKIES_BUFFER,   max_cookies_buffer),
+     PARSE_INT(RC_MAX_COOKIES_DOMAIN,   max_cookies_domain),
+     PARSE_INT(RC_MAX_COOKIES_GLOBAL,   max_cookies_global),
+     PARSE_TIM(RC_MESSAGESECS,          MessageSecs),
+     PARSE_SET(RC_MINIMAL_COMMENTS,     minimal_comments),
+     PARSE_PRG(RC_MKDIR_PATH,           ppMKDIR),
+     PARSE_ENU(RC_MULTI_BOOKMARK_SUPPORT, LYMultiBookmarks, tbl_multi_bookmarks),
+     PARSE_PRG(RC_MV_PATH,              ppMV),
+     PARSE_SET(RC_NCR_IN_BOOKMARKS,     UCSaveBookmarksInUnicode),
+#ifdef EXP_NESTED_TABLES
+     PARSE_SET(RC_NESTED_TABLES,        nested_tables),
+#endif
+#ifndef DISABLE_NEWS
+     PARSE_FUN(RC_NEWS_CHUNK_SIZE,      news_chunk_size_fun),
+     PARSE_FUN(RC_NEWS_MAX_CHUNK,       news_max_chunk_fun),
+     PARSE_FUN(RC_NEWS_POSTING,         news_posting_fun),
+     PARSE_Env(RC_NEWS_PROXY,           0),
+     PARSE_Env(RC_NEWSPOST_PROXY,       0),
+     PARSE_Env(RC_NEWSREPLY_PROXY,      0),
+     PARSE_Env(RC_NNTP_PROXY,           0),
+     PARSE_ENV(RC_NNTPSERVER,           0), /* actually NNTPSERVER */
+#endif
+     PARSE_SET(RC_NUMBER_FIELDS_ON_LEFT,number_fields_on_left),
+     PARSE_SET(RC_NUMBER_LINKS_ON_LEFT, number_links_on_left),
+     PARSE_SET(RC_NO_DOT_FILES,         no_dotfiles),
+     PARSE_SET(RC_NO_FILE_REFERER,      no_filereferer),
+#ifndef VMS
+     PARSE_SET(RC_NO_FORCED_CORE_DUMP,  LYNoCore),
+#endif
+     PARSE_SET(RC_NO_FROM_HEADER,       LYNoFromHeader),
+     PARSE_SET(RC_NO_ISMAP_IF_USEMAP,   LYNoISMAPifUSEMAP),
+     PARSE_SET(RC_NO_MARGINS,           no_margins),
+     PARSE_SET(RC_NO_PAUSE,             no_pause),
+     PARSE_Env(RC_NO_PROXY,             0),
+     PARSE_SET(RC_NO_REFERER_HEADER,    LYNoRefererHeader),
+     PARSE_SET(RC_NO_TABLE_CENTER,      no_table_center),
+     PARSE_SET(RC_NO_TITLE,             no_title),
+     PARSE_FUN(RC_NONRESTARTING_SIGWINCH, nonrest_sigwinch_fun),
+     PARSE_FUN(RC_OUTGOING_MAIL_CHARSET, outgoing_mail_charset_fun),
+#ifdef DISP_PARTIAL
+     PARSE_SET(RC_PARTIAL,              display_partial_flag),
+     PARSE_INT(RC_PARTIAL_THRES,        partial_threshold),
+#endif
+#ifdef USE_PERSISTENT_COOKIES
+     PARSE_SET(RC_PERSISTENT_COOKIES,   persistent_cookies),
+#endif /* USE_PERSISTENT_COOKIES */
+     PARSE_STR(RC_PERSONAL_EXTENSION_MAP, personal_extension_map),
+     PARSE_STR(RC_PERSONAL_MAILCAP,     personal_type_map),
+     PARSE_LST(RC_POSITIONABLE_EDITOR,	positionable_editor),
+     PARSE_STR(RC_PREFERRED_CHARSET,    pref_charset),
+     PARSE_ENU(RC_PREFERRED_ENCODING,   LYAcceptEncoding, tbl_preferred_encoding),
+     PARSE_STR(RC_PREFERRED_LANGUAGE,   language),
+     PARSE_ENU(RC_PREFERRED_MEDIA_TYPES, LYAcceptMedia, tbl_preferred_media),
+     PARSE_SET(RC_PREPEND_BASE_TO_SOURCE, LYPrependBaseToSource),
+     PARSE_SET(RC_PREPEND_CHARSET_TO_SOURCE, LYPrependCharsetToSource),
+#ifdef USE_PRETTYSRC
+     PARSE_SET(RC_PRETTYSRC,            LYpsrc),
+     PARSE_FUN(RC_PRETTYSRC_SPEC,       psrcspec_fun),
+     PARSE_SET(RC_PRETTYSRC_VIEW_NO_ANCHOR_NUM, psrcview_no_anchor_numbering),
+#endif
+     PARSE_ADD(RC_PRINTER,              printers),
+     PARSE_SET(RC_QUIT_DEFAULT_YES,     LYQuitDefaultYes),
+     PARSE_INT(RC_READ_TIMEOUT,         reading_timeout),
+     PARSE_FUN(RC_REFERER_WITH_QUERY,   referer_with_query_fun),
+#ifdef USE_CMD_LOGGING
+     PARSE_TIM(RC_REPLAYSECS,           ReplaySecs),
+#endif
+     PARSE_SET(RC_REUSE_TEMPFILES,      LYReuseTempfiles),
+     PARSE_PRG(RC_RLOGIN_PATH,          ppRLOGIN),
+     PARSE_PRG(RC_RM_PATH,              ppRM),
+#ifndef NO_RULES
+     PARSE_FUN(RC_RULE,                 HTSetConfiguration),
+     PARSE_FUN(RC_RULESFILE,            cern_rulesfile_fun),
+#endif /* NO_RULES */
+     PARSE_STR(RC_SAVE_SPACE,           lynx_save_space),
+     PARSE_SET(RC_SCAN_FOR_BURIED_NEWS_REFS, scan_for_buried_news_references),
+#if defined(PDCURSES) && defined(PDC_BUILD) && PDC_BUILD >= 2401
+     PARSE_FUN(RC_SCREEN_SIZE,          screen_size_fun),
+#endif
+#ifdef USE_SCROLLBAR
+     PARSE_SET(RC_SCROLLBAR,            LYShowScrollbar),
+     PARSE_SET(RC_SCROLLBAR_ARROW,      LYsb_arrow),
+#endif
+     PARSE_SET(RC_SEEK_FRAG_AREA_IN_CUR, LYSeekFragAREAinCur),
+     PARSE_SET(RC_SEEK_FRAG_MAP_IN_CUR, LYSeekFragMAPinCur),
+#ifdef USE_SESSIONS
+     PARSE_SET(RC_AUTO_SESSION,         LYAutoSession),
+     PARSE_STR(RC_SESSION_FILE,         LYSessionFile),
+     PARSE_FUN(RC_SESSION_LIMIT,        session_limit_fun),
+#endif
+     PARSE_SET(RC_SET_COOKIES,          LYSetCookies),
+     PARSE_SET(RC_SHOW_CURSOR,          LYShowCursor),
+     PARSE_STR(RC_SHOW_KB_NAME,         LYTransferName),
+     PARSE_ENU(RC_SHOW_KB_RATE,         LYTransferRate, tbl_transfer_rate),
+     PARSE_Env(RC_SNEWS_PROXY,          0),
+     PARSE_Env(RC_SNEWSPOST_PROXY,      0),
+     PARSE_Env(RC_SNEWSREPLY_PROXY,     0),
+     PARSE_SET(RC_SOFT_DQUOTES,         soft_dquotes),
+#ifdef USE_SOURCE_CACHE
+     PARSE_ENU(RC_SOURCE_CACHE,         LYCacheSource, tbl_source_cache),
+     PARSE_ENU(RC_SOURCE_CACHE_FOR_ABORTED, LYCacheSourceForAborted, tbl_abort_source_cache),
+#endif
+     PARSE_STR(RC_SSL_CERT_FILE,        SSL_cert_file),
+     PARSE_STR(RC_STARTFILE,            startfile),
+     PARSE_FUN(RC_STATUS_BUFFER_SIZE,   status_buffer_size_fun),
+     PARSE_SET(RC_STRIP_DOTDOT_URLS,    LYStripDotDotURLs),
+     PARSE_SET(RC_SUBSTITUTE_UNDERSCORES, use_underscore),
+     PARSE_FUN(RC_SUFFIX,               suffix_fun),
+     PARSE_FUN(RC_SUFFIX_ORDER,         suffix_order_fun),
+#ifdef SYSLOG_REQUESTED_URLS
+     PARSE_SET(RC_SYSLOG_REQUESTED_URLS, syslog_requested_urls),
+     PARSE_STR(RC_SYSLOG_TEXT,          syslog_txt),
+#endif
+     PARSE_FUN(RC_SYSTEM_EDITOR,        system_editor_fun),
+     PARSE_STR(RC_SYSTEM_MAIL,          system_mail),
+     PARSE_STR(RC_SYSTEM_MAIL_FLAGS,    system_mail_flags),
+     PARSE_FUN(RC_TAGSOUP,              get_tagsoup),
+     PARSE_PRG(RC_TAR_PATH,             ppTAR),
+     PARSE_PRG(RC_TELNET_PATH,          ppTELNET),
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+     PARSE_SET(RC_TEXTFIELDS_NEED_ACTIVATION, textfields_activation_option),
+#endif
+     PARSE_PRG(RC_TN3270_PATH,          ppTN3270),
+#if defined(_WINDOWS)
+     PARSE_INT(RC_TIMEOUT,              lynx_timeout),
+#endif
+     PARSE_PRG(RC_TOUCH_PATH,           ppTOUCH),
+     PARSE_SET(RC_TRIM_INPUT_FIELDS,    LYtrimInputFields),
+#ifdef EXEC_LINKS
+     PARSE_DEF(RC_TRUSTED_EXEC,         EXEC_PATH),
+#endif
+#ifdef LYNXCGI_LINKS
+     PARSE_DEF(RC_TRUSTED_LYNXCGI,      CGI_PATH),
+#endif
+     PARSE_PRG(RC_UNCOMPRESS_PATH,      ppUNCOMPRESS),
+     PARSE_SET(RC_UNDERLINE_LINKS,      LYUnderlineLinks),
+     PARSE_PRG(RC_UNZIP_PATH,           ppUNZIP),
+#ifdef DIRED_SUPPORT
+     PARSE_ADD(RC_UPLOADER,             uploaders),
+#endif
+     PARSE_STR(RC_URL_DOMAIN_PREFIXES,  URLDomainPrefixes),
+     PARSE_STR(RC_URL_DOMAIN_SUFFIXES,  URLDomainSuffixes),
+#ifdef VMS
+     PARSE_SET(RC_USE_FIXED_RECORDS,    UseFixedRecords),
+#endif
+#if defined(USE_MOUSE)
+     PARSE_SET(RC_USE_MOUSE,            LYUseMouse),
+#endif
+     PARSE_SET(RC_USE_SELECT_POPUPS,    LYSelectPopups),
+     PARSE_PRG(RC_UUDECODE_PATH,        ppUUDECODE),
+     PARSE_SET(RC_VERBOSE_IMAGES,       verbose_img),
+     PARSE_SET(RC_VI_KEYS_ALWAYS_ON,    vi_keys),
+     PARSE_FUN(RC_VIEWER,               viewer_fun),
+     PARSE_Env(RC_WAIS_PROXY,           0),
+     PARSE_STR(RC_XLOADIMAGE_COMMAND,   XLoadImageCommand),
+     PARSE_SET(RC_XHTML_PARSING,        LYxhtml_parsing),
+     PARSE_PRG(RC_ZCAT_PATH,            ppZCAT),
+     PARSE_PRG(RC_ZIP_PATH,             ppZIP),
+
+     PARSE_NIL
+};
+/* *INDENT-ON* */
+
+static char *lynxcfginfo_url = NULL;	/* static */
+
+#if defined(HAVE_CONFIG_H) && !defined(NO_CONFIG_INFO)
+static char *configinfo_url = NULL;	/* static */
+#endif
+
+/*
+ * Free memory allocated in 'read_cfg()'
+ */
+void free_lynx_cfg(void)
+{
+    Config_Type *tbl;
+
+    for (tbl = Config_Table; tbl->name != 0; tbl++) {
+	ParseUnionPtr q = ParseUnionOf(tbl);
+
+	switch (tbl->type) {
+	case CONF_ENV:
+	    if (q->str_value != 0) {
+		char *name = *(q->str_value);
+		char *eqls = strchr(name, '=');
+
+		if (eqls != 0) {
+		    *eqls = 0;
+#ifdef VMS
+		    Define_VMSLogical(name, NULL);
+#else
+# ifdef HAVE_PUTENV
+		    if (putenv(name))
+			break;
+# else
+		    unsetenv(name);
+# endif
+#endif
+		}
+		FREE(*(q->str_value));
+		FREE(q->str_value);
+		/* is it enough for reload_read_cfg() to clean up
+		 * the result of putenv()?  No for certain platforms.
+		 */
+	    }
+	    break;
+	default:
+	    break;
+	}
+    }
+    free_all_item_lists();
+#ifdef DIRED_SUPPORT
+    reset_dired_menu();		/* frees and resets dired menu items - kw */
+#endif
+    FREE(lynxcfginfo_url);
+#if defined(HAVE_CONFIG_H) && !defined(NO_CONFIG_INFO)
+    FREE(configinfo_url);
+#endif
+}
+
+static Config_Type *lookup_config(const char *name)
+{
+    Config_Type *tbl = Config_Table;
+    char ch = (char) TOUPPER(*name);
+
+    while (tbl->name != 0) {
+	char ch1 = tbl->name[0];
+
+	if ((ch == TOUPPER(ch1))
+	    && (0 == strcasecomp(name, tbl->name)))
+	    break;
+
+	tbl++;
+    }
+    return tbl;
+}
+
+/*
+ * If the given value is an absolute path (by syntax), or we can read it, use
+ * the value as given.  Otherwise, assume it must be in the same place we read
+ * the parent configuration file from.
+ *
+ * Note:  only read files from the current directory if there's no parent
+ * filename, otherwise it leads to user surprise.
+ */
+static char *actual_filename(const char *cfg_filename,
+			     const char *parent_filename,
+			     const char *dft_filename)
+{
+    char *my_filename = NULL;
+
+    if (!LYisAbsPath(cfg_filename)
+	&& !(parent_filename == 0 && LYCanReadFile(cfg_filename))) {
+	if (LYIsTilde(cfg_filename[0]) && LYIsPathSep(cfg_filename[1])) {
+	    HTSprintf0(&my_filename, "%s%s", Home_Dir(), cfg_filename + 1);
+	} else {
+	    if (parent_filename != 0) {
+		StrAllocCopy(my_filename, parent_filename);
+		*LYPathLeaf(my_filename) = '\0';
+		StrAllocCat(my_filename, cfg_filename);
+	    }
+	    if (my_filename == 0 || !LYCanReadFile(my_filename)) {
+		StrAllocCopy(my_filename, dft_filename);
+		*LYPathLeaf(my_filename) = '\0';
+		StrAllocCat(my_filename, cfg_filename);
+		if (!LYCanReadFile(my_filename)) {
+		    StrAllocCopy(my_filename, cfg_filename);
+		}
+	    }
+	}
+    } else {
+	StrAllocCopy(my_filename, cfg_filename);
+    }
+    return my_filename;
+}
+
+FILE *LYOpenCFG(const char *cfg_filename,
+		const char *parent_filename,
+		const char *dft_filename)
+{
+    char *my_file = actual_filename(cfg_filename, parent_filename, dft_filename);
+    FILE *result;
+
+    CTRACE((tfp, "opening config file %s\n", my_file));
+    result = fopen(my_file, TXT_R);
+    FREE(my_file);
+
+    return result;
+}
+
+#define NOPTS_ ( TABLESIZE(Config_Table) - 1 )
+typedef BOOL (optidx_set_t)[NOPTS_];
+
+ /* if element is FALSE, then it's allowed in the current file */
+
+#define optidx_set_AND(r,a,b) \
+    {\
+	unsigned i1;\
+	for (i1 = 0; i1 < NOPTS_; ++i1) \
+	    (r)[i1]= (BOOLEAN) ((a)[i1] || (b)[i1]); \
+    }
+
+/*
+ * For simple (boolean, string, integer, time) values, set the corresponding
+ * configuration variable.
+ */
+void LYSetConfigValue(const char *name,
+		      char *value)
+{
+    Config_Type *tbl = lookup_config(name);
+    ParseUnionPtr q = ParseUnionOf(tbl);
+    char *temp_name = 0;
+    char *temp_value = 0;
+
+    switch (tbl->type) {
+    case CONF_BOOL:
+	if (q->set_value != 0)
+	    *(q->set_value) = is_true(value);
+	break;
+
+    case CONF_FUN:
+	if (q->fun_value != 0)
+	    (*(q->fun_value)) (value);
+	break;
+
+    case CONF_TIME:
+	if (q->int_value != 0) {
+	    float ival;
+
+	    if (1 == LYscanFloat(value, &ival)) {
+		*(q->int_value) = (int) SECS2Secs(ival);
+	    }
+	}
+	break;
+
+    case CONF_ENUM:
+	if (tbl->table != 0)
+	    LYgetEnum(tbl->table, value, q->int_value);
+	break;
+
+    case CONF_INT:
+	if (q->int_value != 0) {
+	    int ival;
+
+	    if (1 == sscanf(value, "%d", &ival))
+		*(q->int_value) = ival;
+	}
+	break;
+
+    case CONF_STR:
+	if (q->str_value != 0)
+	    StrAllocCopy(*(q->str_value), value);
+	break;
+
+    case CONF_ENV:
+    case CONF_ENV2:
+
+	if (StrAllocCopy(temp_name, name)) {
+	    if (tbl->type == CONF_ENV)
+		LYLowerCase(temp_name);
+	    else
+		LYUpperCase(temp_name);
+
+	    if (LYGetEnv(temp_name) == 0) {
+#ifdef VMS
+		Define_VMSLogical(temp_name, value);
+#else
+		if (q->str_value == 0)
+		    q->str_value = typecalloc(char *);
+
+		HTSprintf0(q->str_value, "%s=%s", temp_name, value);
+		putenv(*(q->str_value));
+#endif
+	    }
+	    FREE(temp_name);
+	}
+	break;
+    case CONF_ADD_ITEM:
+	if (q->add_value != 0)
+	    add_item_to_list(value, q->add_value, (q->add_value == &printers));
+	break;
+
+    case CONF_ADD_STRING:
+	if (*(q->lst_value) == NULL) {
+	    *(q->lst_value) = HTList_new();
+	}
+	if (q->lst_value != 0) {
+	    char *my_value = NULL;
+
+	    StrAllocCopy(my_value, value);
+	    HTList_appendObject(*(q->lst_value), my_value);
+	}
+	break;
+
+#if defined(EXEC_LINKS) || defined(LYNXCGI_LINKS)
+    case CONF_ADD_TRUSTED:
+	add_trusted(value, q->def_value);
+	break;
+#endif
+
+    case CONF_PRG:
+	if (StrAllocCopy(temp_value, value))
+	    HTSetProgramPath((ProgramPaths) (q->def_value), temp_value);
+	break;
+
+    default:
+	break;
+    }
+}
+
+/*
+ * Process the configuration file (lynx.cfg).
+ *
+ * 'allowed' is a pointer to HTList of allowed options.  Since the included
+ * file can also include other files with a list of acceptable options, these
+ * lists are ANDed.
+ */
+static void do_read_cfg(const char *cfg_filename,
+			const char *parent_filename,
+			int nesting_level,
+			FILE *fp0,
+			optidx_set_t * allowed)
+{
+    FILE *fp;
+    char *buffer = 0;
+
+    CTRACE((tfp, "Loading cfg file '%s'.\n", cfg_filename));
+
+    /*
+     * Don't get hung up by an include file loop.  Arbitrary max depth
+     * of 10.  - BL
+     */
+    if (nesting_level > 10) {
+	fprintf(stderr,
+		gettext("More than %d nested lynx.cfg includes -- perhaps there is a loop?!?\n"),
+		nesting_level - 1);
+	fprintf(stderr, gettext("Last attempted include was '%s',\n"), cfg_filename);
+	fprintf(stderr, gettext("included from '%s'.\n"), parent_filename);
+	exit_immediately(EXIT_FAILURE);
+    }
+    /*
+     * Locate and open the file.
+     */
+    if (!cfg_filename || strlen(cfg_filename) == 0) {
+	CTRACE((tfp, "No filename following -cfg switch!\n"));
+	return;
+    }
+    if ((fp = LYOpenCFG(cfg_filename, parent_filename, LYNX_CFG_FILE)) == 0) {
+	CTRACE((tfp, "lynx.cfg file not found as '%s'\n", cfg_filename));
+	return;
+    }
+    have_read_cfg = TRUE;
+
+    /*
+     * Process each line in the file.
+     */
+    if (show_cfg) {
+	time_t t;
+
+	time(&t);
+	printf("### %s %s, at %s", LYNX_NAME, LYNX_VERSION, ctime(&t));
+    }
+    while (LYSafeGets(&buffer, fp) != 0) {
+	char *name, *value;
+	char *cp;
+	Config_Type *tbl;
+
+	/* Most lines in the config file are comment lines.  Weed them out
+	 * now.  Also, leading whitespace is ok, so trim it.
+	 */
+	name = LYSkipBlanks(buffer);
+
+	if (ispunct(UCH(*name)))
+	    continue;
+
+	LYTrimTrailing(name);
+
+	if (*name == 0)
+	    continue;
+
+	/* Significant lines are of the form KEYWORD:WHATEVER */
+	if ((value = strchr(name, ':')) == 0) {
+	    /* fprintf (stderr, "Bad line-- no :\n"); */
+	    CTRACE((tfp, "LYReadCFG: missing ':' %s\n", name));
+	    continue;
+	}
+
+	/* skip past colon, but replace ':' with 0 to make name meaningful */
+	*value++ = 0;
+
+	/*
+	 * Trim off any trailing comments.
+	 *
+	 * (Apparently, the original code considers a trailing comment valid
+	 * only if preceded by a space character but is not followed by a
+	 * colon.  -- JED)
+	 */
+	if ((cp = strrchr(value, ':')) == 0)
+	    cp = value;
+	if ((cp = strchr(cp, '#')) != 0) {
+	    cp--;
+	    if (isspace(UCH(*cp)))
+		*cp = 0;
+	}
+
+	CTRACE2(TRACE_CFG, (tfp, "LYReadCFG %s:%s\n", name, value));
+	tbl = lookup_config(name);
+	if (tbl->name == 0) {
+	    /* lynx ignores unknown keywords */
+	    CTRACE((tfp, "LYReadCFG: ignored %s:%s\n", name, value));
+	    continue;
+	}
+	if (show_cfg)
+	    printf("%s:%s\n", name, value);
+
+	if (allowed && (*allowed)[tbl - Config_Table]) {
+	    if (fp0 == NULL)
+		fprintf(stderr, "%s is not allowed in the %s\n",
+			name, cfg_filename);
+	    /*FIXME: we can do something wiser if we are generating
+	       the html representation of lynx.cfg - say include this line
+	       in bold, or something... */
+
+	    continue;
+	}
+
+	(void) ParseUnionOf(tbl);
+	switch ((fp0 != 0 && tbl->type != CONF_INCLUDE)
+		? CONF_NIL
+		: tbl->type) {
+	case CONF_BOOL:
+	case CONF_FUN:
+	case CONF_TIME:
+	case CONF_ENUM:
+	case CONF_INT:
+	case CONF_STR:
+	case CONF_ENV:
+	case CONF_ENV2:
+	case CONF_PRG:
+	case CONF_ADD_ITEM:
+	case CONF_ADD_STRING:
+	case CONF_ADD_TRUSTED:
+	    LYSetConfigValue(name, value);
+	    break;
+
+	case CONF_INCLUDE:{
+		/* include another file */
+		optidx_set_t cur_set, anded_set;
+		optidx_set_t *resultant_set = NULL;
+		char *p1, *p2, savechar;
+		BOOL any_optname_found = FALSE;
+
+		char *url = NULL;
+		char *cp1 = NULL;
+		const char *sep = NULL;
+
+		if ((p1 = strstr(value, sep = " for ")) != 0
+#if defined(UNIX) && !defined(USE_DOS_DRIVES)
+		    || (p1 = strstr(value, sep = ":")) != 0
+#endif
+		    ) {
+		    *p1 = '\0';
+		    p1 += strlen(sep);
+		}
+#ifndef NO_CONFIG_INFO
+		if (fp0 != 0 && !no_lynxcfg_xinfo) {
+		    char *my_file = actual_filename(value, cfg_filename, LYNX_CFG_FILE);
+
+		    LYLocalFileToURL(&url, my_file);
+		    FREE(my_file);
+		    StrAllocCopy(cp1, value);
+		    if (strchr(value, '&') || strchr(value, '<')) {
+			LYEntify(&cp1, TRUE);
+		    }
+
+		    fprintf(fp0, "%s:<a href=\"%s\">%s</a>\n\n", name, url, cp1);
+		    fprintf(fp0, "    #&lt;begin  %s&gt;\n", cp1);
+		}
+#endif
+
+		if (p1) {
+		    while (*(p1 = LYSkipBlanks(p1)) != 0) {
+			Config_Type *tbl2;
+
+			p2 = LYSkipNonBlanks(p1);
+			savechar = *p2;
+			*p2 = 0;
+
+			tbl2 = lookup_config(p1);
+			if (tbl2->name == 0) {
+			    if (fp0 == NULL)
+				fprintf(stderr,
+					"unknown option name %s in %s\n",
+					p1, cfg_filename);
+			} else {
+			    unsigned i;
+
+			    if (!any_optname_found) {
+				any_optname_found = TRUE;
+				for (i = 0; i < NOPTS_; ++i)
+				    cur_set[i] = TRUE;
+			    }
+			    cur_set[tbl2 - Config_Table] = FALSE;
+			}
+			if (savechar && p2[1])
+			    p1 = p2 + 1;
+			else
+			    break;
+		    }
+		}
+		if (!allowed) {
+		    if (!any_optname_found)
+			resultant_set = NULL;
+		    else
+			resultant_set = &cur_set;
+		} else {
+		    if (!any_optname_found)
+			resultant_set = allowed;
+		    else {
+			optidx_set_AND(anded_set, *allowed, cur_set);
+			resultant_set = &anded_set;
+		    }
+		}
+
+#ifndef NO_CONFIG_INFO
+		/*
+		 * Now list the opts that are allowed in included file.  If all
+		 * opts are allowed, then emit nothing, else emit an effective set
+		 * of allowed options in <ul>.  Option names will be uppercased.
+		 * FIXME:  uppercasing option names can be considered redundant.
+		 */
+		if (fp0 != 0 && !no_lynxcfg_xinfo && resultant_set) {
+		    char *buf = NULL;
+		    unsigned i;
+
+		    fprintf(fp0, "     Options allowed in this file:\n");
+		    for (i = 0; i < NOPTS_; ++i) {
+			if ((*resultant_set)[i])
+			    continue;
+			StrAllocCopy(buf, Config_Table[i].name);
+			LYUpperCase(buf);
+			fprintf(fp0, "         * %s\n", buf);
+		    }
+		    FREE(buf);
+		}
+#endif
+		do_read_cfg(value, cfg_filename, nesting_level + 1, fp0, resultant_set);
+
+#ifndef NO_CONFIG_INFO
+		if (fp0 != 0 && !no_lynxcfg_xinfo) {
+		    fprintf(fp0, "    #&lt;end of %s&gt;\n\n", cp1);
+		    FREE(url);
+		    FREE(cp1);
+		}
+#endif
+	    }
+	    break;
+
+	default:
+	    if (fp0 != 0) {
+		if (strchr(value, '&') || strchr(value, '<')) {
+		    char *cp1 = NULL;
+
+		    StrAllocCopy(cp1, value);
+		    LYEntify(&cp1, TRUE);
+		    fprintf(fp0, "%s:%s\n", name, cp1);
+		    FREE(cp1);
+		} else {
+		    fprintf(fp0, "%s:%s\n", name, value);
+		}
+	    }
+	    break;
+	}
+    }
+
+    LYCloseInput(fp);
+
+    /*
+     * If any DOWNLOADER:  commands have always_enabled set (:TRUE), make
+     * override_no_download TRUE, so that other restriction settings will not
+     * block presentation of a download menu with those always_enabled options
+     * still available.  - FM
+     */
+    if (downloaders != 0) {
+	lynx_list_item_type *cur_download;
+
+	cur_download = downloaders;
+	while (cur_download != 0) {
+	    if (cur_download->always_enabled) {
+		override_no_download = TRUE;
+		break;
+	    }
+	    cur_download = cur_download->next;
+	}
+    }
+
+    /*
+     * If any COOKIE_{ACCEPT,REJECT}_DOMAINS have been defined,
+     * process them.  These are comma delimited lists of
+     * domains. - BJP
+     *
+     * And for query/strict/loose invalid cookie checking. - BJP
+     */
+    LYConfigCookies();
+}
+
+/* this is a public interface to do_read_cfg */
+void read_cfg(const char *cfg_filename,
+	      const char *parent_filename,
+	      int nesting_level,
+	      FILE *fp0)
+{
+    HTInitProgramPaths();
+    do_read_cfg(cfg_filename, parent_filename, nesting_level, fp0, NULL);
+}
+
+#ifndef NO_CONFIG_INFO
+static void extra_cfg_link(FILE *fp, const char *href,
+			   const char *name)
+{
+    fprintf(fp, "<a href=\"%s\">%s</a>",
+	    href, name);
+}
+#endif /* NO_CONFIG_INFO */
+
+/*
+ * Show rendered lynx.cfg data without comments, LYNXCFG:/ internal page. 
+ * Called from getfile() cycle:  we create and load the page just in place and
+ * return to mainloop().
+ */
+int lynx_cfg_infopage(DocInfo *newdoc)
+{
+    static char tempfile[LY_MAXPATH] = "\0";
+    DocAddress WWWDoc;		/* need on exit */
+    char *temp = 0;
+    char *cp1 = NULL;
+    FILE *fp0;
+
+#ifndef NO_CONFIG_INFO
+    /*-------------------------------------------------
+     * kludge a link from LYNXCFG:/, the URL was:
+     * "  <a href=\"LYNXCFG://reload\">RELOAD THE CHANGES</a>\n"
+     *--------------------------------------------------*/
+
+    if (!no_lynxcfg_xinfo && (strstr(newdoc->address, "LYNXCFG://reload"))) {
+	/*
+	 * Some stuff to reload read_cfg(), but also load options menu items
+	 * and command-line options to make things consistent.  Implemented in
+	 * LYMain.c
+	 */
+	reload_read_cfg();
+
+	/*
+	 * now pop-up and return to updated LYNXCFG:/ page, remind
+	 * postoptions() but much simpler:
+	 */
+	/*
+	 * But check whether the top history document is really the expected
+	 * LYNXCFG:  page.  - kw
+	 */
+	if (HTMainText && nhist > 0 &&
+	    !strcmp(HTLoadedDocumentTitle(), LYNXCFG_TITLE) &&
+	    !strcmp(HTLoadedDocumentURL(), HDOC(nhist - 1).address) &&
+	    LYIsUIPage(HDOC(nhist - 1).address, UIP_LYNXCFG) &&
+	    (!lynxcfginfo_url ||
+	     strcmp(HTLoadedDocumentURL(), lynxcfginfo_url))) {
+	    /*  the page was pushed, so pop-up. */
+	    LYpop(newdoc);
+	    WWWDoc.address = newdoc->address;
+	    WWWDoc.post_data = newdoc->post_data;
+	    WWWDoc.post_content_type = newdoc->post_content_type;
+	    WWWDoc.bookmark = newdoc->bookmark;
+	    WWWDoc.isHEAD = newdoc->isHEAD;
+	    WWWDoc.safe = newdoc->safe;
+	    LYforce_no_cache = FALSE;	/* ! */
+	    LYoverride_no_cache = TRUE;		/* ! */
+
+	    /*
+	     * Working out of getfile() cycle we reset *no_cache manually here
+	     * so HTLoadAbsolute() will return "Document already in memory": 
+	     * it was forced reloading obsolete file again without this
+	     * (overhead).
+	     *
+	     * Probably *no_cache was set in a wrong position because of the
+	     * internal page...
+	     */
+	    if (!HTLoadAbsolute(&WWWDoc))
+		return (NOT_FOUND);
+
+	    HTuncache_current_document();	/* will never use again */
+	    LYUnRegisterUIPage(UIP_LYNXCFG);
+	}
+
+	/*  now set up the flag and fall down to create a new LYNXCFG:/ page */
+	FREE(lynxcfginfo_url);	/* see below */
+    }
+#endif /* !NO_CONFIG_INFO */
+
+    /*
+     * We regenerate the file if reloading has been requested (with LYK_NOCACHE
+     * key).  If we did not regenerate, there would be no way to recover in a
+     * session from a situation where the file is corrupted (for example
+     * truncated because the file system was full when it was first created -
+     * lynx doesn't check for write errors below), short of manual complete
+     * removal or perhaps forcing regeneration with LYNXCFG://reload. 
+     * Similarly, there would be no simple way to get a different page if
+     * user_mode has changed to Advanced after the file was first generated in
+     * a non-Advanced mode (the difference being in whether the page includes
+     * the link to LYNXCFG://reload or not).
+     *
+     * We also try to regenerate the file if lynxcfginfo_url is set, indicating
+     * that tempfile is valid, but the file has disappeared anyway.  This can
+     * happen to a long-lived lynx process if for example some system script
+     * periodically cleans up old files in the temp file space.  - kw
+     */
+
+    if (LYforce_no_cache && reloading) {
+	FREE(lynxcfginfo_url);	/* flag to code below to regenerate - kw */
+    } else if (lynxcfginfo_url != NULL) {
+	if (!LYCanReadFile(tempfile)) {		/* check existence */
+	    FREE(lynxcfginfo_url);	/* flag to code below to try again - kw */
+	}
+    }
+    if (lynxcfginfo_url == 0) {
+
+	if ((fp0 = InternalPageFP(tempfile, TRUE)) == 0)
+	    return (NOT_FOUND);
+
+	LYLocalFileToURL(&lynxcfginfo_url, tempfile);
+
+	LYforce_no_cache = TRUE;	/* don't cache this doc */
+
+	BeginInternalPage(fp0, LYNXCFG_TITLE, NULL);
+	fprintf(fp0, "<pre>\n");
+
+#ifndef NO_CONFIG_INFO
+	if (!no_lynxcfg_xinfo) {
+#if defined(HAVE_CONFIG_H) || defined(VMS)
+	    if (strcmp(lynx_cfg_file, LYNX_CFG_FILE)) {
+		fprintf(fp0, "<em>%s\n%s",
+			gettext("The following is read from your lynx.cfg file."),
+			gettext("Please read the distribution"));
+		LYLocalFileToURL(&temp, LYNX_CFG_FILE);
+		fprintf(fp0, " <a href=\"%s\">lynx.cfg</a> ",
+			temp);
+		FREE(temp);
+		fprintf(fp0, "%s</em>\n\n",
+			gettext("for more comments."));
+	    } else
+#endif /* HAVE_CONFIG_H */
+	    {
+		/* no absolute path... for lynx.cfg on DOS/Win32 */
+		fprintf(fp0, "<em>%s\n%s",
+			gettext("The following is read from your lynx.cfg file."),
+			gettext("Please read the distribution"));
+		fprintf(fp0, " </em>lynx.cfg<em> ");
+		fprintf(fp0, "%s</em>\n",
+			gettext("for more comments."));
+	    }
+
+#ifndef NO_CONFIG_INFO
+#if defined(HAVE_CONFIG_H) && defined(USE_COLOR_STYLE)
+	    if (!no_compileopts_info && !no_lynxcfg_xinfo) {
+		fprintf(fp0, "%s</pre><ul><li>", SEE_ALSO);
+		extra_cfg_link(fp0, STR_LYNXCFLAGS, COMPILE_OPT_SEGMENT);
+
+		fprintf(fp0, "<li>");
+		LYLocalFileToURL(&temp, lynx_lss_file);
+		extra_cfg_link(fp0, temp, COLOR_STYLE_SEGMENT);
+		fprintf(fp0, "</ul><pre>\n");
+	    } else
+#endif
+	    {
+		fprintf(fp0, "%s ", SEE_ALSO);
+#if defined(HAVE_CONFIG_H)
+		if (!no_compileopts_info) {
+		    extra_cfg_link(fp0, STR_LYNXCFLAGS, COMPILE_OPT_SEGMENT);
+		}
+#endif
+#if defined(USE_COLOR_STYLE)
+		if (!no_lynxcfg_xinfo) {
+		    LYLocalFileToURL(&temp, lynx_lss_file);
+		    extra_cfg_link(fp0, temp, COLOR_STYLE_SEGMENT);
+		}
+#endif
+		fprintf(fp0, "\n\n");
+	    }
+#endif /* NO_CONFIG_INFO */
+
+	    /** a new experimental link ... **/
+	    if (user_mode == ADVANCED_MODE)
+		fprintf(fp0, "  <a href=\"%s//reload\">%s</a>\n",
+			STR_LYNXCFG,
+			gettext("RELOAD THE CHANGES"));
+
+	    LYLocalFileToURL(&temp, lynx_cfg_file);
+	    StrAllocCopy(cp1, lynx_cfg_file);
+	    if (strchr(lynx_cfg_file, '&') || strchr(lynx_cfg_file, '<')) {
+		LYEntify(&cp1, TRUE);
+	    }
+	    fprintf(fp0, "\n    #<em>%s <a href=\"%s\">%s</a></em>\n",
+		    gettext("Your primary configuration"),
+		    temp,
+		    cp1);
+	    FREE(temp);
+	    FREE(cp1);
+
+	} else
+#endif /* !NO_CONFIG_INFO */
+
+	    fprintf(fp0, "<em>%s</em>\n\n",
+		    gettext("The following is read from your lynx.cfg file."));
+
+	/*
+	 * Process the configuration file.
+	 */
+	read_cfg(lynx_cfg_file, "main program", 1, fp0);
+
+	fprintf(fp0, "</pre>\n");
+	EndInternalPage(fp0);
+	LYCloseTempFP(fp0);
+	LYRegisterUIPage(lynxcfginfo_url, UIP_LYNXCFG);
+    }
+
+    /* return to getfile() cycle */
+    StrAllocCopy(newdoc->address, lynxcfginfo_url);
+    WWWDoc.address = newdoc->address;
+    WWWDoc.post_data = newdoc->post_data;
+    WWWDoc.post_content_type = newdoc->post_content_type;
+    WWWDoc.bookmark = newdoc->bookmark;
+    WWWDoc.isHEAD = newdoc->isHEAD;
+    WWWDoc.safe = newdoc->safe;
+
+    if (!HTLoadAbsolute(&WWWDoc))
+	return (NOT_FOUND);
+#ifdef DIRED_SUPPORT
+    lynx_edit_mode = FALSE;
+#endif /* DIRED_SUPPORT */
+    return (NORMAL);
+}
+
+#if defined(HAVE_CONFIG_H) && !defined(NO_CONFIG_INFO)
+/*
+ * Compile-time definitions info, LYNXCOMPILEOPTS:/ internal page, from
+ * getfile() cycle.
+ */
+int lynx_compile_opts(DocInfo *newdoc)
+{
+    static char tempfile[LY_MAXPATH] = "\0";
+
+#define PutDefs(table, N) fprintf(fp0, "%-35s %s\n", table[N].name, table[N].value)
+#include <cfg_defs.h>
+    unsigned n;
+    DocAddress WWWDoc;		/* need on exit */
+    FILE *fp0;
+
+    /* In general, create the page only once - compile-time data will not
+     * change...  But we will regenerate the file anyway, in a few situations:
+     *
+     * (a) configinfo_url has been FREEd - this can happen if free_lynx_cfg()
+     * was called as part of a LYNXCFG://reload action.
+     *
+     * (b) reloading has been requested (with LYK_NOCACHE key).  If we did not
+     * regenerate, there would be no way to recover in a session from a
+     * situation where the file is corrupted (for example truncated because the
+     * file system was full when it was first created - lynx doesn't check for
+     * write errors below), short of manual complete removal or forcing
+     * regeneration with LYNXCFG://reload.
+     *
+     * (c) configinfo_url is set, indicating that tempfile is valid, but the
+     * file has disappeared anyway.  This can happen to a long-lived lynx
+     * process if for example some system script periodically cleans up old
+     * files in the temp file space.  - kw
+     */
+
+    if (LYforce_no_cache && reloading) {
+	FREE(configinfo_url);	/* flag to code below to regenerate - kw */
+    } else if (configinfo_url != NULL) {
+	if (!LYCanReadFile(tempfile)) {		/* check existence */
+	    FREE(configinfo_url);	/* flag to code below to try again - kw */
+	}
+    }
+    if (configinfo_url == NULL) {
+	if ((fp0 = InternalPageFP(tempfile, TRUE)) == 0)
+	    return (NOT_FOUND);
+
+	LYLocalFileToURL(&configinfo_url, tempfile);
+
+	BeginInternalPage(fp0, CONFIG_DEF_TITLE, NULL);
+	fprintf(fp0, "<pre>\n");
+
+	fprintf(fp0, "\n%s<br>\n<em>config.cache</em>\n", AUTOCONF_CONFIG_CACHE);
+	for (n = 0; n < TABLESIZE(config_cache); n++) {
+	    PutDefs(config_cache, n);
+	}
+	fprintf(fp0, "\n%s<br>\n<em>lynx_cfg.h</em>\n", AUTOCONF_LYNXCFG_H);
+	for (n = 0; n < TABLESIZE(config_defines); n++) {
+	    PutDefs(config_defines, n);
+	}
+	fprintf(fp0, "</pre>\n");
+	EndInternalPage(fp0);
+	LYCloseTempFP(fp0);
+	LYRegisterUIPage(configinfo_url, UIP_CONFIG_DEF);
+    }
+
+    /* exit to getfile() cycle */
+    StrAllocCopy(newdoc->address, configinfo_url);
+    WWWDoc.address = newdoc->address;
+    WWWDoc.post_data = newdoc->post_data;
+    WWWDoc.post_content_type = newdoc->post_content_type;
+    WWWDoc.bookmark = newdoc->bookmark;
+    WWWDoc.isHEAD = newdoc->isHEAD;
+    WWWDoc.safe = newdoc->safe;
+
+    if (!HTLoadAbsolute(&WWWDoc))
+	return (NOT_FOUND);
+#ifdef DIRED_SUPPORT
+    lynx_edit_mode = FALSE;
+#endif /* DIRED_SUPPORT */
+    return (NORMAL);
+}
+#endif /* !NO_CONFIG_INFO */
diff --git a/src/LYReadCFG.h b/src/LYReadCFG.h
new file mode 100644
index 00000000..8092a8a5
--- /dev/null
+++ b/src/LYReadCFG.h
@@ -0,0 +1,72 @@
+/*
+ * $LynxId: LYReadCFG.h,v 1.23 2009/11/27 12:52:34 tom Exp $
+ */
+#ifndef LYREADCFG_H
+#define LYREADCFG_H
+
+#ifndef LYSTRUCTS_H
+#include <LYStructs.h>
+#endif /* LYSTRUCTS_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#if defined(USE_COLOR_STYLE) || defined(USE_COLOR_TABLE)
+#define DEFAULT_COLOR -1
+#define NO_COLOR      -2
+#define ERR_COLOR     -3
+/* Note: the sense of colors that Lynx uses for defaults is the reverse of
+ * the standard for color-curses.
+ */
+#ifdef USE_DEFAULT_COLORS
+# ifdef USE_SLANG
+#  define DEFAULT_FG "default"
+#  define DEFAULT_BG "default"
+# else
+#  ifdef HAVE_USE_DEFAULT_COLORS
+#   define DEFAULT_FG DEFAULT_COLOR
+#   define DEFAULT_BG DEFAULT_COLOR
+#  else
+#   define DEFAULT_FG COLOR_BLACK
+#   define DEFAULT_BG COLOR_WHITE
+#  endif
+# endif
+#else
+# ifdef USE_SLANG
+#  define DEFAULT_FG "black"
+#  define DEFAULT_BG "white"
+# else
+#  define DEFAULT_FG COLOR_BLACK
+#  define DEFAULT_BG COLOR_WHITE
+# endif
+#endif				/* USE_DEFAULT_COLORS */
+    extern int default_fg;
+    extern int default_bg;
+    extern BOOL default_color_reset;
+
+    extern int check_color(const char *color, int the_default);
+    extern const char *lookup_color(int code);
+#endif
+
+    extern void read_cfg(const char *cfg_filename,
+			 const char *parent_filename,
+			 int nesting_level,
+			 FILE *fp0);
+    extern void free_lynx_cfg(void);
+    extern BOOLEAN have_read_cfg;
+
+    extern FILE *LYOpenCFG(const char *cfg_filename, const char
+			   *parent_filename, const char *dft_filename);
+    extern int lynx_cfg_infopage(DocInfo *newdoc);
+    extern int lynx_compile_opts(DocInfo *newdoc);
+    extern int match_item_by_name(lynx_list_item_type *ptr, char *name, BOOLEAN only_overriders);
+    extern lynx_list_item_type *find_item_by_number(lynx_list_item_type *
+						    list_ptr,
+						    char *number);
+    extern void reload_read_cfg(void);	/* implemented in LYMain.c */
+    extern void LYSetConfigValue(const char *name, char *value);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYREADCFG_H */
diff --git a/src/LYSearch.c b/src/LYSearch.c
new file mode 100644
index 00000000..421a40f0
--- /dev/null
+++ b/src/LYSearch.c
@@ -0,0 +1,376 @@
+/*
+ * $LynxId: LYSearch.c,v 1.23 2009/01/01 23:28:39 tom Exp $
+ */
+#include <HTUtils.h>
+#include <HTAlert.h>
+#include <LYUtils.h>
+#include <LYStrings.h>
+#include <LYSearch.h>
+#include <LYGlobalDefs.h>
+#include <GridText.h>
+
+#include <LYLeaks.h>
+
+static BOOL link_has_target(int cur,
+			    char *target)
+{
+    LinkInfo *a = &links[cur];
+    OptionType *option;
+    char *stars = NULL;
+    const char *cp;
+    int count;
+
+    /*
+     * Search the hightext strings, if present, taking the case_sensitive
+     * setting into account.
+     */
+    for (count = 0;; ++count) {
+	const char *text = LYGetHiliteStr(cur, count);
+
+	if (text == NULL)
+	    break;
+	if (LYno_attr_strstr(text, target))
+	    return TRUE;
+    }
+
+    /*
+     * Search the relevant form fields, taking the case_sensitive setting into
+     * account.  - FM
+     */
+    if ((a->l_form != NULL && a->l_form->value != NULL) &&
+	a->l_form->type != F_HIDDEN_TYPE) {
+	if (a->l_form->type == F_PASSWORD_TYPE) {
+	    /*
+	     * Check the actual, hidden password, and then the displayed
+	     * string.  - FM
+	     */
+	    if (LYno_attr_strstr(a->l_form->value, target)) {
+		return TRUE;
+	    }
+	    StrAllocCopy(stars, a->l_form->value);
+	    memset(stars, '*', strlen(stars));
+	    if (LYno_attr_strstr(stars, target)) {
+		FREE(stars);
+		return TRUE;
+	    }
+	    FREE(stars);
+	} else if (a->l_form->type == F_OPTION_LIST_TYPE) {
+	    /*
+	     * Search the option strings that are displayed when the popup is
+	     * invoked.  - FM
+	     */
+	    option = a->l_form->select_list;
+	    while (option != NULL) {
+		if (LYno_attr_strstr(option->name, target)) {
+		    return TRUE;
+		}
+		option = option->next;
+	    }
+	} else if (a->l_form->type == F_RADIO_TYPE) {
+	    /*
+	     * Search for checked or unchecked parens.  - FM
+	     */
+	    if (a->l_form->num_value) {
+		cp = checked_radio;
+	    } else {
+		cp = unchecked_radio;
+	    }
+	    if (LYno_attr_strstr(cp, target)) {
+		return TRUE;
+	    }
+	} else if (a->l_form->type == F_CHECKBOX_TYPE) {
+	    /*
+	     * Search for checked or unchecked square brackets.  - FM
+	     */
+	    if (a->l_form->num_value) {
+		cp = checked_box;
+	    } else {
+		cp = unchecked_box;
+	    }
+	    if (LYno_attr_strstr(cp, target)) {
+		return TRUE;
+	    }
+	} else {
+	    /*
+	     * Check the values intended for display.  May have been found
+	     * already via the hightext search, but make sure here that the
+	     * entire value is searched.  - FM
+	     */
+	    if (LYno_attr_strstr(a->l_form->value, target)) {
+		return TRUE;
+	    }
+	}
+    }
+    return FALSE;
+}
+
+/*
+ * Search for the target string inside of the links that are currently
+ * displayed on the screen beginning with the one after the currently selected
+ * one.  If found set cur to the new value and return TRUE.  If not found do
+ * not reset cur and return FALSE.
+ */
+
+static int check_next_target_in_links(int *cur,
+				      char *target)
+{
+    int i;
+
+    if (nlinks != 0) {
+	for (i = *cur + 1; i < nlinks; ++i) {
+	    if (link_has_target(i, target)) {
+		*cur = i;
+		return TRUE;
+	    }
+	}
+    }
+    return FALSE;
+}
+
+static int check_prev_target_in_links(int *cur,
+				      char *target)
+{
+    int i;
+
+    if (nlinks != 0) {
+	for (i = *cur - 1; i >= 0; --i) {
+	    if (link_has_target(i, target)) {
+		*cur = i;
+		return TRUE;
+	    }
+	}
+    }
+    return FALSE;
+}
+
+/*
+ * Textsearch checks the prev_target variable to see if it is empty.  If it is
+ * then it requests a new search string.  It then searches the current file for
+ * the next instance of the search string and finds the line number that the
+ * string is on
+ *
+ * This is the primary USER search engine and is case sensitive or case
+ * insensitive depending on the 'case_sensitive' global variable
+ */
+BOOL textsearch(DocInfo *cur_doc, char *prev_target,
+		int target_size,
+		int direction)
+{
+    int offset;
+    int oldcur = cur_doc->link;
+    static char prev_target_buffer[512];	/* Search string buffer */
+    static BOOL first = TRUE;
+    char *cp;
+    int ch = 0;
+    RecallType recall;
+    int QueryTotal;
+    int QueryNum;
+    BOOLEAN FirstRecall = TRUE;
+
+    /*
+     * Initialize the search string buffer.  - FM
+     */
+    if (first) {
+	*prev_target_buffer = '\0';
+	first = FALSE;
+    }
+
+    QueryTotal = (search_queries ? HTList_count(search_queries) : 0);
+    recall = ((QueryTotal >= 1) ? RECALL_URL : NORECALL);
+    QueryNum = QueryTotal;
+
+    if (direction != 0)
+	/*
+	 * LYK_NEXT or LYK_PREV was pressed, so copy the buffer into
+	 * prev_target.
+	 */
+	LYstrncpy(prev_target, prev_target_buffer, target_size);
+
+    if (strlen(prev_target) == 0) {
+	/*
+	 * This is a new WHEREIS search ('/'), or LYK_NEXT was pressed but
+	 * there was no previous search, so we need to get a search string from
+	 * the user.  - FM
+	 */
+	_statusline(ENTER_WHEREIS_QUERY);
+
+	ch = LYgetstr(prev_target, VISIBLE, (unsigned) target_size, recall);
+	if (ch < 0) {
+	    /*
+	     * User cancelled the search via ^G.  Restore prev_target and
+	     * return.  - FM
+	     */
+	    LYstrncpy(prev_target, prev_target_buffer, target_size);
+	    HTInfoMsg(CANCELLED);
+	    return (FALSE);
+	}
+    }
+
+  check_recall:
+    if (strlen(prev_target) == 0 &&
+	!(recall && (ch == UPARROW || ch == DNARROW))) {
+	/*
+	 * No entry.  Simply return, retaining the current buffer.  Because
+	 * prev_target is now reset, highlighting of the previous search string
+	 * will no longer occur, but it can be used again via LYK_NEXT or
+	 * LYK_PREV.
+	 */
+	HTInfoMsg(CANCELLED);
+	return (FALSE);
+    }
+
+    if (recall && ch == UPARROW) {
+	if (FirstRecall) {
+	    /*
+	     * Use the current string or last query in the list.  - FM
+	     */
+	    FirstRecall = FALSE;
+	    if (*prev_target_buffer) {
+		for (QueryNum = (QueryTotal - 1); QueryNum > 0; QueryNum--) {
+		    if ((cp = (char *) HTList_objectAt(search_queries,
+						       QueryNum)) != NULL &&
+			!strcmp(prev_target_buffer, cp)) {
+			break;
+		    }
+		}
+	    } else {
+		QueryNum = 0;
+	    }
+	} 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) {
+	    LYstrncpy(prev_target, cp, target_size);
+	    if (*prev_target_buffer &&
+		!strcmp(prev_target_buffer, prev_target)) {
+		_statusline(EDIT_CURRENT_QUERY);
+	    } else if ((*prev_target_buffer && QueryTotal == 2) ||
+		       (!(*prev_target_buffer) && QueryTotal == 1)) {
+		_statusline(EDIT_THE_PREV_QUERY);
+	    } else {
+		_statusline(EDIT_A_PREV_QUERY);
+	    }
+	    ch = LYgetstr(prev_target, VISIBLE, (unsigned) target_size, recall);
+	    if (ch < 0) {
+		/*
+		 * User canceled the search via ^G.  Restore prev_target and
+		 * return.  - FM
+		 */
+		LYstrncpy(prev_target, prev_target_buffer, target_size);
+		HTInfoMsg(CANCELLED);
+		return (FALSE);
+	    }
+	    goto check_recall;
+	}
+    } else if (recall && ch == DNARROW) {
+	if (FirstRecall) {
+	    /*
+	     * Use the current string or first query in the list.  - FM
+	     */
+	    FirstRecall = FALSE;
+	    if (*prev_target_buffer) {
+		for (QueryNum = 0; QueryNum < (QueryTotal - 1); QueryNum++) {
+		    if ((cp = (char *) HTList_objectAt(search_queries,
+						       QueryNum)) != NULL &&
+			!strcmp(prev_target_buffer, cp)) {
+			break;
+		    }
+		}
+	    } else {
+		QueryNum = QueryTotal - 1;
+	    }
+	} 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) {
+	    LYstrncpy(prev_target, cp, target_size);
+	    if (*prev_target_buffer &&
+		!strcmp(prev_target_buffer, prev_target)) {
+		_statusline(EDIT_CURRENT_QUERY);
+	    } else if ((*prev_target_buffer && QueryTotal == 2) ||
+		       (!(*prev_target_buffer) && QueryTotal == 1)) {
+		_statusline(EDIT_THE_PREV_QUERY);
+	    } else {
+		_statusline(EDIT_A_PREV_QUERY);
+	    }
+	    ch = LYgetstr(prev_target, VISIBLE, (unsigned) target_size, recall);
+	    if (ch < 0) {
+		/*
+		 * User cancelled the search via ^G.  Restore prev_target and
+		 * return.  - FM
+		 */
+		LYstrncpy(prev_target, prev_target_buffer, target_size);
+		HTInfoMsg(CANCELLED);
+		return (FALSE);
+	    }
+	    goto check_recall;
+	}
+    }
+    /*
+     * Replace the search string buffer with the new target.  - FM
+     */
+    LYstrncpy(prev_target_buffer, prev_target, sizeof(prev_target_buffer) - 1);
+    HTAddSearchQuery(prev_target_buffer);
+
+    if (direction < 0) {
+	offset = 0;
+	if (check_prev_target_in_links(&cur_doc->link, prev_target)) {
+	    /*
+	     * Found in link, changed cur, we're done.
+	     */
+	    LYhighlight(OFF, oldcur, prev_target);
+	    return (TRUE);
+	}
+    } else {
+
+	/*
+	 * Search the links on the currently displayed page for the string,
+	 * starting after the current link.  - FM
+	 */
+	if (check_next_target_in_links(&cur_doc->link, prev_target)) {
+	    /*
+	     * Found in link, changed cur, we're done.
+	     */
+	    LYhighlight(OFF, oldcur, prev_target);
+	    return (TRUE);
+	}
+
+	/*
+	 * We'll search the text starting from the link we are on, or the next
+	 * page.
+	 */
+	if (nlinks == 0)
+	    offset = (display_lines - 1);
+	else
+	    offset = links[cur_doc->link].ly - 1;
+    }
+
+    /*
+     * Resume search, this time for all text.  Set www_search_result if string
+     * found, and position the hit near top of screen.
+     */
+    www_user_search((cur_doc->line + offset), cur_doc, prev_target, direction);
+    if (cur_doc->link != oldcur) {
+	LYhighlight(OFF, oldcur, prev_target);
+	return (TRUE);
+    }
+    return (BOOL) (www_search_result > 0);
+}
diff --git a/src/LYSearch.h b/src/LYSearch.h
new file mode 100644
index 00000000..f9763301
--- /dev/null
+++ b/src/LYSearch.h
@@ -0,0 +1,26 @@
+#ifndef LYSEARCH_H
+#define LYSEARCH_H
+
+#ifndef LYSTRUCTS_H
+#include <LYStructs.h>
+#endif /* LYSTRUCT_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    extern BOOL textsearch(DocInfo *cur_doc,
+			   char *prev_target,
+			   int target_size,
+			   int direction);
+
+#define IN_FILE 1
+#define IN_LINKS 2
+
+#ifndef NOT_FOUND
+#define NOT_FOUND 0
+#endif				/* NOT_FOUND */
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYSEARCH_H */
diff --git a/src/LYSession.c b/src/LYSession.c
new file mode 100644
index 00000000..3578c194
--- /dev/null
+++ b/src/LYSession.c
@@ -0,0 +1,265 @@
+/* $LynxId: LYSession.c,v 1.6 2008/07/02 21:24:27 Paul.B.Mahol Exp $ */
+
+#include <LYSession.h>
+
+#include <LYUtils.h>
+#include <LYStrings.h>
+#include <LYHistory.h>
+#include <LYGlobalDefs.h>
+#include <LYMainLoop.h>
+#include <GridText.h>
+
+#ifdef USE_SESSIONS
+
+/* Example of how a session file may look:
+ */
+
+/* # lynx session
+ * / files
+ * / hereby
+ * / reduce
+ * g file://localhost/COPYRIGHT
+ * g http://lynx.isc.org
+ * h 1 -1 file://localhost/COPYRIGHT       Entry into main screen
+ * h 1 0 LYNXCACHE:/       Cache Jar
+ * h 1 16 file://localhost/usr/local/share/lynx_help/Lynx_users_guide.html#Cache   Lynx Users Guide v2.8.6
+ * h 1 -1 file://localhost/COPYRIGHT       Entry into main screen
+ * h 1 2 file://localhost/tmp/lynxmSefvcbXes/L12110-6407TMP.html#current   Visited Links Page
+ * h 1 -1 file://localhost/COPYRIGHT       Entry into main screen
+ * h 1 -1 LYNXMESSAGES:/   Your recent statusline messages
+ * V 0 file://localhost/COPYRIGHT  Entry into main screen
+ * V 3 file://localhost/usr/local/share/lynx_help/Lynx_users_guide.html#Bookmarks  Lynx Users Guide v2.8.6
+ */
+
+static char *get_filename(char *given_name)
+{
+    char *actual_filename = given_name;
+
+    /*
+     * If the specific "-sessionin" or "-sessionout" value is not given,
+     * try the "-session" value (if the AUTO_SESSION configuration is set).
+     * Finally try the SESSION_FILE configuration value.
+     */
+    if (isEmpty(actual_filename)) {
+	actual_filename = session_file;
+	if (isEmpty(actual_filename)) {
+	    if (LYAutoSession) {
+		actual_filename = LYSessionFile;
+	    }
+	}
+    }
+
+    return actual_filename;
+}
+
+/* Restore session from file, pretty slow, but it should be fine
+ * for everyday, normal use.
+ */
+void RestoreSession(void)
+{
+    char *my_filename = get_filename(sessionin_file);
+    FILE *fp;
+    char *buffer = 0;
+    DocInfo doc;
+    VisitedLink *vl;
+    int i = 0;
+    short errors = 10;		/* how many syntax errors are allowed in */
+
+    /* session file before aborting. */
+    char *value1, *value2, *rsline, *linktext, *rslevel;
+
+    /*
+     * This should be done only once, here:  iff USE_SESSIONS is defined or: 
+     * in mainloop(), otherwise history entries are lost
+     */
+    nhist = 0;
+
+    if (my_filename == NULL) {
+	/* nothing to do, so exit */
+	return;
+    }
+
+    CTRACE((tfp, "RestoreSession %s\n", my_filename));
+    SetDefaultMode(O_TEXT);
+    if ((fp = fopen(my_filename, TXT_R)) != NULL) {
+
+	/*
+	 * This should be safe, entries are added to lynx until memory is
+	 * exhausted
+	 */
+	while (LYSafeGets(&buffer, fp) != 0) {
+	    LYTrimNewline(buffer);
+	    if (*buffer == '/') {
+#ifdef SEARCH_OUT_SESSION
+		if ((value1 = strchr(buffer, ' ')) == 0) {
+		    continue;
+		} else {
+		    value1++;
+		    HTAddSearchQuery(value1);
+		}
+#endif /* SEARCH_OUT_SESSION */
+	    } else if (*buffer == 'g') {
+#ifdef GOTOURL_OUT_SESSION
+		if ((value1 = strchr(buffer, ' ')) == 0)
+		    continue;
+		else {
+		    value1++;
+		    HTAddGotoURL(value1);
+		}
+#endif /* GOTOURL_OUT_SESSION */
+	    } else if (*buffer == 'h') {
+#ifdef HISTORY_OUT_SESSION
+		if ((rsline = strchr(buffer, ' ')) == 0)
+		    continue;
+		else {
+		    rsline++;
+		    if ((linktext = strchr(rsline, ' ')) == 0)
+			continue;
+		    else
+			*linktext++ = 0;
+		    if ((value1 = strchr(linktext, ' ')) == 0)
+			continue;
+		    else
+			*value1++ = 0;
+		    if ((value2 = strchr(value1, '\t')) != 0) {
+			*value2++ = 0;
+			doc.line = atoi(rsline);
+			doc.link = atoi(linktext);
+			StrAllocCopy(doc.address, value1);
+			StrAllocCopy(doc.title, value2);
+			LYpush(&doc, TRUE);
+		    }
+		}
+#endif /* HISTORY_OUT_SESSION */
+	    } else if (*buffer == 'V') {
+#ifdef VLINK_OUT_SESSION
+		if ((rslevel = strchr(buffer, ' ')) == 0)
+		    continue;
+		else {
+		    rslevel++;
+		    if ((value1 = strchr(rslevel, ' ')) == 0)
+			continue;
+		    else
+			*value1++ = 0;
+		    if ((value2 = strchr(value1, '\t')) != 0) {
+			*value2++ = 0;
+			StrAllocCopy(doc.address, value1);
+			StrAllocCopy(doc.title, value2);
+			LYAddVisitedLink(&doc);
+			vl = (VisitedLink *)
+			    HTList_objectAt(Visited_Links, i);
+			if (vl != NULL) {
+			    vl->level = atoi(rslevel);
+			    i++;
+			}
+		    }
+		}
+#endif /* VLINK_OUT_SESSION */
+	    } else if (*buffer == '#') {
+		/* This is comment; ignore it */
+		continue;
+	    } else if (errors-- < 0) {
+		FREE(buffer);
+		break;
+	    } else
+		continue;
+	}
+
+	LYCloseOutput(fp);
+    }
+    SetDefaultMode(O_BINARY);
+}
+
+/*
+ * Save session to file, overwriting one.
+ */
+void SaveSession(void)
+{
+    char *my_filename = get_filename(sessionout_file);
+    FILE *fp;
+    VisitedLink *vl;
+    int i, j, k;
+
+    if (my_filename == NULL) {
+	/* nothing to do, so exit */
+	return;
+    }
+
+    CTRACE((tfp, "SaveSession %s\n", my_filename));
+
+    SetDefaultMode(O_TEXT);
+    if ((fp = fopen(my_filename, TXT_W)) != NULL) {
+
+	fprintf(fp, "# lynx session\n");	/* @@@ simple for now */
+
+	/* Note use of session_limit, the most recent entries in list,
+	 * from the end of list, are saved.
+	 */
+
+#ifdef SEARCH_IN_SESSION
+	k = HTList_count(search_queries);
+	if (k > session_limit)
+	    j = k - session_limit;
+	else
+	    j = 0;
+	for (i = j; i < k; i++) {
+	    fprintf(fp, "/ ");
+	    fputs((char *) HTList_objectAt(search_queries, i), fp);
+	    fprintf(fp, "\n");
+	}
+#endif /* SEARCH_IN_SESSION */
+
+#ifdef GOTOURL_IN_SESSION
+	k = HTList_count(Goto_URLs);
+	if (k > session_limit)
+	    j = k - session_limit;
+	else
+	    j = 0;
+	for (i = j; i < k; i++) {
+	    fprintf(fp, "g ");
+	    fputs((char *) HTList_objectAt(Goto_URLs, i), fp);
+	    fprintf(fp, "\n");
+	}
+#endif /* GOTOURL_IN_SESSION */
+
+#ifdef HISTORY_IN_SESSION
+	k = nhist + nhist_extra;
+	if (k > session_limit)
+	    j = k - session_limit;
+	else
+	    j = 0;
+
+	for (i = j; i < k; i++) {
+	    fprintf(fp, "h %d %d ", HDOC(i).line, HDOC(i).link);
+	    fputs(HDOC(i).address, fp);
+	    fprintf(fp, "\t");
+	    fputs(HDOC(i).title, fp);
+	    fprintf(fp, "\n");
+	}
+#endif /* HISTORY_IN_SESSION */
+
+#ifdef VLINK_IN_SESSION
+	k = HTList_count(Visited_Links);
+	if (k > session_limit)
+	    j = k - session_limit;
+	else
+	    j = 0;
+
+	for (i = j; i < k; i++) {
+	    vl = (VisitedLink *) HTList_objectAt(Visited_Links, i);
+	    if (vl != NULL) {
+		fprintf(fp, "V %d ", vl->level);
+		fputs(vl->address, fp);
+		fprintf(fp, "\t");
+		fputs(vl->title, fp);
+		fprintf(fp, "\n");
+	    }
+	}
+#endif /* VLINK_IN_SESSION */
+
+	LYCloseOutput(fp);
+    }
+    SetDefaultMode(O_BINARY);
+}
+
+#endif /* USE_SESSIONS */
diff --git a/src/LYSession.h b/src/LYSession.h
new file mode 100644
index 00000000..90d74fca
--- /dev/null
+++ b/src/LYSession.h
@@ -0,0 +1,16 @@
+/* $LynxId: LYSession.h,v 1.3 2008/02/10 23:47:39 Paul.B.Mahol Exp $ */
+#ifndef LYSESSION_H
+#define LYSESSION_H
+
+#include <HTUtils.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    extern void RestoreSession(void);
+    extern void SaveSession(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYSESSION_H */
diff --git a/src/LYShowInfo.c b/src/LYShowInfo.c
new file mode 100644
index 00000000..6e6f916d
--- /dev/null
+++ b/src/LYShowInfo.c
@@ -0,0 +1,481 @@
+/* $LynxId: LYShowInfo.c,v 1.69 2009/01/19 23:42:23 tom Exp $ */
+#include <HTUtils.h>
+#include <HTFile.h>
+#include <HTParse.h>
+#include <HTAlert.h>
+#include <HTTP.h>
+#include <LYCurses.h>
+#include <LYUtils.h>
+#include <LYStructs.h>
+#include <LYGlobalDefs.h>
+#include <LYShowInfo.h>
+#include <LYCharUtils.h>
+#include <GridText.h>
+#include <LYReadCFG.h>
+#include <LYCharSets.h>
+#include <LYStrings.h>
+
+#include <LYLeaks.h>
+
+#ifdef DIRED_SUPPORT
+#include <HTAAProt.h>
+#include <time.h>
+#include <LYLocal.h>
+#endif /* DIRED_SUPPORT */
+
+#define ADVANCED_INFO 1		/* to get more info in advanced mode */
+
+#define BEGIN_DL(text) fprintf(fp0, "<h2>%s</h2>\n<dl compact>", LYEntifyTitle(&buffer, text))
+#define END_DL()       fprintf(fp0, "\n</dl>\n")
+
+#define ADD_SS(label,value)       dt_String(fp0, label, value)
+#define ADD_NN(label,value,units) dt_Number(fp0, label, value, units)
+
+static int label_columns;
+
+/*
+ * LYNX_VERSION and LYNX_DATE are automatically generated by PRCS, the tool
+ * which we use to archive versions of Lynx.  We use a convention for naming
+ * the successive versions:
+ *	{release}{status}{patch}
+ * where
+ *	{release} is the release that we are working on, e.g., 2.8.4
+ *	{status} is one of "dev", "pre" or "rel", and
+ *	{patch} is a number assigned by PRCS.
+ */
+BOOL LYVersionIsRelease(void)
+{
+    return (BOOL) (strstr(LYNX_VERSION, "rel") != 0);
+}
+
+const char *LYVersionStatus(void)
+{
+    if (LYVersionIsRelease())
+	return REL_VERSION;
+    else if (strstr(LYNX_VERSION, "pre") != 0)
+	return PRE_VERSION;
+    return DEV_VERSION;
+}
+
+const char *LYVersionDate(void)
+{
+    static char temp[LYNX_DATE_LEN + 1];
+
+    LYstrncpy(temp, &LYNX_DATE[LYNX_DATE_OFF], LYNX_DATE_LEN);
+    return temp;
+}
+
+static void dt_String(FILE *fp,
+		      const char *label,
+		      const char *value)
+{
+    int have;
+    int need;
+    char *the_label = 0;
+    char *the_value = 0;
+
+    StrAllocCopy(the_label, label);
+    StrAllocCopy(the_value, value);
+
+    have = (int) strlen(the_label);
+    need = LYstrExtent(the_label, have, label_columns);
+
+    LYEntify(&the_label, TRUE);
+    LYEntify(&the_value, TRUE);
+
+    fprintf(fp, "<dt>");
+    while (need++ < label_columns)
+	fprintf(fp, "&nbsp;");
+    fprintf(fp, "<em>%s</em> %s\n", the_label, the_value);
+
+    FREE(the_label);
+    FREE(the_value);
+}
+
+static void dt_Number(FILE *fp0,
+		      const char *label,
+		      long number,
+		      const char *units)
+{
+    char *value = NULL;
+    char *buffer = NULL;
+
+    HTSprintf(&value, "%ld %s", number, LYEntifyTitle(&buffer, units));
+    ADD_SS(label, value);
+    FREE(value);
+    FREE(buffer);
+}
+
+/*
+ * LYShowInfo prints a page of info about the current file and the link that
+ * the cursor is on.
+ */
+int LYShowInfo(DocInfo *doc,
+	       DocInfo *newdoc,
+	       char *owner_address)
+{
+    static char tempfile[LY_MAXPATH] = "\0";
+
+    int url_type;
+    FILE *fp0;
+    char *Title = NULL;
+    const char *cp;
+    char *temp = 0;
+    char *buffer = 0;
+
+#ifdef ADVANCED_INFO
+    BOOLEAN LYInfoAdvanced = (BOOL) (user_mode == ADVANCED_MODE);
+#endif
+#ifdef DIRED_SUPPORT
+    struct stat dir_info;
+    const char *name;
+#endif /* DIRED_SUPPORT */
+
+    if (LYReuseTempfiles) {
+	fp0 = LYOpenTempRewrite(tempfile, HTML_SUFFIX, "w");
+    } else {
+	LYRemoveTemp(tempfile);
+	fp0 = LYOpenTemp(tempfile, HTML_SUFFIX, "w");
+    }
+    if (fp0 == NULL) {
+	HTAlert(CANNOT_OPEN_TEMP);
+	return (-1);
+    }
+
+    /*
+     * Point the address pointer at this Url
+     */
+    LYLocalFileToURL(&newdoc->address, tempfile);
+
+    if (nlinks > 0 && links[doc->link].lname != NULL &&
+	(url_type = is_url(links[doc->link].lname)) != 0 &&
+	(url_type == LYNXEXEC_URL_TYPE ||
+	 url_type == LYNXPROG_URL_TYPE)) {
+	char *last_slash = strrchr(links[doc->link].lname, '/');
+	int next_to_last = (int) strlen(links[doc->link].lname) - 1;
+
+	if ((last_slash - links[doc->link].lname) == next_to_last) {
+	    links[doc->link].lname[next_to_last] = '\0';
+	}
+    }
+
+    label_columns = 9;
+
+    WriteInternalTitle(fp0, SHOWINFO_TITLE);
+
+    fprintf(fp0, "<h1>%s %s (%s) (<a href=\"%s\">%s</a>)",
+	    LYNX_NAME, LYNX_VERSION,
+	    LYVersionDate(),
+	    (LYVersionIsRelease()? LYNX_WWW_HOME : LYNX_WWW_DIST),
+	    LYVersionStatus());
+
+    fprintf(fp0, "</h1>\n");	/* don't forget to close <h1> */
+
+#ifdef DIRED_SUPPORT
+    if (lynx_edit_mode && nlinks > 0) {
+
+	BEGIN_DL(gettext("Directory that you are currently viewing"));
+
+	temp = HTfullURL_toFile(doc->address);
+	ADD_SS(gettext("Name:"), temp);
+	FREE(temp);
+
+	ADD_SS(gettext("URL:"), doc->address);
+
+	END_DL();
+
+	temp = HTfullURL_toFile(links[doc->link].lname);
+
+	if (lstat(temp, &dir_info) == -1) {
+	    CTRACE((tfp, "lstat(%s) failed, errno=%d\n", temp, errno));
+	    HTAlert(CURRENT_LINK_STATUS_FAILED);
+	} else {
+	    char modes[80];
+
+	    label_columns = 16;
+	    if (S_ISDIR(dir_info.st_mode)) {
+		BEGIN_DL(gettext("Directory that you have currently selected"));
+	    } else if (S_ISREG(dir_info.st_mode)) {
+		BEGIN_DL(gettext("File that you have currently selected"));
+#ifdef S_IFLNK
+	    } else if (S_ISLNK(dir_info.st_mode)) {
+		BEGIN_DL(gettext("Symbolic link that you have currently selected"));
+#endif
+	    } else {
+		BEGIN_DL(gettext("Item that you have currently selected"));
+	    }
+	    ADD_SS(gettext("Full name:"), temp);
+#ifdef S_IFLNK
+	    if (S_ISLNK(dir_info.st_mode)) {
+		char buf[MAX_LINE];
+		int buf_size;
+
+		if ((buf_size = readlink(temp, buf, sizeof(buf) - 1)) != -1) {
+		    buf[buf_size] = '\0';
+		} else {
+		    sprintf(buf, "%.*s", (int) sizeof(buf) - 1,
+			    gettext("Unable to follow link"));
+		}
+		ADD_SS(gettext("Points to file:"), buf);
+	    }
+#endif
+	    name = HTAA_UidToName((int) dir_info.st_uid);
+	    if (*name)
+		ADD_SS(gettext("Name of owner:"), name);
+	    name = HTAA_GidToName((int) dir_info.st_gid);
+	    if (*name)
+		ADD_SS(gettext("Group name:"), name);
+	    if (S_ISREG(dir_info.st_mode)) {
+		ADD_NN(gettext("File size:"),
+		       (long) dir_info.st_size,
+		       gettext("(bytes)"));
+	    }
+	    /*
+	     * Include date and time information.
+	     */
+	    ADD_SS(gettext("Creation date:"),
+		   ctime(&dir_info.st_ctime));
+
+	    ADD_SS(gettext("Last modified:"),
+		   ctime(&dir_info.st_mtime));
+
+	    ADD_SS(gettext("Last accessed:"),
+		   ctime(&dir_info.st_atime));
+
+	    END_DL();
+
+	    label_columns = 9;
+	    BEGIN_DL(gettext("Access Permissions"));
+	    modes[0] = '\0';
+	    modes[1] = '\0';	/* In case there are no permissions */
+	    modes[2] = '\0';
+	    if ((dir_info.st_mode & S_IRUSR))
+		strcat(modes, ", read");
+	    if ((dir_info.st_mode & S_IWUSR))
+		strcat(modes, ", write");
+	    if ((dir_info.st_mode & S_IXUSR)) {
+		if (S_ISDIR(dir_info.st_mode))
+		    strcat(modes, ", search");
+		else {
+		    strcat(modes, ", execute");
+		    if ((dir_info.st_mode & S_ISUID))
+			strcat(modes, ", setuid");
+		}
+	    }
+	    ADD_SS(gettext("Owner:"), &modes[2]);
+
+	    modes[0] = '\0';
+	    modes[1] = '\0';	/* In case there are no permissions */
+	    modes[2] = '\0';
+	    if ((dir_info.st_mode & S_IRGRP))
+		strcat(modes, ", read");
+	    if ((dir_info.st_mode & S_IWGRP))
+		strcat(modes, ", write");
+	    if ((dir_info.st_mode & S_IXGRP)) {
+		if (S_ISDIR(dir_info.st_mode))
+		    strcat(modes, ", search");
+		else {
+		    strcat(modes, ", execute");
+		    if ((dir_info.st_mode & S_ISGID))
+			strcat(modes, ", setgid");
+		}
+	    }
+	    ADD_SS(gettext("Group:"), &modes[2]);
+
+	    modes[0] = '\0';
+	    modes[1] = '\0';	/* In case there are no permissions */
+	    modes[2] = '\0';
+	    if ((dir_info.st_mode & S_IROTH))
+		strcat(modes, ", read");
+	    if ((dir_info.st_mode & S_IWOTH))
+		strcat(modes, ", write");
+	    if ((dir_info.st_mode & S_IXOTH)) {
+		if (S_ISDIR(dir_info.st_mode))
+		    strcat(modes, ", search");
+		else {
+		    strcat(modes, ", execute");
+#ifdef S_ISVTX
+		    if ((dir_info.st_mode & S_ISVTX))
+			strcat(modes, ", sticky");
+#endif
+		}
+	    }
+	    ADD_SS(gettext("World:"), &modes[2]);
+	    END_DL();
+	}
+	FREE(temp);
+    } else {
+#endif /* DIRED_SUPPORT */
+
+	BEGIN_DL(gettext("File that you are currently viewing"));
+
+	LYformTitle(&Title, doc->title);
+	HTSprintf(&temp, "%s%s",
+		  LYEntifyTitle(&buffer, Title),
+		  ((doc->isHEAD &&
+		    !strstr(Title, " (HEAD)") &&
+		    !strstr(Title, " - HEAD")) ? " (HEAD)" : ""));
+	ADD_SS(gettext("Linkname:"), temp);
+	FREE(temp);
+
+	ADD_SS("URL:", doc->address);
+
+	if (HTLoadedDocumentCharset()) {
+	    ADD_SS(gettext("Charset:"),
+		   HTLoadedDocumentCharset());
+	} else {
+	    LYUCcharset *p_in = HTAnchor_getUCInfoStage(HTMainAnchor,
+							UCT_STAGE_PARSER);
+
+	    if (!p_in || isEmpty(p_in->MIMEname) ||
+		HTAnchor_getUCLYhndl(HTMainAnchor, UCT_STAGE_PARSER) < 0) {
+		p_in = HTAnchor_getUCInfoStage(HTMainAnchor, UCT_STAGE_MIME);
+	    }
+	    if (p_in && non_empty(p_in->MIMEname) &&
+		HTAnchor_getUCLYhndl(HTMainAnchor, UCT_STAGE_MIME) >= 0) {
+		HTSprintf(&temp, "%s %s",
+			  LYEntifyTitle(&buffer, p_in->MIMEname),
+			  gettext("(assumed)"));
+		ADD_SS(gettext("Charset:"), p_in->MIMEname);
+		FREE(temp);
+	    }
+	}
+
+	if ((cp = HText_getServer()) != NULL && *cp != '\0')
+	    ADD_SS(gettext("Server:"), cp);
+
+	if ((cp = HText_getDate()) != NULL && *cp != '\0')
+	    ADD_SS(gettext("Date:"), cp);
+
+	if ((cp = HText_getLastModified()) != NULL && *cp != '\0')
+	    ADD_SS(gettext("Last Mod:"), cp);
+
+#ifdef ADVANCED_INFO
+	if (LYInfoAdvanced) {
+	    if (HTMainAnchor && HTMainAnchor->expires) {
+		ADD_SS(gettext("Expires:"), HTMainAnchor->expires);
+	    }
+	    if (HTMainAnchor && HTMainAnchor->cache_control) {
+		ADD_SS(gettext("Cache-Control:"), HTMainAnchor->cache_control);
+	    }
+	    if (HTMainAnchor && HTMainAnchor->content_length > 0) {
+		ADD_NN(gettext("Content-Length:"),
+		       HTMainAnchor->content_length,
+		       gettext("bytes"));
+	    } else {
+		ADD_NN(gettext("Length:"),
+		       HText_getNumOfBytes(),
+		       gettext("bytes"));
+	    }
+	    if (HTMainAnchor && HTMainAnchor->content_language) {
+		ADD_SS(gettext("Language:"), HTMainAnchor->content_language);
+	    }
+	}
+#endif /* ADVANCED_INFO */
+
+	if (doc->post_data) {
+	    fprintf(fp0, "<dt><em>%s</em> <xmp>%.*s</xmp>\n",
+		    LYEntifyTitle(&buffer, gettext("Post Data:")),
+		    BStrLen(doc->post_data),
+		    BStrData(doc->post_data));
+	    ADD_SS(gettext("Post Content Type:"), doc->post_content_type);
+	}
+
+	ADD_SS(gettext("Owner(s):"),
+	       (owner_address
+		? owner_address
+		: NO_NOTHING));
+
+	ADD_NN(gettext("size:"),
+	       HText_getNumOfLines(),
+	       gettext("lines"));
+
+	StrAllocCopy(temp,
+		     ((lynx_mode == FORMS_LYNX_MODE)
+		      ? gettext("forms mode")
+		      : (HTisDocumentSource()
+			 ? gettext("source")
+			 : gettext("normal"))));
+	if (doc->safe)
+	    StrAllocCat(temp, gettext(", safe"));
+	if (doc->internal_link)
+	    StrAllocCat(temp, gettext(", via internal link"));
+
+#ifdef ADVANCED_INFO
+	if (LYInfoAdvanced) {
+	    if (HText_hasNoCacheSet(HTMainText))
+		StrAllocCat(temp, gettext(", no-cache"));
+	    if (HTAnchor_isISMAPScript((HTAnchor *) HTMainAnchor))
+		StrAllocCat(temp, gettext(", ISMAP script"));
+	    if (doc->bookmark)
+		StrAllocCat(temp, gettext(", bookmark file"));
+	}
+#endif /* ADVANCED_INFO */
+
+	ADD_SS(gettext("mode:"), temp);
+	FREE(temp);
+
+	END_DL();
+
+	if (nlinks > 0) {
+	    BEGIN_DL(gettext("Link that you currently have selected"));
+	    ADD_SS(gettext("Linkname:"),
+		   LYGetHiliteStr(doc->link, 0));
+	    if (lynx_mode == FORMS_LYNX_MODE &&
+		links[doc->link].type == WWW_FORM_LINK_TYPE) {
+		if (links[doc->link].l_form->submit_method) {
+		    int method = links[doc->link].l_form->submit_method;
+		    char *enctype = links[doc->link].l_form->submit_enctype;
+
+		    ADD_SS(gettext("Method:"),
+			   ((method == URL_POST_METHOD) ? "POST" :
+			    ((method == URL_MAIL_METHOD) ? "(email)" :
+			     "GET")));
+		    ADD_SS(gettext("Enctype:"),
+			   (non_empty(enctype)
+			    ? enctype
+			    : "application/x-www-form-urlencoded"));
+		}
+		if (links[doc->link].l_form->submit_action) {
+		    ADD_SS(gettext("Action:"),
+			   links[doc->link].l_form->submit_action);
+		}
+		if (!(links[doc->link].l_form->submit_method &&
+		      links[doc->link].l_form->submit_action)) {
+		    fprintf(fp0, "<dt>&nbsp;%s\n",
+			    LYEntifyTitle(&buffer, gettext("(Form field)")));
+		}
+	    } else {
+		ADD_SS("URL:",
+		       NonNull(links[doc->link].lname));
+	    }
+	    END_DL();
+
+	} else {
+	    fprintf(fp0, "<h2>%s</h2>",
+		    LYEntifyTitle(&buffer,
+				  gettext("No Links on the current page")));
+	}
+
+#ifdef EXP_HTTP_HEADERS
+	if ((cp = HText_getHttpHeaders()) != 0) {
+	    fprintf(fp0, "<h2>%s</h2>",
+		    LYEntifyTitle(&buffer, gettext("Server Headers:")));
+	    fprintf(fp0, "<pre>%s</pre>",
+		    LYEntifyTitle(&buffer, cp));
+	}
+#endif
+
+#ifdef DIRED_SUPPORT
+    }
+#endif /* DIRED_SUPPORT */
+    EndInternalPage(fp0);
+
+    LYrefresh();
+
+    LYCloseTemp(tempfile);
+    FREE(Title);
+    FREE(buffer);
+
+    return (0);
+}
diff --git a/src/LYShowInfo.h b/src/LYShowInfo.h
new file mode 100644
index 00000000..443bff37
--- /dev/null
+++ b/src/LYShowInfo.h
@@ -0,0 +1,21 @@
+#ifndef LYSHOWINFO_H
+#define LYSHOWINFO_H
+
+#ifndef LYSTRUCTS_H
+#include <LYStructs.h>
+#endif /* LYSTRUCTS_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    extern BOOL LYVersionIsRelease(void);
+    extern const char *LYVersionStatus(void);
+    extern const char *LYVersionDate(void);
+    extern int LYShowInfo(DocInfo *doc,
+			  DocInfo *newdoc,
+			  char *owner_address);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYSHOWINFO_H */
diff --git a/src/LYSignal.h b/src/LYSignal.h
new file mode 100644
index 00000000..ccb81021
--- /dev/null
+++ b/src/LYSignal.h
@@ -0,0 +1,31 @@
+#ifndef LYSIGNAL_H
+#define LYSIGNAL_H
+
+#include <signal.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef VMS
+    extern void VMSsignal(int sig, void (*func) ());
+
+#ifdef signal
+#undef signal
+#endif				/* signal */
+#define signal(a,b) VMSsignal(a,b)	/* use LYCurses.c routines for interrupts */
+#endif				/* VMS */
+
+#ifdef HAVE_SIGACTION
+    typedef void LYSigHandlerFunc_t(int);
+
+/* implementation in LYUtils.c */
+    extern void LYExtSignal(int sig, LYSigHandlerFunc_t * handler);
+
+#else
+#define LYExtSignal(sig,h) signal(sig, h)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYSIGNAL_H */
diff --git a/src/LYStrings.c b/src/LYStrings.c
new file mode 100644
index 00000000..f599e5cc
--- /dev/null
+++ b/src/LYStrings.c
@@ -0,0 +1,6121 @@
+/* $LynxId: LYStrings.c,v 1.171 2010/05/03 00:36:05 tom Exp $ */
+#include <HTUtils.h>
+#include <HTCJK.h>
+#include <UCAux.h>
+#include <LYGlobalDefs.h>
+#include <LYUtils.h>
+#include <LYStrings.h>
+#include <GridText.h>
+#include <LYKeymap.h>
+#include <LYClean.h>
+#include <LYMail.h>
+#include <LYNews.h>
+#include <LYOptions.h>
+#include <LYCharSets.h>
+#include <HTAlert.h>
+#include <HTString.h>
+#include <LYCharUtils.h>
+#include <HTList.h>
+#include <HTParse.h>
+#ifdef USE_MOUSE
+#include <LYMainLoop.h>
+#endif
+
+#ifdef DJGPP_KEYHANDLER
+#include <pc.h>
+#include <keys.h>
+#endif /* DJGPP_KEYHANDLER */
+
+#ifdef USE_COLOR_STYLE
+#include <LYHash.h>
+#include <AttrList.h>
+#endif
+
+#ifdef USE_SCROLLBAR
+#include <LYMainLoop.h>
+#endif
+
+#ifdef USE_CMD_LOGGING
+#include <LYReadCFG.h>
+#endif
+
+#include <LYShowInfo.h>
+#include <LYLeaks.h>
+
+#if defined(WIN_EX)
+#undef  BUTTON_CTRL
+#define BUTTON_CTRL	0	/* Quick hack */
+#endif
+
+#ifdef DEBUG_EDIT
+#define CTRACE_EDIT(p) CTRACE(p)
+#else
+#define CTRACE_EDIT(p)		/*nothing */
+#endif
+
+/*
+ * The edit_history lists allow the user to press tab when entering URL to get
+ * the closest match in the closet
+ */
+#define LYClosetSize 100
+
+static HTList *URL_edit_history;
+static HTList *MAIL_edit_history;
+
+/* If you want to add mouse support for some new platform, it's fairly
+ * simple to do.  Once you've determined the X and Y coordinates of
+ * the mouse event, loop through the elements in the links[] array and
+ * see if the coordinates fall within a highlighted link area.	If so,
+ * the code must set mouse_link to the index of the chosen link,
+ * and return a key value that corresponds to LYK_ACTIVATE.  The
+ * LYK_ACTIVATE code in LYMainLoop.c will then check mouse_link
+ * and activate that link.  If the mouse event didn't fall within a
+ * link, the code should just set mouse_link to -1 and return -1. --AMK
+ */
+
+/* The number of the link selected w/ the mouse (-1 if none) */
+static int mouse_link = -1;
+
+static int have_levent;
+
+#if defined(USE_MOUSE) && defined(NCURSES)
+static MEVENT levent;
+#endif
+
+/* Return the value of mouse_link */
+int peek_mouse_levent(void)
+{
+#if defined(USE_MOUSE) && defined(NCURSES)
+    if (have_levent > 0) {
+	ungetmouse(&levent);
+	have_levent--;
+	return 1;
+    }
+#endif
+    return 0;
+}
+
+/* Return the value of mouse_link, erasing it */
+int get_mouse_link(void)
+{
+    int t;
+
+    t = mouse_link;
+    mouse_link = -1;
+    if (t < 0)
+	t = -1;			/* Backward compatibility. */
+    return t;
+}
+
+/* Return the value of mouse_link */
+int peek_mouse_link(void)
+{
+    return mouse_link;
+}
+
+int fancy_mouse(WINDOW * win, int row,
+		int *position)
+{
+    int cmd = LYK_DO_NOTHING;
+
+#ifdef USE_MOUSE
+/*********************************************************************/
+
+#if defined(WIN_EX) && defined(PDCURSES)
+
+    request_mouse_pos();
+
+    if (BUTTON_STATUS(1)
+	&& (MOUSE_X_POS >= getbegx(win) &&
+	    MOUSE_X_POS < (getbegx(win) + getmaxx(win)))) {
+	int mypos = MOUSE_Y_POS - getbegy(win);
+	int delta = mypos - row;
+
+	if (mypos + 1 == getmaxy(win)) {
+	    /* At the decorative border: scroll forward */
+	    if (BUTTON_STATUS(1) & BUTTON1_TRIPLE_CLICKED)
+		cmd = LYK_END;
+	    else if (BUTTON_STATUS(1) & BUTTON1_DOUBLE_CLICKED)
+		cmd = LYK_NEXT_PAGE;
+	    else
+		cmd = LYK_NEXT_LINK;
+	} else if (mypos >= getmaxy(win)) {
+	    if (BUTTON_STATUS(1) & (BUTTON1_DOUBLE_CLICKED | BUTTON1_TRIPLE_CLICKED))
+		cmd = LYK_END;
+	    else
+		cmd = LYK_NEXT_PAGE;
+	} else if (mypos == 0) {
+	    /* At the decorative border: scroll back */
+	    if (BUTTON_STATUS(1) & BUTTON1_TRIPLE_CLICKED)
+		cmd = LYK_HOME;
+	    else if (BUTTON_STATUS(1) & BUTTON1_DOUBLE_CLICKED)
+		cmd = LYK_PREV_PAGE;
+	    else
+		cmd = LYK_PREV_LINK;
+	} else if (mypos < 0) {
+	    if (BUTTON_STATUS(1) & (BUTTON1_DOUBLE_CLICKED | BUTTON1_TRIPLE_CLICKED))
+		cmd = LYK_HOME;
+	    else
+		cmd = LYK_PREV_PAGE;
+#ifdef KNOW_HOW_TO_TOGGLE
+	} else if (BUTTON_STATUS(1) & (BUTTON_CTRL)) {
+	    cur_selection += delta;
+	    cmd = LYX_TOGGLE;
+#endif
+	} else if (BUTTON_STATUS(1) & (BUTTON_ALT | BUTTON_SHIFT | BUTTON_CTRL)) {
+	    /* Probably some unrelated activity, such as selecting some text.
+	     * Select, but do nothing else.
+	     */
+	    *position += delta;
+	    cmd = -1;
+	} else {
+	    /* No scrolling or overflow checks necessary. */
+	    *position += delta;
+	    cmd = LYK_ACTIVATE;
+	}
+    } else if (BUTTON_STATUS(1) & (BUTTON3_CLICKED | BUTTON3_DOUBLE_CLICKED | BUTTON3_TRIPLE_CLICKED)) {
+	cmd = LYK_QUIT;
+    }
+#else
+#if defined(NCURSES)
+#define ButtonModifiers (BUTTON_ALT | BUTTON_SHIFT | BUTTON_CTRL)
+    MEVENT event;
+
+    getmouse(&event);
+    if ((event.bstate & (BUTTON1_CLICKED |
+			 BUTTON1_DOUBLE_CLICKED |
+			 BUTTON1_TRIPLE_CLICKED))) {
+	int mypos = event.y - getbegy(win);
+	int delta = mypos - row;
+
+	if ((event.x < getbegx(win) ||
+	     event.x >= (getbegx(win) + getmaxx(win)))
+	    && !(event.bstate & ButtonModifiers))
+	    return LYK_QUIT;	/* User clicked outside, wants to quit? */
+	if (mypos + 1 == getmaxy(win)) {
+	    /* At the decorative border: scroll forward */
+	    if (event.bstate & BUTTON1_TRIPLE_CLICKED)
+		cmd = LYK_END;
+	    else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
+		cmd = LYK_NEXT_PAGE;
+	    else
+		cmd = LYK_NEXT_LINK;
+	} else if (mypos >= getmaxy(win)) {
+	    if (event.bstate & (BUTTON1_DOUBLE_CLICKED |
+				BUTTON1_TRIPLE_CLICKED))
+		cmd = LYK_END;
+	    else
+		cmd = LYK_NEXT_PAGE;
+	} else if (mypos == 0) {
+	    /* At the decorative border: scroll back */
+	    if (event.bstate & BUTTON1_TRIPLE_CLICKED)
+		cmd = LYK_HOME;
+	    else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
+		cmd = LYK_PREV_PAGE;
+	    else
+		cmd = LYK_PREV_LINK;
+	} else if (mypos < 0) {
+	    if (event.bstate & (BUTTON1_DOUBLE_CLICKED |
+				BUTTON1_TRIPLE_CLICKED))
+		cmd = LYK_HOME;
+	    else
+		cmd = LYK_PREV_PAGE;
+#ifdef KNOW_HOW_TO_TOGGLE
+	} else if (event.bstate & (BUTTON_CTRL)) {
+	    cur_selection += delta;
+	    cmd = LYX_TOGGLE;
+#endif
+	} else if (event.x <= getbegx(win) + 1 ||
+		   event.x >= getbegx(win) + getmaxx(win) - 2) {
+	    /* Click on left or right border for positioning without
+	     * immediate action: select, but do nothing else.
+	     * Actually, allow an error of one position inwards. - kw
+	     */
+	    *position += delta;
+	    cmd = -1;
+	} else if (event.bstate & ButtonModifiers) {
+	    /* Probably some unrelated activity, such as selecting some text.
+	     * Select, but do nothing else.
+	     */
+	    /* Possibly this is never returned by ncurses, so this case
+	     * may be useless depending on situation (kind of mouse support
+	     * and library versions). - kw
+	     */
+	    *position += delta;
+	    cmd = -1;
+	} else {
+	    /* No scrolling or overflow checks necessary. */
+	    *position += delta;
+	    cmd = LYK_ACTIVATE;
+	}
+    } else if (event.bstate & (BUTTON3_CLICKED | BUTTON3_DOUBLE_CLICKED | BUTTON3_TRIPLE_CLICKED)) {
+	cmd = LYK_QUIT;
+    }
+#endif /* NCURSES */
+#endif /* PDCURSES */
+
+/************************************************************************/
+#endif /* USE_MOUSE */
+    (void) win;
+    (void) row;
+    (void) position;
+
+    return cmd;
+}
+
+/*
+ * Manage the collection of edit-histories
+ */
+static HTList *whichRecall(RecallType recall)
+{
+    HTList **list;
+
+    switch (recall) {
+    case RECALL_CMD:
+	return LYcommandList();
+    case RECALL_MAIL:
+	list = &MAIL_edit_history;
+	break;
+    default:
+	list = &URL_edit_history;
+	break;
+    }
+    if (*list == 0)
+	*list = HTList_new();
+    return *list;
+}
+
+/*
+ * Remove the oldest item in the closet
+ */
+static void LYRemoveFromCloset(HTList *list)
+{
+    void *data = HTList_removeFirstObject(list);
+
+    if (data != 0)
+	FREE(data);
+}
+
+void LYCloseCloset(RecallType recall)
+{
+    HTList *list = whichRecall(recall);
+
+    while (!HTList_isEmpty(list)) {
+	LYRemoveFromCloset(list);
+    }
+    HTList_delete(list);	/* should already be empty */
+}
+
+/*
+ * Strategy:  We begin at the top and search downwards.  We return the first
+ * match, i.e., the newest since we search from the top.  This should be made
+ * more intelligent, but works for now.
+ */
+static char *LYFindInCloset(RecallType recall, char *base)
+{
+    HTList *list = whichRecall(recall);
+    char *data;
+    unsigned len = strlen(base);
+
+    while (!HTList_isEmpty(list)) {
+	data = (char *) HTList_nextObject(list);
+	if (data != NULL && !strncmp(base, data, len))
+	    return (data);
+    }
+
+    return (0);
+}
+
+static void LYAddToCloset(RecallType recall, char *str)
+{
+    HTList *list = whichRecall(recall);
+    char *data = NULL;
+
+    StrAllocCopy(data, str);
+    HTList_addObject(list, data);
+    while (HTList_count(list) > LYClosetSize)
+	LYRemoveFromCloset(list);
+}
+
+#ifdef USE_MOUSE
+static int XYdist(int x1,
+		  int y1,
+		  int x2,
+		  int y2,
+		  int dx2)
+{
+    int xerr = 3 * (x2 - x1), yerr = 9 * (y2 - y1);
+
+    if (xerr < 0)
+	xerr = 3 * (x1 - x2 - dx2) + 1;		/* pos after string not really in it */
+    if (xerr < 0)
+	xerr = 0;
+    if (yerr < 0)
+	yerr = -yerr;
+    if (!yerr)			/* same line is good */
+	return (xerr > 0) ? (xerr * 2 - 1) : 0;
+    if (xerr < 9 && yerr)	/* x-dist of 3 cell better than y-dist of 1 cell */
+	yerr += (9 - xerr);
+    return 2 * xerr + yerr;	/* Subjective factor; ratio -> approx. 6 / 9 */
+/*
+old: (IZ 1999-07-30)
+ 3  2  2  2  1  1  1 XX XX XX XX XX  0  1  1  1  2  2  2  3  3
+ 4\ 3  3  3  2  2  2  2  2  2  2  2  2  2  2  2  3  3  3/ 4  4
+ 5  4  4  4\ 3  3  3  3  3  3  3  3  3  3  3  3/ 4  4  4  5  5
+ 6  5  5  5  4  4  4  4  4  4  4  4  4  4  4  4  5  5  5  6  5
+now: (kw 1999-10-23)
+41 35 29|23 17 11  5 XX XX XX XX XX  1  7 13 19 25|31 37 43 49
+   45 39 33\27 24 21 18 18 18 18 18 19 22 25 28/34 40 46 50
+      48 42 36 33 30\27 27 27 27 27 28/31 34 37 43 49
+         51 45 42 39 36 36 36 36 36 37 40 43 46 49
+               51 48 45 45 45 45 45 46 49 52
+*/
+}
+
+/* Given X and Y coordinates of a mouse event, set mouse_link to the
+ * index of the corresponding hyperlink, or set mouse_link to -1 if no
+ * link matches the event.  Returns -1 if no link matched the click,
+ * or a keycode that must be returned from LYgetch() to activate the
+ * link.
+ */
+
+static int set_clicked_link(int x,
+			    int y,
+			    int code,
+			    int clicks)
+{
+    int left = 6;
+    int right = LYcolLimit - 5;
+
+    /* yes, I am assuming that my screen will be a certain width. */
+    int i;
+    int c = -1;
+
+    if (y == (LYlines - 1) || y == 0) {		/* First or last row */
+	/* XXXX In fact # is not always at x==0?  KANJI_CODE_OVERRIDE? */
+	int toolbar = (y == 0 && HText_hasToolbar(HTMainText));
+
+	mouse_link = -2;
+	if (x == 0 && toolbar)	/* On '#' */
+	    c = LAC_TO_LKC0(LYK_TOOLBAR);
+#if defined(CAN_CUT_AND_PASTE) && defined(USE_COLOR_STYLE)
+	else if (y == 0 && x == LYcolLimit && s_hot_paste != NOSTYLE)
+	    c = LAC_TO_LKC0(LYK_PASTE_URL);
+#endif
+	else if (clicks > 1) {
+	    if (x < left + toolbar)
+		c = (code == FOR_PROMPT && y)
+		    ? HOME : LAC_TO_LKC0(LYK_MAIN_MENU);
+	    else if (x > right)
+		c = (code == FOR_PROMPT && y)
+		    ? END_KEY : LAC_TO_LKC0(LYK_VLINKS);
+	    else if (y)		/* Last row */
+		c = LAC_TO_LKC0(LYK_END);
+	    else		/* First row */
+		c = LAC_TO_LKC0(LYK_HOME);
+	} else {
+	    if (x < left + toolbar)
+		c = (code == FOR_PROMPT && y)
+		    ? LTARROW
+		    : (
+#ifdef USE_COLOR_STYLE
+			  (s_forw_backw != NOSTYLE && x - toolbar >= 3)
+			  ? LAC_TO_LKC0(LYK_NEXT_DOC)
+			  : LAC_TO_LKC0(LYK_PREV_DOC)
+#else
+			  LAC_TO_LKC0(LYK_NEXT_DOC)
+#endif
+		    );
+	    else if (x > right)
+		c = (code == FOR_PROMPT && y)
+		    ? RTARROW : LAC_TO_LKC0(LYK_HISTORY);
+	    else if (y)		/* Last row */
+		c = LAC_TO_LKC0(LYK_NEXT_PAGE);
+	    else		/* First row */
+		c = LAC_TO_LKC0(LYK_PREV_PAGE);
+	}
+#ifdef USE_SCROLLBAR
+    } else if (x == (LYcols - 1) && LYShowScrollbar && LYsb_begin >= 0) {
+	int h = display_lines - 2 * (LYsb_arrow != 0);
+
+	mouse_link = -2;
+	y -= 1 + (LYsb_arrow != 0);
+	if (y < 0)
+	    return LAC_TO_LKC0(LYK_UP_TWO);
+	if (y >= h)
+	    return LAC_TO_LKC0(LYK_DOWN_TWO);
+
+	if (clicks >= 2) {
+	    double frac = (1. * y) / (h - 1);
+	    int l = HText_getNumOfLines() + 1;	/* NOL() off by one? */
+
+	    l -= display_lines;
+	    if (l > 0)
+		LYSetNewline((int) (frac * l + 1 + 0.5));
+	    return LYReverseKeymap(LYK_DO_NOTHING);
+	}
+
+	if (y < LYsb_begin)
+	    return LAC_TO_LKC0(LYK_PREV_PAGE);
+	if (y >= LYsb_end)
+	    return LAC_TO_LKC0(LYK_NEXT_PAGE);
+	mouse_link = -1;	/* No action in edit fields */
+#endif
+    } else {
+	int mouse_err = 29, /* subjctv-dist better than this for approx stuff */ cur_err;
+
+	/* Loop over the links and see if we can get a match */
+	for (i = 0; i < nlinks; i++) {
+	    int len, lx = links[i].lx, is_text = 0;
+	    int count = 0;
+	    const char *text = LYGetHiliteStr(i, count);
+
+	    if (links[i].type == WWW_FORM_LINK_TYPE
+		&& F_TEXTLIKE(links[i].l_form->type))
+		is_text = 1;
+
+	    /* Check the first line of the link */
+	    if (text != NULL) {
+		if (is_text)
+		    len = links[i].l_form->size;
+		else
+		    len = (int) LYstrCells(text);
+		cur_err = XYdist(x, y, links[i].lx, links[i].ly, len);
+		/* Check the second line */
+		while (cur_err > 0
+		       && (text = LYGetHiliteStr(i, ++count)) != NULL) {
+		    /* Note that there is at most one hightext if is_text */
+		    int cur_err_2 = XYdist(x, y,
+					   LYGetHilitePos(i, count),
+					   links[i].ly + count,
+					   (int) LYstrCells(text));
+
+		    cur_err = HTMIN(cur_err, cur_err_2);
+		}
+		if (cur_err > 0 && is_text)
+		    cur_err--;	/* a bit of preference for text fields,
+				   enter field if hit exactly at end - kw */
+		if (cur_err == 0) {
+		    int cury, curx;
+
+		    LYGetYX(cury, curx);
+		    /* double-click, if we care:
+		       submit text submit fields. - kw */
+		    if (clicks > 1 && is_text &&
+			links[i].l_form->type == F_TEXT_SUBMIT_TYPE) {
+			if (code != FOR_INPUT
+			/* submit current input field directly */
+			    || !(cury == y && (curx >= lx) && ((curx - lx) <= len))) {
+			    c = LAC_TO_LKC0(LYK_SUBMIT);
+			    mouse_link = i;
+			} else {
+			    c = LAC_TO_LKC0(LYK_SUBMIT);
+			    mouse_link = -1;
+			}
+			mouse_err = 0;
+			break;
+		    }
+		    if (code != FOR_INPUT
+		    /* Do not pick up the current input field */
+			|| !((cury == y && (curx >= lx) && ((curx - lx) <= len)))) {
+			if (is_text) {
+			    have_levent = 1;
+#if defined(TEXTFIELDS_MAY_NEED_ACTIVATION) && defined(INACTIVE_INPUT_STYLE_VH)
+			    if (x == links[i].lx && y == links[i].ly)
+				textinput_redrawn = FALSE;
+#endif /* TEXTFIELDS_MAY_NEED_ACTIVATION && INACTIVE_INPUT_STYLE_VH */
+			}
+			mouse_link = i;
+		    } else
+			mouse_link = -1;
+		    mouse_err = 0;
+		    break;
+		} else if (cur_err < mouse_err) {
+		    mouse_err = cur_err;
+		    mouse_link = i;
+		}
+	    }
+	}
+	/*
+	 * If a link was hit, we must look for a key which will activate
+	 * LYK_ACTIVATE We expect to find LYK_ACTIVATE (it's usually mapped to
+	 * the Enter key).
+	 */
+	if (mouse_link >= 0) {
+	    if (mouse_err == 0) {
+		if (c == -1)
+		    c = LAC_TO_LKC0(LYK_ACTIVATE);
+	    } else if (mouse_err >= 0)
+		c = LAC_TO_LKC0(LYK_CHANGE_LINK);
+	} else {
+	    if (2 * y > LYlines) {	/* Bottom Half of the screen */
+		if (4 * y < 3 * LYlines) {
+		    c = LAC_TO_LKC0(LYK_DOWN_TWO);	/* Third quarter */
+		} else
+		    c = LAC_TO_LKC0(LYK_DOWN_HALF);	/* Fourth quarter */
+	    } else {		/* Upper Half of the screen */
+		if (4 * y < LYlines) {
+		    c = LAC_TO_LKC0(LYK_UP_HALF);	/* First quarter */
+		} else
+		    c = LAC_TO_LKC0(LYK_UP_TWO);	/* Second quarter */
+	    }
+	}
+    }
+    return c;
+}
+#endif /* USE_MOUSE */
+
+/*
+ * LYstrncpy() terminates strings with a null byte.  Writes a null byte into
+ * the n+1 byte of dst.
+ */
+char *LYstrncpy(char *dst,
+		const char *src,
+		int n)
+{
+    char *val;
+    int len;
+
+    if (src == 0)
+	src = "";
+    len = (int) strlen(src);
+
+    if (n < 0)
+	n = 0;
+
+    val = strncpy(dst, src, (unsigned) n);
+    if (len < n)
+	*(dst + len) = '\0';
+    else
+	*(dst + n) = '\0';
+    return val;
+}
+
+#define IS_NEW_GLYPH(ch) (utf_flag && (UCH(ch)&0xc0) != 0x80)
+#define IS_UTF_EXTRA(ch) (utf_flag && (UCH(ch)&0xc0) == 0x80)
+
+/*
+ * LYmbcsstrncpy() terminates strings with a null byte.  It takes account of
+ * multibyte characters.  The src string is copied until either end of string
+ * or max number of either bytes or glyphs (mbcs sequences) (CJK or UTF8).  The
+ * utf_flag argument should be TRUE for UTF8.  - KW & FM
+ */
+char *LYmbcsstrncpy(char *dst,
+		    const char *src,
+		    int n_bytes,
+		    int n_glyphs,
+		    BOOL utf_flag)
+{
+    char *val = dst;
+    int i_bytes = 0, i_glyphs = 0;
+
+    if (n_bytes < 0)
+	n_bytes = 0;
+    if (n_glyphs < 0)
+	n_glyphs = 0;
+
+    for (; *src != '\0' && i_bytes < n_bytes; i_bytes++) {
+	if (IS_NEW_GLYPH(*src)) {
+	    if (i_glyphs++ >= n_glyphs) {
+		*dst = '\0';
+		return val;
+	    }
+	}
+	*(dst++) = *(src++);
+    }
+    *dst = '\0';
+
+    return val;
+}
+
+/*
+ * LYmbcs_skip_glyphs() skips a given number of character positions in a string
+ * and returns the resulting pointer.  It takes account of UTF-8 encoded
+ * characters.  - KW
+ */
+const char *LYmbcs_skip_glyphs(const char *data,
+			       int n_glyphs,
+			       BOOL utf_flag)
+{
+    int i_glyphs = 0;
+
+    if (n_glyphs < 0)
+	n_glyphs = 0;
+
+    if (non_empty(data)) {
+	if (!utf_flag) {
+	    while (n_glyphs-- > 0) {
+		if (!*++data)
+		    break;
+	    }
+	} else {
+	    while (*data) {
+		if (IS_NEW_GLYPH(*data)) {
+		    if (i_glyphs++ >= n_glyphs) {
+			break;
+		    }
+		}
+		data++;
+	    }
+	}
+    }
+    return data;
+}
+
+/*
+ * LYmbcs_skip_cells() skips a given number of display positions in a string
+ * and returns the resulting pointer.  It takes account of UTF-8 encoded
+ * characters.  - TD
+ */
+const char *LYmbcs_skip_cells(const char *data,
+			      int n_cells,
+			      BOOL utf_flag)
+{
+    const char *result;
+    int actual;
+    int target = n_cells;
+
+    do {
+	result = LYmbcs_skip_glyphs(data, target--, utf_flag);
+	actual = LYstrExtent2(data, result - data);
+    } while ((actual > 0) && (actual > n_cells));
+    return result;
+}
+
+/*
+ * LYmbcsstrlen() returns the printable length of a string that might contain
+ * IsSpecial or multibyte (CJK or UTF8) characters.  - FM
+ *
+ * Counts glyph cells if count_gcells is set.  (Full-width characters in CJK
+ * mode count as two.) Counts character glyphs if count_gcells is unset. 
+ * (Full- width characters in CJK mode count as one.) - kw
+ */
+int LYmbcsstrlen(const char *str,
+		 BOOL utf_flag,
+		 BOOL count_gcells)
+{
+    int i, j, len = 0;
+
+    if (non_empty(str)) {
+#ifdef WIDEC_CURSES
+	if (count_gcells) {
+	    len = LYstrCells(str);
+	} else
+#endif
+	{
+	    for (i = 0; str[i] != '\0'; i++) {
+		if (!IsSpecialAttrChar(str[i])) {
+		    len++;
+		    if (IS_NEW_GLYPH(str[i])) {
+			j = 0;
+			while (IsNormalChar(str[(i + 1)]) &&
+			       j < 5 &&
+			       IS_UTF_EXTRA(str[(i + 1)])) {
+			    i++;
+			    j++;
+			}
+		    } else if (!utf_flag && IS_CJK_TTY && !count_gcells &&
+			       is8bits(str[i]) &&
+			       IsNormalChar(str[(i + 1)])) {
+			i++;
+		    }
+		}
+	    }
+	}
+    }
+    return (len);
+}
+
+#undef GetChar
+
+#ifdef USE_SLANG
+#if defined(VMS)
+#define GetChar() ttgetc()
+#elif defined(__DJGPP__)
+#define GetChar() getxkey()	/* HTDos.c */
+#elif defined(__CYGWIN__)
+#define GetChar SLkp_getkey
+#else
+#define GetChar (int)SLang_getkey
+#endif
+#else /* curses */
+#if defined(DJGPP)
+#define GetChar() (djgpp_idle_loop(), wgetch(LYtopwindow()))
+#elif defined(NCURSES_VERSION) && defined(__BEOS__)
+#define GetChar() myGetCharNodelay()
+#elif defined(NCURSES)
+#define GetChar() wgetch(LYtopwindow())
+#endif
+#endif
+
+#ifdef USE_CURSES_NODELAY
+/* PDCurses - until version 2.7 in 2005 - defined ERR as 0, unlike other
+ * versions of curses.  Generally both EOF and ERR are defined as -1's. 
+ * However, there is a special case (see HTCheckForInterrupt()) to handle a
+ * case where no select() function is used in the win32 environment.
+ *
+ * HTCheckForInterrupt() uses nodelay() in this special case to check for
+ * pending input.  That normally returns ERR.  But LYgetch_for() checks the
+ * return value of this function for EOF (to handle some antique runtime
+ * libraries which did not set the state for feof/ferror).  Returning a zero
+ * (0) is safer since normally that is not mapped to any commands, and will be
+ * ignored by lynx.
+ */
+static int myGetCharNodelay(void)
+{
+    int c = wgetch(LYwin);
+
+    if (c == -1)
+	c = 0;
+
+    return c;
+}
+#else
+#define myGetCharNodelay() wgetch(LYwin)
+#endif
+
+#if !defined(GetChar) && defined(PDCURSES) && defined(PDC_BUILD) && PDC_BUILD >= 2401
+/* PDCurses sends back key-modifiers that we don't use, but would waste time
+ * upon, e.g., repainting the status line
+ */
+static int myGetChar(void)
+{
+    int c;
+    BOOL done = FALSE;
+
+    do {
+	switch (c = myGetCharNodelay()) {
+	case KEY_SHIFT_L:
+	case KEY_SHIFT_R:
+	case KEY_CONTROL_L:
+	case KEY_CONTROL_R:
+	case KEY_ALT_L:
+	case KEY_ALT_R:
+	case KEY_RESIZE:
+	    break;
+	default:
+	    done = TRUE;
+	    break;
+	}
+    } while (!done);
+
+    return c;
+}
+#define GetChar() myGetChar()
+#endif
+
+#if !defined(GetChar) && defined(SNAKE)
+#define GetChar() wgetch(LYwin)
+#endif
+
+#if !defined(GetChar) && defined(VMS)
+#define GetChar() ttgetc()
+#endif
+
+#if !defined(GetChar)
+#ifdef HAVE_KEYPAD
+#define GetChar() getch()
+#else
+#ifndef USE_GETCHAR
+#define USE_GETCHAR
+#endif /* !USE_GETCHAR */
+#define GetChar() getchar()	/* used to be "getc(stdin)" and "getch()" */
+#endif /* HAVE_KEYPAD */
+#endif /* !defined(GetChar) */
+
+#if defined(USE_SLANG) && defined(USE_MOUSE)
+static int sl_parse_mouse_event(int *x, int *y, int *button)
+{
+    /* "ESC [ M" has already been processed.  There more characters are
+     * expected:  BUTTON X Y
+     */
+    *button = SLang_getkey();
+    switch (*button) {
+    case 040:			/* left button */
+    case 041:			/* middle button */
+    case 042:			/* right button */
+	*button -= 040;
+	break;
+
+    default:			/* Hmmm.... */
+	SLang_flush_input();
+	return -1;
+    }
+
+    *x = SLang_getkey();
+    if (*x == CH_ESC)		/* Undo 7-bit replace for large x - kw */
+	*x = SLang_getkey() + 64 - 33;
+    else
+	*x -= 33;
+    *y = SLang_getkey();
+    if (*y == CH_ESC)		/* Undo 7-bit replace for large y - kw */
+	*y = SLang_getkey() + 64 - 33;
+    else
+	*y -= 33;
+    return 0;
+}
+
+static int sl_read_mouse_event(int code)
+{
+    int mouse_x, mouse_y, button;
+
+    mouse_link = -1;
+    if (-1 != sl_parse_mouse_event(&mouse_x, &mouse_y, &button)) {
+	if (button == 0)	/* left */
+	    return set_clicked_link(mouse_x, mouse_y, FOR_PANEL, 1);
+
+	if (button == 1)	/* middle */
+	    return LYReverseKeymap(LYK_VIEW_BOOKMARK);
+
+	if (button == 2)	/* right */
+	{
+	    /* Right button: go back to prev document.
+	     * The problem is that we need to determine
+	     * what to return to achieve this.
+	     */
+	    return LYReverseKeymap(LYK_PREV_DOC);
+	}
+    }
+    if (code == FOR_INPUT || code == FOR_PROMPT)
+	return DO_NOTHING;
+    else
+	return -1;
+}
+#endif /* USE_SLANG and USE_MOUSE */
+
+static BOOLEAN csi_is_csi = TRUE;
+void ena_csi(BOOLEAN flag)
+{
+    csi_is_csi = flag;
+}
+
+#if defined(USE_KEYMAPS)
+
+#ifdef USE_SLANG
+#define define_key(string, code) \
+	SLkm_define_keysym ((char*)(string), code, Keymap_List)
+#if SLANG_VERSION < 20000
+#define expand_substring(dst, first, last, final) \
+ 	(SLexpand_escaped_string(dst, (char *)first, (char *)last), 1)
+static int SLang_get_error(void)
+{
+    return SLang_Error;
+}
+#else
+int LY_Slang_UTF8_Mode = 0;
+
+#define expand_substring(dst, first, last, final) \
+	(SLexpand_escaped_string(dst, (char *)first, (char *)last, LY_Slang_UTF8_Mode), 1)
+#endif
+
+static SLKeyMap_List_Type *Keymap_List;
+
+/* This value should be larger than anything in LYStrings.h */
+#define MOUSE_KEYSYM 0x0400
+#endif
+
+#define SQUOTE '\''
+#define DQUOTE '"'
+#define ESCAPE '\\'
+#define LPAREN '('
+#define RPAREN ')'
+
+/*
+ * For ncurses, we use the predefined keysyms, since that lets us also reuse
+ * the CSI logic and other special cases for VMS, NCSA telnet, etc.
+ */
+#ifdef USE_SLANG
+# ifdef VMS
+#  define EXTERN_KEY(string,string1,lynx,curses) {string,lynx}
+# else
+#  define EXTERN_KEY(string,string1,lynx,curses) {string,lynx},{string1,lynx}
+# endif
+# define INTERN_KEY(string,lynx,curses)          {string,lynx}
+#else
+#define INTERN_KEY(string,lynx,curses)           {string,curses}
+#define EXTERN_KEY(string,string1,lynx,curses)   {string,curses}
+#endif
+
+typedef struct {
+    const char *string;
+    int value;
+} Keysym_String_List;
+/* *INDENT-OFF* */
+static Keysym_String_List Keysym_Strings [] =
+{
+    INTERN_KEY( "UPARROW",	UPARROW,	KEY_UP ),
+    INTERN_KEY( "DNARROW",	DNARROW,	KEY_DOWN ),
+    INTERN_KEY( "RTARROW",	RTARROW,	KEY_RIGHT ),
+    INTERN_KEY( "LTARROW",	LTARROW,	KEY_LEFT ),
+    INTERN_KEY( "PGDOWN",	PGDOWN,		KEY_NPAGE ),
+    INTERN_KEY( "PGUP",		PGUP,		KEY_PPAGE ),
+    INTERN_KEY( "HOME",		HOME,		KEY_HOME ),
+    INTERN_KEY( "END",		END_KEY,	KEY_END ),
+    INTERN_KEY( "F1",		F1,		KEY_F(1) ),
+    INTERN_KEY( "DO_KEY",	DO_KEY,		KEY_F(16) ),
+    INTERN_KEY( "FIND_KEY",	FIND_KEY,	KEY_FIND ),
+    INTERN_KEY( "SELECT_KEY",	SELECT_KEY,	KEY_SELECT ),
+    INTERN_KEY( "INSERT_KEY",	INSERT_KEY,	KEY_IC ),
+    INTERN_KEY( "REMOVE_KEY",	REMOVE_KEY,	KEY_DC ),
+    INTERN_KEY( "DO_NOTHING",	DO_NOTHING,	DO_NOTHING|LKC_ISLKC ),
+    INTERN_KEY( NULL,		-1,		ERR )
+};
+/* *INDENT-ON* */
+
+#ifdef NCURSES_VERSION
+/*
+ * Ncurses stores the termcap/terminfo names in arrays sorted to match the
+ * array of strings in the TERMTYPE struct.
+ */
+static int lookup_tiname(char *name, NCURSES_CONST char *const *names)
+{
+    int code;
+
+    for (code = 0; names[code] != 0; code++)
+	if (!strcmp(names[code], name))
+	    return code;
+    return -1;
+}
+
+static const char *expand_tiname(const char *first, size_t len, char **result, char *final)
+{
+    char name[BUFSIZ];
+    int code;
+
+    strncpy(name, first, len);
+    name[len] = '\0';
+    if ((code = lookup_tiname(name, strnames)) >= 0
+	|| (code = lookup_tiname(name, strfnames)) >= 0) {
+	if (cur_term->type.Strings[code] != 0) {
+	    LYstrncpy(*result, cur_term->type.Strings[code], final - *result);
+	    (*result) += strlen(*result);
+	}
+    }
+    return first + len;
+}
+
+static const char *expand_tichar(const char *first, char **result, char *final)
+{
+    int ch;
+    int limit = 0;
+    int radix = 0;
+    int value = 0;
+    const char *name = 0;
+
+    switch (ch = *first++) {
+    case 'E':
+    case 'e':
+	value = 27;
+	break;
+    case 'a':
+	name = "bel";
+	break;
+    case 'b':
+	value = '\b';
+	break;
+    case 'f':
+	value = '\f';
+	break;
+    case 'n':
+	value = '\n';
+	break;
+    case 'r':
+	value = '\r';
+	break;
+    case 't':
+	value = '\t';
+	break;
+    case 'v':
+	value = '\v';
+	break;
+    case 'd':
+	radix = 10;
+	limit = 3;
+	break;
+    case 'x':
+	radix = 16;
+	limit = 2;
+	break;
+    default:
+	if (isdigit(ch)) {
+	    radix = 8;
+	    limit = 3;
+	    first--;
+	} else {
+	    value = *first;
+	}
+	break;
+    }
+
+    if (radix != 0) {
+	char *last = 0;
+	char tmp[80];
+
+	LYstrncpy(tmp, first, limit);
+	value = strtol(tmp, &last, radix);
+	if (last != 0 && last != tmp)
+	    first += (last - tmp);
+    }
+
+    if (name != 0) {
+	(void) expand_tiname(name, strlen(name), result, final);
+    } else {
+	**result = (char) value;
+	(*result) += 1;
+    }
+
+    return first;
+}
+
+static BOOLEAN expand_substring(char *dst,
+				const char *first,
+				const char *last,
+				char *final)
+{
+    int ch;
+
+    while (first < last) {
+	switch (ch = *first++) {
+	case ESCAPE:
+	    first = expand_tichar(first, &dst, final);
+	    break;
+	case '^':
+	    ch = *first++;
+	    if (ch == LPAREN) {
+		const char *s = strchr(first, RPAREN);
+		char *was = dst;
+
+		if (s == 0)
+		    s = first + strlen(first);
+		first = expand_tiname(first, (unsigned) (s - first), &dst, final);
+		if (dst == was)
+		    return FALSE;
+		if (*first)
+		    first++;
+	    } else if (ch == '?') {	/* ASCII delete? */
+		*dst++ = 127;
+	    } else if ((ch & 0x3f) < 0x20) {	/* ASCII control char? */
+		*dst++ = (char) (ch & 0x1f);
+	    } else {
+		*dst++ = '^';
+		first--;	/* not legal... */
+	    }
+	    break;
+	case 0:		/* convert nulls for terminfo */
+	    ch = 0200;
+	    /* FALLTHRU */
+	default:
+	    *dst++ = (char) ch;
+	    break;
+	}
+    }
+    *dst = '\0';
+    return TRUE;
+}
+#endif
+
+static void unescaped_char(const char *parse, int *keysym)
+{
+    size_t len = strlen(parse);
+    char buf[BUFSIZ];
+
+    if (len >= 3) {
+	(void) expand_substring(buf,
+				parse + 1,
+				parse + len - 1,
+				buf + sizeof(buf) - 1);
+	if (strlen(buf) == 1)
+	    *keysym = *buf;
+    }
+}
+
+static BOOLEAN unescape_string(char *src, char *dst, char *final)
+{
+    BOOLEAN ok = FALSE;
+
+    if (*src == SQUOTE) {
+	int keysym = -1;
+
+	unescaped_char(src, &keysym);
+	if (keysym >= 0) {
+	    dst[0] = (char) keysym;
+	    dst[1] = '\0';
+	    ok = TRUE;
+	}
+    } else if (*src == DQUOTE) {
+	ok = expand_substring(dst, src + 1, src + strlen(src) - 1, final);
+	(void) final;
+    }
+    return ok;
+}
+
+int map_string_to_keysym(const char *str, int *keysym)
+{
+    int modifier = 0;
+
+    *keysym = -1;
+
+    if (strncasecomp(str, "LAC:", 4) == 0) {
+	char *other = strchr(str + 4, ':');
+
+	if (other) {
+	    int othersym = lecname_to_lec(other + 1);
+	    char buf[BUFSIZ];
+
+	    if (othersym >= 0 && other - str - 4 < BUFSIZ) {
+		strncpy(buf, str + 4, (unsigned) (other - str - 4));
+		buf[other - str - 4] = '\0';
+		*keysym = lacname_to_lac(buf);
+		if (*keysym >= 0) {
+		    *keysym = LACLEC_TO_LKC0(*keysym, othersym);
+		    return (*keysym);
+		}
+	    }
+	}
+	*keysym = lacname_to_lac(str + 4);
+	if (*keysym >= 0) {
+	    *keysym = LAC_TO_LKC0(*keysym);
+	    return (*keysym);
+	}
+    }
+    if (strncasecomp(str, "Meta-", 5) == 0) {
+	str += 5;
+	modifier = LKC_MOD2;
+	if (*str) {
+	    size_t len = strlen(str);
+
+	    if (len == 1) {
+		return (*keysym = (UCH(str[0])) | modifier);
+	    } else if (len == 2 && str[0] == '^' &&
+		       (isalpha(UCH(str[1])) ||
+			(TOASCII(str[1]) >= '@' && TOASCII(str[1]) <= '_'))) {
+		return (*keysym = FROMASCII(UCH(str[1] & 0x1f)) | modifier);
+	    } else if (len == 2 && str[0] == '^' &&
+		       str[1] == '?') {
+		return (*keysym = CH_DEL | modifier);
+	    }
+	    if (*str == '^' || *str == '\\') {
+		char buf[BUFSIZ];
+
+		(void) expand_substring(buf,
+					str,
+					str + HTMIN(len, 28),
+					buf + sizeof(buf) - 1);
+		if (strlen(buf) <= 1)
+		    return (*keysym = (UCH(buf[0])) | modifier);
+	    }
+	}
+    }
+    if (*str == SQUOTE) {
+	unescaped_char(str, keysym);
+    } else if (isdigit(UCH(*str))) {
+	char *tmp;
+	long value = strtol(str, &tmp, 0);
+
+	if (!isalnum(UCH(*tmp))) {
+	    *keysym = value;
+#ifndef USE_SLANG
+	    if (*keysym > 255)
+		*keysym |= LKC_ISLKC;	/* caller should remove this flag - kw */
+#endif
+	}
+    } else {
+	Keysym_String_List *k;
+
+	k = Keysym_Strings;
+	while (k->string != NULL) {
+	    if (0 == strcmp(k->string, str)) {
+		*keysym = k->value;
+		break;
+	    }
+	    k++;
+	}
+    }
+
+    if (*keysym >= 0)
+	*keysym |= modifier;
+    return (*keysym);
+}
+
+/*
+ * Starting at a nonblank character, skip over a token, counting quoted and
+ * escaped characters.
+ */
+static char *skip_keysym(char *parse)
+{
+    int quoted = 0;
+    int escaped = 0;
+
+    while (*parse) {
+	if (escaped) {
+	    escaped = 0;
+	} else if (quoted) {
+	    if (*parse == ESCAPE) {
+		escaped = 1;
+	    } else if (*parse == quoted) {
+		quoted = 0;
+	    }
+	} else if (*parse == ESCAPE) {
+	    escaped = 1;
+	} else if (*parse == DQUOTE || *parse == SQUOTE) {
+	    quoted = *parse;
+	} else if (isspace(UCH(*parse))) {
+	    break;
+	}
+	parse++;
+    }
+    return (quoted || escaped) ? 0 : parse;
+}
+
+/*
+ * The first token is the string to define, the second is the name (of the
+ * keysym) to define it to.
+ */
+#define MY_TRACE(p) CTRACE2(TRACE_CFG, p)
+
+static int setkey_cmd(char *parse)
+{
+    char *s, *t;
+    int keysym;
+    char buf[BUFSIZ];
+
+    MY_TRACE((tfp, "KEYMAP(PA): in=%s", parse));	/* \n-terminated */
+    if ((s = skip_keysym(parse)) != 0) {
+	if (isspace(UCH(*s))) {
+	    *s++ = '\0';
+	    s = LYSkipBlanks(s);
+	    if ((t = skip_keysym(s)) == 0) {
+		MY_TRACE((tfp, "KEYMAP(SKIP) no key expansion found\n"));
+		return -1;
+	    }
+	    if (t != s)
+		*t = '\0';
+	    if (map_string_to_keysym(s, &keysym) >= 0) {
+		if (!unescape_string(parse, buf, buf + sizeof(buf) - 1)) {
+		    MY_TRACE((tfp, "KEYMAP(SKIP) could unescape key\n"));
+		    return 0;	/* Trace the failure and continue. */
+		}
+		if (LYTraceLogFP == 0) {
+		    MY_TRACE((tfp, "KEYMAP(DEF) keysym=%#x\n", keysym));
+		} else {
+		    MY_TRACE((tfp, "KEYMAP(DEF) keysym=%#x, seq='%s'\n",
+			      keysym, buf));
+		}
+		return define_key(buf, keysym);
+	    } else {
+		MY_TRACE((tfp, "KEYMAP(SKIP) could not map to keysym\n"));
+	    }
+	} else {
+	    MY_TRACE((tfp, "KEYMAP(SKIP) junk after key description: '%s'\n", s));
+	}
+    } else {
+	MY_TRACE((tfp, "KEYMAP(SKIP) no key description\n"));
+    }
+    return -1;
+}
+#undef MY_TRACE
+
+static int unsetkey_cmd(char *parse)
+{
+    char *s = skip_keysym(parse);
+
+    if (s != parse) {
+	*s = '\0';
+#ifdef NCURSES_VERSION
+	/*
+	 * This won't work with Slang.  Remove the definition for the given
+	 * keysym.
+	 */
+	{
+	    int keysym;
+
+	    if (map_string_to_keysym(parse, &keysym) >= 0)
+		define_key((char *) 0, keysym);
+	}
+#endif
+#ifdef USE_SLANG
+	/* Slang implements this, for undefining the string which is associated
+	 * with a keysym (the reverse of what we normally want, but may
+	 * occasionally find useful).
+	 */
+	SLang_undefine_key(parse, Keymap_List);
+	if (SLang_get_error())
+	    return -1;
+#endif
+    }
+    return 0;
+}
+
+#ifdef FNAMES_8_3
+#define FNAME_LYNX_KEYMAPS "_lynxkey.map"
+#else
+#define FNAME_LYNX_KEYMAPS ".lynx-keymaps"
+#endif /* FNAMES_8_3 */
+
+static int read_keymap_file(void)
+{
+    /* *INDENT-OFF* */
+    static struct {
+	const char *name;
+	int (*func) (char *s);
+    } table[] = {
+	{ "setkey",   setkey_cmd },
+	{ "unsetkey", unsetkey_cmd },
+    };
+    /* *INDENT-ON* */
+
+    char *line = NULL;
+    FILE *fp;
+    char file[LY_MAXPATH];
+    int linenum;
+    size_t n;
+
+    LYAddPathToHome(file, sizeof(file), FNAME_LYNX_KEYMAPS);
+
+    if ((fp = fopen(file, "r")) == 0)
+	return 0;
+
+    linenum = 0;
+    while (LYSafeGets(&line, fp) != 0) {
+	char *s = LYSkipBlanks(line);
+
+	linenum++;
+
+	if ((*s == 0) || (*s == '#'))
+	    continue;
+
+	for (n = 0; n < TABLESIZE(table); n++) {
+	    size_t len = strlen(table[n].name);
+
+	    if (strlen(s) > len && !strncmp(s, table[n].name, len)
+		&& (*(table[n].func)) (LYSkipBlanks(s + len)) < 0)
+		fprintf(stderr, FAILED_READING_KEYMAP, linenum, file);
+	}
+    }
+    FREE(line);
+    LYCloseInput(fp);
+    return 0;
+}
+
+static void setup_vtXXX_keymap(void)
+{
+    /* *INDENT-OFF* */
+    static Keysym_String_List table[] = {
+	INTERN_KEY( "\033[A",	UPARROW,	KEY_UP ),
+	INTERN_KEY( "\033OA",	UPARROW,	KEY_UP ),
+	INTERN_KEY( "\033[B",	DNARROW,	KEY_DOWN ),
+	INTERN_KEY( "\033OB",	DNARROW,	KEY_DOWN ),
+	INTERN_KEY( "\033[C",	RTARROW,	KEY_RIGHT ),
+	INTERN_KEY( "\033OC",	RTARROW,	KEY_RIGHT ),
+	INTERN_KEY( "\033[D",	LTARROW,	KEY_LEFT ),
+	INTERN_KEY( "\033OD",	LTARROW,	KEY_LEFT ),
+	INTERN_KEY( "\033[1~",	FIND_KEY,	KEY_FIND ),
+	INTERN_KEY( "\033[2~",	INSERT_KEY,	KEY_IC ),
+	INTERN_KEY( "\033[3~",	REMOVE_KEY,	KEY_DC ),
+	INTERN_KEY( "\033[4~",	SELECT_KEY,	KEY_SELECT ),
+	INTERN_KEY( "\033[5~",	PGUP,		KEY_PPAGE ),
+	INTERN_KEY( "\033[6~",	PGDOWN,		KEY_NPAGE ),
+	INTERN_KEY( "\033[7~",	HOME,		KEY_HOME),
+	INTERN_KEY( "\033[8~",	END_KEY,	KEY_END ),
+	INTERN_KEY( "\033[11~",	F1,		KEY_F(1) ),
+	INTERN_KEY( "\033[28~",	F1,		KEY_F(1) ),
+	INTERN_KEY( "\033OP",	F1,		KEY_F(1) ),
+	INTERN_KEY( "\033[OP",	F1,		KEY_F(1) ),
+	INTERN_KEY( "\033[29~",	DO_KEY,		KEY_F(16) ),
+#if defined(USE_SLANG) && (defined(__WIN32__) || defined(__MINGW32__))
+	INTERN_KEY( "\xE0H",	UPARROW,	KEY_UP ),
+	INTERN_KEY( "\xE0P",	DNARROW,	KEY_DOWN ),
+	INTERN_KEY( "\xE0M",	RTARROW,	KEY_RIGHT ),
+	INTERN_KEY( "\xE0K",	LTARROW,	KEY_LEFT ),
+	INTERN_KEY( "\xE0R",	INSERT_KEY,	KEY_IC ),
+	INTERN_KEY( "\xE0S",	REMOVE_KEY,	KEY_DC ),
+	INTERN_KEY( "\xE0I",	PGUP,		KEY_PPAGE ),
+	INTERN_KEY( "\xE0Q",	PGDOWN,		KEY_NPAGE ),
+	INTERN_KEY( "\xE0G",	HOME,		KEY_HOME),
+	INTERN_KEY( "\xE0O",	END_KEY,	KEY_END ),
+#endif
+#if defined(USE_SLANG) && !defined(VMS)
+	INTERN_KEY(	"^(ku)", UPARROW,	KEY_UP ),
+	INTERN_KEY(	"^(kd)", DNARROW,	KEY_DOWN ),
+	INTERN_KEY(	"^(kr)", RTARROW,	KEY_RIGHT ),
+	INTERN_KEY(	"^(kl)", LTARROW,	KEY_LEFT ),
+	INTERN_KEY(	"^(@0)", FIND_KEY,	KEY_FIND ),
+	INTERN_KEY(	"^(kI)", INSERT_KEY,	KEY_IC ),
+	INTERN_KEY(	"^(kD)", REMOVE_KEY,	KEY_DC ),
+	INTERN_KEY(	"^(*6)", SELECT_KEY,	KEY_SELECT ),
+	INTERN_KEY(	"^(kP)", PGUP,		KEY_PPAGE ),
+	INTERN_KEY(	"^(kN)", PGDOWN,	KEY_NPAGE ),
+	INTERN_KEY(	"^(@7)", END_KEY,	KEY_END ),
+	INTERN_KEY(	"^(kh)", HOME,		KEY_HOME),
+	INTERN_KEY(	"^(k1)", F1,		KEY_F(1) ),
+	INTERN_KEY(	"^(F6)", DO_KEY,	KEY_F(16) ),
+#endif /* SLANG && !VMS */
+    };
+    /* *INDENT-ON* */
+
+    size_t n;
+
+    for (n = 0; n < TABLESIZE(table); n++)
+	define_key(table[n].string, table[n].value);
+}
+
+int lynx_initialize_keymaps(void)
+{
+#ifdef USE_SLANG
+    int i;
+    char keybuf[2];
+
+    /* The escape sequences may contain embedded termcap strings.  Make
+     * sure the library is initialized for that.
+     */
+    SLtt_get_terminfo();
+
+    if (NULL == (Keymap_List = SLang_create_keymap("Lynx", NULL)))
+	return -1;
+
+    keybuf[1] = 0;
+    for (i = 1; i < 256; i++) {
+	keybuf[0] = (char) i;
+	define_key(keybuf, i);
+    }
+
+    setup_vtXXX_keymap();
+    define_key("\033[M", MOUSE_KEYSYM);
+
+    if (SLang_get_error())
+	SLang_exit_error("Unable to initialize keymaps");
+#else
+    setup_vtXXX_keymap();
+#endif
+    return read_keymap_file();
+}
+
+#endif /* USE_KEYMAPS */
+
+#if defined(USE_MOUSE) && (defined(NCURSES))
+static int LYmouse_menu(int x, int y, int atlink, int code)
+{
+#define ENT_ONLY_DOC	1
+#define ENT_ONLY_LINK	2
+    /* *INDENT-OFF* */
+    static const struct {
+	const char *txt;
+	int  action;
+	unsigned int  flag;
+    } possible_entries[] = {
+	{"Quit",			LYK_ABORT,		ENT_ONLY_DOC},
+	{"Home page",			LYK_MAIN_MENU,		ENT_ONLY_DOC},
+	{"Previous document",		LYK_PREV_DOC,		ENT_ONLY_DOC},
+	{"Beginning of document",	LYK_HOME,		ENT_ONLY_DOC},
+	{"Page up",			LYK_PREV_PAGE,		ENT_ONLY_DOC},
+	{"Half page up",		LYK_UP_HALF,		ENT_ONLY_DOC},
+	{"Two lines up",		LYK_UP_TWO,		ENT_ONLY_DOC},
+	{"History",			LYK_HISTORY,		ENT_ONLY_DOC},
+	{"Help",			LYK_HELP,		0},
+	{"Do nothing (refresh)",	LYK_REFRESH,		0},
+	{"Load again",			LYK_RELOAD,		ENT_ONLY_DOC},
+	{"Edit Doc URL and load",	LYK_ECGOTO,		ENT_ONLY_DOC},
+	{"Edit Link URL and load",	LYK_ELGOTO,		ENT_ONLY_LINK},
+	{"Show info",			LYK_INFO,		0},
+	{"Search",			LYK_WHEREIS,		ENT_ONLY_DOC},
+	{"Print",			LYK_PRINT,		ENT_ONLY_DOC},
+	{"Two lines down",		LYK_DOWN_TWO,		ENT_ONLY_DOC},
+	{"Half page down",		LYK_DOWN_HALF,		ENT_ONLY_DOC},
+	{"Page down",			LYK_NEXT_PAGE,		ENT_ONLY_DOC},
+	{"End of document",		LYK_END,		ENT_ONLY_DOC},
+	{"Bookmarks",			LYK_VIEW_BOOKMARK,	ENT_ONLY_DOC},
+	{"Cookie jar",			LYK_COOKIE_JAR,		ENT_ONLY_DOC},
+#ifdef USE_CACHEJAR
+	{"Cache jar",			LYK_CACHE_JAR,		ENT_ONLY_DOC},
+#endif
+	{"Search index",		LYK_INDEX_SEARCH,	ENT_ONLY_DOC},
+	{"Set Options",			LYK_OPTIONS,		ENT_ONLY_DOC},
+	{"Activate this link",		LYK_SUBMIT,		ENT_ONLY_LINK},
+	{"Download",			LYK_DOWNLOAD,		ENT_ONLY_LINK}
+    };
+    /* *INDENT-ON* */
+
+#define TOTAL_MENUENTRIES	TABLESIZE(possible_entries)
+    const char *choices[TOTAL_MENUENTRIES + 1];
+    int actions[TOTAL_MENUENTRIES];
+
+    int c, c1, retlac, filter_out = (atlink ? ENT_ONLY_DOC : ENT_ONLY_LINK);
+
+    c = c1 = 0;
+    while (c < (int) TOTAL_MENUENTRIES) {
+	if (!(possible_entries[c].flag & filter_out)) {
+	    choices[c1] = possible_entries[c].txt;
+	    actions[c1++] = possible_entries[c].action;
+	}
+	c++;
+    }
+    choices[c1] = NULL;
+
+    /* Somehow the mouse is over the number instead of being over the
+       name, so we decrease x. */
+    c = LYChoosePopup((atlink ? 2 : 10) - 1, y, (x > 5 ? x - 5 : 1),
+		      choices, c1, FALSE, TRUE);
+
+    /*
+     * LYhandlePopupList() wasn't really meant to be used outside of old-style
+     * Options menu processing.  One result of mis-using it here is that we
+     * have to deal with side-effects regarding SIGINT signal handler and the
+     * term_options global variable.  - kw
+     */
+    if (term_options) {
+	retlac = LYK_DO_NOTHING;
+	term_options = FALSE;
+    } else {
+	retlac = actions[c];
+    }
+
+    if (code == FOR_INPUT && mouse_link == -1) {
+	switch (retlac) {
+	case LYK_ABORT:
+	    retlac = LYK_QUIT;	/* a bit softer... */
+	    /* fall through */
+	case LYK_MAIN_MENU:
+	case LYK_PREV_DOC:
+	case LYK_HOME:
+	case LYK_PREV_PAGE:
+	case LYK_UP_HALF:
+	case LYK_UP_TWO:
+	case LYK_HISTORY:
+	case LYK_HELP:
+/*	    case LYK_REFRESH:*/
+	case LYK_RELOAD:
+	case LYK_ECGOTO:
+	case LYK_INFO:
+	case LYK_WHEREIS:
+	case LYK_PRINT:
+	case LYK_DOWN_TWO:
+	case LYK_DOWN_HALF:
+	case LYK_NEXT_PAGE:
+	case LYK_END:
+	case LYK_VIEW_BOOKMARK:
+	case LYK_COOKIE_JAR:
+#ifdef USE_CACHEJAR
+	case LYK_CACHE_JAR:
+#endif
+	case LYK_INDEX_SEARCH:
+	case LYK_OPTIONS:
+	    mouse_link = -3;	/* so LYgetch_for() passes it on - kw */
+	}
+    }
+    if (retlac == LYK_DO_NOTHING ||
+	retlac == LYK_REFRESH) {
+	mouse_link = -1;	/* mainloop should not change cur link - kw */
+    }
+    if (code == FOR_INPUT && retlac == LYK_DO_NOTHING) {
+	repaint_main_statusline(FOR_INPUT);
+    }
+    return retlac;
+}
+#endif /* USE_MOUSE && (NCURSES || PDCURSES) */
+
+#if defined(USE_KEYMAPS) && defined(USE_SLANG)
+/************************************************************************/
+
+static int current_sl_modifier = 0;
+
+/* We cannot guarantee the type for 'GetChar', and should not use a cast. */
+static int myGetChar(void)
+{
+    int i = GetChar();
+
+    if (i == 0)			/* trick to get NUL char through - kw */
+	current_sl_modifier = LKC_ISLKC;
+    return i;
+}
+
+static int LYgetch_for(int code)
+{
+    SLang_Key_Type *key;
+    int keysym;
+
+    current_sl_modifier = 0;
+
+    key = SLang_do_key(Keymap_List, myGetChar);
+    if ((key == NULL) || (key->type != SLKEY_F_KEYSYM)) {
+#if defined(__WIN32__) || defined(__MINGW32__)
+	if ((key == NULL) && (current_sl_modifier == LKC_ISLKC)) {
+	    key = SLang_do_key(Keymap_List, myGetChar);
+	    keysym = key->f.keysym;
+	    switch (keysym) {
+	    case 'H':
+		keysym = UPARROW;
+		break;
+	    case 'P':
+		keysym = DNARROW;
+		break;
+	    case 'M':
+		keysym = RTARROW;
+		break;
+	    case 'K':
+		keysym = LTARROW;
+		break;
+	    case 'R':
+		keysym = INSERT_KEY;
+		break;
+	    case 'S':
+		keysym = REMOVE_KEY;
+		break;
+	    case 'I':
+		keysym = PGUP;
+		break;
+	    case 'Q':
+		keysym = PGDOWN;
+		break;
+	    case 'G':
+		keysym = HOME;
+		break;
+	    case 'O':
+		keysym = END_KEY;
+		break;
+	    case ';':
+		keysym = F1;
+		break;
+	    }
+	    return (keysym);
+	}
+#endif
+
+	return (current_sl_modifier ? 0 : DO_NOTHING);
+    }
+
+    keysym = key->f.keysym;
+
+#if defined (USE_MOUSE)
+    if (keysym == MOUSE_KEYSYM)
+	return sl_read_mouse_event(code);
+#endif
+
+    if (keysym < 0)
+	return 0;
+
+    if (keysym & (LKC_ISLECLAC | LKC_ISLAC))
+	return (keysym);
+
+    current_sl_modifier = 0;
+    if (LKC_HAS_ESC_MOD(keysym)) {
+	current_sl_modifier = LKC_MOD2;
+	keysym &= LKC_MASK;
+    }
+
+    if (keysym + 1 >= KEYMAP_SIZE)
+	return 0;
+
+    return (keysym | current_sl_modifier);
+}
+
+/************************************************************************/
+#else /* NOT  defined(USE_KEYMAPS) && defined(USE_SLANG) */
+
+/*
+ * LYgetch() translates some escape sequences and may fake noecho.
+ */
+#define found_CSI(first,second) ((second) == '[' || (first) == 155)
+#define found_TLD(value)	((value) == '~')
+
+static int LYgetch_for(int code)
+{
+    int a, b, c, d = -1;
+    int current_modifier = 0;
+    BOOLEAN done_esc = FALSE;
+
+    (void) code;
+
+    have_levent = 0;
+
+  re_read:
+#if !defined(UCX) || !defined(VAXC)	/* errno not modifiable ? */
+    if (errno == EINTR)
+	set_errno(0);		/* reset - kw */
+#endif /* UCX && VAXC */
+#ifndef USE_SLANG
+    clearerr(stdin);		/* needed here for ultrix and SOCKETSHR, but why? - FM */
+#endif /* !USE_SLANG */
+#if !defined(USE_SLANG) || defined(VMS) || defined(DJGPP_KEYHANDLER)
+    c = GetChar();
+    lynx_nl2crlf(FALSE);
+#else
+    if (LYCursesON) {
+	c = GetChar();
+	lynx_nl2crlf(FALSE);
+    } else {
+	c = getchar();
+	if (c == EOF && errno == EINTR)		/* Ctrl-Z causes EINTR in getchar() */
+	    clearerr(stdin);
+	if (feof(stdin) || ferror(stdin) || c == EOF) {
+#ifdef IGNORE_CTRL_C
+	    if (sigint)
+		sigint = FALSE;
+#endif /* IGNORE_CTRL_C */
+	    CTRACE((tfp, "GETCH: Translate ^C to ^G.\n"));
+	    return (LYCharINTERRUPT2);	/* use ^G to cancel whatever called us. */
+	}
+    }
+#endif /* !USE_SLANG || VMS */
+
+    CTRACE((tfp, "GETCH: Got %#x.\n", c));
+#ifdef MISC_EXP
+    if (LYNoZapKey > 1 && errno != EINTR &&
+	(c == EOF
+#ifdef USE_SLANG
+	 || (c == 0xFFFF)
+#endif
+	)) {
+
+	CTRACE((tfp,
+		"nozap: Got EOF, curses %s, stdin is %p, LYNoZapKey reduced from %d to 0.\n",
+		LYCursesON ? "on" : "off", (void *) stdin, LYNoZapKey));
+	LYNoZapKey = 0;		/* 2 -> 0 */
+	if (LYReopenInput() > 0) {
+	    if (LYCursesON) {
+		stop_curses();
+		start_curses();
+		LYrefresh();
+	    }
+	    goto re_read;
+	}
+    }
+#endif /* MISC_EXP */
+
+#ifdef USE_GETCHAR
+    if (c == EOF && errno == EINTR)	/* Ctrl-Z causes EINTR in getchar() */
+	goto re_read;
+#else
+    if (c == EOF && errno == EINTR) {
+
+#if defined(HAVE_SIZECHANGE) || defined(USE_SLANG)
+	CTRACE((tfp, "Got EOF with EINTR, recent_sizechange so far is %d\n",
+		recent_sizechange));
+	if (!recent_sizechange) {	/* not yet detected by ourselves */
+	    size_change(0);
+	    CTRACE((tfp, "Now recent_sizechange is %d\n", recent_sizechange));
+	}
+#else /* HAVE_SIZECHANGE || USE_SLANG */
+	CTRACE((tfp, "Got EOF with EINTR, recent_sizechange is %d\n",
+		recent_sizechange));
+#endif /* HAVE_SIZECHANGE || USE_SLANG */
+#if !defined(UCX) || !defined(VAXC)	/* errno not modifiable ? */
+	set_errno(0);		/* reset - kw */
+#endif /* UCX && VAXC */
+	return (DO_NOTHING);
+    }
+#endif /* USE_GETCHAR */
+
+#ifdef USE_SLANG
+    if (c == 0xFFFF && LYCursesON) {
+#ifdef IGNORE_CTRL_C
+	if (sigint) {
+	    sigint = FALSE;
+	    goto re_read;
+	}
+#endif /* IGNORE_CTRL_C */
+	return (LYCharINTERRUPT2);	/* use ^G to cancel whatever called us. */
+    }
+#else /* not USE_SLANG: */
+    if (feof(stdin) || ferror(stdin) || c == EOF) {
+	if (recent_sizechange)
+	    return (LYCharINTERRUPT2);	/* use ^G to cancel whatever called us. */
+#ifdef IGNORE_CTRL_C
+	if (sigint) {
+	    sigint = FALSE;
+	    /* clearerr(stdin);  don't need here if stays above - FM */
+	    goto re_read;
+	}
+#endif /* IGNORE_CTRL_C */
+#if !defined(USE_GETCHAR) && !defined(VMS) && !defined(NCURSES)
+	if (c == ERR && errno == EINTR)		/* may have been handled signal - kw */
+	    goto re_read;
+#endif /* USE_GETCHAR */
+
+	cleanup();
+	exit_immediately(EXIT_SUCCESS);
+    }
+#endif /* USE_SLANG */
+
+    if (!escape_bound
+	&& (c == CH_ESC || (csi_is_csi && c == UCH(CH_ESC_PAR)))) {
+	/* handle escape sequence  S/390 -- gil -- 2024 */
+	done_esc = TRUE;	/* Flag: we did it, not keypad() */
+	b = GetChar();
+
+	if (b == '[' || b == 'O') {
+	    a = GetChar();
+	} else {
+	    a = b;
+	}
+
+	switch (a) {
+	case 'A':
+	    c = UPARROW;
+	    break;
+	case 'B':
+	    c = DNARROW;
+	    break;
+	case 'C':
+	    c = RTARROW;
+	    break;
+	case 'D':
+	    c = LTARROW;
+	    break;
+	case 'q':		/* vt100 application keypad 1 */
+	    c = END_KEY;
+	    break;
+	case 'r':		/* vt100 application keypad 2 */
+	    c = DNARROW;
+	    break;
+	case 's':		/* vt100 application keypad 3 */
+	    c = PGDOWN;
+	    break;
+	case 't':		/* vt100 application keypad 4 */
+	    c = LTARROW;
+	    break;
+	case 'v':		/* vt100 application keypad 6 */
+	    c = RTARROW;
+	    break;
+	case 'w':		/* vt100 application keypad 7 */
+	    c = HOME;
+	    break;
+	case 'x':		/* vt100 application keypad 8 */
+	    c = UPARROW;
+	    break;
+	case 'y':		/* vt100 application keypad 9 */
+	    c = PGUP;
+	    break;
+	case 'M':
+#if defined(USE_SLANG) && defined(USE_MOUSE)
+	    if (found_CSI(c, b)) {
+		c = sl_read_mouse_event(code);
+	    } else
+#endif
+		c = '\n';	/* keypad enter on pc ncsa telnet */
+	    break;
+
+	case 'm':
+#ifdef VMS
+	    if (b != 'O')
+#endif /* VMS */
+		c = '-';	/* keypad on pc ncsa telnet */
+	    break;
+	case 'k':
+	    if (b == 'O')
+		c = '+';	/* keypad + on my xterminal :) */
+	    else
+		done_esc = FALSE;	/* we have another look below - kw */
+	    break;
+	case 'l':
+#ifdef VMS
+	    if (b != 'O')
+#endif /* VMS */
+		c = '+';	/* keypad on pc ncsa telnet */
+	    break;
+	case 'P':
+#ifdef VMS
+	    if (b != 'O')
+#endif /* VMS */
+		c = F1;
+	    break;
+	case 'u':
+#ifdef VMS
+	    if (b != 'O')
+#endif /* VMS */
+		c = F1;		/* macintosh help button */
+	    break;
+	case 'p':
+#ifdef VMS
+	    if (b == 'O')
+#endif /* VMS */
+		c = '0';	/* keypad 0 */
+	    break;
+	case '1':		/* VTxxx  Find  */
+	    if (found_CSI(c, b) && found_TLD(d = GetChar()))
+		c = FIND_KEY;
+	    else
+		done_esc = FALSE;	/* we have another look below - kw */
+	    break;
+	case '2':
+	    if (found_CSI(c, b)) {
+		if (found_TLD(d = GetChar()))	/* VTxxx Insert */
+		    c = INSERT_KEY;
+		else if ((d == '8' ||
+			  d == '9') &&
+			 found_TLD(GetChar())) {
+		    if (d == '8')	/* VTxxx   Help */
+			c = F1;
+		    else if (d == '9')	/* VTxxx    Do  */
+			c = DO_KEY;
+		    d = -1;
+		}
+	    } else
+		done_esc = FALSE;	/* we have another look below - kw */
+	    break;
+	case '3':			     /** VTxxx Delete **/
+	    if (found_CSI(c, b) && found_TLD(d = GetChar()))
+		c = REMOVE_KEY;
+	    else
+		done_esc = FALSE;	/* we have another look below - kw */
+	    break;
+	case '4':			     /** VTxxx Select **/
+	    if (found_CSI(c, b) && found_TLD(d = GetChar()))
+		c = SELECT_KEY;
+	    else
+		done_esc = FALSE;	/* we have another look below - kw */
+	    break;
+	case '5':			     /** VTxxx PrevScreen **/
+	    if (found_CSI(c, b) && found_TLD(d = GetChar()))
+		c = PGUP;
+	    else
+		done_esc = FALSE;	/* we have another look below - kw */
+	    break;
+	case '6':			     /** VTxxx NextScreen **/
+	    if (found_CSI(c, b) && found_TLD(d = GetChar()))
+		c = PGDOWN;
+	    else
+		done_esc = FALSE;	/* we have another look below - kw */
+	    break;
+	case '[':			     /** Linux F1-F5: ^[[[A etc. **/
+	    if (found_CSI(c, b)) {
+		if ((d = GetChar()) == 'A')
+		    c = F1;
+		break;
+	    }
+	    /* FALLTHRU */
+	default:
+	    if (c == CH_ESC && a == b && !found_CSI(c, b)) {
+		current_modifier = LKC_MOD2;
+		c = a;
+		/* We're not yet done if ESC + curses-keysym: */
+		done_esc = (BOOL) ((a & ~0xFF) == 0);
+		break;
+	    }
+	    CTRACE((tfp, "Unknown key sequence: %d:%d:%d\n", c, b, a));
+	    CTRACE_SLEEP(MessageSecs);
+	    break;
+	}
+	if (isdigit(a) && found_CSI(c, b) && d != -1 && !found_TLD(d))
+	    d = GetChar();
+	if (!done_esc && (a & ~0xFF) == 0) {
+	    if (a == b && !found_CSI(c, b) && c == CH_ESC) {
+		current_modifier = LKC_MOD2;
+		c = a;
+		done_esc = TRUE;
+	    } else {
+		done_esc = TRUE;
+	    }
+	}
+    }
+#ifdef USE_KEYMAPS
+    /* Extract a single code if two are merged: */
+    if (c >= 0 && (c & LKC_ISLECLAC)) {
+	if (!(code == FOR_INPUT || code == FOR_PROMPT))
+	    c = LKC2_TO_LKC(c);
+    } else if (c >= 0 && (c & LKC_ISLKC)) {
+	c &= ~LKC_ISLKC;
+	done_esc = TRUE;	/* already a lynxkeycode, skip keypad switches - kw */
+    }
+    if (c >= 0 && LKC_HAS_ESC_MOD(c)) {
+	current_modifier = LKC_MOD2;
+	c &= LKC_MASK;
+    }
+    if (c >= 0 && (c & (LKC_ISLECLAC | LKC_ISLAC))) {
+	done_esc = TRUE;	/* already a lynxactioncode, skip keypad switches - iz */
+    }
+#endif
+    if (done_esc) {
+	/* don't do keypad() switches below, we already got it - kw */
+    } else {
+#ifdef HAVE_KEYPAD
+	/*
+	 * Convert keypad() mode keys into Lynx defined keys.
+	 */
+	switch (c) {
+	case KEY_DOWN:		/* The four arrow keys ... */
+	    c = DNARROW;
+	    break;
+	case KEY_UP:
+	    c = UPARROW;
+	    break;
+	case KEY_LEFT:
+	    c = LTARROW;
+	    break;
+	case KEY_RIGHT:	/* ... */
+	    c = RTARROW;
+	    break;
+#if defined(PDCURSES)		/* for NEC PC-9800 1998/08/30 (Sun) 21:50:35 */
+	case KEY_C2:
+	    c = DNARROW;
+	    break;
+	case KEY_A2:
+	    c = UPARROW;
+	    break;
+	case KEY_B1:
+	    c = LTARROW;
+	    break;
+	case KEY_B3:
+	    c = RTARROW;
+	    break;
+	case PAD0:		/* PC-9800 Ins */
+	    c = INSERT_KEY;
+	    break;
+	case PADSTOP:		/* PC-9800 DEL */
+	    c = REMOVE_KEY;
+	    break;
+#endif /* PDCURSES */
+	case KEY_HOME:		/* Home key (upward+left arrow) */
+	    c = HOME;
+	    break;
+	case KEY_CLEAR:	/* Clear screen */
+	    c = 18;		/* CTRL-R */
+	    break;
+	case KEY_NPAGE:	/* Next page */
+	    c = PGDOWN;
+	    break;
+	case KEY_PPAGE:	/* Previous page */
+	    c = PGUP;
+	    break;
+	case KEY_LL:		/* home down or bottom (lower left) */
+	    c = END_KEY;
+	    break;
+#if defined(KEY_A1) && defined(KEY_C3)
+	    /* The keypad is arranged like this: */
+	    /*    a1    up    a3   */
+	    /*   left   b2  right  */
+	    /*    c1   down   c3   */
+	case KEY_A1:		/* upper left of keypad */
+	    c = HOME;
+	    break;
+	case KEY_A3:		/* upper right of keypad */
+	    c = PGUP;
+	    break;
+	case KEY_B2:		/* center of keypad */
+	    c = DO_NOTHING;
+	    break;
+	case KEY_C1:		/* lower left of keypad */
+	    c = END_KEY;
+	    break;
+	case KEY_C3:		/* lower right of keypad */
+	    c = PGDOWN;
+	    break;
+#endif /* defined(KEY_A1) && defined(KEY_C3) */
+#ifdef KEY_ENTER
+	case KEY_ENTER:	/* enter/return      */
+	    c = '\n';
+	    break;
+#endif /* KEY_ENTER */
+#ifdef PADENTER			/* PDCURSES */
+	case PADENTER:
+	    c = '\n';
+	    break;
+#endif /* PADENTER */
+#ifdef KEY_END
+	case KEY_END:		/* end key           001 */
+	    c = END_KEY;
+	    break;
+#endif /* KEY_END */
+#ifdef KEY_HELP
+	case KEY_HELP:		/* help key          001 */
+	    c = F1;
+	    break;
+#endif /* KEY_HELP */
+#ifdef KEY_BACKSPACE
+	case KEY_BACKSPACE:
+	    c = CH_DEL;		/* backspace key (delete, not Ctrl-H)  S/390 -- gil -- 2041 */
+	    break;
+#endif /* KEY_BACKSPACE */
+	case KEY_F(1):
+	    c = F1;		/* VTxxx Help */
+	    break;
+#if defined(KEY_F) && !defined(__DJGPP__) && !defined(_WINDOWS)
+	case KEY_F(16):
+	    c = DO_KEY;		/* VTxxx Do */
+	    break;
+#endif /* KEY_F */
+#ifdef KEY_REDO
+	case KEY_REDO:		/* VTxxx Do */
+	    c = DO_KEY;
+	    break;
+#endif /* KEY_REDO */
+#ifdef KEY_FIND
+	case KEY_FIND:
+	    c = FIND_KEY;	/* VTxxx Find */
+	    break;
+#endif /* KEY_FIND */
+#ifdef KEY_SELECT
+	case KEY_SELECT:
+	    c = SELECT_KEY;	/* VTxxx Select */
+	    break;
+#endif /* KEY_SELECT */
+#ifdef KEY_IC
+	case KEY_IC:
+	    c = INSERT_KEY;	/* VTxxx Insert */
+	    break;
+#endif /* KEY_IC */
+#ifdef KEY_DC
+	case KEY_DC:
+	    c = REMOVE_KEY;	/* VTxxx Remove */
+	    break;
+#endif /* KEY_DC */
+#ifdef KEY_BTAB
+	case KEY_BTAB:
+	    c = BACKTAB_KEY;	/* Back tab, often Shift-Tab */
+	    break;
+#endif /* KEY_BTAB */
+#ifdef KEY_RESIZE
+	case KEY_RESIZE:	/* size change detected by ncurses */
+#if defined(HAVE_SIZECHANGE) || defined(USE_SLANG)
+	    /* Make call to detect new size, if that may be implemented.
+	     * The call may set recent_sizechange (except for USE_SLANG),
+	     * which will tell mainloop() to refresh. - kw
+	     */
+	    CTRACE((tfp, "Got KEY_RESIZE, recent_sizechange so far is %d\n",
+		    recent_sizechange));
+	    size_change(0);
+	    CTRACE((tfp, "Now recent_sizechange is %d\n", recent_sizechange));
+#else /* HAVE_SIZECHANGE || USE_SLANG */
+	    CTRACE((tfp, "Got KEY_RESIZE, recent_sizechange is %d\n",
+		    recent_sizechange));
+#endif /* HAVE_SIZECHANGE || USE_SLANG */
+	    if (!recent_sizechange) {
+#if 0				/* assumption seems flawed? */
+		/* Not detected by us or already processed by us.  It can
+		 * happens that ncurses lags behind us in detecting the change,
+		 * since its own SIGTSTP handler is not installed so detecting
+		 * happened *at the end* of the last refresh.  Tell it to
+		 * refresh again...  - kw
+		 */
+		LYrefresh();
+#endif
+#if defined(NCURSES)
+		/*
+		 * Work-around for scenario (Linux libc5) where we got a
+		 * recent sizechange before reading KEY_RESIZE.  If we do
+		 * not reset the flag, we'll next get an EOF read, which
+		 * causes Lynx to exit.
+		 */
+		recent_sizechange = TRUE;
+#endif
+		/*
+		 * May be just the delayed effect of mainloop()'s call to
+		 * resizeterm().  Pretend we haven't read anything yet, don't
+		 * return.  - kw
+		 */
+		goto re_read;
+	    }
+	    /*
+	     * Yep, we agree there was a change.  Return now so that the caller
+	     * can react to it.  - kw
+	     */
+	    c = DO_NOTHING;
+	    break;
+#endif /* KEY_RESIZE */
+
+/* The following maps PDCurses keys away from lynx reserved values */
+#if (defined(_WINDOWS) || defined(__DJGPP__)) && !defined(USE_SLANG)
+	case KEY_F(2):
+	    c = 0x213;
+	    break;
+	case KEY_F(3):
+	    c = 0x214;
+	    break;
+	case KEY_F(4):
+	    c = 0x215;
+	    break;
+	case KEY_F(5):
+	    c = 0x216;
+	    break;
+	case KEY_F(6):
+	    c = 0x217;
+	    break;
+	case KEY_F(7):
+	    c = 0x218;
+	    break;
+#endif /* PDCurses */
+
+#if defined(USE_MOUSE)
+/********************************************************************/
+
+#if defined(NCURSES) || defined(PDCURSES)
+	case KEY_MOUSE:
+	    CTRACE((tfp, "KEY_MOUSE\n"));
+	    if (code == FOR_CHOICE) {
+		c = MOUSE_KEY;	/* Will be processed by the caller */
+	    }
+#if defined(NCURSES)
+	    else if (code == FOR_SINGLEKEY) {
+		MEVENT event;
+
+		getmouse(&event);	/* Completely ignore event - kw */
+		c = DO_NOTHING;
+	    }
+#endif
+	    else {
+#if defined(NCURSES)
+		MEVENT event;
+		int err;
+		int lac = LYK_UNKNOWN;
+
+		c = -1;
+		mouse_link = -1;
+		err = getmouse(&event);
+		if (err != OK) {
+		    CTRACE((tfp, "Mouse error: no event available!\n"));
+		    return (code == FOR_PANEL ? 0 : DO_NOTHING);
+		}
+		levent = event;	/* Allow setting pos in entry fields */
+		if (event.bstate & BUTTON1_CLICKED) {
+		    c = set_clicked_link(event.x, event.y, code, 1);
+		} else if (event.bstate & BUTTON1_DOUBLE_CLICKED) {
+		    c = set_clicked_link(event.x, event.y, code, 2);
+		    if (c == LAC_TO_LKC0(LYK_SUBMIT) && code == FOR_INPUT)
+			lac = LYK_SUBMIT;
+		} else if (event.bstate & BUTTON3_CLICKED) {
+		    c = LAC_TO_LKC0(LYK_PREV_DOC);
+		} else if (code == FOR_PROMPT
+		    /* Cannot ignore: see LYCurses.c */
+			   || (event.bstate &
+			       (BUTTON1_PRESSED | BUTTON1_RELEASED
+				| BUTTON2_PRESSED | BUTTON2_RELEASED
+				| BUTTON3_PRESSED | BUTTON3_RELEASED))) {
+		    /* Completely ignore - don't return anything, to
+		       avoid canceling the prompt - kw */
+		    goto re_read;
+		} else if (event.bstate & BUTTON2_CLICKED) {
+		    int atlink;
+
+		    c = set_clicked_link(event.x, event.y, code, 1);
+		    atlink = (c == LAC_TO_LKC0(LYK_ACTIVATE));
+		    if (!atlink)
+			mouse_link = -1;	/* Forget about approx stuff. */
+
+		    lac = LYmouse_menu(event.x, event.y, atlink, code);
+		    if (lac == LYK_SUBMIT) {
+			if (mouse_link == -1)
+			    lac = LYK_ACTIVATE;
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+			else if (mouse_link >= 0 &&
+				 textfields_need_activation &&
+				 links[mouse_link].type == WWW_FORM_LINK_TYPE &&
+				 F_TEXTLIKE(links[mouse_link].l_form->type))
+			    lac = LYK_ACTIVATE;
+#endif
+		    }
+		    if (lac == LYK_ACTIVATE && mouse_link == -1) {
+			HTAlert(gettext("No link chosen"));
+			lac = LYK_REFRESH;
+		    }
+		    c = LAC_TO_LKC(lac);
+#if 0				/* Probably not necessary any more - kw */
+		    lynx_force_repaint();
+		    LYrefresh();
+#endif
+		}
+#if NCURSES_MOUSE_VERSION > 1
+		else if (event.bstate & BUTTON4_PRESSED) {
+		    c = LAC_TO_LKC(LYK_UP_HALF);
+		} else if (event.bstate & BUTTON5_PRESSED) {
+		    c = LAC_TO_LKC(LYK_DOWN_HALF);
+		}
+#endif
+		if (code == FOR_INPUT && mouse_link == -1 &&
+		    lac != LYK_REFRESH && lac != LYK_SUBMIT) {
+		    ungetmouse(&event);		/* Caller will process this. */
+		    wgetch(LYwin);	/* ungetmouse puts KEY_MOUSE back */
+		    c = MOUSE_KEY;
+		}
+#else /* pdcurses version */
+
+#define H_CMD_AREA	6
+#define HIST_CMD_2	12
+#define V_CMD_AREA	1
+
+		int left = H_CMD_AREA;
+		int right = (LYcolLimit - H_CMD_AREA - 1);
+
+		/* yes, I am assuming that my screen will be a certain width. */
+
+		int tick_count;
+		char *p = NULL;
+		char mouse_info[128];
+		static int old_click = 0;	/* [m Sec] */
+
+		c = -1;
+		mouse_link = -1;
+
+		if (!system_is_NT) {
+		    tick_count = GetTickCount();
+
+		    /* Guard Mouse button miss click */
+		    if ((tick_count - old_click) < 700) {
+			c = DO_NOTHING;
+			break;
+		    } else {
+			old_click = tick_count;
+		    }
+		}
+		request_mouse_pos();
+
+		if (BUTTON_STATUS(1) & BUTTON_PRESSED) {
+		    if (MOUSE_Y_POS > (LYlines - V_CMD_AREA - 1)) {
+			/* Screen BOTTOM */
+			if (MOUSE_X_POS < left) {
+			    c = LTARROW;
+			    p = "<-";
+			} else if (MOUSE_X_POS < HIST_CMD_2) {
+			    c = RTARROW;
+			    p = "->";
+			} else if (MOUSE_X_POS > right) {
+			    c = 'z';
+			    p = "Cancel";
+			} else {
+			    c = PGDOWN;
+			    p = "PGDOWN";
+			}
+		    } else if (MOUSE_Y_POS < V_CMD_AREA) {
+			/* Screen TOP */
+			if (MOUSE_X_POS < left) {
+			    c = LTARROW;
+			    p = "<-";
+			} else if (MOUSE_X_POS < HIST_CMD_2) {
+			    c = RTARROW;
+			    p = "->";
+			} else if (MOUSE_X_POS > right) {
+			    c = 'z';
+			    p = "Cancel";
+			} else {
+			    c = PGUP;
+			    p = "PGUP";
+			}
+		    } else {
+			c = set_clicked_link(MOUSE_X_POS,
+					     MOUSE_Y_POS,
+					     FOR_PANEL, 1);
+		    }
+		}
+		if (p && c != -1) {
+		    sprintf(mouse_info, "Mouse = 0x%x, [%s]", c, p);
+		    SetConsoleTitle(mouse_info);
+		}
+#endif /* !(WIN_EX) */
+		if ((c + 1) >= KEYMAP_SIZE && (c & LKC_ISLAC))
+		    return (c);
+	    }
+	    break;
+#endif /* NCURSES || PDCURSES */
+
+/********************************************************************/
+#endif /* USE_MOUSE */
+
+	}
+#endif /* HAVE_KEYPAD */
+#ifdef DJGPP_KEYHANDLER
+	switch (c) {
+	case K_Down:		/* The four arrow keys ... */
+	case K_EDown:
+	    c = DNARROW;
+	    break;
+	case K_Up:
+	case K_EUp:
+	    c = UPARROW;
+	    break;
+	case K_Left:
+	case K_ELeft:
+	    c = LTARROW;
+	    break;
+	case K_Right:		/* ... */
+	case K_ERight:
+	    c = RTARROW;
+	    break;
+	case K_Home:		/* Home key (upward+left arrow) */
+	case K_EHome:
+	    c = HOME;
+	    break;
+	case K_PageDown:	/* Next page */
+	case K_EPageDown:
+	    c = PGDOWN;
+	    break;
+	case K_PageUp:		/* Previous page */
+	case K_EPageUp:
+	    c = PGUP;
+	    break;
+	case K_End:		/* home down or bottom (lower left) */
+	case K_EEnd:
+	    c = END_KEY;
+	    break;
+	case K_F1:		/* F1 key */
+	    c = F1;
+	    break;
+	case K_Insert:		/* Insert key */
+	case K_EInsert:
+	    c = INSERT_KEY;
+	    break;
+	case K_Delete:		/* Delete key */
+	case K_EDelete:
+	    c = REMOVE_KEY;
+	    break;
+	case K_Alt_Escape:	/* Alt-Escape */
+	    c = 0x1a7;
+	    break;
+	case K_Control_At:	/* CTRL-@ */
+	    c = 0x1a8;
+	    break;
+	case K_Alt_Backspace:	/* Alt-Backspace */
+	    c = 0x1a9;
+	    break;
+	case K_BackTab:	/* BackTab */
+	    c = BACKTAB_KEY;
+	    break;
+	}
+#endif /* DGJPP_KEYHANDLER */
+#if defined(USE_SLANG) && (defined(__DJGPP__) || defined(__CYGWIN__)) && !defined(DJGPP_KEYHANDLER)  && !defined(USE_KEYMAPS)
+	switch (c) {
+	case SL_KEY_DOWN:	/* The four arrow keys ... */
+	    c = DNARROW;
+	    break;
+	case SL_KEY_UP:
+	    c = UPARROW;
+	    break;
+	case SL_KEY_LEFT:
+	    c = LTARROW;
+	    break;
+	case SL_KEY_RIGHT:	/* ... */
+	    c = RTARROW;
+	    break;
+	case SL_KEY_HOME:	/* Home key (upward+left arrow) */
+	case SL_KEY_A1:	/* upper left of keypad */
+	    c = HOME;
+	    break;
+	case SL_KEY_NPAGE:	/* Next page */
+	case SL_KEY_C3:	/* lower right of keypad */
+	    c = PGDOWN;
+	    break;
+	case SL_KEY_PPAGE:	/* Previous page */
+	case SL_KEY_A3:	/* upper right of keypad */
+	    c = PGUP;
+	    break;
+	case SL_KEY_END:	/* home down or bottom (lower left) */
+	case SL_KEY_C1:	/* lower left of keypad */
+	    c = END_KEY;
+	    break;
+	case SL_KEY_F(1):	/* F1 key */
+	    c = F1;
+	    break;
+	case SL_KEY_IC:	/* Insert key */
+	    c = INSERT_KEY;
+	    break;
+	case SL_KEY_DELETE:	/* Delete key */
+	    c = REMOVE_KEY;
+	    break;
+	}
+#endif /* USE_SLANG && __DJGPP__ && !DJGPP_KEYHANDLER && !USE_KEYMAPS */
+    }
+
+    if (c & (LKC_ISLAC | LKC_ISLECLAC))
+	return (c);
+    if ((c + 1) >= KEYMAP_SIZE) {
+	/*
+	 * Don't return raw values for KEYPAD symbols which we may have missed
+	 * in the switch above if they are obviously invalid when used as an
+	 * index into (e.g.) keypad[].  - KW
+	 */
+	return (0);
+    } else {
+	return (c | current_modifier);
+    }
+}
+
+/************************************************************************/
+#endif /* NOT  defined(USE_KEYMAPS) && defined(USE_SLANG) */
+
+int LYgetch(void)
+{
+    return LYReadCmdKey(FOR_PANEL);
+}
+
+/*
+ * Read a single keystroke, allows mouse-selection.
+ */
+int LYgetch_choice(void)
+{
+    int ch = LYReadCmdKey(FOR_CHOICE);
+
+    if (ch == LYCharINTERRUPT1)
+	ch = LYCharINTERRUPT2;	/* treat ^C the same as ^G */
+    return ch;
+}
+
+/*
+ * Read a single keystroke, allows mouse events.
+ */
+int LYgetch_input(void)
+{
+    int ch = LYReadCmdKey(FOR_INPUT);
+
+    if (ch == LYCharINTERRUPT1)
+	ch = LYCharINTERRUPT2;	/* treat ^C the same as ^G */
+    return ch;
+}
+
+/*
+ * Read a single keystroke, ignoring case by translating it to uppercase.
+ * Ignore mouse events, if any.
+ */
+int LYgetch_single(void)
+{
+    int ch = LYReadCmdKey(FOR_SINGLEKEY);
+
+    if (ch == LYCharINTERRUPT1)
+	ch = LYCharINTERRUPT2;	/* treat ^C the same as ^G */
+    else if (ch > 0 && ch < 256)
+	ch = TOUPPER(ch);	/* will ignore case of result */
+    return ch;
+}
+
+/*
+ * Convert a null-terminated string to lowercase
+ */
+void LYLowerCase(char *arg_buffer)
+{
+    register unsigned char *buffer = (unsigned char *) arg_buffer;
+    size_t i;
+
+    for (i = 0; buffer[i]; i++)
+#ifdef SUPPORT_MULTIBYTE_EDIT	/* 1998/11/23 (Mon) 17:04:55 */
+    {
+	if ((buffer[i] & 0x80) != 0
+	    && buffer[i + 1] != 0) {
+	    if ((kanji_code == SJIS) && IS_SJIS_X0201KANA(UCH((buffer[i])))) {
+		continue;
+	    }
+	    i++;
+	} else {
+	    buffer[i] = UCH(TOLOWER(buffer[i]));
+	}
+    }
+#else
+	buffer[i] = TOLOWER(buffer[i]);
+#endif
+}
+
+/*
+ * Convert a null-terminated string to uppercase
+ */
+void LYUpperCase(char *arg_buffer)
+{
+    register unsigned char *buffer = (unsigned char *) arg_buffer;
+    size_t i;
+
+    for (i = 0; buffer[i]; i++)
+#ifdef SUPPORT_MULTIBYTE_EDIT	/* 1998/11/23 (Mon) 17:05:10 */
+    {
+	if ((buffer[i] & 0x80) != 0
+	    && buffer[i + 1] != 0) {
+	    if ((kanji_code == SJIS) && IS_SJIS_X0201KANA(UCH((buffer[i])))) {
+		continue;
+	    }
+	    i++;
+	} else {
+	    buffer[i] = UCH(TOUPPER(buffer[i]));
+	}
+    }
+#else
+	buffer[i] = UCH(TOUPPER(buffer[i]));
+#endif
+}
+
+/*
+ * Remove newlines from a string, returning true if we removed any.
+ */
+BOOLEAN LYRemoveNewlines(char *buffer)
+{
+    if (buffer != 0) {
+	register char *buf = buffer;
+
+	for (; *buf && *buf != '\n' && *buf != '\r'; buf++) ;
+	if (*buf) {
+	    /* runs very seldom */
+	    char *old = buf;
+
+	    for (; *old; old++) {
+		if (*old != '\n' && *old != '\r')
+		    *buf++ = *old;
+	    }
+	    *buf = '\0';
+	    return TRUE;
+	}
+    }
+    return FALSE;
+}
+
+/*
+ * Remove leading/trailing whitespace from a string, reduce runs of embedded
+ * whitespace to single blanks.
+ */
+char *LYReduceBlanks(char *buffer)
+{
+    if (non_empty(buffer)) {
+	LYTrimLeading(buffer);
+	LYTrimTrailing(buffer);
+	convert_to_spaces(buffer, TRUE);
+    }
+    return buffer;
+}
+
+/*
+ * Remove ALL whitespace from a string (including embedded blanks), and returns
+ * a pointer to the end of the trimmed string.
+ */
+char *LYRemoveBlanks(char *buffer)
+{
+    if (buffer != 0) {
+	register char *buf = buffer;
+
+	for (; *buf && !isspace(UCH(*buf)); buf++) ;
+	if (*buf) {
+	    /* runs very seldom */
+	    char *old = buf;
+
+	    for (; *old; old++) {
+		if (!isspace(UCH(*old)))
+		    *buf++ = *old;
+	    }
+	    *buf = '\0';
+	}
+	return buf;
+    }
+    return NULL;
+}
+
+/*
+ * Skip whitespace
+ */
+char *LYSkipBlanks(char *buffer)
+{
+    while (isspace(UCH((*buffer))))
+	buffer++;
+    return buffer;
+}
+
+/*
+ * Skip non-whitespace
+ */
+char *LYSkipNonBlanks(char *buffer)
+{
+    while (*buffer != 0 && !isspace(UCH((*buffer))))
+	buffer++;
+    return buffer;
+}
+
+/*
+ * Skip const whitespace
+ */
+const char *LYSkipCBlanks(const char *buffer)
+{
+    while (isspace(UCH((*buffer))))
+	buffer++;
+    return buffer;
+}
+
+/*
+ * Skip const non-whitespace
+ */
+const char *LYSkipCNonBlanks(const char *buffer)
+{
+    while (*buffer != 0 && !isspace(UCH((*buffer))))
+	buffer++;
+    return buffer;
+}
+
+/*
+ * Trim leading blanks from a string
+ */
+void LYTrimLeading(char *buffer)
+{
+    char *skipped = LYSkipBlanks(buffer);
+
+    while ((*buffer++ = *skipped++) != 0) ;
+}
+
+/*
+ * Trim trailing newline(s) from a string
+ */
+char *LYTrimNewline(char *buffer)
+{
+    size_t i = strlen(buffer);
+
+    while (i != 0 && (buffer[i - 1] == '\n' || buffer[i - 1] == '\r'))
+	buffer[--i] = 0;
+    return buffer;
+}
+
+/*
+ * Trim trailing blanks from a string
+ */
+void LYTrimTrailing(char *buffer)
+{
+    size_t i = strlen(buffer);
+
+    while (i != 0 && isspace(UCH(buffer[i - 1])))
+	buffer[--i] = 0;
+}
+
+/* 1997/11/10 (Mon) 14:26:10, originally string_short() in LYExterns.c, but
+ * moved here because LYExterns is not always configured.
+ */
+char *LYElideString(char *str,
+		    int cut_pos)
+{
+    char buff[MAX_LINE], *s, *d;
+    static char s_str[MAX_LINE];
+    int len;
+
+    LYstrncpy(buff, str, sizeof(buff) - 1);
+    len = (int) strlen(buff);
+    if (len > (LYcolLimit - 9)) {
+	buff[cut_pos] = '.';
+	buff[cut_pos + 1] = '.';
+	for (s = (buff + len) - (LYcolLimit - 9) + cut_pos + 1,
+	     d = (buff + cut_pos) + 2;
+	     s >= buff &&
+	     d >= buff &&
+	     d < buff + LYcols &&
+	     (*d++ = *s++) != 0;) ;
+	buff[LYcols] = 0;
+    }
+    strcpy(s_str, buff);
+    return (s_str);
+}
+
+/*
+ * Trim a startfile, returning true if it looks like one of the Lynx tags.
+ */
+BOOLEAN LYTrimStartfile(char *buffer)
+{
+    LYTrimHead(buffer);
+    if (isLYNXEXEC(buffer) ||
+	isLYNXPROG(buffer)) {
+	/*
+	 * The original implementations of these schemes expected white space
+	 * without hex escaping, and did not check for hex escaping, so we'll
+	 * continue to support that, until that code is redone in conformance
+	 * with SGML principles.  - FM
+	 */
+	HTUnEscapeSome(buffer, " \r\n\t");
+	convert_to_spaces(buffer, TRUE);
+	return TRUE;
+    }
+    return FALSE;
+}
+
+/*
+ * Escape unsafe characters in startfile, except for lynx internal URLs.
+ */
+void LYEscapeStartfile(char **buffer)
+{
+    if (!LYTrimStartfile(*buffer)) {
+	char *escaped = HTEscapeUnsafe(*buffer);
+
+	StrAllocCopy(*buffer, escaped);
+	FREE(escaped);
+    }
+}
+
+/*
+ * Trim all blanks from startfile, except for lynx internal URLs.
+ */
+void LYTrimAllStartfile(char *buffer)
+{
+    if (!LYTrimStartfile(buffer)) {
+	LYRemoveBlanks(buffer);
+    }
+}
+
+/*
+ *  Display the current value of the string and allow the user
+ *  to edit it.
+ */
+
+#define EDREC	 EditFieldData
+
+/*
+ * Shorthand to get rid of the "edit->suchandsos".
+ */
+#define IsDirty  edit->dirty
+#define IsHidden edit->hidden
+#define StartX	 edit->sx
+#define StartY	 edit->sy
+#define Buf	 edit->buffer
+#define Pos	 edit->pos	/* current editing position (bytes) */
+#define StrLen	 edit->strlen	/* length (bytes) */
+#define MaxLen	 edit->maxlen
+#define DspWdth  edit->dspwdth
+#define DspStart edit->xpan	/* display-start (columns) */
+#define Margin	 edit->margin
+#define PanOn	 edit->panon
+#define PadChar  edit->pad
+#ifdef ENHANCED_LINEEDIT
+#define Mark	 edit->mark
+#endif
+
+#ifdef ENHANCED_LINEEDIT
+static char killbuffer[MAX_EDIT] = "\0";
+#endif
+
+void LYSetupEdit(EDREC * edit, char *old,
+		 int maxstr,
+		 int maxdsp)
+{
+    /*
+     * Initialize edit record
+     */
+    LYGetYX(StartY, StartX);
+    PadChar = ' ';
+    IsDirty = TRUE;
+    PanOn = FALSE;
+    edit->current_modifiers = 0;
+
+    MaxLen = maxstr;
+    DspWdth = maxdsp;
+    Margin = 0;
+    Pos = (int) strlen(old);
+#ifdef ENHANCED_LINEEDIT
+    Mark = -1;			/* pos=0, but do not show it yet */
+#endif
+    DspStart = 0;
+
+    if (maxstr > maxdsp) {	/* Need panning? */
+	if (DspWdth > 4)	/* Else "{}" take up precious screen space */
+	    PanOn = TRUE;
+
+	/*
+	 * Figure out margins.  If too big, we do a lot of unnecessary
+	 * scrolling.  If too small, user doesn't have sufficient look-ahead. 
+	 * Let's say 25% for each margin, upper bound is 10 columns.
+	 */
+	Margin = DspWdth / 4;
+	if (Margin > 10)
+	    Margin = 10;
+    }
+
+    LYstrncpy(Buf, old, maxstr);
+    StrLen = (int) strlen(Buf);
+}
+
+#ifdef SUPPORT_MULTIBYTE_EDIT
+
+/*
+ * MBCS positioning routines below are specific to SUPPORT_MULTIBYTE_EDIT code.
+ * Currently they handle UTF-8 and (hopefully) CJK.
+ * Current encoding is recognized using defines below.
+ *
+ * LYmbcs* functions don't look very convenient to use here...
+ * Do we really need utf_flag as an argument?
+ *
+ * It is set (see IS_UTF8_TTY) for every invocation out there, and they use
+ * HTCJK flag internally anyway.  Something like LYmbcsstrnlen == mbcs_glyphs
+ * would be useful to work with string slices -Sergej Kvachonok 
+ */
+
+#define IS_UTF8_EXTRA(x) (((unsigned char)(x) & 0300) == 0200)
+
+/*
+ * Counts glyphs in a multibyte (sub)string s of length len.
+ */
+static int mbcs_glyphs(char *s, int len)
+{
+    int glyphs = 0;
+    int i;
+
+    if (IS_UTF8_TTY) {
+	for (i = 0; s[i] && i < len; i++)
+	    if (!IS_UTF8_EXTRA(s[i]))
+		glyphs++;
+    } else if (IS_CJK_TTY) {
+	for (i = 0; s[i] && i < len; i++, glyphs++)
+	    if (is8bits(s[i]))
+		i++;
+    } else {
+	glyphs = len;
+    }
+    return glyphs;
+}
+
+/*
+ * Calculates offset in bytes of a glyph at cell position pos.
+ */
+static int mbcs_skip(char *s, int pos)
+{
+    int p, i;
+
+    if (IS_UTF8_TTY) {
+	for (i = 0, p = 0; s[i]; i++) {
+	    if (!IS_UTF8_EXTRA(s[i]))
+		p++;
+	    if (p > pos)
+		break;
+	}
+    } else if (IS_CJK_TTY) {
+	for (p = i = 0; s[i] && p < pos; p++, i++)
+	    if (is8bits(s[i]))
+		i++;
+    } else {
+	i = pos;
+    }
+
+    return i;
+}
+
+/*
+ * Given a string that would display (at least) the given number of cells,
+ * determine the number of multibyte characters that comprised those cells.
+ */
+static int cell2char(char *s, int cells)
+{
+    int result = 0;
+    int len = (int) strlen(s);
+    int pos;
+    int have;
+
+    CTRACE_EDIT((tfp, "cell2char(%d) %d:%s\n", cells, len, s));
+    /* FIXME - make this a binary search */
+    for (pos = 0; pos <= len; ++pos) {
+	have = LYstrExtent2(s, pos);
+	CTRACE_EDIT((tfp, "  %2d:%2d:%.*s\n", pos, have, pos, s));
+	if (have >= cells) {
+	    break;
+	}
+    }
+    if (pos > len)
+	pos = len;
+    result = mbcs_glyphs(s, pos);
+    CTRACE_EDIT((tfp, "->%d\n", result));
+    return result;
+}
+
+#endif /* SUPPORT_MULTIBYTE_EDIT */
+
+#ifdef EXP_KEYBOARD_LAYOUT
+static int map_active = 0;
+
+#else
+#define map_active 0
+#endif
+
+int LYEditInsert(EDREC * edit, unsigned const char *s,
+		 int len,
+		 int map GCC_UNUSED,
+		 BOOL maxMessage)
+{
+    int length = (int) strlen(Buf);
+    int remains = MaxLen - (length + len);
+    int edited = 0, overflow = 0;
+
+    /*
+     * ch is (presumably) printable character.
+     */
+    if (remains < 0) {
+	overflow = 1;
+	len = 0;
+	if (MaxLen > length)	/* Insert as much as we can */
+	    len = MaxLen - length;
+	else
+	    goto finish;
+    }
+    Buf[length + len] = '\0';
+    for (; length >= Pos; length--)	/* Make room */
+	Buf[length + len] = Buf[length];
+#ifdef EXP_KEYBOARD_LAYOUT
+    if (map < 0)
+	map = map_active;
+    if (map && IS_UTF8_TTY) {
+	int off = Pos;
+	unsigned const char *e = s + len;
+	char *tail = 0;
+
+	while (s < e) {
+	    char utfbuf[8];
+	    int l = 1;
+
+	    utfbuf[0] = (char) *s;
+	    if (*s < 128 && LYKbLayouts[current_layout][*s]) {
+		UCode_t ucode = LYKbLayouts[current_layout][*s];
+
+		if (ucode > 127) {
+		    if (UCConvertUniToUtf8(ucode, utfbuf)) {
+			l = (int) strlen(utfbuf);
+			remains -= l - 1;
+			if (remains < 0) {
+			    if (tail)
+				strcpy(Buf + off, tail);
+			    FREE(tail);
+			    len = off;
+			    overflow = 1;
+			    goto finish;
+			}
+			if (l > 1 && !tail)
+			    StrAllocCopy(tail, Buf + Pos + len);
+		    } else
+			utfbuf[0] = '?';
+		} else
+		    utfbuf[0] = (char) ucode;
+	    }
+	    strncpy(Buf + off, utfbuf, (unsigned) l);
+	    edited = 1;
+	    off += l;
+	    s++;
+	}
+	if (tail)
+	    strcpy(Buf + off, tail);
+	len = off - Pos;
+	FREE(tail);
+    } else if (map) {
+	unsigned const char *e = s + len;
+	unsigned char *t = (unsigned char *) Buf + Pos;
+
+	while (s < e) {
+	    int ch;
+
+	    if (*s < 128 && LYKbLayouts[current_layout][*s]) {
+		ch = UCTransUniChar(LYKbLayouts[current_layout][*s],
+				    current_char_set);
+		if (ch < 0)
+		    ch = '?';
+	    } else
+		ch = *s;
+	    *t = UCH(ch);
+	    t++, s++;
+	}
+	edited = 1;
+    } else
+#endif /* defined EXP_KEYBOARD_LAYOUT */
+    {
+	strncpy(Buf + Pos, (const char *) s, (unsigned) len);
+	edited = 1;
+    }
+
+  finish:
+    Pos += len;
+    StrLen += len;
+    if (edited)
+	IsDirty = TRUE;
+    if (overflow && maxMessage)
+	_statusline(MAXLEN_REACHED_DEL_OR_MOV);
+#ifdef ENHANCED_LINEEDIT
+    if (Mark > Pos)
+	Mark += len;
+    else if (Mark < -1 - Pos)
+	Mark -= len;
+    if (Mark >= 0)
+	Mark = -1 - Mark;	/* Disable it */
+#endif
+    return edited;
+}
+
+/* returns 0    character processed
+ *         -ch  if action should be performed outside of line-editing mode
+ *         ch   otherwise
+ */
+int LYEdit1(EDREC * edit, int ch,
+	    int action,
+	    BOOL maxMessage)
+{
+    int i;
+    int length;
+    unsigned char uch;
+    int offset;
+
+    if (MaxLen <= 0)
+	return (0);		/* Be defensive */
+
+    length = (int) strlen(&Buf[0]);
+    StrLen = length;
+
+    switch (action) {
+#ifdef EXP_KEYBOARD_LAYOUT
+    case LYE_SWMAP:
+	/*
+	 * Turn input character mapping on or off.
+	 */
+	map_active = ~map_active;
+	break;
+#endif
+#ifndef CJK_EX
+    case LYE_AIX:
+	/*
+	 * Hex 97.
+	 * Fall through as a character for CJK, or if this is a valid character
+	 * in the current display character set.  Otherwise, we treat this as
+	 * LYE_ENTER.
+	 */
+	if (!IS_CJK_TTY && LYlowest_eightbit[current_char_set] > 0x97)
+	    return (ch);
+	/* FALLTHRU */
+#endif
+    case LYE_CHAR:
+	uch = UCH(ch);
+	LYEditInsert(edit, &uch, 1, map_active, maxMessage);
+	return 0;		/* All changes already registered */
+
+    case LYE_C1CHAR:
+	/*
+	 * ch is the second part (in most cases, a capital letter) of a 7-bit
+	 * replacement for a character in the 8-bit C1 control range.
+	 *
+	 * This is meant to undo transformations like 0x81 -> 0x1b 0x41 (ESC A)
+	 * etc.  done by slang on Unix and possibly some comm programs.  It's
+	 * an imperfect workaround that doesn't work for all such characters.
+	 */
+	ch &= 0xFF;
+	if (ch + 64 >= LYlowest_eightbit[current_char_set])
+	    ch += 64;
+
+	if (Pos <= (MaxLen) && StrLen < (MaxLen)) {
+#ifdef ENHANCED_LINEEDIT
+	    if (Mark > Pos)
+		Mark++;
+	    else if (Mark < -1 - Pos)
+		Mark--;
+	    if (Mark >= 0)
+		Mark = -1 - Mark;	/* Disable it */
+#endif
+	    for (i = length; i >= Pos; i--)	/* Make room */
+		Buf[i + 1] = Buf[i];
+	    Buf[length + 1] = '\0';
+	    Buf[Pos] = (char) ch;
+	    Pos++;
+	} else {
+	    if (maxMessage) {
+		_statusline(MAXLEN_REACHED_DEL_OR_MOV);
+	    }
+	    return (ch);
+	}
+	break;
+
+    case LYE_BACKW:
+	/*
+	 * Backword.
+	 * Definition of word is very naive:  1 or more a/n characters.
+	 */
+#ifndef SUPPORT_MULTIBYTE_EDIT
+	while (Pos && !isalnum(UCH(Buf[Pos - 1])))
+	    Pos--;
+	while (Pos && isalnum(UCH(Buf[Pos - 1])))
+	    Pos--;
+#else
+	while (Pos && !(isalnum(UCH(Buf[Pos - 1])) || is8bits(Buf[Pos - 1])))
+	    Pos--;
+	while (Pos && (isalnum(UCH(Buf[Pos - 1])) || is8bits(Buf[Pos - 1])))
+	    Pos--;
+#endif
+	break;
+
+    case LYE_FORWW:
+	/*
+	 * Word forward.
+	 */
+#ifndef SUPPORT_MULTIBYTE_EDIT
+	while (isalnum(UCH(Buf[Pos])))
+	    Pos++;		/* '\0' is not a/n */
+	while (!isalnum(UCH(Buf[Pos])) && Buf[Pos])
+	    Pos++;
+#else
+	while (isalnum(UCH(Buf[Pos])) || is8bits(Buf[Pos]))
+	    Pos++;		/* '\0' is not a/n */
+	while (!(isalnum(UCH(Buf[Pos])) || is8bits(Buf[Pos])) && Buf[Pos])
+	    Pos++;
+#endif
+	break;
+
+    case LYE_ERASE:
+	/*
+	 * Erase the line to start fresh.
+	 */
+	Buf[0] = '\0';
+#ifdef ENHANCED_LINEEDIT
+	Mark = -1;		/* Do not show the mark */
+#endif
+	/* fall through */
+
+    case LYE_BOL:
+	/*
+	 * Go to first column.
+	 */
+	Pos = 0;
+	break;
+
+    case LYE_EOL:
+	/*
+	 * Go to last column.
+	 */
+	Pos = length;
+	break;
+
+    case LYE_DELNW:
+	/*
+	 * Delete next word.
+	 */
+	offset = Pos;
+	LYEdit1(edit, 0, LYE_FORWW, FALSE);
+	offset = Pos - offset;
+	Pos -= offset;
+
+	goto shrink;		/* right below */
+
+    case LYE_DELPW:
+	/*
+	 * Delete previous word.
+	 */
+	offset = Pos;
+	LYEdit1(edit, 0, LYE_BACKW, FALSE);
+	offset -= Pos;
+
+      shrink:
+	for (i = Pos; i < length - offset + 1; i++)
+	    Buf[i] = Buf[i + offset];
+#ifdef ENHANCED_LINEEDIT
+	if (Mark >= 0)
+	    Mark = -1 - Mark;	/* Disable it */
+	if (Mark <= -1 - Pos - offset)
+	    Mark += offset;	/* Shift it */
+	if (-1 - Pos - offset < Mark && Mark < -1 - Pos)
+	    Mark = -1 - Pos;	/* Set to the current position */
+#endif
+
+	break;
+
+    case LYE_DELBL:
+	/*
+	 * Delete from current cursor position back to BOL.
+	 */
+	for (i = Pos; i < length + 1; i++)
+	    Buf[i - Pos] = Buf[i];
+
+#ifdef ENHANCED_LINEEDIT
+	if (Mark >= 0)
+	    Mark = -1 - Mark;	/* Disable it */
+	if (Mark <= -1 - Pos)
+	    Mark += Pos;	/* Shift it */
+	else
+	    Mark = -1;		/* Reset it */
+#endif
+	Pos = 0;
+	break;
+
+    case LYE_DELEL:		/* @@@ */
+	/*
+	 * Delete from current cursor position thru EOL.
+	 */
+	Buf[Pos] = '\0';
+#ifdef ENHANCED_LINEEDIT
+	if (Mark >= 0)
+	    Mark = -1 - Mark;	/* Disable it */
+	if (Mark <= -1 - Pos)
+	    Mark = -1;		/* Reset it */
+#endif
+	break;
+
+    case LYE_DELN:
+	/*
+	 * Delete next character (I-beam style cursor), or current character
+	 * (box/underline style cursor).
+	 */
+	if (Pos >= length)
+	    break;
+#ifndef SUPPORT_MULTIBYTE_EDIT
+	Pos++;
+#else
+	Pos += mbcs_skip(Buf + Pos, 1);
+#endif
+	/* fall through - DO NOT RELOCATE the LYE_DELN case wrt LYE_DELP */
+
+    case LYE_DELP:
+	/*
+	 * Delete preceding character.
+	 */
+	if (length == 0 || Pos == 0)
+	    break;
+
+#ifndef SUPPORT_MULTIBYTE_EDIT
+#ifdef ENHANCED_LINEEDIT
+	if (Mark >= 0)
+	    Mark = -1 - Mark;	/* Disable it */
+	if (Mark <= -1 - Pos)
+	    Mark++;
+#endif
+	Pos--;
+	for (i = Pos; i < length; i++)
+	    Buf[i] = Buf[i + 1];
+#else /* SUPPORT_MULTIBYTE_EDIT */
+	offset = Pos - mbcs_skip(Buf, mbcs_glyphs(Buf, Pos) - 1);
+	Pos -= offset;
+	for (i = Pos; i < length - offset + 1; i++)
+	    Buf[i] = Buf[i + offset];
+
+#ifdef ENHANCED_LINEEDIT
+	if (Mark >= 0)
+	    Mark = -1 - Mark;	/* Disable it */
+	if (Mark <= -1 - Pos)
+	    Mark += offset;	/* Shift it */
+#endif
+
+#endif /* SUPPORT_MULTIBYTE_EDIT */
+	break;
+
+    case LYE_FORW_RL:
+    case LYE_FORW:
+	/*
+	 * Move cursor to the right.
+	 */
+#ifndef SUPPORT_MULTIBYTE_EDIT
+	if (Pos < length)
+	    Pos++;
+#else
+	if (Pos < length)
+	    Pos += mbcs_skip(Buf + Pos, 1);
+#endif
+	else if (action == LYE_FORW_RL)
+	    return -ch;
+	break;
+
+    case LYE_BACK_LL:
+    case LYE_BACK:
+	/*
+	 * Left-arrow move cursor to the left.
+	 */
+#ifndef SUPPORT_MULTIBYTE_EDIT
+	if (Pos > 0)
+	    Pos--;
+#else
+	if (Pos > 0)
+	    Pos = mbcs_skip(Buf, mbcs_glyphs(Buf, Pos) - 1);
+#endif
+	else if (action == LYE_BACK_LL)
+	    return -ch;
+	break;
+
+#ifdef ENHANCED_LINEEDIT
+    case LYE_TPOS:
+	/*
+	 * Transpose characters - bash or ksh(emacs not gmacs) style
+	 */
+
+#ifdef SUPPORT_MULTIBYTE_EDIT
+	if (IS_UTF8_TTY || IS_CJK_TTY)
+	    break;		/* Can't help it now */
+#endif
+
+	if (length <= 1 || Pos == 0)
+	    return (ch);
+	if (Pos == length)
+	    Pos--;
+	if (Mark < 0)
+	    Mark = -1 - Mark;	/* Temporary enable it */
+	if (Mark == Pos || Mark == Pos + 1)
+	    Mark = Pos - 1;
+	if (Mark >= 0)
+	    Mark = -1 - Mark;	/* Disable it */
+	if (Buf[Pos - 1] == Buf[Pos]) {
+	    Pos++;
+	    break;
+	}
+	i = Buf[Pos - 1];
+	Buf[Pos - 1] = Buf[Pos];
+	Buf[Pos++] = (char) i;
+	break;
+
+    case LYE_SETMARK:
+	/*
+	 * primitive emacs-like set-mark-command
+	 */
+	Mark = Pos;
+	return (0);
+
+    case LYE_XPMARK:
+	/*
+	 * emacs-like exchange-point-and-mark
+	 */
+	if (Mark < 0)
+	    Mark = -1 - Mark;	/* Enable it */
+	if (Mark == Pos)
+	    return (0);
+	i = Pos;
+	Pos = Mark;
+	Mark = i;
+	break;
+
+    case LYE_KILLREG:
+	/*
+	 * primitive emacs-like kill-region
+	 */
+	if (Mark < 0)
+	    Mark = -1 - Mark;	/* Enable it */
+	if (Mark == Pos) {
+	    killbuffer[0] = '\0';
+	    return (0);
+	}
+	if (Mark > Pos)
+	    LYEdit1(edit, 0, LYE_XPMARK, FALSE);
+	{
+	    int reglen = Pos - Mark;
+
+	    LYstrncpy(killbuffer, &Buf[Mark],
+		      HTMIN(reglen, (int) sizeof(killbuffer) - 1));
+	    for (i = Mark; Buf[i + reglen]; i++)
+		Buf[i] = Buf[i + reglen];
+	    Buf[i] = Buf[i + reglen];	/* terminate */
+	    Pos = Mark;
+	}
+	if (Mark >= 0)
+	    Mark = -1 - Mark;	/* Disable it */
+	break;
+
+    case LYE_YANK:
+	/*
+	 * primitive emacs-like yank
+	 */
+	if (!killbuffer[0]) {
+	    Mark = -1 - Pos;
+	    return (0);
+	} {
+	    int yanklen = (int) strlen(killbuffer);
+
+	    if (Pos + yanklen <= (MaxLen) && StrLen + yanklen <= (MaxLen)) {
+		Mark = -1 - Pos;
+
+		for (i = length; i >= Pos; i--)		/* Make room */
+		    Buf[i + yanklen] = Buf[i];
+		for (i = 0; i < yanklen; i++)
+		    Buf[Pos++] = killbuffer[i];
+
+	    } else if (maxMessage) {
+		_statusline(MAXLEN_REACHED_DEL_OR_MOV);
+	    }
+	}
+	break;
+
+#endif /* ENHANCED_LINEEDIT */
+
+    case LYE_UPPER:
+	LYUpperCase(Buf);
+	break;
+
+    case LYE_LOWER:
+	LYLowerCase(Buf);
+	break;
+
+    default:
+	return (ch);
+    }
+    IsDirty = TRUE;
+    StrLen = (int) strlen(&Buf[0]);
+    return (0);
+}
+
+/*
+ *  This function prompts for a choice or page number.
+ *  If a 'g' or 'p' suffix is included, that will be
+ *  loaded into c.  Otherwise, c is zeroed. - FM & LE
+ */
+int get_popup_number(const char *msg,
+		     int *c,
+		     int *rel)
+{
+    char temp[120];
+    char *p = temp;
+    int num;
+
+    /*
+     * Load the c argument into the prompt buffer.
+     */
+    temp[0] = (char) *c;
+    temp[1] = '\0';
+    _statusline(msg);
+
+    /*
+     * Get the number, possibly with a suffix, from the user.
+     */
+    if (LYgetstr(temp, VISIBLE, sizeof(temp), NORECALL) < 0 || *temp == 0) {
+	HTInfoMsg(CANCELLED);
+	*c = '\0';
+	*rel = '\0';
+	return (0);
+    }
+
+    *rel = '\0';
+    num = atoi(p);
+    while (isdigit(UCH(*p)))
+	++p;
+    switch (*p) {
+    case '+':
+    case '-':
+	/* 123+ or 123- */
+	*rel = *p++;
+	*c = *p;
+	break;
+    default:
+	*c = *p++;
+	*rel = *p;
+	break;
+    case 0:
+	break;
+    }
+
+    /*
+     * If we had a 'g' or 'p' suffix, load it into c.  Otherwise, zero c.  Then
+     * return the number.
+     */
+    if (*p == 'g' || *p == 'G') {
+	*c = 'g';
+    } else if (*p == 'p' || *p == 'P') {
+	*c = 'p';
+    } else {
+	*c = '\0';
+    }
+    if (*rel != '+' && *rel != '-')
+	*rel = 0;
+    return num;
+}
+
+#ifdef USE_COLOR_STYLE
+#  define TmpStyleOn(s)		curses_style((s), STACK_ON)
+#  define TmpStyleOff(s)	curses_style((s), STACK_OFF)
+#else
+#  define TmpStyleOn(s)
+#  define TmpStyleOff(s)
+#endif /* defined USE_COLOR_STYLE */
+
+static void remember_column(EDREC * edit, int offset)
+{
+    int y0, x0;
+
+#if defined(USE_SLANG)
+    y0 = 0;
+    x0 = SLsmg_get_column();
+#elif defined(USE_CURSES_PADS)
+    getyx(LYwin, y0, x0);
+#else
+    getyx(stdscr, y0, x0);
+#endif
+    edit->offset2col[offset] = x0;
+}
+
+static void fill_edited_line(int prompting GCC_UNUSED, int length, int ch)
+{
+    int i;
+
+    TmpStyleOn(prompting ? s_prompt_edit_pad : s_aedit_pad);
+
+    for (i = 0; i < length; i++) {
+	LYaddch(UCH(ch));
+    }
+
+    TmpStyleOff(prompting ? s_prompt_edit_pad : s_aedit_pad);
+}
+
+/*
+ * Multibyte string display subroutine.
+ * EDREC fields retain their values as byte offsets.
+ * All external logic still works fine with byte values.
+ */
+void LYRefreshEdit(EDREC * edit)
+{
+    /* bytes and characters are not the same thing */
+    int all_bytes;
+    int pos_bytes = Pos;
+    int dpy_bytes;
+    int lft_bytes;		/* base of string which is displayed */
+
+    /* cells refer to display-columns on the screen */
+    int all_cells;		/* total of display-cells in Buf */
+    int dpy_cells;		/* number of cells which are displayed */
+    int lft_cells;		/* number of cells before display (on left) */
+    int pos_cells;		/* number of display-cells up to Pos */
+
+#if defined(SUPPORT_MULTIBYTE_EDIT)
+    int dpy_chars;
+    int lft_chars;
+
+#if defined(DEBUG_EDIT)
+    int all_chars;
+    int pos_chars;
+#endif
+#endif
+
+    /* other data */
+    int i;
+    int padsize;
+    char *str;
+    int lft_shift = 0;
+    int rgt_shift = 0;
+
+#ifdef USE_COLOR_STYLE
+    int estyle;
+#endif
+    int prompting = 0;
+
+    (void) pos_bytes;
+
+    /*
+     * If we've made no changes, or if there is nothing to display, just leave.
+     */
+    if (!IsDirty || (DspWdth == 0))
+	return;
+
+    IsDirty = FALSE;
+
+    all_bytes = (int) strlen(&Buf[0]);
+    StrLen = all_bytes;
+
+    all_cells = LYstrCells(Buf);
+    pos_cells = LYstrExtent2(Buf, Pos);
+
+#if defined(SUPPORT_MULTIBYTE_EDIT) && defined(DEBUG_EDIT)
+    lft_chars = mbcs_glyphs(Buf, DspStart);
+    pos_chars = mbcs_glyphs(Buf, Pos);
+    all_chars = mbcs_glyphs(Buf, all_bytes);
+#endif
+
+    /*
+     * Now we have:
+     *                .--DspWdth---.
+     *      +---------+=============+-----------+
+     *      |         |M           M|           |   (M=margin)
+     *      +---------+=============+-----------+
+     *      0         DspStart                   StrLen
+     *
+     * Insertion point can be anywhere between 0 and stringlength.  Figure out
+     * new display starting point.
+     *
+     * The first "if" below makes Lynx scroll several columns at a time when
+     * extending the string.  Looks awful, but that way we can keep up with
+     * data entry at low baudrates.
+     */
+
+    lft_bytes = DspStart;
+    lft_cells = LYstrExtent2(Buf, DspStart);
+
+    if ((lft_cells + DspWdth) <= all_cells) {
+	if (pos_cells >= (lft_cells + DspWdth) - Margin) {
+	    lft_cells = (pos_cells - DspWdth) + Margin;
+#ifdef SUPPORT_MULTIBYTE_EDIT
+	    lft_chars = cell2char(Buf, lft_cells);
+	    lft_bytes = mbcs_skip(Buf, lft_chars);
+#else
+	    lft_bytes = lft_cells;
+#endif /* SUPPORT_MULTIBYTE_EDIT */
+	}
+    }
+
+    if (pos_cells < lft_cells + Margin) {
+	lft_cells = pos_cells - Margin;
+	if (lft_cells < 0)
+	    lft_cells = 0;
+#ifdef SUPPORT_MULTIBYTE_EDIT
+	lft_chars = cell2char(Buf, lft_cells);
+	lft_bytes = mbcs_skip(Buf, lft_chars);
+#else
+	lft_bytes = lft_cells;
+#endif /* SUPPORT_MULTIBYTE_EDIT */
+    }
+
+    LYmove(StartY, StartX);
+
+    /*
+     * Draw the left scrolling-indicator now, to avoid the complication of
+     * overwriting part of a multicolumn character which may lie in the first
+     * position.
+     */
+    if (PanOn && lft_cells) {
+	CTRACE_EDIT((tfp, "Draw left scroll-indicator\n"));
+	TmpStyleOn(prompting ? s_prompt_edit_arr : s_aedit_arr);
+	LYmove(StartY, StartX);
+	LYaddch(ACS_LARROW);
+	TmpStyleOff(prompting ? s_prompt_edit_arr : s_aedit_arr);
+	lft_shift = 1;
+    }
+
+    str = &Buf[lft_bytes];
+    DspStart = lft_bytes;
+
+    dpy_cells = all_cells - lft_cells;
+    CTRACE_EDIT((tfp, "Comparing dpy_cells %d > (%d - %d)\n",
+		 dpy_cells, DspWdth, lft_shift));
+    if (dpy_cells > (DspWdth - lft_shift)) {
+	rgt_shift = 1;
+	dpy_cells = (DspWdth - lft_shift - rgt_shift);
+    }
+    for (;;) {
+#ifdef SUPPORT_MULTIBYTE_EDIT
+	dpy_chars = cell2char(str, dpy_cells);
+	dpy_bytes = mbcs_skip(str, dpy_chars);
+#else
+	dpy_bytes = dpy_cells;
+#endif /* SUPPORT_MULTIBYTE_EDIT */
+	/*
+	 * The last character on the display may be multicolumn, and if we take
+	 * away a single cell for the right scroll-indicator, that would force
+	 * us to display fewer characters.  Check for that, and recompute.
+	 */
+	if (rgt_shift) {
+	    int old_cells = dpy_cells;
+
+	    dpy_cells = LYstrExtent2(str, dpy_bytes);
+	    if (dpy_cells > old_cells)
+		dpy_cells = old_cells - 1;
+
+	    CTRACE_EDIT((tfp, "Comparing cells %d vs %d\n", dpy_cells, old_cells));
+	    if (dpy_cells < old_cells) {
+		CTRACE_EDIT((tfp, "Recomputing...\n"));
+		continue;
+	    }
+	}
+	break;
+    }
+
+    CTRACE_EDIT((tfp, "BYTES left %2d pos %2d dpy %2d all %2d\n",
+		 lft_bytes, pos_bytes, dpy_bytes, all_bytes));
+    CTRACE_EDIT((tfp, "CELLS left %2d pos %2d dpy %2d all %2d\n",
+		 lft_cells, pos_cells, dpy_cells, all_cells));
+    CTRACE_EDIT((tfp, "CHARS left %2d pos %2d dpy %2d all %2d\n",
+		 lft_chars, pos_chars, dpy_chars, all_chars));
+
+#ifdef USE_COLOR_STYLE
+    /*
+     * If this is the last screen line, set attributes to normal, should only
+     * be needed for color styles.  The curses function may be used directly to
+     * avoid complications.  - kw
+     */
+    if (StartY == (LYlines - 1))
+	prompting = 1;
+    if (prompting) {
+	estyle = s_prompt_edit;
+    } else {
+	estyle = s_aedit;
+    }
+    CTRACE2(TRACE_STYLE,
+	    (tfp, "STYLE.getstr: switching to <edit.%s>.\n",
+	     prompting ? "prompt" : "active"));
+    if (estyle != NOSTYLE) {
+	curses_style(estyle, STACK_ON);
+    } else {
+	(void) wattrset(LYwin, A_NORMAL);	/* need to do something about colors? */
+    }
+#endif
+    if (IsHidden) {
+	BOOL utf_flag = IS_UTF8_TTY;
+	int cell = 0;
+
+	fill_edited_line(0, dpy_cells, '*');
+
+	i = 0;
+	do {
+	    const char *last = str + i;
+	    const char *next = LYmbcs_skip_glyphs(last, 1, utf_flag);
+	    int j = (next - str);
+
+	    while (i < j) {
+		edit->offset2col[i++] = cell + StartX;
+	    }
+	    cell += LYstrExtent2(last, (next - last));
+	} while (i < dpy_bytes);
+	edit->offset2col[i] = cell + StartX;
+    } else {
+#if defined(ENHANCED_LINEEDIT) && defined(USE_COLOR_STYLE)
+	if (Mark >= 0 && DspStart > Mark)
+	    TmpStyleOn(prompting ? s_prompt_sel : s_aedit_sel);
+#endif
+	remember_column(edit, 0);
+	for (i = 0; i < dpy_bytes; i++) {
+#if defined(ENHANCED_LINEEDIT) && defined(USE_COLOR_STYLE)
+	    if (Mark >= 0 && ((DspStart + i == Mark && Pos > Mark)
+			      || (DspStart + i == Pos && Pos < Mark)))
+		TmpStyleOn(prompting ? s_prompt_sel : s_aedit_sel);
+	    if (Mark >= 0 && ((DspStart + i == Mark && Pos < Mark)
+			      || (DspStart + i == Pos && Pos > Mark)))
+		TmpStyleOff(prompting ? s_prompt_sel : s_aedit_sel);
+#endif
+	    if (str[i] == 1 || str[i] == 2 ||
+		(UCH(str[i]) == 160 &&
+		 !(HTPassHighCtrlRaw || IS_CJK_TTY ||
+		   (LYCharSet_UC[current_char_set].enc != UCT_ENC_8859 &&
+		    !(LYCharSet_UC[current_char_set].like8859
+		      & UCT_R_8859SPECL))))) {
+		LYaddch(' ');
+	    } else if (str[i] == '\t') {
+		int col = edit->offset2col[i] - StartX;
+
+		/*
+		 * Like LYwaddnstr(), expand tabs from the beginning of the
+		 * field.
+		 */
+		while (++col % 8)
+		    LYaddch(' ');
+		LYaddch(' ');
+	    } else {
+		LYaddch(UCH(str[i]));
+	    }
+	    remember_column(edit, i + 1);
+	}
+#if defined(ENHANCED_LINEEDIT) && defined(USE_COLOR_STYLE)
+	if (Mark >= 0 &&
+	    ((DspStart + dpy_bytes <= Mark && DspStart + dpy_bytes > Pos)
+	     || (DspStart + dpy_bytes > Mark
+		 && DspStart + dpy_bytes <= Pos))) {
+	    TmpStyleOff(prompting ? s_prompt_sel : s_aedit_sel);
+	}
+#endif
+    }
+
+    /*
+     * Erase rest of input area.
+     */
+    padsize = DspWdth - (edit->offset2col[dpy_bytes] - StartX);
+    fill_edited_line(prompting, padsize, PadChar);
+
+    /*
+     * Scrolling indicators.
+     */
+    if (PanOn && dpy_bytes && rgt_shift) {
+	CTRACE((tfp, "Draw right-scroller offset (%d + %d)\n",
+		dpy_cells, lft_shift));
+	TmpStyleOn(prompting ? s_prompt_edit_arr : s_aedit_arr);
+	LYmove(StartY, StartX + dpy_cells + lft_shift);
+	LYaddch(ACS_RARROW);
+	TmpStyleOff(prompting ? s_prompt_edit_arr : s_aedit_arr);
+    }
+
+    /*
+     * Finally, move the cursor to the point where the next edit will occur.
+     */
+    LYmove(StartY, edit->offset2col[Pos - DspStart]);
+
+#ifdef USE_COLOR_STYLE
+    if (estyle != NOSTYLE)
+	curses_style(estyle, STACK_OFF);
+#endif
+    LYrefresh();
+}
+
+static void reinsertEdit(EditFieldData *edit, char *result)
+{
+    if (result != 0) {
+	LYEdit1(edit, '\0', LYE_ERASE, FALSE);
+	while (*result != '\0') {
+	    LYLineEdit(edit, (int) (*result), FALSE);
+	    result++;
+	}
+    }
+}
+
+static int caselessCmpList(const void *a,
+			   const void *b)
+{
+    return strcasecomp(*(const char *const *) a, *(const char *const *) b);
+}
+
+static int normalCmpList(const void *a,
+			 const void *b)
+{
+    return strcmp(*(const char *const *) a, *(const char *const *) b);
+}
+
+static char **sortedList(HTList *list, BOOL ignorecase)
+{
+    unsigned count = (unsigned) HTList_count(list);
+    unsigned j = 0;
+    unsigned k, jk;
+    char **result = typecallocn(char *, count + 1);
+
+    if (result == 0)
+	outofmem(__FILE__, "sortedList");
+
+    assert(result != 0);
+
+    while (!HTList_isEmpty(list))
+	result[j++] = (char *) HTList_nextObject(list);
+
+    if (count > 1) {
+	qsort((char *) result, count, sizeof(*result),
+	      ignorecase ? caselessCmpList : normalCmpList);
+
+	/* remove duplicate entries from the sorted index */
+	for (j = 0; result[j] != 0; j++) {
+	    k = j;
+	    while (result[k] != 0
+		   && !strcmp(result[j], result[k])) {
+		k++;
+	    }
+	    k--;
+	    if (j != k) {
+		for (jk = j;; jk++) {
+		    result[jk] = result[jk + k - j];
+		    if (result[jk] == 0)
+			break;
+		}
+	    }
+	}
+    }
+
+    return result;
+}
+
+int LYarrayLength(const char **list)
+{
+    int result = 0;
+
+    while (*list++ != 0)
+	result++;
+    return result;
+}
+
+int LYarrayWidth(const char **list)
+{
+    int result = 0;
+    int check;
+
+    while (*list != 0) {
+	check = (int) strlen(*list++);
+	if (check > result)
+	    result = check;
+    }
+    return result;
+}
+
+static void FormatChoiceNum(char *dst,
+			    int num_choices,
+			    int choice,
+			    const char *value)
+{
+    if (num_choices >= 0) {
+	int digits = (num_choices > 9) ? 2 : 1;
+
+	sprintf(dst, "%*d: %.*s",
+		digits, (choice + 1),
+		MAX_LINE - 9 - digits, value);
+    } else {
+	LYstrncpy(dst, value, MAX_LINE - 1);
+    }
+}
+
+static unsigned options_width(const char **list)
+{
+    unsigned width = 0;
+    int count = 0;
+
+    while (list[count] != 0) {
+	unsigned ncells = LYstrCells(list[count]);
+
+	if (ncells > width) {
+	    width = ncells;
+	}
+	count++;
+    }
+    return width;
+}
+
+static void draw_option(WINDOW * win, int entry,
+			int width,
+			BOOL reversed,
+			int num_choices,
+			int number,
+			const char *value)
+{
+    char Cnum[MAX_LINE];
+
+    (void) width;
+
+    FormatChoiceNum(Cnum, num_choices, number, "");
+#ifdef USE_SLANG
+    SLsmg_gotorc(win->top_y + entry, (win->left_x + 2));
+    LYaddstr(Cnum);
+    if (reversed)
+	SLsmg_set_color(2);
+    SLsmg_write_nstring((char *) value, win->width);
+    if (reversed)
+	SLsmg_set_color(0);
+#else
+    wmove(win, entry, 1);
+    LynxWChangeStyle(win, s_menu_entry, STACK_ON);
+    waddch(win, ' ');
+    LynxWChangeStyle(win, s_menu_entry, STACK_OFF);
+    LynxWChangeStyle(win, s_menu_number, STACK_ON);
+    waddstr(win, Cnum);
+    LynxWChangeStyle(win, s_menu_number, STACK_OFF);
+#ifdef USE_COLOR_STYLE
+    LynxWChangeStyle(win, reversed ? s_menu_active : s_menu_entry, STACK_ON);
+#else
+    if (reversed)
+	wstart_reverse(win);
+#endif
+    LYpaddstr(win, width, value);
+#ifdef USE_COLOR_STYLE
+    LynxWChangeStyle(win, reversed ? s_menu_active : s_menu_entry, STACK_OFF);
+#else
+    if (reversed)
+	wstop_reverse(win);
+#endif
+    LynxWChangeStyle(win, s_menu_entry, STACK_ON);
+    waddch(win, ' ');
+    LynxWChangeStyle(win, s_menu_entry, STACK_OFF);
+#endif /* USE_SLANG */
+}
+
+/*
+ * This function offers the choices for values of an option via a popup window
+ * which functions like that for selection of options in a form.  - FM
+ *
+ * Also used for mouse popups with ncurses; this is indicated by for_mouse.
+ */
+int LYhandlePopupList(int cur_choice,
+		      int ly,
+		      int lx,
+		      const char **choices,
+		      int width,
+		      int i_length,
+		      int disabled,
+		      BOOLEAN for_mouse)
+{
+    BOOLEAN numbered = (BOOLEAN) (keypad_mode != NUMBERS_AS_ARROWS);
+    int c = 0, cmd = 0, i = 0, j = 0, rel = 0;
+    int orig_choice;
+    WINDOW *form_window;
+    int num_choices = 0;
+    int max_choices = 0;
+    int top, bottom, length = -1;
+    int window_offset = 0;
+    int lines_to_show;
+    char Cnum[64];
+    int Lnum;
+    int npages;
+    static char prev_target[MAX_LINE];	/* Search string buffer */
+    static char prev_target_buffer[MAX_LINE];	/* Next search buffer */
+    static BOOL first = TRUE;
+    char *cp;
+    int ch = 0;
+    RecallType recall;
+    int QueryTotal;
+    int QueryNum;
+    BOOLEAN FirstRecall = TRUE;
+    BOOLEAN ReDraw = FALSE;
+    int number;
+    char buffer[MAX_LINE];
+    const char *popup_status_msg = NULL;
+    const char **Cptr = NULL;
+
+#define CAN_SCROLL_DOWN	1
+#define CAN_SCROLL_UP	2
+#define CAN_SCROLL	4
+    int can_scroll = 0, can_scroll_was = 0;
+
+    orig_choice = cur_choice;
+    if (cur_choice < 0)
+	cur_choice = 0;
+
+    /*
+     * Initialize the search string buffer. - FM
+     */
+    if (first) {
+	*prev_target_buffer = '\0';
+	first = FALSE;
+    }
+    *prev_target = '\0';
+    QueryTotal = (search_queries ? HTList_count(search_queries) : 0);
+    recall = ((QueryTotal >= 1) ? RECALL_URL : NORECALL);
+    QueryNum = QueryTotal;
+
+    /*
+     * Count the number of choices to be displayed, where num_choices ranges
+     * from 0 to n, and set width to the longest choice string length.  Also
+     * set Lnum to the length for the highest choice number, then decrement
+     * num_choices so as to be zero-based.  The window width will be based on
+     * the sum of width and Lnum.  - FM
+     */
+    num_choices = LYarrayLength(choices) - 1;
+    if (width <= 0)
+	width = (int) options_width(choices);
+    if (numbered) {
+	sprintf(Cnum, "%d: ", num_choices);
+	Lnum = (int) strlen(Cnum);
+	max_choices = num_choices;
+    } else {
+	Lnum = 0;
+	max_choices = -1;
+    }
+
+    /*
+     * Let's assume for the sake of sanity that ly is the number corresponding
+     * to the line the choice is on.
+     *
+     * Let's also assume that cur_choice is the number of the item that should
+     * be initially selected, as 0 being the first item.
+     *
+     * So what we have, is the top equal to the current screen line subtracting
+     * the cur_choice + 1 (the one must be for the top line we will draw in a
+     * box).  If the top goes under 0, consider it 0.
+     */
+    top = ly - (cur_choice + 1);
+    if (top < 0)
+	top = 0;
+
+    /*
+     * Check and see if we need to put the i_length parameter up to the number
+     * of real choices.
+     */
+    if (i_length < 1) {
+	i_length = num_choices;
+    } else {
+	/*
+	 * Otherwise, it is really one number too high.
+	 */
+	i_length--;
+    }
+
+    /*
+     * The bottom is the value of the top plus the number of options to view
+     * plus 3 (one for the top line, one for the bottom line, and one to offset
+     * the 0 counted in the num_choices).
+     */
+    bottom = top + i_length + 3;
+
+    /*
+     * Set lines_to_show based on the user_mode global.
+     */
+    if (user_mode == NOVICE_MODE)
+	lines_to_show = LYlines - 4;
+    else
+	lines_to_show = LYlines - 2;
+
+    if (for_mouse && user_mode == NOVICE_MODE && lines_to_show > 2)
+	lines_to_show--;
+
+    /*
+     * Hmm...  If the bottom goes beyond the number of lines available,
+     */
+    if (bottom > lines_to_show) {
+	/*
+	 * Position the window at the top if we have more choices than will fit
+	 * in the window.
+	 */
+	if ((i_length + 3) > lines_to_show) {
+	    top = 0;
+	    bottom = (top + (i_length + 3));
+	    if (bottom > lines_to_show)
+		bottom = (lines_to_show + 1);
+	} else {
+	    /*
+	     * Try to position the window so that the selected choice will
+	     * appear where the selection box currently is positioned.  It
+	     * could end up too high, at this point, but we'll move it down
+	     * latter, if that has happened.
+	     */
+	    top = (lines_to_show + 1) - (i_length + 3);
+	    bottom = (lines_to_show + 1);
+	}
+    }
+
+    /*
+     * This is really fun, when the length is 4, it means 0 to 4, or 5.
+     */
+    length = (bottom - top) - 2;
+    if (length <= num_choices)
+	can_scroll = CAN_SCROLL;
+
+    /*
+     * Move the window down if it's too high.
+     */
+    if (bottom < ly + 2) {
+	bottom = ly + 2;
+	if (bottom > lines_to_show + 1)
+	    bottom = lines_to_show + 1;
+	top = bottom - length - 2;
+    }
+
+    if (for_mouse) {
+	int check = (Lnum + (int) width + 4);
+	int limit = LYcols;
+
+	/* shift horizontally to lie within screen width, if possible */
+	if (check < limit) {
+	    if (lx - 1 + check > limit)
+		lx = limit + 1 - check;
+	    else if (lx <= 0)
+		lx = 1;
+	}
+    }
+
+    /*
+     * Set up the overall window, including the boxing characters ('*'), if it
+     * all fits.  Otherwise, set up the widest window possible.  - FM
+     */
+    width += Lnum;
+    bottom -= top;
+
+    if (num_choices <= 0
+	|| cur_choice > num_choices
+	|| (form_window = LYstartPopup(&top,
+				       &lx,
+				       &bottom,
+				       &width)) == 0)
+	return (orig_choice);
+
+    width -= Lnum;
+    bottom += top;
+
+    /*
+     * Clear the command line and write the popup statusline.  - FM
+     */
+    if (disabled) {
+	popup_status_msg = CHOICE_LIST_UNM_MSG;
+    } else if (!for_mouse) {
+	popup_status_msg = CHOICE_LIST_MESSAGE;
+#if defined(USE_MOUSE) && (defined(NCURSES) || defined(PDCURSES))
+    } else {
+	popup_status_msg =
+	    gettext("Left mouse button or return to select, arrow keys to scroll.");
+#endif
+    }
+    _statusline(popup_status_msg);
+
+    /*
+     * Set up the window_offset for choices.
+     * cur_choice ranges from 0...n
+     * length ranges from 0...m
+     */
+    if (cur_choice >= length) {
+	window_offset = cur_choice - length + 1;
+    }
+
+    /*
+     * Compute the number of popup window pages.  - FM
+     */
+    npages = ((num_choices + 1) > length) ?
+	(((num_choices + 1) + (length - 1)) / (length))
+	: 1;
+    /*
+     * OH!  I LOVE GOTOs!  hack hack hack
+     */
+  redraw:
+
+    /*
+     * Display the boxed choices.
+     */
+    for (i = 0; i <= num_choices; i++) {
+	if (i >= window_offset && i - window_offset < length) {
+	    draw_option(form_window, ((i + 1) - window_offset), width, FALSE,
+			max_choices, i, choices[i]);
+	}
+    }
+    LYbox(form_window, (BOOLEAN) !numbered);
+    Cptr = NULL;
+
+    /*
+     * Loop on user input.
+     */
+    while (cmd != LYK_ACTIVATE) {
+	int row = ((i + 1) - window_offset);
+
+	/* Show scroll indicators. */
+	if (can_scroll) {
+	    can_scroll = ((window_offset ? CAN_SCROLL_UP : 0)
+			  | (num_choices - window_offset >= length
+			     ? CAN_SCROLL_DOWN : 0));
+	    if (~can_scroll & can_scroll_was) {		/* Need to redraw */
+		LYbox(form_window, (BOOLEAN) !numbered);
+		can_scroll_was = 0;
+	    }
+	    if (can_scroll & ~can_scroll_was & CAN_SCROLL_UP) {
+		wmove(form_window, 1, Lnum + width + 3);
+		LynxWChangeStyle(form_window, s_menu_sb, STACK_ON);
+		waddch(form_window, ACS_UARROW);
+		LynxWChangeStyle(form_window, s_menu_sb, STACK_OFF);
+	    }
+	    if (can_scroll & ~can_scroll_was & CAN_SCROLL_DOWN) {
+		wmove(form_window, length, Lnum + width + 3);
+		LynxWChangeStyle(form_window, s_menu_sb, STACK_ON);
+		waddch(form_window, ACS_DARROW);
+		LynxWChangeStyle(form_window, s_menu_sb, STACK_OFF);
+	    }
+	}
+
+	/*
+	 * Unreverse cur choice.
+	 */
+	if (Cptr != NULL) {
+	    draw_option(form_window, row, width, FALSE,
+			max_choices, i, Cptr[i]);
+	}
+	Cptr = choices;
+	i = cur_choice;
+	row = ((cur_choice + 1) - window_offset);
+	draw_option(form_window, row, width, TRUE,
+		    max_choices, cur_choice, Cptr[cur_choice]);
+	LYstowCursor(form_window, row, 1);
+
+	c = LYgetch_choice();
+	if (term_options || LYCharIsINTERRUPT(c)) {	/* Control-C or Control-G */
+	    cmd = LYK_QUIT;
+#ifndef USE_SLANG
+	} else if (c == MOUSE_KEY) {
+	    if ((cmd = fancy_mouse(form_window, row, &cur_choice)) < 0)
+		goto redraw;
+	    if (cmd == LYK_ACTIVATE)
+		break;
+#endif
+	} else {
+	    cmd = LKC_TO_LAC(keymap, c);
+	}
+#ifdef VMS
+	if (HadVMSInterrupt) {
+	    HadVMSInterrupt = FALSE;
+	    cmd = LYK_QUIT;
+	}
+#endif /* VMS */
+
+	switch (cmd) {
+	case LYK_F_LINK_NUM:
+	    c = '\0';
+	    /* FALLTHRU */
+	case LYK_1:		/* FALLTHRU */
+	case LYK_2:		/* FALLTHRU */
+	case LYK_3:		/* FALLTHRU */
+	case LYK_4:		/* FALLTHRU */
+	case LYK_5:		/* FALLTHRU */
+	case LYK_6:		/* FALLTHRU */
+	case LYK_7:		/* FALLTHRU */
+	case LYK_8:		/* FALLTHRU */
+	case LYK_9:
+	    /*
+	     * Get a number from the user, possibly with a 'g' or 'p' suffix
+	     * (which will be loaded into c).  - FM & LE
+	     */
+	    number = get_popup_number(SELECT_OPTION_NUMBER, &c, &rel);
+
+	    /* handle + or - suffix */
+	    CTRACE((tfp, "got popup option number %d, ", number));
+	    CTRACE((tfp, "rel='%c', c='%c', cur_choice=%d\n",
+		    rel, c, cur_choice));
+	    if (c == 'p') {
+		int curpage = ((cur_choice + 1) > length) ?
+		(((cur_choice + 1) + (length - 1)) / (length))
+		: 1;
+
+		CTRACE((tfp, "  curpage=%d\n", curpage));
+		if (rel == '+')
+		    number = curpage + number;
+		else if (rel == '-')
+		    number = curpage - number;
+	    } else if (rel == '+') {
+		number = cur_choice + number + 1;
+	    } else if (rel == '-') {
+		number = cur_choice - number + 1;
+	    }
+	    if (rel)
+		CTRACE((tfp, "new number=%d\n", number));
+	    /*
+	     * Check for a 'p' suffix.  - FM
+	     */
+	    if (c == 'p') {
+		/*
+		 * Treat 1 or less as the first page.  - FM
+		 */
+		if (number <= 1) {
+		    if (window_offset == 0) {
+			HTUserMsg(ALREADY_AT_OPTION_BEGIN);
+			_statusline(popup_status_msg);
+			break;
+		    }
+		    window_offset = 0;
+		    cur_choice = 0;
+		    _statusline(popup_status_msg);
+		    goto redraw;
+		}
+
+		/*
+		 * Treat a number equal to or greater than the number of pages
+		 * as the last page.  - FM
+		 */
+		if (number >= npages) {
+		    if (window_offset >= ((num_choices - length) + 1)) {
+			HTUserMsg(ALREADY_AT_OPTION_END);
+			_statusline(popup_status_msg);
+			break;
+		    }
+		    window_offset = ((npages - 1) * length);
+		    if (window_offset > (num_choices - length)) {
+			window_offset = (num_choices - length + 1);
+		    }
+		    if (cur_choice < window_offset)
+			cur_choice = window_offset;
+		    _statusline(popup_status_msg);
+		    goto redraw;
+		}
+
+		/*
+		 * We want an intermediate page.  - FM
+		 */
+		if (((number - 1) * length) == window_offset) {
+		    char *msg = 0;
+
+		    HTSprintf0(&msg, ALREADY_AT_OPTION_PAGE, number);
+		    HTUserMsg(msg);
+		    FREE(msg);
+		    _statusline(popup_status_msg);
+		    break;
+		}
+		cur_choice = window_offset = ((number - 1) * length);
+		_statusline(popup_status_msg);
+		goto redraw;
+
+	    }
+
+	    /*
+	     * Check for a positive number, which signifies that a choice
+	     * should be sought.  - FM
+	     */
+	    if (number > 0) {
+		/*
+		 * Decrement the number so as to correspond with our cur_choice
+		 * values.  - FM
+		 */
+		number--;
+
+		/*
+		 * If the number is in range and had no legal suffix, select
+		 * the indicated choice.  - FM
+		 */
+		if (number <= num_choices && c == '\0') {
+		    cur_choice = number;
+		    cmd = LYK_ACTIVATE;
+		    break;
+		}
+
+		/*
+		 * Verify that we had a 'g' suffix, and act on the number.  -
+		 * FM
+		 */
+		if (c == 'g') {
+		    if (cur_choice == number) {
+			/*
+			 * The choice already is current.  - FM
+			 */
+			char *msg = 0;
+
+			HTSprintf0(&msg, OPTION_ALREADY_CURRENT, (number + 1));
+			HTUserMsg(msg);
+			FREE(msg);
+			_statusline(popup_status_msg);
+			break;
+		    }
+
+		    if (number <= num_choices) {
+			/*
+			 * The number is in range and had a 'g' suffix, so make
+			 * it the current option, scrolling if needed.  - FM
+			 */
+			j = (number - cur_choice);
+			cur_choice = number;
+			if ((j > 0) &&
+			    (cur_choice - window_offset) >= length) {
+			    window_offset += j;
+			    if (window_offset > (num_choices - length + 1))
+				window_offset = (num_choices - length + 1);
+			} else if ((cur_choice - window_offset) < 0) {
+			    window_offset -= abs(j);
+			    if (window_offset < 0)
+				window_offset = 0;
+			}
+			_statusline(popup_status_msg);
+			goto redraw;
+		    }
+
+		    /*
+		     * Not in range.  - FM
+		     */
+		    HTUserMsg(BAD_OPTION_NUM_ENTERED);
+		}
+	    }
+
+	    /*
+	     * Restore the popup statusline.  - FM
+	     */
+	    _statusline(popup_status_msg);
+	    break;
+
+	case LYK_PREV_LINK:
+	case LYK_LPOS_PREV_LINK:
+	case LYK_FASTBACKW_LINK:
+	case LYK_UP_LINK:
+
+	    if (cur_choice > 0)
+		cur_choice--;
+
+	    /*
+	     * Scroll the window up if necessary.
+	     */
+	    if ((cur_choice - window_offset) < 0) {
+		window_offset--;
+		goto redraw;
+	    }
+	    break;
+
+	case LYK_NEXT_LINK:
+	case LYK_LPOS_NEXT_LINK:
+	case LYK_FASTFORW_LINK:
+	case LYK_DOWN_LINK:
+	    if (cur_choice < num_choices)
+		cur_choice++;
+
+	    /*
+	     * Scroll the window down if necessary
+	     */
+	    if ((cur_choice - window_offset) >= length) {
+		window_offset++;
+		goto redraw;
+	    }
+	    break;
+
+	case LYK_NEXT_PAGE:
+	    /*
+	     * Okay, are we on the last page of the list?  If not then,
+	     */
+	    if (window_offset != (num_choices - length + 1)) {
+		/*
+		 * Modify the current choice to not be a coordinate in the
+		 * list, but a coordinate on the item selected in the window.
+		 */
+		cur_choice -= window_offset;
+
+		/*
+		 * Page down the proper length for the list.  If simply to far,
+		 * back up.
+		 */
+		window_offset += length;
+		if (window_offset > (num_choices - length)) {
+		    window_offset = (num_choices - length + 1);
+		}
+
+		/*
+		 * Readjust the current selection to be a list coordinate
+		 * rather than window.  Redraw this thing.
+		 */
+		cur_choice += window_offset;
+		goto redraw;
+	    } else if (cur_choice < num_choices) {
+		/*
+		 * Already on last page of the list so just redraw it with the
+		 * last item selected.
+		 */
+		cur_choice = num_choices;
+	    }
+	    break;
+
+	case LYK_PREV_PAGE:
+	    /*
+	     * Are we on the first page of the list?  If not then,
+	     */
+	    if (window_offset != 0) {
+		/*
+		 * Modify the current selection to not be a list coordinate,
+		 * but a window coordinate.
+		 */
+		cur_choice -= window_offset;
+
+		/*
+		 * Page up the proper length.  If too far, back up.
+		 */
+		window_offset -= length;
+		if (window_offset < 0) {
+		    window_offset = 0;
+		}
+
+		/*
+		 * Readjust the current choice.
+		 */
+		cur_choice += window_offset;
+		goto redraw;
+	    } else if (cur_choice > 0) {
+		/*
+		 * Already on the first page so just back up to the first item.
+		 */
+		cur_choice = 0;
+	    }
+	    break;
+
+	case LYK_HOME:
+	    cur_choice = 0;
+	    if (window_offset > 0) {
+		window_offset = 0;
+		goto redraw;
+	    }
+	    break;
+
+	case LYK_END:
+	    cur_choice = num_choices;
+	    if (window_offset != (num_choices - length + 1)) {
+		window_offset = (num_choices - length + 1);
+		goto redraw;
+	    }
+	    break;
+
+	case LYK_DOWN_TWO:
+	    cur_choice += 2;
+	    if (cur_choice > num_choices)
+		cur_choice = num_choices;
+
+	    /*
+	     * Scroll the window down if necessary.
+	     */
+	    if ((cur_choice - window_offset) >= length) {
+		window_offset += 2;
+		if (window_offset > (num_choices - length + 1))
+		    window_offset = (num_choices - length + 1);
+		goto redraw;
+	    }
+	    break;
+
+	case LYK_UP_TWO:
+	    cur_choice -= 2;
+	    if (cur_choice < 0)
+		cur_choice = 0;
+
+	    /*
+	     * Scroll the window up if necessary.
+	     */
+	    if ((cur_choice - window_offset) < 0) {
+		window_offset -= 2;
+		if (window_offset < 0)
+		    window_offset = 0;
+		goto redraw;
+	    }
+	    break;
+
+	case LYK_DOWN_HALF:
+	    cur_choice += (length / 2);
+	    if (cur_choice > num_choices)
+		cur_choice = num_choices;
+
+	    /*
+	     * Scroll the window down if necessary.
+	     */
+	    if ((cur_choice - window_offset) >= length) {
+		window_offset += (length / 2);
+		if (window_offset > (num_choices - length + 1))
+		    window_offset = (num_choices - length + 1);
+		goto redraw;
+	    }
+	    break;
+
+	case LYK_UP_HALF:
+	    cur_choice -= (length / 2);
+	    if (cur_choice < 0)
+		cur_choice = 0;
+
+	    /*
+	     * Scroll the window up if necessary.
+	     */
+	    if ((cur_choice - window_offset) < 0) {
+		window_offset -= (length / 2);
+		if (window_offset < 0)
+		    window_offset = 0;
+		goto redraw;
+	    }
+	    break;
+
+	case LYK_REFRESH:
+	    lynx_force_repaint();
+	    LYrefresh();
+	    break;
+
+	case LYK_NEXT:
+	    if (recall && *prev_target_buffer == '\0') {
+		/*
+		 * We got a 'n'ext command with no prior query specified within
+		 * the popup window.  See if one was entered when the popup was
+		 * retracted, and if so, assume that's what's wanted.  Note
+		 * that it will become the default within popups, unless
+		 * another is entered within a popup.  If the within popup
+		 * default is to be changed at that point, use WHEREIS ('/')
+		 * and enter it, or the up- or down-arrow keys to seek any of
+		 * the previously entered queries, regardless of whether they
+		 * were entered within or outside of a popup window.  - FM
+		 */
+		if ((cp = (char *) HTList_objectAt(search_queries,
+						   0)) != NULL) {
+		    LYstrncpy(prev_target_buffer,
+			      cp,
+			      sizeof(prev_target_buffer) - 1);
+		    QueryNum = 0;
+		    FirstRecall = FALSE;
+		}
+	    }
+	    strcpy(prev_target, prev_target_buffer);
+	    /* FALLTHRU */
+	case LYK_WHEREIS:
+	    if (*prev_target == '\0') {
+		_statusline(ENTER_WHEREIS_QUERY);
+		if ((ch = LYgetstr(prev_target, VISIBLE,
+				   sizeof(prev_target_buffer),
+				   recall)) < 0) {
+		    /*
+		     * User cancelled the search via ^G.  - FM
+		     */
+		    HTInfoMsg(CANCELLED);
+		    goto restore_popup_statusline;
+		}
+	    }
+
+	  check_recall:
+	    if (*prev_target == '\0' &&
+		!(recall && (ch == UPARROW || ch == DNARROW))) {
+		/*
+		 * No entry.  Simply break.  - FM
+		 */
+		HTInfoMsg(CANCELLED);
+		goto restore_popup_statusline;
+	    }
+
+	    if (recall && ch == UPARROW) {
+		if (FirstRecall) {
+		    /*
+		     * Use the current string or last query in the list.  - FM
+		     */
+		    FirstRecall = FALSE;
+		    if (*prev_target_buffer) {
+			for (QueryNum = (QueryTotal - 1);
+			     QueryNum > 0; QueryNum--) {
+			    if ((cp = (char *) HTList_objectAt(search_queries,
+							       QueryNum))
+				!= NULL &&
+				!strcmp(prev_target_buffer, cp)) {
+				break;
+			    }
+			}
+		    } else {
+			QueryNum = 0;
+		    }
+		} 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) {
+		    LYstrncpy(prev_target, cp, sizeof(prev_target) - 1);
+		    if (*prev_target_buffer &&
+			!strcmp(prev_target_buffer, prev_target)) {
+			_statusline(EDIT_CURRENT_QUERY);
+		    } else if ((*prev_target_buffer && QueryTotal == 2) ||
+			       (!(*prev_target_buffer) &&
+				QueryTotal == 1)) {
+			_statusline(EDIT_THE_PREV_QUERY);
+		    } else {
+			_statusline(EDIT_A_PREV_QUERY);
+		    }
+		    if ((ch = LYgetstr(prev_target, VISIBLE,
+				       sizeof(prev_target_buffer), recall)) < 0) {
+			/*
+			 * User cancelled the search via ^G.  - FM
+			 */
+			HTInfoMsg(CANCELLED);
+			goto restore_popup_statusline;
+		    }
+		    goto check_recall;
+		}
+	    } else if (recall && ch == DNARROW) {
+		if (FirstRecall) {
+		    /*
+		     * Use the current string or first query in the list.  - FM
+		     */
+		    FirstRecall = FALSE;
+		    if (*prev_target_buffer) {
+			for (QueryNum = 0;
+			     QueryNum < (QueryTotal - 1); QueryNum++) {
+			    if ((cp = (char *) HTList_objectAt(search_queries,
+							       QueryNum))
+				!= NULL &&
+				!strcmp(prev_target_buffer, cp)) {
+				break;
+			    }
+			}
+		    } else {
+			QueryNum = (QueryTotal - 1);
+		    }
+		} 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) {
+		    LYstrncpy(prev_target, cp, sizeof(prev_target) - 1);
+		    if (*prev_target_buffer &&
+			!strcmp(prev_target_buffer, prev_target)) {
+			_statusline(EDIT_CURRENT_QUERY);
+		    } else if ((*prev_target_buffer &&
+				QueryTotal == 2) ||
+			       (!(*prev_target_buffer) &&
+				QueryTotal == 1)) {
+			_statusline(EDIT_THE_PREV_QUERY);
+		    } else {
+			_statusline(EDIT_A_PREV_QUERY);
+		    }
+		    if ((ch = LYgetstr(prev_target, VISIBLE,
+				       sizeof(prev_target_buffer),
+				       recall)) < 0) {
+			/*
+			 * User cancelled the search via ^G. - FM
+			 */
+			HTInfoMsg(CANCELLED);
+			goto restore_popup_statusline;
+		    }
+		    goto check_recall;
+		}
+	    }
+	    /*
+	     * Replace the search string buffer with the new target.  - FM
+	     */
+	    strcpy(prev_target_buffer, prev_target);
+	    HTAddSearchQuery(prev_target_buffer);
+
+	    /*
+	     * Start search at the next choice.  - FM
+	     */
+	    for (j = 1; Cptr[i + j] != NULL; j++) {
+		FormatChoiceNum(buffer, max_choices, (i + j), Cptr[i + j]);
+		if (case_sensitive) {
+		    if (strstr(buffer, prev_target_buffer) != NULL)
+			break;
+		} else {
+		    if (LYstrstr(buffer, prev_target_buffer) != NULL)
+			break;
+		}
+	    }
+	    if (Cptr[i + j] != NULL) {
+		/*
+		 * We have a hit, so make that choice the current.  - FM
+		 */
+		cur_choice += j;
+		/*
+		 * Scroll the window down if necessary.
+		 */
+		if ((cur_choice - window_offset) >= length) {
+		    window_offset += j;
+		    if (window_offset > (num_choices - length + 1))
+			window_offset = (num_choices - length + 1);
+		    ReDraw = TRUE;
+		}
+		goto restore_popup_statusline;
+	    }
+
+	    /*
+	     * If we started at the beginning, it can't be present.  - FM
+	     */
+	    if (cur_choice == 0) {
+		HTUserMsg2(STRING_NOT_FOUND, prev_target_buffer);
+		goto restore_popup_statusline;
+	    }
+
+	    /*
+	     * Search from the beginning to the current choice.  - FM
+	     */
+	    for (j = 0; j < cur_choice; j++) {
+		FormatChoiceNum(buffer, max_choices, (j + 1), Cptr[j]);
+		if (case_sensitive) {
+		    if (strstr(buffer, prev_target_buffer) != NULL)
+			break;
+		} else {
+		    if (LYstrstr(buffer, prev_target_buffer) != NULL)
+			break;
+		}
+	    }
+	    if (j < cur_choice) {
+		/*
+		 * We have a hit, so make that choice the current.  - FM
+		 */
+		j = (cur_choice - j);
+		cur_choice -= j;
+		/*
+		 * Scroll the window up if necessary.
+		 */
+		if ((cur_choice - window_offset) < 0) {
+		    window_offset -= j;
+		    if (window_offset < 0)
+			window_offset = 0;
+		    ReDraw = TRUE;
+		}
+		goto restore_popup_statusline;
+	    }
+
+	    /*
+	     * Didn't find it in the preceding choices either.  - FM
+	     */
+	    HTUserMsg2(STRING_NOT_FOUND, prev_target_buffer);
+
+	  restore_popup_statusline:
+	    /*
+	     * Restore the popup statusline and reset the search variables.  -
+	     * FM
+	     */
+	    _statusline(popup_status_msg);
+	    *prev_target = '\0';
+	    QueryTotal = (search_queries ? HTList_count(search_queries)
+			  : 0);
+	    recall = ((QueryTotal >= 1) ? RECALL_URL : NORECALL);
+	    QueryNum = QueryTotal;
+	    if (ReDraw == TRUE) {
+		ReDraw = FALSE;
+		goto redraw;
+	    }
+	    break;
+
+	case LYK_QUIT:
+	case LYK_ABORT:
+	case LYK_PREV_DOC:
+	case LYK_INTERRUPT:
+	    cur_choice = orig_choice;
+	    cmd = LYK_ACTIVATE;	/* to exit */
+	    break;
+	}
+    }
+    LYstopPopup();
+
+    return (disabled ? orig_choice : cur_choice);
+}
+
+#define CurModif MyEdit.current_modifiers
+
+int LYgetstr(char *inputline,
+	     int hidden,
+	     size_t bufsize,
+	     RecallType recall)
+{
+    int x, y, MaxStringSize;
+    int ch;
+    int xlec = -2;
+    int last_xlec = -1;
+    int last_xlkc = -1;
+    EditFieldData MyEdit;
+
+#ifdef SUPPORT_MULTIBYTE_EDIT
+    BOOL refresh_mb = TRUE;
+#endif /* SUPPORT_MULTIBYTE_EDIT */
+
+    LYGetYX(y, x);		/* Use screen from cursor position to eol */
+    MaxStringSize = (int) ((bufsize < sizeof(MyEdit.buffer))
+			   ? (bufsize - 1)
+			   : (sizeof(MyEdit.buffer) - 1));
+    LYSetupEdit(&MyEdit, inputline, MaxStringSize, LYcolLimit - x);
+    MyEdit.hidden = (BOOL) hidden;
+
+    CTRACE((tfp, "called LYgetstr\n"));
+    for (;;) {
+      again:
+#ifndef SUPPORT_MULTIBYTE_EDIT
+	LYRefreshEdit(&MyEdit);
+#else /* SUPPORT_MULTIBYTE_EDIT */
+	if (refresh_mb)
+	    LYRefreshEdit(&MyEdit);
+#endif /* SUPPORT_MULTIBYTE_EDIT */
+	ch = LYReadCmdKey(FOR_PROMPT);
+#ifdef SUPPORT_MULTIBYTE_EDIT
+#ifdef CJK_EX			/* for SJIS code */
+	if (!refresh_mb
+	    && (EditBinding(ch) != LYE_CHAR))
+	    goto again;
+#else
+	if (!refresh_mb
+	    && (EditBinding(ch) != LYE_CHAR)
+	    && (EditBinding(ch) != LYE_AIX))
+	    goto again;
+#endif
+#endif /* SUPPORT_MULTIBYTE_EDIT */
+
+	if (term_letter || term_options
+#ifdef VMS
+	    || HadVMSInterrupt
+#endif /* VMS */
+#ifndef DISABLE_NEWS
+	    || term_message
+#endif
+	    ) {
+#ifdef VMS
+	    HadVMSInterrupt = FALSE;
+#endif /* VMS */
+	    ch = LYCharINTERRUPT2;
+	}
+
+	if (recall != NORECALL && (ch == UPARROW || ch == DNARROW)) {
+	    LYstrncpy(inputline, MyEdit.buffer, (int) bufsize);
+	    LYAddToCloset(recall, MyEdit.buffer);
+	    CTRACE((tfp, "LYgetstr(%s) recall\n", inputline));
+	    return (ch);
+	}
+	ch |= CurModif;
+	CurModif = 0;
+	if (last_xlkc != -1) {
+	    if (ch == last_xlkc)
+		ch |= LKC_MOD3;
+	    last_xlkc = -1;	/* consumed */
+	}
+#ifndef WIN_EX
+	if (LKC_TO_LAC(keymap, ch) == LYK_REFRESH)
+	    goto again;
+#endif
+	last_xlec = xlec;
+	xlec = EditBinding(ch);
+	if ((xlec & LYE_DF) && !(xlec & LYE_FORM_LAC)) {
+	    last_xlkc = ch;
+	    xlec &= ~LYE_DF;
+	} else {
+	    last_xlkc = -1;
+	}
+	switch (xlec) {
+	case LYE_SETM1:
+	    /*
+	     * Set flag for modifier 1.
+	     */
+	    CurModif |= LKC_MOD1;
+	    break;
+	case LYE_SETM2:
+	    /*
+	     * Set flag for modifier 2.
+	     */
+	    CurModif |= LKC_MOD2;
+	    break;
+	case LYE_TAB:
+	    if (xlec == last_xlec && recall != NORECALL) {
+		HTList *list = whichRecall(recall);
+
+		if (!HTList_isEmpty(list)) {
+		    char **data = sortedList(list, (BOOL) (recall == RECALL_CMD));
+		    int old_y, old_x;
+		    int cur_choice = 0;
+		    int num_options = LYarrayLength((const char **) data);
+
+		    while (cur_choice < num_options
+			   && strcasecomp(data[cur_choice], MyEdit.buffer) < 0)
+			cur_choice++;
+
+		    LYGetYX(old_y, old_x);
+		    cur_choice = LYhandlePopupList(cur_choice,
+						   0,
+						   old_x,
+						   (const char **) data,
+						   -1,
+						   -1,
+						   FALSE,
+						   FALSE);
+		    if (cur_choice >= 0) {
+			if (recall == RECALL_CMD)
+			    _statusline(": ");
+			reinsertEdit(&MyEdit, data[cur_choice]);
+		    }
+		    LYmove(old_y, old_x);
+		    FREE(data);
+		}
+	    } else {
+		reinsertEdit(&MyEdit, LYFindInCloset(recall, MyEdit.buffer));
+	    }
+	    break;
+
+#ifndef CJK_EX			/* 1997/11/03 (Mon) 20:13:45 */
+	case LYE_AIX:
+	    /*
+	     * Hex 97.
+	     * Treat as a character for CJK, or if this is a valid character in
+	     * the current display character set.  Otherwise, we treat this as
+	     * LYE_ENTER.
+	     */
+	    if (ch != '\t' &&
+		(IS_CJK_TTY ||
+		 LYlowest_eightbit[current_char_set] <= 0x97)) {
+		LYLineEdit(&MyEdit, ch, FALSE);
+		break;
+	    }
+	    /* FALLTHRU */
+#endif
+	case LYE_ENTER:
+	    /*
+	     * Terminate the string and return.
+	     */
+	    LYstrncpy(inputline, MyEdit.buffer, (int) bufsize);
+	    if (!hidden)
+		LYAddToCloset(recall, MyEdit.buffer);
+	    CTRACE((tfp, "LYgetstr(%s) LYE_ENTER\n", inputline));
+	    return (ch);
+
+#ifdef CAN_CUT_AND_PASTE
+	    /* 1998/10/01 (Thu) 15:05:49 */
+
+	case LYE_PASTE:
+	    {
+		unsigned char *s = (unsigned char *) get_clip_grab(), *e;
+		int len;
+
+		if (!s)
+		    break;
+		len = (int) strlen((const char *) s);
+		e = s + len;
+
+		if (len > 0) {
+		    unsigned char *e1 = s;
+
+		    while (e1 < e) {
+			if (*e1 < ' ') {	/* Stop here? */
+			    if (e1 > s)
+				LYEditInsert(&MyEdit, s, e1 - s, map_active, TRUE);
+			    s = e1;
+			    if (*e1 == '\t') {	/* Replace by space */
+				LYEditInsert(&MyEdit,
+					     (unsigned const char *) " ",
+					     1,
+					     map_active,
+					     TRUE);
+				s = ++e1;
+			    } else
+				break;
+			} else
+			    ++e1;
+		    }
+		    if (e1 > s)
+			LYEditInsert(&MyEdit, s, e1 - s, map_active, TRUE);
+		}
+		get_clip_release();
+		break;
+	    }
+#endif
+
+	case LYE_ABORT:
+	    /*
+	     * Control-C or Control-G aborts.
+	     */
+	    inputline[0] = '\0';
+	    CTRACE((tfp, "LYgetstr LYE_ABORT\n"));
+	    return (-1);
+
+	case LYE_STOP:
+	    /*
+	     * Deactivate.
+	     */
+	    CTRACE((tfp, "LYgetstr LYE_STOP\n"));
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+	    textfields_need_activation = TRUE;
+	    return (-1);
+#else
+#ifdef ENHANCED_LINEEDIT
+	    if (Mark >= 0)
+		Mark = -1 - Mark;	/* Disable it */
+#endif
+#endif
+	    break;
+
+	case LYE_LKCMD:
+	    /*
+	     * Used only in form_getstr() for invoking the LYK_F_LINK_NUM
+	     * prompt when in form text fields.  - FM
+	     */
+	    break;
+
+	case LYE_FORM_PASS:
+	    /*
+	     * Used in form_getstr() to end line editing and pass on the input
+	     * char/lynxkeycode.  Here it is just ignored.  - kw
+	     */
+	    break;
+
+	default:
+	    if (xlec & LYE_FORM_LAC) {
+		/*
+		 * Used in form_getstr() to end line editing and pass on the
+		 * lynxkeycode already containing a lynxactioncode.  Here it is
+		 * just ignored.  - kw
+		 */
+		break;
+	    }
+#ifndef SUPPORT_MULTIBYTE_EDIT
+	    LYLineEdit(&MyEdit, ch, FALSE);
+#else /* SUPPORT_MULTIBYTE_EDIT */
+	    if (LYLineEdit(&MyEdit, ch, FALSE) == 0) {
+		if (refresh_mb && IS_CJK_TTY && (0x81 <= ch) && (ch <= 0xfe))
+		    refresh_mb = FALSE;
+		else
+		    refresh_mb = TRUE;
+	    } else {
+		if (!refresh_mb) {
+		    LYEdit1(&MyEdit, 0, LYE_DELP, FALSE);
+		}
+	    }
+#endif /* SUPPORT_MULTIBYTE_EDIT */
+	}
+    }
+}
+
+const char *LYLineeditHelpURL(void)
+{
+    static int lasthelp_lineedit = -1;
+    static char helpbuf[LY_MAXPATH] = "\0";
+    static char *phelp = &helpbuf[0];
+
+    if (lasthelp_lineedit == current_lineedit)
+	return &helpbuf[0];
+    if (lasthelp_lineedit == -1) {
+	LYstrncpy(helpbuf, helpfilepath, sizeof(helpbuf) - 1);
+	phelp += strlen(helpbuf);
+    }
+    if (LYLineeditHelpURLs[current_lineedit] &&
+	strlen(LYLineeditHelpURLs[current_lineedit]) &&
+	(strlen(LYLineeditHelpURLs[current_lineedit]) <=
+	 sizeof(helpbuf) - (unsigned) (phelp - helpbuf))) {
+	LYstrncpy(phelp, LYLineeditHelpURLs[current_lineedit],
+		  (int) (sizeof(helpbuf) - (unsigned) (phelp - helpbuf) - 1));
+	lasthelp_lineedit = current_lineedit;
+	return (&helpbuf[0]);
+    }
+    return NULL;
+}
+
+/*
+ * Wrapper for sscanf to ensure that lynx can "always" read a POSIX float.
+ * In some locales, the decimal point changes.
+ */
+int LYscanFloat2(const char **source, float *result)
+{
+    int count = 0;
+    char *temp;
+    const char *src = *source;
+
+    src = LYSkipCBlanks(src);
+    if (strchr(src, '.') != 0) {
+	long frc_part = 0;
+	float scale = 1.0;
+
+	if (*src != '.') {
+	    temp = NULL;
+#ifdef _WIN32_WINNT
+#define WIN32_FIX (float)
+#else
+#define WIN32_FIX		/* nothing */
+#endif
+	    *result = WIN32_FIX strtol(src, &temp, 10);
+	    src = temp;
+	}
+	if (src != 0 && *src == '.') {
+	    ++src;
+	    if (isdigit(UCH(*src))) {
+		temp = NULL;
+		frc_part = strtol(src, &temp, 10);
+		if (temp != 0) {
+		    int digits = temp - src;
+
+		    while (digits-- > 0)
+			scale *= 10.0;
+		    *result += (frc_part / scale);
+		}
+		src = temp;
+	    }
+	}
+	if (src != 0 && *src != '\0' && strchr(" \t+", *src) == 0) {
+	    char *extra = (char *) malloc(2 + strlen(src));
+
+	    if (extra != 0) {
+		extra[0] = '1';
+		strcpy(extra + 1, src);
+		if (sscanf(extra, "%f", &scale) == 1) {
+		    *result *= scale;
+		}
+		src = LYSkipCNonBlanks(src);
+	    } else {
+		src = 0;
+	    }
+	}
+	if (src != 0)
+	    count = 1;
+    } else {
+	count = sscanf(src, "%f", result);
+	src = LYSkipCNonBlanks(src);
+    }
+    CTRACE2(TRACE_CFG,
+	    (tfp, "LYscanFloat \"%s\" -> %f (%s)\n",
+	     *source, *result,
+	     count ? "ok" : "error"));
+    *source = src;
+    return count;
+}
+
+int LYscanFloat(const char *source, float *result)
+{
+    const char *temp = source;
+
+    return LYscanFloat2(&temp, result);
+}
+
+/*
+ * A replacement for 'strsep()'
+ */
+char *LYstrsep(char **stringp,
+	       const char *delim)
+{
+    char *tmp, *out;
+
+    if (isEmpty(stringp))	/* nothing to do? */
+	return 0;		/* then don't fall on our faces */
+
+    out = *stringp;		/* save the start of the string */
+    tmp = strpbrk(*stringp, delim);
+    if (tmp) {
+	*tmp = '\0';		/* terminate the substring with \0 */
+	*stringp = ++tmp;	/* point at the next substring */
+    } else
+	*stringp = 0;		/* this was the last substring: */
+    /* let caller see he's done */
+    return out;
+}
+
+/*
+ * LYstrstr will find the first occurrence of the string pointed to by tarptr
+ * in the string pointed to by chptr.  It returns NULL if string not found.  It
+ * is a case insensitive search.
+ */
+char *LYstrstr(char *chptr,
+	       const char *tarptr)
+{
+    int len = (int) strlen(tarptr);
+
+    for (; *chptr != '\0'; chptr++) {
+	if (0 == UPPER8(*chptr, *tarptr)) {
+	    if (0 == strncasecomp8(chptr + 1, tarptr + 1, len - 1))
+		return (chptr);
+	}
+    }				/* end for */
+
+    return (NULL);		/* string not found or initial chptr was empty */
+}
+
+/*
+ * LYno_attr_char_case_strstr will find the first occurrence of the
+ * string pointed to by tarptr in the string pointed to by chptr.
+ * It ignores the characters:  LY_UNDERLINE_START_CHAR and
+ *			       LY_UNDERLINE_END_CHAR
+ *			       LY_BOLD_START_CHAR
+ *			       LY_BOLD_END_CHAR
+ *			       LY_SOFT_HYPHEN
+ *			       if present in chptr.
+ * It is a case insensitive search.
+ */
+const char *LYno_attr_char_case_strstr(const char *chptr,
+				       const char *tarptr)
+{
+    register const char *tmpchptr, *tmptarptr;
+
+    if (!chptr)
+	return (NULL);
+
+    while (IsSpecialAttrChar(*chptr) && *chptr != '\0')
+	chptr++;
+
+    for (; *chptr != '\0'; chptr++) {
+	if (0 == UPPER8(*chptr, *tarptr)) {
+	    /*
+	     * See if they line up.
+	     */
+	    tmpchptr = chptr + 1;
+	    tmptarptr = tarptr + 1;
+
+	    if (*tmptarptr == '\0')	/* one char target */
+		return (chptr);
+
+	    while (1) {
+		if (!IsSpecialAttrChar(*tmpchptr)) {
+		    if (0 != UPPER8(*tmpchptr, *tmptarptr))
+			break;
+		    tmpchptr++;
+		    tmptarptr++;
+		} else {
+		    tmpchptr++;
+		}
+		if (*tmptarptr == '\0')
+		    return (chptr);
+		if (*tmpchptr == '\0')
+		    break;
+	    }
+	}
+    }				/* end for */
+
+    return (NULL);
+}
+
+/*
+ * LYno_attr_char_strstr will find the first occurrence of the
+ * string pointed to by tarptr in the string pointed to by chptr.
+ * It ignores the characters:  LY_UNDERLINE_START_CHAR and
+ *			       LY_UNDERLINE_END_CHAR
+ *			       LY_BOLD_START_CHAR
+ *			       LY_BOLD_END_CHAR
+ *			       LY_SOFT_HYPHEN
+ *			       if present in chptr.
+ * It is a case sensitive search.
+ */
+const char *LYno_attr_char_strstr(const char *chptr,
+				  const char *tarptr)
+{
+    register const char *tmpchptr, *tmptarptr;
+
+    if (!chptr)
+	return (NULL);
+
+    while (IsSpecialAttrChar(*chptr) && *chptr != '\0')
+	chptr++;
+
+    for (; *chptr != '\0'; chptr++) {
+	if ((*chptr) == (*tarptr)) {
+	    /*
+	     * See if they line up.
+	     */
+	    tmpchptr = chptr + 1;
+	    tmptarptr = tarptr + 1;
+
+	    if (*tmptarptr == '\0')	/* one char target */
+		return (chptr);
+
+	    while (1) {
+		if (!IsSpecialAttrChar(*tmpchptr)) {
+		    if ((*tmpchptr) != (*tmptarptr))
+			break;
+		    tmpchptr++;
+		    tmptarptr++;
+		} else {
+		    tmpchptr++;
+		}
+		if (*tmptarptr == '\0')
+		    return (chptr);
+		if (*tmpchptr == '\0')
+		    break;
+	    }
+	}
+    }				/* end for */
+
+    return (NULL);
+}
+
+/*
+ * LYno_attr_mbcs_case_strstr will find the first occurrence of the string
+ * pointed to by tarptr in the string pointed to by chptr.  It takes account of
+ * MultiByte Character Sequences (UTF8).  The physical lengths of the displayed
+ * string up to the start and end (= next position after) of the target string
+ * are returned in *nstartp and *nendp if the search is successful.
+ *
+ * These lengths count glyph cells if count_gcells is set.  (Full-width
+ * characters in CJK mode count as two.) Normally that's what we want.  They
+ * count actual glyphs if count_gcells is unset.  (Full-width characters in CJK
+ * mode count as one.)
+ *
+ * It ignores the characters: LY_UNDERLINE_START_CHAR and
+ *			      LY_UNDERLINE_END_CHAR
+ *			      LY_BOLD_START_CHAR
+ *			      LY_BOLD_END_CHAR
+ *			      LY_SOFT_HYPHEN
+ *			      if present in chptr.
+ * It assumes UTF8 if utf_flag is set.
+ * It is a case insensitive search.  - KW & FM
+ */
+const char *LYno_attr_mbcs_case_strstr(const char *chptr,
+				       const char *tarptr,
+				       BOOL utf_flag,
+				       BOOL count_gcells,
+				       int *nstartp,
+				       int *nendp)
+{
+    const char *tmpchptr;
+    const char *tmptarptr;
+    int len = 0;
+    int offset;
+
+    if (!(chptr && tarptr))
+	return (NULL);
+
+    /*
+     * Skip initial IsSpecial chars.  - FM
+     */
+    while (IsSpecialAttrChar(*chptr) && *chptr != '\0')
+	chptr++;
+
+    /*
+     * Seek a first target match.  - FM
+     */
+    for (; *chptr != '\0'; chptr++) {
+	if ((!utf_flag && IS_CJK_TTY && is8bits(*chptr) &&
+	     *chptr == *tarptr &&
+	     IsNormalChar(*(chptr + 1))) ||
+	    (0 == UPPER8(*chptr, *tarptr))) {
+	    int tarlen = 0;
+
+	    offset = len;
+	    len++;
+
+	    /*
+	     * See if they line up.
+	     */
+	    tmpchptr = (chptr + 1);
+	    tmptarptr = (tarptr + 1);
+
+	    if (*tmptarptr == '\0') {
+		/*
+		 * One char target.
+		 */
+		if (nstartp)
+		    *nstartp = offset;
+		if (nendp)
+		    *nendp = len;
+		return (chptr);
+	    }
+	    if (!utf_flag && IS_CJK_TTY && is8bits(*chptr) &&
+		*chptr == *tarptr &&
+		IsNormalChar(*tmpchptr)) {
+		/*
+		 * Check the CJK multibyte.  - FM
+		 */
+		if (*tmpchptr == *tmptarptr) {
+		    /*
+		     * It's a match.  Advance to next char.  - FM
+		     */
+		    tmpchptr++;
+		    tmptarptr++;
+		    if (count_gcells)
+			tarlen++;
+		    if (*tmptarptr == '\0') {
+			/*
+			 * One character match.  - FM
+			 */
+			if (nstartp)
+			    *nstartp = offset;
+			if (nendp)
+			    *nendp = len + tarlen;
+			return (chptr);
+		    }
+		} else {
+		    /*
+		     * It's not a match, so go back to seeking a first target
+		     * match.  - FM
+		     */
+		    chptr++;
+		    if (count_gcells)
+			len++;
+		    continue;
+		}
+	    }
+	    /*
+	     * See if the rest of the target matches.  - FM
+	     */
+	    while (1) {
+		if (!IsSpecialAttrChar(*tmpchptr)) {
+		    if (!utf_flag && IS_CJK_TTY && is8bits(*tmpchptr)) {
+			if (*tmpchptr == *tmptarptr &&
+			    *(tmpchptr + 1) == *(tmptarptr + 1) &&
+			    !IsSpecialAttrChar(*(tmpchptr + 1))) {
+			    tmpchptr++;
+			    tmptarptr++;
+			    if (count_gcells)
+				tarlen++;
+			} else {
+			    break;
+			}
+		    } else if (0 != UPPER8(*tmpchptr, *tmptarptr)) {
+			break;
+		    }
+
+		    if (!IS_UTF_EXTRA(*tmptarptr)) {
+			tarlen++;
+		    }
+		    tmpchptr++;
+		    tmptarptr++;
+
+		} else {
+		    tmpchptr++;
+		}
+
+		if (*tmptarptr == '\0') {
+		    if (nstartp)
+			*nstartp = offset;
+		    if (nendp)
+			*nendp = len + tarlen;
+		    return (chptr);
+		}
+		if (*tmpchptr == '\0')
+		    break;
+	    }
+	} else if (!(IS_UTF_EXTRA(*chptr) ||
+		     IsSpecialAttrChar(*chptr))) {
+	    if (!utf_flag && IS_CJK_TTY && is8bits(*chptr) &&
+		IsNormalChar(*(chptr + 1))) {
+		chptr++;
+		if (count_gcells)
+		    len++;
+	    }
+	    len++;
+	}
+    }				/* end for */
+
+    return (NULL);
+}
+
+/*
+ * LYno_attr_mbcs_strstr will find the first occurrence of the string pointed
+ * to by tarptr in the string pointed to by chptr.
+ *
+ * It takes account of CJK and MultiByte Character Sequences (UTF8).  The
+ * physical lengths of the displayed string up to the start and end (= next
+ * position after) the target string are returned in *nstartp and *nendp if the
+ * search is successful.
+ *
+ * These lengths count glyph cells if count_gcells is set.  (Full-width
+ * characters in CJK mode count as two.) Normally that's what we want.  They
+ * count actual glyphs if count_gcells is unset.  (Full-width characters in CJK
+ * mode count as one.)
+ *
+ * It ignores the characters: LY_UNDERLINE_START_CHAR and
+ *			      LY_UNDERLINE_END_CHAR
+ *			      LY_BOLD_START_CHAR
+ *			      LY_BOLD_END_CHAR
+ *			      LY_SOFT_HYPHEN
+ *			      if present in chptr.
+ * It assumes UTF8 if utf_flag is set.
+ * It is a case sensitive search.  - KW & FM
+ */
+const char *LYno_attr_mbcs_strstr(const char *chptr,
+				  const char *tarptr,
+				  BOOL utf_flag,
+				  BOOL count_gcells,
+				  int *nstartp,
+				  int *nendp)
+{
+    const char *tmpchptr;
+    const char *tmptarptr;
+    int len = 0;
+    int offset;
+
+    if (!(chptr && tarptr))
+	return (NULL);
+
+    /*
+     * Skip initial IsSpecial chars.  - FM
+     */
+    while (IsSpecialAttrChar(*chptr) && *chptr != '\0')
+	chptr++;
+
+    /*
+     * Seek a first target match.  - FM
+     */
+    for (; *chptr != '\0'; chptr++) {
+	if ((*chptr) == (*tarptr)) {
+	    int tarlen = 0;
+
+	    offset = len;
+	    len++;
+
+	    /*
+	     * See if they line up.
+	     */
+	    tmpchptr = (chptr + 1);
+	    tmptarptr = (tarptr + 1);
+
+	    if (*tmptarptr == '\0') {
+		/*
+		 * One char target.
+		 */
+		if (nstartp)
+		    *nstartp = offset;
+		if (nendp)
+		    *nendp = len;
+		return (chptr);
+	    }
+	    if (!utf_flag && IS_CJK_TTY && is8bits(*chptr) &&
+		IsNormalChar(*tmpchptr)) {
+		/*
+		 * Check the CJK multibyte.  - FM
+		 */
+		if (*tmpchptr == *tmptarptr) {
+		    /*
+		     * It's a match.  Advance to next char.  - FM
+		     */
+		    tmpchptr++;
+		    tmptarptr++;
+		    if (count_gcells)
+			tarlen++;
+		    if (*tmptarptr == '\0') {
+			/*
+			 * One character match.  - FM
+			 */
+			if (nstartp)
+			    *nstartp = offset;
+			if (nendp)
+			    *nendp = len + tarlen;
+			return (chptr);
+		    }
+		} else {
+		    /*
+		     * It's not a match, so go back to seeking a first target
+		     * match.  - FM
+		     */
+		    chptr++;
+		    if (count_gcells)
+			len++;
+		    continue;
+		}
+	    }
+	    /*
+	     * See if the rest of the target matches.  - FM
+	     */
+	    while (1) {
+		if (!IsSpecialAttrChar(*tmpchptr)) {
+		    if (!utf_flag && IS_CJK_TTY && is8bits(*tmpchptr)) {
+			if (*tmpchptr == *tmptarptr &&
+			    *(tmpchptr + 1) == *(tmptarptr + 1) &&
+			    !IsSpecialAttrChar(*(tmpchptr + 1))) {
+			    tmpchptr++;
+			    tmptarptr++;
+			    if (count_gcells)
+				tarlen++;
+			} else {
+			    break;
+			}
+		    } else if ((*tmpchptr) != (*tmptarptr)) {
+			break;
+		    }
+
+		    if (!IS_UTF_EXTRA(*tmptarptr)) {
+			tarlen++;
+		    }
+		    tmpchptr++;
+		    tmptarptr++;
+		} else {
+		    tmpchptr++;
+		}
+
+		if (*tmptarptr == '\0') {
+		    if (nstartp)
+			*nstartp = offset;
+		    if (nendp)
+			*nendp = len + tarlen;
+		    return (chptr);
+		}
+		if (*tmpchptr == '\0')
+		    break;
+	    }
+	} else if (!(IS_UTF_EXTRA(*chptr) ||
+		     IsSpecialAttrChar(*chptr))) {
+	    if (!utf_flag && IS_CJK_TTY && is8bits(*chptr) &&
+		IsNormalChar(*(chptr + 1))) {
+		chptr++;
+		if (count_gcells)
+		    len++;
+	    }
+	    len++;
+	}
+    }				/* end for */
+
+    return (NULL);
+}
+
+/*
+ * Allocate a new copy of a string, and returns it.
+ */
+char *SNACopy(char **dest,
+	      const char *src,
+	      int n)
+{
+    FREE(*dest);
+    if (src) {
+	*dest = typeMallocn(char, (unsigned) n + 1);
+
+	if (*dest == NULL) {
+	    CTRACE((tfp, "Tried to malloc %d bytes\n", n));
+	    outofmem(__FILE__, "SNACopy");
+	}
+	strncpy(*dest, src, (unsigned) n);
+	*(*dest + n) = '\0';	/* terminate */
+    }
+    return *dest;
+}
+
+/*
+ * String Allocate and Concatenate.
+ */
+char *SNACat(char **dest,
+	     const char *src,
+	     int n)
+{
+    if (non_empty(src)) {
+	if (*dest) {
+	    int length = (int) strlen(*dest);
+
+	    *dest = (char *) realloc(*dest, (unsigned) (length + n + 1));
+	    if (*dest == NULL)
+		outofmem(__FILE__, "SNACat");
+	    strncpy(*dest + length, src, (unsigned) n);
+	    *(*dest + length + n) = '\0';	/* terminate */
+	} else {
+	    *dest = typeMallocn(char, (unsigned) n + 1);
+
+	    if (*dest == NULL)
+		outofmem(__FILE__, "SNACat");
+	    memcpy(*dest, src, (unsigned) n);
+	    (*dest)[n] = '\0';	/* terminate */
+	}
+    }
+    return *dest;
+}
+
+#include <caselower.h>
+
+/*
+ * Returns lowercase equivalent for unicode,
+ * transparent output if no equivalent found.
+ */
+static long UniToLowerCase(long upper)
+{
+    size_t i, high, low;
+    long diff = 0;
+
+    /*
+     * Make check for sure.
+     */
+    if (upper <= 0)
+	return (upper);
+
+    /*
+     * Try unicode_to_lower_case[].
+     */
+    low = 0;
+    high = TABLESIZE(unicode_to_lower_case);
+    while (low < high) {
+	/*
+	 * Binary search.
+	 */
+	i = (low + (high - low) / 2);
+	diff = (unicode_to_lower_case[i].upper - upper);
+	if (diff < 0)
+	    low = i + 1;
+	if (diff > 0)
+	    high = i;
+	if (diff == 0)
+	    return (unicode_to_lower_case[i].lower);
+    }
+
+    return (upper);		/* if we came here */
+}
+
+/*
+ *   UPPER8 ?
+ *   it was "TOUPPER(a) - TOUPPER(b)" in its previous life...
+ *
+ *   It was realized that case-insensitive user search
+ *   got information about upper/lower mapping from TOUPPER
+ *   (precisely from "(TOUPPER(a) - TOUPPER(b))==0")
+ *   and depends on locale in its 8bit mapping. -
+ *   Usually fails with DOS/WINDOWS display charsets
+ *   as well as on non-UNIX systems.
+ *
+ *   So use unicode case mapping.
+ */
+int UPPER8(int ch1, int ch2)
+{
+    /* if they are the same or one is a null characters return immediately. */
+    if (ch1 == ch2)
+	return 0;
+    if (!ch2)
+	return UCH(ch1);
+    else if (!ch1)
+	return -UCH(ch2);
+
+    /* case-insensitive match for us-ascii */
+    if (UCH(TOASCII(ch1)) < 128 && UCH(TOASCII(ch2)) < 128)
+	return (TOUPPER(ch1) - TOUPPER(ch2));
+
+    /* case-insensitive match for upper half */
+    if (UCH(TOASCII(ch1)) > 127 &&	/* S/390 -- gil -- 2066 */
+	UCH(TOASCII(ch2)) > 127) {
+	if (DisplayCharsetMatchLocale)
+	    return (TOUPPER(ch1) - TOUPPER(ch2));	/* old-style */
+	else {
+	    long uni_ch2 = UCTransToUni((char) ch2, current_char_set);
+	    long uni_ch1;
+
+	    if (uni_ch2 < 0)
+		return UCH(ch1);
+	    uni_ch1 = UCTransToUni((char) ch1, current_char_set);
+	    return (UniToLowerCase(uni_ch1) - UniToLowerCase(uni_ch2));
+	}
+    }
+
+    return (-10);		/* mismatch, if we come to here */
+}
+
+/*
+ * Replaces 'fgets()' calls into a fixed-size buffer with reads into a buffer
+ * that is allocated.  When an EOF or error is found, the buffer is freed
+ * automatically.
+ */
+char *LYSafeGets(char **src,
+		 FILE *fp)
+{
+    char buffer[BUFSIZ];
+    char *result = 0;
+
+    if (src != 0)
+	result = *src;
+    if (result != 0)
+	*result = 0;
+
+    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
+	if (*buffer)
+	    result = StrAllocCat(result, buffer);
+	if (strchr(buffer, '\n') != 0)
+	    break;
+    }
+    if (ferror(fp)) {
+	FREE(result);
+    } else if (feof(fp) && result && *result == '\0') {
+	/*
+	 * If the file ends in the middle of a line, return the partial line;
+	 * if another call is made after this, it will return NULL.  - kw
+	 */
+	FREE(result);
+    }
+    if (src != 0)
+	*src = result;
+    return result;
+}
+
+#ifdef USE_CMD_LOGGING
+static FILE *cmd_logfile;
+static FILE *cmd_script;
+
+void LYOpenCmdLogfile(int argc,
+		      char **argv)
+{
+    int n;
+
+    if (lynx_cmd_logfile != 0) {
+	cmd_logfile = LYNewTxtFile(lynx_cmd_logfile);
+	if (cmd_logfile != 0) {
+	    fprintf(cmd_logfile, "# Command logfile created by %s %s (%s)\n",
+		    LYNX_NAME, LYNX_VERSION, LYVersionDate());
+	    for (n = 0; n < argc; n++) {
+		fprintf(cmd_logfile, "# Arg%d = %s\n", n, argv[n]);
+	    }
+	}
+    }
+}
+
+BOOL LYHaveCmdScript(void)
+{
+    return (BOOL) (cmd_script != 0);
+}
+
+void LYOpenCmdScript(void)
+{
+    if (lynx_cmd_script != 0) {
+	cmd_script = fopen(lynx_cmd_script, TXT_R);
+	CTRACE((tfp, "LYOpenCmdScript(%s) %s\n",
+		lynx_cmd_script,
+		cmd_script != 0 ? "SUCCESS" : "FAIL"));
+    }
+}
+
+int LYReadCmdKey(int mode)
+{
+    int ch = -1;
+
+    if (cmd_script != 0) {
+	char *buffer = 0;
+	char *src;
+	char *tmp;
+
+	while ((ch < 0) && LYSafeGets(&buffer, cmd_script) != 0) {
+	    LYTrimTrailing(buffer);
+	    src = LYSkipBlanks(buffer);
+	    tmp = LYSkipNonBlanks(src);
+	    switch ((unsigned) (tmp - src)) {
+	    case 4:
+		if (!strncasecomp(src, "exit", 4))
+		    exit_immediately(0);
+		break;
+	    case 3:
+		if (!strncasecomp(src, "key", 3)) {
+		    ch = LYStringToKeycode(LYSkipBlanks(tmp));
+		} else if (!strncasecomp(src, "set", 3)) {
+		    src = LYSkipBlanks(tmp);
+		    tmp = src;
+		    while (*tmp != '\0') {
+			if (isspace(UCH(*tmp)) || *tmp == '=')
+			    break;
+			++tmp;
+		    }
+		    if (*tmp != '\0') {
+			*tmp++ = '\0';
+			tmp = LYSkipBlanks(tmp);
+		    }
+		    CTRACE((tfp, "LYSetConfigValue(%s, %s)\n", src, tmp));
+		    LYSetConfigValue(src, tmp);
+		}
+		break;
+	    }
+	}
+	if (feof(cmd_script)) {
+	    fclose(cmd_script);
+	    cmd_script = 0;
+	}
+	if (ch >= 0) {
+	    LYSleepReplay();
+	    LYrefresh();
+	}
+	FREE(buffer);
+    } else {
+	ch = LYgetch_for(mode);
+    }
+    CTRACE((tfp, "LYReadCmdKey(%d) ->%s (%#x)\n",
+	    mode, LYKeycodeToString(ch, TRUE), ch));
+    LYWriteCmdKey(ch);
+    return ch;
+}
+
+/*
+ * Write a LYKeymapCode 'ch' to the logfile.
+ */
+void LYWriteCmdKey(int ch)
+{
+    if (cmd_logfile != 0) {
+	fprintf(cmd_logfile, "key %s\n", LYKeycodeToString(ch, FALSE));
+    }
+}
+
+void LYCloseCmdLogfile(void)
+{
+    if (cmd_logfile != 0) {
+	LYCloseOutput(cmd_logfile);
+	cmd_logfile = 0;
+    }
+    if (cmd_script != 0) {
+	LYCloseInput(cmd_script);
+	cmd_script = 0;
+    }
+    FREE(lynx_cmd_logfile);
+    FREE(lynx_cmd_script);
+}
+#endif /* USE_CMD_LOGGING */
diff --git a/src/LYStrings.h b/src/LYStrings.h
new file mode 100644
index 00000000..4f2ab677
--- /dev/null
+++ b/src/LYStrings.h
@@ -0,0 +1,367 @@
+/*
+ * $LynxId: LYStrings.h,v 1.74 2009/11/21 17:05:33 Bela.Lubkin Exp $
+ */
+#ifndef LYSTRINGS_H
+#define LYSTRINGS_H
+
+#include <LYCurses.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    typedef enum {
+	NORECALL = 0
+	,RECALL_URL
+	,RECALL_CMD
+	,RECALL_MAIL
+    } RecallType;
+
+#define IS_UTF8_TTY (BOOLEAN) (LYCharSet_UC[current_char_set].enc == UCT_ENC_UTF8)
+#define IS_CJK_TTY  (BOOLEAN) (HTCJK != NOCJK)
+
+#define is8bits(ch) (BOOLEAN) (UCH(ch) >= 128)	/* isascii(ch) is not POSIX */
+
+/*  UPPER8(ch1,ch2) is an extension of (TOUPPER(ch1) - TOUPPER(ch2))  */
+    extern int UPPER8(int ch1,
+		      int ch2);
+
+    extern int get_mouse_link(void);
+    extern int peek_mouse_link(void);
+    extern int peek_mouse_levent(void);
+    extern int fancy_mouse(WINDOW * win, int row, int *position);
+
+    extern char *LYstrncpy(char *dst,
+			   const char *src,
+			   int n);
+    extern void ena_csi(BOOLEAN flag);
+    extern int get_popup_number(const char *msg,
+				int *c,
+				int *rel);
+    extern int LYarrayLength(const char **list);
+    extern int LYarrayWidth(const char **list);
+    extern int LYgetch(void);
+    extern int LYgetch_choice(void);
+    extern int LYgetch_input(void);
+    extern int LYgetch_single(void);
+    extern int LYgetstr(char *inputline,
+			int hidden,
+			size_t bufsize,
+			RecallType recall);
+    extern int LYscanFloat(const char *source, float *result);
+    extern int LYscanFloat2(const char **source, float *result);
+    extern char *LYstrsep(char **stringp,
+			  const char *delim);
+    extern char *LYstrstr(char *chptr,
+			  const char *tarptr);
+    extern char *LYmbcsstrncpy(char *dst,
+			       const char *src,
+			       int n_bytes,
+			       int n_glyphs,
+			       BOOL utf_flag);
+    extern const char *LYmbcs_skip_cells(const char *data,
+					 int n_cells,
+					 BOOL utf_flag);
+    extern const char *LYmbcs_skip_glyphs(const char *data,
+					  int n_glyphs,
+					  BOOL utf_flag);
+    extern int LYmbcsstrlen(const char *str,
+			    BOOL utf_flag,
+			    BOOL count_gcells);
+
+    extern const char *LYno_attr_mbcs_strstr(const char *chptr,
+					     const char *tarptr,
+					     BOOL utf_flag,
+					     BOOL count_gcells,
+					     int *nstartp,
+					     int *nendp);
+    extern const char *LYno_attr_mbcs_case_strstr(const char *chptr,
+						  const char *tarptr,
+						  BOOL utf_flag,
+						  BOOL count_gcells,
+						  int *nstartp,
+						  int *nendp);
+
+#define LYno_attr_mb_strstr(chptr, tarptr, utf_flag, count_gcells, nstartp, nendp) \
+	(case_sensitive \
+	    ? LYno_attr_mbcs_strstr(chptr, tarptr, utf_flag, count_gcells, nstartp, nendp) \
+	    : LYno_attr_mbcs_case_strstr(chptr, tarptr, utf_flag, count_gcells, nstartp, nendp))
+
+    extern const char *LYno_attr_char_strstr(const char *chptr,
+					     const char *tarptr);
+    extern const char *LYno_attr_char_case_strstr(const char *chptr,
+						  const char *tarptr);
+
+#define LYno_attr_strstr(chptr, tarptr) \
+	(case_sensitive \
+	? LYno_attr_char_strstr(chptr, tarptr) \
+	: LYno_attr_char_case_strstr(chptr, tarptr))
+
+    extern char *SNACopy(char **dest,
+			 const char *src,
+			 int n);
+    extern char *SNACat(char **dest,
+			const char *src,
+			int n);
+
+#define StrnAllocCopy(dest, src, n)  SNACopy (&(dest), src, n)
+#define StrnAllocCat(dest, src, n)   SNACat  (&(dest), src, n)
+
+    extern char *LYSafeGets(char **src, FILE *fp);
+
+#ifdef USE_CMD_LOGGING
+    extern BOOL LYHaveCmdScript(void);
+    extern int LYReadCmdKey(int mode);
+    extern void LYCloseCmdLogfile(void);
+    extern void LYOpenCmdLogfile(int argc, char **argv);
+    extern void LYOpenCmdScript(void);
+    extern void LYWriteCmdKey(int ch);
+
+#else
+#define LYHaveCmdScript() FALSE
+#define LYReadCmdKey(mode) LYgetch_for(mode)
+#define LYCloseCmdLogfile()	/* nothing */
+#endif
+
+/* values for LYgetch */
+/* The following are lynxkeycodes, not to be confused with
+   lynxactioncodes (LYK_*) to which they are often mapped.
+   The lynxkeycodes include all single-byte keys as a subset. - kw
+*/
+#define UPARROW		256	/* 0x100 */
+#define DNARROW		257	/* 0x101 */
+#define RTARROW		258	/* 0x102 */
+#define LTARROW		259	/* 0x103 */
+#define PGDOWN		260	/* 0x104 */
+#define PGUP		261	/* 0x105 */
+#define HOME		262	/* 0x106 */
+#define END_KEY		263	/* 0x107 */
+#define F1		264	/* 0x108 */
+#define DO_KEY		265	/* 0x109 */
+#define FIND_KEY	266	/* 0x10A */
+#define SELECT_KEY	267	/* 0x10B */
+#define INSERT_KEY	268	/* 0x10C */
+#define REMOVE_KEY	269	/* 0x10D */
+#define DO_NOTHING	270	/* 0x10E */
+#define BACKTAB_KEY	271	/* 0x10F */
+#define MOUSE_KEY	285	/* 0x11D */
+/*  ***** NOTES: *****
+    If you add definitions for new lynxkeycodes to the above list that need to
+    be mapped to LYK_* lynxactioncodes -
+
+    - AT LEAST the tables keymap[] and key_override[] in LYKeymap.c have to be
+      changed/reviewed, AS WELL AS the lineedit binding tables in LYEditmap.c !
+
+    - KEYMAP_SIZE, defined in LYKeymap.h, may need to be changed !
+
+    - See also table named_keys[] in LYKeymap.c for 'pretty' strings for the
+      keys with codes >= 256 (to appear on the 'K'eymap page).  New keycodes
+      should probably be assigned consecutively, so their key names can be
+      easily added to named_keys[] (but see next point).  They should also be
+      documented in lynx.cfg.
+
+    - The DOS port uses its own native codes for some keys, unless they are
+      remapped by the code in LYgetch().  See *.key files in docs/ directory. 
+      Adding new keys here may conflict with those codes (affecting DOS users),
+      unless/until remapping is added or changed in LYgetch().  (N)curses
+      keypad codes (KEY_* from curses.h) can also directly appear as
+      lynxkeycodes and conflict with our assignments, although that shouldn't
+      happen - the useful ones should be recognized in LYgetch().
+
+    - The actual recognition of raw input keys or escape sequences, and mapping
+      to our lynxkeycodes, take place in LYgetch() and/or its subsidiary
+      functions and/or the curses/slang/etc.  libraries.
+
+    The basic lynxkeycodes can appear combined with various flags in
+    higher-order bits as extended lynxkeycodes; see macros in LYKeymap.h.  The
+    range of possible basic values is therefore limited, they have to be less
+    than LKC_ISLKC (even if KEYMAP_SIZE is increased).
+*/
+
+#  define FOR_PANEL	0	/* normal screen, also LYgetch default */
+#  define FOR_CHOICE	1	/* mouse menu */
+#  define FOR_INPUT	2	/* form input and textarea field */
+#  define FOR_PROMPT	3	/* string prompt editing */
+#  define FOR_SINGLEKEY	4	/* single key prompt, confirmation */
+
+#define VISIBLE  0
+#define HIDDEN   1
+
+#ifdef EXP_ALT_BINDINGS
+/*  Enable code implementing additional, mostly emacs-like, line-editing
+    functions. - kw */
+#define ENHANCED_LINEEDIT
+#endif
+
+#define MAX_EDIT 1024
+
+/* EditFieldData preserves state between calls to LYEdit1
+ */
+    typedef struct _EditFieldData {
+
+	int sx;			/* Origin of editfield                       */
+	int sy;
+	int dspwdth;		/* Screen real estate for editting           */
+
+	int strlen;		/* Current size of string.                   */
+	int maxlen;		/* Max size of string, excluding zero at end */
+	char pad;		/* Right padding  typically ' ' or '_'       */
+	BOOL hidden;		/* Masked password entry flag                */
+
+	BOOL dirty;		/* accumulate refresh requests               */
+	BOOL panon;		/* Need horizontal scroll indicator          */
+	int xpan;		/* Horizontal scroll offset                  */
+	int pos;		/* Insertion point in string                 */
+	int margin;		/* Number of columns look-ahead/look-back    */
+	int current_modifiers;	/* Modifiers for next input lynxkeycode */
+#ifdef ENHANCED_LINEEDIT
+	int mark;		/* position of emacs-like mark, or -1-pos to denote
+				   unactive mark.  */
+#endif
+
+	char buffer[MAX_EDIT];	/* String buffer                          */
+
+	int offset2col[MAX_EDIT * 2];
+	int col2offset[MAX_EDIT * 2];
+
+    } EditFieldData;
+
+/* line-edit action encoding */
+
+    typedef enum {
+	LYE_NOP = 0		/* Do Nothing            */
+	,LYE_CHAR		/* Insert printable char */
+	,LYE_ENTER		/* Input complete, return char/lynxkeycode */
+	,LYE_TAB		/* Input complete, return TAB  */
+	,LYE_STOP		/* Input complete, deactivate  */
+	,LYE_ABORT		/* Input cancelled       */
+
+	,LYE_FORM_PASS		/* In form fields: input complete,
+				   return char / lynxkeycode;
+				   Elsewhere: Do Nothing */
+
+	,LYE_DELN		/* Delete next/curr char */
+	,LYE_DELC		/* Obsolete (DELC case was equiv to DELN) */
+	,LYE_DELP		/* Delete prev      char */
+	,LYE_DELNW		/* Delete next word      */
+	,LYE_DELPW		/* Delete prev word      */
+
+	,LYE_ERASE		/* Erase the line        */
+
+	,LYE_BOL		/* Go to begin of line   */
+	,LYE_EOL		/* Go to end   of line   */
+	,LYE_FORW		/* Cursor forwards       */
+	,LYE_FORW_RL		/* Cursor forwards or right link */
+	,LYE_BACK		/* Cursor backwards      */
+	,LYE_BACK_LL		/* Cursor backwards or left link */
+	,LYE_FORWW		/* Word forward          */
+	,LYE_BACKW		/* Word back             */
+
+	,LYE_LOWER		/* Lower case the line   */
+	,LYE_UPPER		/* Upper case the line   */
+
+	,LYE_LKCMD		/* Invoke command prompt */
+
+	,LYE_AIX		/* Hex 97                */
+
+	,LYE_DELBL		/* Delete back to BOL    */
+	,LYE_DELEL		/* Delete thru EOL       */
+
+	,LYE_SWMAP		/* Switch input keymap   */
+
+	,LYE_TPOS		/* Transpose characters  */
+
+	,LYE_SETM1		/* Set modifier 1 flag   */
+	,LYE_SETM2		/* Set modifier 2 flag   */
+	,LYE_UNMOD		/* Fall back to no-modifier command */
+
+	,LYE_C1CHAR		/* Insert C1 char if printable */
+
+	,LYE_SETMARK		/* emacs-like set-mark-command */
+	,LYE_XPMARK		/* emacs-like exchange-point-and-mark */
+	,LYE_KILLREG		/* emacs-like kill-region */
+	,LYE_YANK		/* emacs-like yank */
+#ifdef CAN_CUT_AND_PASTE
+	,LYE_PASTE		/* ClipBoard to Lynx       */
+#endif
+    } LYEditCodes;
+
+/* All preceding values must be within 0x00..0x7f - kw */
+
+/*  The following are meant to be bitwise or-ed:  */
+#define LYE_DF       0x80	/* Flag to set modifier 3 AND do other
+				   action */
+#define LYE_FORM_LAC 0x1000	/* Flag to pass lynxactioncode given by
+				   lower bits.  Doesn't fit in a char! */
+
+#if defined(USE_KEYMAPS)
+    extern int lynx_initialize_keymaps(void);
+    extern int map_string_to_keysym(const char *src, int *lec);
+#endif
+
+    extern char *LYElideString(char *str,
+			       int cut_pos);
+    extern void LYEscapeStartfile(char **buffer);
+    extern void LYLowerCase(char *buffer);
+    extern void LYUpperCase(char *buffer);
+    extern BOOLEAN LYRemoveNewlines(char *buffer);
+    extern char *LYReduceBlanks(char *buffer);
+    extern char *LYRemoveBlanks(char *buffer);
+    extern char *LYSkipBlanks(char *buffer);
+    extern char *LYSkipNonBlanks(char *buffer);
+    extern const char *LYSkipCBlanks(const char *buffer);
+    extern const char *LYSkipCNonBlanks(const char *buffer);
+    extern void LYTrimLeading(char *buffer);
+    extern char *LYTrimNewline(char *buffer);
+    extern void LYTrimTrailing(char *buffer);
+    extern void LYTrimAllStartfile(char *buffer);
+    extern BOOLEAN LYTrimStartfile(char *buffer);
+    extern void LYSetupEdit(EditFieldData *edit, char *old,
+			    int maxstr,
+			    int maxdsp);
+    extern void LYRefreshEdit(EditFieldData *edit);
+    extern int EditBinding(int ch);	/* in LYEditmap.c */
+    extern BOOL LYRemapEditBinding(int xlkc,
+				   int lec,
+				   int select_edi);	/* in LYEditmap.c */
+    extern int LYKeyForEditAction(int lec);	/* in LYEditmap.c */
+    extern int LYEditKeyForAction(int lac, int *pmodkey);	/* LYEditmap.c */
+    extern int LYEdit1(EditFieldData *edit, int ch,
+		       int action,
+		       BOOL maxMessage);
+    extern void LYCloseCloset(RecallType recall);
+    extern int LYhandlePopupList(int cur_choice,
+				 int ly,
+				 int lx,
+				 const char **choices,
+				 int width,
+				 int i_length,
+				 int disabled,
+				 BOOLEAN for_mouse);
+
+    typedef unsigned char LYEditCode;
+
+    extern int current_lineedit;
+    extern const char *LYLineeditNames[];
+    extern LYEditCode *LYLineEditors[];
+    extern const char *LYLineeditHelpURLs[];
+
+#define CurrentLineEditor() LYLineEditors[current_lineedit]
+
+    extern const char *LYLineeditHelpURL(void);
+
+    extern int escape_bound;
+
+#define LYLineEdit(e,c,m) LYEdit1(e, c, EditBinding(c) & ~LYE_DF, m)
+
+/* Dummy initializer for LYEditmap.c */
+    extern int LYEditmapDeclared(void);
+
+    extern int LYEditInsert(EditFieldData *edit,
+			    unsigned const char *s,
+			    int len, int map_active,
+			    BOOL maxMessage);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYSTRINGS_H */
diff --git a/src/LYStructs.h b/src/LYStructs.h
new file mode 100644
index 00000000..b4c1422b
--- /dev/null
+++ b/src/LYStructs.h
@@ -0,0 +1,191 @@
+/*
+ * $LynxId: LYStructs.h,v 1.29 2009/02/02 19:56:38 tom Exp $
+ */
+#ifndef LYSTRUCTS_H
+#define LYSTRUCTS_H
+
+#ifndef HTANCHOR_H
+#include <HTAnchor.h>
+#endif /* HTANCHOR_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    typedef struct {
+	char *hl_text;
+	short hl_x;
+    } HiliteInfo;
+
+    typedef struct {
+	HiliteInfo *hl_info;
+	HiliteInfo hl_base;
+	short hl_len;		/* number of strings in this struct */
+    } HiliteList;
+
+    typedef struct {
+	char *lname;
+	char *target;
+	char *l_hightext;
+	char *l_hightext2;
+	int l_hightext2_offset;
+	BOOL inUnderline;	/* TRUE when this link is in underlined context. */
+	int lx;
+	int ly;
+	int type;		/* Type of link, Forms, WWW, etc. */
+	int sgml_offset;	/* document offset used in reparsing */
+	int anchor_number;	/* The anchor number within the HText structure.  */
+	int anchor_line_num;	/* The anchor line number in the HText structure. */
+	HiliteList list;
+	struct _FormInfo *l_form;	/* Pointer to form info. */
+    } LinkInfo;
+    extern LinkInfo links[MAXLINKS];
+    extern int nlinks;
+
+    typedef struct {
+	/* FIXME: see DocAddress */
+	char *title;
+	char *address;
+	bstring *post_data;
+	char *post_content_type;
+	char *bookmark;
+	BOOL isHEAD;
+	BOOL safe;
+
+	int link;
+	int line;
+	BOOL internal_link;	/* whether doc was reached via an internal
+				   (fragment) link. - kw */
+#ifdef USE_COLOR_STYLE
+	char *style;
+#endif
+    } DocInfo;
+
+    typedef struct {
+	DocInfo hdoc;
+	int intern_seq_start;	/* indicates which element on the history
+				   is the start of this sequence of
+				   "internal links", otherwise -1 */
+    } HistInfo;
+
+#define HDOC(n) history[n].hdoc
+
+    extern int Visited_Links_As;
+
+#define VISITED_LINKS_AS_FIRST_V 0
+#define VISITED_LINKS_AS_TREE    1
+#define VISITED_LINKS_AS_LATEST  2
+#define VISITED_LINKS_REVERSE    4
+
+    typedef struct _VisitedLink {
+	char *title;
+	char *address;
+	int level;
+	struct _VisitedLink *next_tree;
+	struct _VisitedLink *prev_latest;
+	struct _VisitedLink *next_latest;
+	struct _VisitedLink *prev_first;
+    } VisitedLink;
+
+    extern HistInfo *history;
+    extern int nhist;
+    extern int size_history;
+
+/******************************************************************************/
+
+    typedef struct _lynx_list_item_type {
+	struct _lynx_list_item_type *next;	/* the next item in the linked list */
+	char *name;		/* a description of the item */
+	char *command;		/* the command to execute */
+	int always_enabled;	/* a constant to tell whether or
+				 * not to disable the printer
+				 * when the no_print option is on
+				 */
+	/* HTML lists: */
+	BOOL override_primary_action;	/* whether primary action will be
+					 * overridden by this - e.g. this
+					 * allows invoking user's MUA when
+					 * mailto:  link is activated using
+					 * normal "activate" command.  This
+					 * field is only examined by code that
+					 * handles EXTERNAL command.
+					 */
+	/* PRINTER lists: */
+	int pagelen;		/* an integer to store the printer's
+				 * page length
+				 */
+    } lynx_list_item_type;
+
+    extern lynx_list_item_type *printers;
+
+/* for download commands */
+    extern lynx_list_item_type *downloaders;
+
+/* for upload commands */
+    extern lynx_list_item_type *uploaders;
+
+#ifdef USE_EXTERNALS
+/* for external commands */
+    extern lynx_list_item_type *externals;
+#endif
+
+/******************************************************************************/
+
+    typedef struct {
+	const char *name;
+	int value;
+    } Config_Enum;
+
+    typedef int (*ParseFunc) (char *);
+
+#define ParseUnionMembers \
+	lynx_list_item_type** add_value; \
+	BOOLEAN * set_value; \
+	int *     int_value; \
+	char **   str_value; \
+	ParseFunc fun_value; \
+	long	  def_value; \
+	HTList**  lst_value
+
+    typedef union {
+	ParseUnionMembers;
+    } ParseUnion;
+
+#define	PARSE_DEBUG 1
+#ifdef	PARSE_DEBUG
+
+#define ParseUnionPtr      Config_Type *
+#define ParseUnionOf(tbl)  tbl
+#define ParseData          ParseUnionMembers
+
+#define UNION_ADD(v) &v,  0,  0,  0,  0,  0,  0
+#define UNION_SET(v)  0, &v,  0,  0,  0,  0,  0
+#define UNION_INT(v)  0,  0, &v,  0,  0,  0,  0
+#define UNION_STR(v)  0,  0,  0, &v,  0,  0,  0
+#define UNION_ENV(v)  0,  0,  0,  v,  0,  0,  0
+#define UNION_FUN(v)  0,  0,  0,  0,  v,  0,  0
+#define UNION_DEF(v)  0,  0,  0,  0,  0,  v,  0
+#define UNION_LST(v)  0,  0,  0,  0,  0,  0, &v
+
+#else
+
+    typedef void *ParseType;
+
+#define ParseUnionPtr      ParseUnion *
+#define ParseUnionOf(tbl) (ParseUnionPtr)(&(tbl->value))
+#define ParseData          ParseType value
+
+#define UNION_ADD(v) (ParseType)&(v)
+#define UNION_SET(v) (ParseType)&(v)
+#define UNION_INT(v) (ParseType)&(v)
+#define UNION_STR(v) (ParseType)&(v)
+#define UNION_ENV(v) (ParseType) (v)
+#define UNION_FUN(v) (ParseType) (v)
+#define UNION_DEF(v) (ParseType) (v)
+#define UNION_LST(v) (ParseType)&(v)
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYSTRUCTS_H */
diff --git a/src/LYStyle.c b/src/LYStyle.c
new file mode 100644
index 00000000..3378d5da
--- /dev/null
+++ b/src/LYStyle.c
@@ -0,0 +1,797 @@
+/*
+ * $LynxId: LYStyle.c,v 1.67 2010/05/02 22:23:21 tom Exp $
+ *
+ * character level styles for Lynx
+ * (c) 1996 Rob Partington -- donated to the Lyncei (if they want it :-)
+ */
+#include <HTUtils.h>
+#include <HTML.h>
+#include <LYGlobalDefs.h>
+
+#include <LYStructs.h>
+#include <LYReadCFG.h>
+#include <LYCurses.h>
+#include <LYCharUtils.h>
+#include <LYUtils.h>		/* defines TABLESIZE */
+#include <AttrList.h>
+#include <SGML.h>
+#include <HTMLDTD.h>
+
+/* Hash table definitions */
+#include <LYHash.h>
+#include <LYStyle.h>
+
+#include <LYexit.h>
+#include <LYLeaks.h>
+#include <LYStrings.h>
+#include <LYHash.h>
+
+#define CTRACE1(p) CTRACE2(TRACE_CFG || TRACE_STYLE, p)
+
+#ifdef USE_COLOR_STYLE
+
+static void style_initialiseHashTable(void);
+
+/* because curses isn't started when we parse the config file, we
+ * need to remember the STYLE: lines we encounter and parse them
+ * after curses has started
+ */
+static HTList *lss_styles = NULL;
+
+#define CACHEW 128
+#define CACHEH 64
+
+static unsigned *cached_styles_ptr = NULL;
+static int cached_styles_rows = 0;
+static int cached_styles_cols = 0;
+
+/* stack of attributes during page rendering */
+int last_styles[MAX_LAST_STYLES + 1] =
+{0};
+int last_colorattr_ptr = 0;
+
+bucket hashStyles[CSHASHSIZE];
+bucket special_bucket =
+{
+    "<special>",		/* in order something to be in trace. */
+    0, 0, 0, 0, NULL
+};
+bucket nostyle_bucket =
+{
+    "<NOSTYLE>",		/* in order something to be in trace. */
+    0, 0, 0, 0, NULL
+};
+
+int cached_tag_styles[HTML_ELEMENTS];
+int current_tag_style;
+BOOL force_current_tag_style = FALSE;
+char *forced_classname;
+BOOL force_classname;
+
+/* Remember the hash codes for common elements */
+int s_a = NOSTYLE;
+int s_aedit = NOSTYLE;
+int s_aedit_arr = NOSTYLE;
+int s_aedit_pad = NOSTYLE;
+int s_aedit_sel = NOSTYLE;
+int s_alert = NOSTYLE;
+int s_alink = NOSTYLE;
+int s_curedit = NOSTYLE;
+int s_forw_backw = NOSTYLE;
+int s_hot_paste = NOSTYLE;
+int s_menu_active = NOSTYLE;
+int s_menu_bg = NOSTYLE;
+int s_menu_entry = NOSTYLE;
+int s_menu_frame = NOSTYLE;
+int s_menu_number = NOSTYLE;
+int s_menu_sb = NOSTYLE;
+int s_normal = NOSTYLE;
+int s_prompt_edit = NOSTYLE;
+int s_prompt_edit_arr = NOSTYLE;
+int s_prompt_edit_pad = NOSTYLE;
+int s_prompt_sel = NOSTYLE;
+int s_status = NOSTYLE;
+int s_title = NOSTYLE;
+int s_whereis = NOSTYLE;
+
+#ifdef USE_SCROLLBAR
+int s_sb_aa = NOSTYLE;
+int s_sb_bar = NOSTYLE;
+int s_sb_bg = NOSTYLE;
+int s_sb_naa = NOSTYLE;
+#endif
+
+/* start somewhere safe */
+#define MAX_COLOR 16
+static int colorPairs = 0;
+
+#ifdef USE_BLINK
+#  define MAX_BLINK	2
+#  define M_BLINK	A_BLINK
+#else
+#  define MAX_BLINK	1
+#  define M_BLINK	0
+#endif
+
+#define MAX_PAIR 255		/* because our_pairs[] type is unsigned-char */
+static unsigned char our_pairs[2]
+[MAX_BLINK]
+[MAX_COLOR + 1]
+[MAX_COLOR + 1];
+
+static char *TrimLowercase(char *buffer)
+{
+    LYRemoveBlanks(buffer);
+    strtolower(buffer);
+    return buffer;
+}
+
+/*
+ * Parse a string containing a combination of video attributes and color.
+ */
+static void parse_either(const char *attrs,
+			 int dft_color,
+			 int *monop,
+			 int *colorp)
+{
+    int value;
+    char *temp_attrs = NULL;
+
+    if (StrAllocCopy(temp_attrs, attrs) != NULL) {
+	char *to_free = temp_attrs;
+
+	while (*temp_attrs != '\0') {
+	    char *next = strchr(temp_attrs, '+');
+	    char save = (char) ((next != NULL) ? *next : '\0');
+
+	    if (next == NULL)
+		next = temp_attrs + strlen(temp_attrs);
+
+	    if (save != 0)
+		*next = '\0';
+	    if ((value = string_to_attr(temp_attrs)) != 0)
+		*monop |= value;
+	    else if (colorp != 0
+		     && (value = check_color(temp_attrs, dft_color)) != ERR_COLOR)
+		*colorp = value;
+
+	    temp_attrs = next;
+	    if (save != '\0')
+		*temp_attrs++ = save;
+	}
+	FREE(to_free);
+    }
+}
+
+/* icky parsing of the style options */
+static void parse_attributes(const char *mono,
+			     const char *fg,
+			     const char *bg,
+			     int style,
+			     const char *element)
+{
+    int mA = A_NORMAL;
+    int fA = default_fg;
+    int bA = default_bg;
+    int cA = A_NORMAL;
+    int newstyle = hash_code(element);
+
+    CTRACE2(TRACE_STYLE, (tfp, "CSS(PA):style d=%d / h=%d, e=%s\n",
+			  style, newstyle, element));
+
+    parse_either(mono, ERR_COLOR, &mA, (int *) 0);
+    parse_either(bg, default_bg, &cA, &bA);
+    parse_either(fg, default_fg, &cA, &fA);
+
+    if (style == -1) {		/* default */
+	CTRACE2(TRACE_STYLE, (tfp, "CSS(DEF):default_fg=%d, default_bg=%d\n",
+			      fA, bA));
+	default_fg = fA;
+	default_bg = bA;
+	default_color_reset = TRUE;
+	return;
+    }
+    if (fA == NO_COLOR) {
+	bA = NO_COLOR;
+    } else if (COLORS) {
+#ifdef USE_BLINK
+	if (term_blink_is_boldbg) {
+	    if (fA >= COLORS)
+		cA = A_BOLD;
+	    if (bA >= COLORS)
+		cA |= M_BLINK;
+	} else
+#endif
+	if (fA >= COLORS || bA >= COLORS)
+	    cA = A_BOLD;
+	if (fA >= COLORS)
+	    fA %= COLORS;
+	if (bA >= COLORS)
+	    bA %= COLORS;
+    } else {
+	cA = A_BOLD;
+	fA = NO_COLOR;
+	bA = NO_COLOR;
+    }
+
+    /*
+     * If we have colour, and space to create a new colour attribute,
+     * and we have a valid colour description, then add this style
+     */
+    if (lynx_has_color && colorPairs < COLOR_PAIRS - 1 && fA != NO_COLOR) {
+	int curPair = 0;
+	int iFg = (1 + (fA >= 0 ? fA : 0));
+	int iBg = (1 + (bA >= 0 ? bA : 0));
+	int iBold = !!(cA & A_BOLD);
+	int iBlink = !!(cA & M_BLINK);
+
+	CTRACE2(TRACE_STYLE, (tfp, "parse_attributes %d/%d %d/%d %#x\n",
+			      fA, default_fg, bA, default_bg, cA));
+	if (fA < MAX_COLOR
+	    && bA < MAX_COLOR
+#ifdef USE_CURSES_PAIR_0
+	    && (cA != A_NORMAL || fA != default_fg || bA != default_bg)
+#endif
+	    && curPair < MAX_PAIR) {
+	    if (our_pairs[iBold][iBlink][iFg][iBg] != 0) {
+		curPair = our_pairs[iBold][iBlink][iFg][iBg];
+	    } else {
+		curPair = ++colorPairs;
+		init_pair((short) curPair, (short) fA, (short) bA);
+		our_pairs[iBold][iBlink][iFg][iBg] = UCH(curPair);
+	    }
+	}
+	CTRACE2(TRACE_STYLE, (tfp, "CSS(CURPAIR):%d\n", curPair));
+	if (style < DSTYLE_ELEMENTS)
+	    setStyle(style, COLOR_PAIR(curPair) | cA, cA, mA);
+	setHashStyle(newstyle, COLOR_PAIR(curPair) | cA, cA, mA, element);
+    } else {
+	if (lynx_has_color && fA != NO_COLOR) {
+	    CTRACE2(TRACE_STYLE,
+		    (tfp, "CSS(NC): maximum of %d colorpairs exhausted\n",
+		     COLOR_PAIRS - 1));
+	}
+	/* only mono is set */
+	if (style < DSTYLE_ELEMENTS)
+	    setStyle(style, -1, -1, mA);
+	setHashStyle(newstyle, -1, -1, mA, element);
+    }
+}
+
+/* parse a style option of the format
+ * STYLE:<OBJECT>:FG:BG
+ */
+static void parse_style(char *param)
+{
+    /* *INDENT-OFF* */
+    static struct {
+	const char *name;
+	int style;
+	int *set_hash;
+    } table[] = {
+	{ "default",		-1,			0 }, /* default fg/bg */
+	{ "alink",		DSTYLE_ALINK,		0 }, /* active link */
+	{ "a",			DSTYLE_LINK,		0 }, /* normal link */
+	{ "a",			HTML_A,			0 }, /* normal link */
+	{ "status",		DSTYLE_STATUS,		0 }, /* status bar */
+	{ "label",		DSTYLE_OPTION,		0 }, /* [INLINE]'s */
+	{ "value",		DSTYLE_VALUE,		0 }, /* [INLINE]'s */
+	{ "normal",		DSTYLE_NORMAL,		0 },
+	{ "candy",		DSTYLE_CANDY,		0 }, /* [INLINE]'s */
+	{ "whereis",		DSTYLE_WHEREIS,		&s_whereis },
+	{ "edit.active.pad",	DSTYLE_ELEMENTS,	&s_aedit_pad },
+	{ "edit.active.arrow",	DSTYLE_ELEMENTS,	&s_aedit_arr },
+	{ "edit.active.marked",	DSTYLE_ELEMENTS,	&s_aedit_sel },
+	{ "edit.active",	DSTYLE_ELEMENTS,	&s_aedit },
+	{ "edit.current",	DSTYLE_ELEMENTS,	&s_curedit },
+	{ "edit.prompt.pad",	DSTYLE_ELEMENTS,	&s_prompt_edit_pad },
+	{ "edit.prompt.arrow",	DSTYLE_ELEMENTS,	&s_prompt_edit_arr },
+	{ "edit.prompt.marked",	DSTYLE_ELEMENTS,	&s_prompt_sel },
+	{ "edit.prompt",	DSTYLE_ELEMENTS,	&s_prompt_edit },
+	{ "forwbackw.arrow",	DSTYLE_ELEMENTS,	&s_forw_backw },
+	{ "hot.paste",		DSTYLE_ELEMENTS,	&s_hot_paste },
+	{ "menu.frame",		DSTYLE_ELEMENTS,	&s_menu_frame },
+	{ "menu.bg",		DSTYLE_ELEMENTS,	&s_menu_bg },
+	{ "menu.n",		DSTYLE_ELEMENTS,	&s_menu_number },
+	{ "menu.entry",		DSTYLE_ELEMENTS,	&s_menu_entry },
+	{ "menu.active",	DSTYLE_ELEMENTS,	&s_menu_active },
+	{ "menu.sb",		DSTYLE_ELEMENTS,	&s_menu_sb },
+    };
+    /* *INDENT-ON* */
+
+    unsigned n;
+    BOOL found = FALSE;
+
+    char *buffer = 0;
+    char *tmp = 0;
+    char *element, *mono;
+    const char *fg, *bg;
+
+    if (param == 0)
+	return;
+    CTRACE2(TRACE_STYLE, (tfp, "parse_style(%s)\n", param));
+    StrAllocCopy(buffer, param);
+    if (buffer == 0)
+	return;
+
+    TrimLowercase(buffer);
+    if ((tmp = strchr(buffer, ':')) == 0) {
+	fprintf(stderr, gettext("\
+Syntax Error parsing style in lss file:\n\
+[%s]\n\
+The line must be of the form:\n\
+OBJECT:MONO:COLOR (ie em:bold:brightblue:white)\n\
+where OBJECT is one of EM,STRONG,B,I,U,BLINK etc.\n\n"), buffer);
+	exit_immediately(EXIT_FAILURE);
+    }
+    *tmp = '\0';
+    element = buffer;
+
+    mono = tmp + 1;
+    tmp = strchr(mono, ':');
+
+    if (!tmp) {
+	fg = "nocolor";
+	bg = "nocolor";
+    } else {
+	*tmp = '\0';
+	fg = tmp + 1;
+	tmp = strchr(fg, ':');
+	if (!tmp)
+	    bg = "default";
+	else {
+	    *tmp = '\0';
+	    bg = tmp + 1;
+	}
+    }
+
+    CTRACE2(TRACE_STYLE, (tfp, "CSSPARSE:%s => %d %s\n",
+			  element, hash_code(element),
+			  (hashStyles[hash_code(element)].name ? "used" : "")));
+
+    /*
+     * We use some pseudo-elements, so catch these first
+     */
+    for (n = 0; n < TABLESIZE(table); n++) {
+	if (!strcasecomp(element, table[n].name)) {
+	    parse_attributes(mono, fg, bg, table[n].style, table[n].name);
+	    if (table[n].set_hash != 0)
+		*(table[n].set_hash) = hash_code(table[n].name);
+	    found = TRUE;
+	    break;
+	}
+    }
+
+    if (found) {
+	if (!strcasecomp(element, "normal")) {
+	    /* added - kw */
+	    parse_attributes(mono, fg, bg, DSTYLE_NORMAL, "html");
+	    s_normal = hash_code("html");	/* rather bizarre... - kw */
+
+	    LYnormalColor();
+	}
+    } else {
+	/* It must be a HTML element, so look through the list until we find it. */
+	int element_number = -1;
+	HTTag *t = SGMLFindTag(&HTML_dtd, element);
+
+	if (t && t->name) {
+	    element_number = t - HTML_dtd.tags;
+	}
+	if (element_number >= HTML_A &&
+	    element_number < HTML_ELEMENTS) {
+	    parse_attributes(mono, fg, bg, element_number + STARTAT, element);
+	} else {
+	    parse_attributes(mono, fg, bg, DSTYLE_ELEMENTS, element);
+	}
+    }
+    FREE(buffer);
+}
+
+static void style_deleteStyleList(void)
+{
+    LYFreeStringList(lss_styles);
+    lss_styles = NULL;
+}
+
+static void free_colorstylestuff(void)
+{
+    style_initialiseHashTable();
+    style_deleteStyleList();
+    memset(our_pairs, 0, sizeof(our_pairs));
+    FreeCachedStyles();
+}
+
+/*
+ * Initialise the default style sheet to match the vanilla-curses lynx.
+ */
+static void initialise_default_stylesheet(void)
+{
+    /* Use the data setup in USE_COLOR_TABLE */
+    /* *INDENT-OFF* */
+    static const struct {
+	int		color;	/* index into lynx_color_pairs[] */
+	const char	*type;
+    } table2[] = {
+	/*
+	 * non-color-style colors encode bold/reverse/underline as a 0-7
+	 * index like this:
+	 *  b,r,u 0
+	 *  b,r,U 1
+	 *  b,R,u 2
+	 *  b,R,U 3
+	 *  B,r,u 4
+	 *  B,r,U 5
+	 *  B,R,u 6
+	 *  B,R,U 7
+	 */
+	{ 0,	"normal" },
+	{ 1,	"a" },
+	{ 2,	"status" },
+	{ 4,	"b" },
+	{ 4,	"blink" },
+	{ 4,	"cite" },
+	{ 4,	"del" },
+	{ 4,	"em" },
+	{ 4,	"i" },
+	{ 4,	"ins" },
+	{ 4,	"strike" },
+	{ 4,	"strong" },
+	{ 4,	"u" },
+#if 0
+	{ 5,	"a.b" },
+	{ 5,	"b.a" },
+	{ 5,	"var.a" },
+#endif
+	{ 6,	"alink" },
+	{ 7,	"whereis" },
+#if 0
+	{ 0,	"h2.link" },
+	{ 0,	"link.h2" },
+#endif
+#ifdef USE_PRETTYSRC
+	/* FIXME: HTL_tagspecs_defaults[] has similar info */
+	{ 4,	"span.htmlsrc_comment" },
+	{ 4,	"span.htmlsrc_tag" },
+	{ 4,	"span.htmlsrc_attrib" },
+	{ 4,	"span.htmlsrc_attrval" },
+	{ 4,	"span.htmlsrc_abracket" },
+	{ 4,	"span.htmlsrc_entity" },
+	{ 4,	"span.htmlsrc_href" },
+	{ 4,	"span.htmlsrc_entire" },
+	{ 4,	"span.htmlsrc_badseq" },
+	{ 4,	"span.htmlsrc_badtag" },
+	{ 4,	"span.htmlsrc_badattr" },
+	{ 4,	"span.htmlsrc_sgmlspecial" },
+#endif
+    };
+    /* *INDENT-ON* */
+
+    unsigned n;
+    char *normal = LYgetTableString(0);
+    char *strong = LYgetTableString(4);
+
+    CTRACE1((tfp, "initialise_default_stylesheet\n"));
+
+    /*
+     * For debugging this function, create hash codes for all of the tags.
+     * That makes it simpler to find the cases that are overlooked in the
+     * table.
+     */
+    for (n = 0; n < (unsigned) HTML_dtd.number_of_tags; ++n) {
+	char *name = 0;
+
+	HTSprintf0(&name, "%s:%s", HTML_dtd.tags[n].name, normal);
+	parse_style(name);
+	FREE(name);
+    }
+
+    for (n = 0; n < TABLESIZE(table2); ++n) {
+	int code = table2[n].color;
+	char *name = 0;
+	char *value = 0;
+
+	switch (code) {
+	case 0:
+	    value = normal;
+	    break;
+	case 4:
+	    value = strong;
+	    break;
+	default:
+	    value = LYgetTableString(code);
+	    break;
+	}
+	HTSprintf0(&name, "%s:%s", table2[n].type, value);
+	parse_style(name);
+	FREE(name);
+	if (value != normal && value != strong && value != 0)
+	    free(value);
+    }
+    FREE(normal);
+    FREE(strong);
+}
+
+/* Set all the buckets in the hash table to be empty */
+static void style_initialiseHashTable(void)
+{
+    int i;
+    static int firsttime = 1;
+
+    for (i = 0; i < CSHASHSIZE; i++) {
+	if (firsttime)
+	    hashStyles[i].name = NULL;
+	else
+	    FREE(hashStyles[i].name);
+	hashStyles[i].color = 0;
+	hashStyles[i].cattr = 0;
+	hashStyles[i].mono = 0;
+    }
+    if (firsttime) {
+	firsttime = 0;
+#ifdef LY_FIND_LEAKS
+	atexit(free_colorstylestuff);
+#endif
+    }
+    s_alink = hash_code("alink");
+    s_a = hash_code("a");
+    s_status = hash_code("status");
+    s_alert = hash_code("alert");
+    s_title = hash_code("title");
+#ifdef USE_SCROLLBAR
+    s_sb_bar = hash_code("scroll.bar");
+    s_sb_bg = hash_code("scroll.back");
+    s_sb_aa = hash_code("scroll.arrow");
+    s_sb_naa = hash_code("scroll.noarrow");
+#endif
+}
+
+void parse_userstyles(void)
+{
+    char *name;
+    HTList *cur = lss_styles;
+
+    colorPairs = 0;
+    style_initialiseHashTable();
+
+    if (HTList_isEmpty(cur)) {
+	initialise_default_stylesheet();
+    } else {
+	while ((name = (char *) HTList_nextObject(cur)) != NULL) {
+	    CTRACE2(TRACE_STYLE, (tfp, "LSS:%s\n",
+				  (name
+				   ? name
+				   : "!?! empty !?!")));
+	    if (name != NULL)
+		parse_style(name);
+	}
+    }
+
+#define dft_style(a,b) if (a == NOSTYLE) a = b
+    /* *INDENT-OFF* */
+    dft_style(s_prompt_edit,		s_normal);
+    dft_style(s_prompt_edit_arr,	s_prompt_edit);
+    dft_style(s_prompt_edit_pad,	s_prompt_edit);
+    dft_style(s_prompt_sel,		s_prompt_edit);
+    dft_style(s_aedit,			s_alink);
+    dft_style(s_aedit_arr,		s_aedit);
+    dft_style(s_aedit_pad,		s_aedit);
+    dft_style(s_curedit,		s_aedit);
+    dft_style(s_aedit_sel,		s_aedit);
+    dft_style(s_menu_bg,		s_normal);
+    dft_style(s_menu_entry,		s_menu_bg);
+    dft_style(s_menu_frame,		s_menu_bg);
+    dft_style(s_menu_number,		s_menu_bg);
+    dft_style(s_menu_active,		s_alink);
+    /* *INDENT-ON* */
+
+}
+
+/* Add a STYLE: option line to our list.  Process "default:" early
+ * for it to have the same semantic as other lines: works at any place
+ * of the style file, the first line overrides the later ones.
+ */
+static void HStyle_addStyle(char *buffer)
+{
+    char *name = NULL;
+
+    CTRACE1((tfp, "HStyle_addStyle(%s)\n", buffer));
+
+    StrAllocCopy(name, buffer);
+    TrimLowercase(name);
+
+    if (lss_styles == NULL)
+	lss_styles = HTList_new();
+
+    if (!strncasecomp(name, "default:", 8)) {
+	/* default fg/bg */
+	CTRACE2(TRACE_STYLE, (tfp, "READCSS.default%s:%s\n",
+			      (default_color_reset ? ".ignore" : ""),
+			      name ? name : "!?! empty !?!"));
+	if (!default_color_reset)
+	    parse_style(name);
+	return;			/* do not need to process it again */
+    }
+    CTRACE2(TRACE_STYLE, (tfp, "READCSS:%s\n", name ? name : "!?! empty !?!"));
+    HTList_addObject(lss_styles, name);
+}
+
+static int style_readFromFileREC(char *lss_filename,
+				 char *parent_filename)
+{
+    FILE *fh;
+    char *buffer = NULL;
+
+    CTRACE2(TRACE_STYLE, (tfp, "CSS:Reading styles from file: %s\n",
+			  lss_filename ? lss_filename : "?!? empty ?!?"));
+    if (isEmpty(lss_filename))
+	return -1;
+    if ((fh = LYOpenCFG(lss_filename, parent_filename, LYNX_LSS_FILE)) == 0) {
+	/* this should probably be an alert or something */
+	CTRACE2(TRACE_STYLE, (tfp,
+			      "CSS:Can't open style file '%s', using defaults\n", lss_filename));
+	return -1;
+    }
+
+    if (parent_filename == 0) {
+	free_colorstylestuff();
+    }
+
+    while (LYSafeGets(&buffer, fh) != NULL) {
+	LYTrimTrailing(buffer);
+	LYTrimTail(buffer);
+	LYTrimHead(buffer);
+	if (!strncasecomp(buffer, "include:", 8))
+	    style_readFromFileREC(LYSkipBlanks(buffer + 8), lss_filename);
+	else if (buffer[0] != '#' && strlen(buffer) != 0)
+	    HStyle_addStyle(buffer);
+    }
+
+    LYCloseInput(fh);
+    if ((parent_filename == 0) && LYCursesON)
+	parse_userstyles();
+    return 0;
+}
+
+int style_readFromFile(char *filename)
+{
+    return style_readFromFileREC(filename, (char *) 0);
+}
+
+/* Used in HTStructured methods: - kw */
+
+void TrimColorClass(const char *tagname,
+		    char *styleclassname,
+		    int *phcode)
+{
+    char *end, *start = NULL, *lookfrom;
+    char tmp[64];
+
+    sprintf(tmp, ";%.*s", (int) sizeof(tmp) - 3, tagname);
+    TrimLowercase(tmp);
+
+    if ((lookfrom = styleclassname) != 0) {
+	do {
+	    end = start;
+	    start = strstr(lookfrom, tmp);
+	    if (start)
+		lookfrom = start + 1;
+	}
+	while (start);
+	/* trim the last matching element off the end
+	 * - should match classes here as well (rp)
+	 */
+	if (end)
+	    *end = '\0';
+    }
+    *phcode = hash_code(lookfrom && *lookfrom ? lookfrom : &tmp[1]);
+}
+
+/* This function is designed as faster analog to TrimColorClass.
+ * It assumes that tag_name is present in stylename! -HV
+ */
+void FastTrimColorClass(const char *tag_name,
+			unsigned name_len,
+			char *stylename,
+			char **pstylename_end,	/*will be modified */
+			int *phcode)	/*will be modified */
+{
+    char *tag_start = *pstylename_end;
+    BOOLEAN found = FALSE;
+
+    CTRACE2(TRACE_STYLE,
+	    (tfp, "STYLE.fast-trim: [%s] from [%s]: ",
+	     tag_name, stylename));
+    while (tag_start >= stylename) {
+	for (; (tag_start >= stylename) && (*tag_start != ';'); --tag_start) ;
+	if (!strncasecomp(tag_start + 1, tag_name, (int) name_len)) {
+	    found = TRUE;
+	    break;
+	}
+	--tag_start;
+    }
+    if (found) {
+	*tag_start = '\0';
+	*pstylename_end = tag_start;
+    }
+    CTRACE2(TRACE_STYLE, (tfp, found ? "success.\n" : "failed.\n"));
+    *phcode = hash_code(tag_start + 1);
+}
+
+/* This is called each time lss styles are read. It will fill
+ * each element of 'cached_tag_styles' -HV
+ */
+void cache_tag_styles(void)
+{
+    char buf[200];
+    int i;
+
+    for (i = 0; i < HTML_ELEMENTS; ++i) {
+	LYstrncpy(buf, HTML_dtd.tags[i].name, sizeof(buf) - 1);
+	LYLowerCase(buf);
+	cached_tag_styles[i] = hash_code(buf);
+    }
+}
+
+#define SIZEOF_CACHED_STYLES (unsigned) (cached_styles_rows * cached_styles_cols)
+
+static unsigned *RefCachedStyle(int y, int x)
+{
+    unsigned *result = 0;
+
+    if (cached_styles_ptr == 0) {
+	cached_styles_rows = display_lines;
+	cached_styles_cols = LYcols;
+	cached_styles_ptr = typecallocn(unsigned, SIZEOF_CACHED_STYLES);
+    }
+    if (y >= 0 &&
+	x >= 0 &&
+	y < cached_styles_rows &&
+	x < cached_styles_cols) {
+	result = cached_styles_ptr + (y * cached_styles_cols) + x;
+    }
+    return result;
+}
+
+BOOL ValidCachedStyle(int y, int x)
+{
+    return (BOOL) (RefCachedStyle(y, x) != 0);
+}
+
+unsigned GetCachedStyle(int y, int x)
+{
+    unsigned value = 0;
+    unsigned *cache = RefCachedStyle(y, x);
+
+    if (cache != 0) {
+	value = *cache;
+    }
+    return value;
+}
+
+void SetCachedStyle(int y, int x, unsigned value)
+{
+    unsigned *cache = RefCachedStyle(y, x);
+
+    if (cache != 0) {
+	*cache = value;
+    }
+}
+
+void ResetCachedStyles(void)
+{
+    if (cached_styles_ptr != NULL) {
+	memset(cached_styles_ptr, 0, sizeof(unsigned) * SIZEOF_CACHED_STYLES);
+    }
+}
+
+void FreeCachedStyles(void)
+{
+    if (cached_styles_ptr != NULL) {
+	FREE(cached_styles_ptr);
+	cached_styles_rows = 0;
+	cached_styles_cols = 0;
+    }
+}
+
+#endif /* USE_COLOR_STYLE */
diff --git a/src/LYStyle.h b/src/LYStyle.h
new file mode 100644
index 00000000..93ff1193
--- /dev/null
+++ b/src/LYStyle.h
@@ -0,0 +1,82 @@
+/* $LynxId: LYStyle.h,v 1.16 2009/01/01 23:06:08 tom Exp $ */
+#ifndef LYSTYLE_H
+#define LYSTYLE_H
+
+#include <HTUtils.h>
+
+#ifdef USE_COLOR_STYLE
+
+#include <AttrList.h>
+#include <HTMLDTD.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* list of elements */ extern const SGML_dtd HTML_dtd;
+
+/* array of currently set styles */
+    extern HTCharStyle displayStyles[DSTYLE_ELEMENTS];
+
+/* Set all the buckets in the hash table to be empty */
+    extern void parse_userstyles(void);
+
+    extern void style_defaultStyleSheet(void);
+
+    extern int style_readFromFile(char *file);
+
+    extern void TrimColorClass(const char *tagname,
+			       char *styleclassname,
+			       int *phcode);
+
+/* this is an array of styles for tags that don't specify 'class' - the values
+ * from that array will be suggested by SGML.c by setting the following
+ * variable.  Value of -1 means that style value should be calculated honestly. 
+ * -HV
+ */
+    extern int cached_tag_styles[HTML_ELEMENTS];
+
+/* the style for current tag is suggested in current_tag_style.  If
+ * force_current_tag_style =TRUE, then no attempts to calculate the color style
+ * for current tag should be made - the value of 'current_tag_style' must be
+ * used.
+ */
+    extern int current_tag_style;
+    extern BOOL force_current_tag_style;
+
+    extern BOOL force_classname;
+
+/* if force_current_tag_style =TRUE, then here will be the classname (this is
+ * done to avoid copying the class name to the buffer class_name.
+ */
+    extern char *forced_classname;
+
+/* This is called each time lss styles are read.  It will fill each elt of
+ * 'cached_tag_styles' -HV
+ */
+    extern void cache_tag_styles(void);
+
+/* this is global var - it can be used for reading the end of string found
+ * during last invokation of TrimColorClass.
+ */
+    extern void FastTrimColorClass(const char *tag_name,
+				   unsigned name_len,
+				   char *stylename,
+				   char **pstylename_end,
+				   int *hcode);
+
+/*
+ * Functions for cached-styles
+ */
+    extern BOOL ValidCachedStyle(int y, int x);
+    extern unsigned GetCachedStyle(int y, int x);
+    extern void FreeCachedStyles(void);
+    extern void ResetCachedStyles(void);
+    extern void SetCachedStyle(int y, int x, unsigned value);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* USE_COLOR_STYLE */
+extern int lynx_has_color;
+
+#endif /* LYSTYLE_H */
diff --git a/src/LYTraversal.c b/src/LYTraversal.c
new file mode 100644
index 00000000..02d516d3
--- /dev/null
+++ b/src/LYTraversal.c
@@ -0,0 +1,182 @@
+/*
+ * $LynxId: LYTraversal.c,v 1.27 2009/01/01 22:37:06 tom Exp $
+ */
+#include <HTUtils.h>
+#include <LYGlobalDefs.h>
+#include <LYUtils.h>
+#include <LYClean.h>
+#include <LYCurses.h>
+#include <LYStrings.h>
+#include <LYTraversal.h>
+
+#include <LYexit.h>
+#include <LYLeaks.h>
+
+/* routines to handle special traversal feature */
+
+static void final_perror(const char *msg, BOOLEAN clean_flag)
+{
+    int saved_errno = errno;
+
+    if (LYCursesON) {
+	if (clean_flag)
+	    cleanup();
+	else
+	    stop_curses();
+    }
+    set_errno(saved_errno);
+    perror(msg);
+}
+
+static void exit_with_perror(const char *msg)
+{
+    final_perror(msg, TRUE);
+    exit_immediately(EXIT_FAILURE);
+}
+
+BOOLEAN lookup_link(char *target)
+{
+    FILE *ifp;
+    char *buffer = NULL;
+    char *line = NULL;
+    int result = FALSE;
+
+    if ((ifp = fopen(TRAVERSE_FILE, TXT_R)) == NULL) {
+	if ((ifp = LYNewTxtFile(TRAVERSE_FILE)) == NULL) {
+	    exit_with_perror(CANNOT_OPEN_TRAV_FILE);
+	} else {
+	    LYCloseOutput(ifp);
+	    return (FALSE);
+	}
+    }
+
+    HTSprintf0(&line, "%s\n", target);
+
+    while (LYSafeGets(&buffer, ifp) != NULL) {
+	if (STREQ(line, buffer)) {
+	    result = TRUE;
+	    break;
+	}
+    }				/* end while */
+    FREE(line);
+    FREE(buffer);
+
+    LYCloseInput(ifp);
+    return (BOOL) (result);
+}
+
+void add_to_table(char *target)
+{
+
+    FILE *ifp;
+
+    if ((ifp = LYAppendToTxtFile(TRAVERSE_FILE)) == NULL) {
+	exit_with_perror(CANNOT_OPEN_TRAV_FILE);
+    }
+
+    fprintf(ifp, "%s\n", target);
+
+    LYCloseOutput(ifp);
+}
+
+void add_to_traverse_list(char *fname, char *prev_link_name)
+{
+
+    FILE *ifp;
+
+    if ((ifp = LYAppendToTxtFile(TRAVERSE_FOUND_FILE)) == NULL) {
+	exit_with_perror(CANNOT_OPEN_TRAF_FILE);
+    }
+
+    fprintf(ifp, "%s\t%s\n", fname, prev_link_name);
+
+    LYCloseOutput(ifp);
+}
+
+void dump_traversal_history(void)
+{
+    int x;
+    FILE *ifp;
+
+    if (nhist <= 0)
+	return;
+
+    if ((ifp = LYAppendToTxtFile(TRAVERSE_FILE)) == NULL) {
+	final_perror(CANNOT_OPEN_TRAV_FILE, FALSE);
+	return;
+    }
+
+    fprintf(ifp, "\n\n%s\n\n\t    %s\n\n",
+	    TRAV_WAS_INTERRUPTED,
+	    gettext("here is a list of the history stack so that you may rebuild"));
+
+    for (x = nhist - 1; x >= 0; x--) {
+	fprintf(ifp, "%s\t%s\n", HDOC(x).title, HDOC(x).address);
+    }
+
+    LYCloseOutput(ifp);
+}
+
+void add_to_reject_list(char *target)
+{
+
+    FILE *ifp;
+
+    CTRACE((tfp, "add_to_reject_list(%s)\n", target));
+
+    if ((ifp = LYAppendToTxtFile(TRAVERSE_REJECT_FILE)) == NULL) {
+	exit_with_perror(CANNOT_OPEN_REJ_FILE);
+    }
+
+    fprintf(ifp, "%s\n", target);
+
+    LYCloseOutput(ifp);
+}
+
+/* there need not be a reject file, so if it doesn't open, just return
+   FALSE, meaning "target not in reject file" If the last character in
+   a line in a reject file is "*", then also reject if target matches up to
+   that point in the string
+   Blank lines are ignored
+   Lines that contain just a * are allowed, but since they mean "reject
+   everything" it shouldn't come up much!
+ */
+
+BOOLEAN lookup_reject(char *target)
+{
+    FILE *ifp;
+    char *buffer = NULL;
+    char *line = NULL;
+    unsigned len;
+    int result = FALSE;
+
+    if ((ifp = fopen(TRAVERSE_REJECT_FILE, TXT_R)) == NULL) {
+	return (FALSE);
+    }
+
+    HTSprintf0(&line, "%s", target);
+
+    while (LYSafeGets(&buffer, ifp) != NULL && !result) {
+	LYTrimTrailing(buffer);
+	len = strlen(buffer);
+	if (len != 0) {		/* if not an empty line */
+	    if (buffer[len - 1] == '*') {
+		/* if last char is * and the rest of the chars match */
+		if ((len == 1) || (strncmp(line, buffer, len - 1) == 0)) {
+		    result = TRUE;
+		}
+	    } else {
+		if (STREQ(line, buffer)) {
+		    result = TRUE;
+		}
+	    }
+	}
+    }				/* end while loop over the file */
+    FREE(buffer);
+    FREE(line);
+
+    LYCloseInput(ifp);
+
+    CTRACE((tfp, "lookup_reject(%s) -> %d\n", target, result));
+    return (BOOL) (result);
+}
diff --git a/src/LYTraversal.h b/src/LYTraversal.h
new file mode 100644
index 00000000..8e712e51
--- /dev/null
+++ b/src/LYTraversal.h
@@ -0,0 +1,23 @@
+/*	traversal.c function declarations
+*/
+#ifndef TRAVERSAL_H
+#define TRAVERSAL_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>		/* BOOL, ARGS */
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    extern BOOLEAN lookup_link(char *target);
+    extern void add_to_table(char *target);
+    extern void add_to_traverse_list(char *fname, char *prev_link_name);
+    extern void dump_traversal_history(void);
+    extern void add_to_reject_list(char *target);
+    extern BOOLEAN lookup_reject(char *target);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* TRAVERSAL_H */
diff --git a/src/LYUpload.c b/src/LYUpload.c
new file mode 100644
index 00000000..7556ed06
--- /dev/null
+++ b/src/LYUpload.c
@@ -0,0 +1,225 @@
+/*
+ *  Routines to upload files to the local filesystem.
+ *  Created by: Rick Mallett, Carleton University
+ *  Report problems to rmallett@ccs.carleton.ca
+ *  Modified 15-Dec-95 George Lindholm (lindholm@ucs.ubc.ca):
+ *	Reread the upload menu page every time, in case the "upload" directory
+ *	  has changed (make the current directory that for the upload process).
+ *	Prompt for the upload file name if there is no "%s" in the command
+ *	  string.  Most protocols allow the user to specify the file name
+ *	  from the client side.  Xmodem appears to be the only that can't
+ *	  figure out the filename from the transfer data so it needs the
+ *	  information from lynx (or an upload script which prompts for it).
+ *	  On the other hand, zmodem aborts when you give it a filename on
+ *	  the command line (great way of bypassing the nodotfile code :=( ).
+ */
+
+#include <HTUtils.h>
+#include <HTFile.h>
+#include <HTParse.h>
+#include <HTAlert.h>
+#include <LYCurses.h>
+#include <LYUtils.h>
+#include <LYGlobalDefs.h>
+#include <LYStrings.h>
+#include <LYClean.h>
+#include <LYGetFile.h>
+#include <LYUpload.h>
+#include <LYLocal.h>
+
+#include <LYexit.h>
+#include <LYLeaks.h>
+
+#define SUBDIR_COMMAND "cd %s ; "
+
+/*
+ * LYUpload uploads a file to a given location using a specified upload method. 
+ * It parses an incoming link that looks like:
+ *	LYNXDIRED://UPLOAD=<#>/TO=<STRING>
+ */
+int LYUpload(char *line)
+{
+    char *method, *directory;
+    int method_number;
+    int count;
+    char *the_upload = 0;
+    char tmpbuf[LY_MAXPATH];
+    char *filename = NULL;
+    lynx_list_item_type *upload_command = 0;
+    char *the_command = 0;
+
+    /*
+     * Use configured upload commands.
+     */
+    if ((directory = strstr(line, "TO=")) == NULL)
+	goto failed;
+    *(directory - 1) = '\0';
+    /* go past "Directory=" */
+    directory += 3;
+
+    if ((method = strstr(line, "UPLOAD=")) == NULL)
+	goto failed;
+    /*
+     * Go past "Method=".
+     */
+    method += 7;
+    method_number = atoi(method);
+
+    for (count = 0, upload_command = uploaders; count < method_number;
+	 count++, upload_command = upload_command->next) ;	/* null body */
+
+    /*
+     * Parsed out the Method and the Location?
+     */
+    if (upload_command->command == NULL) {
+	HTAlert(gettext("ERROR! - upload command is misconfigured"));
+	goto failed;
+    }
+
+    /*
+     * Care about the local name?
+     */
+    if (HTCountCommandArgs(upload_command->command)) {
+	/*
+	 * Commands have the form "command %s [etc]" where %s is the filename.
+	 */
+	_statusline(FILENAME_PROMPT);
+      retry:
+	*tmpbuf = '\0';
+	if (LYgetstr(tmpbuf, VISIBLE, sizeof(tmpbuf), NORECALL) < 0)
+	    goto cancelled;
+
+	if (*tmpbuf == '\0')
+	    goto cancelled;
+
+	if (strstr(tmpbuf, "../") != NULL) {
+	    HTAlert(gettext("Illegal redirection \"../\" found! Request ignored."));
+	    goto cancelled;
+	} else if (strchr(tmpbuf, '/') != NULL) {
+	    HTAlert(gettext("Illegal character \"/\" found! Request ignored."));
+	    goto cancelled;
+	} else if (tmpbuf[0] == '~') {
+	    HTAlert(gettext("Illegal redirection using \"~\" found! Request ignored."));
+	    goto cancelled;
+	}
+	HTSprintf0(&filename, "%s/%s", directory, tmpbuf);
+
+#ifdef HAVE_POPEN
+	if (LYIsPipeCommand(filename)) {
+	    HTAlert(CANNOT_WRITE_TO_FILE);
+	    _statusline(NEW_FILENAME_PROMPT);
+	    goto retry;
+	}
+#endif
+	switch (LYValidateOutput(filename)) {
+	case 'Y':
+	    break;
+	case 'N':
+	    goto retry;
+	default:
+	    goto cancelled;
+	}
+
+	/*
+	 * See if we can write to it.
+	 */
+	CTRACE((tfp, "LYUpload: filename is %s", filename));
+
+	if (!LYCanWriteFile(filename)) {
+	    goto retry;
+	}
+
+	HTAddParam(&the_upload, upload_command->command, 1, filename);
+	HTEndParam(&the_upload, upload_command->command, 1);
+    } else {			/* No substitution, no changes */
+	StrAllocCopy(the_upload, upload_command->command);
+    }
+
+    HTAddParam(&the_command, SUBDIR_COMMAND, 1, directory);
+    HTEndParam(&the_command, SUBDIR_COMMAND, 1);
+    StrAllocCat(the_command, the_upload);
+
+    CTRACE((tfp, "command: %s\n", the_command));
+
+    stop_curses();
+    LYSystem(the_command);
+    start_curses();
+
+    FREE(the_command);
+    FREE(the_upload);
+#if defined(MULTI_USER_UNIX)
+    if (filename != 0)
+	chmod(filename, HIDE_CHMOD);
+#endif /* UNIX */
+    FREE(filename);
+
+    return 1;
+
+  failed:
+    HTAlert(gettext("Unable to upload file."));
+    return 0;
+
+  cancelled:
+    HTInfoMsg(CANCELLING);
+    return 0;
+}
+
+/*
+ * LYUpload_options writes out the current upload choices to a file so that the
+ * user can select printers in the same way that they select all other links. 
+ * Upload links look like:
+ *	LYNXDIRED://UPLOAD=<#>/TO=<STRING>
+ */
+int LYUpload_options(char **newfile,
+		     char *directory)
+{
+    static char tempfile[LY_MAXPATH];
+    FILE *fp0;
+    lynx_list_item_type *cur_upload;
+    int count;
+    static char curloc[LY_MAXPATH];
+    char *cp;
+
+    if ((fp0 = InternalPageFP(tempfile, TRUE)) == 0)
+	return (-1);
+
+#ifdef VMS
+    strcpy(curloc, "/sys$login");
+#else
+    cp = HTfullURL_toFile(directory);
+    strcpy(curloc, cp);
+    LYTrimPathSep(curloc);
+    FREE(cp);
+#endif /* VMS */
+
+    LYLocalFileToURL(newfile, tempfile);
+    LYRegisterUIPage(*newfile, UIP_UPLOAD_OPTIONS);
+
+    BeginInternalPage(fp0, UPLOAD_OPTIONS_TITLE, UPLOAD_OPTIONS_HELP);
+
+    fprintf(fp0, "<pre>\n");
+    fprintf(fp0, "   <em>%s</em> %s\n", gettext("Upload To:"), curloc);
+    fprintf(fp0, "\n%s\n", gettext("Upload options:"));
+
+    if (uploaders != NULL) {
+	for (count = 0, cur_upload = uploaders;
+	     cur_upload != NULL;
+	     cur_upload = cur_upload->next, count++) {
+	    fprintf(fp0, "   <a href=\"LYNXDIRED://UPLOAD=%d/TO=%s\">",
+		    count, curloc);
+	    fprintf(fp0, "%s", (cur_upload->name ?
+				cur_upload->name : gettext("No Name Given")));
+	    fprintf(fp0, "</a>\n");
+	}
+    } else {
+	fprintf(fp0, "   &lt;NONE&gt;\n");
+    }
+
+    fprintf(fp0, "</pre>\n");
+    EndInternalPage(fp0);
+    LYCloseTempFP(fp0);
+
+    LYforce_no_cache = TRUE;
+
+    return (0);
+}
diff --git a/src/LYUpload.h b/src/LYUpload.h
new file mode 100644
index 00000000..84a71a03
--- /dev/null
+++ b/src/LYUpload.h
@@ -0,0 +1,17 @@
+#ifndef LYUPLOAD_H
+#define LYUPLOAD_H
+
+#ifndef LYSTRUCTS_H
+#include <LYStructs.h>
+#endif /* LYSTRUCTS_H */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    extern int LYUpload(char *line);
+    extern int LYUpload_options(char **newfile, char *directory);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYUPLOAD_H */
diff --git a/src/LYUtils.c b/src/LYUtils.c
new file mode 100644
index 00000000..d89b6963
--- /dev/null
+++ b/src/LYUtils.c
@@ -0,0 +1,7841 @@
+/*
+ * $LynxId: LYUtils.c,v 1.195 2010/04/29 20:52:32 tom Exp $
+ */
+#include <HTUtils.h>
+#include <HTTCP.h>
+#include <HTParse.h>
+#include <HTAccess.h>
+#include <HTCJK.h>
+#include <HTAlert.h>
+
+#if defined(__MINGW32__)
+
+extern int kbhit(void);		/* FIXME: use conio.h */
+
+#undef UNIX
+
+#elif defined(_WINDOWS)
+
+#ifdef DONT_USE_GETTEXT
+#undef gettext
+#endif
+
+#include <conio.h>
+
+#ifdef DONT_USE_GETTEXT
+#define gettext(s) s
+#endif
+
+#if !defined(kbhit) && defined(_WCONIO_DEFINED)
+#define kbhit() _kbhit()	/* reasonably recent conio.h */
+#endif
+
+#endif /* __MINGW32__ */
+
+#include <LYCurses.h>
+#include <LYHistory.h>
+#include <LYStrings.h>
+#include <LYGlobalDefs.h>
+#include <LYUtils.h>
+#include <LYSignal.h>
+#include <GridText.h>
+#include <LYClean.h>
+#include <LYCharSets.h>
+#include <LYCharUtils.h>
+
+#include <LYMainLoop.h>
+#include <LYKeymap.h>
+
+#ifdef __DJGPP__
+#include <go32.h>
+#include <sys/exceptn.h>
+#endif /* __DJGPP__ */
+
+#ifndef NO_GROUPS
+#include <HTFile.h>
+#endif
+
+#ifdef _WINDOWS			/* 1998/04/30 (Thu) 19:04:25 */
+#define GETPID()	(unsigned) (getpid() & 0xffff)
+#else
+#define GETPID()	(unsigned) getpid()
+#endif /* _WINDOWS */
+
+#ifdef FNAMES_8_3
+#define PID_FMT "%04x"
+#else
+#define PID_FMT "%u"
+#endif
+
+#ifdef DJGPP_KEYHANDLER
+#include <bios.h>
+#endif /* DJGPP_KEYHANDLER */
+
+#ifdef __EMX__
+#  define BOOLEAN OS2_BOOLEAN	/* Conflicts, but is used */
+#  undef HT_ERROR		/* Conflicts too */
+#  define INCL_PM		/* I want some PM functions.. */
+#  define INCL_DOSPROCESS	/* TIB PIB. */
+#  include <os2.h>
+#  undef BOOLEAN
+#endif
+
+#ifdef VMS
+#include <descrip.h>
+#include <libclidef.h>
+#include <lib$routines.h>
+#endif /* VMS */
+
+#ifdef HAVE_UTMP
+#include <pwd.h>
+#ifdef UTMPX_FOR_UTMP
+#include <utmpx.h>
+#define utmp utmpx
+#ifdef UTMPX_FILE
+#ifdef UTMP_FILE
+#undef UTMP_FILE
+#endif /* UTMP_FILE */
+#define UTMP_FILE UTMPX_FILE
+#else
+#ifdef __UTMPX_FILE
+#define UTMP_FILE __UTMPX_FILE	/* at least in OS/390  S/390 -- gil -- 2100 */
+#else
+#ifndef UTMP_FILE
+#define UTMP_FILE "/var/adm/utmpx"	/* Digital Unix 4.0 */
+#endif
+#endif
+#endif /* UTMPX_FILE */
+#else
+#include <utmp.h>
+#endif /* UTMPX_FOR_UTMP */
+#endif /* HAVE_UTMP */
+
+#ifdef NEED_PTEM_H
+/* they neglected to define struct winsize in termios.h -- it's only in
+ * termio.h and ptem.h (the former conflicts with other definitions).
+ */
+#include	<sys/stream.h>
+#include	<sys/ptem.h>
+#endif
+
+#include <LYLeaks.h>
+
+#ifdef USE_COLOR_STYLE
+#include <AttrList.h>
+#include <LYHash.h>
+#include <LYStyle.h>
+#endif
+
+#ifdef SVR4_BSDSELECT
+extern int BSDselect(int nfds, fd_set * readfds, fd_set * writefds,
+		     fd_set * exceptfds, struct timeval *timeout);
+
+#ifdef select
+#undef select
+#endif /* select */
+#define select BSDselect
+#ifdef SOCKS
+#ifdef Rselect
+#undef Rselect
+#endif /* Rselect */
+#define Rselect BSDselect
+#endif /* SOCKS */
+#endif /* SVR4_BSDSELECT */
+
+#ifdef __DJGPP__
+#undef select			/* defined to select_s in www_tcp.h */
+#endif
+
+#ifndef UTMP_FILE
+#if defined(__FreeBSD__) || defined(__bsdi__)
+#define UTMP_FILE _PATH_UTMP
+#else
+#define UTMP_FILE "/etc/utmp"
+#endif /* __FreeBSD__ || __bsdi__ */
+#endif /* !UTMP_FILE */
+
+/*
+ * experimental - make temporary filenames random to make the scheme less
+ * obvious.  However, as noted by KW, there are instances (such as the
+ * 'O'ption page, for which Lynx will store a temporary filename even when
+ * it no longer applies, since it will reuse that filename at a later time.
+ */
+#ifdef USE_RAND_TEMPNAME
+#if defined(LYNX_RAND_MAX)
+#define HAVE_RAND_TEMPNAME 1
+#define MAX_TEMPNAME 10000
+#ifndef BITS_PER_CHAR
+#define BITS_PER_CHAR 8
+#endif
+#endif
+#endif
+
+#define COPY_COMMAND "%s %s %s"
+
+static HTList *localhost_aliases = NULL;	/* Hosts to treat as local */
+static char *HomeDir = NULL;	/* HOME directory */
+
+HTList *sug_filenames = NULL;	/* Suggested filenames   */
+
+/*
+ * Maintain a list of all of the temp-files we create so that we can remove
+ * them during the cleanup.
+ */
+typedef struct _LYTemp {
+    struct _LYTemp *next;
+    char *name;
+    BOOLEAN outs;
+    FILE *file;
+} LY_TEMP;
+
+static LY_TEMP *ly_temp;
+
+static LY_TEMP *FindTempfileByName(const char *name)
+{
+    LY_TEMP *p;
+
+    for (p = ly_temp; p != 0; p = p->next) {
+	if (!strcmp(p->name, name)) {
+	    break;
+	}
+    }
+    return p;
+}
+
+static LY_TEMP *FindTempfileByFP(FILE *fp)
+{
+    LY_TEMP *p;
+
+    for (p = ly_temp; p != 0; p = p->next) {
+	if (p->file == fp) {
+	    break;
+	}
+    }
+    return p;
+}
+
+#if defined(_WIN32)
+/*
+ * Use RegQueryValueExA() rather than RegQueryValueEx() for compatibility
+ * with non-Unicode winvile
+ */
+int w32_get_reg_sz(HKEY hkey, const char *name, char *value, unsigned length)
+{
+    int result;
+    DWORD dwSzBuffer = length;
+
+    CTRACE((tfp, "w32_get_reg_sz(%s)\n", name));
+    result = RegQueryValueExA(hkey,
+			      name,
+			      NULL,
+			      NULL,
+			      (LPBYTE) value,
+			      &dwSzBuffer);
+    if (result == ERROR_SUCCESS) {
+	value[dwSzBuffer] = 0;
+	CTRACE((tfp, "->%s\n", value));
+    }
+    return result;
+}
+#endif
+
+/*
+ * Get an environment variable, rejecting empty strings
+ */
+char *LYGetEnv(const char *name)
+{
+    char *result = getenv(name);
+
+#if defined(_WIN32)
+    if (result == 0) {
+	static HKEY rootkeys[] =
+	{HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE};
+
+	int j;
+	HKEY hkey;
+	char buffer[256];
+
+	for (j = 0; j < TABLESIZE(rootkeys); ++j) {
+	    if (RegOpenKeyEx(rootkeys[j],
+			     LYNX_SUBKEY W32_STRING("\\Environment"),
+			     0,
+			     KEY_READ,
+			     &hkey) == ERROR_SUCCESS) {
+		if (w32_get_reg_sz(hkey, name, buffer, sizeof(buffer)) == ERROR_SUCCESS) {
+
+		    result = strdup(buffer);
+		    (void) RegCloseKey(hkey);
+		    break;
+		}
+
+		(void) RegCloseKey(hkey);
+	    }
+	}
+    }
+#endif
+    return non_empty(result) ? result : 0;
+}
+
+/*
+ * ascii versions of locale sensitive functions needed because in
+ * Turkish locales tolower("I") is not "i". That's fatal for case
+ * sensitive operations with charset names, HTML tags etc.
+ */
+#ifdef USE_ASCII_CTYPES
+int ascii_tolower(int i)
+{
+    if (91 > i && i > 64)
+	return (i + 32);
+    else
+	return i;
+}
+
+int ascii_toupper(int i)
+{
+    if (123 > i && i > 96)
+	return (i - 32);
+    else
+	return i;
+}
+
+int ascii_isupper(int i)
+{
+    if (91 > i && i > 64)
+	return 1;
+    else
+	return 0;
+}
+#endif /* USE_ASCII_CTYPES */
+
+/*
+ * Check for UTF-8 data, returning the length past the first character.
+ * Return zero if we found an ordinary character rather than UTF-8.
+ */
+size_t utf8_length(BOOL utf_flag,
+		   const char *data)
+{
+    size_t utf_extra = 0;
+
+    if (utf_flag && is8bits(*data)) {
+	if ((*data & 0xe0) == 0xc0) {
+	    utf_extra = 1;
+	} else if ((*data & 0xf0) == 0xe0) {
+	    utf_extra = 2;
+	} else if ((*data & 0xf8) == 0xf0) {
+	    utf_extra = 3;
+	} else if ((*data & 0xfc) == 0xf8) {
+	    utf_extra = 4;
+	} else if ((*data & 0xfe) == 0xfc) {
+	    utf_extra = 5;
+	} else {
+	    /*
+	     * Garbage.
+	     */
+	    utf_extra = 0;
+	}
+	if (strlen(data + 1) < utf_extra) {
+	    /*
+	     * Shouldn't happen.
+	     */
+	    utf_extra = 0;
+	}
+    }
+    return utf_extra;
+}
+
+/*
+ * Free storage used for the link-highlighting.
+ */
+void LYFreeHilites(int first, int last)
+{
+    int i;
+
+    for (i = first; i < last; i++) {
+	LYSetHilite(i, NULL);
+	FREE(links[i].lname);
+    }
+}
+
+#define LXP (links[cur].lx)
+#define LYP (links[cur].ly)
+
+/*
+ * Set the initial highlight information for a given link.
+ */
+void LYSetHilite(int cur,
+		 const char *text)
+{
+    links[cur].list.hl_base.hl_text = (char *) text;
+    links[cur].list.hl_len = (short) ((text != NULL) ? 1 : 0);
+    FREE(links[cur].list.hl_info);
+}
+
+/*
+ * Add highlight information for the next line of a link.
+ */
+void LYAddHilite(int cur,
+		 char *text,
+		 int x)
+{
+    HiliteList *list = &(links[cur].list);
+    HiliteInfo *have = list->hl_info;
+    unsigned need = (unsigned) (list->hl_len - 1);
+    unsigned want = (unsigned) (list->hl_len += 1);
+
+    if (have != NULL) {
+	have = typeRealloc(HiliteInfo, have, want);
+    } else {
+	have = typeMallocn(HiliteInfo, want);
+    }
+    list->hl_info = have;
+    have[need].hl_text = text;
+    have[need].hl_x = (short) x;
+}
+
+/*
+ * Get the highlight text, counting from zero.
+ */
+const char *LYGetHiliteStr(int cur,
+			   int count)
+{
+    const char *result;
+
+    if (count >= links[cur].list.hl_len)
+	result = NULL;
+    else if (count > 0)
+	result = links[cur].list.hl_info[count - 1].hl_text;
+    else
+	result = links[cur].list.hl_base.hl_text;
+    return result;
+}
+
+/*
+ * Get the X-ordinate at which to draw the corresponding highlight-text
+ */
+int LYGetHilitePos(int cur,
+		   int count)
+{
+    int result;
+
+    if (count >= links[cur].list.hl_len)
+	result = -1;
+    else if (count > 0)
+	result = links[cur].list.hl_info[count - 1].hl_x;
+    else
+	result = LXP;
+    return result;
+}
+
+#ifdef SHOW_WHEREIS_TARGETS
+
+#define SKIP_GLYPHS(theFlag, theData, theOffset) \
+	(theFlag \
+	    ? LYmbcs_skip_glyphs(theData, (theOffset), theFlag) \
+	    : (theData + (theOffset)))
+
+/*
+ * If we have an emphasized WHEREIS hit in the highlighted text, restore the
+ * emphasis.  Note that we never emphasize the first and last characters of the
+ * highlighted text when we are making the link current, so the link attributes
+ * for the current link will persist at the beginning and end, providing an
+ * indication to the user that it has been made current.  Also note that we use
+ * HText_getFirstTargetInLine() to determine if there's a hit in the HText
+ * structure line containing the link, and if so, get back a copy of the line
+ * starting at that first hit (which might be before or after our link), and
+ * with all IsSpecial characters stripped, so we don't need to deal with them
+ * here.  -FM
+ */
+static BOOL show_whereis_targets(int flag,
+				 int cur,
+				 int count,
+				 const char *target,
+				 BOOL TargetEmphasisON,
+				 BOOL utf_flag)
+{
+    const char *Data = NULL;
+    const char *cp;
+    char *theData = NULL;
+    char buffer[MAX_LINE];
+    char tmp[7];
+    int HitOffset;
+    int LenNeeded;
+    int Offset;
+    int tLen;
+
+    tmp[0] = tmp[1] = tmp[2] = '\0';
+
+    if (non_empty(target)
+	&& (links[cur].type & WWW_LINK_TYPE)
+	&& non_empty(LYGetHiliteStr(cur, count))
+	&& LYP + count < display_lines
+	&& HText_getFirstTargetInLine(HTMainText,
+				      links[cur].anchor_line_num + count,
+				      utf_flag,
+				      &Offset,
+				      &tLen,
+				      &theData,
+				      target)) {
+	int itmp, written, len, y, offset;
+	const char *data;
+	int tlen = (int) strlen(target);
+	int hlen, hLen;
+	int hLine = LYP + count;
+	int hoffset = LYGetHilitePos(cur, count);
+	size_t utf_extra = 0;
+
+	/*
+	 * Copy into the buffer only what will fit up to the right border of
+	 * the screen.  -FM
+	 */
+	LYmbcsstrncpy(buffer,
+		      NonNull(LYGetHiliteStr(cur, count)),
+		      (sizeof(buffer) - 1),
+		      (LYcolLimit - LYGetHilitePos(cur, count)),
+		      utf_flag);
+	hlen = (int) strlen(buffer);
+	hLen = ((IS_CJK_TTY || utf_flag) ?
+		LYmbcsstrlen(buffer, utf_flag, YES) : hlen);
+
+	/*
+	 * Break out if the first hit in the line starts after this link.  -FM
+	 */
+	if (Offset < (hoffset + hLen)) {
+	    /*
+	     * Recursively skip hits that end before this link, and break out
+	     * if there is no hit beyond those.  -FM
+	     */
+	    Data = theData;
+	    while ((Offset < hoffset) &&
+		   ((Offset + tLen) <= hoffset)) {
+		data = (Data + tlen);
+		offset = (Offset + tLen);
+		if (((cp = LYno_attr_mb_strstr(data,
+					       target,
+					       utf_flag, YES,
+					       &HitOffset,
+					       &LenNeeded)) != NULL)
+		    && (offset + LenNeeded) < LYcols) {
+		    Data = cp;
+		    Offset = (offset + HitOffset);
+		} else {
+		    goto highlight_search_done;
+		}
+	    }
+	    data = buffer;
+	    offset = hoffset;
+
+	    /*
+	     * If the hit starts before the hightext, and ends in or beyond the
+	     * hightext, restore the emphasis, skipping the first and last
+	     * characters of the hightext if we're making the link current.
+	     * -FM
+	     */
+	    if ((Offset < offset) &&
+		((Offset + tLen) > offset)) {
+		itmp = 0;
+		written = 0;
+		len = (tlen - (offset - Offset));
+
+		/*
+		 * Go to the start of the hightext and handle its first
+		 * character.  -FM
+		 */
+		LYmove(hLine, offset);
+		tmp[0] = data[itmp];
+		utf_extra = utf8_length(utf_flag, data + itmp);
+		if (utf_extra) {
+		    LYstrncpy(&tmp[1], &data[itmp + 1], (int) utf_extra);
+		    itmp += (int) utf_extra;
+		    /*
+		     * Start emphasis immediately if we are making the link
+		     * non-current.  -FM
+		     */
+		    if (flag != ON) {
+			LYstartTargetEmphasis();
+			TargetEmphasisON = TRUE;
+			LYaddstr(tmp);
+		    } else {
+			LYmove(hLine, (offset + 1));
+		    }
+		    tmp[1] = '\0';
+		    written += (int) (utf_extra + 1);
+		} else if (IS_CJK_TTY && is8bits(tmp[0])) {
+		    /*
+		     * For CJK strings, by Masanobu Kimura.
+		     */
+		    tmp[1] = data[++itmp];
+		    /*
+		     * Start emphasis immediately if we are making the link
+		     * non-current.  -FM
+		     */
+		    if (flag != ON) {
+			LYstartTargetEmphasis();
+			TargetEmphasisON = TRUE;
+			LYaddstr(tmp);
+		    } else {
+			LYmove(hLine, (offset + 1));
+		    }
+		    tmp[1] = '\0';
+		    written += 2;
+		} else {
+		    /*
+		     * Start emphasis immediately if we are making the link
+		     * non-current.  -FM
+		     */
+		    if (flag != ON) {
+			LYstartTargetEmphasis();
+			TargetEmphasisON = TRUE;
+			LYaddstr(tmp);
+		    } else {
+			LYmove(hLine, (offset + 1));
+		    }
+		    written++;
+		}
+		itmp++;
+		/*
+		 * Start emphasis after the first character if we are making
+		 * the link current and this is not the last character.  -FM
+		 */
+		if (!TargetEmphasisON &&
+		    data[itmp] != '\0') {
+		    LYstartTargetEmphasis();
+		    TargetEmphasisON = TRUE;
+		}
+
+		/*
+		 * Handle the remaining characters.  -FM
+		 */
+		for (;
+		     written < len && (tmp[0] = data[itmp]) != '\0';
+		     itmp++) {
+		    /*
+		     * Print all the other target chars, except the last
+		     * character if it is also the last character of hightext
+		     * and we are making the link current.  -FM
+		     */
+		    utf_extra = utf8_length(utf_flag, data + itmp);
+		    if (utf_extra) {
+			LYstrncpy(&tmp[1], &data[itmp + 1], (int) utf_extra);
+			itmp += (int) utf_extra;
+			/*
+			 * Make sure we don't restore emphasis to the last
+			 * character of hightext if we are making the link
+			 * current.  -FM
+			 */
+			if (flag == ON && data[(itmp + 1)] == '\0') {
+			    LYstopTargetEmphasis();
+			    TargetEmphasisON = FALSE;
+			    LYGetYX(y, offset);
+			    LYmove(hLine, (offset + 1));
+			} else {
+			    LYaddstr(tmp);
+			}
+			tmp[1] = '\0';
+			written += (int) (utf_extra + 1);
+		    } else if (IS_CJK_TTY && is8bits(tmp[0])) {
+			/*
+			 * For CJK strings, by Masanobu Kimura.
+			 */
+			tmp[1] = data[++itmp];
+			/*
+			 * Make sure we don't restore emphasis to the last
+			 * character of hightext if we are making the link
+			 * current.  -FM
+			 */
+			if (flag == ON && data[(itmp + 1)] == '\0') {
+			    LYstopTargetEmphasis();
+			    TargetEmphasisON = FALSE;
+			    LYGetYX(y, offset);
+			    LYmove(hLine, (offset + 1));
+			} else {
+			    LYaddstr(tmp);
+			}
+			tmp[1] = '\0';
+			written += 2;
+		    } else {
+			/*
+			 * Make sure we don't restore emphasis to the last
+			 * character of hightext if we are making the link
+			 * current.  -FM
+			 */
+			if (flag == ON && data[(itmp + 1)] == '\0') {
+			    LYstopTargetEmphasis();
+			    TargetEmphasisON = FALSE;
+			    LYGetYX(y, offset);
+			    LYmove(hLine, (offset + 1));
+			} else {
+			    LYaddstr(tmp);
+			}
+			written++;
+		    }
+		}
+
+		/*
+		 * Stop the emphasis if we haven't already, then reset the
+		 * offset to our current position in the line, and if that is
+		 * beyond the link, or or we are making the link current and it
+		 * is the last character of the hightext, we are done.  -FM
+		 */
+		if (TargetEmphasisON) {
+		    LYstopTargetEmphasis();
+		    TargetEmphasisON = FALSE;
+		}
+		LYGetYX(y, offset);
+		if (offset < (hoffset + (flag == ON ? (hLen - 1) : hLen))
+		/*
+		 * See if we have another hit that starts within the
+		 * hightext.  -FM
+		 */
+		    && ((cp =
+			 LYno_attr_mb_strstr(data = SKIP_GLYPHS(utf_flag,
+								Data,
+								offset - Offset),
+					     target,
+					     utf_flag, YES,
+					     &HitOffset,
+					     &LenNeeded)) != NULL)
+		    && (offset + LenNeeded) < LYcols
+		/*
+		 * If the hit starts after the end of the hightext, or we
+		 * are making the link current and the hit starts at its
+		 * last character, we are done.  -FM
+		 */
+		    && (HitOffset + offset) <
+		    (hoffset +
+		     (flag == ON ? (hLen - 1) : hLen))) {
+		    /*
+		     * Set up the data and offset for the hit, and let the code
+		     * for within hightext hits handle it.  -FM
+		     */
+		    Data = cp;
+		    Offset = (offset + HitOffset);
+		    data = buffer;
+		    offset = hoffset;
+		    goto highlight_hit_within_hightext;
+		}
+		goto highlight_search_done;
+	    }
+
+	  highlight_hit_within_hightext:
+	    /*
+	     * If we get to here, the hit starts within the hightext.  If we
+	     * are making the link current and it's the last character in the
+	     * hightext, we are done.  Otherwise, move there and start
+	     * restoring the emphasis.  -FM
+	     */
+	    if ((Offset - offset) <= (flag == ON ? (hLen - 1) : hLen)) {
+		data = SKIP_GLYPHS(utf_flag, data, Offset - offset);
+		if (utf_flag) {
+		    LYrefresh();
+		}
+		offset = Offset;
+		itmp = 0;
+		written = 0;
+		len = tlen;
+
+		/*
+		 * Go to the start of the hit and handle its first character.
+		 * -FM
+		 */
+		LYmove(hLine, offset);
+		tmp[0] = data[itmp];
+		utf_extra = utf8_length(utf_flag, data + itmp);
+		if (utf_extra) {
+		    LYstrncpy(&tmp[1], &data[itmp + 1], (int) utf_extra);
+		    itmp += (int) utf_extra;
+		    /*
+		     * Start emphasis immediately if we are making the link
+		     * non-current, or we are making it current but this is not
+		     * the first or last character of the hightext.  -FM
+		     */
+		    if (flag != ON ||
+			(offset > hoffset && data[itmp + 1] != '\0')) {
+			LYstartTargetEmphasis();
+			TargetEmphasisON = TRUE;
+			LYaddstr(tmp);
+		    } else {
+			LYmove(hLine, (offset + 1));
+		    }
+		    tmp[1] = '\0';
+		    written += (int) (utf_extra + 1);
+		} else if (IS_CJK_TTY && is8bits(tmp[0])) {
+		    /*
+		     * For CJK strings, by Masanobu Kimura.
+		     */
+		    tmp[1] = data[++itmp];
+		    /*
+		     * Start emphasis immediately if we are making the link
+		     * non-current, or we are making it current but this is not
+		     * the first or last character of the hightext.  -FM
+		     */
+		    if (flag != ON ||
+			(offset > hoffset && data[itmp + 1] != '\0')) {
+			LYstartTargetEmphasis();
+			TargetEmphasisON = TRUE;
+			LYaddstr(tmp);
+		    } else {
+			LYmove(hLine, (offset + 2));
+		    }
+		    tmp[1] = '\0';
+		    written += 2;
+		} else {
+		    /*
+		     * Start emphasis immediately if we are making the link
+		     * non-current, or we are making it current but this is not
+		     * the first or last character of the hightext.  -FM
+		     */
+		    if (flag != ON ||
+			(offset > hoffset && data[itmp + 1] != '\0')) {
+			LYstartTargetEmphasis();
+			TargetEmphasisON = TRUE;
+			LYaddstr(tmp);
+		    } else {
+			LYmove(hLine, (offset + 1));
+		    }
+		    written++;
+		}
+		itmp++;
+		/*
+		 * Start emphasis after the first character if we are making
+		 * the link current and this is not the last character.  -FM
+		 */
+		if (!TargetEmphasisON &&
+		    data[itmp] != '\0') {
+		    LYstartTargetEmphasis();
+		    TargetEmphasisON = TRUE;
+		}
+
+		for (;
+		     written < len && (tmp[0] = data[itmp]) != '\0';
+		     itmp++) {
+		    /*
+		     * Print all the other target chars, except the last
+		     * character if it is also the last character of hightext
+		     * and we are making the link current.  -FM
+		     */
+		    utf_extra = utf8_length(utf_flag, data + itmp);
+		    if (utf_extra) {
+			LYstrncpy(&tmp[1], &data[itmp + 1], (int) utf_extra);
+			itmp += (int) utf_extra;
+			/*
+			 * Make sure we don't restore emphasis to the last
+			 * character of hightext if we are making the link
+			 * current.  -FM
+			 */
+			if (flag == ON && data[(itmp + 1)] == '\0') {
+			    LYstopTargetEmphasis();
+			    TargetEmphasisON = FALSE;
+			    LYGetYX(y, offset);
+			    LYmove(hLine, (offset + 1));
+			} else {
+			    LYaddstr(tmp);
+			}
+			tmp[1] = '\0';
+			written += (int) (utf_extra + 1);
+		    } else if (IS_CJK_TTY && is8bits(tmp[0])) {
+			/*
+			 * For CJK strings, by Masanobu Kimura.
+			 */
+			tmp[1] = data[++itmp];
+			/*
+			 * Make sure we don't restore emphasis to the last
+			 * character of hightext if we are making the link
+			 * current.  -FM
+			 */
+			if (flag == ON && data[(itmp + 1)] == '\0') {
+			    LYstopTargetEmphasis();
+			    TargetEmphasisON = FALSE;
+			    LYGetYX(y, offset);
+			    LYmove(hLine, (offset + 1));
+			} else {
+			    LYaddstr(tmp);
+			}
+			tmp[1] = '\0';
+			written += 2;
+		    } else {
+			/*
+			 * Make sure we don't restore emphasis to the last
+			 * character of hightext if we are making the link
+			 * current.  -FM
+			 */
+			if (flag == ON && data[(itmp + 1)] == '\0') {
+			    LYstopTargetEmphasis();
+			    TargetEmphasisON = FALSE;
+			    LYGetYX(y, offset);
+			    LYmove(hLine, (offset + 1));
+			} else {
+			    LYaddstr(tmp);
+			}
+			written++;
+		    }
+		}
+
+		/*
+		 * Stop the emphasis if we haven't already, then reset the
+		 * offset to our current position in the line, and if that is
+		 * beyond the link, or we are making the link current and it is
+		 * the last character in the hightext, we are done.  -FM
+		 */
+		if (TargetEmphasisON) {
+		    LYstopTargetEmphasis();
+		    TargetEmphasisON = FALSE;
+		}
+		LYGetYX(y, offset);
+		if (offset < (hoffset + (flag == ON ? (hLen - 1) : hLen))
+		/*
+		 * See if we have another hit that starts within the
+		 * hightext.  -FM
+		 */
+		    && ((cp =
+			 LYno_attr_mb_strstr(data = SKIP_GLYPHS(utf_flag,
+								Data,
+								offset - Offset),
+					     target,
+					     utf_flag, YES,
+					     &HitOffset,
+					     &LenNeeded)) != NULL)
+		    && (offset + LenNeeded) < LYcols
+		/*
+		 * If the hit starts after the end of the hightext, or we
+		 * are making the link current and the hit starts at its
+		 * last character, we are done.  -FM
+		 */
+		    && (HitOffset + offset) <
+		    (hoffset + (flag == ON ? (hLen - 1) : hLen))) {
+		    /*
+		     * If the target extends beyond our buffer, emphasize
+		     * everything in the hightext starting at this hit.
+		     * Otherwise, set up the data and offsets, and loop back.
+		     * -FM
+		     */
+		    if ((HitOffset + (offset + tLen)) >= (hoffset + hLen)) {
+			offset = (HitOffset + offset);
+			data = SKIP_GLYPHS(utf_flag, Data, offset - hoffset);
+			if (utf_flag) {
+			    LYrefresh();
+			}
+			LYmove(hLine, offset);
+			itmp = 0;
+			written = 0;
+			len = (int) strlen(data);
+
+			/*
+			 * Turn the emphasis back on.  -FM
+			 */
+			LYstartTargetEmphasis();
+			TargetEmphasisON = TRUE;
+			for (;
+			     written < len && (tmp[0] = data[itmp]) != '\0';
+			     itmp++) {
+			    /*
+			     * Print all the other target chars, except the
+			     * last character if it is also the last character
+			     * of hightext and we are making the link current.
+			     * -FM
+			     */
+			    utf_extra = utf8_length(utf_flag, data + itmp);
+			    if (utf_extra) {
+				LYstrncpy(&tmp[1], &data[itmp + 1], (int) utf_extra);
+				itmp += (int) utf_extra;
+				/*
+				 * Make sure we don't restore emphasis to the
+				 * last character of hightext if we are making
+				 * the link current.  -FM
+				 */
+				if (flag == ON && data[(itmp + 1)] == '\0') {
+				    LYstopTargetEmphasis();
+				    TargetEmphasisON = FALSE;
+				    LYGetYX(y, offset);
+				    LYmove(hLine, (offset + 1));
+				} else {
+				    LYaddstr(tmp);
+				}
+				tmp[1] = '\0';
+				written += (int) (utf_extra + 1);
+			    } else if (IS_CJK_TTY && is8bits(tmp[0])) {
+				/*
+				 * For CJK strings, by Masanobu Kimura.
+				 */
+				tmp[1] = data[++itmp];
+				/*
+				 * Make sure we don't restore emphasis to the
+				 * last character of hightext if we are making
+				 * the link current.  -FM
+				 */
+				if (flag == ON && data[(itmp + 1)] == '\0') {
+				    LYstopTargetEmphasis();
+				    TargetEmphasisON = FALSE;
+				} else {
+				    LYaddstr(tmp);
+				}
+				tmp[1] = '\0';
+				written += 2;
+			    } else {
+				/*
+				 * Make sure we don't restore emphasis to the
+				 * last character of hightext if we are making
+				 * the link current.  -FM
+				 */
+				if (flag == ON && data[(itmp + 1)] == '\0') {
+				    LYstopTargetEmphasis();
+				    TargetEmphasisON = FALSE;
+				} else {
+				    LYaddstr(tmp);
+				}
+				written++;
+			    }
+			}
+			/*
+			 * Turn off the emphasis if we haven't already, and
+			 * then we're done.  -FM
+			 */
+			if (TargetEmphasisON) {
+			    LYstopTargetEmphasis();
+			}
+		    } else {
+			Data = cp;
+			Offset = (offset + HitOffset);
+			data = buffer;
+			offset = hoffset;
+			goto highlight_hit_within_hightext;
+		    }
+		}
+	    }
+	}
+    }
+  highlight_search_done:
+    FREE(theData);
+    return TargetEmphasisON;
+}
+#endif /* SHOW_WHEREIS_TARGETS */
+
+#ifdef USE_COLOR_STYLE
+static int find_cached_style(int cur,
+			     int flag)
+{
+    int s = s_alink;
+
+#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
+    if (textfields_need_activation
+	&& links[cur].type == WWW_FORM_LINK_TYPE
+	&& F_TEXTLIKE(links[cur].l_form->type))
+	s = s_curedit;
+#endif
+
+    if (flag != ON) {
+	int x;
+
+	/*
+	 * This is where we try to restore the original style when a link is
+	 * unhighlighted.  The cached styles array saves the original style
+	 * just for this case.  If it doesn't have a color change saved at just
+	 * the right position, we look at preceding positions in the same line
+	 * until we find one.
+	 */
+	if (ValidCachedStyle(LYP, LXP)) {
+	    CTRACE2(TRACE_STYLE,
+		    (tfp, "STYLE.highlight.off: cached style @(%d,%d): ",
+		     LYP, LXP));
+	    s = (int) GetCachedStyle(LYP, LXP);
+	    if (s == 0) {
+		for (x = LXP - 1; x >= 0; x--) {
+		    s = (int) GetCachedStyle(LYP, x);
+		    if (s != 0) {
+			SetCachedStyle(LYP, LXP, (unsigned) s);
+			CTRACE2(TRACE_STYLE,
+				(tfp, "found %d, x_offset=%d.\n", s, x - LXP));
+			break;
+		    }
+		}
+		if (s == 0) {
+		    CTRACE2(TRACE_STYLE, (tfp, "not found, assume <a>.\n"));
+		    s = s_a;
+		}
+	    } else {
+		CTRACE2(TRACE_STYLE, (tfp, "found %d.\n", s));
+	    }
+	} else {
+	    CTRACE2(TRACE_STYLE,
+		    (tfp, "STYLE.highlight.off: can't use cache.\n"));
+	    s = s_a;
+	}
+    } else {
+	CTRACE2(TRACE_STYLE, (tfp, "STYLE.highlight.on: @(%d,%d).\n", LYP, LXP));
+    }
+    return s;
+}
+#endif /* USE_COLOR_STYLE */
+
+/*
+ * Highlight (or unhighlight) a given link.
+ */
+void LYhighlight(int flag,
+		 int cur,
+		 const char *target)
+{
+    char buffer[MAX_LINE];
+    int i;
+    int hi_count;
+    int hi_offset;
+    int title_adjust = (no_title ? -TITLE_LINES : 0);
+    char tmp[7];
+    const char *hi_string;
+
+#ifdef SHOW_WHEREIS_TARGETS
+    BOOL TargetEmphasisON = FALSE;
+    BOOL target1_drawn = NO;
+#endif
+    BOOL utf_flag = (BOOL) IS_UTF8_TTY;
+    BOOL hl1_drawn = NO;
+
+#ifdef USE_COLOR_STYLE
+    BOOL hl2_drawn = FALSE;	/* whether links[cur].l_hightext2 is already drawn
+
+				   properly */
+#endif
+    tmp[0] = tmp[1] = tmp[2] = '\0';
+
+    /*
+     * Bugs in the history code might cause -1 to be sent for cur, which yields
+     * a crash when LYstrncpy() is called with a nonsense pointer.  As far as I
+     * know, such bugs have been squashed, but if they should reappear, this
+     * works around them.  -FM
+     */
+    if (cur < 0) {
+	CTRACE((tfp, "LYhighlight cur %d (bug workaround)\n", cur));
+	cur = 0;
+    }
+
+    CTRACE((tfp, "LYhighlight at(%2d,%2d) %s %d [%d]:%s\n",
+	    links[cur].ly, links[cur].lx,
+	    (flag
+	     ? "on"
+	     : "off"),
+	    cur,
+	    links[cur].anchor_number,
+	    NONNULL(target)));
+
+#if defined(TEXTFIELDS_MAY_NEED_ACTIVATION) && defined(INACTIVE_INPUT_STYLE_VH)
+    if (flag == OFF)
+	textinput_redrawn = FALSE;
+#endif
+
+    if (nlinks > 0) {
+#ifdef USE_COLOR_STYLE
+	if (flag == ON || links[cur].type == WWW_FORM_LINK_TYPE) {
+	    LYmove(LYP + title_adjust, LXP);
+	    LynxChangeStyle(find_cached_style(cur, flag), STACK_ON);
+	}
+#else
+	if (links[cur].type == WWW_FORM_LINK_TYPE
+	    || LYGetHiliteStr(cur, 0) == NULL) {
+	    LYMoveToLink(cur, target, NULL,
+			 flag, links[cur].inUnderline, utf_flag);
+	    lynx_start_link_color(flag == ON, links[cur].inUnderline);
+	} else {
+	    LYMoveToLink(cur, target, LYGetHiliteStr(cur, 0),
+			 flag, links[cur].inUnderline, utf_flag);
+	    hl1_drawn = YES;
+#ifdef SHOW_WHEREIS_TARGETS
+	    target1_drawn = YES;
+#endif
+	}
+#endif
+
+	if (links[cur].type == WWW_FORM_LINK_TYPE) {
+	    int len;
+	    int avail_space = (LYcolLimit - LXP) + (LYcolLimit * (LYlines - LYP));
+	    const char *text = LYGetHiliteStr(cur, 0);
+
+	    if (text == 0)
+		text = "";
+
+	    if (avail_space > links[cur].l_form->size)
+		avail_space = links[cur].l_form->size;
+
+	    len = LYmbcs_skip_cells(text, avail_space, utf_flag) - text;
+	    LYwaddnstr(LYwin, text, (unsigned) len);
+	    while (len++ < avail_space)
+		LYaddch('_');
+
+#ifdef USE_COLOR_STYLE
+	} else if (flag == OFF) {
+	    hl2_drawn = TRUE;
+	    redraw_lines_of_link(cur);
+	    CTRACE2(TRACE_STYLE,
+		    (tfp, "STYLE.highlight.off: NOFIX branch @(%d,%d).\n",
+		     LYP, LXP));
+#endif
+	} else if (!hl1_drawn) {
+	    /*
+	     * Copy into the buffer only what will fit within the width of the
+	     * screen.
+	     */
+	    LYmbcsstrncpy(buffer,
+			  NonNull(LYGetHiliteStr(cur, 0)),
+			  (sizeof(buffer) - 1),
+			  (LYcolLimit - LXP),
+			  utf_flag);
+	    LYaddstr(buffer);
+	}
+
+	/*
+	 * Display a second line as well.
+	 */
+#ifdef USE_COLOR_STYLE
+	if (hl2_drawn == FALSE)
+#endif
+	{
+	    for (hi_count = 1;
+		 (hi_string = LYGetHiliteStr(cur, hi_count)) != NULL
+		 && LYP + hi_count <= display_lines;
+		 ++hi_count) {
+		int row = LYP + hi_count + title_adjust;
+
+		hi_offset = LYGetHilitePos(cur, hi_count);
+		lynx_stop_link_color(flag == ON, links[cur].inUnderline);
+		LYmove(row, hi_offset);
+
+#ifdef USE_COLOR_STYLE
+		CTRACE2(TRACE_STYLE,
+			(tfp, "STYLE.highlight.line2: @(%d,%d), style=%d.\n",
+			 row, hi_offset,
+			 flag == ON ? s_alink : s_a));
+		LynxChangeStyle(flag == ON ? s_alink : s_a, ABS_ON);
+#else
+		lynx_start_link_color(flag == ON, links[cur].inUnderline);
+#endif
+
+		for (i = 0; (tmp[0] = hi_string[i]) != '\0'
+		     && (i + hi_offset) < LYcols; i++) {
+		    if (!IsSpecialAttrChar(hi_string[i])) {
+			/*
+			 * For CJK strings, by Masanobu Kimura.
+			 */
+			if (IS_CJK_TTY && is8bits(tmp[0])) {
+			    tmp[1] = hi_string[++i];
+			    LYaddstr(tmp);
+			    tmp[1] = '\0';
+			} else {
+			    LYaddstr(tmp);
+			}
+		    }
+		}
+	    }
+	    lynx_stop_link_color(flag == ON, links[cur].inUnderline);
+	}
+#ifdef SHOW_WHEREIS_TARGETS
+	for (hi_count = target1_drawn ? 1 : 0;
+	     LYGetHiliteStr(cur, hi_count) != NULL;
+	     hi_count++) {
+	    TargetEmphasisON = show_whereis_targets(flag,
+						    cur,
+						    hi_count,
+						    target,
+						    TargetEmphasisON,
+						    utf_flag);
+	}
+
+	if (!LYShowCursor)
+	    /*
+	     * Get cursor out of the way.
+	     */
+	    LYHideCursor();
+	else
+#endif /* SHOW_WHEREIS_TARGETS */
+	    /*
+	     * Never hide the cursor if there's no FANCY CURSES or SLANG.
+	     */
+	    LYmove(LYP + title_adjust, ((LXP > 0) ? (LXP - 1) : 0));
+
+	if (flag)
+	    LYrefresh();
+    }
+    return;
+}
+
+/*
+ * free_and_clear will free a pointer if it is non-zero and then set it to
+ * zero.
+ */
+void free_and_clear(char **pointer)
+{
+    if (*pointer) {
+	FREE(*pointer);
+	*pointer = 0;
+    }
+    return;
+}
+
+/*
+ * Convert single or serial newlines to single spaces throughout a string
+ * (ignore newlines if the preceding character is a space) and convert tabs to
+ * single spaces.  Don't ignore any explicit tabs or spaces if the condense
+ * argument is FALSE, otherwise, condense any serial spaces or tabs to one
+ * space.  - FM
+ */
+void convert_to_spaces(char *string,
+		       BOOL condense)
+{
+    char *s = string;
+    char *ns;
+    BOOL last_is_space = FALSE;
+
+    if (!s)
+	return;
+
+    s = LYSkipNonBlanks(s);
+    ns = s;
+
+    while (*s) {
+	switch (*s) {
+	case ' ':
+	case '\t':
+	    if (!(condense && last_is_space))
+		*(ns++) = ' ';
+	    last_is_space = TRUE;
+	    break;
+
+	case '\r':
+	case '\n':
+	    if (!last_is_space) {
+		*(ns++) = ' ';
+		last_is_space = TRUE;
+	    }
+	    break;
+
+	default:
+	    *(ns++) = *s;
+	    last_is_space = FALSE;
+	    break;
+	}
+	s++;
+    }
+    *ns = '\0';
+    return;
+}
+
+/*
+ * Strip trailing slashes from directory paths.
+ */
+char *strip_trailing_slash(char *dirname)
+{
+    int i;
+
+    i = (int) strlen(dirname) - 1;
+    while (i >= 0 && dirname[i] == '/')
+	dirname[i--] = '\0';
+    return (dirname);
+}
+
+/*
+ * Remove most blanks, but restore one trailing blank to make prompts nicer.
+ */
+static void remove_most_blanks(char *buffer)
+{
+    int length = (int) strlen(buffer);
+    BOOL trailing = (BOOL) ((length != 0) && (buffer[length - 1] == ' '));
+
+    LYReduceBlanks(buffer);
+    if (trailing)
+	strcat(buffer, " ");
+}
+
+/*
+ * Display (or hide) the status line.
+ */
+BOOLEAN mustshow = FALSE;
+
+void statusline(const char *text)
+{
+    char buffer[MAX_LINE];
+    unsigned char *temp = NULL;
+    int max_length, len, i, j;
+    int at_lineno;
+    unsigned char k;
+    char *p;
+    char text_buff[MAX_LINE];
+
+    if (text == NULL)
+	return;
+
+    /*
+     * Don't print statusline messages if dumping to stdout.
+     */
+    if (dump_output_immediately)
+	return;
+
+    /*
+     * Don't print statusline message if turned off.
+     */
+    if (mustshow != TRUE) {
+	if (no_statusline == TRUE) {
+	    return;
+	}
+    }
+    mustshow = FALSE;
+
+    /* "LYNXDOWNLOAD://Method=-1/File=%s/SugFile=%s%s\">Save to disk</a>\n" */
+    LYstrncpy(text_buff, text, sizeof(text_buff) - 1);
+    p = strchr(text_buff, '\n');
+    if (p)
+	*p = '\0';
+
+    /*
+     * Deal with any CJK escape sequences and Kanji if we have a CJK character
+     * set selected, otherwise, strip any escapes.  Also, make sure text is not
+     * longer than the statusline window.  - FM
+     */
+    max_length = (((LYcolLimit - 1) < (int) sizeof(buffer))
+		  ? (LYcolLimit - 1)
+		  : (int) sizeof(buffer) - 1);
+    if ((text_buff[0] != '\0') &&
+	(LYHaveCJKCharacterSet)) {
+	/*
+	 * Translate or filter any escape sequences.  - FM
+	 */
+	if ((temp = typecallocn(unsigned char, strlen(text_buff) + 1)) == NULL)
+	      outofmem(__FILE__, "statusline");
+
+	assert(temp != NULL);
+
+	if (kanji_code == EUC) {
+	    TO_EUC((const unsigned char *) text_buff, temp);
+	} else if (kanji_code == SJIS) {
+#ifdef KANJI_CODE_OVERRIDE
+	    if (!LYRawMode || last_kcode == SJIS)
+		strcpy(temp, text_buff);
+	    else
+		TO_SJIS((const unsigned char *) text_buff, temp);
+#else
+	    strcpy((char *) temp, text_buff);
+#endif
+	} else {
+	    for (i = 0, j = 0; text_buff[i]; i++) {
+		if (text_buff[i] != CH_ESC) {	/* S/390 -- gil -- 2119 */
+		    temp[j++] = UCH(text_buff[i]);
+		}
+	    }
+	    temp[j] = '\0';
+	}
+
+	/*
+	 * Deal with any newlines or tabs in the string.  - FM
+	 */
+	remove_most_blanks((char *) temp);
+
+	/*
+	 * Handle the Kanji, making sure the text is not longer than the
+	 * statusline window.  - FM
+	 */
+	for (i = 0, j = 0, len = 0, k = '\0';
+	     temp[i] != '\0' && len < max_length; i++) {
+	    if (k != '\0') {
+		buffer[j++] = (char) k;
+		buffer[j++] = (char) temp[i];
+		k = '\0';
+		len += 2;
+	    } else if ((temp[i] & 0200) != 0) {
+		k = temp[i];
+	    } else {
+		buffer[j++] = (char) temp[i];
+		len++;
+	    }
+	}
+	buffer[j] = '\0';
+	FREE(temp);
+    } else {
+	/*
+	 * Deal with any newlines or tabs in the string.  - FM
+	 */
+	remove_most_blanks(text_buff);
+#ifdef WIDEC_CURSES
+	len = strlen(text_buff);
+	if (len >= (int) (sizeof(buffer) - 1))
+	    len = (int) (sizeof(buffer) - 1);
+	strncpy(buffer, text_buff, len)[len] = '\0';
+	/* FIXME: a binary search might be faster */
+	while (len > 0 && LYstrExtent(buffer, len, len) > max_length)
+	    buffer[--len] = '\0';
+#else
+	/*
+	 * Strip any escapes, and shorten text if necessary.  Note that we
+	 * don't deal with the possibility of UTF-8 characters in the string. 
+	 * This is unlikely, but if strings with such characters are used in
+	 * LYMessages_en.h, a compilation symbol of HAVE_UTF8_STATUSLINES could
+	 * be added there, and code added here for determining the displayed
+	 * string length, as we do above for CJK.  - FM
+	 */
+	for (i = 0, len = 0; text_buff[i] != '\0' && len < max_length; i++) {
+	    if (text_buff[i] != CH_ESC) {	/* S/390 -- gil -- 2119 */
+		buffer[len++] = text_buff[i];
+	    }
+	}
+	buffer[len] = '\0';
+#endif
+    }
+
+    /*
+     * Move to the desired statusline window and output the text highlighted. 
+     * - FM
+     */
+    if (LYStatusLine >= 0) {
+	if (LYStatusLine < LYlines - 1) {
+	    at_lineno = LYStatusLine;
+	} else {
+	    at_lineno = LYlines - 1;
+	}
+    } else if (user_mode == NOVICE_MODE) {
+	at_lineno = LYlines - 3;
+    } else {
+	at_lineno = LYlines - 1;
+    }
+    LYmove(at_lineno, 0);
+    LYclrtoeol();
+
+    if (buffer[0] != '\0') {
+	BOOLEAN has_CJK = FALSE;
+
+	if (IS_CJK_TTY) {
+	    for (i = 0; buffer[i] != '\0'; i++) {
+		if (buffer[i] & 0x80) {
+		    has_CJK = TRUE;
+		    break;
+		}
+	    }
+	}
+
+	if (has_CJK
+#ifdef HAVE_UTF8_STATUSLINES
+	    || IS_UTF8_TTY
+#endif
+	    ) {
+	    LYrefresh();
+	}
+#ifndef USE_COLOR_STYLE
+	lynx_start_status_color();
+	LYaddstr(buffer);
+	lynx_stop_status_color();
+#else
+	/* draw the status bar in the STATUS style */
+	{
+	    int y, x;
+	    int a = ((strncmp(buffer, ALERT_FORMAT, ALERT_PREFIX_LEN)
+		      || !hashStyles[s_alert].name)
+		     ? s_status
+		     : s_alert);
+
+	    LynxChangeStyle(a, STACK_ON);
+	    LYaddstr(buffer);
+	    wbkgdset(LYwin,
+		     ((lynx_has_color && LYShowColor >= SHOW_COLOR_ON)
+		      ? (chtype) hashStyles[a].color
+		      : A_NORMAL) | ' ');
+	    LYGetYX(y, x);
+	    if (y == at_lineno) {
+		LYclrtoeol();
+	    }
+	    if (!(lynx_has_color && LYShowColor >= SHOW_COLOR_ON))
+		wbkgdset(LYwin, A_NORMAL | ' ');
+	    else if (s_normal != NOSTYLE)
+		wbkgdset(LYwin, (chtype) (hashStyles[s_normal].color | ' '));
+	    else
+		wbkgdset(LYwin, (chtype) (displayStyles[DSTYLE_NORMAL].color | ' '));
+	    LynxChangeStyle(a, STACK_OFF);
+	}
+#endif
+    }
+    LYrefresh();
+
+    return;
+}
+
+static const char *novice_lines(int lineno)
+{
+    switch (lineno) {
+    case 0:
+	return NOVICE_LINE_TWO_A;
+    case 1:
+	return NOVICE_LINE_TWO_B;
+    case 2:
+	return NOVICE_LINE_TWO_C;
+    default:
+	return "";
+    }
+}
+
+static int lineno = 0;
+
+void toggle_novice_line(void)
+{
+    lineno++;
+    if (*novice_lines(lineno) == '\0')
+	lineno = 0;
+    return;
+}
+
+void noviceline(int more_flag GCC_UNUSED)
+{
+    if (dump_output_immediately)
+	return;
+
+    LYmove(LYlines - 2, 0);
+    LYclrtoeol();
+    LYaddstr(NOVICE_LINE_ONE);
+
+    LYmove(LYlines - 1, 0);
+    LYclrtoeol();
+#if defined(DIRED_SUPPORT ) && defined(OK_OVERRIDE)
+    if (lynx_edit_mode && !no_dired_support)
+	LYaddstr(DIRED_NOVICELINE);
+    else
+#endif /* DIRED_SUPPORT && OK_OVERRIDE */
+
+    if (LYUseNoviceLineTwo)
+	LYaddstr(NOVICE_LINE_TWO);
+    else
+	LYaddstr(novice_lines(lineno));
+
+    LYrefresh();
+    return;
+}
+
+#if defined(MISC_EXP) || defined(TTY_DEVICE) || defined(HAVE_TTYNAME)
+/*
+ * If the standard input is not a tty, and Lynx is really reading from the
+ * standard input, attempt to reopen it, pointing to a real tty.  Normally
+ * this would happen if the user pipes data to Lynx and wants to run
+ * interactively after that.
+ *
+ * Returns:
+ *     1  if successfully reopened
+ *    -1  if we cannot reopen
+ *     0  if we do not have to reopen
+ */
+int LYReopenInput(void)
+{
+    int result = 0;
+    int fd;
+
+    if ((fd = fileno(stdin)) == 0
+	&& !isatty(fd)
+	&& LYConsoleInputFD(FALSE) == fd) {
+	char *term_name = NULL;
+	int new_fd = -1;
+
+#ifdef HAVE_TTYNAME
+	if (isatty(fileno(stdout)) &&
+	    (term_name = ttyname(fileno(stdout))) != NULL)
+	    new_fd = open(term_name, O_RDONLY);
+
+	if (new_fd == -1 &&
+	    isatty(fileno(stderr)) &&
+	    (term_name = ttyname(fileno(stderr))) != NULL)
+	    new_fd = open(term_name, O_RDONLY);
+#endif
+
+#ifdef HAVE_CTERMID
+	if (new_fd == -1 &&
+	    (term_name = ctermid(NULL)) != NULL)
+	    new_fd = open(term_name, O_RDONLY);
+#endif
+
+#ifdef TTY_DEVICE
+	if (new_fd == -1)
+	    new_fd = open(term_name = TTY_DEVICE, O_RDONLY);
+#endif
+
+	CTRACE((tfp, "LYReopenInput open(%s) returned %d.\n", term_name, new_fd));
+	if (new_fd >= 0) {
+	    FILE *frp;
+
+	    close(new_fd);
+	    frp = freopen(term_name, "r", stdin);
+	    CTRACE((tfp,
+		    "LYReopenInput freopen(%s,\"r\",stdin) returned %p, stdin is now %p with fd %d.\n",
+		    term_name, (void *) frp, (void *) stdin, fileno(stdin)));
+	    result = 1;
+	} else {
+	    result = -1;
+	}
+    }
+    return result;
+}
+#endif
+
+#if defined(NSL_FORK) || defined(MISC_EXP) || defined (TTY_DEVICE) || defined(HAVE_TTYNAME)
+/*
+ * Returns the file descriptor from which keyboard input is expected, or INVSOC
+ * (-1) if not available.  If need_selectable is true, returns non-INVSOC fd
+ * only if select() is possible - actually, currently only checks if fd is
+ * connected to a tty.  - kw
+ */
+int LYConsoleInputFD(BOOLEAN need_selectable)
+{
+    int fd = INVSOC;
+
+#ifdef USE_SLANG
+    if (!LYCursesON)
+	fd = fileno(stdin);
+#if ((SLANG_VERSION >= 9919) && defined(REAL_UNIX_SYSTEM) && !defined(__CYGWIN__))
+    /* SLang_TT_Read_FD introduced in slang 0.99.19, from its changelog: 
+     * SLang_TT_Read_FD variable is now available for unix.  This is the file
+     * descriptor used by SLang_getkey.  */
+    else
+	fd = SLang_TT_Read_FD;
+#endif /* SLANG_VERSION >= 9919 */
+#else /* !USE_SLANG */
+    fd = fileno(stdin);
+#endif /* !USE_SLANG */
+
+    if (need_selectable && fd != INVSOC) {
+	if (isatty(fd)) {
+	    return fd;
+	} else {
+	    return INVSOC;
+	}
+    }
+    return fd;
+}
+#endif /* NSL_FORK || MISC_EXP */
+
+static int fake_zap = 0;
+
+void LYFakeZap(BOOL set)
+{
+    if (set && fake_zap < 1) {
+	CTRACE((tfp, "\r *** Set simulated 'Z'"));
+	if (fake_zap)
+	    CTRACE((tfp, ", %d pending", fake_zap));
+	CTRACE((tfp, " ***\n"));
+	fake_zap++;
+    } else if (!set && fake_zap) {
+	CTRACE((tfp, "\r *** Unset simulated 'Z'"));
+	CTRACE((tfp, ", %d pending", fake_zap));
+	CTRACE((tfp, " ***\n"));
+	fake_zap = 0;
+    }
+
+}
+
+static int DontCheck(void)
+{
+    static long last;
+    long next;
+
+    /** Curses or slang setup was not invoked **/
+    if (dump_output_immediately)
+	return (TRUE);
+
+    if (LYHaveCmdScript())	/* we may be running from a script */
+	return (TRUE);
+
+#ifdef MISC_EXP
+    if (LYNoZapKey)
+	return (TRUE);
+#endif
+    /*
+     * Avoid checking interrupts more than one per second, since it is a slow
+     * and expensive operation - TD
+     */
+#ifdef HAVE_GETTIMEOFDAY
+#undef timezone			/* U/Win defines a conflicting macro */
+    {
+	struct timeval tv;
+
+	gettimeofday(&tv, (struct timezone *) 0);
+	next = tv.tv_usec / 100000L;	/* 0.1 seconds is a compromise */
+    }
+#else
+    next = time((time_t *) 0);
+#endif
+    if (next == last)
+	return (TRUE);
+
+    last = next;
+    return FALSE;
+}
+
+int HTCheckForInterrupt(void)
+{
+    int c;
+    int cmd;
+
+    if (fake_zap > 0) {
+	fake_zap--;
+	CTRACE((tfp, "\r *** Got simulated 'Z' ***\n"));
+	CTRACE_FLUSH(tfp);
+	CTRACE_SLEEP(AlertSecs);
+	return ((int) TRUE);
+    }
+
+    /** Curses or slang setup was not invoked **/
+    if (DontCheck())
+	return ((int) FALSE);
+
+#ifndef VMS			/* UNIX stuff: */
+
+#if !defined(_WINDOWS) || defined(__MINGW32__)
+
+    /*
+     * First, check if there is a character.
+     */
+#ifdef USE_SLANG
+    /** No keystroke was entered
+	Note that this isn't taking possible SOCKSification
+	and the socks_flag into account, and may fail on the
+	slang library's select() when SOCKSified. - FM **/
+#ifdef DJGPP_KEYHANDLER
+    if (0 == _bios_keybrd(_NKEYBRD_READY))
+	return (FALSE);
+#else
+    if (0 == SLang_input_pending(0))
+	return (FALSE);
+#endif /* DJGPP_KEYHANDLER */
+
+#else /* Unix curses: */
+    {
+	struct timeval socket_timeout;
+	int ret = 0;
+	fd_set readfds;
+
+	socket_timeout.tv_sec = 0;
+	socket_timeout.tv_usec = 0;
+	FD_ZERO(&readfds);
+	FD_SET(0, &readfds);
+#ifdef SOCKS
+	if (socks_flag)
+	    ret = Rselect(1, &readfds, NULL, NULL, &socket_timeout);
+	else
+#endif /* SOCKS */
+	    ret = select(1, &readfds, NULL, NULL, &socket_timeout);
+
+	/** Suspended? **/
+	if ((ret == -1) && (SOCKET_ERRNO == EINTR))
+	    return ((int) FALSE);
+
+	/** No keystroke was entered? **/
+	if (!FD_ISSET(0, &readfds))
+	    return ((int) FALSE);
+    }
+#endif /* USE_SLANG */
+
+#endif /* !_WINDOWS */
+
+    /*
+     * Now, read the character.
+     */
+#if defined(USE_CURSES_NODELAY)
+    nodelay(LYwin, TRUE);
+    c = LYgetch();
+    nodelay(LYwin, FALSE);
+#elif defined(USE_SLANG) && defined(_WINDOWS)
+    if (!SLang_input_pending(0))
+	return ((int) FALSE);
+    c = LYgetch();
+#else
+    c = LYgetch();
+#endif
+
+#else /* VMS: */
+    extern int typeahead(void);
+
+    /** Control-C or Control-Y and a 'N'o reply to exit query **/
+    if (HadVMSInterrupt) {
+	HadVMSInterrupt = FALSE;
+	return ((int) TRUE);
+    }
+
+    c = typeahead();
+
+#endif /* !VMS */
+
+    /*
+     * 'c' contains whatever character we're able to read from keyboard
+     */
+
+    /** Keyboard 'Z' or 'z', or Control-G or Control-C **/
+    if (LYCharIsINTERRUPT(c))
+	return ((int) TRUE);
+
+    /* There is a subset of mainloop() actions available at this stage:  no new
+     * getfile() cycle is possible until the previous finished.  Currently we
+     * have scrolling in partial mode, toggling of trace log, and pasting. 
+     * User search now in progress...
+     */
+    cmd = (LKC_TO_LAC(keymap, c));
+    switch (cmd) {
+    case LYK_TRACE_TOGGLE:	/*  Toggle TRACE mode. */
+	handle_LYK_TRACE_TOGGLE();
+	break;
+#ifdef CAN_CUT_AND_PASTE
+    case LYK_TO_CLIPBOARD:{	/* ^S */
+	    const char *s = LYDownLoadAddress();
+
+	    if (!s || !*s || put_clip(s))
+		HTInfoMsg(gettext("Copy to clipboard failed."));
+	    else
+		HTInfoMsg(gettext("Download document URL put to clipboard."));
+	    break;
+	}
+#endif /* defined CAN_CUT_AND_PASTE */
+    default:
+#ifdef DISP_PARTIAL
+	/* OK, we got several lines from new document and want to scroll... */
+	if (display_partial && (NumOfLines_partial > 2)) {
+	    BOOLEAN do_refresh;
+	    int res;
+	    int Newline_partial = LYGetNewline();
+
+	    switch (cmd) {
+	    case LYK_WHEREIS:	/* search within the document */
+	    case LYK_NEXT:	/* search for the next occurrence in the document */
+	    case LYK_PREV:	/* search for the previous occurrence in the document */
+		handle_LYK_WHEREIS(cmd, &do_refresh);
+		if (www_search_result != -1) {
+		    Newline_partial = www_search_result;
+		    www_search_result = -1;	/* reset */
+		}
+		break;
+
+	    case LYK_FASTBACKW_LINK:
+		if (Newline_partial <= (display_lines) + 1) {
+		    Newline_partial -= display_lines;
+		} else if ((res =
+			    HTGetLinkOrFieldStart(-1,
+						  &Newline_partial, NULL,
+						  -1, TRUE)) == LINK_LINE_FOUND) {
+		    Newline_partial++;
+		} else if (res == LINK_DO_ARROWUP) {
+		    Newline_partial -= display_lines;
+		}
+		break;
+	    case LYK_FASTFORW_LINK:
+		if (HText_canScrollDown()) {
+		    /* This is not an exact science... - kw */
+		    if (HTGetLinkOrFieldStart(HText_LinksInLines(HTMainText,
+								 Newline_partial,
+								 display_lines)
+					      - 1,
+					      &Newline_partial, NULL,
+					      1, TRUE) == LINK_LINE_FOUND) {
+			Newline_partial++;
+		    }
+		}
+		break;
+	    case LYK_PREV_PAGE:
+		if (Newline_partial > 1)
+		    Newline_partial -= display_lines;
+		break;
+	    case LYK_NEXT_PAGE:
+		if (HText_canScrollDown())
+		    Newline_partial += display_lines;
+		break;
+	    case LYK_UP_HALF:
+		if (Newline_partial > 1)
+		    Newline_partial -= (display_lines / 2);
+		break;
+	    case LYK_DOWN_HALF:
+		if (HText_canScrollDown())
+		    Newline_partial += (display_lines / 2);
+		break;
+	    case LYK_UP_TWO:
+		if (Newline_partial > 1)
+		    Newline_partial -= 2;
+		break;
+	    case LYK_DOWN_TWO:
+		if (HText_canScrollDown())
+		    Newline_partial += 2;
+		break;
+	    case LYK_HOME:
+		if (Newline_partial > 1)
+		    Newline_partial = 1;
+		break;
+	    case LYK_END:
+		if (HText_canScrollDown())
+		    Newline_partial = HText_getNumOfLines() - display_lines + 1;
+		/* calculate for "current" bottom value */
+		break;
+	    case LYK_REFRESH:
+		break;
+	    default:
+		/** Other or no keystrokes **/
+		return ((int) FALSE);
+	    }			/* end switch */
+	    if (Newline_partial < 1)
+		Newline_partial = 1;
+	    if (LYMainLoop_pageDisplay(Newline_partial))
+		NumOfLines_partial = HText_getNumOfLines();
+	}
+#endif /* DISP_PARTIAL */
+	break;
+    }				/* end switch */
+    /** Other or no keystrokes **/
+    return ((int) FALSE);
+}
+
+/*
+ * Check if the given filename looks like it's an absolute pathname, i.e.,
+ * references a directory.
+ */
+BOOLEAN LYisAbsPath(const char *path)
+{
+    BOOLEAN result = FALSE;
+
+    if (non_empty(path)) {
+#ifdef VMS
+	result = TRUE;
+#else
+#if defined(USE_DOS_DRIVES)
+	result = (BOOLEAN) (LYIsPathSep(path[0])
+			    || (LYIsDosDrive(path)
+				&& LYIsPathSep(path[2])));
+#else
+	result = (BOOLEAN) (LYIsPathSep(path[0]));
+#endif /* USE_DOS_DRIVES */
+#endif
+    }
+    return result;
+}
+
+/*
+ * Check if the given filename is the root path, e.g., "/" on Unix.
+ */
+BOOLEAN LYisRootPath(const char *path)
+{
+#if defined(USE_DOS_DRIVES)
+    if (strlen(path) == 3
+	&& LYIsDosDrive(path)
+	&& LYIsPathSep(path[2]))
+	return TRUE;
+#endif
+    return (BOOL) ((strlen(path) == 1) && LYIsPathSep(path[0]));
+}
+
+/*
+ * A file URL for a remote host is an obsolete ftp URL.
+ * Return YES only if we're certain it's a local file.  - FM
+ */
+BOOLEAN LYisLocalFile(const char *filename)
+{
+    char *host = NULL;
+    char *acc_method = NULL;
+    char *cp;
+
+    if (!filename)
+	return NO;
+    if (!(host = HTParse(filename, "", PARSE_HOST)))
+	return NO;
+    if (!*host) {
+	FREE(host);
+	return NO;
+    }
+
+    if ((cp = strchr(host, ':')) != NULL)
+	*cp = '\0';
+
+    if ((acc_method = HTParse(filename, "", PARSE_ACCESS))) {
+	if (0 == strcmp("file", acc_method) &&
+	    (0 == strcmp(host, "localhost") ||
+	     LYSameFilename(host, HTHostName()))) {
+	    FREE(host);
+	    FREE(acc_method);
+	    return YES;
+	}
+    }
+
+    FREE(host);
+    FREE(acc_method);
+    return NO;
+}
+
+/*
+ * Utility for checking URLs with a host field.  Return YES only if we're
+ * certain it's the local host.  - FM
+ */
+BOOLEAN LYisLocalHost(const char *filename)
+{
+    char *host = NULL;
+    char *cp;
+
+    if (!filename)
+	return NO;
+    if (!(host = HTParse(filename, "", PARSE_HOST)))
+	return NO;
+    if (!*host) {
+	FREE(host);
+	return NO;
+    }
+
+    if ((cp = strchr(host, ':')) != NULL)
+	*cp = '\0';
+
+    if ((LYSameFilename(host, "localhost") ||
+	 LYSameFilename(host, LYHostName) ||
+	 LYSameFilename(host, HTHostName()))) {
+	FREE(host);
+	return YES;
+    }
+
+    FREE(host);
+    return NO;
+}
+
+/*
+ * Free an HTList that contains strings.
+ */
+void LYFreeStringList(HTList *list)
+{
+    if (list != NULL) {
+	char *argument;
+	HTList *cur = list;
+
+	while (NULL != (argument = (char *) HTList_nextObject(cur))) {
+	    FREE(argument);
+	}
+	HTList_delete(list);
+    }
+}
+
+/*
+ * Utility for freeing the list of local host aliases.  - FM
+ */
+void LYLocalhostAliases_free(void)
+{
+    LYFreeStringList(localhost_aliases);
+    localhost_aliases = NULL;
+}
+
+/*
+ * Utility for listing hosts to be treated as local aliases.  - FM
+ */
+void LYAddLocalhostAlias(char *alias)
+{
+    char *LocalAlias = NULL;
+
+    if (!non_empty(alias))
+	return;
+
+    if (!localhost_aliases) {
+	localhost_aliases = HTList_new();
+#ifdef LY_FIND_LEAKS
+	atexit(LYLocalhostAliases_free);
+#endif
+    }
+
+    StrAllocCopy(LocalAlias, alias);
+    HTList_addObject(localhost_aliases, LocalAlias);
+
+    return;
+}
+
+/*
+ * Utility for checking URLs with a host field.  Return YES only if we've
+ * listed the host as a local alias.  - FM
+ */
+BOOLEAN LYisLocalAlias(const char *filename)
+{
+    char *host = NULL;
+    char *alias;
+    char *cp;
+    HTList *cur = localhost_aliases;
+
+    if (!cur || !filename)
+	return NO;
+    if (!(host = HTParse(filename, "", PARSE_HOST)))
+	return NO;
+    if (!(*host)) {
+	FREE(host);
+	return NO;
+    }
+
+    if ((cp = strchr(host, ':')) != NULL)
+	*cp = '\0';
+
+    while (NULL != (alias = (char *) HTList_nextObject(cur))) {
+	if (LYSameFilename(host, alias)) {
+	    FREE(host);
+	    return YES;
+	}
+    }
+
+    FREE(host);
+    return NO;
+}
+
+/*
+ *  This function checks for a URL with an unknown scheme,
+ *  but for which proxying has been set up, and if so,
+ *  returns PROXY_URL_TYPE. - FM
+ *
+ *  If a colon is present but the string segment which
+ *  precedes it is not being proxied, and we can be sure
+ *  that what follows the colon is not a port field,
+ *  it returns UNKNOWN_URL_TYPE.  Otherwise, it returns
+ *  0 (not a URL). - FM
+ */
+UrlTypes LYCheckForProxyURL(char *filename)
+{
+    char *cp = filename;
+    char *cp1;
+    char *cp2 = NULL;
+
+    /*
+     * Don't crash on an empty argument.
+     */
+    if (isEmpty(cp))
+	return (NOT_A_URL_TYPE);
+
+    /* kill beginning spaces */
+    cp = LYSkipBlanks(cp);
+
+    /*
+     * Check for a colon, and if present,
+     * see if we have proxying set up.
+     */
+    if ((cp1 = strchr((cp + 1), ':')) != NULL) {
+	if ((cp2 = strchr((cp + 1), '/')) != NULL && cp2 < cp1)
+	    return (NOT_A_URL_TYPE);
+	*cp1 = '\0';
+	cp2 = NULL;
+	StrAllocCopy(cp2, cp);
+	*cp1 = ':';
+	StrAllocCat(cp2, "_proxy");
+	if (LYGetEnv(cp2) != NULL) {
+	    FREE(cp2);
+	    return (PROXY_URL_TYPE);
+	}
+	FREE(cp2);
+#if defined (USE_DOS_DRIVES)
+	if (LYIsDosDrive(cp))
+	    return (NOT_A_URL_TYPE);
+#endif
+	cp1++;
+	if (!*cp) {
+	    return (NOT_A_URL_TYPE);
+	} else if (isdigit(UCH(*cp1))) {
+	    while (*cp1 && isdigit(UCH(*cp1)))
+		cp1++;
+	    if (*cp1 && !LYIsHtmlSep(*cp1))
+		return (UNKNOWN_URL_TYPE);
+	} else {
+	    return (UNKNOWN_URL_TYPE);
+	}
+    }
+
+    return (NOT_A_URL_TYPE);
+}
+
+/*
+ * Compare a "type:" string, replacing it by the comparison-string if it
+ * matches (and return true in that case).
+ */
+static BOOLEAN compare_type(char *tst,
+			    const char *cmp,
+			    size_t len)
+{
+    if (!strncasecomp(tst, cmp, (int) len)) {
+	if (strncmp(tst, cmp, len)) {
+	    size_t i;
+
+	    for (i = 0; i < len; i++)
+		tst[i] = cmp[i];
+	}
+	return TRUE;
+    }
+    return FALSE;
+}
+
+#define DoubleHtmlSep(s) (LYIsHtmlSep((s)[0]) && LYIsHtmlSep((s)[1]))
+#define compare_two(tst,cmp,len,limit) \
+	((len + 2) <= limit \
+	&& DoubleHtmlSep(tst + len) \
+	&& compare_type(tst, cmp, len))
+
+/*
+ *  Must recognize a URL and return the type.
+ *  If recognized, based on a case-insensitive
+ *  analysis of the scheme field, ensures that
+ *  the scheme field has the expected case.
+ *
+ *  Returns 0 (not a URL) for a NULL argument,
+ *  one which lacks a colon.
+ *
+ *  Chains to LYCheckForProxyURL() if a colon
+ *  is present but the type is not recognized.
+ */
+UrlTypes is_url(char *filename)
+{
+    char *cp = filename;
+    char *cp1;
+    UrlTypes result = NOT_A_URL_TYPE;
+    int limit;
+
+    /*
+     * Don't crash on an empty argument.
+     */
+    if (isEmpty(cp))
+	return (result);
+
+    /*
+     * Can't be a URL if it lacks a colon and if it starts with '[' it's
+     * probably IPv6 adress.
+     */
+    if (NULL == strchr(cp, ':') || cp[0] == '[')
+	return (result);
+
+    /*
+     * Kill beginning spaces.
+     */
+    cp = LYSkipBlanks(cp);
+
+    /*
+     * Can't be a URL if it starts with a slash.  So return immediately for
+     * this common case, also to avoid false positives if there was a colon
+     * later in the string.  Also can't be a URL if it starts with a colon.  -
+     * KW
+     */
+    if (*cp == ':' || LYIsHtmlSep(*cp)) {
+	result = NOT_A_URL_TYPE;
+
+    } else {
+	limit = (int) strlen(cp);
+	switch (*cp) {
+	case 'L':
+	case 'l':
+	    /*
+	     * Lynx internal pages ("LYNXfoo:" or "lynxfoo:") start with 'l' or
+	     * 'L', other URLs aren't.
+	     */
+	    if (compare_type(cp, STR_LYNXEXEC, LEN_LYNXEXEC)) {
+		/*
+		 * Special External Lynx type to handle execution of commands
+		 * or scripts which require a pause to read the screen upon
+		 * completion.
+		 */
+		result = LYNXEXEC_URL_TYPE;
+
+	    } else if (compare_type(cp, STR_LYNXPROG, LEN_LYNXPROG)) {
+		/*
+		 * Special External Lynx type to handle execution of commands,
+		 * scripts or programs with do not require a pause to read
+		 * screen upon completion.
+		 */
+		result = LYNXPROG_URL_TYPE;
+
+	    } else if (compare_type(cp, STR_LYNXCGI, LEN_LYNXCGI)) {
+		/*
+		 * Special External Lynx type to handle cgi scripts.
+		 */
+		result = LYNXCGI_URL_TYPE;
+
+	    } else if (compare_type(cp, STR_LYNXPRINT, LEN_LYNXPRINT)) {
+		/*
+		 * Special Internal Lynx type.
+		 */
+		result = LYNXPRINT_URL_TYPE;
+
+	    } else if (compare_type(cp, STR_LYNXOPTIONS, LEN_LYNXOPTIONS)) {
+		/*
+		 * Special Internal Lynx type.
+		 */
+		result = LYNXOPTIONS_URL_TYPE;
+
+	    } else if (compare_type(cp, STR_LYNXCFG, LEN_LYNXCFG)) {
+		/*
+		 * Special Internal Lynx type.
+		 */
+		result = LYNXCFG_URL_TYPE;
+
+	    } else if (compare_type(cp, STR_LYNXMESSAGES, LEN_LYNXMESSAGES)) {
+		/*
+		 * Special Internal Lynx type.
+		 */
+		result = LYNXMESSAGES_URL_TYPE;
+
+	    } else if (compare_type(cp, STR_LYNXCFLAGS, LEN_LYNXCFLAGS)) {
+		/*
+		 * Special Internal Lynx type.
+		 */
+		result = LYNXCOMPILE_OPTS_URL_TYPE;
+
+	    } else if (compare_type(cp, STR_LYNXDOWNLOAD, LEN_LYNXDOWNLOAD)) {
+		/*
+		 * Special Internal Lynx type.
+		 */
+		result = LYNXDOWNLOAD_URL_TYPE;
+
+	    } else if (compare_type(cp, STR_LYNXDIRED, LEN_LYNXDIRED)) {
+		/*
+		 * Special Internal Lynx type.
+		 */
+		result = LYNXDIRED_URL_TYPE;
+
+	    } else if (compare_type(cp, STR_LYNXHIST, LEN_LYNXHIST)) {
+		/*
+		 * Special Internal Lynx type.
+		 */
+		result = LYNXHIST_URL_TYPE;
+
+#ifdef USE_CACHEJAR
+	    } else if (compare_type(cp, STR_LYNXCACHE, LEN_LYNXCACHE)) {
+		/* 
+		 * Special Internal Lynx type.
+		 */
+		result = LYNXCACHE_URL_TYPE;
+#endif
+
+	    } else if (compare_type(cp, STR_LYNXKEYMAP, LEN_LYNXKEYMAP)) {
+		/*
+		 * Special Internal Lynx type.
+		 */
+		result = LYNXKEYMAP_URL_TYPE;
+
+	    } else if (compare_type(cp, STR_LYNXIMGMAP, LEN_LYNXIMGMAP)) {
+		/*
+		 * Special Internal Lynx type.
+		 */
+		/* force lower/uppercase of next part */
+		(void) is_url(&cp[LEN_LYNXIMGMAP]);
+		result = LYNXIMGMAP_URL_TYPE;
+
+	    } else if (compare_type(cp, STR_LYNXCOOKIE, LEN_LYNXCOOKIE)) {
+		/*
+		 * Special Internal Lynx type.
+		 */
+		result = LYNXCOOKIE_URL_TYPE;
+	    }
+	    break;
+#ifndef DISABLE_NEWS
+	    /*
+	     * NEWSfoo:  schemes -
+	     */
+	case 'N':
+	case 'n':
+	    if (compare_type(cp, STR_NEWS_URL, LEN_NEWS_URL)) {
+		result = NEWS_URL_TYPE;
+
+	    } else if (compare_type(cp, STR_NNTP_URL, LEN_NNTP_URL)) {
+		result = NNTP_URL_TYPE;
+
+	    } else if (compare_type(cp, "newspost:", 9)) {
+		/*
+		 * Special Lynx type to handle news posts.
+		 */
+		result = NEWSPOST_URL_TYPE;
+
+	    } else if (compare_type(cp, "newsreply:", 10)) {
+		/*
+		 * Special Lynx type to handle news replies (followups).
+		 */
+		result = NEWSREPLY_URL_TYPE;
+	    }
+	    break;
+
+	    /*
+	     * SNEWSfoo:  schemes -
+	     */
+	case 'S':
+	case 's':
+	    if (compare_type(cp, STR_SNEWS_URL, LEN_SNEWS_URL)) {
+		result = SNEWS_URL_TYPE;
+
+	    } else if (compare_type(cp, "snewspost:", 10)) {
+		/*
+		 * Special Lynx type to handle snews posts.
+		 */
+		result = NEWSPOST_URL_TYPE;
+
+	    } else if (compare_type(cp, "snewsreply:", 11)) {
+		/*
+		 * Special Lynx type to handle snews replies (followups).
+		 */
+		result = NEWSREPLY_URL_TYPE;
+	    }
+	    break;
+#endif
+	case 'M':
+	case 'm':
+	    if (compare_type(cp, STR_MAILTO_URL, LEN_MAILTO_URL)) {
+		result = MAILTO_URL_TYPE;
+	    }
+	    break;
+
+	case 'F':
+	case 'f':
+	    if (compare_type(cp, STR_FILE_URL, LEN_FILE_URL)) {
+		if (LYisLocalFile(cp)) {
+		    result = FILE_URL_TYPE;
+		} else if (DoubleHtmlSep(cp + LEN_FILE_URL)) {
+		    result = FTP_URL_TYPE;
+		}
+	    }
+#ifndef DISABLE_FTP
+	    else if (compare_two(cp, STR_FTP_URL, LEN_FTP_URL, limit)) {
+		result = FTP_URL_TYPE;
+	    }
+#endif
+#ifndef DISABLE_FINGER
+	    else if (compare_two(cp, STR_FINGER_URL, LEN_FINGER_URL, limit)) {
+		result = FINGER_URL_TYPE;
+	    }
+#endif
+	    break;
+
+	case 'B':
+	case 'b':
+#ifndef DISABLE_BIBP
+	    if (compare_type(cp, STR_BIBP_URL, LEN_BIBP_URL)) {
+		result = BIBP_URL_TYPE;
+	    }
+#endif
+	    break;
+
+	case 'D':
+	case 'd':
+	    if (compare_type(cp, "data:", 5)) {
+		result = DATA_URL_TYPE;
+	    }
+	    break;
+
+	default:
+	    if (limit >= 3
+		&& ((cp1 = strchr(cp + 3, ':')) == NULL
+		    || !DoubleHtmlSep(cp1 + 1))) {
+		/*
+		 * If it doesn't contain "://", and it's not one of the the
+		 * above, it can't be a URL with a scheme we know, so check if
+		 * it's an unknown scheme for which proxying has been set up.
+		 * - FM
+		 */
+		if (cp1 != NULL
+		    && (cp1 - cp) > 1	/* exclude DOS-style device:/path */
+		    && LYisAbsPath(cp1 + 1)) {
+		    result = NCFTP_URL_TYPE;
+		}
+
+	    } else {
+		switch (*cp) {
+		case 'H':
+		case 'h':
+		    if (compare_type(cp, STR_HTTP_URL, LEN_HTTP_URL)) {
+			result = HTTP_URL_TYPE;
+
+		    } else if (compare_type(cp, STR_HTTPS_URL, LEN_HTTPS_URL)) {
+			result = HTTPS_URL_TYPE;
+		    }
+		    break;
+
+#ifndef DISABLE_GOPHER
+		case 'G':
+		case 'g':
+		    if (compare_type(cp, STR_GOPHER_URL, LEN_GOPHER_URL)) {
+			if (strlen(cp) >= 11
+			    && (cp1 = strchr(cp + 11, '/')) != NULL) {
+
+			    if (TOUPPER(*(cp1 + 1)) == 'H' || *(cp1 + 1) == 'w')
+				/* if this is a gopher html type */
+				result = HTML_GOPHER_URL_TYPE;
+			    else if (*(cp1 + 1) == 'T' || *(cp1 + 1) == '8')
+				result = TELNET_GOPHER_URL_TYPE;
+			    else if (*(cp1 + 1) == '7')
+				result = INDEX_GOPHER_URL_TYPE;
+			    else
+				result = GOPHER_URL_TYPE;
+			} else {
+			    result = GOPHER_URL_TYPE;
+			}
+		    }
+		    break;
+#endif
+		case 'W':
+		case 'w':
+		    if (compare_type(cp, STR_WAIS_URL, LEN_WAIS_URL)) {
+			result = WAIS_URL_TYPE;
+		    }
+		    break;
+
+		case 'T':
+		case 't':
+		    if (compare_type(cp, STR_TELNET_URL, LEN_TELNET_URL)) {
+			result = TELNET_URL_TYPE;
+
+		    } else if (compare_type(cp, STR_TN3270_URL, LEN_TN3270_URL)) {
+			result = TN3270_URL_TYPE;
+		    }
+		    break;
+
+		case 'R':
+		case 'r':
+		    if (compare_type(cp, STR_RLOGIN_URL, LEN_RLOGIN_URL)) {
+			result = RLOGIN_URL_TYPE;
+		    }
+		    break;
+
+		case 'C':
+		case 'c':
+		    if (compare_type(cp, STR_CSO_URL, LEN_CSO_URL)) {
+			result = CSO_URL_TYPE;
+		    }
+		    break;
+
+		case 'A':
+		case 'a':
+		    if (compare_type(cp, "afs:", 4)) {
+			result = AFS_URL_TYPE;
+		    }
+		    break;
+
+		case 'P':
+		case 'p':
+		    if (compare_type(cp, "prospero:", 9)) {
+			result = PROSPERO_URL_TYPE;
+		    }
+		    break;
+		}
+	    }
+	}
+	/*
+	 * Check if it is an unknown scheme for which proxying has been set up.
+	 */
+	if (result == NOT_A_URL_TYPE)
+	    result = LYCheckForProxyURL(filename);
+    }
+    return result;
+}
+
+/*
+ * Sometimes it is just expected that curses is on when an alert or other
+ * statusline message needs to be shown and we are not just dumping
+ * immediately.  Calling this will 'fix' it, but may not always be appropriate. 
+ * - kw
+ */
+void LYFixCursesOn(const char *reason)
+{
+    if (dump_output_immediately || LYCursesON)
+	return;
+    if (reason) {
+	CTRACE((tfp, "Forcing curses on to %s\n", reason));
+    }
+    start_curses();
+}
+
+/*
+ * Most protocol modules called through HTLoad* expect that curses is on unless
+ * dump_output_immediately is set, so that statusline messages can be shown. 
+ * Some protocols expect the opposite, namely telnet and friends.  This
+ * function should be called after the 'physical' URL for accessing addr has
+ * been established.  It does the right thing to the degree that curses is
+ * turned on for known problem cases.  In any normal circumstances this should
+ * never apply, but proxying or rule substitution is not prevented for
+ * telnet-like URLs, and this 'fix' avoids some crashes that can otherwise
+ * occur.  - kw
+ */
+BOOLEAN LYFixCursesOnForAccess(const char *addr,
+			       const char *physical)
+{
+    /*
+     * If curses is off when maybe it shouldn't...
+     */
+    if (!dump_output_immediately && !LYCursesON && physical) {
+	char *cp1;
+
+	/*
+	 * If requested resource wants to be accessed with curses off, and
+	 * getfile() would indeed have turned curses off for it...
+	 */
+	if (strstr(addr, "://") != NULL &&
+	    (isTELNET_URL(addr) ||
+	     isRLOGIN_URL(addr) ||
+	     isTN3270_URL(addr) ||
+	     (!isGOPHER_URL(addr) &&
+	      (cp1 = strchr(addr + 11, '/')) != NULL &&
+	      (*(cp1 + 1) == 'T' || *(cp1 + 1) == '8')))) {
+	    /*
+	     * If actual access that will be done is ok with curses off, then
+	     * do nothing special, else force curses on.  - kw
+	     */
+	    if (!isTELNET_URL(physical) &&
+		!isRLOGIN_URL(physical) &&
+		!isTN3270_URL(physical)) {
+		start_curses();
+		HTAlert(gettext("Unexpected access protocol for this URL scheme."));
+		return TRUE;
+	    }
+	}
+    }
+    return FALSE;
+}
+
+/*
+ * Determine whether we allow HEAD and related flags for a URL.  - kw
+ */
+BOOLEAN LYCanDoHEAD(const char *address)
+{
+    char *temp0 = NULL;
+    int isurl;
+
+    if (!non_empty(address))
+	return FALSE;
+    if (!strncmp(address, "http", 4))
+	return TRUE;
+    /* Make copy for is_url() since caller may not care for case changes */
+    StrAllocCopy(temp0, address);
+    isurl = is_url(temp0);
+    if (!isurl) {
+	FREE(temp0);
+	return FALSE;
+    }
+    if (isurl == LYNXCGI_URL_TYPE) {
+	FREE(temp0);
+#if defined(LYNXCGI_LINKS) && !defined(VMS)
+	return TRUE;
+#else
+	return FALSE;
+#endif
+    }
+    /*
+     * The idea of the following is to allow HEAD for news URLs that identify
+     * single articles, not those that identify ranges of articles or groups or
+     * a list of groups.  - kw
+     */
+    if (isurl == NEWS_URL_TYPE || isurl == NNTP_URL_TYPE) {
+	char *temp = HTParse(address, "", PARSE_PATH);
+	char *cp = strrchr(temp, '/');
+
+	if (strchr((cp ? cp : temp), '@') != NULL) {
+	    FREE(temp0);
+	    FREE(temp);
+	    return TRUE;
+	}
+	if (cp && isdigit(UCH(cp[1])) && strchr(cp, '-') == NULL) {
+	    FREE(temp0);
+	    FREE(temp);
+	    return TRUE;
+	}
+	FREE(temp);
+    }
+#define ALLOW_PROXY_HEAD
+/* If defined, also allow head requests for URLs proxied through the "http" or
+ * "lynxcgi" protocols, which understand HEAD.  Only the proxy environment
+ * variables are checked, not the HTRules system.  - kw
+ */
+#ifdef ALLOW_PROXY_HEAD
+    if (isurl != FILE_URL_TYPE) {
+	char *acc_method = HTParse(temp0, "", PARSE_ACCESS);
+
+	if (non_empty(acc_method)) {
+	    char *proxy;
+
+	    StrAllocCat(acc_method, "_proxy");
+	    proxy = LYGetEnv(acc_method);
+	    if (proxy && (isHTTP_URL(proxy) ||
+			  isLYNXCGI(proxy)) &&
+		!override_proxy(temp0)) {
+		FREE(temp0);
+		FREE(acc_method);
+		return TRUE;
+	    }
+	}
+	FREE(acc_method);
+    }
+#endif /* ALLOW_PROXY_HEAD */
+
+    FREE(temp0);
+    return FALSE;
+}
+
+/*
+ * Close an input file.
+ */
+BOOLEAN LYCloseInput(FILE *fp)
+{
+    if (fp != 0) {
+	int err = ferror(fp);
+
+	fclose(fp);
+	if (!err) {
+	    return TRUE;
+	}
+    }
+    return FALSE;
+}
+
+/*
+ * Close an output file, reporting any problems with writing to it.
+ */
+BOOLEAN LYCloseOutput(FILE *fp)
+{
+    if (fp != 0) {
+	int err = ferror(fp);
+
+	fclose(fp);
+	if (!err) {
+	    return TRUE;
+	}
+    }
+    HTAlert(CANNOT_WRITE_TO_FILE);
+    return FALSE;
+}
+
+/*
+ * Test if we'll be able to write a file.  If not, warn the user.
+ */
+BOOLEAN LYCanWriteFile(const char *filename)
+{
+    if (LYCloseOutput(fopen(filename, "w"))) {
+	remove(filename);
+	return TRUE;
+    } else {
+	_statusline(NEW_FILENAME_PROMPT);
+	return FALSE;
+    }
+}
+
+/*
+ * Test if we'll be able to read a file.
+ */
+BOOLEAN LYCanReadFile(const char *filename)
+{
+    FILE *fp;
+
+    if (non_empty(filename)) {
+	if ((fp = fopen(filename, "r")) != 0) {
+	    return LYCloseInput(fp);
+	}
+    }
+    return FALSE;
+}
+
+/*
+ * Remove backslashes from any string.
+ */
+void remove_backslashes(char *buf)
+{
+    char *cp;
+
+    for (cp = buf; *cp != '\0'; cp++) {
+
+	if (*cp != '\\') {	/* don't print slashes */
+	    *buf = *cp;
+	    buf++;
+	} else if (*cp == '\\' &&	/* print one slash if there */
+		   *(cp + 1) == '\\') {		/* are two in a row         */
+	    *buf = *cp;
+	    buf++;
+	}
+    }
+    *buf = '\0';
+    return;
+}
+
+/*
+ * Checks to see if the current process is attached via a terminal in the local
+ * domain.
+ */
+BOOLEAN inlocaldomain(void)
+{
+    BOOLEAN result = TRUE;
+
+#ifdef HAVE_UTMP
+    int n;
+    FILE *fp;
+    struct utmp me;
+    char *cp, *mytty = NULL;
+
+    if ((cp = ttyname(0)))
+	mytty = LYLastPathSep(cp);
+
+    result = FALSE;
+    if (mytty && (fp = fopen(UTMP_FILE, "r")) != NULL) {
+	mytty++;
+	do {
+	    n = (int) fread((char *) &me, sizeof(struct utmp), 1, fp);
+	} while (n > 0 && !STREQ(me.ut_line, mytty));
+	(void) LYCloseInput(fp);
+
+	if (n > 0) {
+	    if (strlen(me.ut_host) > strlen(LYLocalDomain) &&
+		STREQ(LYLocalDomain,
+		      me.ut_host + strlen(me.ut_host) - strlen(LYLocalDomain))) {
+		result = TRUE;
+	    }
+#ifdef LINUX
+	    /* Linux fix to check for local user. J.Cullen 11Jul94              */
+	    else if (strlen(me.ut_host) == 0) {
+		result = TRUE;
+	    }
+#endif /* LINUX */
+	}
+
+    } else {
+	CTRACE((tfp,
+		"Could not get ttyname (returned %s) or open UTMP file %s\n",
+		NONNULL(cp), UTMP_FILE));
+    }
+#else
+    CTRACE((tfp, "LYUtils: inlocaldomain() not supported.\n"));
+#endif /* HAVE_UTMP */
+    return (result);
+}
+
+#ifdef HAVE_SIGACTION
+/*
+ * An extended alternative for calling signal(), sets some flags for signal
+ * handler as we want them if that functionality is available.  (We don't
+ * return anything from this function since the return value would currently be
+ * ignored anyway.) - kw
+ */
+void LYExtSignal(int sig,
+		 LYSigHandlerFunc_t * handler)
+{
+#ifdef SIGWINCH
+    /* add more cases to if(condition) if required... */
+    if (sig == SIGWINCH && LYNonRestartingSIGWINCH) {
+	struct sigaction act;
+
+	act.sa_handler = handler;
+	sigemptyset(&act.sa_mask);
+	act.sa_flags = 0;
+#ifdef SA_RESTART
+	if (sig != SIGWINCH)
+	    act.sa_flags |= SA_RESTART;
+#endif /* SA_RESTART */
+	sigaction(sig, &act, NULL);
+    } else
+#endif /* defined(SIGWINCH) */
+	signal(sig, handler);
+}
+#endif /* HAVE_SIGACTION */
+
+#if defined(SIGTSTP) && !defined(USE_SLANG)
+#ifdef HAVE_SIGACTION
+/*
+ * For switching a signal's handling between SIG_DFL and something (possibly)
+ * different that may have been set up by lynx code or e.g. by curses library. 
+ * Uses sigaction to preserve / restore as much state as possible.
+ *
+ * Second arg is where to save or restore from.
+ *
+ * Third arg to_dfl specifies what to do:
+ *	1	Save current state in where, set handling to SIG_DFL
+ *	0	Restore current state to previously saved one in where
+ *
+ * Currently only used for SIGTSTP without SLANG, to prevent (n)curses signal
+ * handler from running while lynx is waiting in system() for an interactive
+ * command like an editor.  - kw
+ */
+static BOOLEAN LYToggleSigDfl(int sig,
+			      struct sigaction *where,
+			      int to_dfl)
+{
+    int rv = -1;
+    struct sigaction oact;
+
+    if (to_dfl == 1) {
+	rv = sigaction(sig, NULL, &oact);
+	if (rv == 0) {
+	    if (oact.sa_handler != SIG_DFL) {
+		oact.sa_handler = SIG_DFL;
+		rv = sigaction(sig, &oact, where);
+	    } else if (where) {
+		memcpy(where, &oact, sizeof(oact));
+		rv = 0;
+	    }
+	}
+    } else {
+	rv = sigaction(sig, where, NULL);
+    }
+    if (rv != 0) {
+	CTRACE((tfp, "Error in LYToggleSigDfl: %s\n", LYStrerror(errno)));
+	return FALSE;
+    } else
+	return TRUE;
+}
+#endif /* HAVE_SIGACTION */
+#endif /* SIGTSTP && !USE_SLANG */
+
+/**************
+ * This bit of code catches window size change signals
+ */
+
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+/* For systems that have both, but both can't be included, duh (or neither) */
+/* FIXME: this whole chunk may be redundant */
+#ifdef TERMIO_AND_CURSES
+# ifdef TERMIO_AND_TERMIOS
+#  include <termio.h>
+# else
+#  ifdef HAVE_TERMIOS_H
+#   include <termios.h>
+#  else
+#   ifdef HAVE_TERMIO_H
+#    include <termio.h>
+#   endif			/* HAVE_TERMIO_H */
+#  endif			/* HAVE_TERMIOS_H */
+# endif				/* TERMIO_AND_TERMIOS */
+#endif /* TERMIO_AND_CURSES */
+
+void size_change(int sig GCC_UNUSED)
+{
+    int old_lines = LYlines;
+    int old_cols = LYcols;
+
+#ifdef USE_SLANG
+#if defined(VMS) || defined(UNIX)
+    SLtt_get_screen_size();
+#endif /* VMS || UNIX */
+    LYlines = SLtt_Screen_Rows;
+    LYcols = SLtt_Screen_Cols;
+#ifdef SLANG_MBCS_HACK
+    PHYSICAL_SLtt_Screen_Cols = LYcols;
+#ifdef SLANG_NO_LIMIT		/* define this if slang has been fixed */
+    SLtt_Screen_Cols = LYcolLimit * 6;
+#else
+    /* Needs to be limited: fixed buffer bugs in slang can cause crash,
+       see slang's SLtt_smart_puts - kw */
+    SLtt_Screen_Cols = HTMIN(LYcolLimit * 6, 255);
+#endif
+#endif /* SLANG_MBCS_HACK */
+    if (sig == 0)
+	/*
+	 * Called from start_curses().
+	 */
+	return;
+#else /* Curses: */
+#ifdef HAVE_SIZECHANGE
+#ifdef TIOCGSIZE
+    struct ttysize win;
+
+#else
+#ifdef TIOCGWINSZ
+    struct winsize win;
+#endif /* TIOCGWINSZ */
+#endif /* TIOCGSIZE */
+
+#ifdef TIOCGSIZE
+    if (ioctl(0, TIOCGSIZE, &win) == 0) {
+	if (win.ts_lines != 0) {
+	    LYlines = win.ts_lines;
+	}
+	if (win.ts_cols != 0) {
+	    LYcols = win.ts_cols;
+	}
+    }
+#else
+#ifdef TIOCGWINSZ
+    if (ioctl(0, TIOCGWINSZ, &win) == 0) {
+	if (win.ws_row != 0) {
+	    LYlines = win.ws_row;
+	}
+	if (win.ws_col != 0) {
+	    LYcols = win.ws_col;
+	}
+    }
+#endif /* TIOCGWINSZ */
+#endif /* TIOCGSIZE */
+#endif /* HAVE_SIZECHANGE */
+
+#ifdef __EMX__
+    {
+	int scrsize[2];
+
+	_scrsize(scrsize);
+	LYcols = scrsize[0];
+	LYlines = scrsize[1];
+    }
+#endif
+
+    if (LYlines <= 0)
+	LYlines = DFT_ROWS;
+    if (LYcols <= 0)
+	LYcols = DFT_COLS;
+#endif /* USE_SLANG */
+
+    /*
+     * Check if the screen size has actually changed.  - AJL
+     */
+    if (LYlines != old_lines || LYcols != old_cols) {
+	recent_sizechange = TRUE;
+	CTRACE((tfp, "Window size changed from (%d,%d) to (%d,%d)\n",
+		old_lines, old_cols, LYlines, LYcols));
+#if defined(CAN_SWITCH_DISPLAY_CHARSET) && defined(CAN_AUTODETECT_DISPLAY_CHARSET)
+	/* May need to reload the font due to different char-box size */
+	if (current_char_set != auto_display_charset)
+	    Switch_Display_Charset(current_char_set, SWITCH_DISPLAY_CHARSET_RESIZE);
+#endif
+    }
+#ifdef SIGWINCH
+    LYExtSignal(SIGWINCH, size_change);
+#endif /* SIGWINCH */
+
+    return;
+}
+
+/*
+ * Utility for freeing the list of previous suggested filenames.  - FM
+ */
+void HTSugFilenames_free(void)
+{
+    LYFreeStringList(sug_filenames);
+    sug_filenames = NULL;
+}
+
+/*
+ * Utility for listing suggested filenames, making any repeated filenames the
+ * most current in the list.  - FM
+ */
+void HTAddSugFilename(char *fname)
+{
+    char *tmp = NULL;
+    char *old;
+    HTList *cur;
+
+    if (!non_empty(fname))
+	return;
+
+    StrAllocCopy(tmp, fname);
+
+    if (!sug_filenames) {
+	sug_filenames = HTList_new();
+#ifdef LY_FIND_LEAKS
+	atexit(HTSugFilenames_free);
+#endif
+	HTList_addObject(sug_filenames, tmp);
+	return;
+    }
+
+    cur = sug_filenames;
+    while (NULL != (old = (char *) HTList_nextObject(cur))) {
+	if (!strcmp(old, tmp)) {
+	    HTList_removeObject(sug_filenames, old);
+	    FREE(old);
+	    break;
+	}
+    }
+    HTList_addObject(sug_filenames, tmp);
+
+    return;
+}
+
+/*
+ * CHANGE_SUG_FILENAME -- Foteos Macrides 29-Dec-1993 Upgraded for use with
+ * Lynx2.2 - FM 17-Jan-1994
+ */
+void change_sug_filename(char *fname)
+{
+    const char *cp2;
+    char *temp = 0, *cp, *cp1, *end;
+
+#ifdef VMS
+    char *dot;
+    int j, k;
+#endif /* VMS */
+
+    /*
+     * Establish the current end of fname.
+     */
+    end = fname + strlen(fname);
+
+    /*
+     * Unescape fname.
+     */
+    HTUnEscape(fname);
+
+    /*
+     * Rename any temporary files.
+     */
+    cp2 = wwwName(lynx_temp_space);
+    if (LYIsHtmlSep(*cp2)) {
+	HTSprintf0(&temp, "file://localhost%s" PID_FMT, cp2, GETPID());
+    } else {
+	HTSprintf0(&temp, "file://localhost/%s" PID_FMT, cp2, GETPID());
+    }
+    if (!strncmp(fname, temp, strlen(temp))) {
+	cp = strrchr(fname, '.');
+	if (strlen(cp) > (strlen(temp) - 4))
+	    cp = NULL;
+	StrAllocCopy(temp, NonNull(cp));
+	sprintf(fname, "temp%.*s", LY_MAXPATH - 10, temp);
+    }
+    FREE(temp);
+
+    if (fname[strlen(fname) - 1] == '/')
+	/*
+	 * Hmm...  we have a directory name.  It is annoying to see a
+	 * scheme+host+path name as a suggested one, let's remove the
+	 * last_slash and go ahead like we have a file name.  - LP
+	 */
+	fname[strlen(fname) - 1] = '\0';
+
+    /*
+     * Remove everything up the the last_slash if there is one.
+     */
+    if ((cp = strrchr(fname, '/')) != NULL && strlen(cp) > 1) {
+	cp1 = fname;
+	/*
+	 * Go past the slash.
+	 */
+	cp++;
+	for (; *cp != '\0'; cp++, cp1++) {
+	    *cp1 = *cp;
+	}
+	*cp1 = '\0';
+    }
+#ifdef _WINDOWS			/* 1998/05/05 (Tue) 10:08:05 */
+    if ((cp = strrchr(fname, '=')) != NULL && strlen(cp) > 1) {
+	cp1 = fname;
+	/*
+	 * Go past the '='.
+	 */
+	cp++;
+	for (; *cp != '\0'; cp++, cp1++) {
+	    *cp1 = *cp;
+	}
+	*cp1 = '\0';
+    }
+#endif
+
+    /*
+     * Trim off date-size suffix, if present.
+     */
+    if ((*(end - 1) == ']') && ((cp = strrchr(fname, '[')) != NULL) &&
+	(cp > fname) && *(--cp) == ' ') {
+	while (*cp == ' ') {
+	    *(cp--) = '\0';
+	}
+    }
+#ifdef VMS
+    /*
+     * Trim off VMS device and/or directory specs, if present.
+     */
+    if ((cp = strchr(fname, '[')) != NULL &&
+	(cp1 = strrchr(cp, ']')) != NULL && strlen(cp1) > 1) {
+	cp1++;
+	for (cp = fname; *cp1 != '\0'; cp1++) {
+	    *(cp++) = *cp1;
+	}
+	*cp = '\0';
+    }
+    /*
+     * Replace illegal or problem characters.
+     */
+    dot = fname + strlen(fname);
+    for (cp = fname; cp < dot; cp++) {
+	/*
+	 * Replace with underscores.
+	 */
+	if (*cp == ' ' || *cp == '/' || *cp == ':' ||
+	    *cp == '[' || *cp == ']' || *cp == '&') {
+	    *cp = '_';
+	    /*
+	     * Replace with dashes.
+	     */
+	} else if (*cp == '!' || *cp == '?' || *cp == '\'' ||
+		   *cp == ',' || *cp == ':' || *cp == '"' ||
+		   *cp == '+' || *cp == '@' || *cp == '\\' ||
+		   *cp == '(' || *cp == ')' || *cp == '=' ||
+		   *cp == '<' || *cp == '>' || *cp == '#' ||
+		   *cp == '%' || *cp == '*' || *cp == '`' ||
+		   *cp == '~' || *cp == '^' || *cp == '|' ||
+		   *cp < ' ' || (UCH(*cp)) > 126) {
+	    *cp = '-';
+	}
+    }
+
+    /*
+     * Collapse any serial underscores.
+     */
+    cp = fname + 1;
+    j = 0;
+    while (cp < dot) {
+	if (fname[j] == '_' && *cp == '_') {
+	    cp++;
+	} else {
+	    fname[++j] = *cp++;
+	}
+    }
+    fname[++j] = '\0';
+
+    /*
+     * Collapse any serial dashes.
+     */
+    dot = fname + (strlen(fname));
+    cp = fname + 1;
+    j = 0;
+    while (cp < dot) {
+	if (fname[j] == '-' && *cp == '-') {
+	    cp++;
+	} else {
+	    fname[++j] = *cp++;
+	}
+    }
+    fname[++j] = '\0';
+
+    /*
+     * Trim any trailing or leading underscores or dashes.
+     */
+    cp = fname + (strlen(fname)) - 1;
+    while (*cp == '_' || *cp == '-') {
+	*cp-- = '\0';
+    }
+    if (fname[0] == '_' || fname[0] == '-') {
+	dot = fname + (strlen(fname));
+	cp = fname;
+	while ((*cp == '_' || *cp == '-') && cp < dot) {
+	    cp++;
+	}
+	j = 0;
+	while (cp < dot) {
+	    fname[j++] = *cp++;
+	}
+	fname[j] = '\0';
+    }
+
+    /*
+     * Replace all but the last period with _'s, or second to last if last is
+     * followed by a terminal Z or z, or GZ or gz,
+     * e.g., convert foo.tar.Z to foo.tar_Z
+     * or, convert foo.tar.gz to foo.tar-gz
+     */
+    j = strlen(fname) - 1;
+    if ((dot = strrchr(fname, '.')) != NULL) {
+	if (TOUPPER(fname[j]) == 'Z') {
+	    if ((fname[j - 1] == '.') &&
+		(((cp = strchr(fname, '.')) != NULL) && cp < dot)) {
+		*dot = '_';
+		dot = strrchr(fname, '.');
+	    } else if (((TOUPPER(fname[j - 1]) == 'G') &&
+			fname[j - 2] == '.') &&
+		       (((cp = strchr(fname, '.')) != NULL) && cp < dot)) {
+		*dot = '-';
+		dot = strrchr(fname, '.');
+	    }
+	}
+	cp = fname;
+	while ((cp = strchr(cp, '.')) != NULL && cp < dot) {
+	    *cp = '_';
+	}
+
+	/*
+	 * But if the root is > 39 characters, move the period appropriately to
+	 * the left.
+	 */
+	while (dot - fname > 39) {
+	    *dot = '\0';
+	    if ((cp = strrchr(fname, '_')) != NULL) {
+		*cp = '.';
+		*dot = '_';
+	    } else if ((cp = strrchr(fname, '-')) != NULL) {
+		*cp = '.';
+		*dot = '_';
+	    } else if (*(dot + 1) == '\0') {
+		j = strlen(fname);
+		while (j > 39) {
+		    fname[j] = fname[j - 1];
+		    j--;
+		}
+		fname[j] = '.';
+	    } else {
+		*dot = '.';
+		j = 39;
+		k = 0;
+		while (dot[k] != '\0') {
+		    fname[j++] = dot[k++];
+		}
+		fname[j] = '\0';
+	    }
+	    dot = strrchr(fname, '.');
+	}
+
+	/*
+	 * Make sure the extension is < 40 characters.
+	 */
+	if ((fname + strlen(fname) - dot) > 39) {
+	    *(dot + 40) = '\0';
+	}
+
+	/*
+	 * Trim trailing dashes or underscores.
+	 */
+	j = (strlen(fname) - 1);
+	while (fname[j] == '_' || fname[j] == '-') {
+	    fname[j--] = '\0';
+	}
+    } else {
+	/*
+	 * No period, so put one on the end, or after the 39th character,
+	 * trimming trailing dashes or underscores.
+	 */
+	if (strlen(fname) > 39) {
+	    fname[39] = '\0';
+	}
+	j = (strlen(fname) - 1);
+	while ((fname[j] == '_') || (fname[j] == '-')) {
+	    j--;
+	}
+	fname[++j] = '.';
+	fname[++j] = '\0';
+    }
+
+#else /* Not VMS (UNIX): */
+
+    /*
+     * Replace problem characters.
+     */
+    for (cp = fname; *cp != '\0'; cp++) {
+	switch (*cp) {
+	case '\'':
+	case '"':
+	case '/':
+	case ' ':
+	    *cp = '-';
+	}
+    }
+#endif /* VMS (UNIX) */
+
+    /*
+     * Make sure the rest of the original string in nulled.
+     */
+    cp = fname + strlen(fname);
+    while (cp < end) {
+	*cp++ = '\0';
+    }
+
+    return;
+}
+
+/*
+ * Construct a temporary-filename.  Assumes result is LY_MAXPATH chars long.
+ */
+static int fmt_tempname(char *result,
+			const char *prefix,
+			const char *suffix)
+{
+    int code;
+
+#ifdef HAVE_RAND_TEMPNAME
+#define SIZE_TEMPNAME ((MAX_TEMPNAME / BITS_PER_CHAR) + 1)
+    static BOOL first = TRUE;
+    static int names_used = 0;
+    static unsigned char used_tempname[SIZE_TEMPNAME];
+    unsigned offset, mask;
+#endif
+    static unsigned counter;
+    char leaf[LY_MAXPATH];
+
+    if (prefix == 0)
+	prefix = "";
+    if (suffix == 0)
+	suffix = "";
+    /*
+     * Prefer a random value rather than a counter.
+     */
+#ifdef HAVE_RAND_TEMPNAME
+    if (first) {
+	lynx_srand((unsigned) ((long) time((time_t *) 0) + (long) result));
+	first = FALSE;
+    }
+
+    /* We don't really need all of the bits from rand().  The high-order bits
+     * are the more-random portion in any case, but limiting the width of the
+     * generated name is done partly to avoid problems on systems that may not
+     * support long filenames.
+     */
+    counter = MAX_TEMPNAME;
+    if (names_used < MAX_TEMPNAME) {
+	counter = (unsigned) (((float) MAX_TEMPNAME * lynx_rand()) /
+			      LYNX_RAND_MAX + 1);
+	/*
+	 * Avoid reusing a temporary name, since there are places in the code
+	 * which can refer to a temporary filename even after it has been
+	 * closed and removed from the filesystem.
+	 */
+	do {
+	    counter %= MAX_TEMPNAME;
+	    offset = counter / BITS_PER_CHAR;
+	    mask = (unsigned) (1 << (counter % BITS_PER_CHAR));
+	    if ((used_tempname[offset] & mask) == 0) {
+		names_used++;
+		used_tempname[offset] |= UCH(mask);
+		break;
+	    }
+	} while ((used_tempname[offset] & mask) == 0);
+    }
+    if (names_used >= MAX_TEMPNAME)
+	HTAlert(gettext("Too many tempfiles"));
+#else
+    counter++;
+#endif
+
+#ifdef FNAMES_8_3
+    /*
+     * The 'lynx_temp_space' string ends with a '/' or '\\', so we only have to
+     * limit the length of the leaf.  As received (e.g., from HTCompressed),
+     * the suffix may contain more than a ".htm", e.g., "-txt.gz", so we trim
+     * off from the filename portion to make room.
+     */
+    sprintf(leaf, PID_FMT PID_FMT, counter, GETPID());
+    if (strlen(leaf) > 8)
+	leaf[8] = 0;
+    if (strlen(suffix) > 4 || *suffix != '.') {
+	const char *tail = strchr(suffix, '.');
+
+	if (tail == 0)
+	    tail = suffix + strlen(suffix);
+	if (8 - (tail - suffix) >= 0)
+	    leaf[8 - (tail - suffix)] = 0;
+    }
+    strcat(leaf, suffix);
+#else
+    sprintf(leaf, "L" PID_FMT "-%uTMP%s", GETPID(), counter, suffix);
+#endif
+    /*
+     * Someone could have configured the temporary pathname to be too long.
+     */
+    if ((strlen(prefix) + strlen(leaf)) < LY_MAXPATH) {
+	sprintf(result, "%s%s", prefix, leaf);
+	code = TRUE;
+    } else {
+	sprintf(result, "%.*s", LY_MAXPATH - 1, leaf);
+	code = FALSE;
+    }
+    CTRACE((tfp, "-> '%s'\n", result));
+    return (code);
+}
+
+/*
+ * Convert 4, 6, 2, 8 to left, right, down, up, etc.
+ */
+int number2arrows(int number)
+{
+    switch (number) {
+    case '1':
+	number = END_KEY;
+	break;
+    case '2':
+	number = DNARROW;
+	break;
+    case '3':
+	number = PGDOWN;
+	break;
+    case '4':
+	number = LTARROW;
+	break;
+    case '5':
+	number = DO_NOTHING;
+	break;
+    case '6':
+	number = RTARROW;
+	break;
+    case '7':
+	number = HOME;
+	break;
+    case '8':
+	number = UPARROW;
+	break;
+    case '9':
+	number = PGUP;
+	break;
+    }
+
+    return (number);
+}
+
+/*
+ * parse_restrictions takes a string of comma-separated restrictions and sets
+ * the corresponding flags to restrict the facilities available.
+ */
+/* The first two are special:  we want to record whether "default" or "all"
+ * restrictions were applied, in addition to the detailed effects of those
+ * options.  - kw
+ */
+/* skip the special flags when processing "all" and "default": */
+#define N_SPECIAL_RESTRICT_OPTIONS 2
+/* *INDENT-OFF* */
+static const struct {
+    const char *name;
+    BOOLEAN *flag;
+    BOOLEAN can;
+} restrictions[] = {
+    { "default",	&had_restrictions_default, TRUE },
+    { "all",		&had_restrictions_all,	TRUE },
+    { "inside_telnet",	&no_inside_telnet,	CAN_ANONYMOUS_INSIDE_DOMAIN_TELNET },
+    { "outside_telnet",	&no_outside_telnet,	CAN_ANONYMOUS_OUTSIDE_DOMAIN_TELNET },
+    { "telnet_port",	&no_telnet_port,	CAN_ANONYMOUS_GOTO_TELNET_PORT },
+    { "inside_ftp",	&no_inside_ftp,		CAN_ANONYMOUS_INSIDE_DOMAIN_FTP },
+    { "outside_ftp",	&no_outside_ftp,	CAN_ANONYMOUS_OUTSIDE_DOMAIN_FTP },
+    { "inside_rlogin",	&no_inside_rlogin,	CAN_ANONYMOUS_INSIDE_DOMAIN_RLOGIN },
+    { "outside_rlogin",	&no_outside_rlogin,	CAN_ANONYMOUS_OUTSIDE_DOMAIN_RLOGIN },
+    { "suspend",	&no_suspend,		FALSE },
+    { "editor",		&no_editor,		FALSE },
+    { "shell",		&no_shell,		FALSE },
+    { "bookmark",	&no_bookmark,		FALSE },
+    { "multibook",	&no_multibook,		FALSE },
+    { "bookmark_exec",	&no_bookmark_exec,	FALSE },
+    { "option_save",	&no_option_save,	FALSE },
+    { "print",		&no_print,		CAN_ANONYMOUS_PRINT },
+    { "download",	&no_download,		FALSE },
+    { "disk_save",	&no_disk_save,		FALSE },
+#if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)
+    { "exec",		&no_exec,		LOCAL_EXECUTION_LINKS_ALWAYS_OFF_FOR_ANONYMOUS },
+#endif
+    { "lynxcgi",	&no_lynxcgi,		FALSE },
+    { "exec_frozen",	&exec_frozen,		FALSE },
+    { "goto",		&no_goto,		CAN_ANONYMOUS_GOTO },
+    { "jump",		&no_jump,		CAN_ANONYMOUS_JUMP },
+    { "file_url",	&no_file_url,		FALSE },
+#ifndef DISABLE_NEWS
+    { "news_post",	&no_newspost,		FALSE },
+    { "inside_news",	&no_inside_news,	CAN_ANONYMOUS_INSIDE_DOMAIN_READ_NEWS },
+    { "outside_news",	&no_outside_news,	CAN_ANONYMOUS_OUTSIDE_DOMAIN_READ_NEWS },
+#endif
+    { "mail",		&no_mail,		CAN_ANONYMOUS_MAIL },
+    { "dotfiles",	&no_dotfiles,		FALSE },
+    { "useragent",	&no_useragent,		FALSE },
+#ifdef SUPPORT_CHDIR
+    { "chdir",		&no_chdir,		FALSE },
+#endif
+#ifdef DIRED_SUPPORT
+    { "dired_support",	&no_dired_support,	FALSE },
+#ifdef OK_PERMIT
+    { "change_exec_perms", &no_change_exec_perms, FALSE },
+#endif /* OK_PERMIT */
+#endif /* DIRED_SUPPORT */
+#ifdef USE_EXTERNALS
+    { "externals",	&no_externals,		FALSE },
+#endif
+    { "lynxcfg_info",	&no_lynxcfg_info,	CAN_ANONYMOUS_VIEW_LYNXCFG_INFO },
+#ifndef NO_CONFIG_INFO
+    { "lynxcfg_xinfo",	&no_lynxcfg_xinfo,	CAN_ANONYMOUS_VIEW_LYNXCFG_EXTENDED_INFO },
+#ifdef HAVE_CONFIG_H
+    { "compileopts_info", &no_compileopts_info,	CAN_ANONYMOUS_VIEW_COMPILEOPTS_INFO },
+#endif
+#endif
+    /* put "goto" restrictions on the end, since they are a refinement */
+#ifndef DISABLE_BIBP
+    { "goto_bibp",	&no_goto_bibp,		CAN_ANONYMOUS_GOTO_BIBP	},
+#endif
+#ifdef HAVE_CONFIG_H
+#ifndef NO_CONFIG_INFO
+    { "goto_configinfo", &no_goto_configinfo,	CAN_ANONYMOUS_GOTO_CONFIGINFO },
+#endif
+#endif
+    { "goto_cso",	&no_goto_cso,		CAN_ANONYMOUS_GOTO_CSO },
+    { "goto_file",	&no_goto_file,		CAN_ANONYMOUS_GOTO_FILE },
+#ifndef DISABLE_FINGER
+    { "goto_finger",	&no_goto_finger,	CAN_ANONYMOUS_GOTO_FINGER },
+#endif
+    { "goto_ftp",	&no_goto_ftp,		CAN_ANONYMOUS_GOTO_FTP },
+#ifndef DISABLE_GOPHER
+    { "goto_gopher",	&no_goto_gopher,	CAN_ANONYMOUS_GOTO_GOPHER },
+#endif
+    { "goto_http",	&no_goto_http,		CAN_ANONYMOUS_GOTO_HTTP },
+    { "goto_https",	&no_goto_https,		CAN_ANONYMOUS_GOTO_HTTPS },
+    { "goto_lynxcgi",	&no_goto_lynxcgi,	CAN_ANONYMOUS_GOTO_LYNXCGI },
+    { "goto_lynxexec",	&no_goto_lynxexec,	CAN_ANONYMOUS_GOTO_LYNXEXEC },
+    { "goto_lynxprog",	&no_goto_lynxprog,	CAN_ANONYMOUS_GOTO_LYNXPROG },
+    { "goto_mailto",	&no_goto_mailto,	CAN_ANONYMOUS_GOTO_MAILTO },
+#ifndef DISABLE_NEWS
+    { "goto_news",	&no_goto_news,		CAN_ANONYMOUS_GOTO_NEWS },
+    { "goto_nntp",	&no_goto_nntp,		CAN_ANONYMOUS_GOTO_NNTP },
+#endif
+    { "goto_rlogin",	&no_goto_rlogin,	CAN_ANONYMOUS_GOTO_RLOGIN },
+#ifndef DISABLE_NEWS
+    { "goto_snews",	&no_goto_snews,		CAN_ANONYMOUS_GOTO_SNEWS },
+#endif
+    { "goto_telnet",	&no_goto_telnet,	CAN_ANONYMOUS_GOTO_TELNET },
+    { "goto_tn3270",	&no_goto_tn3270,	CAN_ANONYMOUS_GOTO_TN3270 },
+    { "goto_wais",	&no_goto_wais,		CAN_ANONYMOUS_GOTO_WAIS },
+};
+/* *INDENT-ON* */
+
+/* This will make no difference between '-' and '_'.  It does only in/equality
+ * compare.  It assumes that p2 can't contain dashes, but p1 can.  This
+ * function is also used (if macro OPTNAME_ALLOW_DASHES doesn't have value of
+ * zero) for compare of commandline options -VH
+ */
+BOOL strn_dash_equ(const char *p1,
+		   const char *p2,
+		   int len)
+{
+    while (len--) {
+	if (!*p2)
+	    return 0;		/* canonical name is shorter */
+	switch (*p1) {
+	case 0:
+	    return 0;
+	case '-':
+	case '_':
+	    if (*p2 != '_')
+		return 0;
+	    else
+		break;
+	default:
+	    if (*p1 != *p2)
+		return 0;
+	}
+	++p1;
+	++p2;
+    }
+    return 1;
+}
+
+/* Uncomment following lines to allow only exact string matching */
+/* #define RESTRICT_NM_ALLOW_DASHES 0 */
+
+#ifndef RESTRICT_NM_ALLOW_DASHES
+# define RESTRICT_NM_ALLOW_DASHES 1
+#endif
+
+#if RESTRICT_NM_ALLOW_DASHES
+#	define RESTRICT_NM_EQU(a,b,len) strn_dash_equ(a,b,len)
+#else
+#	define RESTRICT_NM_EQU(a,b,len) STRNEQ(a,b,len)
+#endif
+
+/*
+ * Returns the inx'th name from the restrictions table, or null if inx is
+ * out of range.
+ */
+const char *index_to_restriction(unsigned inx)
+{
+    if (inx < TABLESIZE(restrictions))
+	return restrictions[inx].name;
+    return NULL;
+}
+
+/*
+ * Returns the value TRUE/FALSE of a given restriction, or -1 if it is not
+ * one that we recognize.
+ */
+int find_restriction(const char *name,
+		     int len)
+{
+    unsigned i;
+
+    if (len < 0)
+	len = (int) strlen(name);
+    for (i = 0; i < TABLESIZE(restrictions); i++) {
+	if (RESTRICT_NM_EQU(name, restrictions[i].name, len)) {
+	    return (*restrictions[i].flag);
+	}
+    }
+    return -1;
+}
+
+void parse_restrictions(const char *s)
+{
+    const char *p;
+    const char *word;
+    unsigned i;
+    BOOLEAN found;
+
+    p = s;
+    while (*p) {
+	p = LYSkipCBlanks(p);
+	if (*p == '\0')
+	    break;
+	word = p;
+	while (*p != ',' && *p != '\0')
+	    p++;
+
+	found = FALSE;
+	if (RESTRICT_NM_EQU(word, "all", p - word)) {
+	    found = TRUE;
+	    for (i = N_SPECIAL_RESTRICT_OPTIONS;
+		 i < TABLESIZE(restrictions);
+		 i++)
+		*(restrictions[i].flag) = TRUE;
+	} else if (RESTRICT_NM_EQU(word, "default", p - word)) {
+	    found = TRUE;
+	    for (i = N_SPECIAL_RESTRICT_OPTIONS;
+		 i < TABLESIZE(restrictions);
+		 i++)
+		*(restrictions[i].flag) = (BOOLEAN) !restrictions[i].can;
+	} else {
+	    for (i = 0; i < TABLESIZE(restrictions); i++) {
+		if (RESTRICT_NM_EQU(word, restrictions[i].name, p - word)) {
+		    *(restrictions[i].flag) = TRUE;
+		    found = TRUE;
+		    break;
+		}
+	    }
+	}
+	if (!found) {
+	    printf("%s: %.*s\n", gettext("unknown restriction"),
+		   (int) (p - word), word);
+	    exit_immediately(EXIT_FAILURE);
+	}
+	if (*p)
+	    p++;
+    }
+
+    /*
+     * If shell is restricted, set restrictions on related topics.
+     */
+    if (no_shell) {
+	no_goto_lynxexec = TRUE;
+	no_goto_lynxprog = TRUE;
+	no_goto_lynxcgi = TRUE;
+#ifdef EXEC_LINKS
+	local_exec_on_local_files = TRUE;
+#endif
+    }
+}
+
+void print_restrictions_to_fd(FILE *fp)
+{
+    unsigned i, count = 0;
+
+    for (i = 0; i < TABLESIZE(restrictions); i++) {
+	if (*(restrictions[i].flag) == TRUE) {
+	    count++;
+	}
+    }
+    if (!count) {
+	fprintf(fp, gettext("No restrictions set.\n"));
+	return;
+    }
+    fprintf(fp, gettext("Restrictions set:\n"));
+    for (i = 0; i < TABLESIZE(restrictions); i++) {
+	if (*(restrictions[i].flag) == TRUE) {
+	    /* if "goto" is restricted, don't bother tell about its
+	     * refinements
+	     */
+	    if (strncmp(restrictions[i].name, "goto_", 5)
+		|| !no_goto)
+		fprintf(fp, "   %s\n", restrictions[i].name);
+	}
+    }
+}
+
+#ifdef VMS
+#include <jpidef.h>
+#include <maildef.h>
+#include <starlet.h>
+
+typedef struct _VMSMailItemList {
+    short buffer_length;
+    short item_code;
+    void *buffer_address;
+    long *return_length_address;
+} VMSMailItemList;
+
+void LYCheckMail(void)
+{
+    static BOOL firsttime = TRUE, failure = FALSE;
+    static char user[13], dir[252];
+    static long userlen = 0, dirlen;
+    static time_t lastcheck = 0;
+    time_t now;
+    static short new, lastcount;
+    long ucontext = 0, status;
+    short flags = MAIL$M_NEWMSG;
+    /* *INDENT-OFF* */
+    VMSMailItemList
+      null_list[] = {{0,0,0,0}},
+      jpi_list[]  = {{sizeof(user) - 1,JPI$_USERNAME,(void *)user,&userlen},
+		     {0,0,0,0}},
+      uilist[]	  = {{0,MAIL$_USER_USERNAME,0,0},
+		     {0,0,0,0}},
+      uolist[]	  = {{sizeof(new),MAIL$_USER_NEW_MESSAGES,&new,0},
+		     {sizeof(dir),MAIL$_USER_FULL_DIRECTORY,dir,&dirlen},
+		     {0,0,0,0}};
+    /* *INDENT-ON* */
+
+    extern long mail$user_begin();
+    extern long mail$user_get_info();
+    extern long mail$user_end();
+
+    if (failure)
+	return;
+
+    if (firsttime) {
+	firsttime = FALSE;
+	/* Get the username. */
+	status = sys$getjpiw(0, 0, 0, jpi_list, 0, 0, 0);
+	if (!(status & 1)) {
+	    failure = TRUE;
+	    return;
+	}
+	user[userlen] = '\0';
+	LYTrimTrailing(user);
+    }
+
+    /* Minimum report interval is 60 sec. */
+    time(&now);
+    if (now - lastcheck < 60)
+	return;
+    lastcheck = now;
+
+    /* Get the current newmail count. */
+    status = mail$user_begin(&ucontext, null_list, null_list);
+    if (!(status & 1)) {
+	failure = TRUE;
+	return;
+    }
+    uilist[0].buffer_length = strlen(user);
+    uilist[0].buffer_address = user;
+    status = mail$user_get_info(&ucontext, uilist, uolist);
+    if (!(status & 1)) {
+	failure = TRUE;
+	return;
+    }
+
+    /* Should we report anything to the user? */
+    if (new > 0) {
+	if (lastcount == 0)
+	    /* Have newmail at startup of Lynx. */
+	    HTUserMsg(HAVE_UNREAD_MAIL_MSG);
+	else if (new > lastcount)
+	    /* Have additional mail since last report. */
+	    HTUserMsg(HAVE_NEW_MAIL_MSG);
+	lastcount = new;
+	return;
+    }
+    lastcount = new;
+
+    /* Clear the context */
+    mail$user_end((long *) &ucontext, null_list, null_list);
+    return;
+}
+#else
+void LYCheckMail(void)
+{
+    static BOOL firsttime = TRUE;
+    static char *mf;
+    static time_t lastcheck;
+    static time_t lasttime;
+    static long lastsize;
+    time_t now;
+    struct stat st;
+
+    if (firsttime) {
+	mf = LYGetEnv("MAIL");
+	firsttime = FALSE;
+	time(&lasttime);
+    }
+
+    if (mf == NULL)
+	return;
+
+    time(&now);
+    if (now - lastcheck < 60)
+	return;
+    lastcheck = now;
+
+    if ((stat(mf, &st) < 0)
+	|| !S_ISREG(st.st_mode)) {
+	mf = NULL;
+	return;
+    }
+
+    if (st.st_size > 0) {
+	if (((lasttime != st.st_mtime) && (st.st_mtime > st.st_atime))
+	    || ((lastsize != 0) && (st.st_size > lastsize)))
+	    HTUserMsg(HAVE_NEW_MAIL_MSG);
+	else if (lastsize == 0)
+	    HTUserMsg(HAVE_MAIL_MSG);
+    }
+    lastsize = (long) st.st_size;
+    lasttime = st.st_mtime;
+    return;
+}
+#endif /* VMS */
+
+/*
+ *  This function ensures that an href will be
+ *  converted to a fully resolved, absolute URL,
+ *  with guessing of the host or expansions of
+ *  lead tildes via LYConvertToURL() if needed,
+ *  and tweaking/simplifying via HTParse().  It
+ *  is used for LynxHome, startfile, homepage,
+ *  and 'g'oto entries, after they have been
+ *  passed to LYFillLocalFileURL(). - FM
+ *  Such URLs have no `base' reference to which they
+ *  could be resolved.  LYLegitimizeHREF could not be used.
+ */
+void LYEnsureAbsoluteURL(char **href,
+			 const char *name,
+			 int fixit)
+{
+    char *temp = NULL;
+
+    if (isEmpty(*href))
+	return;
+
+    /*
+     * Check whether to fill in localhost.  - FM
+     */
+    LYFillLocalFileURL(href, "file://localhost");
+
+    /*
+     * If it is not a URL then make it one.
+     */
+    if (!strcasecomp(*href, STR_NEWS_URL)) {
+	StrAllocCat(*href, "*");
+    } else if (!strcasecomp(*href, STR_SNEWS_URL)) {
+	StrAllocCat(*href, "/*");
+    }
+
+    if (!is_url(*href)) {
+	CTRACE((tfp, "%s%s'%s' is not a URL\n",
+		NonNull(name), (name ? " " : ""), *href));
+	LYConvertToURL(href, fixit);
+    }
+
+    temp = HTParse(*href, "", PARSE_ALL);
+    if (non_empty(temp))
+	StrAllocCopy(*href, temp);
+    FREE(temp);
+}
+
+/*
+ * Rewrite and reallocate a previously allocated string as a file URL if the
+ * string resolves to a file or directory on the local system, otherwise as an
+ * http URL.  - FM
+ */
+void LYConvertToURL(char **AllocatedString,
+		    int fixit)
+{
+    char *old_string = *AllocatedString;
+    char *temp = NULL;
+    char *cp = NULL;
+
+#ifndef VMS
+    struct stat st;
+#endif /* !VMS */
+
+    if (isEmpty(old_string))
+	return;
+
+#if defined(USE_DOS_DRIVES)
+    {
+	char *cp_url = *AllocatedString;
+
+	for (; *cp_url != '\0'; cp_url++)
+	    if (*cp_url == '\\')
+		*cp_url = '/';
+	cp_url--;
+	if (LYIsDosDrive(*AllocatedString) && *cp_url == ':')
+	    LYAddPathSep(AllocatedString);
+    }
+#endif /* USE_DOS_DRIVES */
+
+    *AllocatedString = NULL;	/* so StrAllocCopy doesn't free it */
+    StrAllocCopy(*AllocatedString, "file://localhost");
+
+    if (*old_string != '/') {
+	char *fragment = NULL;
+
+#if defined(USE_DOS_DRIVES)
+	StrAllocCat(*AllocatedString, "/");
+#endif /* USE_DOS_DRIVES */
+#ifdef VMS
+	/*
+	 * Not a SHELL pathspec.  Get the full VMS spec and convert it.
+	 */
+	char *cur_dir = NULL;
+	static char url_file[LY_MAXPATH], file_name[LY_MAXPATH], dir_name[LY_MAXPATH];
+	unsigned long context = 0;
+
+	$DESCRIPTOR(url_file_dsc, url_file);
+	$DESCRIPTOR(file_name_dsc, file_name);
+	if (LYIsTilde(*old_string)) {
+	    /*
+	     * On VMS, we'll accept '~' on the command line as Home_Dir(), and
+	     * assume the rest of the path, if any, has SHELL syntax.
+	     */
+	    StrAllocCat(*AllocatedString, HTVMS_wwwName(Home_Dir()));
+	    if ((cp = strchr(old_string, '/')) != NULL) {
+		/*
+		 * Append rest of path, if present, skipping "user" if "~user"
+		 * was entered, simplifying, and eliminating any residual
+		 * relative elements.  - FM
+		 */
+		StrAllocCopy(temp, cp);
+		LYTrimRelFromAbsPath(temp);
+		StrAllocCat(*AllocatedString, temp);
+		FREE(temp);
+	    }
+	    goto have_VMS_URL;
+	} else {
+	    fragment = trimPoundSelector(old_string);
+	    LYstrncpy(url_file, old_string, sizeof(url_file) - 1);
+	}
+	url_file_dsc.dsc$w_length = (short) strlen(url_file);
+	if (1 & lib$find_file(&url_file_dsc, &file_name_dsc, &context,
+			      0, 0, 0, 0)) {
+	    /*
+	     * We found the file.  Convert to a URL pathspec.
+	     */
+	    if ((cp = strchr(file_name, ';')) != NULL) {
+		*cp = '\0';
+	    }
+	    LYLowerCase(file_name);
+	    StrAllocCat(*AllocatedString, HTVMS_wwwName(file_name));
+	    if ((cp = strchr(old_string, ';')) != NULL) {
+		StrAllocCat(*AllocatedString, cp);
+	    }
+	    if (fragment != NULL) {
+		restorePoundSelector(fragment);
+		StrAllocCat(*AllocatedString, fragment);
+		fragment = NULL;
+	    }
+	} else if ((NULL != getcwd(dir_name, sizeof(dir_name) - 1, 0)) &&
+		   0 == chdir(old_string)) {
+	    /*
+	     * Probably a directory.  Try converting that.
+	     */
+	    StrAllocCopy(cur_dir, dir_name);
+	    restorePoundSelector(fragment);
+	    if (NULL != getcwd(dir_name, sizeof(dir_name) - 1, 0)) {
+		/*
+		 * Yup, we got it!
+		 */
+		LYLowerCase(dir_name);
+		StrAllocCat(*AllocatedString, dir_name);
+		if (fragment != NULL) {
+		    StrAllocCat(*AllocatedString, fragment);
+		    fragment = NULL;
+		}
+	    } else {
+		/*
+		 * Nope.  Assume it's an http URL with the "http://" defaulted,
+		 * if we can't rule out a bad VMS path.
+		 */
+		fragment = NULL;
+		if (strchr(old_string, '[') ||
+		    ((cp = strchr(old_string, ':')) != NULL &&
+		     !isdigit(UCH(cp[1]))) ||
+		    !LYExpandHostForURL(&old_string,
+					URLDomainPrefixes,
+					URLDomainSuffixes)) {
+		    /*
+		     * Probably a bad VMS path (but can't be sure).  Use
+		     * original pathspec for the error message that will
+		     * result.
+		     */
+		    sprintf(url_file, "/%.*s", sizeof(url_file) - 2, old_string);
+		    CTRACE((tfp,
+			    "Can't find '%s'  Will assume it's a bad path.\n",
+			    old_string));
+		    StrAllocCat(*AllocatedString, url_file);
+		} else {
+		    /*
+		     * Assume a URL is wanted, so guess the scheme with
+		     * "http://" as the default.  - FM
+		     */
+		    if (!LYAddSchemeForURL(&old_string, "http://")) {
+			StrAllocCopy(*AllocatedString, "http://");
+			StrAllocCat(*AllocatedString, old_string);
+		    } else {
+			StrAllocCopy(*AllocatedString, old_string);
+		    }
+		}
+	    }
+	} else {
+	    /*
+	     * Nothing found.  Assume it's an http URL with the "http://"
+	     * defaulted, if we can't rule out a bad VMS path.
+	     */
+	    restorePoundSelector(fragment);
+	    fragment = NULL;
+
+	    if (strchr(old_string, '[') ||
+		((cp = strchr(old_string, ':')) != NULL &&
+		 !isdigit(UCH(cp[1]))) ||
+		!LYExpandHostForURL(&old_string,
+				    URLDomainPrefixes,
+				    URLDomainSuffixes)) {
+		/*
+		 * Probably a bad VMS path (but can't be sure).  Use original
+		 * pathspec for the error message that will result.
+		 */
+		sprintf(url_file, "/%.*s", sizeof(url_file) - 2, old_string);
+		CTRACE((tfp, "Can't find '%s'  Will assume it's a bad path.\n",
+			old_string));
+		StrAllocCat(*AllocatedString, url_file);
+	    } else {
+		/*
+		 * Assume a URL is wanted, so guess the scheme with "http://"
+		 * as the default.  - FM
+		 */
+		if (!LYAddSchemeForURL(&old_string, "http://")) {
+		    StrAllocCopy(*AllocatedString, "http://");
+		    StrAllocCat(*AllocatedString, old_string);
+		} else {
+		    StrAllocCopy(*AllocatedString, old_string);
+		}
+	    }
+	}
+	lib$find_file_end(&context);
+	FREE(cur_dir);
+      have_VMS_URL:
+	CTRACE((tfp, "Trying: '%s'\n", *AllocatedString));
+#else /* not VMS: */
+#if defined(USE_DOS_DRIVES)
+#ifdef _WINDOWS
+	if (*old_string == '.') {
+	    char fullpath[MAX_PATH + 1];
+	    char *filepart = NULL;
+	    DWORD chk;
+
+	    chk = GetFullPathNameA(old_string, MAX_PATH + 1,
+				   fullpath, &filepart);
+	    if (chk != 0) {
+		StrAllocCopy(temp, wwwName(fullpath));
+		StrAllocCat(*AllocatedString, temp);
+		FREE(temp);
+		CTRACE((tfp, "Converted '%s' to '%s'\n",
+			old_string, *AllocatedString));
+	    } else {
+		StrAllocCat(*AllocatedString, old_string);
+	    }
+	}
+#else
+	if (strlen(old_string) == 1 && *old_string == '.') {
+	    /*
+	     * They want .
+	     */
+	    char curdir[LY_MAXPATH];
+
+	    StrAllocCopy(temp, wwwName(Current_Dir(curdir)));
+	    StrAllocCat(*AllocatedString, temp);
+	    FREE(temp);
+	    CTRACE((tfp, "Converted '%s' to '%s'\n",
+		    old_string, *AllocatedString));
+	}
+#endif
+	else
+#endif /* USE_DOS_DRIVES */
+	if (LYIsTilde(*old_string)) {
+	    /*
+	     * On Unix, convert '~' to Home_Dir().
+	     */
+	    StrAllocCat(*AllocatedString, wwwName(Home_Dir()));
+	    if ((cp = strchr(old_string, '/')) != NULL) {
+		/*
+		 * Append rest of path, if present, skipping "user" if "~user"
+		 * was entered, simplifying, and eliminating any residual
+		 * relative elements.  - FM
+		 */
+		StrAllocCopy(temp, cp);
+		LYTrimRelFromAbsPath(temp);
+		StrAllocCat(*AllocatedString, temp);
+		FREE(temp);
+	    }
+	    CTRACE((tfp, "Converted '%s' to '%s'\n",
+		    old_string, *AllocatedString));
+	} else {
+	    /*
+	     * Create a full path to the current default directory.
+	     */
+	    char curdir[LY_MAXPATH];
+	    char *temp2 = NULL;
+	    BOOL is_local = FALSE;
+
+	    Current_Dir(curdir);
+	    /*
+	     * Concatenate and simplify, trimming any residual relative
+	     * elements.  - FM
+	     */
+#if defined (USE_DOS_DRIVES)
+	    if (old_string[1] != ':' && old_string[1] != '|') {
+		StrAllocCopy(temp, wwwName(curdir));
+		LYAddHtmlSep(&temp);
+		LYstrncpy(curdir, temp, (sizeof(curdir) - 1));
+		StrAllocCat(temp, old_string);
+	    } else {
+		curdir[0] = '\0';
+		/* 1998/01/13 (Tue) 12:24:33 */
+		if (old_string[1] == '|')
+		    old_string[1] = ':';
+		StrAllocCopy(temp, old_string);
+
+		if (strlen(temp) == 2 && LYIsDosDrive(temp))
+		    LYAddPathSep(&temp);
+	    }
+#else
+	    StrAllocCopy(temp, curdir);
+	    StrAllocCat(temp, "/");
+	    StrAllocCat(temp, old_string);
+#endif /* USE_DOS_DRIVES */
+	    LYTrimRelFromAbsPath(temp);
+	    CTRACE((tfp, "Converted '%s' to '%s'\n", old_string, temp));
+	    if ((stat(temp, &st) > -1) ||
+		LYCanReadFile(temp)) {
+		/*
+		 * It is a subdirectory or file on the local system.
+		 */
+#if defined (USE_DOS_DRIVES)
+		/* Don't want to see DOS local paths like c: escaped  */
+		/* especially when we really have file://localhost/   */
+		/* at the beginning.  To avoid any confusion we allow */
+		/* escaping the path if URL specials % or # present.  */
+		if (strchr(temp, '#') == NULL && strchr(temp, '%') == NULL)
+		    StrAllocCopy(cp, temp);
+		else
+		    cp = HTEscape(temp, URL_PATH);
+#else
+		cp = HTEscape(temp, URL_PATH);
+#endif /* USE_DOS_DRIVES */
+		StrAllocCat(*AllocatedString, cp);
+		FREE(cp);
+		CTRACE((tfp, "Converted '%s' to '%s'\n",
+			old_string, *AllocatedString));
+		is_local = TRUE;
+	    } else {
+		char *cp2 = NULL;
+
+		StrAllocCopy(temp2, curdir);
+		LYAddPathSep(&temp2);
+		StrAllocCopy(cp, old_string);
+		fragment = trimPoundSelector(cp);
+		HTUnEscape(cp);	/* unescape given path without fragment */
+		StrAllocCat(temp2, cp);		/* append to current dir  */
+		StrAllocCopy(cp2, temp2);	/* keep a copy in cp2     */
+		LYTrimRelFromAbsPath(temp2);
+#ifdef WIN_EX			/* 1998/07/31 (Fri) 09:09:03 */
+		HTUnEscape(temp2);	/* for LFN */
+#endif
+
+		if (strcmp(temp2, temp) != 0 &&
+		    ((stat(temp2, &st) > -1) ||
+		     LYCanReadFile(temp2))) {
+		    /*
+		     * It is a subdirectory or file on the local system with
+		     * escaped characters and/or a fragment to be appended to
+		     * the URL.  - FM
+		     */
+
+		    FREE(temp);
+		    if (strcmp(cp2, temp2) == 0) {
+			/*
+			 * LYTrimRelFromAbsPath did nothing, use old_string as
+			 * given.  - kw
+			 */
+			temp = HTEscape(curdir, URL_PATH);
+			LYAddHtmlSep(&temp);
+			StrAllocCat(temp, old_string);
+		    } else {
+			temp = HTEscape(temp2, URL_PATH);
+			if (fragment != NULL) {
+			    restorePoundSelector(fragment);
+			    StrAllocCat(temp, fragment);
+			}
+		    }
+		    StrAllocCat(*AllocatedString, temp);
+		    CTRACE((tfp, "Converted '%s' to '%s'\n",
+			    old_string, *AllocatedString));
+		    is_local = TRUE;
+
+		} else if (strchr(curdir, '#') != NULL ||
+			   strchr(curdir, '%') != NULL) {
+		    /*
+		     * If PWD has some unusual characters, construct a filename
+		     * in temp where those are escaped.  This is mostly to
+		     * prevent this function from returning with some weird URL
+		     * if the LYExpandHostForURL tests further down fail.  - kw
+		     */
+		    FREE(temp);
+		    if (strcmp(cp2, temp2) == 0) {
+			/*
+			 * LYTrimRelFromAbsPath did nothing, use old_string as
+			 * given.  - kw
+			 */
+			temp = HTEscape(curdir, URL_PATH);
+			LYAddHtmlSep(&temp);
+			StrAllocCat(temp, old_string);
+		    } else {
+			temp = HTEscape(temp2, URL_PATH);
+			if (fragment != NULL) {
+			    restorePoundSelector(fragment);
+			    StrAllocCat(temp, fragment);
+			}
+		    }
+		}
+		FREE(cp);
+		FREE(cp2);
+	    }
+	    if (is_local == FALSE) {
+		/*
+		 * It's not an accessible subdirectory or file on the local
+		 * system, so assume it's a URL request and guess the scheme
+		 * with "http://" as the default.
+		 */
+		CTRACE((tfp, "Can't stat() or fopen() '%s'\n",
+			temp2 ? temp2 : temp));
+#ifdef WIN_EX			/* 1998/01/13 (Tue) 09:07:37 */
+		{
+		    const char *p, *q;
+		    char buff[LY_MAXPATH + 128];
+
+		    p = Home_Dir();
+		    q = temp2 ? temp2 : temp;
+
+		    if (strlen(q) == 3 && LYIsDosDrive(q)) {
+			sprintf(buff,
+				"'%s' not exist, Goto LynxHome '%s'.", q, p);
+			_statusline(buff);
+			LYSleepAlert();
+			FREE(temp);
+			StrAllocCat(*AllocatedString, p);
+			goto Retry;
+		    }
+		}
+#endif
+		if (LYExpandHostForURL(&old_string,
+				       URLDomainPrefixes,
+				       URLDomainSuffixes)) {
+		    if (!LYAddSchemeForURL(&old_string, "http://")) {
+			StrAllocCopy(*AllocatedString, "http://");
+			StrAllocCat(*AllocatedString, old_string);
+		    } else {
+			StrAllocCopy(*AllocatedString, old_string);
+		    }
+		} else if (fixit) {
+		    /* RW 1998Mar16  Restore AllocatedString to 'old_string' */
+		    StrAllocCopy(*AllocatedString, old_string);
+		} else {
+		    /* Return file URL for the file that does not exist */
+		    StrAllocCat(*AllocatedString, temp);
+		}
+#ifdef WIN_EX
+	      Retry:
+#endif
+		CTRACE((tfp, "Trying: '%s'\n", *AllocatedString));
+	    }
+	    FREE(temp);
+	    FREE(temp2);
+	}
+#endif /* VMS */
+    } else {
+	/*
+	 * Path begins with a slash.  Simplify and use it.
+	 */
+	if (old_string[1] == '\0') {
+	    /*
+	     * Request for root.  Respect it on Unix, but on VMS we treat that
+	     * as a listing of the login directory.  - FM
+	     */
+#ifdef VMS
+	    StrAllocCat(*AllocatedString, HTVMS_wwwName(Home_Dir()));
+#else
+	    StrAllocCat(*AllocatedString, "/");
+	} else if ((stat(old_string, &st) > -1) ||
+		   LYCanReadFile(old_string)) {
+	    /*
+	     * It is an absolute directory or file on the local system.  - KW
+	     */
+	    StrAllocCopy(temp, old_string);
+	    LYTrimRelFromAbsPath(temp);
+	    CTRACE((tfp, "Converted '%s' to '%s'\n", old_string, temp));
+	    cp = HTEscape(temp, URL_PATH);
+	    StrAllocCat(*AllocatedString, cp);
+	    FREE(cp);
+	    FREE(temp);
+	    CTRACE((tfp, "Converted '%s' to '%s'\n",
+		    old_string, *AllocatedString));
+#endif /* VMS */
+	} else if (LYIsTilde(old_string[1])) {
+	    /*
+	     * Has a Home_Dir() reference.  Handle it as if there weren't a
+	     * lead slash.  - FM
+	     */
+	    StrAllocCat(*AllocatedString, wwwName(Home_Dir()));
+	    if ((cp = strchr((old_string + 1), '/')) != NULL) {
+		/*
+		 * Append rest of path, if present, skipping "user" if "~user"
+		 * was entered, simplifying, and eliminating any residual
+		 * relative elements.  - FM
+		 */
+		StrAllocCopy(temp, cp);
+		LYTrimRelFromAbsPath(temp);
+		StrAllocCat(*AllocatedString, temp);
+		FREE(temp);
+	    }
+	} else {
+	    /*
+	     * Normal absolute path.  Simplify, trim any residual relative
+	     * elements, and append it.  - FM
+	     */
+	    StrAllocCopy(temp, old_string);
+	    LYTrimRelFromAbsPath(temp);
+	    StrAllocCat(*AllocatedString, temp);
+	    FREE(temp);
+	}
+	CTRACE((tfp, "Converted '%s' to '%s'\n",
+		old_string, *AllocatedString));
+    }
+    FREE(old_string);
+    /* Pause so we can read the messages before invoking curses */
+    CTRACE_SLEEP(AlertSecs);
+}
+
+#if defined(_WINDOWS)		/* 1998/06/23 (Tue) 16:45:20 */
+
+int win32_check_interrupt(void)
+{
+    int c;
+
+    if (kbhit()) {
+	c = LYgetch();
+	/** Keyboard 'Z' or 'z', or Control-G or Control-C **/
+	if (LYCharIsINTERRUPT(c) || c == 0x1b) {
+	    return TRUE;
+	}
+    }
+    return FALSE;
+}
+
+void sleep(unsigned sec)
+{
+    unsigned int i, j;
+    int c;
+
+    for (j = 0; j < sec; j++) {
+	for (i = 0; i < 10; i++) {
+	    Sleep(100);
+	    if (kbhit()) {
+		c = LYgetch();
+		return;
+	    }
+	}
+    }
+}
+#endif
+
+/*
+ * This function rewrites and reallocates a previously allocated string so that
+ * the first element is a confirmed Internet host, and returns TRUE, otherwise
+ * it does not modify the string and returns FALSE.  It first tries the element
+ * as is, then, if the element does not end with a dot, it adds prefixes from
+ * the (comma separated) prefix list argument, and, if the element does not
+ * begin with a dot, suffixes from the (comma separated) suffix list arguments
+ * (e.g., www.host.com, then www.host,edu, then www.host.net, then
+ * www.host.org).  The remaining path, if one is present, will be appended to
+ * the expanded host.  It also takes into account whether a colon is in the
+ * element or suffix, and includes that and what follows as a port field for
+ * the expanded host field (e.g, wfbr:8002/dir/lynx should yield
+ * www.wfbr.edu:8002/dir/lynx).  The calling function should prepend the scheme
+ * field (e.g., http://), or pass the string to LYAddSchemeForURL(), if this
+ * function returns TRUE.  - FM
+ */
+BOOLEAN LYExpandHostForURL(char **AllocatedString,
+			   char *prefix_list,
+			   char *suffix_list)
+{
+    char *DomainPrefix = NULL;
+    const char *StartP, *EndP;
+    char *DomainSuffix = NULL;
+    const char *StartS, *EndS;
+    char *Str = NULL, *StrColon = NULL, *MsgStr = NULL;
+    char *Host = NULL, *HostColon = NULL, *host = NULL;
+    char *Path = NULL;
+    char *Fragment = NULL;
+    BOOLEAN GotHost = FALSE;
+    BOOLEAN Startup = (BOOL) (helpfilepath == NULL);
+
+#ifdef INET6
+    struct addrinfo hints, *res;
+    int error;
+    char *begin;
+    char *end = NULL;
+#endif /* INET6 */
+
+    /*
+     * If it's a NULL or zero-length string, or if it begins with a slash or
+     * hash, don't continue pointlessly.  - FM
+     */
+    if (!(*AllocatedString) || *AllocatedString[0] == '\0' ||
+	*AllocatedString[0] == '/' || *AllocatedString[0] == '#') {
+	return GotHost;
+    }
+
+    /*
+     * If it's a partial or relative path, don't continue pointlessly.  - FM
+     */
+    if (!strncmp(*AllocatedString, "..", 2) ||
+	!strncmp(*AllocatedString, "./", 2)) {
+	return GotHost;
+    }
+
+    /*
+     * Make a clean copy of the string, and trim off the path if one is
+     * present, but save the information so we can restore the path after
+     * filling in the Host[:port] field.  - FM
+     */
+    StrAllocCopy(Str, *AllocatedString);
+    if ((Path = strchr(Str, '/')) != NULL) {
+	/*
+	 * Have a path.  Any fragment should already be included in Path.  - FM
+	 */
+	*Path = '\0';
+    } else {
+	/*
+	 * No path, so check for a fragment and trim that, to be restored after
+	 * filling in the Host[:port] field.  - FM
+	 */
+	Fragment = trimPoundSelector(Str);
+    }
+
+    /*
+     * If the potential host string has a colon, assume it begins a port field,
+     * and trim it off, but save the information so we can restore the port
+     * field after filling in the host field.  - FM
+     */
+    if ((StrColon = strrchr(Str, ':')) != NULL &&
+	isdigit(UCH(StrColon[1])) && strchr(StrColon, ']') == NULL) {
+	if (StrColon == Str) {
+	    goto cleanup;
+	}
+	*StrColon = '\0';
+    }
+
+    /*
+     * Do a DNS test on the potential host field as presently trimmed.  - FM
+     */
+    StrAllocCopy(host, Str);
+    HTUnEscape(host);
+    if (LYCursesON) {
+	StrAllocCopy(MsgStr, WWW_FIND_MESSAGE);
+	StrAllocCat(MsgStr, host);
+	StrAllocCat(MsgStr, FIRST_SEGMENT);
+	HTProgress(MsgStr);
+    } else if (Startup && !dump_output_immediately) {
+	fprintf(stdout, "%s '%s'%s\r\n", WWW_FIND_MESSAGE, host, FIRST_SEGMENT);
+    }
+#ifdef INET6
+    begin = host;
+    if (host[0] == '[' && ((end = strrchr(host, ']')))) {
+	/*
+	 * cut '[' and ']' from the IPv6 address, e.g. [::1]
+	 */
+	begin = host + 1;
+	*end = '\0';
+    }
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = PF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+    error = getaddrinfo(begin, "80", &hints, &res);
+    if (end)
+	*end = ']';
+
+    if (!error && res)
+#else
+    if (LYGetHostByName(host) != NULL)
+#endif /* INET6 */
+    {
+	/*
+	 * Clear any residual interrupt.  - FM
+	 */
+	if (LYCursesON && HTCheckForInterrupt()) {
+	    CTRACE((tfp,
+		    "LYExpandHostForURL: Ignoring interrupt because '%s' resolved.\n",
+		    host));
+	}
+
+	/*
+	 * Return success.  - FM
+	 */
+	GotHost = TRUE;
+	goto cleanup;
+    } else if (LYCursesON && (lynx_nsl_status == HT_INTERRUPTED)) {
+	/*
+	 * Give the user chance to interrupt lookup cycles.  - KW & FM
+	 */
+	CTRACE((tfp,
+		"LYExpandHostForURL: Interrupted while '%s' failed to resolve.\n",
+		host));
+
+	/*
+	 * Return failure.  - FM
+	 */
+	goto cleanup;
+    }
+
+    /*
+     * Set the first prefix, making it a zero-length string if the list is NULL
+     * or if the potential host field ends with a dot.  - FM
+     */
+    StartP = ((prefix_list && Str[strlen(Str) - 1] != '.')
+	      ? prefix_list
+	      : "");
+    /*
+     * If we have a prefix, but the allocated string is one of the common host
+     * prefixes, make our prefix a zero-length string.  - FM
+     */
+    if (*StartP && *StartP != '.') {
+	if (!strncasecomp(*AllocatedString, "www.", 4) ||
+	    !strncasecomp(*AllocatedString, "ftp.", 4) ||
+	    !strncasecomp(*AllocatedString, "gopher.", 7) ||
+	    !strncasecomp(*AllocatedString, "wais.", 5) ||
+	    !strncasecomp(*AllocatedString, "cso.", 4) ||
+	    !strncasecomp(*AllocatedString, "ns.", 3) ||
+	    !strncasecomp(*AllocatedString, "ph.", 3) ||
+	    !strncasecomp(*AllocatedString, "finger.", 7) ||
+	    !strncasecomp(*AllocatedString, "news.", 5) ||
+	    !strncasecomp(*AllocatedString, "nntp.", 5)) {
+	    StartP = "";
+	}
+    }
+    while ((*StartP) && (WHITE(*StartP) || *StartP == ',')) {
+	StartP++;		/* Skip whitespace and separators */
+    }
+    EndP = StartP;
+    while (*EndP && !WHITE(*EndP) && *EndP != ',') {
+	EndP++;			/* Find separator */
+    }
+    StrAllocCopy(DomainPrefix, StartP);
+    DomainPrefix[EndP - StartP] = '\0';
+
+    /*
+     * Test each prefix with each suffix.  - FM
+     */
+    do {
+	/*
+	 * Set the first suffix, making it a zero-length string if the list is
+	 * NULL or if the potential host field begins with a dot.  - FM
+	 */
+	StartS = ((suffix_list && *Str != '.')
+		  ? suffix_list
+		  : "");
+	while ((*StartS) && (WHITE(*StartS) || *StartS == ',')) {
+	    StartS++;		/* Skip whitespace and separators */
+	}
+	EndS = StartS;
+	while (*EndS && !WHITE(*EndS) && *EndS != ',') {
+	    EndS++;		/* Find separator */
+	}
+	StrAllocCopy(DomainSuffix, StartS);
+	DomainSuffix[EndS - StartS] = '\0';
+
+	/*
+	 * Create domain names and do DNS tests.  - FM
+	 */
+	do {
+	    StrAllocCopy(Host, DomainPrefix);
+	    StrAllocCat(Host, ((*Str == '.') ? (Str + 1) : Str));
+	    if (Host[strlen(Host) - 1] == '.') {
+		Host[strlen(Host) - 1] = '\0';
+	    }
+	    StrAllocCat(Host, DomainSuffix);
+	    if ((HostColon = strrchr(Host, ':')) != NULL &&
+		isdigit(UCH(HostColon[1]))) {
+		*HostColon = '\0';
+	    }
+	    StrAllocCopy(host, Host);
+	    HTUnEscape(host);
+	    if (LYCursesON) {
+		StrAllocCopy(MsgStr, WWW_FIND_MESSAGE);
+		StrAllocCat(MsgStr, host);
+		StrAllocCat(MsgStr, GUESSING_SEGMENT);
+		HTProgress(MsgStr);
+	    } else if (Startup && !dump_output_immediately) {
+		fprintf(stdout, "%s '%s'%s\n", WWW_FIND_MESSAGE, host, GUESSING_SEGMENT);
+	    }
+	    GotHost = (BOOL) (LYGetHostByName(host) != NULL);
+	    if (HostColon != NULL) {
+		*HostColon = ':';
+	    }
+	    if (GotHost == FALSE) {
+		/*
+		 * Give the user chance to interrupt lookup cycles.  - KW
+		 */
+		if (LYCursesON && (lynx_nsl_status == HT_INTERRUPTED)) {
+		    CTRACE((tfp,
+			    "LYExpandHostForURL: Interrupted while '%s' failed to resolve.\n",
+			    host));
+		    goto cleanup;	/* We didn't find a valid name. */
+		}
+
+		/*
+		 * Advance to the next suffix, or end of suffix list.  - FM
+		 */
+		StartS = ((*EndS == '\0') ? EndS : (EndS + 1));
+		while ((*StartS) && (WHITE(*StartS) || *StartS == ',')) {
+		    StartS++;	/* Skip whitespace and separators */
+		}
+		EndS = StartS;
+		while (*EndS && !WHITE(*EndS) && *EndS != ',') {
+		    EndS++;	/* Find separator */
+		}
+		LYstrncpy(DomainSuffix, StartS, (EndS - StartS));
+	    }
+	} while ((GotHost == FALSE) && (*DomainSuffix != '\0'));
+
+	if (GotHost == FALSE) {
+	    /*
+	     * Advance to the next prefix, or end of prefix list.  - FM
+	     */
+	    StartP = ((*EndP == '\0') ? EndP : (EndP + 1));
+	    while ((*StartP) && (WHITE(*StartP) || *StartP == ',')) {
+		StartP++;	/* Skip whitespace and separators */
+	    }
+	    EndP = StartP;
+	    while (*EndP && !WHITE(*EndP) && *EndP != ',') {
+		EndP++;		/* Find separator */
+	    }
+	    LYstrncpy(DomainPrefix, StartP, (EndP - StartP));
+	}
+    } while ((GotHost == FALSE) && (*DomainPrefix != '\0'));
+
+    /*
+     * If a test passed, restore the port field if we had one and there is no
+     * colon in the expanded host, and the path if we had one, and reallocate
+     * the original string with the expanded Host[:port] field included.  - FM
+     */
+    if (GotHost) {
+	if (StrColon && strchr(Host, ':') == NULL) {
+	    *StrColon = ':';
+	    StrAllocCat(Host, StrColon);
+	}
+	if (Path) {
+	    *Path = '/';
+	    StrAllocCat(Host, Path);
+	} else if (Fragment) {
+	    StrAllocCat(Host, "/");
+	    restorePoundSelector(Fragment);
+	    StrAllocCat(Host, Fragment);
+	}
+	StrAllocCopy(*AllocatedString, Host);
+    }
+
+    /*
+     * Clear any residual interrupt.  - FM
+     */
+    if (LYCursesON && HTCheckForInterrupt()) {
+	CTRACE((tfp,
+		"LYExpandHostForURL: Ignoring interrupt because '%s' %s.\n",
+		host,
+		(GotHost ? "resolved" : "timed out")));
+    }
+
+    /*
+     * Clean up and return the last test result.  - FM
+     */
+  cleanup:
+    FREE(DomainPrefix);
+    FREE(DomainSuffix);
+    FREE(Str);
+    FREE(MsgStr);
+    FREE(Host);
+    FREE(host);
+    return GotHost;
+}
+
+/*
+ * This function rewrites and reallocates a previously allocated string that
+ * begins with an Internet host name so that the string begins with its guess
+ * of the scheme based on the first field of the host name, or the default
+ * scheme if no guess was made, and returns TRUE, otherwise it does not modify
+ * the string and returns FALSE.  It also returns FALSE without modifying the
+ * string if the default_scheme argument was NULL or zero-length and no guess
+ * was made.  - FM
+ */
+BOOLEAN LYAddSchemeForURL(char **AllocatedString,
+			  const char *default_scheme)
+{
+    char *Str = NULL;
+    BOOLEAN GotScheme = FALSE;
+
+    /*
+     * If we were passed a NULL or zero-length string, don't continue
+     * pointlessly.  - FM
+     */
+    if (!(*AllocatedString) || *AllocatedString[0] == '\0') {
+	return GotScheme;
+    }
+
+    /*
+     * Try to guess the appropriate scheme. - FM
+     */
+    if (0 == strncasecomp(*AllocatedString, "www", 3)) {
+	/*
+	 * This could be either http or https, so check the default and
+	 * otherwise use "http".  - FM
+	 */
+	if (default_scheme != NULL &&
+	    NULL != strstr(default_scheme, "http")) {
+	    StrAllocCopy(Str, default_scheme);
+	} else {
+	    StrAllocCopy(Str, "http://");
+	}
+	GotScheme = TRUE;
+
+    } else if (0 == strncasecomp(*AllocatedString, "ftp", 3)) {
+	StrAllocCopy(Str, "ftp://");
+	GotScheme = TRUE;
+
+    } else if (0 == strncasecomp(*AllocatedString, "gopher", 6)) {
+	StrAllocCopy(Str, "gopher://");
+	GotScheme = TRUE;
+
+    } else if (0 == strncasecomp(*AllocatedString, "wais", 4)) {
+	StrAllocCopy(Str, "wais://");
+	GotScheme = TRUE;
+
+    } else if (0 == strncasecomp(*AllocatedString, "cso", 3) ||
+	       0 == strncasecomp(*AllocatedString, "ns.", 3) ||
+	       0 == strncasecomp(*AllocatedString, "ph.", 3)) {
+	StrAllocCopy(Str, "cso://");
+	GotScheme = TRUE;
+
+    } else if (0 == strncasecomp(*AllocatedString, "finger", 6)) {
+	StrAllocCopy(Str, "finger://");
+	GotScheme = TRUE;
+
+    } else if (0 == strncasecomp(*AllocatedString, "news", 4)) {
+	/*
+	 * This could be either news, snews, or nntp, so check the default, and
+	 * otherwise use news.  - FM
+	 */
+	if ((default_scheme != NULL) &&
+	    (NULL != strstr(default_scheme, "news") ||
+	     NULL != strstr(default_scheme, "nntp"))) {
+	    StrAllocCopy(Str, default_scheme);
+	} else {
+	    StrAllocCopy(Str, "news://");
+	}
+	GotScheme = TRUE;
+
+    } else if (0 == strncasecomp(*AllocatedString, "nntp", 4)) {
+	StrAllocCopy(Str, "nntp://");
+	GotScheme = TRUE;
+
+    }
+
+    /*
+     * If we've make a guess, use it.  Otherwise, if we were passed a default
+     * scheme prefix, use that.  - FM
+     */
+    if (GotScheme == TRUE) {
+	StrAllocCat(Str, *AllocatedString);
+	StrAllocCopy(*AllocatedString, Str);
+	FREE(Str);
+	return GotScheme;
+
+    } else if (non_empty(default_scheme)) {
+	StrAllocCopy(Str, default_scheme);
+	GotScheme = TRUE;
+	StrAllocCat(Str, *AllocatedString);
+	StrAllocCopy(*AllocatedString, Str);
+	FREE(Str);
+	return GotScheme;
+    }
+
+    return GotScheme;
+}
+
+/*
+ * This function expects an absolute Unix or VMS SHELL path spec as an
+ * allocated string, simplifies it, and trims out any residual relative
+ * elements.  It also checks whether the path had a terminal slash, and if it
+ * didn't, makes sure that the simplified path doesn't either.  If it's a
+ * directory, our convention is to exclude "Up to parent" links when a terminal
+ * slash is present.  - FM
+ */
+void LYTrimRelFromAbsPath(char *path)
+{
+    char *cp;
+    int i;
+    BOOL TerminalSlash;
+
+    /*
+     * Make sure we have a pointer to an absolute path.  - FM
+     */
+    if (path == NULL || !LYIsPathSep(*path))
+	return;
+
+    /*
+     * Check whether the path has a terminal slash.  - FM
+     */
+    TerminalSlash = (BOOL) (LYIsPathSep(path[(strlen(path) - 1)]));
+
+    /*
+     * Simplify the path and then do any necessary trimming.  - FM
+     */
+    HTSimplify(path);
+    cp = path;
+    while (cp[1] == '.') {
+	if (cp[2] == '\0') {
+	    /*
+	     * Eliminate trailing dot.  - FM
+	     */
+	    cp[1] = '\0';
+	} else if (LYIsPathSep(cp[2])) {
+	    /*
+	     * Skip over the "/." of a "/./".  - FM
+	     */
+	    cp += 2;
+	} else if (cp[2] == '.' && cp[3] == '\0') {
+	    /*
+	     * Eliminate trailing dotdot.  - FM
+	     */
+	    cp[1] = '\0';
+	} else if (cp[2] == '.' && cp[3] == '/') {
+	    /*
+	     * Skip over the "/.." of a "/../".  - FM
+	     */
+	    cp += 3;
+	} else {
+	    /*
+	     * Done trimming.  - FM
+	     */
+	    break;
+	}
+    }
+
+    /*
+     * Load any shifts into path, and eliminate any terminal slash created by
+     * HTSimplify() or our walk, but not present originally.  - FM
+     */
+    if (cp > path) {
+	for (i = 0; cp[i] != '\0'; i++)
+	    path[i] = cp[i];
+	path[i] = '\0';
+    }
+    if (TerminalSlash == FALSE) {
+	LYTrimPathSep(path);
+    }
+}
+
+/*
+ * Example Client-Side Include interface.
+ *
+ * This is called from SGML.c and simply returns markup for reporting the URL
+ * of the document being loaded if a comment begins with "<!--#lynxCSI".  The
+ * markup will be included as if it were in the document.  Move this function
+ * to a separate module for doing this kind of thing seriously, someday.  - FM
+ */
+void LYDoCSI(char *url,
+	     const char *comment,
+	     char **csi)
+{
+    const char *cp = comment;
+
+    if (cp == NULL)
+	return;
+
+    if (strncmp(cp, "!--#", 4))
+	return;
+
+    cp += 4;
+    if (!strncasecomp(cp, "lynxCSI", 7)) {
+	StrAllocCat(*csi, "\n<p align=\"center\">URL: ");
+	StrAllocCat(*csi, url);
+	StrAllocCat(*csi, "</p>\n\n");
+    }
+
+    return;
+}
+
+#ifdef VMS
+/*
+ * Define_VMSLogical -- Fote Macrides 04-Apr-1995
+ * Define VMS logicals in the process table.
+ */
+void Define_VMSLogical(char *LogicalName,
+		       char *LogicalValue)
+{
+    $DESCRIPTOR(lname, "");
+    $DESCRIPTOR(lvalue, "");
+    $DESCRIPTOR(ltable, "LNM$PROCESS");
+
+    if (isEmpty(LogicalName))
+	return;
+
+    lname.dsc$w_length = strlen(LogicalName);
+    lname.dsc$a_pointer = LogicalName;
+
+    if (isEmpty(LogicalValue)) {
+	lib$delete_logical(&lname, &ltable);
+	return;
+    }
+
+    lvalue.dsc$w_length = strlen(LogicalValue);
+    lvalue.dsc$a_pointer = LogicalValue;
+    lib$set_logical(&lname, &lvalue, &ltable, 0, 0);
+    return;
+}
+#endif /* VMS */
+
+#ifdef LY_FIND_LEAKS
+static void LYHomeDir_free(void)
+{
+    FREE(HomeDir);
+}
+#endif /* LY_FIND_LEAKS */
+
+char *Current_Dir(char *pathname)
+{
+    char *result;
+
+#ifdef HAVE_GETCWD
+    result = getcwd(pathname, LY_MAXPATH);
+#else
+    result = getwd(pathname);
+#endif /* NO_GETCWD */
+    if (result == 0)
+	strcpy(pathname, ".");
+    return pathname;
+}
+
+/*
+ * Verify that the given path refers to an existing directory, returning the
+ * string if the directory exists.  If not, return null.
+ */
+static char *CheckDir(char *path)
+{
+    struct stat stat_info;
+
+    if (!LYisAbsPath(path)
+	|| (HTStat(path, &stat_info) < 0
+	    || !S_ISDIR(stat_info.st_mode))) {
+	path = NULL;
+    }
+    return path;
+}
+
+/*
+ * Lookup various possibilities for $HOME, and check that the directory exists.
+ */
+static char *HomeEnv(void)
+{
+    char *result = CheckDir(LYGetEnv("HOME"));
+
+#if defined (USE_DOS_DRIVES)
+    if (result == 0) {
+	char *head;
+	char *leaf;
+	static char *temp = NULL;
+
+	/* Windows 2000 */
+	if ((result = LYGetEnv("USERPROFILE")) != 0) {
+	    HTSprintf0(&temp, "%s%sMy Documents", result, PATHSEP_STR);
+	    result = CheckDir(temp);
+	}
+	/* NT4 */
+	if (result == 0) {
+	    if ((head = LYGetEnv("HOMEDRIVE")) != 0) {
+		if ((leaf = LYGetEnv("HOMEPATH")) != 0) {
+		    HTSprintf0(&temp, "%s%s%s", head, PATHSEP_STR, leaf);
+		    result = CheckDir(temp);
+		}
+	    }
+	}
+	/* General M$ */
+	if (result == 0)
+	    result = CheckDir(LYGetEnv("TEMP"));
+	if (result == 0)
+	    result = CheckDir(LYGetEnv("TMP"));
+	if (result == 0) {
+	    if ((head = LYGetEnv("SystemDrive")) != 0) {
+		HTSprintf0(&temp, "%s%s", head, PATHSEP_STR);
+		result = CheckDir(temp);
+	    }
+	}
+	if (result == 0)
+	    result = CheckDir("C:" PATHSEP_STR);
+    }
+#endif
+
+    return result;
+}
+
+const char *Home_Dir(void)
+{
+    static const char *homedir = NULL;
+    char *cp = NULL;
+
+    if (homedir == NULL) {
+	if ((cp = HomeEnv()) == NULL) {
+#ifdef VMS
+	    if ((cp = LYGetEnv("SYS$LOGIN")) == NULL
+		&& (cp = LYGetEnv("SYS$SCRATCH")) == NULL) {
+		cp = "sys$scratch:";
+	    }
+	    StrAllocCopy(HomeDir, cp);
+#else
+#ifdef UNIX
+#ifdef HAVE_UTMP
+	    /*
+	     * One could use getlogin() and getpwnam() here instead.
+	     */
+	    struct passwd *pw = getpwuid(geteuid());
+
+	    if (pw && pw->pw_dir) {
+		StrAllocCopy(HomeDir, pw->pw_dir);
+	    } else
+#endif
+	    {
+		/*
+		 * Use /tmp; it should be writable.
+		 */
+		StrAllocCopy(HomeDir, "/tmp");
+	    }
+#endif
+#endif /* VMS */
+	} else {
+	    StrAllocCopy(HomeDir, cp);
+	}
+	homedir = (const char *) HomeDir;
+#ifdef LY_FIND_LEAKS
+	atexit(LYHomeDir_free);
+#endif
+    }
+    if (homedir == NULL) {
+	printf("%s\n", gettext("Cannot find HOME directory"));
+	exit_immediately(EXIT_FAILURE);
+    }
+    return homedir;
+}
+
+/*
+ * Return a pointer to the final leaf of the given pathname, If no pathname
+ * separators are found, returns the original pathname.  The leaf may be
+ * empty.
+ */
+char *LYPathLeaf(char *pathname)
+{
+    char *leaf;
+
+#ifdef UNIX
+    if ((leaf = strrchr(pathname, '/')) != 0) {
+	leaf++;
+    }
+#else
+#ifdef VMS
+    if ((leaf = strrchr(pathname, ']')) == 0)
+	leaf = strrchr(pathname, ':');
+    if (leaf != 0)
+	leaf++;
+#else
+    int n;
+
+    for (leaf = 0, n = strlen(pathname) - 1; n >= 0; n--) {
+	if (strchr("\\/:", pathname[n]) != 0) {
+	    leaf = pathname + n + 1;
+	    break;
+	}
+    }
+#endif
+#endif
+    return (leaf != 0) ? leaf : pathname;
+}
+
+/*
+ * This function checks the acceptability of file paths that are intended to be
+ * off the home directory.  The file path should be passed in fbuffer, together
+ * with the size of the buffer.  The function simplifies the file path, and if
+ * it is acceptable, loads it into fbuffer and returns TRUE.  Otherwise, it
+ * does not modify fbuffer and returns FALSE.  If a subdirectory is present and
+ * the path does not begin with "./", that is prefixed to make the situation
+ * clear.  - FM
+ */
+BOOLEAN LYPathOffHomeOK(char *fbuffer,
+			size_t fbuffer_size)
+{
+    char *file = NULL;
+    char *cp, *cp1;
+
+    /*
+     * Make sure we have an fbuffer and a string in it.  - FM
+     */
+    if (fbuffer_size < 2 || isEmpty(fbuffer)) {
+	return (FALSE);
+    }
+    StrAllocCopy(file, fbuffer);
+    cp = file;
+
+    /*
+     * Check for an inappropriate reference to the home directory, and correct
+     * it if we can.  - FM
+     */
+#ifdef VMS
+    if (!strncasecomp(cp, "sys$login", 9)) {
+	if (*(cp + 9) == '\0') {
+	    /*
+	     * Reject "sys$login".  - FM
+	     */
+	    FREE(file);
+	    return (FALSE);
+	}
+	if (*(cp + 9) == ':') {
+	    cp += 10;
+	    if (*cp == '\0') {
+		/*
+		 * Reject "sys$login:".  Otherwise, we have converted
+		 * "sys$login:file" to "file", or have left a strange path for
+		 * VMS as it was originally.  - FM
+		 */
+		FREE(file);
+		return (FALSE);
+	    }
+	}
+    }
+#endif /* VMS */
+    if (LYIsTilde(cp[0])) {
+	if (LYIsPathSep(cp[1])) {
+	    if (cp[2] != '\0') {
+		if (strchr((cp + 2), '/') != NULL) {
+		    /*
+		     * Convert "~/subdir(s)/file" to "./subdir(s)/file".  - FM
+		     */
+		    *cp = '.';
+		} else {
+		    /*
+		     * Convert "~/file" to "file".  - FM
+		     */
+		    cp += 2;
+		}
+	    } else {
+		/*
+		 * Reject "~/".  - FM
+		 */
+		FREE(file);
+		return (FALSE);
+	    }
+	} else if ((*(cp + 1) != '\0') &&
+		   (cp1 = strchr((cp + 1), '/')) != NULL) {
+	    cp = (cp1 - 1);
+	    if (*(cp + 2) != '\0') {
+		if (strchr((cp + 2), '/') != NULL) {
+		    /*
+		     * Convert "~user/subdir(s)/file" to "./subdir(s)/file". 
+		     * If user is someone else, we covered a spoof.  Otherwise,
+		     * we simplified.  - FM
+		     */
+		    *cp = '.';
+		} else {
+		    /*
+		     * Convert "~user/file" to "file".  - FM
+		     */
+		    cp += 2;
+		}
+	    } else {
+		/*
+		 * Reject "~user/".  - FM
+		 */
+		FREE(file);
+		return (FALSE);
+	    }
+	} else {
+	    /*
+	     * Reject "~user".  - FM
+	     */
+	    FREE(file);
+	    return (FALSE);
+	}
+    }
+#ifdef VMS
+    /*
+     * Check for VMS path specs, and reject if still present.  - FM
+     */
+    if (strchr(cp, ':') != NULL || strchr(cp, ']') != NULL) {
+	FREE(file);
+	return (FALSE);
+    }
+#endif /* VMS */
+
+    /*
+     * Check for a URL or absolute path, and reject if present.  - FM
+     */
+    if (is_url(cp) || LYIsPathSep(*cp)) {
+	FREE(file);
+	return (FALSE);
+    }
+
+    /*
+     * Simplify it.  - FM
+     */
+    HTSimplify(cp);
+
+    /*
+     * Check if it has a pointless "./".  - FM
+     */
+    if (!strncmp(cp, "./", 2)) {
+	if (strchr((cp + 2), '/') == NULL) {
+	    cp += 2;
+	}
+    }
+
+    /*
+     * Check for spoofing.  - FM
+     */
+    if (*cp == '\0'
+	|| LYIsPathSep(*cp)
+	|| LYIsPathSep(cp[(strlen(cp) - 1)])
+	|| strstr(cp, "..") != NULL
+	|| !strcmp(cp, ".")) {
+	FREE(file);
+	return (FALSE);
+    }
+
+    /*
+     * Load what we have at this point into fbuffer, trimming if too long, and
+     * claim it's OK.  - FM
+     */
+    if (fbuffer_size > 3 && strncmp(cp, "./", 2) && strchr(cp, '/')) {
+	/*
+	 * We have a subdirectory and no lead "./", so prefix it to make the
+	 * situation clear.  - FM
+	 */
+	strcpy(fbuffer, "./");
+	if (strlen(cp) > (fbuffer_size - 3))
+	    cp[(fbuffer_size - 3)] = '\0';
+	strcat(fbuffer, cp);
+    } else {
+	if (strlen(cp) > (fbuffer_size - 1))
+	    cp[(fbuffer_size - 1)] = '\0';
+	strcpy(fbuffer, cp);
+    }
+    FREE(file);
+    return (TRUE);
+}
+
+/*
+ * Search for a leading tilde, optionally embedded.  If found, return a pointer
+ * to the tilde.  If not found, return the original parameter.
+ */
+static char *FindLeadingTilde(char *pathname, BOOL embedded)
+{
+    char *result = pathname;
+
+    if (pathname != NULL) {
+	if (embedded) {
+	    while (pathname[0] != '\0') {
+		if (LYIsPathSep(pathname[0])) {
+		    if (LYIsTilde(pathname[1])) {
+			++pathname;
+			break;
+		    }
+		}
+		++pathname;
+	    }
+	}
+	if (LYIsTilde(*pathname))
+	    result = pathname;
+    }
+    return result;
+}
+
+/*
+ * Convert a non-absolute path to one which is off the home directory.  Expand
+ * tildes as a side-effect.  Return a pointer to the converted result.
+ */
+char *LYAbsOrHomePath(char **fname)
+{
+    if (!LYisAbsPath(*fname)) {
+	if (LYIsTilde((*fname)[0])) {
+	    LYTildeExpand(fname, FALSE);
+	} else {
+	    char temp[LY_MAXPATH];
+
+	    LYAddPathToHome(temp, sizeof(temp), *fname);
+	    StrAllocCopy(*fname, temp);
+	}
+    }
+    return *fname;
+}
+
+/*
+ * Expand a "leading" tilde into the user's home directory in WWW format.  If
+ * "embedded" is true, allow that "leading" tilde to follow a path separator.
+ */
+char *LYTildeExpand(char **pathname,
+		    BOOL embedded)
+{
+    char *temp = FindLeadingTilde(*pathname, embedded);
+
+    if (LYIsTilde(temp[0])) {
+
+	CTRACE((tfp, "LYTildeExpand %s\n", *pathname));
+	if (LYIsPathSep(temp[1])) {
+	    char *first = NULL;
+	    char *second = NULL;
+
+	    StrAllocCopy(first, *pathname);
+	    first[temp - *pathname] = '\0';
+
+	    StrAllocCopy(second, temp + 2);
+
+	    StrAllocCopy(*pathname, first);
+	    StrAllocCat(*pathname, wwwName(Home_Dir()));
+	    LYAddPathSep(pathname);
+	    StrAllocCat(*pathname, second);
+
+	    FREE(first);
+	    FREE(second);
+	} else if (temp[1] == '\0') {
+	    StrAllocCopy(*pathname, wwwName(Home_Dir()));
+	}
+	CTRACE((tfp, "expanded path %s\n", *pathname));
+    }
+    return *pathname;
+}
+
+/*
+ * This function appends fname to the home path and returns the full path and
+ * filename.  The fname string can be just a filename (e.g.,
+ * "lynx_bookmarks.html"), or include a subdirectory off the home directory, in
+ * which case fname should begin with "./" (e.g., ./BM/lynx_bookmarks.html) Use
+ * LYPathOffHomeOK() to check and/or fix up fname before calling this function. 
+ * On VMS, the resultant full path and filename are converted to VMS syntax.  -
+ * FM
+ */
+void LYAddPathToHome(char *fbuffer,
+		     size_t fbuffer_size,
+		     const char *fname)
+{
+    char *home = NULL;
+    const char *file = fname;
+    int len;
+
+    /*
+     * Make sure we have a buffer.  - FM
+     */
+    if (!fbuffer)
+	return;
+    if (fbuffer_size < 2) {
+	fbuffer[0] = '\0';
+	return;
+    }
+    fbuffer[(fbuffer_size - 1)] = '\0';
+
+    /*
+     * Make sure we have a file name.  - FM
+     */
+    if (!file)
+	file = "";
+
+    /*
+     * Set up home string and length.  - FM
+     */
+    StrAllocCopy(home, Home_Dir());
+
+#ifdef VMS
+#define NO_HOMEPATH "Error:"
+#else
+#define NO_HOMEPATH "/error"
+#endif /* VMS */
+    if (!non_empty(home))
+	/*
+	 * Home_Dir() has a bug if this ever happens.  - FM
+	 */
+	StrAllocCopy(home, NO_HOMEPATH);
+
+    len = (int) fbuffer_size - ((int) strlen(home) + 1);
+    if (len <= 0) {
+	/*
+	 * Buffer is smaller than or only big enough for the home path.  Load
+	 * what fits of the home path and return.  This will fail, but we need
+	 * something in the buffer.  - FM
+	 */
+	LYstrncpy(fbuffer, home, (int) (fbuffer_size - 1));
+	FREE(home);
+	return;
+    }
+#ifdef VMS
+    /*
+     * Check whether we have a subdirectory path or just a filename.  - FM
+     */
+    if (!strncmp(file, "./", 2)) {
+	/*
+	 * We have a subdirectory path.  - FM
+	 */
+	if (home[strlen(home) - 1] == ']') {
+	    /*
+	     * We got the home directory, so convert it to SHELL syntax and
+	     * append subdirectory path, then convert that to VMS syntax.  - FM
+	     */
+	    char *temp = NULL;
+
+	    HTSprintf0(&temp, "%s%s", HTVMS_wwwName(home), (file + 1));
+	    sprintf(fbuffer, "%.*s",
+		    (fbuffer_size - 1), HTVMS_name("", temp));
+	    FREE(temp);
+	} else {
+	    /*
+	     * This will fail, but we need something in the buffer.  - FM
+	     */
+	    sprintf(fbuffer, "%s%.*s", home, len, file);
+	}
+    } else {
+	/*
+	 * We have a file in the home directory.  - FM
+	 */
+	sprintf(fbuffer, "%s%.*s", home, len, file);
+    }
+#else
+    /*
+     * Check whether we have a subdirectory path or just a filename.  - FM
+     */
+    sprintf(fbuffer, "%s/%.*s", home, len,
+	    (strncmp(file, "./", 2) ? file : (file + 2)));
+#endif /* VMS */
+    FREE(home);
+}
+
+/*
+ * Given a filename, concatenate it to the save-space pathname, unless it is
+ * an absolute pathname.  If there is no save-space defined, use the home
+ * directory. Return a new string with the result.
+ */
+char *LYAddPathToSave(char *fname)
+{
+    char *result = NULL;
+
+    if (LYisAbsPath(fname)) {
+	StrAllocCopy(result, fname);
+    } else {
+	if (lynx_save_space != NULL) {
+	    StrAllocCopy(result, lynx_save_space);
+	} else {
+	    char temp[LY_MAXPATH];
+
+	    LYAddPathToHome(temp, sizeof(temp), fname);
+	    StrAllocCopy(result, temp);
+	}
+    }
+    return result;
+}
+
+#if !defined(HAVE_PUTENV) && !defined(_WINDOWS)
+/*
+ * No putenv on the NeXT so we use this code instead!
+ */
+
+/* Copyright (C) 1991 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can  redistribute it and/or
+modify it under the terms of the GNU Library General  Public License as
+published by the Free Software Foundation; either  version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it  will be useful,
+but WITHOUT ANY WARRANTY; without even the implied  warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library  General Public
+License along with the GNU C Library; see the file  COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675  Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#if defined(STDC_HEADERS) || defined(USG)
+#include <string.h>
+#else /* Not (STDC_HEADERS or USG): */
+#include <strings.h>
+#endif /* STDC_HEADERS or USG */
+
+#ifndef NULL
+#define NULL 0
+#endif /* !NULL */
+
+extern char **environ;
+
+/*
+ * Put STRING, which is of the form "NAME=VALUE", in the environment.
+ */
+int putenv(const char *string)
+{
+    char *name_end = strchr(string, '=');
+    register size_t size;
+    register char **ep;
+
+    if (name_end == NULL) {
+	/* Remove the variable from the environment.  */
+	size = strlen(string);
+	for (ep = environ; *ep != NULL; ++ep)
+	    if (!strncmp(*ep, string, size) && (*ep)[size] == '=') {
+		while (ep[1] != NULL) {
+		    ep[0] = ep[1];
+		    ++ep;
+		}
+		*ep = NULL;
+		return 0;
+	    }
+    }
+
+    size = 0;
+    for (ep = environ; *ep != NULL; ++ep)
+	if (!strncmp(*ep, string, name_end - string) &&
+	    (*ep)[name_end - string] == '=')
+	    break;
+	else
+	    ++size;
+
+    if (*ep == NULL) {
+	static char **last_environ = NULL;
+	char **new_environ = (char **) malloc((size + 2) * sizeof(char *));
+
+	if (new_environ == NULL)
+	    return -1;
+	(void) memcpy((char *) new_environ, (char *) environ, size * sizeof(char *));
+
+	new_environ[size] = (char *) string;
+	new_environ[size + 1] = NULL;
+	if (last_environ != NULL)
+	    FREE(last_environ);
+	last_environ = new_environ;
+	environ = new_environ;
+    } else
+	*ep = (char *) string;
+
+    return 0;
+}
+#endif /* !HAVE_PUTENV */
+
+#ifdef NEED_REMOVE
+int remove(char *name)
+{
+    return unlink(name);
+}
+#endif
+
+#if defined(MULTI_USER_UNIX)
+
+#if defined(HAVE_LSTAT) && defined(S_IFLNK)
+/*
+ * If IsOurFile() is checking a symbolic link, ensure that the target
+ * points to the user's file as well.
+ */
+static BOOL IsOurSymlink(const char *name)
+{
+    BOOL result = FALSE;
+    int size = LY_MAXPATH;
+    int used;
+    char *buffer = typeMallocn(char, (unsigned) size);
+
+    if (buffer != 0) {
+	while ((used = readlink(name, buffer, (unsigned) (size - 1))) == size
+	       - 1) {
+	    buffer = typeRealloc(char, buffer, (unsigned) (size *= 2));
+
+	    if (buffer == 0)
+		break;
+	}
+	if (buffer != 0) {
+	    if (used > 0) {
+		buffer[used] = '\0';
+	    } else {
+		FREE(buffer);
+	    }
+	}
+    }
+    if (buffer != 0) {
+	if (!LYisAbsPath(buffer)) {
+	    char *cutoff = LYLastPathSep(name);
+	    char *clone = NULL;
+
+	    if (cutoff != 0) {
+		HTSprintf0(&clone, "%.*s%s%s",
+			   (int) (cutoff - name),
+			   name, PATHSEP_STR, buffer);
+		FREE(buffer);
+		buffer = clone;
+	    }
+	}
+	CTRACE2(TRACE_CFG, (tfp, "IsOurSymlink(%s -> %s)\n", name, buffer));
+	result = IsOurFile(buffer);
+	FREE(buffer);
+    }
+    return result;
+}
+#endif
+
+/*
+ * Verify if this is really a file, not accessed by a link, except for the
+ * special case of its directory being pointed to by a link from a directory
+ * owned by root and not writable by other users.
+ */
+BOOL IsOurFile(const char *name)
+{
+    BOOL result = FALSE;
+    struct stat data;
+
+    if (!LYIsTilde(name[0])
+	&& lstat(name, &data) == 0
+	&& ((S_ISREG(data.st_mode)
+	     && (data.st_mode & (S_IWOTH | S_IWGRP)) == 0
+	     && data.st_nlink == 1
+	     && data.st_uid == getuid())
+#if defined(HAVE_LSTAT) && defined(S_IFLNK)
+	    || (S_ISLNK(data.st_mode) && IsOurSymlink(name))
+#endif
+	)) {
+	int linked = FALSE;
+
+	/*
+	 * ( If this is not a single-user system, the other user is presumed by
+	 * some people busy trying to use a symlink attack on our files ;-)
+	 */
+#if defined(HAVE_LSTAT)
+	char *path = 0;
+	char *leaf;
+
+	StrAllocCopy(path, name);
+	do {
+	    if ((leaf = LYPathLeaf(path)) != path)
+		*--leaf = '\0';	/* write a null on the '/' */
+	    if (lstat(*path ? path : "/", &data) != 0) {
+		break;
+	    }
+	    /*
+	     * If we find a symbolic link, it has to be in a directory that's
+	     * protected.  Otherwise someone could have switched it to point
+	     * to one of the real user's files.
+	     */
+	    if (S_ISLNK(data.st_mode)) {
+		linked = TRUE;	/* could be link-to-link; doesn't matter */
+	    } else if (S_ISDIR(data.st_mode)) {
+		if (linked) {
+		    linked = FALSE;
+		    /*
+		     * We assume that a properly-configured system has the
+		     * unwritable directories owned by root.  This is not
+		     * necessarily so (bin, news, etc., may), but the only
+		     * uid we can count on is 0.  It would be nice to add a
+		     * check for the gid also, but that wouldn't be
+		     * portable.
+		     */
+		    if (data.st_uid != 0
+			|| (data.st_mode & S_IWOTH) != 0) {
+			linked = TRUE;	/* force an error-return */
+			break;
+		    }
+		}
+	    } else if (linked) {
+		break;
+	    }
+	} while (leaf != path);
+	FREE(path);
+#endif
+	result = (BOOLEAN) !linked;
+    }
+    CTRACE2(TRACE_CFG, (tfp, "IsOurFile(%s) %d\n", name, result));
+    return result;
+}
+
+/*
+ * Open a file that we don't want other users to see.
+ */
+static FILE *OpenHiddenFile(const char *name, const char *mode)
+{
+    FILE *fp = 0;
+    struct stat data;
+    BOOLEAN binary = (BOOLEAN) (strchr(mode, 'b') != 0);
+
+#if defined(O_CREAT) && defined(O_EXCL)		/* we have fcntl.h or kindred? */
+    /*
+     * This is the preferred method for creating new files, since it ensures
+     * that no one has an existing file or link that they happen to own.
+     */
+    if (*mode == 'w') {
+	int fd = open(name, O_CREAT | O_EXCL | O_WRONLY, HIDE_CHMOD);
+
+	if (fd < 0
+	    && errno == EEXIST
+	    && IsOurFile(name)) {
+	    remove(name);
+	    /* FIXME: there's a race at this point if directory is open */
+	    fd = open(name, O_CREAT | O_EXCL | O_WRONLY, HIDE_CHMOD);
+	}
+	if (fd >= 0) {
+#if defined(O_BINARY) && defined(__CYGWIN__)
+	    if (binary)
+		setmode(fd, O_BINARY);
+#endif
+	    fp = fdopen(fd, mode);
+	}
+    } else
+#endif
+    if (*mode == 'a') {
+	if (IsOurFile(name)
+	    && chmod(name, HIDE_CHMOD) == 0)
+	    fp = fopen(name, mode);
+	else if (lstat(name, &data) != 0)
+	    fp = OpenHiddenFile(name, binary ? BIN_W : TXT_W);
+	/*
+	 * This is less stringent, but reasonably portable.  For new files, the
+	 * umask will suffice; however if the file already exists we'll change
+	 * permissions first, before opening it.  If the chmod fails because of
+	 * some reason other than a non-existent file, there's no point in trying
+	 * to open it.
+	 *
+	 * This won't work properly if the user is root, since the chmod succeeds.
+	 */
+    } else if (*mode != 'a') {
+	mode_t save = umask(HIDE_UMASK);
+
+	if (chmod(name, HIDE_CHMOD) == 0 || errno == ENOENT)
+	    fp = fopen(name, mode);
+	umask(save);
+    }
+    return fp;
+}
+#else
+#define OpenHiddenFile(name, mode) fopen(name, mode)
+#endif /* MULTI_USER_UNIX */
+
+FILE *LYNewBinFile(const char *name)
+{
+#ifdef VMS
+    FILE *fp = fopen(name, BIN_W, "mbc=32");
+
+    chmod(name, HIDE_CHMOD);
+#else
+    FILE *fp = OpenHiddenFile(name, BIN_W);
+#endif
+    return fp;
+}
+
+FILE *LYNewTxtFile(const char *name)
+{
+    FILE *fp;
+
+#ifdef VMS
+    fp = fopen(name, TXT_W, "shr=get");
+    chmod(name, HIDE_CHMOD);
+#else
+    SetDefaultMode(O_TEXT);
+
+    fp = OpenHiddenFile(name, TXT_W);
+
+    SetDefaultMode(O_BINARY);
+#endif
+
+    return fp;
+}
+
+FILE *LYAppendToTxtFile(const char *name)
+{
+    FILE *fp;
+
+#ifdef VMS
+    fp = fopen(name, TXT_A, "shr=get");
+    chmod(name, HIDE_CHMOD);
+#else
+    SetDefaultMode(O_TEXT);
+
+    fp = OpenHiddenFile(name, TXT_A);
+
+    SetDefaultMode(O_BINARY);
+#endif
+    return fp;
+}
+
+#if defined(MULTI_USER_UNIX)
+/*
+ * Restore normal permissions to a copy of a file that we have created with
+ * temp file restricted permissions.  The normal umask should apply for user
+ * files.  - kw
+ */
+void LYRelaxFilePermissions(const char *name)
+{
+    mode_t mode;
+    struct stat stat_buf;
+
+    if (stat(name, &stat_buf) == 0 &&
+	S_ISREG(stat_buf.st_mode) &&
+	(mode = (stat_buf.st_mode & 0777)) == HIDE_CHMOD) {
+	/*
+	 * It looks plausible that this is a file we created with temp file
+	 * paranoid permissions (and the umask wasn't even more restrictive
+	 * when it was copied).  - kw
+	 */
+	mode_t save = umask(HIDE_UMASK);
+
+	mode = ((mode & 0700) | 0066) & ~save;
+	umask(save);
+	chmod(name, mode);
+    }
+}
+#endif
+
+/*
+ * Check if the given anchor has an associated file-cache.
+ */
+BOOLEAN LYCachedTemp(char *result,
+		     char **cached)
+{
+    if (*cached) {
+	LYstrncpy(result, *cached, LY_MAXPATH);
+	FREE(*cached);
+	if (LYCanReadFile(result)) {
+	    remove(result);
+	}
+	return TRUE;
+    }
+    return FALSE;
+}
+
+#ifndef HAVE_MKDTEMP
+#define mkdtemp(path) ((mktemp(path) != 0) && (mkdir(path, 0700) == 0))
+#endif
+
+/*
+ * Open a temp-file, ensuring that it is unique, and not readable by other
+ * users.
+ *
+ * The mode can be one of: "w", "a", "wb".
+ */
+FILE *LYOpenTemp(char *result,
+		 const char *suffix,
+		 const char *mode)
+{
+    FILE *fp = 0;
+    BOOL txt = TRUE;
+    char wrt = 'r';
+    LY_TEMP *p;
+
+    CTRACE((tfp, "LYOpenTemp(,%s,%s)\n", suffix, mode));
+    if (result == 0)
+	return 0;
+
+    while (*mode != '\0') {
+	switch (*mode++) {
+	case 'w':
+	    wrt = 'w';
+	    break;
+	case 'a':
+	    wrt = 'a';
+	    break;
+	case 'b':
+	    txt = FALSE;
+	    break;
+	default:
+	    CTRACE((tfp, "%s @%d: BUG\n", __FILE__, __LINE__));
+	    return 0;
+	}
+    }
+
+    /*
+     * Verify if the given space looks secure enough.  Otherwise, make a
+     * secure subdirectory of that.
+     */
+#if defined(MULTI_USER_UNIX) && (defined(HAVE_MKTEMP) || defined(HAVE_MKDTEMP))
+    if (lynx_temp_subspace == 0) {
+	BOOL make_it = FALSE;
+	struct stat sb;
+
+	if (lstat(lynx_temp_space, &sb) == 0
+	    && S_ISDIR(sb.st_mode)) {
+	    if (sb.st_uid != getuid()
+		|| (sb.st_mode & (S_IWOTH | S_IWGRP)) != 0) {
+		make_it = TRUE;
+		CTRACE((tfp,
+			"lynx_temp_space is not our directory %s owner %d mode %03o\n",
+			lynx_temp_space, (int) sb.st_uid, (int) sb.st_mode & 0777));
+	    }
+	} else {
+	    make_it = TRUE;
+	    CTRACE((tfp, "lynx_temp_space is not a directory %s\n", lynx_temp_space));
+	}
+	if (make_it) {
+	    mode_t old_mask = umask(HIDE_UMASK);
+
+	    StrAllocCat(lynx_temp_space, "lynxXXXXXXXXXX");
+	    if (mkdtemp(lynx_temp_space) == 0) {
+		printf("%s: %s\n", lynx_temp_space, LYStrerror(errno));
+		exit_immediately(EXIT_FAILURE);
+	    }
+	    umask(old_mask);
+	    lynx_temp_subspace = 1;
+	    StrAllocCat(lynx_temp_space, "/");
+	    CTRACE((tfp, "made subdirectory %s\n", lynx_temp_space));
+	} else {
+	    lynx_temp_subspace = -1;
+	}
+    }
+#endif
+
+    do {
+	if (!fmt_tempname(result, lynx_temp_space, suffix))
+	    return 0;
+	if (txt) {
+	    switch (wrt) {
+	    case 'w':
+		fp = LYNewTxtFile(result);
+		break;
+	    case 'a':
+		fp = LYAppendToTxtFile(result);
+		break;
+	    }
+	} else {
+	    fp = LYNewBinFile(result);
+	}
+	/*
+	 * If we get a failure to make a temporary file, don't bother to try a
+	 * different name unless the failure was because the file already
+	 * exists.
+	 */
+#ifdef EEXIST			/* FIXME (need a better test) in fcntl.h or unistd.h */
+	if ((fp == 0) && (errno != EEXIST)) {
+	    CTRACE((tfp, "... LYOpenTemp(%s) failed: %s\n",
+		    result, LYStrerror(errno)));
+	    return 0;
+	}
+#endif
+    } while (fp == 0);
+
+    if ((p = typecalloc(LY_TEMP)) != 0) {
+	p->next = ly_temp;
+	StrAllocCopy((p->name), result);
+	p->file = fp;
+	p->outs = (BOOLEAN) (wrt != 'r');
+	ly_temp = p;
+    } else {
+	outofmem(__FILE__, "LYOpenTemp");
+    }
+
+    CTRACE((tfp, "... LYOpenTemp(%s)\n", result));
+    return fp;
+}
+
+/*
+ * Reopen a temporary file
+ */
+FILE *LYReopenTemp(char *name)
+{
+    LY_TEMP *p;
+    FILE *fp = 0;
+
+    LYCloseTemp(name);
+    if ((p = FindTempfileByName(name)) != 0) {
+	fp = p->file = LYAppendToTxtFile(name);
+    }
+    return fp;
+}
+
+/*
+ * Open a temp-file for writing, possibly re-using a previously used
+ * name and file.
+ * If a non-empty fname is given, it is reused if it indicates a file
+ * previously registered as a temp file and, in case the file still
+ * exists, if it looks like we can write to it safely.  Otherwise a
+ * new temp file (with new name) will be generated and returned in fname.
+ *
+ * File permissions are set so that the file is not readable by unprivileged
+ * other users.
+ *
+ * Suffix is only used if fname is not being reused.
+ * The mode should be "w", others are possible (they may be passed on)
+ * but probably don't make sense. - kw
+ */
+FILE *LYOpenTempRewrite(char *fname,
+			const char *suffix,
+			const char *mode)
+{
+    FILE *fp = 0;
+    BOOL txt = TRUE;
+    char wrt = 'r';
+    BOOL registered = NO;
+    BOOL writable_exists = NO;
+    BOOL is_ours = NO;
+    BOOL still_open = NO;
+    LY_TEMP *p;
+    struct stat stat_buf;
+
+    CTRACE((tfp, "LYOpenTempRewrite(%s,%s,%s)\n", fname, suffix, mode));
+    if (*fname == '\0')		/* first time, no filename yet */
+	return (LYOpenTemp(fname, suffix, mode));
+
+    if ((p = FindTempfileByName(fname)) != 0) {
+	registered = YES;
+	if (p->file != 0)
+	    still_open = YES;
+	CTRACE((tfp, "...used before%s\n", still_open ? ", still open!" : "."));
+    }
+
+    if (registered) {
+#ifndef NO_GROUPS
+	writable_exists = HTEditable(fname);	/* existing, can write */
+#define CTRACE_EXISTS "exists and is writable, "
+#else
+	writable_exists = (BOOL) (stat(fname, &stat_buf) == 0);		/* existing, assume can write */
+#define CTRACE_EXISTS "exists, "
+#endif
+
+	if (writable_exists) {
+	    is_ours = IsOurFile(fname);
+	}
+	CTRACE((tfp, "...%s%s\n",
+		writable_exists ? CTRACE_EXISTS : "",
+		is_ours ? "is our file." : "is NOT our file."));
+    }
+
+    /*
+     * Note that in cases where LYOpenTemp is called as fallback below, we
+     * don't call LYRemoveTemp first.  That may be appropriate in some cases,
+     * but not trying to remove a weird existing file seems safer and could
+     * help diagnose an unusual situation.  (They may be removed anyway later.)
+     */
+    if (still_open) {
+	/*
+	 * This should probably not happen.  Make a new one.
+	 */
+	return (LYOpenTemp(fname, suffix, mode));
+    } else if (!registered) {
+	/*
+	 * Not registered.  It should have been registered at one point though,
+	 * otherwise we wouldn't be called like this.
+	 */
+	return (LYOpenTemp(fname, suffix, mode));
+    } else if (writable_exists && !is_ours) {
+	/*
+	 * File exists, writable if we checked, but something is wrong with it.
+	 */
+	return (LYOpenTemp(fname, suffix, mode));
+#ifndef NO_GROUPS
+    } else if (!is_ours && (lstat(fname, &stat_buf) == 0)) {
+	/*
+	 * Exists but not writable, and something is wrong with it.
+	 */
+	return (LYOpenTemp(fname, suffix, mode));
+#endif
+    }
+
+    while (*mode != '\0') {
+	switch (*mode++) {
+	case 'w':
+	    wrt = 'w';
+	    break;
+	case 'a':
+	    wrt = 'a';
+	    break;
+	case 'b':
+	    txt = FALSE;
+	    break;
+	default:
+	    CTRACE((tfp, "%s @%d: BUG\n", __FILE__, __LINE__));
+	    return fp;
+	}
+    }
+
+    if (is_ours) {
+	/*
+	 * Yes, it exists, is writable if we checked, and everything looks ok
+	 * so far.  This should be the most regular case.  - kw
+	 */
+#ifdef HAVE_TRUNCATE
+	if (txt == TRUE) {	/* limitation of LYReopenTemp.  shrug */
+	    /*
+	     * We truncate and then append, this avoids having a small window
+	     * in which the file doesn't exist.  - kw
+	     */
+	    if (truncate(fname, 0) != 0) {
+		CTRACE((tfp, "... truncate(%s,0) failed: %s\n",
+			fname, LYStrerror(errno)));
+		return (LYOpenTemp(fname, suffix, mode));
+	    } else {
+		return (LYReopenTemp(fname));
+	    }
+	}
+#endif
+	remove(fname);
+
+    }
+
+    /* We come here in two cases:  either the file existed and was ours and we
+     * just got rid of it.  Or the file did and does not exist, but is
+     * registered as a temp file.  It must have been removed by some means
+     * other than LYRemoveTemp.  In both cases, reuse the name!  - kw
+     */
+
+    if (txt) {
+	switch (wrt) {
+	case 'w':
+	    fp = LYNewTxtFile(fname);
+	    break;
+	case 'a':
+	    fp = LYAppendToTxtFile(fname);
+	    break;
+	}
+    } else {
+	fp = LYNewBinFile(fname);
+    }
+    p->file = fp;
+
+    CTRACE((tfp, "... LYOpenTempRewrite(%s), %s\n", fname,
+	    (fp) ? "ok" : "failed"));
+    /*
+     * We could fall back to trying LYOpenTemp() here in case of failure. 
+     * After all the checks already done above a filure here should be pretty
+     * unusual though, so maybe it's better to let the user notice that
+     * something went wrong, and not try to fix it up.  - kw
+     */
+    return fp;
+}
+
+/*
+ * Special case of LYOpenTemp, used for manipulating bookmark file, i.e., with
+ * renaming.
+ */
+FILE *LYOpenScratch(char *result,
+		    const char *prefix)
+{
+    FILE *fp;
+    LY_TEMP *p;
+
+    if (!fmt_tempname(result, prefix, HTML_SUFFIX))
+	return 0;
+
+    if ((fp = LYNewTxtFile(result)) != 0) {
+	if ((p = typecalloc(LY_TEMP)) != 0) {
+	    p->next = ly_temp;
+	    StrAllocCopy((p->name), result);
+	    p->file = fp;
+	    ly_temp = p;
+	} else {
+	    outofmem(__FILE__, "LYOpenScratch");
+	}
+    }
+    CTRACE((tfp, "LYOpenScratch(%s)\n", result));
+    return fp;
+}
+
+static void LY_close_temp(LY_TEMP * p)
+{
+    if (p->file != 0) {
+	if (p->outs) {
+	    LYCloseOutput(p->file);
+	} else {
+	    LYCloseInput(p->file);
+	}
+	p->file = 0;
+    }
+}
+
+/*
+ * Close a temp-file, given its name
+ */
+void LYCloseTemp(char *name)
+{
+    LY_TEMP *p;
+
+    CTRACE((tfp, "LYCloseTemp(%s)\n", name));
+    if ((p = FindTempfileByName(name)) != 0) {
+	CTRACE((tfp, "...LYCloseTemp(%s)%s\n", name,
+		(p->file != 0) ? ", closed" : ""));
+	LY_close_temp(p);
+    }
+}
+
+/*
+ * Close a temp-file, given its file-pointer
+ */
+void LYCloseTempFP(FILE *fp)
+{
+    LY_TEMP *p;
+
+    CTRACE((tfp, "LYCloseTempFP\n"));
+    if ((p = FindTempfileByFP(fp)) != 0) {
+	LY_close_temp(p);
+	CTRACE((tfp, "...LYCloseTempFP(%s)\n", p->name));
+    }
+}
+
+/*
+ * Close a temp-file, removing it.
+ */
+int LYRemoveTemp(char *name)
+{
+    LY_TEMP *p, *q;
+    int code = -1;
+
+    if (non_empty(name)) {
+	CTRACE((tfp, "LYRemoveTemp(%s)\n", name));
+	for (p = ly_temp, q = 0; p != 0; q = p, p = p->next) {
+	    if (!strcmp(name, p->name)) {
+		if (q != 0) {
+		    q->next = p->next;
+		} else {
+		    ly_temp = p->next;
+		}
+		LY_close_temp(p);
+		code = HTSYS_remove(name);
+		CTRACE((tfp, "...LYRemoveTemp done(%d)%s\n", code,
+			(p->file != 0) ? ", closed" : ""));
+		CTRACE_FLUSH(tfp);
+		FREE(p->name);
+		FREE(p);
+		break;
+	    }
+	}
+    }
+    return code;
+}
+
+/*
+ * Remove all of the temp-files.  Note that this assumes that they are closed,
+ * since some systems will not allow us to remove a file which is open.
+ */
+void LYCleanupTemp(void)
+{
+    while (ly_temp != 0) {
+	LYRemoveTemp(ly_temp->name);
+    }
+#if defined(MULTI_USER_UNIX)
+    if (lynx_temp_subspace > 0) {
+	char result[LY_MAXPATH];
+
+	LYstrncpy(result, lynx_temp_space, sizeof(result) - 1);
+	LYTrimPathSep(result);
+	CTRACE((tfp, "LYCleanupTemp removing %s\n", result));
+	rmdir(result);
+	lynx_temp_subspace = -1;
+    }
+#endif
+}
+
+/*
+ * We renamed a temporary file.  Keep track so we can remove it on exit.
+ */
+void LYRenamedTemp(char *oldname,
+		   char *newname)
+{
+    LY_TEMP *p;
+
+    CTRACE((tfp, "LYRenamedTemp(old=%s, new=%s)\n", oldname, newname));
+    if ((p = FindTempfileByName(oldname)) != 0) {
+	StrAllocCopy((p->name), newname);
+    }
+}
+
+#ifndef DISABLE_BIBP
+/*
+ * Check that bibhost defines the BibP icon.
+ */
+void LYCheckBibHost(void)
+{
+    DocAddress bibhostIcon;
+    BOOLEAN saveFlag;
+
+    bibhostIcon.address = NULL;
+    StrAllocCopy(bibhostIcon.address, BibP_bibhost);
+    StrAllocCat(bibhostIcon.address, "bibp1.0/bibpicon.jpg");
+    bibhostIcon.post_data = NULL;
+    bibhostIcon.post_content_type = NULL;
+    bibhostIcon.bookmark = FALSE;
+    bibhostIcon.isHEAD = FALSE;
+    bibhostIcon.safe = FALSE;
+    saveFlag = traversal;
+    traversal = TRUE;		/* Hack to force error response. */
+    BibP_bibhost_available = (BOOLEAN) (HTLoadAbsolute(&bibhostIcon) == YES);
+    traversal = saveFlag;
+    BibP_bibhost_checked = TRUE;
+}
+#endif /* !DISABLE_BIBP */
+
+/*
+ * Management of User Interface Pages.  - kw
+ *
+ * These are mostly temp files.  Pages which can be recognized by their special
+ * URL (after having been loaded) need not be tracked here.
+ *
+ * First some private stuff:
+ */
+typedef struct uipage_entry {
+    UIP_t type;
+    unsigned flags;
+    char *url;
+    HTList *alturls;
+    char *file;
+} uip_entry;
+
+#define UIP_F_MULTI	0x0001	/* flag: track multiple instances */
+#define UIP_F_LIMIT	0x0002	/* flag: limit size of alturls list */
+#define UIP_F_LMULTI   (UIP_F_MULTI | UIP_F_LIMIT)
+/* *INDENT-OFF* */
+static uip_entry ly_uip[] =
+{
+    { UIP_HISTORY		, UIP_F_LMULTI, NULL, NULL, NULL }
+  , { UIP_DOWNLOAD_OPTIONS	, 0	      , NULL, NULL, NULL }
+  , { UIP_PRINT_OPTIONS		, 0	      , NULL, NULL, NULL }
+  , { UIP_SHOWINFO		, UIP_F_LMULTI, NULL, NULL, NULL }
+  , { UIP_LIST_PAGE		, UIP_F_LMULTI, NULL, NULL, NULL }
+  , { UIP_VLINKS		, UIP_F_LMULTI, NULL, NULL, NULL }
+#if !defined(NO_OPTION_FORMS)
+  , { UIP_OPTIONS_MENU		, UIP_F_LMULTI, NULL, NULL, NULL }
+#endif
+#ifdef DIRED_SUPPORT
+  , { UIP_DIRED_MENU		, 0	      , NULL, NULL, NULL }
+  , { UIP_PERMIT_OPTIONS	, 0	      , NULL, NULL, NULL }
+  , { UIP_UPLOAD_OPTIONS	, UIP_F_LMULTI, NULL, NULL, NULL }
+#endif
+#ifdef EXP_ADDRLIST_PAGE
+  , { UIP_ADDRLIST_PAGE		, UIP_F_LMULTI, NULL, NULL, NULL }
+#endif
+  , { UIP_LYNXCFG		, UIP_F_LMULTI, NULL, NULL, NULL }
+#if !defined(NO_CONFIG_INFO)
+  , { UIP_CONFIG_DEF		, UIP_F_LMULTI, NULL, NULL, NULL }
+#endif
+/* The following are not generated tempfiles: */
+  , { UIP_TRACELOG		, 0	     , NULL, NULL, NULL }
+#if defined(DIRED_SUPPORT) && defined(OK_INSTALL)
+  , { UIP_INSTALL		, 0	     , NULL, NULL, NULL }
+#endif
+
+};
+/* *INDENT-ON* */
+
+/*  Public entry points for User Interface Page management: */
+
+BOOL LYIsUIPage3(const char *url,
+		 UIP_t type,
+		 int flagparam)
+{
+    unsigned int i;
+    size_t l;
+
+    if (!url)
+	return NO;
+    for (i = 0; i < TABLESIZE(ly_uip); i++) {
+	if (ly_uip[i].type == type) {
+	    if (!ly_uip[i].url) {
+		return NO;
+	    } else if ((flagparam & UIP_P_FRAG) ?
+		       (!strncmp(ly_uip[i].url, url, (l = strlen(ly_uip[i].url)))
+			&& (url[l] == '\0' || url[l] == '#')) :
+		       !strcmp(ly_uip[i].url, url)) {
+		return YES;
+	    } else if (ly_uip[i].flags & UIP_F_MULTI) {
+		char *p;
+		HTList *l0 = ly_uip[i].alturls;
+
+		while ((p = (char *) HTList_nextObject(l0)) != NULL) {
+		    if ((flagparam & UIP_P_FRAG) ?
+			(!strncmp(p, url, (l = strlen(p)))
+			 && (url[l] == '\0' || url[l] == '#')) :
+			!strcmp(p, url))
+			return YES;
+		}
+	    }
+	    return NO;
+	}
+    }
+    return NO;
+}
+
+void LYRegisterUIPage(const char *url,
+		      UIP_t type)
+{
+    unsigned int i;
+
+    for (i = 0; i < TABLESIZE(ly_uip); i++) {
+	if (ly_uip[i].type == type) {
+	    if (ly_uip[i].url && url &&
+		!strcmp(ly_uip[i].url, url)) {
+
+	    } else if (!ly_uip[i].url || !url ||
+		       !(ly_uip[i].flags & UIP_F_MULTI)) {
+		StrAllocCopy(ly_uip[i].url, url);
+
+	    } else {
+		char *p;
+		int n = 0;
+		HTList *l0 = ly_uip[i].alturls;
+
+		while ((p = (char *) HTList_nextObject(l0)) != NULL) {
+		    if (!strcmp(p, url))
+			return;
+		    if (!strcmp(p, ly_uip[i].url)) {
+			StrAllocCopy(ly_uip[i].url, url);
+			return;
+		    }
+		    n++;
+		}
+		if (!ly_uip[i].alturls)
+		    ly_uip[i].alturls = HTList_new();
+
+		if (n >= HTCacheSize && (ly_uip[i].flags & UIP_F_LIMIT))
+		    HTList_removeFirstObject(ly_uip[i].alturls);
+		HTList_addObject(ly_uip[i].alturls, ly_uip[i].url);
+		ly_uip[i].url = NULL;
+		StrAllocCopy(ly_uip[i].url, url);
+	    }
+
+	    return;
+	}
+    }
+}
+
+void LYUIPages_free(void)
+{
+    unsigned int i;
+
+    for (i = 0; i < TABLESIZE(ly_uip); i++) {
+	FREE(ly_uip[i].url);
+	FREE(ly_uip[i].file);
+	LYFreeStringList(ly_uip[i].alturls);
+	ly_uip[i].alturls = NULL;
+    }
+}
+
+/*
+ * Convert local pathname to www name
+ * (do not bother about file://localhost prefix at this point).
+ */
+const char *wwwName(const char *pathname)
+{
+    const char *cp = NULL;
+
+#if defined(USE_DOS_DRIVES)
+    cp = HTDOS_wwwName(pathname);
+#else
+#ifdef VMS
+    cp = HTVMS_wwwName(pathname);
+#else
+    cp = pathname;
+#endif /* VMS */
+#endif
+
+    return cp;
+}
+
+/*
+ * Given a user-specified filename, e.g., for download or print, validate and
+ * expand it.  Expand home-directory expressions in the given string.  Only
+ * allow pipes if the user can spawn shell commands.
+ *
+ * Both strings are fixed buffer sizes, LY_MAXPATH.
+ */
+BOOLEAN LYValidateFilename(char *result,
+			   char *given)
+{
+    BOOLEAN code = TRUE;
+    char *cp = NULL;
+    const char *cp2 = NULL;
+
+    /*
+     * Cancel if the user entered "/dev/null" on Unix, or an "nl:" path on VMS. 
+     * - FM
+     */
+    if (LYIsNullDevice(given)) {
+	/* just ignore it */
+	code = FALSE;
+#ifdef HAVE_POPEN
+    } else if (LYIsPipeCommand(given)) {
+	if (no_shell) {
+	    HTUserMsg(SPAWNING_DISABLED);
+	    code = FALSE;
+	} else {
+	    LYstrncpy(result, given, LY_MAXPATH);
+	}
+#endif
+    } else {
+	if ((cp = FindLeadingTilde(given, TRUE)) != 0
+	    && (cp2 = wwwName(Home_Dir())) != 0
+	    && strlen(cp2) + strlen(given) < LY_MAXPATH) {
+	    if (LYIsTilde(cp[0]) && LYIsPathSep(cp[1])) {
+		*(cp++) = '\0';
+		strcpy(result, given);
+		LYTrimPathSep(result);
+		strcat(result, cp2);
+		strcat(result, cp);
+		strcpy(given, result);
+	    }
+	}
+#ifdef VMS
+	if (strchr(given, '/') != NULL) {
+	    strcpy(result, HTVMS_name("", given));
+	    strcpy(given, result);
+	}
+	if (given[0] != '/'
+	    && strchr(given, ':') == NULL
+	    && strlen(given) < LY_MAXPATH - 13) {
+	    strcpy(result, "sys$disk:");
+	    if (strchr(given, ']') == NULL)
+		strcat(result, "[]");
+	    strcat(result, given);
+	} else {
+	    strcpy(result, given);
+	}
+#else
+
+#ifndef __EMX__
+	if (!LYisAbsPath(given)) {
+#if defined(__DJGPP__) || defined(_WINDOWS)
+	    if (strchr(result, ':') != NULL)
+		cp = NULL;
+	    else
+#endif /*  __DJGPP__ || _WINDOWS */
+	    {
+#ifdef SUPPORT_CHDIR
+		static char buf[LY_MAXPATH];
+
+		cp = Current_Dir(buf);
+#else
+		cp = original_dir;
+#endif
+	    }
+	} else
+#endif /* __EMX__ */
+	    cp = NULL;
+
+	*result = 0;
+	if (cp) {
+	    LYTrimPathSep(cp);
+	    if (strlen(cp) >= LY_MAXPATH - 2) {
+		code = FALSE;
+	    } else {
+		sprintf(result, "%s/", cp);
+	    }
+	}
+	if (code) {
+	    cp = HTSYS_name(given);
+	    if (strlen(result) + strlen(cp) >= LY_MAXPATH - 1) {
+		code = FALSE;
+	    } else {
+		strcat(result, cp);
+	    }
+	}
+#endif /* VMS */
+    }
+    return code;
+}
+
+/*
+ * Given a valid filename, check if it exists.  If so, we'll have to worry
+ * about overwriting it.
+ *
+ * Returns:
+ *	'Y' (yes/success)
+ *	'N' (no/retry)
+ *	3   (cancel)
+ */
+int LYValidateOutput(char *filename)
+{
+    int c;
+
+    /*
+     * Assume we can write to a pipe
+     */
+#ifdef HAVE_POPEN
+    if (LYIsPipeCommand(filename))
+	return 'Y';
+#endif
+
+    if (no_dotfiles || !show_dotfiles) {
+	if (*LYPathLeaf(filename) == '.') {
+	    HTAlert(FILENAME_CANNOT_BE_DOT);
+	    return 'N';
+	}
+    }
+
+    /*
+     * See if it already exists.
+     */
+    if (LYCanReadFile(filename)) {
+#ifdef VMS
+	c = HTConfirm(FILE_EXISTS_HPROMPT);
+#else
+	c = HTConfirm(FILE_EXISTS_OPROMPT);
+#endif /* VMS */
+	if (HTLastConfirmCancelled()) {
+	    HTInfoMsg(SAVE_REQUEST_CANCELLED);
+	    return 3;
+	} else if (c == NO) {
+	    return 'N';
+	}
+    }
+    return 'Y';
+}
+
+/*
+ * Convert a local filename to a URL
+ */
+void LYLocalFileToURL(char **target,
+		      const char *source)
+{
+    const char *leaf;
+
+    StrAllocCopy(*target, "file://localhost");
+
+    leaf = wwwName(source);
+
+    if (!LYisAbsPath(source)) {
+	char temp[LY_MAXPATH];
+
+	Current_Dir(temp);
+	if (!LYIsHtmlSep(*temp))
+	    LYAddHtmlSep(target);
+	StrAllocCat(*target, temp);
+    }
+    if (!LYIsHtmlSep(*leaf))
+	LYAddHtmlSep(target);
+    StrAllocCat(*target, leaf);
+}
+
+/*
+ * Open a temporary file for internal-pages, optionally reusing an existing
+ * filename.
+ */
+FILE *InternalPageFP(char *filename,
+		     int reuse_flag)
+{
+    FILE *fp;
+
+    if (LYReuseTempfiles && reuse_flag) {
+	fp = LYOpenTempRewrite(filename, HTML_SUFFIX, BIN_W);
+    } else {
+	LYRemoveTemp(filename);
+	fp = LYOpenTemp(filename, HTML_SUFFIX, BIN_W);
+    }
+    if (fp == NULL) {
+	HTAlert(CANNOT_OPEN_TEMP);
+    }
+    return fp;
+}
+
+/*
+ * This part is shared by all internal pages.
+ */
+void WriteInternalTitle(FILE *fp0, const char *Title)
+{
+    fprintf(fp0,
+	    "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n");
+
+    fprintf(fp0, "<html>\n<head>\n");
+    LYAddMETAcharsetToFD(fp0, -1);
+    if (LYIsListpageTitle(Title)) {
+	if (strchr(HTLoadedDocumentURL(), '"') == NULL) {
+	    char *Address = NULL;
+
+	    /*
+	     * Insert a BASE tag so there is some way to relate the List Page
+	     * file to its underlying document after we are done.  It won't be
+	     * actually used for resolving relative URLs.  - kw
+	     */
+	    StrAllocCopy(Address, HTLoadedDocumentURL());
+	    LYEntify(&Address, FALSE);
+	    fprintf(fp0, "<base href=\"%s\">\n", Address);
+	    FREE(Address);
+	}
+    }
+    fprintf(fp0, "<title>%s</title>\n</head>\n<body>\n", Title);
+}
+
+/*
+ * This is used to start most internal pages, except for special cases where
+ * the embedded HREF's in the title differ.
+ */
+void BeginInternalPage(FILE *fp0, const char *Title,
+		       const char *HelpURL)
+{
+    WriteInternalTitle(fp0, Title);
+
+    if ((user_mode == NOVICE_MODE)
+	&& LYwouldPush(Title, NULL)
+	&& (HelpURL != 0)) {
+	fprintf(fp0, "<h1>%s (%s%s%s), <a href=\"%s%s\">help</a></h1>\n",
+		Title, LYNX_NAME, VERSION_SEGMENT, LYNX_VERSION,
+		helpfilepath, HelpURL);
+    } else {
+	fprintf(fp0, "<h1>%s (%s%s%s)</h1>\n",
+		Title, LYNX_NAME, VERSION_SEGMENT, LYNX_VERSION);
+    }
+}
+
+void EndInternalPage(FILE *fp0)
+{
+    fprintf(fp0, "</body>\n</html>");
+}
+
+char *trimPoundSelector(char *address)
+{
+    char *pound = findPoundSelector(address);
+
+    if (pound != 0)
+	*pound = '\0';
+    return pound;
+}
+
+/*
+ * Trim a trailing path-separator to avoid confusing other programs when we concatenate
+ * to it.  This only applies to local filesystems.
+ */
+void LYTrimPathSep(char *path)
+{
+    size_t len;
+
+    if (path != 0
+	&& (len = strlen(path)) != 0
+	&& LYIsPathSep(path[len - 1]))
+	path[len - 1] = 0;
+}
+
+/*
+ * Add a trailing path-separator to avoid confusing other programs when we concatenate
+ * to it.  This only applies to local filesystems.
+ */
+void LYAddPathSep(char **path)
+{
+    size_t len;
+    char *temp;
+
+    if ((path != 0)
+	&& ((temp = *path) != 0)
+	&& (len = strlen(temp)) != 0
+	&& !LYIsPathSep(temp[len - 1])) {
+	StrAllocCat(*path, PATHSEP_STR);
+    }
+}
+
+/*
+ * Add a trailing path-separator to avoid confusing other programs when we concatenate
+ * to it.  This only applies to local filesystems.
+ */
+void LYAddPathSep0(char *path)
+{
+    size_t len;
+
+    if ((path != 0)
+	&& (len = strlen(path)) != 0
+	&& (len < LY_MAXPATH - 2)
+	&& !LYIsPathSep(path[len - 1])) {
+	strcat(path, PATHSEP_STR);
+    }
+}
+
+/*
+ * Check if a given string contains a path separator
+ */
+char *LYLastPathSep(const char *path)
+{
+    char *result;
+
+#if defined(USE_DOS_DRIVES)
+    if ((result = strrchr(path, '\\')) == 0)
+	result = strrchr(path, '/');
+#else
+    result = strrchr(path, '/');
+#endif
+    return result;
+}
+
+/*
+ * Trim a trailing path-separator to avoid confusing other programs when we concatenate
+ * to it.  This only applies to HTML paths.
+ */
+void LYTrimHtmlSep(char *path)
+{
+    size_t len;
+
+    if (path != 0
+	&& (len = strlen(path)) != 0
+	&& LYIsHtmlSep(path[len - 1]))
+	path[len - 1] = 0;
+}
+
+/*
+ * Add a trailing path-separator to avoid confusing other programs when we concatenate
+ * to it.  This only applies to HTML paths.
+ */
+void LYAddHtmlSep(char **path)
+{
+    size_t len;
+    char *temp;
+
+    if ((path != 0)
+	&& ((temp = *path) != 0)
+	&& (len = strlen(temp)) != 0
+	&& !LYIsHtmlSep(temp[len - 1])) {
+	StrAllocCat(*path, "/");
+    }
+}
+
+/*
+ * Add a trailing path-separator to avoid confusing other programs when we concatenate
+ * to it.  This only applies to HTML paths.
+ */
+void LYAddHtmlSep0(char *path)
+{
+    size_t len;
+
+    if ((path != 0)
+	&& (len = strlen(path)) != 0
+	&& (len < LY_MAXPATH - 2)
+	&& !LYIsHtmlSep(path[len - 1])) {
+	strcat(path, "/");
+    }
+}
+
+/*
+ * Copy a file
+ */
+int LYCopyFile(char *src,
+	       char *dst)
+{
+    int code;
+    const char *program;
+
+    if ((program = HTGetProgramPath(ppCOPY)) != NULL) {
+	char *the_command = 0;
+
+	HTAddParam(&the_command, COPY_COMMAND, 1, program);
+	HTAddParam(&the_command, COPY_COMMAND, 2, src);
+	HTAddParam(&the_command, COPY_COMMAND, 3, dst);
+	HTEndParam(&the_command, COPY_COMMAND, 3);
+
+	CTRACE((tfp, "command: %s\n", the_command));
+	stop_curses();
+	code = LYSystem(the_command);
+	start_curses();
+
+	FREE(the_command);
+    } else {
+	FILE *fin, *fout;
+	unsigned char buff[BUFSIZ];
+	unsigned len;
+
+	code = EOF;
+	if ((fin = fopen(src, BIN_R)) != 0) {
+	    if ((fout = fopen(dst, BIN_W)) != 0) {
+		code = 0;
+		while ((len = fread(buff, 1, sizeof(buff), fin)) != 0) {
+		    fwrite(buff, 1, len, fout);
+		    if (ferror(fout)) {
+			code = EOF;
+			break;
+		    }
+		}
+		LYCloseOutput(fout);
+	    }
+	    LYCloseInput(fin);
+	}
+	CTRACE((tfp, "builtin copy ->%d\n\tsource=%s\n\ttarget=%s\n",
+		code, src, dst));
+    }
+
+    if (code) {
+	HTAlert(CANNOT_WRITE_TO_FILE);
+    }
+    return code;
+}
+
+#ifdef __DJGPP__
+static char *escape_backslashes(char *source)
+{
+    char *result = 0;
+    int count = 0;
+    int n;
+
+    for (n = 0; source[n] != '\0'; ++n) {
+	if (source[n] == '\\')
+	    ++count;
+    }
+    if (count != 0) {
+	result = malloc(count + n + 1);
+	if (result != 0) {
+	    int ch;
+	    char *target = result;
+
+	    while ((ch = *source++) != '\0') {
+		if (ch == '\\')
+		    *target++ = ch;
+		*target++ = ch;
+	    }
+	    *target = '\0';
+	}
+    }
+    return result;
+}
+#endif /* __DJGPP__ */
+/*
+ * Invoke a shell command, return nonzero on error.
+ */
+int LYSystem(char *command)
+{
+    int code;
+    int do_free = 0;
+
+#if defined(HAVE_SIGACTION) && defined(SIGTSTP) && !defined(USE_SLANG)
+    struct sigaction saved_sigtstp_act;
+    BOOLEAN sigtstp_saved = FALSE;
+#endif
+    int saved_errno = 0;
+
+#ifdef __EMX__
+    int scrsize[4];
+#endif
+
+    fflush(stdout);
+    fflush(stderr);
+    CTRACE((tfp, "LYSystem(%s)\n", command));
+    CTRACE_FLUSH(tfp);
+
+#ifdef __DJGPP__
+    __djgpp_set_ctrl_c(0);
+    _go32_want_ctrl_break(1);
+#endif /* __DJGPP__ */
+
+#ifdef VMS
+    code = DCLsystem(command);
+#else
+#  ifdef __EMX__		/* FIXME: Should be LY_CONVERT_SLASH? */
+    /* Configure writes commands which contain direct slashes.
+       Native command-(non)-shell will not tolerate this. */
+    {
+	char *space = command, *slash = command;
+
+	_scrsize(scrsize);
+	while (*space && *space != ' ' && *space != '\t')
+	    space++;
+	while (slash < space && *slash != '/')
+	    slash++;
+	if (slash != space) {
+	    char *old = command;
+
+	    command = NULL;
+	    StrAllocCopy(command, old);
+	    do_free = 1;
+	    slash = (slash - old) + command - 1;
+	    space = (space - old) + command;
+	    while (++slash < space)
+		if (*slash == '/')
+		    *slash = '\\';
+	}
+    }
+#  endif
+
+    /*
+     * This chunk of code does not work, for two reasons:
+     * a) the Cygwin system() function edits out the backslashes
+     * b) it does not account for more than one parameter, e.g., +number
+     */
+#if defined(__CYGWIN__) && defined(DOSPATH)	/* 1999/02/26 (Fri) */
+    {
+	char cmd[LY_MAXPATH];
+	char win32_name[LY_MAXPATH];
+	char new_cmd[LY_MAXPATH];
+	char new_command[LY_MAXPATH * 2 + 10];
+	char *p, *q;
+
+	p = command;
+	q = cmd;
+	while (*p) {
+	    if (*p == ' ')
+		break;
+	    else
+		*q = *p;
+	    p++;
+	    q++;
+	}
+	*q = '\0';
+
+	if (cmd[0] == '/')
+	    cygwin_conv_to_full_posix_path(cmd, new_cmd);
+	else
+	    strcpy(new_cmd, cmd);
+
+	while (*p == ' ')
+	    p++;
+
+	if (strchr(p, '\\') == NULL) {
+	    /* for Windows Application */
+	    cygwin_conv_to_full_win32_path(p, win32_name);
+	    sprintf(new_command, "%.*s \"%.*s\"",
+		    LY_MAXPATH, new_cmd, LY_MAXPATH, win32_name);
+	} else {
+	    /* for DOS like editor */
+	    q = win32_name;
+	    while (*p) {
+		if (*p == '\\') {
+		    if (*(p + 1) == '\\')
+			p++;
+		}
+		*q = *p;
+		q++, p++;
+	    }
+	    *q = '\0';
+	    sprintf(new_command, "%.*s %.*s", LY_MAXPATH, new_cmd, LY_MAXPATH, win32_name);
+	}
+	command = new_command;
+    }
+#endif
+
+#ifdef __DJGPP__
+    if (dj_is_bash) {
+	char *new_command = escape_backslashes(command);
+
+	if (new_command != 0) {
+	    if (do_free)
+		free(command);
+	    command = new_command;
+	}
+    }
+#endif /* __DJGPP__ */
+
+#ifdef _WIN_CC
+    code = exec_command(command, TRUE);		/* Wait exec */
+#else /* !_WIN_CC */
+#ifdef SIGPIPE
+    if (restore_sigpipe_for_children)
+	signal(SIGPIPE, SIG_DFL);	/* Some commands expect the default */
+#endif
+#if defined(HAVE_SIGACTION) && defined(SIGTSTP) && !defined(USE_SLANG)
+    if (!dump_output_immediately && !LYCursesON && !no_suspend)
+	sigtstp_saved = LYToggleSigDfl(SIGTSTP, &saved_sigtstp_act, 1);
+#endif
+    code = system(command);
+    saved_errno = errno;
+#if defined(HAVE_SIGACTION) && defined(SIGTSTP) && !defined(USE_SLANG)
+    if (sigtstp_saved)
+	LYToggleSigDfl(SIGTSTP, &saved_sigtstp_act, 0);
+#endif
+#ifdef SIGPIPE
+    if (restore_sigpipe_for_children)
+	signal(SIGPIPE, SIG_IGN);	/* Ignore it again - kw */
+#endif
+#endif
+#endif
+
+#ifdef __DJGPP__
+    __djgpp_set_ctrl_c(1);
+    _go32_want_ctrl_break(0);
+#endif /* __DJGPP__ */
+
+    fflush(stdout);
+    fflush(stderr);
+
+    if (do_free)
+	FREE(command);
+#if !defined(UCX) || !defined(VAXC)	/* errno not modifiable ?? */
+    set_errno(saved_errno);	/* may have been clobbered */
+#endif
+#ifdef __EMX__			/* Check whether the screen size changed */
+    size_change(0);
+#endif
+    return code;
+}
+
+/*
+ * Return a string which can be used in LYSystem() for spawning a subshell
+ */
+#if defined(__CYGWIN__)		/* 1999/02/26 (Fri) */
+int Cygwin_Shell(void)
+{
+    char *shell;
+    int code;
+    STARTUPINFO startUpInfo;
+    PROCESS_INFORMATION procInfo;
+    SECURITY_ATTRIBUTES sa;
+
+    /* Set up security attributes to allow inheritance of the file handle */
+
+    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+    sa.lpSecurityDescriptor = 0;
+    sa.bInheritHandle = TRUE;
+
+    /* Init a startup structure */
+    GetStartupInfo(&startUpInfo);
+
+    shell = LYGetEnv("COMSPEC");
+
+    /* Create the child process, specifying
+       inherited handles. Pass the value of the
+       handle as a command line parameter */
+    code = 0;
+    if (shell) {
+	code = CreateProcess(0, shell, 0, 0,
+			     TRUE, 0,
+			     0, 0, &startUpInfo, &procInfo);
+
+	if (!code) {
+	    printf("shell = [%s], code = %ld\n", shell, GetLastError());
+	}
+
+	/* wait for the child to return (this is not a requirement
+	   since the child is its own independent process) */
+	WaitForSingleObject(procInfo.hProcess, INFINITE);
+    }
+
+    return code;
+}
+#endif
+
+#ifdef WIN_EX
+/*
+ * Quote the path to make it safe for shell command processing.
+ *  We always quote it not only includes spaces in it.
+ *  At least we should quote paths which include "&".
+ */
+char *quote_pathname(char *pathname)
+{
+    char *result = NULL;
+
+    HTSprintf0(&result, "\"%s\"", pathname);
+    return result;
+}
+#endif
+
+const char *LYSysShell(void)
+{
+    const char *shell = 0;
+
+#ifdef DOSPATH
+#ifdef WIN_EX
+    shell = LYGetEnv("SHELL");
+    if (shell) {
+	if (access(shell, 0) != 0)
+	    shell = LYGetEnv("COMSPEC");
+    } else {
+	shell = LYGetEnv("COMSPEC");
+    }
+    if (shell == NULL) {
+	if (system_is_NT)
+	    shell = "cmd.exe";
+	else
+	    shell = "command.com";
+    }
+#else
+    shell = LYGetEnv("SHELL");
+    if (shell == NULL) {
+	shell = LYGetEnv("COMSPEC");
+    }
+    if (shell == NULL) {
+	shell = "command.com";
+    }
+#endif /* WIN_EX */
+#else
+#ifdef __EMX__
+    if (LYGetEnv("SHELL") != NULL) {
+	shell = LYGetEnv("SHELL");
+    } else {
+	shell = (LYGetEnv("COMSPEC") == NULL) ? "cmd.exe" : LYGetEnv("COMSPEC");
+    }
+#else
+#ifdef VMS
+    shell = "";
+#else
+    shell = "exec $SHELL";
+#endif /* __EMX__ */
+#endif /* VMS */
+#endif /* DOSPATH */
+    return shell;
+}
+
+#ifdef VMS
+#define DISPLAY "DECW$DISPLAY"
+#else
+#define DISPLAY "DISPLAY"
+#endif /* VMS */
+
+/*
+ * Return the X-Window $DISPLAY string if it is nonnull/nonempty
+ */
+char *LYgetXDisplay(void)
+{
+    return LYGetEnv(DISPLAY);
+}
+
+/*
+ * Set the value of the X-Window $DISPLAY variable (yes it leaks memory, but
+ * that is putenv's fault).
+ */
+void LYsetXDisplay(char *new_display)
+{
+    if (new_display != 0) {
+#ifdef VMS
+	LYUpperCase(new_display);
+	Define_VMSLogical(DISPLAY, new_display);
+#else
+	static char *display_putenv_command;
+
+	HTSprintf0(&display_putenv_command, "DISPLAY=%s", new_display);
+	putenv(display_putenv_command);
+#endif /* VMS */
+	if ((new_display = LYgetXDisplay()) != 0) {
+	    StrAllocCopy(x_display, new_display);
+	}
+    }
+}
+
+#ifdef CAN_CUT_AND_PASTE
+#ifdef __EMX__
+
+static int proc_type = -1;
+static PPIB pib;
+static HAB hab;
+static HMQ hmq;
+
+static void morph_PM(void)
+{
+    PTIB tib;
+    int first = 0;
+
+    if (proc_type == -1) {
+	DosGetInfoBlocks(&tib, &pib);
+	proc_type = pib->pib_ultype;
+	first = 1;
+    }
+    if (pib->pib_ultype != 3)	/* 2 is VIO */
+	pib->pib_ultype = 3;	/* 3 is PM */
+    if (first)
+	hab = WinInitialize(0);
+    /* 64 messages if before OS/2 3.0, ignored otherwise */
+    hmq = WinCreateMsgQueue(hab, 64);
+    WinCancelShutdown(hmq, 1);	/* Do not inform us on shutdown */
+}
+
+static void unmorph_PM(void)
+{
+    WinDestroyMsgQueue(hmq);
+    pib->pib_ultype = proc_type;
+}
+
+int size_clip(void)
+{
+    return 8192;
+}
+
+/* Code partially stolen from FED editor. */
+
+int put_clip(const char *s)
+{
+    int sz = strlen(s) + 1;
+    int ret = EOF, nl = 0;
+    char *pByte = 0, *s1 = s, c, *t;
+
+    while ((c = *s1++)) {
+	if (c == '\r' && *s1 == '\n')
+	    s1++;
+	else if (c == '\n')
+	    nl++;
+    }
+    if (DosAllocSharedMem((PPVOID) & pByte, 0, sz + nl,
+			  PAG_WRITE | PAG_COMMIT | OBJ_GIVEABLE | OBJ_GETTABLE))
+	return ret;
+
+    if (!nl)
+	memcpy(pByte, s, sz);
+    else {
+	t = pByte;
+	while ((c = *t++ = *s++))
+	    if (c == '\n' && (t == pByte + 1 || t[-2] != '\r'))
+		t[-1] = '\r', *t++ = '\n';
+    }
+
+    morph_PM();
+    if (!hab)
+	goto fail;
+
+    WinOpenClipbrd(hab);
+    WinEmptyClipbrd(hab);
+    if (WinSetClipbrdData(hab, (ULONG) pByte, CF_TEXT, CFI_POINTER))
+	ret = 0;
+    WinCloseClipbrd(hab);
+    unmorph_PM();
+    if (ret == 0)
+	return 0;
+  fail:
+    DosFreeMem((PPVOID) & pByte);
+    return EOF;
+}
+
+static int clip_open;
+
+/* get_clip_grab() returns a pointer to the string in the system area.
+   get_clip_release() should be called ASAP after this. */
+
+char *get_clip_grab(void)
+{
+    char *ClipData;
+    ULONG ulFormat;
+    int sz;
+
+    morph_PM();
+    if (!hab)
+	return 0;
+    if (clip_open)
+	get_clip_release();
+
+    WinQueryClipbrdFmtInfo(hab, CF_TEXT, &ulFormat);
+    if (ulFormat != CFI_POINTER) {
+	unmorph_PM();
+	return 0;
+    }
+    WinOpenClipbrd(hab);
+    clip_open = 1;
+    ClipData = (char *) WinQueryClipbrdData(hab, CF_TEXT);
+    sz = strlen(ClipData);
+    if (!ClipData || !sz) {
+	get_clip_release();
+	return 0;
+    }
+    return ClipData;
+}
+
+void get_clip_release(void)
+{
+    if (!clip_open)
+	return;
+    WinCloseClipbrd(hab);
+    clip_open = 0;
+    unmorph_PM();
+}
+
+#else /* !( defined __EMX__ ) */
+
+#  if !defined(WIN_EX) && defined(HAVE_POPEN)
+
+static FILE *paste_handle = 0;
+static char *paste_buf = NULL;
+
+void get_clip_release(void)
+{
+    if (paste_handle != 0)
+	pclose(paste_handle);
+    if (paste_buf)
+	FREE(paste_buf);
+}
+
+static int clip_grab(void)
+{
+    char *cmd = LYGetEnv("RL_PASTE_CMD");
+
+    if (paste_handle)
+	pclose(paste_handle);
+    if (!cmd)
+	return 0;
+
+    paste_handle = popen(cmd, TXT_R);
+    if (!paste_handle)
+	return 0;
+    return 1;
+}
+
+#define PASTE_BUFFER 1008
+#define CF_TEXT 0		/* Not used */
+
+char *get_clip_grab(void)
+{
+    int len;
+    unsigned size = PASTE_BUFFER;
+    int off = 0;
+
+    if (!clip_grab())
+	return NULL;
+    if (!paste_handle)
+	return NULL;
+    if (paste_buf)
+	FREE(paste_buf);
+    paste_buf = (char *) malloc(PASTE_BUFFER);
+    while (1) {
+	len = (int) fread(paste_buf + off, 1, PASTE_BUFFER - 1, paste_handle);
+	paste_buf[off + len] = '\0';
+	if (len < PASTE_BUFFER - 1)
+	    break;
+	if (strchr(paste_buf + off, '\r')
+	    || strchr(paste_buf + off, '\n'))
+	    break;
+	paste_buf = typeRealloc(char, paste_buf, size += PASTE_BUFFER - 1);
+
+	off += len;
+    }
+    return paste_buf;
+}
+
+int put_clip(const char *s)
+{
+    char *cmd = LYGetEnv("RL_CLCOPY_CMD");
+    FILE *fh;
+    unsigned l = strlen(s), res;
+
+    if (!cmd)
+	return -1;
+
+    fh = popen(cmd, TXT_W);
+    if (!fh)
+	return -1;
+    res = fwrite(s, 1, l, fh);
+    if (pclose(fh) != 0 || res != l)
+	return -1;
+    return 0;
+}
+
+#  endif			/* !defined(WIN_EX) && defined(HAVE_POPEN) */
+
+#endif /* __EMX__ */
+
+/*
+ * Sleep for a number of milli-sec.
+ */
+void LYmsec_delay(unsigned msec)
+{
+#if defined(_WINDOWS)
+    Sleep(msec);
+
+#elif defined(HAVE_NAPMS)
+    napms((int) msec);
+
+#elif defined(DJGPP) || defined(HAVE_USLEEP)
+    usleep(1000 * msec);
+
+#else
+    struct timeval tv;
+    unsigned long usec = 1000UL * msec;
+
+    tv.tv_sec = usec / 1000000UL;
+    tv.tv_usec = usec % 1000000UL;
+    select(0, NULL, NULL, NULL, &tv);
+#endif
+}
+
+#if defined(WIN_EX)		/* 1997/10/16 (Thu) 20:13:28 */
+
+int put_clip(const char *szBuffer)
+{
+    HANDLE hWnd;
+    HANDLE m_hLogData;
+    LPTSTR pLogData;
+    HANDLE hClip;
+    int len;
+
+    if (szBuffer == NULL)
+	return EOF;
+
+    len = strlen(szBuffer);
+    if (len == 0)
+	return EOF;
+    else
+	len++;
+
+    m_hLogData = GlobalAlloc(GHND, len);
+    if (m_hLogData == NULL) {
+	return EOF;
+    }
+
+    hWnd = NULL;
+    if (!OpenClipboard(hWnd)) {
+	return EOF;
+    }
+    /* Remove the current Clipboard contents */
+    if (!EmptyClipboard()) {
+	GlobalFree(m_hLogData);
+	return EOF;
+    }
+
+    /* Lock the global memory while we write to it. */
+    pLogData = (LPTSTR) GlobalLock(m_hLogData);
+
+    lstrcpy((LPTSTR) pLogData, szBuffer);
+    GlobalUnlock(m_hLogData);
+
+    /* If there were any lines at all then copy them to clipboard. */
+    hClip = SetClipboardData(CF_TEXT, m_hLogData);
+    if (!hClip) {
+	/* If we couldn't clip the data then free the global handle. */
+	GlobalFree(m_hLogData);
+    }
+
+    CloseClipboard();
+    return 0;
+}
+
+static HANDLE m_hLogData;
+static int m_locked;
+
+/* get_clip_grab() returns a pointer to the string in the system area.
+   get_clip_release() should be called ASAP after this. */
+
+char *get_clip_grab()
+{
+    HANDLE hWnd;
+    LPTSTR pLogData;
+
+    hWnd = NULL;
+    if (!OpenClipboard(hWnd)) {
+	return 0;
+    }
+
+    m_hLogData = GetClipboardData(CF_TEXT);
+
+    if (m_hLogData == NULL) {
+	CloseClipboard();
+	m_locked = 0;
+	return 0;
+    }
+    pLogData = (LPTSTR) GlobalLock(m_hLogData);
+
+    m_locked = 1;
+    return pLogData;
+}
+
+void get_clip_release()
+{
+    if (!m_locked)
+	return;
+    GlobalUnlock(m_hLogData);
+    CloseClipboard();
+    m_locked = 0;
+}
+#endif /* WIN_EX */
+#endif /* CAN_CUT_AND_PASTE */
+
+#if defined(WIN_EX)
+
+#ifndef WSABASEERR
+#define WSABASEERR 10000
+#endif
+
+#ifdef ENABLE_IPV6
+#define WSOCK_NAME  "ws2_32"
+#else
+#define WSOCK_NAME  "wsock32"
+#endif
+
+/*
+ * Description: the windows32 version of perror()
+ *
+ * Returns:  a pointer to a static error
+ *
+ * Notes/Dependencies:  I got this from
+ *	comp.os.ms-windows.programmer.win32
+ */
+char *w32_strerror(DWORD ercode)
+{
+/*  __declspec(thread) necessary if you will use multiple threads */
+#ifdef __CYGWIN__
+    static char msg_buff[256];
+
+#else
+    __declspec(thread) static char msg_buff[256];
+#endif
+    HMODULE hModule;
+    int i, msg_type;
+    unsigned char *p, *q, tmp_buff[256];
+    DWORD rc;
+
+    hModule = NULL;
+    msg_type = FORMAT_MESSAGE_FROM_SYSTEM;
+    /* Fill message buffer with a default message in
+     * case FormatMessage fails
+     */
+    wsprintf(msg_buff, "Error %ld", ercode);
+
+    /*
+     * Special code for winsock error handling.
+     */
+    if (ercode > WSABASEERR) {
+	hModule = GetModuleHandle(WSOCK_NAME);
+	if (hModule)
+	    msg_type = FORMAT_MESSAGE_FROM_HMODULE;
+    }
+    /*
+     * message handling. If not found in module, retry from system.
+     */
+    rc = FormatMessage(msg_type, hModule, ercode, LANG_NEUTRAL,
+		       msg_buff, sizeof(msg_buff), NULL);
+
+    if (rc == 0 && msg_type == FORMAT_MESSAGE_FROM_HMODULE) {
+	FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, ercode,
+		      LANG_NEUTRAL, msg_buff, sizeof(msg_buff), NULL);
+    }
+
+    strcpy(tmp_buff, msg_buff);
+    p = q = tmp_buff;
+    i = 0;
+    while (*p) {
+	if (!(*p == '\n' || *p == '\r'))
+	    msg_buff[i++] = *p;
+	p++;
+    }
+    msg_buff[i] = '\0';
+
+    return msg_buff;
+}
+
+#endif
+
+#if defined(SYSLOG_REQUESTED_URLS)
+/*
+ * syslog() interface
+ */
+void LYOpenlog(const char *banner)
+{
+    if (syslog_requested_urls) {
+	CTRACE((tfp, "LYOpenlog(%s)\n", NONNULL(banner)));
+#if defined(DJGPP)
+	openlog("lynx", LOG_PID | LOG_NDELAY, LOG_LOCAL5);
+#else
+	openlog("lynx", LOG_PID, LOG_LOCAL5);
+#endif
+
+	if (banner) {
+	    syslog(LOG_INFO, "Session start:%s", banner);
+	} else {
+	    syslog(LOG_INFO, "Session start");
+	}
+    }
+}
+
+static BOOLEAN looks_like_password(char *first,
+				   char *last)
+{
+    BOOLEAN result = FALSE;
+
+    while (first <= last) {
+	if (*first == '/'
+	    || *first == ':') {
+	    result = FALSE;
+	    break;
+	}
+	result = TRUE;
+	first++;
+    }
+    return result;
+}
+
+void LYSyslog(char *arg)
+{
+    char *colon1;
+    char *colon2;
+    char *atsign;
+
+    if (syslog_requested_urls) {
+
+	CTRACE((tfp, "LYSyslog %s\n", arg));
+
+	if (is_url(arg)) {	/* proto://user:password@host/path:port */
+	    /*      ^this colon                 */
+	    if ((colon1 = strchr(arg, ':')) != 0
+		&& !strncmp(colon1, "://", 3)
+		&& (colon2 = strchr(colon1 + 3, ':')) != 0
+		&& (atsign = strchr(colon1, '@')) != 0
+		&& (colon2 < atsign)
+		&& looks_like_password(colon2 + 1, atsign - 1)) {
+		char *buf = NULL;
+
+		StrAllocCopy(buf, arg);
+		buf[colon2 - arg + 1] = 0;
+		StrAllocCat(buf, "******");
+		StrAllocCat(buf, atsign);
+		syslog(LOG_INFO | LOG_LOCAL5, "%s", buf);
+		CTRACE((tfp, "...alter %s\n", buf));
+		FREE(buf);
+		return;
+	    }
+	}
+	syslog(LOG_INFO | LOG_LOCAL5, "%s", NONNULL(arg));
+    }
+}
+
+void LYCloselog(void)
+{
+    if (syslog_requested_urls) {
+	syslog(LOG_INFO, "Session over");
+	closelog();
+    }
+}
+
+#endif /* SYSLOG_REQUESTED_URLS */
diff --git a/src/LYUtils.h b/src/LYUtils.h
new file mode 100644
index 00000000..0486a31e
--- /dev/null
+++ b/src/LYUtils.h
@@ -0,0 +1,538 @@
+/* $LynxId: LYUtils.h,v 1.82 2009/11/21 15:46:24 tom Exp $ */
+#ifndef LYUTILS_H
+#define LYUTILS_H
+
+#include <LYCharVals.h>		/* S/390 -- gil -- 2149 */
+#include <LYKeymap.h>
+
+#ifndef HTLIST_H
+#include <HTList.h>
+#endif /* HTLIST_H */
+
+#ifdef VMS
+#include <HTFTP.h>
+#include <HTVMSUtils.h>
+#endif /* VMS */
+
+#if defined(USE_DOS_DRIVES)
+#include <HTDOS.h>
+#endif
+
+#if defined(SYSLOG_REQUESTED_URLS)
+#include <syslog.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef VMS
+#define HTSYS_name(path)   HTVMS_name("", path)
+#define HTSYS_purge(path)  HTVMS_purge(path)
+#define HTSYS_remove(path) HTVMS_remove(path)
+#endif				/* VMS */
+#if defined(USE_DOS_DRIVES)
+#define HTSYS_name(path) HTDOS_name(path)
+#endif
+#ifndef HTSYS_name
+#define HTSYS_name(path) path
+#endif
+#ifndef HTSYS_purge
+#define HTSYS_purge(path)	/* nothing */
+#endif
+#ifndef HTSYS_remove
+#define HTSYS_remove(path) remove(path)
+#endif
+#define LYIsPipeCommand(s) ((s)[0] == '|')
+#ifdef VMS
+#define TTY_DEVICE "tt:"
+#define NUL_DEVICE "nl:"
+#define LYIsNullDevice(s) (!strncasecomp(s, "nl:", 3) || !strncasecomp(s, "/nl/", 4))
+#define LYSameFilename(a,b) (!strcasecomp(a,b))
+#define LYSameHostname(a,b) (!strcasecomp(a,b))
+#else
+#if defined(DOSPATH) || defined(__EMX__)
+#define TTY_DEVICE "con"
+#define NUL_DEVICE "nul"
+#define LYIsNullDevice(s) LYSameFilename(s,NUL_DEVICE)
+#define LYSameFilename(a,b) (!strcasecomp(a,b))
+#define LYSameHostname(a,b) (!strcasecomp(a,b))
+#else
+#if defined(__CYGWIN__)
+#define TTY_DEVICE "/dev/tty"
+#define NUL_DEVICE "/dev/null"
+#define LYIsNullDevice(s) LYSameFilename(s,NUL_DEVICE)
+#define LYSameFilename(a,b) (!strcasecomp(a,b))
+#define LYSameHostname(a,b) (!strcasecomp(a,b))
+#else
+#define TTY_DEVICE "/dev/tty"
+#define NUL_DEVICE "/dev/null"
+#define LYIsNullDevice(s) LYSameFilename(s,NUL_DEVICE)
+#define LYSameFilename(a,b) (!strcmp(a,b))
+#define LYSameHostname(a,b) (!strcmp(a,b))
+#endif				/* __CYGWIN__ */
+#endif				/* DOSPATH */
+#endif				/* VMS */
+/* See definitions in src/LYCharVals.h.  The hardcoded values...
+   This prohibits binding C-c and C-g.  Maybe it is better to remove this? */
+#define LYCharIsINTERRUPT_HARD(ch)	\
+  ((ch) == LYCharINTERRUPT1 || ch == LYCharINTERRUPT2)
+#define LYCharIsINTERRUPT(ch)		\
+  (LYCharIsINTERRUPT_HARD(ch) || LKC_TO_LAC(keymap,ch) == LYK_INTERRUPT)
+#define LYCharIsINTERRUPT_NO_letter(ch)	\
+  (LYCharIsINTERRUPT(ch) && !isprint(ch))
+#if defined(USE_DOS_DRIVES)
+#define PATHSEP_STR "\\"
+#define LYIsPathSep(ch) ((ch) == '/' || (ch) == '\\')
+#define LYIsDosDrive(s) (isalpha(UCH((s)[0])) && (s)[1] == ':')
+#else
+#define PATHSEP_STR "/"
+#define LYIsPathSep(ch) ((ch) == '/')
+#define LYIsDosDrive(s) FALSE	/* really nothing */
+#endif
+#ifdef EXP_ADDRLIST_PAGE
+#define LYIsListpageTitle(name) \
+    (!strcmp((name), LIST_PAGE_TITLE) || \
+     !strcmp((name), ADDRLIST_PAGE_TITLE))
+#else
+#define LYIsListpageTitle(name) \
+    (!strcmp((name), LIST_PAGE_TITLE))
+#endif
+#define LYIsTilde(ch)     ((ch) == '~')
+#define LYIsHtmlSep(ch) ((ch) == '/')
+#define findPoundSelector(address) strchr(address, '#')
+#define restorePoundSelector(pound) if ((pound) != NULL) *(pound) = '#'
+    extern BOOL strn_dash_equ(const char *p1, const char *p2, int len);
+    extern BOOLEAN LYAddSchemeForURL(char **AllocatedString, const char *default_scheme);
+    extern BOOLEAN LYCachedTemp(char *result, char **cached);
+    extern BOOLEAN LYCanDoHEAD(const char *address);
+    extern BOOLEAN LYCanReadFile(const char *name);
+    extern BOOLEAN LYCanWriteFile(const char *name);
+    extern BOOLEAN LYCloseInput(FILE *fp);
+    extern BOOLEAN LYCloseOutput(FILE *fp);
+    extern BOOLEAN LYExpandHostForURL(char **AllocatedString,
+				      char *prefix_list, char *suffix_list);
+    extern BOOLEAN LYFixCursesOnForAccess(const char *addr, const char *physical);
+    extern BOOLEAN LYPathOffHomeOK(char *fbuffer, size_t fbuffer_size);
+    extern BOOLEAN LYValidateFilename(char *result, char *given);
+    extern BOOLEAN LYisAbsPath(const char *path);
+    extern BOOLEAN LYisLocalAlias(const char *filename);
+    extern BOOLEAN LYisLocalFile(const char *filename);
+    extern BOOLEAN LYisLocalHost(const char *filename);
+    extern BOOLEAN LYisRootPath(const char *path);
+    extern BOOLEAN inlocaldomain(void);
+    extern FILE *InternalPageFP(char *filename, int reuse_flag);
+    extern FILE *LYAppendToTxtFile(const char *name);
+    extern FILE *LYNewBinFile(const char *name);
+    extern FILE *LYNewTxtFile(const char *name);
+    extern FILE *LYOpenScratch(char *result, const char *prefix);
+    extern FILE *LYOpenTemp(char *result, const char *suffix, const char *mode);
+    extern FILE *LYOpenTempRewrite(char *result, const char *suffix, const char *mode);
+    extern FILE *LYReopenTemp(char *name);
+    extern char *Current_Dir(char *pathname);
+    extern char *LYAbsOrHomePath(char **fname);
+    extern char *LYAddPathToSave(char *fname);
+    extern char *LYGetEnv(const char *name);
+    extern char *LYLastPathSep(const char *path);
+    extern char *LYPathLeaf(char *pathname);
+    extern char *LYTildeExpand(char **pathname, BOOL embedded);
+    extern char *LYgetXDisplay(void);
+    extern char *strip_trailing_slash(char *my_dirname);
+    extern char *trimPoundSelector(char *address);
+    extern const char *Home_Dir(void);
+    extern const char *LYGetHiliteStr(int cur, int count);
+    extern const char *LYSysShell(void);
+    extern const char *index_to_restriction(unsigned inx);
+    extern const char *wwwName(const char *pathname);
+    extern int HTCheckForInterrupt(void);
+    extern int LYConsoleInputFD(BOOLEAN need_selectable);
+    extern int LYCopyFile(char *src, char *dst);
+    extern int LYGetHilitePos(int cur, int count);
+    extern int LYRemoveTemp(char *name);
+    extern int LYReopenInput(void);
+    extern int LYSystem(char *command);
+    extern int LYValidateOutput(char *filename);
+    extern int find_restriction(const char *name, int len);
+    extern int number2arrows(int number);
+    extern size_t utf8_length(BOOL utf_flag, const char *data);
+    extern time_t LYmktime(char *string, BOOL absolute);
+    extern void BeginInternalPage(FILE *fp0, const char *Title, const char *HelpURL);
+    extern void EndInternalPage(FILE *fp0);
+    extern void HTAddSugFilename(char *fname);
+    extern void HTSugFilenames_free(void);
+    extern void LYAddHilite(int cur, char *text, int x);
+    extern void LYAddHtmlSep(char **path);
+    extern void LYAddHtmlSep0(char *path);
+    extern void LYAddLocalhostAlias(char *alias);
+    extern void LYAddPathSep(char **path);
+    extern void LYAddPathSep0(char *path);
+    extern void LYAddPathToHome(char *fbuffer, size_t fbuffer_size, const char *fname);
+    extern void LYCheckBibHost(void);
+    extern void LYCheckMail(void);
+    extern void LYCleanupTemp(void);
+    extern void LYCloseTemp(char *name);
+    extern void LYCloseTempFP(FILE *fp);
+    extern void LYConvertToURL(char **AllocatedString, int fixit);
+    extern void LYDoCSI(char *url, const char *comment, char **csi);
+    extern void LYEnsureAbsoluteURL(char **href, const char *name, int fixit);
+    extern void LYFakeZap(BOOL set);
+    extern void LYFixCursesOn(const char *reason);
+    extern void LYFreeHilites(int first, int last);
+    extern void LYFreeStringList(HTList *list);
+    extern void LYLocalFileToURL(char **target, const char *source);
+    extern void LYLocalhostAliases_free(void);
+    extern void LYRenamedTemp(char *oldname, char *newname);
+    extern void LYSetHilite(int cur, const char *text);
+    extern void LYTrimHtmlSep(char *path);
+    extern void LYTrimPathSep(char *path);
+    extern void LYTrimRelFromAbsPath(char *path);
+    extern void LYhighlight(int flag, int cur, const char *target);
+    extern void LYmsec_delay(unsigned msec);
+    extern void LYsetXDisplay(char *new_display);
+    extern void WriteInternalTitle(FILE *fp0, const char *Title);
+    extern void change_sug_filename(char *fname);
+    extern void convert_to_spaces(char *string, BOOL condense);
+    extern void free_and_clear(char **obj);
+    extern void noviceline(int more_flag);
+    extern void parse_restrictions(const char *s);
+    extern void print_restrictions_to_fd(FILE *fp);
+    extern void remove_backslashes(char *buf);
+    extern void size_change(int sig);
+    extern void statusline(const char *text);
+    extern void toggle_novice_line(void);
+
+#if defined(MULTI_USER_UNIX)
+    extern BOOL IsOurFile(const char *name);
+#else
+#define IsOurFile(name) TRUE
+#endif
+
+#ifdef USE_ASCII_CTYPES
+    extern int ascii_tolower(int i);
+    extern int ascii_toupper(int i);
+    extern int ascii_isupper(int i);
+#endif
+
+#ifdef __CYGWIN__
+    extern int Cygwin_Shell(void);
+#endif
+
+#if defined(_WIN_CC) || defined(WIN_EX)
+    extern int exec_command(char *cmd, int wait_flag);	/* xsystem.c */
+    extern char *quote_pathname(char *pathname);
+    extern int xsystem(char *cmd);
+#endif
+
+/* Keeping track of User Interface Pages: */
+    typedef enum {
+	UIP_UNKNOWN = -1
+	,UIP_HISTORY = 0
+	,UIP_DOWNLOAD_OPTIONS
+	,UIP_PRINT_OPTIONS
+	,UIP_SHOWINFO
+	,UIP_LIST_PAGE
+	,UIP_VLINKS
+	,UIP_LYNXCFG
+	,UIP_OPTIONS_MENU
+	,UIP_DIRED_MENU
+	,UIP_PERMIT_OPTIONS
+	,UIP_UPLOAD_OPTIONS
+	,UIP_ADDRLIST_PAGE
+	,UIP_CONFIG_DEF
+	,UIP_TRACELOG
+	,UIP_INSTALL
+    } UIP_t;
+
+#define UIP_P_FRAG 0x0001	/* flag: consider "url#frag" as matching "url" */
+
+    extern BOOL LYIsUIPage3(const char *url, UIP_t type, int flagparam);
+
+#define LYIsUIPage(url,type) LYIsUIPage3(url, type, UIP_P_FRAG)
+    extern void LYRegisterUIPage(const char *url, UIP_t type);
+
+#define LYUnRegisterUIPage(type) LYRegisterUIPage(NULL, type)
+    extern void LYUIPages_free(void);
+
+#ifdef CAN_CUT_AND_PASTE
+    extern int put_clip(const char *szBuffer);
+
+/* get_clip_grab() returns a pointer to the string in the system area.
+   get_clip_release() should be called ASAP after this. */
+    extern char *get_clip_grab(void);
+    extern void get_clip_release(void);
+
+#  ifdef WIN_EX
+#    define size_clip()	8192
+#  else
+    extern int size_clip(void);
+
+#  endif
+#endif
+
+#if defined(WIN_EX)		/* 1997/10/16 (Thu) 20:13:28 */
+    extern char *HTDOS_short_name(const char *path);
+    extern char *w32_strerror(DWORD ercode);
+#endif
+
+#ifdef VMS
+    extern void Define_VMSLogical(char *LogicalName, char *LogicalValue);
+#endif				/* VMS */
+
+#if ! HAVE_PUTENV
+    extern int putenv(const char *string);
+#endif				/* HAVE_PUTENV */
+
+#if defined(MULTI_USER_UNIX)
+    extern void LYRelaxFilePermissions(const char *name);
+
+#else
+#define LYRelaxFilePermissions(name)	/* nothing */
+#endif
+
+#if defined(_WINDOWS)
+    extern int win32_check_interrupt(void);
+#endif
+
+/*
+ *  Whether or not the status line must be shown.
+ */
+    extern BOOLEAN mustshow;
+
+#define _statusline(msg)	mustshow = TRUE, statusline(msg)
+
+/*
+ *  For is_url().
+ *
+ *  Universal document id types (see LYCheckForProxyURL)
+ */
+    typedef enum {
+	NOT_A_URL_TYPE = 0,
+	UNKNOWN_URL_TYPE = 1,	/* must be nonzero */
+
+	HTTP_URL_TYPE,
+	FILE_URL_TYPE,
+	FTP_URL_TYPE,
+	NCFTP_URL_TYPE,
+	WAIS_URL_TYPE,
+	NEWS_URL_TYPE,
+	NNTP_URL_TYPE,
+	TELNET_URL_TYPE,
+	TN3270_URL_TYPE,
+	RLOGIN_URL_TYPE,
+	GOPHER_URL_TYPE,
+	HTML_GOPHER_URL_TYPE,
+	TELNET_GOPHER_URL_TYPE,
+	INDEX_GOPHER_URL_TYPE,
+	MAILTO_URL_TYPE,
+	BIBP_URL_TYPE,
+	FINGER_URL_TYPE,
+	CSO_URL_TYPE,
+	HTTPS_URL_TYPE,
+	SNEWS_URL_TYPE,
+	PROSPERO_URL_TYPE,
+	AFS_URL_TYPE,
+
+	DATA_URL_TYPE,
+
+	LYNXCGI_URL_TYPE,
+	LYNXEXEC_URL_TYPE,
+	LYNXPROG_URL_TYPE,
+
+	NEWSPOST_URL_TYPE,
+	NEWSREPLY_URL_TYPE,
+	SNEWSPOST_URL_TYPE,
+	SNEWSREPLY_URL_TYPE,
+
+	LYNXCACHE_URL_TYPE,
+	LYNXCFG_URL_TYPE,
+	LYNXCOMPILE_OPTS_URL_TYPE,
+	LYNXCOOKIE_URL_TYPE,
+	LYNXDIRED_URL_TYPE,
+	LYNXDOWNLOAD_URL_TYPE,
+	LYNXHIST_URL_TYPE,
+	LYNXIMGMAP_URL_TYPE,
+	LYNXKEYMAP_URL_TYPE,
+	LYNXMESSAGES_URL_TYPE,
+	LYNXOPTIONS_URL_TYPE,
+	LYNXPRINT_URL_TYPE,
+
+	PROXY_URL_TYPE
+
+    } UrlTypes;
+
+    extern UrlTypes LYCheckForProxyURL(char *filename);
+    extern UrlTypes is_url(char *filename);
+
+/* common URLs */
+#define STR_BIBP_URL         "bibp:"
+#define LEN_BIBP_URL         5
+#define isBIBP_URL(addr)     !strncasecomp(addr, STR_BIBP_URL, LEN_BIBP_URL)
+
+#define STR_CSO_URL          "cso:"
+#define LEN_CSO_URL          4
+#define isCSO_URL(addr)      !strncasecomp(addr, STR_CSO_URL, LEN_CSO_URL)
+
+#define STR_FILE_URL         "file:"
+#define LEN_FILE_URL         5
+#define isFILE_URL(addr)     ((*addr == 'f' || *addr == 'F') &&\
+                             !strncasecomp(addr, STR_FILE_URL, LEN_FILE_URL))
+
+#define STR_FINGER_URL       "finger:"
+#define LEN_FINGER_URL       7
+#define isFINGER_URL(addr)   !strncasecomp(addr, STR_FINGER_URL, LEN_FINGER_URL)
+
+#define STR_FTP_URL          "ftp:"
+#define LEN_FTP_URL          4
+#define isFTP_URL(addr)      !strncasecomp(addr, STR_FTP_URL, LEN_FTP_URL)
+
+#define STR_GOPHER_URL       "gopher:"
+#define LEN_GOPHER_URL       7
+#define isGOPHER_URL(addr)   !strncasecomp(addr, STR_GOPHER_URL, LEN_GOPHER_URL)
+
+#define STR_HTTP_URL         "http:"
+#define LEN_HTTP_URL         5
+#define isHTTP_URL(addr)     !strncasecomp(addr, STR_HTTP_URL, LEN_HTTP_URL)
+
+#define STR_HTTPS_URL        "https:"
+#define LEN_HTTPS_URL        6
+#define isHTTPS_URL(addr)    !strncasecomp(addr, STR_HTTPS_URL, LEN_HTTPS_URL)
+
+#define STR_MAILTO_URL       "mailto:"
+#define LEN_MAILTO_URL       7
+#define isMAILTO_URL(addr)   !strncasecomp(addr, STR_MAILTO_URL, LEN_MAILTO_URL)
+
+#define STR_NEWS_URL         "news:"
+#define LEN_NEWS_URL         5
+#define isNEWS_URL(addr)     !strncasecomp(addr, STR_NEWS_URL, LEN_NEWS_URL)
+
+#define STR_NNTP_URL         "nntp:"
+#define LEN_NNTP_URL         5
+#define isNNTP_URL(addr)     !strncasecomp(addr, STR_NNTP_URL, LEN_NNTP_URL)
+
+#define STR_RLOGIN_URL       "rlogin:"
+#define LEN_RLOGIN_URL       7
+#define isRLOGIN_URL(addr)   !strncasecomp(addr, STR_RLOGIN_URL, LEN_RLOGIN_URL)
+
+#define STR_SNEWS_URL        "snews:"
+#define LEN_SNEWS_URL        6
+#define isSNEWS_URL(addr)    !strncasecomp(addr, STR_SNEWS_URL, LEN_SNEWS_URL)
+
+#define STR_TELNET_URL       "telnet:"
+#define LEN_TELNET_URL       7
+#define isTELNET_URL(addr)   !strncasecomp(addr, STR_TELNET_URL, LEN_TELNET_URL)
+
+#define STR_TN3270_URL       "tn3270:"
+#define LEN_TN3270_URL       7
+#define isTN3270_URL(addr)   !strncasecomp(addr, STR_TN3270_URL, LEN_TN3270_URL)
+
+#define STR_WAIS_URL         "wais:"
+#define LEN_WAIS_URL         5
+#define isWAIS_URL(addr)     !strncasecomp(addr, STR_WAIS_URL, LEN_WAIS_URL)
+
+/* internal URLs */
+#define STR_LYNXCACHE        "LYNXCACHE:"
+#define LEN_LYNXCACHE        10
+#define isLYNXCACHE(addr)    !strncasecomp(addr, STR_LYNXCACHE, LEN_LYNXCACHE)
+
+#define STR_LYNXCFG          "LYNXCFG:"
+#define LEN_LYNXCFG          8
+#define isLYNXCFG(addr)      !strncasecomp(addr, STR_LYNXCFG, LEN_LYNXCFG)
+
+#define STR_LYNXCFLAGS       "LYNXCOMPILEOPTS:"
+#define LEN_LYNXCFLAGS       16
+#define isLYNXCFLAGS(addr)   !strncasecomp(addr, STR_LYNXCFLAGS, LEN_LYNXCFLAGS)
+
+#define STR_LYNXCGI          "lynxcgi:"
+#define LEN_LYNXCGI          8
+#define isLYNXCGI(addr)      ((*addr == 'l' || *addr == 'L') &&\
+                             !strncasecomp(addr, STR_LYNXCGI, LEN_LYNXCGI))
+
+#define STR_LYNXCOOKIE       "LYNXCOOKIE:"
+#define LEN_LYNXCOOKIE       11
+#define isLYNXCOOKIE(addr)   !strncasecomp(addr, STR_LYNXCOOKIE, LEN_LYNXCOOKIE)
+
+#define STR_LYNXDIRED        "LYNXDIRED:"
+#define LEN_LYNXDIRED        10
+#define isLYNXDIRED(addr)    !strncasecomp(addr, STR_LYNXDIRED, LEN_LYNXDIRED)
+
+#define STR_LYNXEXEC         "lynxexec:"
+#define LEN_LYNXEXEC         9
+#define isLYNXEXEC(addr)     ((*addr == 'l' || *addr == 'L') &&\
+                             !strncasecomp(addr, STR_LYNXEXEC, LEN_LYNXEXEC))
+
+#define STR_LYNXDOWNLOAD     "LYNXDOWNLOAD:"
+#define LEN_LYNXDOWNLOAD     13
+#define isLYNXDOWNLOAD(addr) !strncasecomp(addr, STR_LYNXDOWNLOAD, LEN_LYNXDOWNLOAD)
+
+#define STR_LYNXHIST         "LYNXHIST:"
+#define LEN_LYNXHIST         9
+#define isLYNXHIST(addr)     !strncasecomp(addr, STR_LYNXHIST, LEN_LYNXHIST)
+
+#define STR_LYNXKEYMAP       "LYNXKEYMAP:"
+#define LEN_LYNXKEYMAP       11
+#define isLYNXKEYMAP(addr)   !strncasecomp(addr, STR_LYNXKEYMAP, LEN_LYNXKEYMAP)
+
+#define STR_LYNXIMGMAP       "LYNXIMGMAP:"
+#define LEN_LYNXIMGMAP       11
+#define isLYNXIMGMAP(addr)   !strncasecomp(addr, STR_LYNXIMGMAP, LEN_LYNXIMGMAP)
+
+#define STR_LYNXMESSAGES     "LYNXMESSAGES:"
+#define LEN_LYNXMESSAGES     13
+#define isLYNXMESSAGES(addr) !strncasecomp(addr, STR_LYNXMESSAGES, LEN_LYNXMESSAGES)
+
+#define STR_LYNXOPTIONS      "LYNXOPTIONS:"
+#define LEN_LYNXOPTIONS      12
+#define isLYNXOPTIONS(addr)  !strncasecomp(addr, STR_LYNXOPTIONS, LEN_LYNXOPTIONS)
+
+#define STR_LYNXPRINT        "LYNXPRINT:"
+#define LEN_LYNXPRINT        10
+#define isLYNXPRINT(addr)    !strncasecomp(addr, STR_LYNXPRINT, LEN_LYNXPRINT)
+
+#define STR_LYNXPROG         "lynxprog:"
+#define LEN_LYNXPROG         9
+#define isLYNXPROG(addr)     ((*addr == 'l' || *addr == 'L') &&\
+                             !strncasecomp(addr, STR_LYNXPROG, LEN_LYNXPROG))
+
+#define LYNXOPTIONS_PAGE(s)  STR_LYNXOPTIONS s
+/*
+ *  For change_sug_filename().
+ */
+    extern HTList *sug_filenames;
+
+/*
+ * syslog() facility
+ */
+#if defined(SYSLOG_REQUESTED_URLS)
+    extern void LYOpenlog(const char *banner);
+    extern void LYSyslog(char *arg);
+    extern void LYCloselog(void);
+#endif				/* SYSLOG_REQUESTED_URLS */
+
+/*
+ *  Miscellaneous.
+ */
+#define ON      1
+#define OFF     0
+#define STREQ(a,b) (strcmp(a,b) == 0)
+#define STRNEQ(a,b,c) (strncmp(a,b,c) == 0)
+
+#define HIDE_CHMOD 0600
+#define HIDE_UMASK 0077
+
+#if defined(DOSPATH) || defined(__CYGWIN__)
+#define TXT_R	"rt"
+#define TXT_W	"wt"
+#define TXT_A	"at+"
+#else
+#define TXT_R	"r"
+#define TXT_W	"w"
+#define TXT_A	"a+"
+#endif
+
+#define BIN_R	"rb"
+#define BIN_W	"wb"
+#define BIN_A	"ab+"
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* LYUTILS_H */
diff --git a/src/LYVMSdef.h b/src/LYVMSdef.h
new file mode 100644
index 00000000..6fde3c36
--- /dev/null
+++ b/src/LYVMSdef.h
@@ -0,0 +1,18 @@
+
+#ifndef LYVMSDEF_H
+#define LYVMSDEF_H
+
+/*
+ *  These are VMS system definitions which may not be in the headers
+ *  of old VMS compilers and contain non-ANSI extended tokens that
+ *  generate warnings by some Unix compilers while looking for the
+ *  "#endif" which closes the outer "#ifdef VMS".
+ */
+#ifndef CLI$M_TRUSTED
+#define CLI$M_TRUSTED 64	/* May not be in the compiler's clidef.h */
+#endif /* !CLI$M_TRUSTED */
+#ifndef LIB$_INVARG
+#define LIB$_INVARG 1409588	/* May not be in the compiler's libdef.h */
+#endif /* !LIB$_INVARG */
+
+#endif /* LYVMSDEF_H */
diff --git a/src/LYebcdic.c b/src/LYebcdic.c
new file mode 100644
index 00000000..30c98220
--- /dev/null
+++ b/src/LYebcdic.c
@@ -0,0 +1,48 @@
+/*
+ * $LynxId: LYebcdic.c,v 1.1 2008/12/30 01:03:05 Paul.Gilmartin Exp $
+ */
+#include <HTUtils.h>
+
+#ifdef  EBCDIC
+/* *INDENT-OFF* */
+const       char un_IBM1047[ 256 ] = /* ETOA OEMVS311 */
+{
+0x00,0x01,0x02,0x03,0x9c,0x09,0x86,0x7f,0x97,0x8d,0x8e,0x0b,0x0c,0x0d,0x0e,0x0f,
+0x10,0x11,0x12,0x13,0x9d,0x0a,0x08,0x87,0x18,0x19,0x92,0x8f,0x1c,0x1d,0x1e,0x1f,
+0x80,0x81,0x82,0x83,0x84,0x85,0x17,0x1b,0x88,0x89,0x8a,0x8b,0x8c,0x05,0x06,0x07,
+0x90,0x91,0x16,0x93,0x94,0x95,0x96,0x04,0x98,0x99,0x9a,0x9b,0x14,0x15,0x9e,0x1a,
+0x20,0xa0,0xe2,0xe4,0xe0,0xe1,0xe3,0xe5,0xe7,0xf1,0xa2,0x2e,0x3c,0x28,0x2b,0x7c,
+0x26,0xe9,0xea,0xeb,0xe8,0xed,0xee,0xef,0xec,0xdf,0x21,0x24,0x2a,0x29,0x3b,0x5e,
+0x2d,0x2f,0xc2,0xc4,0xc0,0xc1,0xc3,0xc5,0xc7,0xd1,0xa6,0x2c,0x25,0x5f,0x3e,0x3f,
+0xf8,0xc9,0xca,0xcb,0xc8,0xcd,0xce,0xcf,0xcc,0x60,0x3a,0x23,0x40,0x27,0x3d,0x22,
+0xd8,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0xab,0xbb,0xf0,0xfd,0xfe,0xb1,
+0xb0,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,0x70,0x71,0x72,0xaa,0xba,0xe6,0xb8,0xc6,0xa4,
+0xb5,0x7e,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0xa1,0xbf,0xd0,0x5b,0xde,0xae,
+0xac,0xa3,0xa5,0xb7,0xa9,0xa7,0xb6,0xbc,0xbd,0xbe,0xdd,0xa8,0xaf,0x5d,0xb4,0xd7,
+0x7b,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0xad,0xf4,0xf6,0xf2,0xf3,0xf5,
+0x7d,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0xb9,0xfb,0xfc,0xf9,0xfa,0xff,
+0x5c,0xf7,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0xb2,0xd4,0xd6,0xd2,0xd3,0xd5,
+0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0xb3,0xdb,0xdc,0xd9,0xda,0x9f
+} ;
+const unsigned char IBM1047[ 256 ] = /* ATOE OEMVS311 */
+{
+0x00,0x01,0x02,0x03,0x37,0x2d,0x2e,0x2f,0x16,0x05,0x15,0x0b,0x0c,0x0d,0x0e,0x0f,
+0x10,0x11,0x12,0x13,0x3c,0x3d,0x32,0x26,0x18,0x19,0x3f,0x27,0x1c,0x1d,0x1e,0x1f,
+0x40,0x5a,0x7f,0x7b,0x5b,0x6c,0x50,0x7d,0x4d,0x5d,0x5c,0x4e,0x6b,0x60,0x4b,0x61,
+0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0x7a,0x5e,0x4c,0x7e,0x6e,0x6f,
+0x7c,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,
+0xd7,0xd8,0xd9,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xad,0xe0,0xbd,0x5f,0x6d,
+0x79,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x91,0x92,0x93,0x94,0x95,0x96,
+0x97,0x98,0x99,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xc0,0x4f,0xd0,0xa1,0x07,
+0x20,0x21,0x22,0x23,0x24,0x25,0x06,0x17,0x28,0x29,0x2a,0x2b,0x2c,0x09,0x0a,0x1b,
+0x30,0x31,0x1a,0x33,0x34,0x35,0x36,0x08,0x38,0x39,0x3a,0x3b,0x04,0x14,0x3e,0xff,
+0x41,0xaa,0x4a,0xb1,0x9f,0xb2,0x6a,0xb5,0xbb,0xb4,0x9a,0x8a,0xb0,0xca,0xaf,0xbc,
+0x90,0x8f,0xea,0xfa,0xbe,0xa0,0xb6,0xb3,0x9d,0xda,0x9b,0x8b,0xb7,0xb8,0xb9,0xab,
+0x64,0x65,0x62,0x66,0x63,0x67,0x9e,0x68,0x74,0x71,0x72,0x73,0x78,0x75,0x76,0x77,
+0xac,0x69,0xed,0xee,0xeb,0xef,0xec,0xbf,0x80,0xfd,0xfe,0xfb,0xfc,0xba,0xae,0x59,
+0x44,0x45,0x42,0x46,0x43,0x47,0x9c,0x48,0x54,0x51,0x52,0x53,0x58,0x55,0x56,0x57,
+0x8c,0x49,0xcd,0xce,0xcb,0xcf,0xcc,0xe1,0x70,0xdd,0xde,0xdb,0xdc,0x8d,0x8e,0xdf
+} ;
+/* *INDENT-ON* */
+
+#endif /* EBCDIC */
diff --git a/src/LYexit.c b/src/LYexit.c
new file mode 100644
index 00000000..296f61f7
--- /dev/null
+++ b/src/LYexit.c
@@ -0,0 +1,185 @@
+/*
+ * $LynxId: LYexit.c,v 1.35 2009/03/10 00:12:52 tom Exp $
+ *
+ *	Copyright (c) 1994, University of Kansas, All Rights Reserved
+ *	(most of this file was rewritten in 1996 and 2004).
+ */
+#include <HTUtils.h>
+#include <LYexit.h>
+#include <HTAlert.h>
+#ifndef VMS
+#include <LYGlobalDefs.h>
+#include <LYUtils.h>
+#include <LYSignal.h>
+#include <LYMainLoop.h>
+#endif /* !VMS */
+#include <LYStrings.h>
+#include <LYClean.h>
+
+/*
+ * Flag for outofmem macro.  - FM
+ */
+BOOL LYOutOfMemory = FALSE;
+
+/*
+ * Stack of functions to call upon exit.
+ */
+static void (*callstack[ATEXITSIZE]) (void);
+static int topOfStack = 0;
+
+/*
+ * Purpose:		Registers termination function.
+ * Arguments:		function	The function to register.
+ * Return Value:	int	0	registered
+ *				!0	no more space to register
+ * Remarks/Portability/Dependencies/Restrictions:
+ * Revision History:
+ *	06-15-94	created Lynx 2-3-1 Garrett Arch Blythe
+ */
+
+int LYatexit(void (*function) (void))
+{
+    /*
+     * Check for available space.
+     */
+    if (topOfStack == ATEXITSIZE) {
+	CTRACE((tfp, "(LY)atexit: Too many functions, ignoring one!\n"));
+	return (-1);
+    }
+
+    /*
+     * Register the function.
+     */
+    callstack[topOfStack] = function;
+    topOfStack++;
+    return (0);
+}
+
+/*
+ * Purpose:		Call the functions registered with LYatexit
+ * Arguments:		void
+ * Return Value:	void
+ * Remarks/Portability/Dependencies/Restrictions:
+ * Revision History:
+ *	06-15-94	created Lynx 2-3-1 Garrett Arch Blythe
+ */
+static void LYCompleteExit(void)
+{
+    /*
+     * Just loop through registered functions.  This is reentrant if more exits
+     * occur in the registered functions.
+     */
+    while (--topOfStack >= 0) {
+	callstack[topOfStack] ();
+    }
+}
+
+/*
+ * Purpose:		Terminates program, reports memory not freed.
+ * Arguments:		status	Exit code.
+ * Return Value:	void
+ * Remarks/Portability/Dependencies/Restrictions:
+ *	Function calls stdlib.h exit
+ * Revision History:
+ *	06-15-94	created Lynx 2-3-1 Garrett Arch Blythe
+ */
+void LYexit(int status)
+{
+#ifndef VMS			/*  On VMS, the VMSexit() handler does these. - FM */
+#ifdef _WINDOWS
+    DeleteCriticalSection(&critSec_DNS);
+    DeleteCriticalSection(&critSec_READ);
+
+    WSACleanup();
+#endif
+    if (LYOutOfMemory == TRUE) {
+	/*
+	 * Ignore further interrupts.  - FM
+	 */
+#ifndef NOSIGHUP
+	(void) signal(SIGHUP, SIG_IGN);
+#endif /* NOSIGHUP */
+	(void) signal(SIGTERM, SIG_IGN);
+	(void) signal(SIGINT, SIG_IGN);
+#ifndef __linux__
+#ifdef SIGBUS
+	(void) signal(SIGBUS, SIG_IGN);
+#endif /* SIGBUS */
+#endif /* !__linux__ */
+	(void) signal(SIGSEGV, SIG_IGN);
+	(void) signal(SIGILL, SIG_IGN);
+
+	/*
+	 * Flush all messages.  - FM
+	 */
+	fflush(stderr);
+	fflush(stdout);
+
+	/*
+	 * Deal with curses, if on, and clean up.  - FM
+	 */
+	if (LYCursesON) {
+	    LYSleepAlert();
+	}
+	cleanup_sig(0);
+#ifndef __linux__
+#ifdef SIGBUS
+	signal(SIGBUS, SIG_DFL);
+#endif /* SIGBUS */
+#endif /* !__linux__ */
+	signal(SIGSEGV, SIG_DFL);
+	signal(SIGILL, SIG_DFL);
+    }
+#endif /* !VMS */
+
+    /*
+     * Close syslog before doing atexit-cleanup, since it may use a string
+     * that would be freed there.
+     */
+#ifdef SYSLOG_REQUESTED_URLS
+    LYCloselog();
+#endif
+
+    /*
+     * Do functions registered with LYatexit.  - GAB
+     */
+    LYCompleteExit();
+
+    LYCloseCmdLogfile();
+
+#ifdef exit
+/*  Make sure we use stdlib exit and not LYexit. - GAB
+*/
+#undef exit
+#endif /* exit */
+
+    cleanup_files();		/* if someone starts with LYNXfoo: page */
+#ifndef VMS			/*  On VMS, the VMSexit() handler does these. - FM */
+    fflush(stderr);
+    if (LYOutOfMemory == TRUE) {
+	LYOutOfMemory = FALSE;
+	printf("\r\n%s\r\n\r\n", MEMORY_EXHAUSTED_ABORT);
+	fflush(stdout);
+    }
+    LYCloseTracelog();
+#endif /* !VMS */
+    show_alloc();
+
+#if defined(NCURSES_VERSION) && defined(LY_FIND_LEAKS)
+#if defined(HAVE__NC_FREE_AND_EXIT)
+    _nc_free_and_exit(status);
+#elif defined(HAVE__NC_FREEALL)
+    _nc_freeall();
+#endif
+#endif /* NCURSES_VERSION */
+
+    exit(status);
+}
+
+void outofmem(const char *fname,
+	      const char *func)
+{
+    fprintf(stderr, "\n\n\n%s %s: %s\n", fname, func, MEMORY_EXHAUSTED_ABORTING);
+    LYOutOfMemory = TRUE;
+    LYexit(-1);
+}
diff --git a/src/LYmktime.c b/src/LYmktime.c
new file mode 100644
index 00000000..32de4275
--- /dev/null
+++ b/src/LYmktime.c
@@ -0,0 +1,337 @@
+/* $LynxId: LYmktime.c,v 1.9 2008/12/27 00:46:30 tom Exp $ */
+
+#include <LYStrings.h>
+#include <LYUtils.h>
+
+#include <parsdate.h>
+
+#ifdef TEST_DRIVER
+char *LYstrncpy(char *dst,
+		const char *src,
+		int n)
+{
+    char *val;
+    int len;
+
+    if (src == 0)
+	src = "";
+    len = strlen(src);
+
+    if (n < 0)
+	n = 0;
+
+    val = strncpy(dst, src, n);
+    if (len < n)
+	*(dst + len) = '\0';
+    else
+	*(dst + n) = '\0';
+    return val;
+}
+#define strcasecomp strcasecmp
+BOOLEAN WWW_TraceFlag = FALSE;
+FILE *TraceFP(void)
+{
+    return stderr;
+}
+#define USE_PARSDATE 0
+#else
+#define USE_PARSDATE 1
+#endif
+
+/*
+ * This function takes a string in the format
+ *	"Mon, 01-Jan-96 13:45:35 GMT" or
+ *	"Mon,  1 Jan 1996 13:45:35 GMT" or
+ *	"dd-mm-yyyy"
+ * as an argument, and returns its conversion to clock format (seconds since
+ * 00:00:00 Jan 1 1970), or 0 if the string doesn't match the expected pattern. 
+ * It also returns 0 if the time is in the past and the "absolute" argument is
+ * FALSE.  It is intended for handling 'expires' strings in Version 0 cookies
+ * homologously to 'max-age' strings in Version 1 cookies, for which 0 is the
+ * minimum, and greater values are handled as '[max-age seconds] + time(NULL)'. 
+ * If "absolute" if TRUE, we return the clock format value itself, but if
+ * anything goes wrong when parsing the expected patterns, we still return 0. 
+ * - FM
+ */
+time_t LYmktime(char *string,
+		BOOL absolute)
+{
+#if USE_PARSDATE
+    time_t result = 0;
+
+    if (non_empty(string)) {
+	CTRACE((tfp, "LYmktime: Parsing '%s'\n", string));
+	result = parsedate(string, 0);
+
+	if (!absolute) {
+	    if ((time((time_t *) 0) - result) >= 0)
+		result = 0;
+	}
+	if (result != 0) {
+	    CTRACE((tfp, "LYmktime: clock=%" PRI_time_t ", ctime=%s",
+		    CAST_time_t(result),
+		    ctime(&result)));
+	}
+    }
+    return result;
+#else
+    char *s;
+    time_t now, clock2;
+    int day, month, year, hour, minutes, seconds;
+    char *start;
+    char temp[8];
+
+    /*
+     * Make sure we have a string to parse.  - FM
+     */
+    if (!non_empty(string))
+	return (0);
+    s = string;
+    CTRACE((tfp, "LYmktime: Parsing '%s'\n", s));
+
+    /*
+     * Skip any lead alphabetic "Day, " field and seek a numeric day field.  -
+     * FM
+     */
+    while (*s != '\0' && !isdigit(UCH(*s)))
+	s++;
+    if (*s == '\0')
+	return (0);
+
+    /*
+     * Get the numeric day and convert to an integer.  - FM
+     */
+    start = s;
+    while (*s != '\0' && isdigit(UCH(*s)))
+	s++;
+    if (*s == '\0' || (s - start) > 2)
+	return (0);
+    LYstrncpy(temp, start, (int) (s - start));
+    day = atoi(temp);
+    if (day < 1 || day > 31)
+	return (0);
+
+    /*
+     * Get the month string and convert to an integer.  - FM
+     */
+    while (*s != '\0' && !isalnum(UCH(*s)))
+	s++;
+    if (*s == '\0')
+	return (0);
+    start = s;
+    while (*s != '\0' && isalnum(UCH(*s)))
+	s++;
+    if ((*s == '\0') ||
+	(s - start) < (isdigit(UCH(*(s - 1))) ? 2 : 3) ||
+	(s - start) > (isdigit(UCH(*(s - 1))) ? 2 : 9))
+	return (0);
+    LYstrncpy(temp, start, (isdigit(UCH(*(s - 1))) ? 2 : 3));
+    switch (TOUPPER(temp[0])) {
+    case '0':
+    case '1':
+	month = atoi(temp);
+	if (month < 1 || month > 12) {
+	    return (0);
+	}
+	break;
+    case 'A':
+	if (!strcasecomp(temp, "Apr")) {
+	    month = 4;
+	} else if (!strcasecomp(temp, "Aug")) {
+	    month = 8;
+	} else {
+	    return (0);
+	}
+	break;
+    case 'D':
+	if (!strcasecomp(temp, "Dec")) {
+	    month = 12;
+	} else {
+	    return (0);
+	}
+	break;
+    case 'F':
+	if (!strcasecomp(temp, "Feb")) {
+	    month = 2;
+	} else {
+	    return (0);
+	}
+	break;
+    case 'J':
+	if (!strcasecomp(temp, "Jan")) {
+	    month = 1;
+	} else if (!strcasecomp(temp, "Jun")) {
+	    month = 6;
+	} else if (!strcasecomp(temp, "Jul")) {
+	    month = 7;
+	} else {
+	    return (0);
+	}
+	break;
+    case 'M':
+	if (!strcasecomp(temp, "Mar")) {
+	    month = 3;
+	} else if (!strcasecomp(temp, "May")) {
+	    month = 5;
+	} else {
+	    return (0);
+	}
+	break;
+    case 'N':
+	if (!strcasecomp(temp, "Nov")) {
+	    month = 11;
+	} else {
+	    return (0);
+	}
+	break;
+    case 'O':
+	if (!strcasecomp(temp, "Oct")) {
+	    month = 10;
+	} else {
+	    return (0);
+	}
+	break;
+    case 'S':
+	if (!strcasecomp(temp, "Sep")) {
+	    month = 9;
+	} else {
+	    return (0);
+	}
+	break;
+    default:
+	return (0);
+    }
+
+    /*
+     * Get the numeric year string and convert to an integer.  - FM
+     */
+    while (*s != '\0' && !isdigit(UCH(*s)))
+	s++;
+    if (*s == '\0')
+	return (0);
+    start = s;
+    while (*s != '\0' && isdigit(UCH(*s)))
+	s++;
+    if ((s - start) == 4) {
+	LYstrncpy(temp, start, 4);
+    } else if ((s - start) == 2) {
+	now = time(NULL);
+	/*
+	 * Assume that received 2-digit dates >= 70 are 19xx; others
+	 * are 20xx.  Only matters when dealing with broken software
+	 * (HTTP server or web page) which is not Y2K compliant.  The
+	 * line is drawn on a best-guess basis; it is impossible for
+	 * this to be completely accurate because it depends on what
+	 * the broken sender software intends.  (This totally breaks
+	 * in 2100 -- setting up the next crisis...) - BL
+	 */
+	if (atoi(start) >= 70)
+	    LYstrncpy(temp, "19", 2);
+	else
+	    LYstrncpy(temp, "20", 2);
+	strncat(temp, start, 2);
+	temp[4] = '\0';
+    } else {
+	return (0);
+    }
+    year = atoi(temp);
+
+    /*
+     * Get the numeric hour string and convert to an integer.  - FM
+     */
+    while (*s != '\0' && !isdigit(UCH(*s)))
+	s++;
+    if (*s == '\0') {
+	hour = 0;
+	minutes = 0;
+	seconds = 0;
+    } else {
+	start = s;
+	while (*s != '\0' && isdigit(UCH(*s)))
+	    s++;
+	if (*s != ':' || (s - start) > 2)
+	    return (0);
+	LYstrncpy(temp, start, (int) (s - start));
+	hour = atoi(temp);
+
+	/*
+	 * Get the numeric minutes string and convert to an integer.  - FM
+	 */
+	while (*s != '\0' && !isdigit(UCH(*s)))
+	    s++;
+	if (*s == '\0')
+	    return (0);
+	start = s;
+	while (*s != '\0' && isdigit(UCH(*s)))
+	    s++;
+	if (*s != ':' || (s - start) > 2)
+	    return (0);
+	LYstrncpy(temp, start, (int) (s - start));
+	minutes = atoi(temp);
+
+	/*
+	 * Get the numeric seconds string and convert to an integer.  - FM
+	 */
+	while (*s != '\0' && !isdigit(UCH(*s)))
+	    s++;
+	if (*s == '\0')
+	    return (0);
+	start = s;
+	while (*s != '\0' && isdigit(UCH(*s)))
+	    s++;
+	if (*s == '\0' || (s - start) > 2)
+	    return (0);
+	LYstrncpy(temp, start, (int) (s - start));
+	seconds = atoi(temp);
+    }
+
+    /*
+     * Convert to clock format (seconds since 00:00:00 Jan 1 1970), but then
+     * zero it if it's in the past and "absolute" is not TRUE.  - FM
+     */
+    month -= 3;
+    if (month < 0) {
+	month += 12;
+	year--;
+    }
+    day += (year - 1968) * 1461 / 4;
+    day += ((((month * 153) + 2) / 5) - 672);
+    clock2 = (time_t) ((day * 60 * 60 * 24) +
+		       (hour * 60 * 60) +
+		       (minutes * 60) +
+		       seconds);
+    if (absolute == FALSE && (long) (time((time_t *) 0) - clock2) >= 0)
+	clock2 = (time_t) 0;
+    if (clock2 > 0)
+	CTRACE((tfp, "LYmktime: clock=%" PRI_time_t ", ctime=%s",
+		CAST_time_t(clock2),
+		ctime(&clock2)));
+
+    return (clock2);
+#endif
+}
+
+#ifdef TEST_DRIVER
+static void test_mktime(char *source)
+{
+    time_t before = LYmktime(source, TRUE);
+    time_t after = parsedate(source, 0);
+
+    printf("TEST %s\n", source);
+    printf("\t%" PRI_time_t "  %s", CAST_time_t(before), ctime(&before));
+    printf("\t%" PRI_time_t "  %s", CAST_time_t(after), ctime(&after));
+    if (before != after)
+	printf("\t****\n");
+}
+
+int main(void)
+{
+    test_mktime("Mon, 01-Jan-96 13:45:35 GMT");
+    test_mktime("Mon,  1 Jan 1996 13:45:35 GMT");
+    test_mktime("31-12-1999");
+    test_mktime("Wed May 14 22:00:00 2008");
+    test_mktime("Sun, 29-Jun-2008 23:19:30 GMT");
+    test_mktime("Sun Jul 06 07:00:00 2008 GMT");
+    return 0;
+}
+#endif
diff --git a/src/LYrcFile.c b/src/LYrcFile.c
new file mode 100644
index 00000000..1d10cdee
--- /dev/null
+++ b/src/LYrcFile.c
@@ -0,0 +1,1042 @@
+/* $LynxId: LYrcFile.c,v 1.82 2009/11/27 11:15:54 tom Exp $ */
+#include <HTUtils.h>
+#include <HTFTP.h>
+#include <LYUtils.h>
+#include <LYrcFile.h>
+#include <LYStrings.h>
+#include <LYGlobalDefs.h>
+#include <LYCharSets.h>
+#include <LYBookmark.h>
+#include <LYCookie.h>
+#include <LYKeymap.h>
+#include <HTMLDTD.h>
+
+#include <LYLeaks.h>
+
+#define MSG_ENABLE_LYNXRC N_("Normally disabled.  See ENABLE_LYNXRC in lynx.cfg\n")
+#define putBool(value) ((value) ? "on" : "off")
+/* *INDENT-OFF* */
+static Config_Enum tbl_DTD_recovery[] = {
+    { "true",		TRUE },
+    { "false",		FALSE },
+    { "on",		TRUE },
+    { "off",		FALSE },
+    { "sortasgml",	TRUE },
+    { "tagsoup",	FALSE },
+    { NULL,		-1 },
+};
+
+static Config_Enum tbl_bad_html[] = {
+    { "ignore",		BAD_HTML_IGNORE	 },
+    { "trace",		BAD_HTML_TRACE	 },
+    { "message",	BAD_HTML_MESSAGE },
+    { "warn",		BAD_HTML_WARN	 },
+    { NULL,		-1		 }
+};
+
+#ifdef DIRED_SUPPORT
+static Config_Enum tbl_dir_list_style[] = {
+    { "FILES_FIRST",	FILES_FIRST },
+    { "DIRECTORIES_FIRST", DIRS_FIRST },
+    { "MIXED_STYLE",	MIXED_STYLE },
+    { NULL,		MIXED_STYLE },
+};
+#ifdef LONG_LIST
+static Config_Enum tbl_dir_list_order[] = {
+    { "ORDER_BY_NAME",	ORDER_BY_NAME },
+    { "ORDER_BY_TYPE",	ORDER_BY_TYPE },
+    { "ORDER_BY_SIZE",  ORDER_BY_SIZE },
+    { "ORDER_BY_DATE",	ORDER_BY_DATE },
+    { "ORDER_BY_MODE",	ORDER_BY_MODE },
+#ifndef NO_GROUPS
+    { "ORDER_BY_USER",	ORDER_BY_USER },
+    { "ORDER_BY_GROUP",	ORDER_BY_GROUP },
+#endif
+    { NULL,		ORDER_BY_NAME },
+};
+#endif /* LONG_LIST */
+#endif /* DIRED_SUPPORT */
+
+static Config_Enum tbl_file_sort[] = {
+    { "BY_FILENAME",	FILE_BY_NAME },
+    { "BY_TYPE",	FILE_BY_TYPE },
+    { "BY_SIZE",	FILE_BY_SIZE },
+    { "BY_DATE",	FILE_BY_DATE },
+    { NULL,		-1 },
+};
+
+Config_Enum tbl_keypad_mode[] = {
+    { "FIELDS_ARE_NUMBERED", FIELDS_ARE_NUMBERED },
+    { "LINKS_AND_FIELDS_ARE_NUMBERED", LINKS_AND_FIELDS_ARE_NUMBERED },
+    { "LINKS_ARE_NUMBERED", LINKS_ARE_NUMBERED },
+    { "LINKS_ARE_NOT_NUMBERED", NUMBERS_AS_ARROWS },
+    /* obsolete variations: */
+    { "LINKS_AND_FORM_FIELDS_ARE_NUMBERED", LINKS_AND_FIELDS_ARE_NUMBERED },
+    { "NUMBERS_AS_ARROWS", NUMBERS_AS_ARROWS },
+    { NULL,		DEFAULT_KEYPAD_MODE }
+};
+
+Config_Enum tbl_multi_bookmarks[] = {
+    { "OFF",		MBM_OFF },
+    { "STANDARD",	MBM_STANDARD },
+    { "ON",		MBM_STANDARD },
+    { "ADVANCED",	MBM_ADVANCED },
+    { NULL,		-1 }
+};
+
+/* the names in this table are used as lowercase in HTTP.c */
+Config_Enum tbl_preferred_encoding[] = {
+    { "none",		encodingNONE },
+#if defined(USE_ZLIB) || defined(GZIP_PATH)
+    { "gzip",		encodingGZIP },
+    { "deflate",	encodingDEFLATE },
+#endif
+#if defined(USE_ZLIB) || defined(COMPRESS_PATH)
+    { "compress",	encodingCOMPRESS },
+#endif
+#if defined(USE_BZLIB) || defined(BZIP2_PATH)
+    { "bzip2",		encodingBZIP2 },
+#endif
+    { "all",		encodingALL },
+    { NULL,		-1 }
+};
+
+Config_Enum tbl_preferred_media[] = {
+    { "INTERNAL",	mediaOpt1 },
+    { "CONFIGFILE",	mediaOpt2 },
+    { "USER",		mediaOpt3 },
+    { "SYSTEM",		mediaOpt4 },
+    { "ALL",		mediaALL },
+    { NULL,		-1 }
+};
+
+static Config_Enum tbl_show_colors[] = {
+    { "default",	SHOW_COLOR_UNKNOWN },
+    { "default",	SHOW_COLOR_OFF },
+    { "default",	SHOW_COLOR_ON },
+    { "on",		SHOW_COLOR_UNKNOWN },
+    { "off",		SHOW_COLOR_UNKNOWN },
+    { "never",		SHOW_COLOR_NEVER },
+    { "always",		SHOW_COLOR_ALWAYS },
+    { NULL,		SHOW_COLOR_UNKNOWN }
+};
+
+Config_Enum tbl_transfer_rate[] = {
+    { "NONE",		rateOFF },
+    { "KB",		rateKB },
+    { "TRUE",		rateKB },
+    { "BYTES",		rateBYTES },
+    { "FALSE",		rateBYTES },
+#ifdef USE_READPROGRESS
+    { "KB,ETA",		rateEtaKB },
+    { "BYTES,ETA",	rateEtaBYTES },
+#endif
+#ifdef USE_PROGRESSBAR
+    { "METER",		rateBAR },
+    { "FALSE",		rateBAR },
+#endif
+    { NULL,		-1 },
+};
+
+Config_Enum tbl_user_mode[] = {
+    { "ADVANCED",	ADVANCED_MODE },
+    { "INTERMEDIATE",	INTERMEDIATE_MODE },
+    { "NOVICE",		NOVICE_MODE },
+    { NULL,		NOVICE_MODE }
+};
+
+static Config_Enum tbl_visited_links[] = {
+    { "FIRST_REVERSED",	VISITED_LINKS_AS_FIRST_V | VISITED_LINKS_REVERSE },
+    { "FIRST",		VISITED_LINKS_AS_FIRST_V },
+    { "TREE",		VISITED_LINKS_AS_TREE    },
+    { "LAST_REVERSED",	VISITED_LINKS_AS_LATEST | VISITED_LINKS_REVERSE },
+    { "LAST",		VISITED_LINKS_AS_LATEST  },
+    { NULL,		DEFAULT_VISITED_LINKS }
+};
+
+Config_Enum tbl_force_prompt[] = {
+    { "prompt",		FORCE_PROMPT_DFT	},
+    { "yes",		FORCE_PROMPT_YES	},
+    { "no",		FORCE_PROMPT_NO		},
+    { NULL,		-1			}
+};
+/* *INDENT-ON* */
+
+static BOOL getBool(char *src)
+{
+    return (BOOL) (!strncasecomp(src, "on", 2) || !strncasecomp(src, "true", 4));
+}
+
+const char *LYputEnum(Config_Enum * table, int value)
+{
+    while (table->name != 0) {
+	if (table->value == value) {
+	    return table->name;
+	}
+	table++;
+    }
+    return "?";
+}
+
+BOOL LYgetEnum(Config_Enum * table, const char *name,
+	       int *result)
+{
+    Config_Enum *found = 0;
+    unsigned len = strlen(name);
+    int match = 0;
+
+    if (len != 0) {
+	while (table->name != 0) {
+	    if (!strncasecomp(table->name, name, (int) len)) {
+		found = table;
+		if (!strcasecomp(table->name, name)) {
+		    match = 1;
+		    break;
+		}
+		++match;
+	    }
+	    table++;
+	}
+	if (match == 1) {	/* if unambiguous */
+	    *result = found->value;
+	    return TRUE;
+	}
+    }
+    return FALSE;		/* no match */
+}
+
+/* these are for data that are normally not read/written from .lynxrc */
+#define PARSE_SET(n,v,h)   {n,    1, CONF_BOOL,  UNION_SET(v), 0, 0, 0, h}
+#define PARSE_ARY(n,v,t,h) {n,    1, CONF_ARRAY, UNION_INT(v), t, 0, 0, h}
+#define PARSE_ENU(n,v,t,h) {n,    1, CONF_ENUM,  UNION_INT(v), 0, t, 0, h}
+#define PARSE_LIS(n,v,h)   {n,    1, CONF_LIS,   UNION_STR(v), 0, 0, 0, h}
+#define PARSE_STR(n,v,h)   {n,    1, CONF_STR,   UNION_STR(v), 0, 0, 0, h}
+#define PARSE_FUN(n,v,w,h) {n,    1, CONF_FUN,   UNION_FUN(v), 0, 0, w, h}
+#define PARSE_MBM(n,h)     {n,    1, CONF_MBM,   UNION_DEF(0), 0, 0, 0, h}
+
+/* these are for data that are optionally read/written from .lynxrc */
+#define MAYBE_SET(n,v,h)   {n,    0, CONF_BOOL,  UNION_SET(v), 0, 0, 0, h}
+#define MAYBE_ARY(n,v,t,h) {n,    0, CONF_ARRAY, UNION_INT(v), t, 0, 0, h}
+#define MAYBE_ENU(n,v,t,h) {n,    0, CONF_ENUM,  UNION_INT(v), 0, t, 0, h}
+#define MAYBE_LIS(n,v,h)   {n,    0, CONF_LIS,   UNION_STR(v), 0, 0, 0, h}
+#define MAYBE_STR(n,v,h)   {n,    0, CONF_STR,   UNION_STR(v), 0, 0, 0, h}
+#define MAYBE_FUN(n,v,w,h) {n,    0, CONF_FUN,   UNION_FUN(v), 0, 0, w, h}
+#define MAYBE_MBM(n,h)     {n,    0, CONF_MBM,   UNION_DEF(0), 0, 0, 0, h}
+
+#define PARSE_NIL          {NULL, 1, CONF_NIL,   UNION_DEF(0), 0, 0, 0, 0}
+
+typedef enum {
+    CONF_NIL = 0
+    ,CONF_ARRAY
+    ,CONF_BOOL
+    ,CONF_FUN
+    ,CONF_INT
+    ,CONF_ENUM
+    ,CONF_LIS
+    ,CONF_MBM
+    ,CONF_STR
+} Conf_Types;
+
+typedef struct config_type {
+    const char *name;
+    int enabled;		/* see lynx.cfg ENABLE_LYNXRC "off" lines */
+    Conf_Types type;
+      ParseData;
+    const char **strings;
+    Config_Enum *table;
+    void (*write_it) (FILE *fp, struct config_type *);
+    const char *note;
+} Config_Type;
+
+static int get_assume_charset(char *value)
+{
+    int i;
+
+    for (i = 0; i < LYNumCharsets; ++i) {
+	if (!strcasecomp(value, LYCharSet_UC[i].MIMEname)) {
+	    UCLYhndl_for_unspec = i;
+	    break;
+	}
+    }
+    return 0;
+}
+
+static void put_assume_charset(FILE *fp, struct config_type *tbl)
+{
+    int i;
+
+    for (i = 0; i < LYNumCharsets; ++i)
+	fprintf(fp, "#    %s\n", LYCharSet_UC[i].MIMEname);
+    fprintf(fp, "%s=%s\n\n", tbl->name, LYCharSet_UC[UCLYhndl_for_unspec].MIMEname);
+}
+
+static int get_display_charset(char *value)
+{
+    int i = 0;
+
+    i = UCGetLYhndl_byAnyName(value);	/* by MIME or full name */
+    if (i >= 0)
+	current_char_set = i;
+    return 0;
+}
+
+static void put_display_charset(FILE *fp, struct config_type *tbl)
+{
+    int i;
+
+    for (i = 0; LYchar_set_names[i]; i++)
+	fprintf(fp, "#    %s\n", LYchar_set_names[i]);
+    fprintf(fp, "%s=%s\n\n", tbl->name, LYchar_set_names[current_char_set]);
+}
+
+static int get_editor(char *value)
+{
+    if (!system_editor)
+	StrAllocCopy(editor, value);
+    return 0;
+}
+
+static void put_editor(FILE *fp, struct config_type *tbl)
+{
+    fprintf(fp, "%s=%s\n\n", tbl->name, NonNull(editor));
+}
+
+int get_tagsoup(char *value)
+{
+    int found = Old_DTD;
+
+    if (LYgetEnum(tbl_DTD_recovery, value, &found)
+	&& Old_DTD != found) {
+	Old_DTD = found;
+	HTSwitchDTD(!Old_DTD);
+    }
+    return 0;
+}
+
+static void put_tagsoup(FILE *fp, struct config_type *tbl)
+{
+    fprintf(fp, "%s=%s\n\n", tbl->name, LYputEnum(tbl_DTD_recovery, Old_DTD));
+}
+
+/* This table is searched ignoring case */
+/* *INDENT-OFF* */
+static Config_Type Config_Table [] =
+{
+    PARSE_SET(RC_ACCEPT_ALL_COOKIES,    LYAcceptAllCookies, N_("\
+accept_all_cookies allows the user to tell Lynx to automatically\n\
+accept all cookies if desired.  The default is \"FALSE\" which will\n\
+prompt for each cookie.  Set accept_all_cookies to \"TRUE\" to accept\n\
+all cookies.\n\
+")),
+    MAYBE_FUN(RC_ASSUME_CHARSET,        get_assume_charset, put_assume_charset, MSG_ENABLE_LYNXRC),
+#ifndef DISABLE_FTP
+    PARSE_STR(RC_ANONFTP_PASSWORD,      anonftp_password, N_("\
+anonftp_password allows the user to tell Lynx to use the personal\n\
+email address as the password for anonymous ftp.  If no value is given,\n\
+Lynx will use the personal email address.  Set anonftp_password\n\
+to a different value if you choose.\n\
+")),
+#endif
+    MAYBE_ENU(RC_BAD_HTML,              cfg_bad_html,      tbl_bad_html,
+	      MSG_ENABLE_LYNXRC),
+    PARSE_STR(RC_BOOKMARK_FILE,         bookmark_page,     N_("\
+bookmark_file specifies the name and location of the default bookmark\n\
+file into which the user can paste links for easy access at a later\n\
+date.\n\
+")),
+    PARSE_SET(RC_CASE_SENSITIVE_SEARCHING, case_sensitive, N_("\
+If case_sensitive_searching is \"on\" then when the user invokes a search\n\
+using the 's' or '/' keys, the search performed will be case sensitive\n\
+instead of case INsensitive.  The default is usually \"off\".\n\
+")),
+    PARSE_FUN(RC_CHARACTER_SET,         get_display_charset, put_display_charset, N_("\
+The character_set definition controls the representation of 8 bit\n\
+characters for your terminal.  If 8 bit characters do not show up\n\
+correctly on your screen you may try changing to a different 8 bit\n\
+set or using the 7 bit character approximations.\n\
+Current valid characters sets are:\n\
+")),
+    PARSE_LIS(RC_COOKIE_ACCEPT_DOMAINS, LYCookieAcceptDomains, N_("\
+cookie_accept_domains and cookie_reject_domains are comma-delimited\n\
+lists of domains from which Lynx should automatically accept or reject\n\
+all cookies.  If a domain is specified in both options, rejection will\n\
+take precedence.  The accept_all_cookies parameter will override any\n\
+settings made here.\n\
+")),
+#ifdef USE_PERSISTENT_COOKIES
+    PARSE_STR(RC_COOKIE_FILE,	        LYCookieFile, N_("\
+cookie_file specifies the file from which to read persistent cookies.\n\
+The default is ~/" FNAME_LYNX_COOKIES ".\n\
+")),
+#endif
+    PARSE_STR(RC_COOKIE_LOOSE_INVALID_DOMAINS, LYCookieLooseCheckDomains, N_("\
+cookie_loose_invalid_domains, cookie_strict_invalid_domains, and\n\
+cookie_query_invalid_domains are comma-delimited lists of which domains\n\
+should be subjected to varying degrees of validity checking.  If a\n\
+domain is set to strict checking, strict conformance to RFC2109 will\n\
+be applied.  A domain with loose checking will be allowed to set cookies\n\
+with an invalid path or domain attribute.  All domains will default to\n\
+querying the user for an invalid path or domain.\n\
+")),
+    PARSE_STR(RC_COOKIE_QUERY_INVALID_DOMAINS, LYCookieQueryCheckDomains, NULL),
+    PARSE_LIS(RC_COOKIE_REJECT_DOMAINS, LYCookieRejectDomains, NULL),
+    PARSE_STR(RC_COOKIE_STRICT_INVALID_DOMAIN, LYCookieStrictCheckDomains, NULL),
+#ifdef DIRED_SUPPORT
+#ifdef LONG_LIST
+    PARSE_ENU(RC_DIR_LIST_ORDER,        dir_list_order,     tbl_dir_list_order, N_("\
+dir_list_order specifies the directory list order under DIRED_SUPPORT\n\
+(if implemented).  The default is \"ORDER_BY_NAME\"\n\
+")),
+#endif
+    PARSE_ENU(RC_DIR_LIST_STYLE,        dir_list_style,     tbl_dir_list_style, N_("\
+dir_list_styles specifies the directory list style under DIRED_SUPPORT\n\
+(if implemented).  The default is \"MIXED_STYLE\", which sorts both\n\
+files and directories together.  \"FILES_FIRST\" lists files first and\n\
+\"DIRECTORIES_FIRST\" lists directories first.\n\
+")),
+#endif
+    MAYBE_STR(RC_DISPLAY,               x_display,          MSG_ENABLE_LYNXRC),
+    PARSE_SET(RC_EMACS_KEYS,            emacs_keys, N_("\
+If emacs_keys is to \"on\" then the normal EMACS movement keys:\n\
+  ^N = down    ^P = up\n\
+  ^B = left    ^F = right\n\
+will be enabled.\n\
+")),
+    PARSE_FUN(RC_FILE_EDITOR,           get_editor,         put_editor, N_("\
+file_editor specifies the editor to be invoked when editing local files\n\
+or sending mail.  If no editor is specified, then file editing is disabled\n\
+unless it is activated from the command line, and the built-in line editor\n\
+will be used for sending mail.\n\
+")),
+#ifndef DISABLE_FTP
+    PARSE_ENU(RC_FILE_SORTING_METHOD,   HTfileSortMethod,   tbl_file_sort, N_("\
+The file_sorting_method specifies which value to sort on when viewing\n\
+file lists such as FTP directories.  The options are:\n\
+   BY_FILENAME -- sorts on the name of the file\n\
+   BY_TYPE     -- sorts on the type of the file\n\
+   BY_SIZE     -- sorts on the size of the file\n\
+   BY_DATE     -- sorts on the date of the file\n\
+")),
+#endif
+    MAYBE_ENU(RC_FORCE_COOKIE_PROMPT,   cookie_noprompt,    tbl_force_prompt,
+	      MSG_ENABLE_LYNXRC),
+#ifdef USE_SSL
+    MAYBE_ENU(RC_FORCE_SSL_PROMPT,      ssl_noprompt,       tbl_force_prompt,
+	      MSG_ENABLE_LYNXRC),
+#endif
+#ifndef DISABLE_FTP
+    MAYBE_SET(RC_FTP_PASSIVE,           ftp_passive,        MSG_ENABLE_LYNXRC),
+#endif
+#ifdef EXP_KEYBOARD_LAYOUT
+    PARSE_ARY(RC_KBLAYOUT,              current_layout,     LYKbLayoutNames, NULL),
+#endif
+    PARSE_ENU(RC_KEYPAD_MODE,           keypad_mode,        tbl_keypad_mode, NULL),
+    PARSE_ARY(RC_LINEEDIT_MODE,         current_lineedit,   LYLineeditNames, N_("\
+lineedit_mode specifies the key binding used for inputting strings in\n\
+prompts and forms.  If lineedit_mode is set to \"Default Binding\" then\n\
+the following control characters are used for moving and deleting:\n\
+\n\
+             Prev  Next       Enter = Accept input\n\
+   Move char: <-    ->        ^G    = Cancel input\n\
+   Move word: ^P    ^N        ^U    = Erase line\n\
+ Delete char: ^H    ^R        ^A    = Beginning of line\n\
+ Delete word: ^B    ^F        ^E    = End of line\n\
+\n\
+Current lineedit modes are:\n\
+")),
+#ifdef USE_LOCALE_CHARSET
+    MAYBE_SET(RC_LOCALE_CHARSET,      LYLocaleCharset,        MSG_ENABLE_LYNXRC),
+#endif
+    MAYBE_SET(RC_MAKE_PSEUDO_ALTS_FOR_INLINES, pseudo_inline_alts, MSG_ENABLE_LYNXRC),
+    MAYBE_SET(RC_MAKE_LINKS_FOR_ALL_IMAGES, clickable_images, MSG_ENABLE_LYNXRC),
+    PARSE_MBM(RC_MULTI_BOOKMARK, N_("\
+The following allow you to define sub-bookmark files and descriptions.\n\
+The format is multi_bookmark<capital_letter>=<filename>,<description>\n\
+Up to 26 bookmark files (for the English capital letters) are allowed.\n\
+We start with \"multi_bookmarkB\" since 'A' is the default (see above).\n\
+")),
+    PARSE_STR(RC_PERSONAL_MAIL_ADDRESS, personal_mail_address, N_("\
+personal_mail_address specifies your personal mail address.  The\n\
+address will be sent during HTTP file transfers for authorization and\n\
+logging purposes, and for mailed comments.\n\
+If you do not want this information given out, set the NO_FROM_HEADER\n\
+to TRUE in lynx.cfg, or use the -nofrom command line switch.  You also\n\
+could leave this field blank, but then you won't have it included in\n\
+your mailed comments.\n\
+")),
+    PARSE_STR(RC_PREFERRED_CHARSET,     pref_charset, N_("\
+preferred_charset specifies the character set in MIME notation (e.g.,\n\
+ISO-8859-2, ISO-8859-5) which Lynx will indicate you prefer in requests\n\
+to http servers using an Accept-Charset header.  The value should NOT\n\
+include ISO-8859-1 or US-ASCII, since those values are always assumed\n\
+by default.  May be a comma-separated list.\n\
+If a file in that character set is available, the server will send it.\n\
+If no Accept-Charset header is present, the default is that any\n\
+character set is acceptable.  If an Accept-Charset header is present,\n\
+and if the server cannot send a response which is acceptable\n\
+according to the Accept-Charset header, then the server SHOULD send\n\
+an error response, though the sending of an unacceptable response\n\
+is also allowed.\n\
+")),
+    MAYBE_ENU(RC_PREFERRED_ENCODING,    LYAcceptEncoding,   tbl_preferred_encoding,
+	      MSG_ENABLE_LYNXRC),
+    PARSE_STR(RC_PREFERRED_LANGUAGE,    language, N_("\
+preferred_language specifies the language in MIME notation (e.g., en,\n\
+fr, may be a comma-separated list in decreasing preference)\n\
+which Lynx will indicate you prefer in requests to http servers.\n\
+If a file in that language is available, the server will send it.\n\
+Otherwise, the server will send the file in its default language.\n\
+")),
+    MAYBE_ENU(RC_PREFERRED_MEDIA_TYPES, LYAcceptMedia,      tbl_preferred_media,
+	      MSG_ENABLE_LYNXRC),
+    MAYBE_SET(RC_RAW_MODE,              LYRawMode,          MSG_ENABLE_LYNXRC),
+#if defined(ENABLE_OPTS_CHANGE_EXEC) && (defined(EXEC_LINKS) || defined(EXEC_SCRIPTS))
+    PARSE_SET(RC_RUN_ALL_EXECUTION_LINKS, local_exec, N_("\
+If run_all_execution_links is set \"on\" then all local execution links\n\
+will be executed when they are selected.\n\
+\n\
+WARNING - This is potentially VERY dangerous.  Since you may view\n\
+          information that is written by unknown and untrusted sources\n\
+          there exists the possibility that Trojan horse links could be\n\
+          written.  Trojan horse links could be written to erase files\n\
+          or compromise security.  This should only be set to \"on\" if\n\
+          you are viewing trusted source information.\n\
+")),
+    PARSE_SET(RC_RUN_EXECUTION_LINKS_LOCAL, local_exec_on_local_files, N_("\
+If run_execution_links_on_local_files is set \"on\" then all local\n\
+execution links that are found in LOCAL files will be executed when they\n\
+are selected.  This is different from run_all_execution_links in that\n\
+only files that reside on the local system will have execution link\n\
+permissions.\n\
+\n\
+WARNING - This is potentially dangerous.  Since you may view\n\
+          information that is written by unknown and untrusted sources\n\
+          there exists the possibility that Trojan horse links could be\n\
+          written.  Trojan horse links could be written to erase files\n\
+          or compromise security.  This should only be set to \"on\" if\n\
+          you are viewing trusted source information.\n\
+")),
+#endif
+#ifdef USE_SCROLLBAR
+    MAYBE_SET(RC_SCROLLBAR,             LYShowScrollbar, MSG_ENABLE_LYNXRC),
+#endif
+    PARSE_SET(RC_SELECT_POPUPS,         LYSelectPopups, N_("\
+select_popups specifies whether the OPTIONs in a SELECT block which\n\
+lacks a MULTIPLE attribute are presented as a vertical list of radio\n\
+buttons or via a popup menu.  Note that if the MULTIPLE attribute is\n\
+present in the SELECT start tag, Lynx always will create a vertical list\n\
+of checkboxes for the OPTIONs.  A value of \"on\" will set popup menus\n\
+as the default while a value of \"off\" will set use of radio boxes.\n\
+The default can be overridden via the -popup command line toggle.\n\
+")),
+    MAYBE_SET(RC_SEND_USERAGENT,        LYSendUserAgent,   MSG_ENABLE_LYNXRC),
+    MAYBE_SET(RC_SET_COOKIES,           LYSetCookies,      MSG_ENABLE_LYNXRC),
+    PARSE_ENU(RC_SHOW_COLOR,            LYrcShowColor,     tbl_show_colors, N_("\
+show_color specifies how to set the color mode at startup.  A value of\n\
+\"never\" will force color mode off (treat the terminal as monochrome)\n\
+at startup even if the terminal appears to be color capable.  A value of\n\
+\"always\" will force color mode on even if the terminal appears to be\n\
+monochrome, if this is supported by the library used to build lynx.\n\
+A value of \"default\" will yield the behavior of assuming\n\
+a monochrome terminal unless color capability is inferred at startup\n\
+based on the terminal type, or the -color command line switch is used, or\n\
+the COLORTERM environment variable is set.  The default behavior always is\n\
+used in anonymous accounts or if the \"option_save\" restriction is set.\n\
+The effect of the saved value can be overridden via\n\
+the -color and -nocolor command line switches.\n\
+The mode set at startup can be changed via the \"show color\" option in\n\
+the 'o'ptions menu.  If the option settings are saved, the \"on\" and\n\
+\"off\" \"show color\" settings will be treated as \"default\".\n\
+")),
+    PARSE_SET(RC_SHOW_CURSOR,           LYShowCursor, N_("\
+show_cursor specifies whether to 'hide' the cursor to the right (and\n\
+bottom, if possible) of the screen, or to place it to the left of the\n\
+current link in documents, or current option in select popup windows.\n\
+Positioning the cursor to the left of the current link or option is\n\
+helpful for speech or braille interfaces, and when the terminal is\n\
+one which does not distinguish the current link based on highlighting\n\
+or color.  A value of \"on\" will set positioning to the left as the\n\
+default while a value of \"off\" will set 'hiding' of the cursor.\n\
+The default can be overridden via the -show_cursor command line toggle.\n\
+")),
+    PARSE_SET(RC_SHOW_DOTFILES,         show_dotfiles, N_("\
+show_dotfiles specifies that the directory listing should include\n\
+\"hidden\" (dot) files/directories.  If set \"on\", this will be\n\
+honored only if enabled via userdefs.h and/or lynx.cfg, and not\n\
+restricted via a command line switch.  If display of hidden files\n\
+is disabled, creation of such files via Lynx also is disabled.\n\
+")),
+#ifdef USE_READPROGRESS
+    MAYBE_ENU(RC_SHOW_KB_RATE,          LYTransferRate,    tbl_transfer_rate,
+	      MSG_ENABLE_LYNXRC),
+#endif
+    PARSE_ENU(RC_SUB_BOOKMARKS,         LYMultiBookmarks,  tbl_multi_bookmarks, N_("\
+If sub_bookmarks is not turned \"off\", and multiple bookmarks have\n\
+been defined (see below), then all bookmark operations will first\n\
+prompt the user to select an active sub-bookmark file.  If the default\n\
+Lynx bookmark_file is defined (see above), it will be used as the\n\
+default selection.  When this option is set to \"advanced\", and the\n\
+user mode is advanced, the 'v'iew bookmark command will invoke a\n\
+statusline prompt instead of the menu seen in novice and intermediate\n\
+user modes.  When this option is set to \"standard\", the menu will be\n\
+presented regardless of user mode.\n\
+")),
+    MAYBE_FUN(RC_TAGSOUP,               get_tagsoup,        put_tagsoup,
+              MSG_ENABLE_LYNXRC),
+    MAYBE_SET(RC_UNDERLINE_LINKS,       LYUnderlineLinks,   MSG_ENABLE_LYNXRC),
+    PARSE_ENU(RC_USER_MODE,             user_mode,          tbl_user_mode, N_("\
+user_mode specifies the users level of knowledge with Lynx.  The\n\
+default is \"NOVICE\" which displays two extra lines of help at the\n\
+bottom of the screen to aid the user in learning the basic Lynx\n\
+commands.  Set user_mode to \"INTERMEDIATE\" to turn off the extra info.\n\
+Use \"ADVANCED\" to see the URL of the currently selected link at the\n\
+bottom of the screen.\n\
+")),
+    MAYBE_STR(RC_USERAGENT,             LYUserAgent,        MSG_ENABLE_LYNXRC),
+    PARSE_SET(RC_VERBOSE_IMAGES,        verbose_img, N_("\
+If verbose_images is \"on\", lynx will print the name of the image\n\
+source file in place of [INLINE], [LINK] or [IMAGE]\n\
+See also VERBOSE_IMAGES in lynx.cfg\n\
+")),
+    PARSE_SET(RC_VI_KEYS,               vi_keys, N_("\
+If vi_keys is set to \"on\", then the normal VI movement keys:\n\
+  j = down    k = up\n\
+  h = left    l = right\n\
+will be enabled.  These keys are only lower case.\n\
+Capital 'H', 'J' and 'K will still activate help, jump shortcuts,\n\
+and the keymap display, respectively.\n\
+")),
+    PARSE_ENU(RC_VISITED_LINKS,         Visited_Links_As,   tbl_visited_links, N_("\
+The visited_links setting controls how Lynx organizes the information\n\
+in the Visited Links Page.\n\
+")),
+#ifdef USE_SESSIONS
+    MAYBE_SET(RC_AUTO_SESSION,		LYAutoSession,	MSG_ENABLE_LYNXRC),
+    MAYBE_STR(RC_SESSION_FILE,		LYSessionFile,	MSG_ENABLE_LYNXRC),
+#endif
+    MAYBE_SET(RC_NO_PAUSE,		no_pause,	MSG_ENABLE_LYNXRC),
+
+    PARSE_NIL
+};
+/* *INDENT-ON* */
+
+static Config_Type *lookup_config(const char *name)
+{
+    Config_Type *tbl = Config_Table;
+    char ch = (char) TOUPPER(*name);
+
+    while (tbl->name != 0) {
+	if (tbl->enabled) {
+	    char ch1 = tbl->name[0];
+
+	    if ((ch == TOUPPER(ch1))
+		&& (0 == strcasecomp(name, tbl->name)))
+		break;
+	}
+
+	tbl++;
+    }
+    return tbl;
+}
+
+/* Read and process user options.  If the passed-in fp is NULL, open the
+ * regular user defaults file for reading, otherwise use fp which has to be a
+ * file open for reading.  - kw
+ */
+void read_rc(FILE *fp)
+{
+    char *buffer = NULL;
+    char rcfile[LY_MAXPATH];
+    char MBM_line[256];
+    int n;
+
+    if (!fp) {
+	/*
+	 * Make an RC file name, open it for reading.
+	 */
+	LYAddPathToHome(rcfile, sizeof(rcfile), FNAME_LYNXRC);
+	if ((fp = fopen(rcfile, TXT_R)) == NULL) {
+	    return;
+	}
+	CTRACE((tfp, "read_rc opened %s\n", rcfile));
+    } else {
+	CTRACE((tfp, "read_rc used passed-in stream\n"));
+    }
+
+    /*
+     * Process the entries.
+     */
+    while (LYSafeGets(&buffer, fp) != NULL) {
+	char *name, *value, *notes;
+	Config_Type *tbl;
+	ParseUnionPtr q;
+
+	/* Most lines in the config file are comment lines.  Weed them out
+	 * now.  Also, leading whitespace is ok, so trim it.
+	 */
+	LYTrimTrailing(buffer);
+	name = LYSkipBlanks(buffer);
+	if (ispunct(UCH(*name)) || *name == '\0')
+	    continue;
+
+	/*
+	 * Parse the "name=value" strings.
+	 */
+	if ((value = strchr(name, '=')) == 0) {
+	    CTRACE((tfp, "LYrcFile: missing '=' %s\n", name));
+	    continue;
+	}
+	*value++ = '\0';
+	LYTrimTrailing(name);
+	value = LYSkipBlanks(value);
+	CTRACE2(TRACE_CFG, (tfp, "LYrcFile %s:%s\n", name, value));
+
+	tbl = lookup_config(name);
+	if (tbl->name == 0) {
+	    const char *special = RC_MULTI_BOOKMARK;
+
+	    if (!strncasecomp(name, special, (int) strlen(special))) {
+		tbl = lookup_config(special);
+	    }
+	    /* lynx ignores unknown keywords */
+	    if (tbl->name == 0) {
+		CTRACE((tfp, "LYrcFile: ignored %s=%s\n", name, value));
+		continue;
+	    }
+	}
+
+	q = ParseUnionOf(tbl);
+	switch (tbl->type) {
+	case CONF_BOOL:
+	    if (q->set_value != 0)
+		*(q->set_value) = getBool(value);
+	    break;
+
+	case CONF_FUN:
+	    if (q->fun_value != 0)
+		(*(q->fun_value)) (value);
+	    break;
+
+	case CONF_ARRAY:
+	    for (n = 0; tbl->strings[n] != 0; ++n) {
+		if (!strcasecomp(value, tbl->strings[n])) {
+		    *(q->int_value) = n;
+		    break;
+		}
+	    }
+	    break;
+
+	case CONF_ENUM:
+	    if (tbl->table != 0)
+		LYgetEnum(tbl->table, value, q->int_value);
+	    break;
+
+	case CONF_INT:
+	    if (q->int_value != 0) {
+		int ival;
+
+		if (1 == sscanf(value, "%d", &ival))
+		    *(q->int_value) = ival;
+	    }
+	    break;
+
+	case CONF_LIS:
+	    if (q->str_value != 0) {
+		if (*(q->str_value) != NULL)
+		    StrAllocCat(*(q->str_value), ",");
+		StrAllocCat(*(q->str_value), value);
+	    }
+	    break;
+
+	case CONF_MBM:
+	    for (n = 1; n <= MBM_V_MAXFILES; n++) {
+		sprintf(MBM_line, "multi_bookmark%c", LYindex2MBM(n));
+
+		if (!strcasecomp(name, MBM_line)) {
+		    if ((notes = strchr(value, ',')) != 0) {
+			*notes++ = '\0';
+			LYTrimTrailing(value);
+			notes = LYSkipBlanks(notes);
+		    } else {
+			notes = value + strlen(value);
+		    }
+		    StrAllocCopy(MBM_A_subbookmark[n], value);
+		    StrAllocCopy(MBM_A_subdescript[n], notes);
+		    break;
+		}
+	    }
+	    break;
+
+	case CONF_STR:
+	    if (q->str_value != 0)
+		StrAllocCopy(*(q->str_value), value);
+	    break;
+
+	case CONF_NIL:
+	    break;
+	}
+    }
+
+    LYCloseInput(fp);
+    LYConfigCookies();		/* update cookie settings, if any */
+
+#if defined(USE_SLANG) || defined(COLOR_CURSES)
+    /*
+     * We may override the commandline "-color" option with the .lynxrc file
+     */
+    switch (LYrcShowColor) {
+    case SHOW_COLOR_ALWAYS:
+	if (LYShowColor != SHOW_COLOR_NEVER)
+	    LYShowColor = SHOW_COLOR_ALWAYS;
+	break;
+    case SHOW_COLOR_NEVER:
+	if (LYShowColor == SHOW_COLOR_ON)
+	    LYShowColor = SHOW_COLOR_OFF;
+	break;
+    default:
+	/* don't override */
+	break;
+    }
+#endif
+    set_default_bookmark_page(bookmark_page);
+}
+
+/*
+ * Write a set of comments.  Doing it this way avoids preprocessor problems
+ * with the leading '#', makes it simpler to use gettext.
+ */
+static void write_list(FILE *fp, const char *list)
+{
+    int first = TRUE;
+
+    while (*list != 0) {
+	int ch = *list++;
+
+	if (first) {
+	    fputs("# ", fp);
+	    first = FALSE;
+	}
+	if (ch == '\n') {
+	    first = TRUE;
+	}
+	fputc(ch, fp);
+    }
+}
+
+/*
+ * This is too long for some compilers.
+ */
+static void explain_keypad_mode(FILE *fp)
+{
+    write_list(fp, gettext("\
+If keypad_mode is set to \"NUMBERS_AS_ARROWS\", then the numbers on\n\
+your keypad when the numlock is on will act as arrow keys:\n\
+            8 = Up Arrow\n\
+  4 = Left Arrow    6 = Right Arrow\n\
+            2 = Down Arrow\n\
+and the corresponding keyboard numbers will act as arrow keys,\n\
+regardless of whether numlock is on.\n\
+"));
+    write_list(fp, gettext("\
+If keypad_mode is set to \"LINKS_ARE_NUMBERED\", then numbers will\n\
+appear next to each link and numbers are used to select links.\n\
+"));
+    write_list(fp, gettext("\
+If keypad_mode is set to \"LINKS_AND_FORM_FIELDS_ARE_NUMBERED\", then\n\
+numbers will appear next to each link and visible form input field.\n\
+Numbers are used to select links, or to move the \"current link\" to a\n\
+form input field or button.  In addition, options in popup menus are\n\
+indexed so that the user may type an option number to select an option in\n\
+a popup menu, even if the option isn't visible on the screen.  Reference\n\
+lists and output from the list command also enumerate form inputs.\n\
+"));
+    write_list(fp, gettext("\
+NOTE: Some fixed format documents may look disfigured when\n\
+\"LINKS_ARE_NUMBERED\" or \"LINKS_AND_FORM_FIELDS_ARE_NUMBERED\" are\n\
+enabled.\n\
+"));
+}
+
+/* Save user options.  If the passed-in fp is NULL, open the regular user
+ * defaults file for writing, otherwise use fp which has to be a temp file open
+ * for writing.  - kw
+ */
+int save_rc(FILE *fp)
+{
+    Config_Type *tbl = Config_Table;
+    char rcfile[LY_MAXPATH];
+    BOOLEAN is_tempfile = (BOOL) (fp != NULL);
+    int n;
+
+    if (!fp) {
+	/*
+	 * Make a name.
+	 */
+	LYAddPathToHome(rcfile, sizeof(rcfile), FNAME_LYNXRC);
+
+	/*
+	 * Open the file for write.
+	 */
+	if ((fp = LYNewTxtFile(rcfile)) == NULL) {
+	    return FALSE;
+	}
+    }
+
+    write_list(fp, gettext("\
+Lynx User Defaults File\n\
+\n\
+"));
+
+    /*
+     * We have either the HTML options form, or the older menu, or both.
+     */
+#ifndef NO_OPTION_FORMS
+    write_list(fp, gettext("\
+This file contains options saved from the Lynx Options Screen (normally\n\
+with the 'o' key).  To save options with that screen, you must select the\n\
+checkbox:\n\
+"));
+    fprintf(fp, "#\t%s\n", SAVE_OPTIONS);
+    fprintf(fp, "#\n");
+    write_list(fp, gettext("\
+You must then save the settings using the link on the line above the\n\
+checkbox:\n\
+"));
+    fprintf(fp, "#\t%s\n", ACCEPT_CHANGES);
+    fprintf(fp, "#\n");
+#ifndef NO_OPTION_MENU
+    write_list(fp, gettext("\
+You may also use the command-line option \"-forms_options\", which displays\n\
+the simpler Options Menu instead.  Save options with that using the '>' key.\n\
+\n\
+"));
+#endif
+#else /* we only have old options-menu */
+    write_list(fp, gettext("\
+This file contains options saved from the Lynx Options Screen (normally\n\
+with the '>' key).\n\
+\n\
+"));
+#endif
+
+    write_list(fp, gettext("\
+There is normally no need to edit this file manually, since the defaults\n\
+here can be controlled from the Options Screen, and the next time options\n\
+are saved from the Options Screen this file will be completely rewritten.\n\
+You have been warned...\n\
+\n\
+If you are looking for the general configuration file - it is normally\n\
+called \"lynx.cfg\".  It has different content and a different format.\n\
+It is not this file.\n\
+"));
+    fprintf(fp, "\n");
+
+    while (tbl->name != 0) {
+	ParseUnionPtr q = ParseUnionOf(tbl);
+
+	if (!tbl->enabled) {
+	    tbl++;
+	    continue;
+	}
+	if (tbl->note != NULL) {
+	    write_list(fp, gettext(tbl->note));
+	} else if (tbl->table == tbl_keypad_mode) {
+	    explain_keypad_mode(fp);
+	}
+
+	switch (tbl->type) {
+	case CONF_BOOL:
+	    fprintf(fp, "%s=%s\n\n", tbl->name, putBool(*(q->set_value)));
+	    break;
+
+	case CONF_FUN:
+	    if (tbl->write_it != 0)
+		tbl->write_it(fp, tbl);
+	    break;
+
+	case CONF_ARRAY:
+	    for (n = 0; tbl->strings[n] != 0; ++n)
+		fprintf(fp, "#    %s\n", tbl->strings[n]);
+	    fprintf(fp, "%s=%s\n\n", tbl->name,
+		    tbl->strings[*(q->int_value)]);
+	    break;
+
+	case CONF_ENUM:
+	    fprintf(fp, "%s=%s\n\n", tbl->name,
+		    LYputEnum(tbl->table, *(q->int_value)));
+	    break;
+
+	case CONF_INT:
+	    fprintf(fp, "%s=%d\n\n", tbl->name, *(q->int_value));
+	    break;
+
+	case CONF_MBM:
+	    for (n = 1; n <= MBM_V_MAXFILES; n++) {
+		fprintf(fp, "multi_bookmark%c=", LYindex2MBM(n));
+
+		fprintf(fp, "%s", NonNull(MBM_A_subbookmark[n]));
+		if (MBM_A_subdescript[n] != 0
+		    && *MBM_A_subdescript[n] != 0)
+		    fprintf(fp, ",%s", MBM_A_subdescript[n]);
+		fprintf(fp, "\n");
+	    }
+	    fprintf(fp, "\n");
+	    break;
+
+	case CONF_LIS:
+	    /* FALLTHRU */
+	case CONF_STR:
+	    fprintf(fp, "%s=%s\n\n", tbl->name,
+		    (q->str_value != 0 && *(q->str_value) != 0)
+		    ? *(q->str_value)
+		    : "");
+	    break;
+
+	case CONF_NIL:
+	    break;
+	}
+	tbl++;
+    }
+
+    /*
+     * Close the RC file.
+     */
+    if (is_tempfile) {
+	LYCloseTempFP(fp);
+    } else {
+	LYCloseOutput(fp);
+	HTSYS_purge(rcfile);
+    }
+
+    return TRUE;
+}
+
+/*
+ * Returns true if the given name would be saved in .lynxrc
+ */
+BOOL will_save_rc(const char *name)
+{
+    Config_Type *tbl = lookup_config(name);
+
+    return (BOOL) (tbl->name != 0);
+}
+
+int enable_lynxrc(char *value)
+{
+    Config_Type *tbl;
+    char *colon = strchr(value, ':');
+
+    if (colon != 0) {
+	*colon++ = 0;
+	LYTrimLeading(value);
+	LYTrimTrailing(value);
+
+	for (tbl = Config_Table; tbl->name != 0; tbl++) {
+	    if (!strcasecomp(value, tbl->name)) {
+		tbl->enabled = getBool(colon);
+		break;
+	    }
+	}
+    }
+    return 0;
+}
diff --git a/src/LYrcFile.h b/src/LYrcFile.h
new file mode 100644
index 00000000..f1ee99b0
--- /dev/null
+++ b/src/LYrcFile.h
@@ -0,0 +1,282 @@
+/*
+ * $LynxId: LYrcFile.h,v 1.34 2009/11/27 11:16:04 tom Exp $
+ */
+#ifndef LYRCFILE_H
+#define LYRCFILE_H
+
+#ifndef LYSTRUCTS_H
+#include <LYStructs.h>
+#endif /* LYSTRUCTS_H */
+
+/* configuration-variable names to share with LYReadCFG.c and LYOptions.c */
+#define RC_ACCEPT_ALL_COOKIES           "accept_all_cookies"
+#define RC_ALERTSECS                    "alertsecs"
+#define RC_ALWAYS_RESUBMIT_POSTS        "always_resubmit_posts"
+#define RC_ALWAYS_TRUSTED_EXEC          "always_trusted_exec"
+#define RC_ANONFTP_PASSWORD             "anonftp_password"
+#define RC_ASSUMED_COLOR                "assumed_color"
+#define RC_ASSUMED_DOC_CHARSET_CHOICE   "assumed_doc_charset_choice"
+#define RC_ASSUME_CHARSET               "assume_charset"
+#define RC_ASSUME_LOCAL_CHARSET         "assume_local_charset"
+#define RC_ASSUME_UNREC_CHARSET         "assume_unrec_charset"
+#define RC_AUTO_SESSION                 "auto_session"
+#define RC_AUTO_UNCACHE_DIRLISTS        "auto_uncache_dirlists"
+#define RC_BAD_HTML                     "bad_html"
+#define RC_BIBP_BIBHOST                 "bibp_bibhost"
+#define RC_BIBP_GLOBALSERVER            "bibp_globalserver"
+#define RC_BLOCK_MULTI_BOOKMARKS        "block_multi_bookmarks"
+#define RC_BOLD_H1                      "bold_h1"
+#define RC_BOLD_HEADERS                 "bold_headers"
+#define RC_BOLD_NAME_ANCHORS            "bold_name_anchors"
+#define RC_BOOKMARK_FILE                "bookmark_file"
+#define RC_BROKEN_FTP_EPSV              "broken_ftp_epsv"
+#define RC_BROKEN_FTP_RETR              "broken_ftp_retr"
+#define RC_BZIP2_PATH                   "bzip2_path"
+#define RC_CASE_SENSITIVE_ALWAYS_ON     "case_sensitive_always_on"
+#define RC_CASE_SENSITIVE_SEARCHING     "case_sensitive_searching"
+#define RC_CHARACTER_SET                "character_set"
+#define RC_CHARSETS_DIRECTORY           "charsets_directory"
+#define RC_CHARSET_SWITCH_RULES         "charset_switch_rules"
+#define RC_CHECKMAIL                    "checkmail"
+#define RC_CHMOD_PATH                   "chmod_path"
+#define RC_COLLAPSE_BR_TAGS             "collapse_br_tags"
+#define RC_COLOR                        "color"
+#define RC_COLOR_STYLE                  "color_style"
+#define RC_COMPRESS_PATH                "compress_path"
+#define RC_CONNECT_TIMEOUT              "connect_timeout"
+#define RC_COOKIE_ACCEPT_DOMAINS        "cookie_accept_domains"
+#define RC_COOKIE_FILE                  "cookie_file"
+#define RC_COOKIE_LOOSE_INVALID_DOMAINS "cookie_loose_invalid_domains"
+#define RC_COOKIE_QUERY_INVALID_DOMAINS "cookie_query_invalid_domains"
+#define RC_COOKIE_REJECT_DOMAINS        "cookie_reject_domains"
+#define RC_COOKIE_SAVE_FILE             "cookie_save_file"
+#define RC_COOKIE_STRICT_INVALID_DOMAIN "cookie_strict_invalid_domains"
+#define RC_COPY_PATH                    "copy_path"
+#define RC_CSO_PROXY                    "cso_proxy"
+#define RC_CSWING_PATH                  "cswing_path"
+#define RC_DEFAULT_BOOKMARK_FILE        "default_bookmark_file"
+#define RC_DEFAULT_CACHE_SIZE           "default_cache_size"
+#define RC_DEFAULT_COLORS               "default_colors"
+#define RC_DEFAULT_EDITOR               "default_editor"
+#define RC_DEFAULT_INDEX_FILE           "default_index_file"
+#define RC_DEFAULT_KEYPAD_MODE          "default_keypad_mode"
+#define RC_DEFAULT_KEYPAD_MODE_NUMARO   "default_keypad_mode_is_numbers_as_arrows"
+#define RC_DEFAULT_USER_MODE            "default_user_mode"
+#define RC_DEFAULT_VIRTUAL_MEMORY_SIZE  "default_virtual_memory_size"
+#define RC_DELAYSECS                    "delaysecs"
+#define RC_DIRED_MENU                   "dired_menu"
+#define RC_DIR_LIST_ORDER               "dir_list_order"
+#define RC_DIR_LIST_STYLE               "dir_list_style"
+#define RC_DISPLAY                      "display"
+#define RC_DISPLAY_CHARSET_CHOICE       "display_charset_choice"
+#define RC_DOWNLOADER                   "downloader"
+#define RC_EMACS_KEYS                   "emacs_keys"
+#define RC_EMACS_KEYS_ALWAYS_ON         "emacs_keys_always_on"
+#define RC_ENABLE_LYNXRC                "enable_lynxrc"
+#define RC_ENABLE_SCROLLBACK            "enable_scrollback"
+#define RC_EXTERNAL                     "external"
+#define RC_FILE_EDITOR                  "file_editor"
+#define RC_FILE_SORTING_METHOD          "file_sorting_method"
+#define RC_FINGER_PROXY                 "finger_proxy"
+#define RC_FOCUS_WINDOW                 "focus_window"
+#define RC_FORCE_8BIT_TOUPPER           "force_8bit_toupper"
+#define RC_FORCE_COOKIE_PROMPT          "force_cookie_prompt"
+#define RC_FORCE_EMPTY_HREFLESS_A       "force_empty_hrefless_a"
+#define RC_FORCE_SSL_COOKIES_SECURE     "force_ssl_cookies_secure"
+#define RC_FORCE_SSL_PROMPT             "force_ssl_prompt"
+#define RC_FORMS_OPTIONS                "forms_options"
+#define RC_FTP_FORMAT                   "ftp_format"
+#define RC_FTP_PASSIVE                  "ftp_passive"
+#define RC_FTP_PROXY                    "ftp_proxy"
+#define RC_GLOBAL_EXTENSION_MAP         "global_extension_map"
+#define RC_GLOBAL_MAILCAP               "global_mailcap"
+#define RC_GOPHER_PROXY                 "gopher_proxy"
+#define RC_GOTOBUFFER                   "gotobuffer"
+#define RC_GZIP_PATH                    "gzip_path"
+#define RC_HELPFILE                     "helpfile"
+#define RC_HIDDEN_LINK_MARKER           "hidden_link_marker"
+#define RC_HISTORICAL_COMMENTS          "historical_comments"
+#define RC_HTMLSRC_ATTRNAME_XFORM       "htmlsrc_attrname_xform"
+#define RC_HTMLSRC_TAGNAME_XFORM        "htmlsrc_tagname_xform"
+#define RC_HTTPS_PROXY                  "https_proxy"
+#define RC_HTTP_PROXY                   "http_proxy"
+#define RC_INCLUDE                      "include"
+#define RC_INFLATE_PATH                 "inflate_path"
+#define RC_INFOSECS                     "infosecs"
+#define RC_INSTALL_PATH                 "install_path"
+#define RC_JUMPBUFFER                   "jumpbuffer"
+#define RC_JUMPFILE                     "jumpfile"
+#define RC_JUMP_PROMPT                  "jump_prompt"
+#define RC_JUSTIFY                      "justify"
+#define RC_JUSTIFY_MAX_VOID_PERCENT     "justify_max_void_percent"
+#define RC_KBLAYOUT                     "kblayout"
+#define RC_KEYBOARD_LAYOUT              "keyboard_layout"
+#define RC_KEYMAP                       "keymap"
+#define RC_KEYPAD_MODE                  "keypad_mode"
+#define RC_LEFTARROW_IN_TEXTFLD_PROMPT  "leftarrow_in_textfield_prompt"
+#define RC_LINEEDIT_MODE                "lineedit_mode"
+#define RC_LIST_FORMAT                  "list_format"
+#define RC_LIST_NEWS_DATES              "list_news_dates"
+#define RC_LIST_NEWS_NUMBERS            "list_news_numbers"
+#define RC_LOCALE_CHARSET               "locale_charset"
+#define RC_LOCALHOST_ALIAS              "localhost_alias"
+#define RC_LOCAL_DOMAIN                 "local_domain"
+#define RC_LOCAL_EXECUTION_LINKS_ALWAYS "local_execution_links_always_on"
+#define RC_LOCAL_EXECUTION_LINKS_LOCAL  "local_execution_links_on_but_not_remote"
+#define RC_LYNXCGI_DOCUMENT_ROOT        "lynxcgi_document_root"
+#define RC_LYNXCGI_ENVIRONMENT          "lynxcgi_environment"
+#define RC_LYNX_HOST_NAME               "lynx_host_name"
+#define RC_LYNX_SIG_FILE                "lynx_sig_file"
+#define RC_MAIL_ADRS                    "mail_adrs"
+#define RC_MAIL_SYSTEM_ERROR_LOGGING    "mail_system_error_logging"
+#define RC_MAKE_LINKS_FOR_ALL_IMAGES    "make_links_for_all_images"
+#define RC_MAKE_PSEUDO_ALTS_FOR_INLINES "make_pseudo_alts_for_inlines"
+#define RC_MAX_COOKIES_BUFFER           "max_cookies_buffer"
+#define RC_MAX_COOKIES_DOMAIN           "max_cookies_domain"
+#define RC_MAX_COOKIES_GLOBAL           "max_cookies_global"
+#define RC_MESSAGESECS                  "messagesecs"
+#define RC_MINIMAL_COMMENTS             "minimal_comments"
+#define RC_MKDIR_PATH                   "mkdir_path"
+#define RC_MULTI_BOOKMARK               "multi_bookmark"
+#define RC_MULTI_BOOKMARK_SUPPORT       "multi_bookmark_support"
+#define RC_MV_PATH                      "mv_path"
+#define RC_NCR_IN_BOOKMARKS             "ncr_in_bookmarks"
+#define RC_NESTED_TABLES                "nested_tables"
+#define RC_NEWSPOST_PROXY               "newspost_proxy"
+#define RC_NEWSREPLY_PROXY              "newsreply_proxy"
+#define RC_NEWS_CHUNK_SIZE              "news_chunk_size"
+#define RC_NEWS_MAX_CHUNK               "news_max_chunk"
+#define RC_NEWS_POSTING                 "news_posting"
+#define RC_NEWS_PROXY                   "news_proxy"
+#define RC_NNTPSERVER                   "nntpserver"
+#define RC_NNTP_PROXY                   "nntp_proxy"
+#define RC_NONRESTARTING_SIGWINCH       "nonrestarting_sigwinch"
+#define RC_NO_DOT_FILES                 "no_dot_files"
+#define RC_NO_FILE_REFERER              "no_file_referer"
+#define RC_NO_FORCED_CORE_DUMP          "no_forced_core_dump"
+#define RC_NO_FROM_HEADER               "no_from_header"
+#define RC_NO_ISMAP_IF_USEMAP           "no_ismap_if_usemap"
+#define RC_NO_MARGINS                   "no_margins"
+#define RC_NO_PAUSE                     "no_pause"
+#define RC_NO_PROXY                     "no_proxy"
+#define RC_NO_REFERER_HEADER            "no_referer_header"
+#define RC_NO_TABLE_CENTER              "no_table_center"
+#define RC_NO_TITLE                     "no_title"
+#define RC_NUMBER_FIELDS_ON_LEFT        "number_fields_on_left"
+#define RC_NUMBER_LINKS_ON_LEFT         "number_links_on_left"
+#define RC_OUTGOING_MAIL_CHARSET        "outgoing_mail_charset"
+#define RC_PARTIAL                      "partial"
+#define RC_PARTIAL_THRES                "partial_thres"
+#define RC_PERSISTENT_COOKIES           "persistent_cookies"
+#define RC_PERSONAL_EXTENSION_MAP       "personal_extension_map"
+#define RC_PERSONAL_MAILCAP             "personal_mailcap"
+#define RC_PERSONAL_MAIL_ADDRESS        "personal_mail_address"
+#define RC_POSITIONABLE_EDITOR		"positionable_editor"
+#define RC_PREFERRED_CHARSET            "preferred_charset"
+#define RC_PREFERRED_ENCODING           "preferred_encoding"
+#define RC_PREFERRED_LANGUAGE           "preferred_language"
+#define RC_PREFERRED_MEDIA_TYPES        "preferred_media_types"
+#define RC_PREPEND_BASE_TO_SOURCE       "prepend_base_to_source"
+#define RC_PREPEND_CHARSET_TO_SOURCE    "prepend_charset_to_source"
+#define RC_PRETTYSRC                    "prettysrc"
+#define RC_PRETTYSRC_SPEC               "prettysrc_spec"
+#define RC_PRETTYSRC_VIEW_NO_ANCHOR_NUM "prettysrc_view_no_anchor_numbering"
+#define RC_PRINTER                      "printer"
+#define RC_QUIT_DEFAULT_YES             "quit_default_yes"
+#define RC_RAW_MODE                     "raw_mode"
+#define RC_READ_TIMEOUT                 "read_timeout"
+#define RC_REFERER_WITH_QUERY           "referer_with_query"
+#define RC_REPLAYSECS                   "replaysecs"
+#define RC_REUSE_TEMPFILES              "reuse_tempfiles"
+#define RC_RLOGIN_PATH                  "rlogin_path"
+#define RC_RM_PATH                      "rm_path"
+#define RC_RULE                         "rule"
+#define RC_RULESFILE                    "rulesfile"
+#define RC_RUN_ALL_EXECUTION_LINKS      "run_all_execution_links"
+#define RC_RUN_EXECUTION_LINKS_LOCAL    "run_execution_links_on_local_files"
+#define RC_SAVE_SPACE                   "save_space"
+#define RC_SCAN_FOR_BURIED_NEWS_REFS    "scan_for_buried_news_refs"
+#define RC_SCREEN_SIZE                  "screen_size"
+#define RC_SCROLLBAR                    "scrollbar"
+#define RC_SCROLLBAR_ARROW              "scrollbar_arrow"
+#define RC_SEEK_FRAG_AREA_IN_CUR        "seek_frag_area_in_cur"
+#define RC_SEEK_FRAG_MAP_IN_CUR         "seek_frag_map_in_cur"
+#define RC_SELECT_POPUPS                "select_popups"
+#define RC_SEND_USERAGENT               "send_useragent"
+#define RC_SESSION_FILE                 "session_file"
+#define RC_SESSION_LIMIT                "session_limit"
+#define RC_SET_COOKIES                  "set_cookies"
+#define RC_SHOW_COLOR                   "show_color"
+#define RC_SHOW_CURSOR                  "show_cursor"
+#define RC_SHOW_DOTFILES                "show_dotfiles"
+#define RC_SHOW_KB_NAME                 "show_kb_name"
+#define RC_SHOW_KB_RATE                 "show_kb_rate"
+#define RC_SNEWSPOST_PROXY              "snewspost_proxy"
+#define RC_SNEWSREPLY_PROXY             "snewsreply_proxy"
+#define RC_SNEWS_PROXY                  "snews_proxy"
+#define RC_SOFT_DQUOTES                 "soft_dquotes"
+#define RC_SOURCE_CACHE                 "source_cache"
+#define RC_SOURCE_CACHE_FOR_ABORTED     "source_cache_for_aborted"
+#define RC_SSL_CERT_FILE                "ssl_cert_file"
+#define RC_STARTFILE                    "startfile"
+#define RC_STATUS_BUFFER_SIZE           "status_buffer_size"
+#define RC_STRIP_DOTDOT_URLS            "strip_dotdot_urls"
+#define RC_SUBSTITUTE_UNDERSCORES       "substitute_underscores"
+#define RC_SUB_BOOKMARKS                "sub_bookmarks"
+#define RC_SUFFIX                       "suffix"
+#define RC_SUFFIX_ORDER                 "suffix_order"
+#define RC_SYSLOG_REQUESTED_URLS        "syslog_requested_urls"
+#define RC_SYSLOG_TEXT                  "syslog_text"
+#define RC_SYSTEM_EDITOR                "system_editor"
+#define RC_SYSTEM_MAIL                  "system_mail"
+#define RC_SYSTEM_MAIL_FLAGS            "system_mail_flags"
+#define RC_TAGSOUP                      "tagsoup"
+#define RC_TAR_PATH                     "tar_path"
+#define RC_TELNET_PATH                  "telnet_path"
+#define RC_TEXTFIELDS_NEED_ACTIVATION   "textfields_need_activation"
+#define RC_TIMEOUT                      "timeout"
+#define RC_TN3270_PATH                  "tn3270_path"
+#define RC_TOUCH_PATH                   "touch_path"
+#define RC_TRIM_INPUT_FIELDS            "trim_input_fields"
+#define RC_TRUSTED_EXEC                 "trusted_exec"
+#define RC_TRUSTED_LYNXCGI              "trusted_lynxcgi"
+#define RC_UNCOMPRESS_PATH              "uncompress_path"
+#define RC_UNDERLINE_LINKS              "underline_links"
+#define RC_UNZIP_PATH                   "unzip_path"
+#define RC_UPLOADER                     "uploader"
+#define RC_URL_DOMAIN_PREFIXES          "url_domain_prefixes"
+#define RC_URL_DOMAIN_SUFFIXES          "url_domain_suffixes"
+#define RC_USERAGENT                    "useragent"
+#define RC_USER_MODE                    "user_mode"
+#define RC_USE_FIXED_RECORDS            "use_fixed_records"
+#define RC_USE_MOUSE                    "use_mouse"
+#define RC_USE_SELECT_POPUPS            "use_select_popups"
+#define RC_UUDECODE_PATH                "uudecode_path"
+#define RC_VERBOSE_IMAGES               "verbose_images"
+#define RC_VIEWER                       "viewer"
+#define RC_VISITED_LINKS                "visited_links"
+#define RC_VI_KEYS                      "vi_keys"
+#define RC_VI_KEYS_ALWAYS_ON            "vi_keys_always_on"
+#define RC_WAIS_PROXY                   "wais_proxy"
+#define RC_XHTML_PARSING                "xhtml_parsing"
+#define RC_XLOADIMAGE_COMMAND           "xloadimage_command"
+#define RC_ZCAT_PATH                    "zcat_path"
+#define RC_ZIP_PATH                     "zip_path"
+
+extern Config_Enum tbl_force_prompt[];
+extern Config_Enum tbl_keypad_mode[];
+extern Config_Enum tbl_multi_bookmarks[];
+extern Config_Enum tbl_preferred_encoding[];
+extern Config_Enum tbl_preferred_media[];
+extern Config_Enum tbl_transfer_rate[];
+extern Config_Enum tbl_user_mode[];
+
+extern BOOL LYgetEnum(Config_Enum * table, const char *name, int *result);
+extern BOOL will_save_rc(const char *name);
+extern const char *LYputEnum(Config_Enum * table, int value);
+extern int enable_lynxrc(char *value);
+extern int get_tagsoup(char *value);
+extern int save_rc(FILE *);
+extern void read_rc(FILE *);
+
+#endif /* LYRCFILE_H */
diff --git a/src/TRSTable.c b/src/TRSTable.c
new file mode 100644
index 00000000..f0b45dfe
--- /dev/null
+++ b/src/TRSTable.c
@@ -0,0 +1,2033 @@
+/*
+ * $LynxId: TRSTable.c,v 1.25 2010/04/29 20:51:14 tom Exp $
+ *		Simple table object
+ *		===================
+ * Authors
+ *	KW	Klaus Weide <kweide@enteract.com>
+ * History:
+ *   2 Jul 1999	KW	Created.
+ */
+
+#include <HTUtils.h>
+#include <HTStyle.h>		/* for HT_LEFT, HT_CENTER, HT_RIGHT */
+#include <LYCurses.h>
+#include <TRSTable.h>
+#include <LYGlobalDefs.h>
+
+#include <LYLeaks.h>
+
+#ifdef SAVE_TIME_NOT_SPACE
+#define CELLS_GROWBY 16
+#define ROWS_GROWBY 16
+#else
+#define CELLS_GROWBY 2
+#define ROWS_GROWBY 2
+#endif
+
+#ifdef USE_CURSES_PADS
+#  define MAX_STBL_POS (LYwideLines ? MAX_COLS - 1 : LYcolLimit)
+#else
+#  define MAX_STBL_POS (LYcolLimit)
+#endif
+
+/* must be different from HT_ALIGN_NONE and HT_LEFT, HT_CENTER etc.: */
+#define RESERVEDCELL (-2)	/* cell's alignment field is overloaded, this
+				   value means cell was reserved by ROWSPAN */
+#define EOCOLG (-2)		/* sumcols' Line field isn't used for line info, this
+				   special value means end of COLGROUP */
+#ifndef NO_AGGRESSIVE_NEWROW
+#  define NO_AGGRESSIVE_NEWROW	0
+#endif
+
+typedef enum {
+    CS_invalid = -1,		/* cell "before the first",
+				   or empty lines after [ce]bc,
+				   or TRST aborted */
+    CS__new = 0,
+    CS__0new,			/* new, at BOL */
+    CS__0eb,			/* starts at BOL, empty, break */
+    CS__eb,			/* empty, break */
+    CS__0cb,			/* starts at BOL, content, break */
+    CS__cb,			/* content, break */
+    CS__0ef,			/* starts at BOL, empty, finished */
+    CS__ef,			/* empty, finished */
+    CS__0cf,			/* starts at BOL, content, finished */
+    CS__cf,			/* content, finished */
+    CS__ebc,			/* empty, break, more content (maybe @BOL) */
+    CS__cbc			/* content, break, more content (maybe @BOL) */
+} cellstate_t;
+
+typedef struct _STable_states {
+    cellstate_t prev_state;	/* Contents type of the previous cell */
+    cellstate_t state;		/* Contents type of the worked-on cell */
+    int lineno;			/* Start line of the current cell */
+    int icell_core;		/* -1 or the 1st cell with <BR></TD> on row */
+    int x_td;			/* x start pos of the current cell or -1 */
+    int pending_len;		/* For multiline cells, the length of
+				   the part on the first line (if
+				   state is CS__0?[ec]b) (??), or 0 */
+} STable_states;
+
+typedef struct _STable_cellinfo {
+    int cLine;			/* lineno in doc (zero-based): -1 for
+				   contentless cells (and cells we do
+				   not want to measure and count?),
+				   line-of-the-start otherwise.  */
+    int pos;			/* column where cell starts */
+    int len;			/* number of character positions */
+    int colspan;		/* number of columns to span */
+    int alignment;		/* one of HT_LEFT, HT_CENTER, HT_RIGHT,
+				   or RESERVEDCELL */
+} STable_cellinfo;
+
+enum ended_state {
+    ROW_not_ended,
+    ROW_ended_by_endtr,
+    ROW_ended_by_splitline
+};
+
+#define HAS_END_OF_CELL			1
+#define HAS_BEG_OF_CELL			2
+#define IS_CONTINUATION_OF_CELL		4
+#define OFFSET_IS_VALID			8
+#define OFFSET_IS_VALID_LAST_CELL	0x10
+#define BELIEVE_OFFSET			0x20
+
+typedef struct _STable_rowinfo {
+    /* Each row may be displayed on many display lines, but we fix up
+       positions of cells on this display line only: */
+    int Line;			/* lineno in doc (zero-based) */
+    int ncells;			/* number of table cells */
+
+    /* What is the meaning of this?!  It is set if:
+       [search for      def of fixed_line       below]
+
+       a1) a non-last cell is not at BOL,
+       a2) a non-last cell has something on the first line,
+       b) a >=3-lines-cell not at BOL, the first row non-empty, the 2nd empty;
+       c) a multiline cell not at BOL, the first row non-empty, the rest empty;
+       d) a multiline cell not at BOL, the first row non-empty;
+       e) a singleline non-empty cell not at BOL;
+
+       Summary: have seen a cell which is one of:
+       (Notation: B: at BOL; L: last; E: the first row is non-empty)
+
+       bcde:    !B && !E
+       a1:      !L && !B
+       a2:      !L && !E
+
+       Or: has at least two of !B, !L, !E, or: has at most one of B,L,E.
+
+       REMARK: If this variable is not set, but icell_core is, Line is
+       reset to the line of icell_core.
+     */
+    BOOL fixed_line;		/* if we have a 'core' line of cells */
+    enum ended_state ended;	/* if we saw </tr> etc */
+    int content;		/* Whether contains end-of-cell etc */
+    int offset;			/* >=0 after line break in a multiline cell */
+    int allocated;		/* number of table cells allocated */
+    STable_cellinfo *cells;
+    int alignment;		/* global align attribute for this row */
+} STable_rowinfo;
+
+struct _STable_info {
+#ifdef EXP_NESTED_TABLES
+    struct _STable_info *enclosing;	/* The table which contain us */
+    struct _TextAnchor *enclosing_last_anchor_before_stbl;
+#endif
+    int startline;		/* lineno where table starts (zero-based) */
+    int nrows;			/* number of rows */
+    int ncols;			/* number of rows */
+    int maxlen;			/* sum of max. cell lengths of any row */
+    int maxpos;			/* max. of max. cell pos's of any row */
+    int allocated_rows;		/* number of rows allocated */
+    int allocated_sumcols;	/* number of sumcols allocated */
+    int ncolinfo;		/* number of COL info collected */
+    STable_cellinfo *sumcols;	/* for summary (max len/pos) col info */
+    STable_rowinfo *rows;
+    STable_rowinfo rowspans2eog;
+    short alignment;		/* global align attribute for this table */
+    short rowgroup_align;	/* align default for current group of rows */
+    short pending_colgroup_align;
+    int pending_colgroup_next;
+    STable_states s;
+};
+
+/*
+ *  Functions and structures in this source file keep track of positions.
+ *  They don't know about the character data in those lines, or about
+ *  the HText and HTLine structures.  GridText.c doesn't know about our
+ *  structures.  It should stay that way.
+ *
+ *  The basic idea: we let the code in HTML.c/GridText.c produce and format
+ *  output "as usual", i.e. as without Simple Table support.  We keep track
+ *  of the positions in the generated output where cells and rows start (or
+ *  end).  If all goes well, that preliminary output (stored in HText/HTLine
+ *  structures) can be fixed up when the TABLE end tag is processed, by just
+ *  inserting spaces in the right places (and possibly changing alignment).
+ *  If all goes not well, we already have a safe fallback.
+ *
+ *  Note that positions passed to and from these functions should be
+ *  in terms of screen positions, not just byte counts in a HTLine.data
+ *  (cf. line->data vs. HText_TrueLineSize).
+ *
+ *  Memory is allocated dynamically, so we can have tables of arbitrary
+ *  length.  On allocation error we just return and error indication
+ *  instead of outofmem(), so caller can give up table tracking and maybe
+ *  recover memory.
+ *
+ *  Implemented:
+ *  - ALIGN={left,right,center,justify} applied to individual table cells
+ *    ("justify" is treated as "left")
+ *  - Inheritance of horizontal alignment according to HTML 4.0
+ *  - COLSPAN >1 (may work incorrectly for some tables?)
+ *  - ROWSPAN >1 (reserving cells in following rows)
+ *  - Line breaks at start of first cell or at end of last cell are treated
+ *    as if they were not part of the cell and row.  This allows us to
+ *    cooperate with one way in which tables have been made friendly to
+ *    browsers without any table support.
+ *  Missing, but can be added:
+ *  - Support for COLGROUP/COL
+ *  - Tables wider than display.  The limitation is not here but in GridText.c
+ *    etc.  If horizontal scrolling were implemented there, the mechanisms
+ *    here coudl deal with wide tables (just change MAX_STBL_POS code).
+ *  Missing, unlikely to add:
+ *  - Support for non-LTR directionality.  A general problem, support is
+ *    lacking throughout the lynx code.
+ *  - Support for most other table-related attributes.  Most of them are
+ *    for decorative purposes.
+ *  Impossible or very unlikely (because it doesn't fit the model):
+ *  - Any cell contents of more than one line, line breaks within cells.
+ *    Anything that requires handling cell contents as paragraphs (block
+ *    elements), like reflowing.  Vertical alignment.
+ */
+static int Stbl_finishCellInRow(STable_rowinfo *me, STable_states *s, int end_td,
+				int lineno,
+				int pos);
+static int Stbl_finishRowInTable(STable_info *me);
+
+static const char *cellstate_s(cellstate_t state)
+{
+    const char *result = "?";
+    /* *INDENT-OFF* */
+    switch (state) {
+    case CS_invalid:	result = "CS_invalid";	break;
+    case CS__new:	result = "CS__new";	break;
+    case CS__0new:	result = "CS__0new";	break;
+    case CS__0eb:	result = "CS__0eb";	break;
+    case CS__eb:	result = "CS__eb";	break;
+    case CS__0cb:	result = "CS__0cb";	break;
+    case CS__cb:	result = "CS__cb";	break;
+    case CS__0ef:	result = "CS__0ef";	break;
+    case CS__ef:	result = "CS__ef";	break;
+    case CS__0cf:	result = "CS__0cf";	break;
+    case CS__cf:	result = "CS__cf";	break;
+    case CS__ebc:	result = "CS__ebc";	break;
+    case CS__cbc:	result = "CS__cbc";	break;
+    }
+    /* *INDENT-ON* */
+
+    return result;
+}
+
+struct _STable_info *Stbl_startTABLE(short alignment)
+{
+    STable_info *me = typecalloc(STable_info);
+
+    CTRACE2(TRACE_TRST,
+	    (tfp, "TRST:Stbl_startTABLE(align=%d)\n", (int) alignment));
+    if (me) {
+	me->alignment = alignment;
+	me->rowgroup_align = HT_ALIGN_NONE;
+	me->pending_colgroup_align = HT_ALIGN_NONE;
+	me->s.x_td = -1;
+	me->s.icell_core = -1;
+#ifdef EXP_NESTED_TABLES
+	if (nested_tables)
+	    me->enclosing = 0;
+#endif
+    }
+    return me;
+}
+
+static void free_rowinfo(STable_rowinfo *me)
+{
+    if (me && me->allocated) {
+	FREE(me->cells);
+    }
+}
+
+void Stbl_free(STable_info *me)
+{
+    CTRACE2(TRACE_TRST,
+	    (tfp, "TRST:Stbl_free()\n"));
+    if (me && me->allocated_rows && me->rows) {
+	int i;
+
+	for (i = 0; i < me->allocated_rows; i++)
+	    free_rowinfo(me->rows + i);
+	FREE(me->rows);
+    }
+    free_rowinfo(&me->rowspans2eog);
+    if (me)
+	FREE(me->sumcols);
+    FREE(me);
+}
+
+/*
+ * Returns -1 on error, otherwise index of just-added table cell.
+ */
+static int Stbl_addCellToRow(STable_rowinfo *me, STable_cellinfo *colinfo, int ncolinfo,
+			     STable_states *s,
+			     int colspan,
+			     int alignment,
+			     int isheader,
+			     int lineno,
+			     int *ppos)
+{
+    STable_cellinfo *cells;
+    int i;
+    int last_colspan = me->ncells ?
+    me->cells[me->ncells - 1].colspan : 1;
+    cellstate_t newstate;
+    int ret;
+
+    CTRACE2(TRACE_TRST,
+	    (tfp, "TRST:Stbl_addCellToRow, line=%d, pos=%d, colspan=%d\n",
+	     lineno, *ppos, colspan));
+    CTRACE2(TRACE_TRST,
+	    (tfp,
+	     " ncells=%d, stateLine=%d, pending_len=%d, pstate=%s, state=%s\n",
+	     me->ncells, s->lineno, s->pending_len,
+	     cellstate_s(s->prev_state), cellstate_s(s->state)));
+    if (me->ncells == 0)
+	s->prev_state = CS_invalid;
+    else if (s->prev_state == CS_invalid ||
+	     (s->state != CS__0new &&
+	      s->state != CS__ef && s->state != CS__0ef))
+	s->prev_state = s->state;
+
+    if (me->ncells == 0 || *ppos == 0)
+	newstate = CS__0new;
+    else
+	newstate = CS__new;
+
+    if (me->ncells > 0 && s->pending_len > 0) {
+	if (s->prev_state != CS__cbc)
+	    me->cells[me->ncells - 1].len = s->pending_len;
+	s->pending_len = 0;
+    }
+    s->x_td = *ppos;
+
+    if (lineno != s->lineno) {
+	if (!me->fixed_line) {
+	    if (me->ncells == 0 || *ppos == 0) {
+		switch (s->prev_state) {
+		case CS_invalid:
+		case CS__0new:
+		case CS__0eb:
+		case CS__0cb:
+		case CS__0ef:
+		case CS__0cf:
+		    if (me->ncells > 0)
+			for (i = me->ncells + last_colspan - 2;
+			     i >= me->ncells - 1; i--) {
+			    me->cells[i].pos = *ppos;
+			    me->cells[i].cLine = lineno;
+			}
+		    me->Line = lineno;
+		    break;
+		case CS__new:
+		case CS__eb:
+		case CS__ef:
+		case CS__cf:
+		default:
+		    break;
+		case CS__cb:
+		    *ppos = me->cells[me->ncells - 1].pos +
+			me->cells[me->ncells - 1].len;
+		}
+	    } else {		/* last cell multiline, ncells != 0, pos != 0 */
+		switch (s->prev_state) {
+		case CS__0new:
+		case CS__0eb:
+		case CS__0ef:
+		    /* Do not fail, but do not set fixed_line either */
+		    break;
+		case CS__cb:
+		    goto trace_and_fail;
+		case CS__cf:
+		    goto trace_and_fail;
+		case CS__0cb:
+		case CS__0cf:
+		    if (*ppos > me->cells[0].pos)
+			me->Line = lineno;
+		    me->fixed_line = YES;	/* type=a def of fixed_line i */
+		    break;
+		case CS__new:
+		case CS__eb:
+		case CS__ef:
+		default:
+		    me->fixed_line = YES;	/* type=e def of fixed_line ii */
+		    break;
+		case CS__cbc:
+		    goto trace_and_fail;
+		}
+	    }
+	}
+	if (me->fixed_line && lineno != me->Line) {
+	    switch (s->prev_state) {
+	    case CS__cb:
+	    case CS__cf:
+		if (*ppos > 0)
+		    goto trace_and_fail;
+		else
+		    *ppos = me->cells[me->ncells - 1].pos /* == 0 */  +
+			me->cells[me->ncells - 1].len;
+		break;
+	    case CS__0cf:
+	    case CS__0cb:
+		if (*ppos == 0 || *ppos <= me->cells[0].pos)
+		    *ppos = me->cells[me->ncells - 1].pos /* == 0 */  +
+			me->cells[me->ncells - 1].len;
+		break;
+	    case CS__0new:
+	    case CS__0ef:
+	    case CS__0eb:
+		break;
+	    case CS__new:
+	    case CS__eb:
+	    case CS__ef:
+	    default:
+		*ppos = me->cells[me->ncells - 1].pos;
+		break;
+	    case CS__cbc:
+		break;
+	    case CS_invalid:
+		break;
+	    }
+	}
+	s->lineno = lineno;
+    } else {			/* lineno == s->lineno: */
+	switch (s->prev_state) {
+	case CS_invalid:
+	case CS__0new:
+	case CS__0eb:		/* cannot happen */
+	case CS__0cb:		/* cannot happen */
+	case CS__0ef:
+	case CS__0cf:		/* ##302?? set icell_core? or only in finish? */
+	    break;
+	case CS__eb:		/* cannot happen */
+	case CS__cb:		/* cannot happen */
+	case CS__ef:
+	    break;
+	case CS__ebc:		/* should have done smth in finish */
+	case CS__cbc:		/* should have done smth in finish */
+	    break;
+	case CS__new:
+	case CS__cf:
+	    if (me->fixed_line && me->Line != lineno) {
+		goto trace_and_fail;
+	    } else {
+		me->fixed_line = YES;
+		me->Line = lineno;
+	    }
+	}
+    }
+
+    s->state = newstate;
+
+    if (me->ncells > 0 && me->cells[me->ncells - 1].colspan > 1) {
+	me->ncells += me->cells[me->ncells - 1].colspan - 1;
+    }
+    while (me->ncells < me->allocated &&
+	   me->cells[me->ncells].alignment == RESERVEDCELL) {
+	me->ncells++;
+    }
+    {
+	int growby = 0;
+
+	while (me->ncells + colspan + 1 > me->allocated + growby)
+	    growby += CELLS_GROWBY;
+	if (growby) {
+	    if (me->allocated == 0 && !me->cells) {
+		cells = typecallocn(STable_cellinfo, (unsigned) growby);
+	    } else {
+		cells = typeRealloc(STable_cellinfo, me->cells,
+				      (unsigned) (me->allocated + growby));
+
+		for (i = 0; cells && i < growby; i++) {
+		    cells[me->allocated + i].alignment = HT_ALIGN_NONE;
+		}
+	    }
+	    if (cells) {
+		me->allocated += growby;
+		me->cells = cells;
+	    } else {
+		goto trace_and_fail;
+	    }
+	}
+    }
+
+    me->cells[me->ncells].cLine = lineno;
+    me->cells[me->ncells].pos = *ppos;
+    me->cells[me->ncells].len = -1;
+    me->cells[me->ncells].colspan = colspan;
+
+    if (alignment != HT_ALIGN_NONE)
+	me->cells[me->ncells].alignment = alignment;
+    else {
+	if (ncolinfo >= me->ncells + 1)
+	    me->cells[me->ncells].alignment = colinfo[me->ncells].alignment;
+	else
+	    me->cells[me->ncells].alignment = me->alignment;
+	if (me->cells[me->ncells].alignment == HT_ALIGN_NONE)
+	    me->cells[me->ncells].alignment = me->alignment;
+	if (me->cells[me->ncells].alignment == HT_ALIGN_NONE)
+	    me->cells[me->ncells].alignment = isheader ? HT_CENTER : HT_LEFT;
+    }
+    for (i = me->ncells + 1; i < me->ncells + colspan; i++) {
+	me->cells[i].cLine = lineno;
+	me->cells[i].pos = *ppos;
+	me->cells[i].len = -1;
+	me->cells[i].colspan = 0;
+	me->cells[i].alignment = HT_LEFT;
+    }
+    me->cells[me->ncells + colspan].pos = -1;	/* not yet used */
+    me->ncells++;
+
+    ret = me->ncells - 1;
+  trace_and_return:
+    CTRACE2(TRACE_TRST,
+	    (tfp, " => prev_state=%s, state=%s, ret=%d\n",
+	     cellstate_s(s->prev_state), cellstate_s(s->state), ret));
+    return (ret);
+
+  trace_and_fail:
+    ret = -1;
+    goto trace_and_return;
+}
+
+/* returns -1 on error, 0 otherwise */
+/* assumes cells have already been allocated (but may need more) */
+static int Stbl_reserveCellsInRow(STable_rowinfo *me, int icell,
+				  int colspan)
+{
+    STable_cellinfo *cells;
+    int i;
+    int growby = 1 + icell + colspan - me->allocated;
+
+    CTRACE2(TRACE_TRST,
+	    (tfp, "TRST:Stbl_reserveCellsInRow(icell=%d, colspan=%d\n",
+	     icell, colspan));
+    if (growby > 0) {
+	cells = typeRealloc(STable_cellinfo, me->cells,
+			      (unsigned) (me->allocated + growby));
+
+	if (cells) {
+	    for (i = 0; i < growby; i++) {
+		cells[me->allocated + i].alignment = HT_ALIGN_NONE;
+	    }
+	    me->allocated += growby;
+	    me->cells = cells;
+	} else {
+	    return -1;
+	}
+    }
+    for (i = icell; i < icell + colspan; i++) {
+	me->cells[i].cLine = -1;
+	me->cells[i].pos = -1;
+	me->cells[i].len = -1;
+	me->cells[i].colspan = 0;
+	me->cells[i].alignment = RESERVEDCELL;
+    }
+    me->cells[icell].colspan = colspan;
+    return 0;
+}
+
+/* Returns -1 on failure. */
+static int Stbl_finishCellInRow(STable_rowinfo *me, STable_states *s, int end_td,
+				int lineno,
+				int pos)
+{
+    STable_cellinfo *lastcell;
+    cellstate_t newstate = CS_invalid;
+    int multiline = NO, empty;
+    int ret;
+
+    CTRACE2(TRACE_TRST,
+	    (tfp,
+	     "TRST:Stbl_finishCellInRow line=%d pos=%d end_td=%d ncells=%d pnd_len=%d\n",
+	     lineno, pos, (int) end_td, me->ncells, s->pending_len));
+
+    if (me->ncells <= 0)
+	return -1;
+    lastcell = me->cells + (me->ncells - 1);
+    multiline = (lineno != lastcell->cLine || lineno != s->lineno);
+    empty = multiline ? (pos == 0) : (pos <= s->x_td);
+
+    CTRACE2(TRACE_TRST,
+	    (tfp,
+	     " [lines: lastCell=%d state=%d multi=%d] empty=%d (prev)state=(%s) %s\n",
+	     lastcell->cLine, s->lineno, multiline, empty,
+	     cellstate_s(s->prev_state), cellstate_s(s->state)));
+
+    if (multiline) {
+	if ((end_td & TRST_ENDCELL_MASK) == TRST_ENDCELL_LINEBREAK) {
+	    switch (s->state) {
+	    case CS_invalid:
+		newstate = empty ? CS_invalid : CS__cbc;
+		break;
+	    case CS__0new:
+		newstate = empty ? CS__0eb : CS__0cb;
+		break;
+	    case CS__0eb:
+		newstate = empty ? CS__0eb : CS__ebc;
+		s->state = newstate;
+		if (me->fixed_line) {
+		    if (empty)
+			ret = (lastcell->len <= 0 ? 0 : lastcell->len);
+		    else
+			ret = (lastcell->len <= 0 ? 0 : -1);
+		} else {
+		    if (empty)
+			ret = (lastcell->len <= 0 ? 0 : lastcell->len);
+		    else
+			ret = (lastcell->len <= 0 ? 0 : 0);
+		}
+		goto trace_and_return;
+	    case CS__0cb:
+		if (!me->fixed_line) {
+		    if (!empty) {
+			if (s->icell_core == -1)
+			    me->Line = -1;
+		    }
+		}
+		if (s->pending_len && empty) {	/* First line non-empty */
+		    if ((me->fixed_line && me->Line == lastcell->cLine) ||
+			s->icell_core == me->ncells - 1)
+			lastcell->len = s->pending_len;
+		    s->pending_len = 0;
+		}		/* @@@ for empty do smth. about ->Line / ->icell_core !! */
+		newstate = empty ? CS__0cb : CS__cbc;	/* ##474_needs_len!=-1? */
+		break;
+	    case CS__0ef:
+	    case CS__0cf:
+		break;
+	    case CS__new:
+		newstate = empty ? CS__eb : CS__cb;
+		break;
+	    case CS__eb:	/* ##484_set_pending_ret_0_if_empty? */
+		newstate = empty ? CS__eb : CS__ebc;
+		s->state = newstate;
+		if (me->fixed_line) {
+		    if (empty)
+			ret = (lastcell->len <= 0 ? 0 : lastcell->len);
+		    else
+			ret = (lastcell->len <= 0 ? 0 : -1);
+		} else {
+		    if (empty)
+			ret = (lastcell->len <= 0 ? 0 : lastcell->len);
+		    else
+			ret = (lastcell->len <= 0 ? 0 : -1);
+		}
+		goto trace_and_return;
+	    case CS__cb:
+		if (s->pending_len && empty) {	/* ##496: */
+		    lastcell->len = s->pending_len;
+		    s->pending_len = 0;
+		}		/* @@@ for empty do smth. about ->Line / ->icell_core !! */
+		ret = -1;
+		if (empty) {
+		    if (!me->fixed_line) {
+			me->fixed_line = YES;	/* type=b def of fixed_line i */
+			me->Line = lastcell->cLine;	/* should've happened in break */
+		    } else {
+			if (me->Line != lastcell->cLine)
+			    goto trace_and_return;
+		    }
+		} else {
+		    if (!me->fixed_line) {
+			me->fixed_line = YES;	/* type=b def of fixed_line ii */
+			me->Line = lastcell->cLine;	/* should've happened in break */
+		    }
+		    s->state = CS__cbc;
+		    goto trace_and_return;
+		}
+		newstate = empty ? CS__cb : CS__cbc;
+		break;
+	    case CS__ef:
+		ret = 0;
+		goto trace_and_return;
+	    case CS__cf:
+		ret = lastcell->len;	/* ##523_change_state? */
+		goto trace_and_return;
+	    case CS__cbc:
+		if (!me->fixed_line) {
+		    if (empty) {
+			if (s->icell_core == -1)	/* ##528??: */
+			    me->Line = lineno;
+			/* lastcell->Line = lineno; */
+		    } else {	/* !empty */
+			if (s->icell_core == -1)
+			    me->Line = -1;
+		    }
+		}
+		s->pending_len = 0;
+		newstate = empty ? CS_invalid : CS__cbc;
+		break;
+	    default:
+		break;
+	    }
+	} else {		/* multiline cell, processing </TD>: */
+	    s->x_td = -1;
+	    switch (s->state) {
+	    case CS_invalid:
+		/* ##540_return_-1_for_invalid_if_len!: */
+		if (!empty && lastcell->len > 0) {
+		    newstate = CS__0cf;
+		    s->state = newstate;
+		    ret = -1;
+		    goto trace_and_return;
+		}
+		/* ##541_set_len_0_Line_-1_sometimes: */
+		lastcell->len = 0;
+		lastcell->cLine = -1;
+		/* fall thru ##546 really fall thru??: */
+		newstate = empty ? CS_invalid : CS__cbc;
+		break;
+	    case CS__0new:
+		newstate = empty ? CS__0ef : CS__0cf;
+		break;
+	    case CS__0eb:
+		newstate = empty ? CS__0ef : CS__0cf;	/* ebc?? */
+		s->state = newstate;
+		if (me->fixed_line) {
+		    if (empty)
+			ret = (lastcell->len <= 0 ? 0 : lastcell->len);
+		    else
+			ret = (lastcell->len <= 0 ? 0 : -1);
+		} else {
+		    if (empty)
+			ret = (lastcell->len <= 0 ? 0 : lastcell->len);
+		    else
+			ret = (lastcell->len <= 0 ? 0 : 0);
+		}
+		goto trace_and_return;
+	    case CS__0cb:
+		if (s->pending_len) {
+		    if (empty)
+			lastcell->len = s->pending_len;
+		    else
+			lastcell->len = 0;
+		    s->pending_len = 0;
+		}
+		if (!me->fixed_line) {
+		    if (empty) {
+			if (s->icell_core == -1)
+			    /* first cell before <BR></TD> => the core cell */
+			    s->icell_core = me->ncells - 1;
+			/* lastcell->cLine = lineno; */
+		    } else {	/* !empty */
+			if (s->icell_core == -1)
+			    me->Line = -1;
+		    }
+		}
+		if (s->pending_len && empty) {
+		    lastcell->len = s->pending_len;
+		    s->pending_len = 0;
+		}		/* @@@ for empty do smth. about ->Line / ->icell_core !! */
+		newstate = empty ? CS__0cf : CS__cbc;
+		break;
+	    case CS__0ef:
+		newstate = CS__0ef;
+		/* FALLTHRU */
+	    case CS__0cf:
+		break;
+	    case CS__new:
+		newstate = empty ? CS__ef : CS__cf;
+		break;
+	    case CS__eb:
+		newstate = empty ? CS__ef : CS__ef;	/* ##579??? !!!!! */
+		s->state = newstate;
+		if (me->fixed_line) {
+		    if (empty)
+			ret = (lastcell->len <= 0 ? 0 : lastcell->len);
+		    else
+			ret = (lastcell->len <= 0 ? 0 : -1);
+		} else {
+		    if (empty)
+			ret = (lastcell->len <= 0 ? 0 : lastcell->len);
+		    else
+			ret = (lastcell->len <= 0 ? 0 : -1);
+		}
+		goto trace_and_return;
+	    case CS__cb:
+		if (s->pending_len && empty) {
+		    lastcell->len = s->pending_len;
+		    s->pending_len = 0;
+		}
+		ret = -1;
+		if (empty) {
+		    if (!me->fixed_line) {
+			me->fixed_line = YES;	/* type=c def of fixed_line */
+			me->Line = lastcell->cLine;	/* should've happened in break */
+		    } else {
+			if (me->Line != lastcell->cLine)
+			    goto trace_and_return;
+		    }
+		} else {
+		    goto trace_and_return;
+		}
+		newstate = empty ? CS__cf : CS__cbc;
+		break;
+	    case CS__ef:	/* ignored error */
+	    case CS__cf:	/* ignored error */
+		break;
+	    case CS__ebc:	/* ##540_handle_ebc: */
+		lastcell->len = 0;
+		if (!me->fixed_line) {
+		    if (!empty) {
+			if (s->icell_core == -1)
+			    lastcell->cLine = -1;
+		    }
+		}
+		s->pending_len = 0;
+		newstate = empty ? CS_invalid : CS__cbc;
+		break;
+	    case CS__cbc:	/* ##586 */
+		lastcell->len = 0;	/* ##613 */
+		ret = -1;
+		if (me->fixed_line && me->Line == lastcell->cLine)
+		    goto trace_and_return;
+		if (!me->fixed_line) {
+		    if (empty) {
+			if (s->icell_core == -1)
+			    me->Line = lineno;
+		    }
+		}
+		s->pending_len = 0;	/* ##629 v */
+		newstate = empty ? CS_invalid : CS__cbc;
+		break;
+	    }
+	}
+    } else {			/* (!multiline) */
+	if ((end_td & TRST_ENDCELL_MASK) == TRST_ENDCELL_LINEBREAK) {
+	    switch (s->state) {
+	    case CS_invalid:
+	    case CS__0new:
+		s->pending_len = empty ? 0 : pos - lastcell->pos;
+		newstate = empty ? CS__0eb : CS__0cb;
+		s->state = newstate;
+		ret = 0;	/* or 0 for xlen to s->pending_len?? */
+		goto trace_and_return;
+	    case CS__0eb:	/* cannot happen */
+		newstate = CS__eb;
+		break;
+	    case CS__0cb:	/* cannot happen */
+		newstate = CS__cb;
+		break;
+	    case CS__0ef:
+	    case CS__0cf:
+		break;
+	    case CS__new:
+		ret = -1;
+		if (!empty && s->prev_state == CS__cbc)		/* ##609: */
+		    goto trace_and_return;
+		if (!empty) {
+		    if (!me->fixed_line) {
+			me->fixed_line = YES;	/* type=d def of fixed_line */
+			me->Line = lineno;
+		    } else {
+			if (me->Line != lineno)
+			    goto trace_and_return;
+		    }
+		}
+		newstate = empty ? CS__eb : CS__cb;
+		s->state = newstate;
+		if (!me->fixed_line) {
+		    s->pending_len = empty ? 0 : pos - lastcell->pos;
+		    ret = 0;
+		    goto trace_and_return;
+		} else {
+		    s->pending_len = 0;
+		    lastcell->len = empty ? 0 : pos - lastcell->pos;
+		    ret = lastcell->len;
+		    goto trace_and_return;
+		}
+	    case CS__eb:	/* cannot happen */
+		newstate = empty ? CS__eb : CS__ebc;
+		break;
+	    case CS__cb:	/* cannot happen */
+		newstate = empty ? CS__cb : CS__cbc;
+		break;
+	    case CS__ef:
+		ret = 0;
+		goto trace_and_return;
+	    case CS__cf:
+		ret = lastcell->len;
+		goto trace_and_return;
+	    case CS__cbc:	/* ??? */
+		break;
+	    default:
+		break;
+	    }
+	} else {		/* !multiline, processing </TD>: */
+	    s->x_td = -1;
+	    switch (s->state) {
+	    case CS_invalid:	/* ##691_no_lastcell_len_for_invalid: */
+		if (!(me->fixed_line && me->Line == lastcell->cLine))
+		    lastcell->len = 0;
+		/* FALLTHRU */
+	    case CS__0new:
+		newstate = empty ? CS__0ef : CS__0cf;
+		break;		/* ##630 */
+	    case CS__0eb:
+		newstate = empty ? CS__0ef : CS__0ef;
+		break;		/* ??? */
+	    case CS__0cb:
+		newstate = empty ? CS__0cf : CS__cbc;
+		break;		/* ??? */
+	    case CS__0ef:
+		newstate = CS__0ef;
+		break;		/* ??? */
+	    case CS__0cf:
+		break;		/* ??? */
+	    case CS__new:
+		ret = -1;
+		if (!empty && s->prev_state == CS__cbc)
+		    goto trace_and_return;
+		if (!empty) {	/* ##642_set_fixed!: */
+		    if (!me->fixed_line) {
+			me->fixed_line = YES;	/* type=e def of fixed_line */
+			me->Line = lineno;
+		    } else {
+			if (me->Line != lineno)
+			    goto trace_and_return;
+		    }
+		}
+		if (lastcell->len < 0)
+		    lastcell->len = empty ? 0 : pos - lastcell->pos;
+		newstate = empty ? CS__ef : CS__cf;
+		s->state = newstate;
+		ret = ((me->fixed_line && lineno != me->Line)
+		       ? -1 : lastcell->len);
+		goto trace_and_return;
+	    case CS__eb:
+		newstate = empty ? CS__ef : CS__cf;
+		break;		/* ??? */
+	    case CS__cb:
+		newstate = empty ? CS__cf : CS__cf;
+		break;		/* ??? */
+	    case CS__ef:	/* ignored error */
+	    case CS__cf:	/* ignored error */
+	    default:
+		break;
+	    }
+	    lastcell->len = pos - lastcell->pos;
+	}			/* if (!end_td) ... else */
+    }				/* if (multiline) ... else */
+
+    s->state = newstate;
+    ret = lastcell->len;
+#ifdef EXP_NESTED_TABLES
+    if (nested_tables) {
+	if (ret == -1 && pos == 0)
+	    ret = 0;		/* XXXX Hack to allow trailing <P> in multiline cells. */
+    }
+#endif
+
+/*    lastcell->len = pos - lastcell->pos; */
+  trace_and_return:
+    CTRACE2(TRACE_TRST,
+	    (tfp, " => prev_state=%s, state=%s, return=%d\n",
+	     cellstate_s(s->prev_state), cellstate_s(s->state), ret));
+    return ret;
+}
+
+/*
+ * Reserve cells, each of given colspan, in (rowspan-1) rows after the current
+ * row of rowspan>1.  If rowspan==0, use special 'row' rowspans2eog to keep
+ * track of rowspans that are to remain in effect until the end of the row
+ * group (until next THEAD/TFOOT/TBODY) or table.
+ */
+static int Stbl_reserveCellsInTable(STable_info *me, int icell,
+				    int colspan,
+				    int rowspan)
+{
+    STable_rowinfo *rows, *row;
+    int growby;
+    int i;
+
+    if (colspan > TRST_MAXCOLSPAN) {
+	CTRACE2(TRACE_TRST,
+		(tfp,
+		 "TRST:*** COLSPAN=%d is too large, ignored!\n",
+		 colspan));
+	return -1;
+    }
+    if (rowspan > TRST_MAXROWSPAN) {
+	CTRACE2(TRACE_TRST,
+		(tfp,
+		 "TRST:*** ROWSPAN=%d is too large, ignored!\n",
+		 rowspan));
+	return -1;
+    }
+    if (me->nrows <= 0)
+	return -1;		/* must already have at least one row */
+
+    CTRACE2(TRACE_TRST,
+	    (tfp,
+	     "TRST:Stbl_reserveCellsInTable(icell=%d, colspan=%d, rowspan=%d)\n",
+	     icell, colspan, rowspan));
+    if (rowspan == 0) {
+	if (!me->rowspans2eog.cells) {
+	    me->rowspans2eog.cells = typecallocn(STable_cellinfo,
+						   (unsigned) HTMAX(1, icell + colspan));
+
+	    if (!me->rowspans2eog.cells)
+		return 0;	/* fail silently */
+	    else
+		me->rowspans2eog.allocated = icell + colspan;
+	}
+	Stbl_reserveCellsInRow(&me->rowspans2eog, icell, colspan);
+    }
+
+    growby = me->nrows + rowspan - 1 - me->allocated_rows;
+    if (growby > 0) {
+	rows = typeRealloc(STable_rowinfo, me->rows,
+			     (unsigned) (me->allocated_rows + growby));
+
+	if (!rows)
+	    return 0;		/* ignore silently, no free memory, may be recoverable */
+	for (i = 0; i < growby; i++) {
+	    row = rows + me->allocated_rows + i;
+	    row->allocated = 0;
+	    row->offset = 0;
+	    row->content = 0;
+	    if (!me->rowspans2eog.allocated) {
+		row->cells = NULL;
+	    } else {
+		row->cells = typecallocn(STable_cellinfo,
+					   (unsigned) me->rowspans2eog.allocated);
+
+		if (row->cells) {
+		    row->allocated = me->rowspans2eog.allocated;
+		    memcpy(row->cells, me->rowspans2eog.cells,
+			   ((unsigned) row->allocated * sizeof(STable_cellinfo)));
+		}
+	    }
+	    row->ncells = 0;
+	    row->fixed_line = NO;
+	    row->alignment = HT_ALIGN_NONE;
+	}
+	me->allocated_rows += growby;
+	me->rows = rows;
+    }
+    for (i = me->nrows;
+	 i < (rowspan == 0 ? me->allocated_rows : me->nrows + rowspan - 1);
+	 i++) {
+	if (!me->rows[i].allocated) {
+	    me->rows[i].cells = typecallocn(STable_cellinfo,
+					      (unsigned) HTMAX(1,
+							       icell
+							       + colspan));
+
+	    if (!me->rows[i].cells)
+		return 0;	/* fail silently */
+	    else
+		me->rows[i].allocated = icell + colspan;
+	}
+	Stbl_reserveCellsInRow(me->rows + i, icell, colspan);
+    }
+    return 0;
+}
+
+/* Remove reserved cells in trailing rows that were added for rowspan,
+ * to be used when a THEAD/TFOOT/TBODY ends. */
+static void Stbl_cancelRowSpans(STable_info *me)
+{
+    int i;
+
+    CTRACE2(TRACE_TRST, (tfp, "TRST:Stbl_cancelRowSpans()"));
+    for (i = me->nrows; i < me->allocated_rows; i++) {
+	if (!me->rows[i].ncells) {	/* should always be the case */
+	    FREE(me->rows[i].cells);
+	    me->rows[i].allocated = 0;
+	}
+    }
+    free_rowinfo(&me->rowspans2eog);
+    me->rowspans2eog.allocated = 0;
+}
+
+/*
+ * Returns -1 on error, otherwise index of just-added table row.
+ */
+int Stbl_addRowToTable(STable_info *me, int alignment,
+		       int lineno)
+{
+    STable_rowinfo *rows, *row;
+    STable_states *s = &me->s;
+
+    CTRACE2(TRACE_TRST,
+	    (tfp, "TRST:Stbl_addRowToTable(alignment=%d, lineno=%d)\n",
+	     alignment, lineno));
+    if (me->nrows > 0 && me->rows[me->nrows - 1].ncells > 0) {
+	if (s->pending_len > 0)
+	    me->rows[me->nrows - 1].cells[
+					     me->rows[me->nrows - 1].ncells - 1
+		].len =
+		s->pending_len;
+	s->pending_len = 0;
+    }
+    Stbl_finishRowInTable(me);
+    if (me->nrows > 0 && me->rows[me->nrows - 1].Line == lineno)
+	me->rows[me->nrows - 1].Line = -1;
+    s->pending_len = 0;
+    s->x_td = -1;
+
+    {
+	int i;
+	int growby = 0;
+
+	while (me->nrows + 2 > me->allocated_rows + growby)
+	    growby += ROWS_GROWBY;
+	if (growby) {
+	    if (me->allocated_rows == 0 && !me->rows) {
+		rows = typecallocn(STable_rowinfo, (unsigned) growby);
+	    } else {
+		rows = typeRealloc(STable_rowinfo, me->rows,
+				     (unsigned) (me->allocated_rows + growby));
+
+		for (i = 0; rows && i < growby; i++) {
+		    row = rows + me->allocated_rows + i;
+		    if (!me->rowspans2eog.allocated) {
+			row->allocated = 0;
+			row->cells = NULL;
+		    } else {
+			row->cells = typecallocn(STable_cellinfo,
+						   (unsigned) me->rowspans2eog.allocated);
+
+			if (row->cells) {
+			    row->allocated = me->rowspans2eog.allocated;
+			    memcpy(row->cells, me->rowspans2eog.cells,
+				   (unsigned) row->allocated * sizeof(STable_cellinfo));
+			} else {
+			    FREE(rows);
+			    break;
+			}
+		    }
+		    row->ncells = 0;
+		    row->fixed_line = NO;
+		    row->alignment = HT_ALIGN_NONE;
+		    row->offset = 0;
+		    row->content = 0;
+		}
+	    }
+	    if (rows) {
+		me->allocated_rows += growby;
+		me->rows = rows;
+	    } else {
+		return -1;
+	    }
+	}
+    }
+
+    me->rows[me->nrows].Line = lineno;
+    if (me->nrows == 0)
+	me->startline = lineno;
+    if (alignment != HT_ALIGN_NONE)
+	me->rows[me->nrows].alignment = alignment;
+    else
+	me->rows[me->nrows].alignment =
+	    (me->rowgroup_align == HT_ALIGN_NONE) ?
+	    me->alignment : me->rowgroup_align;
+    me->nrows++;
+    if (me->pending_colgroup_next > me->ncolinfo) {
+	me->ncolinfo = me->pending_colgroup_next;
+	me->pending_colgroup_next = 0;
+    }
+    me->rows[me->nrows].Line = -1;	/* not yet used */
+    me->rows[me->nrows].ended = ROW_not_ended;	/* No </tr> yet */
+    return (me->nrows - 1);
+}
+
+/*
+ * Returns -1 on error, otherwise current number of rows.
+ */
+static int Stbl_finishRowInTable(STable_info *me)
+{
+    STable_rowinfo *lastrow;
+    STable_states *s = &me->s;
+
+    CTRACE2(TRACE_TRST, (tfp, "TRST:Stbl_finishRowInTable()\n"));
+    if (!me->rows || !me->nrows)
+	return -1;		/* no row started! */
+    lastrow = me->rows + (me->nrows - 1);
+    lastrow->ended = ROW_ended_by_endtr;
+    if (lastrow->ncells > 0) {
+	if (s->pending_len > 0)
+	    lastrow->cells[lastrow->ncells - 1].len = s->pending_len;
+	s->pending_len = 0;
+    }
+    s->prev_state = s->state = CS_invalid;
+    s->lineno = -1;
+
+    if (s->icell_core >= 0 && !lastrow->fixed_line &&
+	lastrow->cells[s->icell_core].cLine >= 0)
+	lastrow->Line = lastrow->cells[s->icell_core].cLine;
+    s->icell_core = -1;
+    return (me->nrows);
+}
+
+static void update_sumcols0(STable_cellinfo *sumcols,
+			    STable_rowinfo *lastrow,
+			    int pos,
+			    int len,
+			    int icell,
+			    int ispan,
+			    int allocated_sumcols)
+{
+    int i;
+
+    if (len > 0) {
+	int sumpos = pos;
+	int prevsumpos = sumcols[icell + ispan].pos;
+	int advance;
+
+	if (ispan > 0) {
+	    if (lastrow->cells[icell].pos + len > sumpos)
+		sumpos = lastrow->cells[icell].pos + len;
+	    if (sumcols[icell + ispan - 1].pos +
+		sumcols[icell + ispan - 1].len >
+		sumpos)
+		sumpos = sumcols[icell + ispan - 1].pos +
+		    sumcols[icell + ispan - 1].len;
+	}
+	advance = sumpos - prevsumpos;
+	if (advance > 0) {
+	    for (i = icell + ispan; i < allocated_sumcols; i++) {
+		if (ispan > 0 && sumcols[i].colspan < -1) {
+		    if (i + sumcols[i].colspan < icell + ispan) {
+			advance = sumpos - sumcols[i].pos;
+			if (i > 0)
+			    advance = HTMAX(advance,
+					    sumcols[i - 1].pos +
+					    sumcols[i - 1].len
+					    - (sumcols[i].pos));
+			if (advance <= 0)
+			    break;
+		    }
+		}
+		if (sumcols[i].pos >= 0)
+		    sumcols[i].pos += advance;
+		else {
+		    sumcols[i].pos = sumpos;
+		    break;
+		}
+	    }
+	}
+    }
+}
+
+static int get_remaining_colspan(STable_rowinfo *me,
+				 STable_cellinfo *colinfo,
+				 int ncolinfo,
+				 int colspan,
+				 int ncols_sofar)
+{
+    int i;
+    int last_colspan = me->ncells ?
+    me->cells[me->ncells - 1].colspan : 1;
+
+    if (ncolinfo == 0 || me->ncells + last_colspan > ncolinfo) {
+	colspan = HTMIN(TRST_MAXCOLSPAN,
+			ncols_sofar - (me->ncells + last_colspan - 1));
+    } else {
+	for (i = me->ncells + last_colspan - 1; i < ncolinfo - 1; i++)
+	    if (colinfo[i].cLine == EOCOLG)
+		break;
+	colspan = i - (me->ncells + last_colspan - 2);
+    }
+    CTRACE2(TRACE_TRST,
+	    (tfp, "TRST:get_remaining_colspan; colspan = %d\n",
+	     colspan));
+    return colspan;
+}
+
+#ifdef EXP_NESTED_TABLES
+/* Returns -1 on failure, 1 if faking was performed, 0 if not needed. */
+static int Stbl_fakeFinishCellInTable(STable_info *me,
+				      STable_rowinfo *lastrow,
+				      int lineno,
+				      int finishing)	/* Processing finish or start */
+{
+    STable_states *s = &me->s;
+    int fake = 0;
+
+    switch (s->state) {		/* We care only about trailing <BR> */
+    case CS_invalid:
+    case CS__0new:
+    case CS__0ef:
+    case CS__0cf:
+    case CS__new:
+    case CS__cbc:
+    case CS__ef:
+    case CS__cf:
+    default:
+	/* <BR></TD> may produce these (XXXX instead of CS__cbf?).  But if
+	   finishing==0, the caller already checked that we are on a
+	   different line.  */
+	if (finishing == 0)
+	    fake = 1;
+	break;			/* Either can't happen, or may be ignored */
+    case CS__eb:
+    case CS__0eb:
+    case CS__0cb:
+    case CS__cb:
+	fake = 1;
+	break;
+    }
+    if (fake) {
+	/* The previous action we did was putting a linebreak.  Now we
+	   want to put another one.  Fake necessary
+	   </TD></TR><TR><TD></TD><TD> (and possibly </TD>) instead. */
+	int ncells = lastrow->ncells;
+	int i;
+	int al = lastrow->alignment;
+	int cs = lastrow->cells[lastrow->ncells - 1].colspan;
+	int rs = 1;		/* XXXX How to find rowspan? */
+	int ih = 0;		/* XXXX How to find is_header? */
+	int end_td = (TRST_ENDCELL_ENDTD | TRST_FAKING_CELLS);
+	int need_reserved = 0;
+	int prev_reserved_last = -1;
+	STable_rowinfo *prev_row;
+	int prev_row_n2 = lastrow - me->rows;
+
+	CTRACE2(TRACE_TRST,
+		(tfp,
+		 "TRST:Stbl_fakeFinishCellInTable(lineno=%d, finishing=%d) START FAKING\n",
+		 lineno, finishing));
+
+	/* Although here we use pos=0, this may commit the previous
+	   cell which had <BR> as a last element.  This may overflow
+	   the screen width, so the additional checks performed in
+	   Stbl_finishCellInTable (comparing to Stbl_finishCellInRow)
+	   are needed. */
+	if (finishing) {
+	    /* Fake </TD> at BOL */
+	    if (Stbl_finishCellInTable(me, end_td, lineno, 0, 0) < 0) {
+		return -1;
+	    }
+	}
+
+	/* Fake </TR> at BOL */
+/* Stbl_finishCellInTable(lineno, 0, 0); *//* Needed? */
+
+	/* Fake <TR> at BOL */
+	if (Stbl_addRowToTable(me, al, lineno) < 0) {
+	    return -1;
+	}
+	lastrow = me->rows + (me->nrows - 1);
+	lastrow->content = IS_CONTINUATION_OF_CELL;
+	for (i = 0; i < lastrow->allocated; i++) {
+	    if (lastrow->cells[i].alignment == RESERVEDCELL) {
+		need_reserved = 1;
+		break;
+	    }
+	}
+
+	prev_row = me->rows + prev_row_n2;
+	for (i = ncells; i < prev_row->allocated; i++) {
+	    if (prev_row->cells[i].alignment == RESERVEDCELL)
+		prev_reserved_last = i;
+	}
+	if (need_reserved || prev_reserved_last >= 0) {
+	    /* Oups, we are going to stomp over a line which somebody
+	       cares about already, or the previous line had reserved
+	       cells which were not skipped over.
+
+	       Remember that STable_rowinfo is about logical (TR)
+	       table lines, not displayed lines.  We need to duplicate
+	       the reservation structure when we fake new logical lines.  */
+	    int prev_row_n = prev_row - me->rows;
+	    STable_rowinfo *rows = typeRealloc(STable_rowinfo, me->rows,
+					       (unsigned) (me->allocated_rows
+							   + 1));
+	    int need_cells = prev_reserved_last + 1;
+	    int n;
+
+	    if (!rows)
+		return -1;	/* ignore silently, no free memory, may be recoverable */
+
+	    CTRACE2(TRACE_TRST,
+		    (tfp, "TRST:Stbl_fakeFinishCellInTable REALLOC ROWSPAN\n"));
+	    me->rows = rows;
+	    lastrow = me->rows + (me->nrows - 1);
+	    prev_row = me->rows + prev_row_n;
+	    me->allocated_rows++;
+
+	    /* Insert a duplicate row after lastrow */
+	    for (n = me->allocated_rows - me->nrows - 1; n >= 0; --n)
+		lastrow[n + 1] = lastrow[n];
+
+	    /* Ignore cells, they belong to the next row now */
+	    lastrow->allocated = 0;
+	    lastrow->cells = 0;
+	    if (need_cells) {
+		lastrow->cells = typecallocn(STable_cellinfo, (unsigned) need_cells);
+
+		/* ignore silently, no free memory, may be recoverable */
+		if (!lastrow->cells) {
+		    return -1;
+		}
+		lastrow->allocated = need_cells;
+		memcpy(lastrow->cells, prev_row->cells,
+		       (unsigned) lastrow->allocated * sizeof(STable_cellinfo));
+
+		i = -1;
+		while (++i < ncells) {
+		    /* Stbl_addCellToTable grants RESERVEDCELL, but we do not
+		       want this action for fake cells.
+		       XXX Maybe always fake RESERVEDCELL instead of explicitly
+		       creating/destroying cells?  */
+		    if (lastrow->cells[i].alignment == RESERVEDCELL)
+			lastrow->cells[i].alignment = HT_LEFT;
+		}
+	    }
+	}
+
+	/* Fake <TD></TD>...<TD> (and maybe a </TD>) at BOL. */
+	CTRACE2(TRACE_TRST,
+		(tfp, "TRST:Stbl_fakeFinishCellInTable FAKE %d elts%s\n",
+		 ncells, (finishing ? ", last unfinished" : "")));
+	i = 0;
+	while (++i <= ncells) {
+	    /* XXXX A lot of args may be wrong... */
+	    if (Stbl_addCellToTable(me, (i == ncells ? cs : 1), rs, al,
+				    ih, lineno, 0, 0) < 0) {
+		return -1;
+	    }
+	    lastrow->content &= ~HAS_BEG_OF_CELL;	/* BEG_OF_CELL was fake */
+	    /* We cannot run out of width here, so it is safe to not
+	       call Stbl_finishCellInTable(), but Stbl_finishCellInRow. */
+	    if (!finishing || (i != ncells)) {
+		if (Stbl_finishCellInRow(lastrow, s, end_td, lineno, 0) < 0) {
+		    return -1;
+		}
+	    }
+	}
+	CTRACE2(TRACE_TRST,
+		(tfp,
+		 "TRST:Stbl_fakeFinishCellInTable(lineno=%d) FINISH FAKING\n",
+		 lineno));
+	return 1;
+    }
+    return 0;
+}
+#endif
+
+/*
+ * Returns -1 on error, otherwise 0.
+ */
+int Stbl_addCellToTable(STable_info *me, int colspan,
+			int rowspan,
+			int alignment,
+			int isheader,
+			int lineno,
+			int offset_not_used_yet GCC_UNUSED,
+			int pos)
+{
+    STable_states *s = &me->s;
+    STable_rowinfo *lastrow;
+    STable_cellinfo *sumcols, *sumcol;
+    int i, icell, ncells, sumpos;
+
+    CTRACE2(TRACE_TRST,
+	    (tfp,
+	     "TRST:Stbl_addCellToTable(lineno=%d, pos=%d, isheader=%d, cs=%d, rs=%d, al=%d)\n",
+	     lineno, pos, (int) isheader, colspan, rowspan, alignment));
+    if (!me->rows || !me->nrows)
+	return -1;		/* no row started! */
+    /* ##850_fail_if_fail?? */
+    if (me->rows[me->nrows - 1].ended != ROW_not_ended)
+	Stbl_addRowToTable(me, alignment, lineno);
+    Stbl_finishCellInTable(me, TRST_ENDCELL_ENDTD, lineno, 0, pos);
+    lastrow = me->rows + (me->nrows - 1);
+
+#ifdef EXP_NESTED_TABLES
+    if (nested_tables) {
+	/* If the last cell was finished by <BR></TD>, we need to fake an
+	   appropriate amount of cells */
+	if (!NO_AGGRESSIVE_NEWROW && pos == 0 && lastrow->ncells > 0
+	    && lastrow->cells[lastrow->ncells - 1].cLine != lineno) {
+	    int rc = Stbl_fakeFinishCellInTable(me, lastrow, lineno, 0);
+
+	    if (rc < 0)
+		return -1;
+	    if (rc)
+		lastrow = me->rows + (me->nrows - 1);
+	}
+    }
+#endif
+    if (colspan == 0) {
+	colspan = get_remaining_colspan(lastrow, me->sumcols, me->ncolinfo,
+					colspan, me->ncols);
+    }
+    ncells = lastrow->ncells;	/* remember what it was before adding cell. */
+    icell = Stbl_addCellToRow(lastrow, me->sumcols, me->ncolinfo, s,
+			      colspan, alignment, isheader,
+			      lineno, &pos);
+    if (icell < 0)
+	return icell;
+    if (me->nrows == 1 && me->startline < lastrow->Line)
+	me->startline = lastrow->Line;
+
+    if (rowspan != 1) {
+	Stbl_reserveCellsInTable(me, icell, colspan, rowspan);
+	/* me->rows may now have been realloc'd, make lastrow valid pointer */
+	lastrow = me->rows + (me->nrows - 1);
+    }
+    lastrow->content |= HAS_BEG_OF_CELL;
+
+    {
+	int growby = 0;
+
+	while (icell + colspan + 1 > me->allocated_sumcols + growby)
+	    growby += CELLS_GROWBY;
+	if (growby) {
+	    if (me->allocated_sumcols == 0 && !me->sumcols) {
+		sumcols = typecallocn(STable_cellinfo, (unsigned) growby);
+	    } else {
+		sumcols = typeRealloc(STable_cellinfo, me->sumcols,
+				        (unsigned) (me->allocated_sumcols + growby));
+
+		for (i = 0; sumcols && i < growby; i++) {
+		    sumcol = sumcols + me->allocated_sumcols + i;
+		    sumcol->pos = sumcols[me->allocated_sumcols - 1].pos;
+		    sumcol->len = 0;
+		    sumcol->colspan = 0;
+		    sumcol->cLine = 0;
+		    sumcol->alignment = HT_ALIGN_NONE;
+		}
+	    }
+	    if (sumcols) {
+		me->allocated_sumcols += growby;
+		me->sumcols = sumcols;
+	    } else {
+		return -1;
+	    }
+	}
+    }
+    if (icell + 1 > me->ncols) {
+	me->ncols = icell + 1;
+    }
+    if (colspan > 1 && colspan + me->sumcols[icell + colspan].colspan > 0)
+	me->sumcols[icell + colspan].colspan = -colspan;
+    sumpos = pos;
+    if (ncells > 0)
+	sumpos += me->sumcols[ncells - 1].pos - lastrow->cells[ncells - 1].pos;
+    update_sumcols0(me->sumcols, lastrow, sumpos,
+		    sumpos - ((ncells > 0)
+			      ? me->sumcols[icell].pos
+			      : me->sumcols[icell].pos),
+		    icell, 0, me->allocated_sumcols);
+
+    me->maxpos = me->sumcols[me->allocated_sumcols - 1].pos;
+    if (me->maxpos > /* @@@ max. line length we can accept */ MAX_STBL_POS)
+	return -1;
+    return 0;
+}
+
+/*
+ * Returns -1 on error, otherwise 0.
+ */
+int Stbl_finishCellInTable(STable_info *me, int end_td,
+			   int lineno,
+			   int offset,
+			   int pos)
+{
+    STable_states *s = &me->s;
+    STable_rowinfo *lastrow;
+    int len, xlen, icell;
+    int i;
+
+    CTRACE2(TRACE_TRST,
+	    (tfp,
+	     "TRST:Stbl_finishCellInTable(lineno=%d, pos=%d, off=%d, end_td=%d)\n",
+	     lineno, pos, offset, (int) end_td));
+    if (me->nrows == 0)
+	return -1;
+    lastrow = me->rows + (me->nrows - 1);
+    icell = lastrow->ncells - 1;
+    if (icell < 0)
+	return icell;
+    if (s->x_td == -1) {	/* Stray </TD> or just-in-case, as on </TR> */
+	if ((end_td & TRST_ENDCELL_MASK) == TRST_ENDCELL_LINEBREAK)
+	    lastrow->ended = ROW_ended_by_splitline;
+	return 0;
+    }
+#ifdef EXP_NESTED_TABLES
+    if (nested_tables) {
+	if (!NO_AGGRESSIVE_NEWROW && !(end_td & TRST_FAKING_CELLS)) {
+	    int rc = Stbl_fakeFinishCellInTable(me, lastrow, lineno, 1);
+
+	    if (rc) {
+		if (rc < 0)
+		    return -1;
+		lastrow = me->rows + (me->nrows - 1);
+		icell = lastrow->ncells - 1;
+	    }
+	}
+    }
+#endif
+    len = Stbl_finishCellInRow(lastrow, s, end_td, lineno, pos);
+    if (len == -1)
+	return len;
+    xlen = (len > 0) ? len : s->pending_len;	/* ##890 use xlen if fixed_line?: */
+    if (lastrow->Line == lineno)
+	len = xlen;
+    if (lastrow->cells[icell].colspan > 1) {
+	/*
+	 * @@@ This is all a too-complicated mess; do we need
+	 * sumcols len at all, or is pos enough??
+	 * Answer: sumcols len is at least used for center/right
+	 * alignment, and should probably continue to be used there;
+	 * all other uses are probably not necessary.
+	 */
+	int spanlen = 0, spanlend = 0;
+
+	for (i = icell; i < icell + lastrow->cells[icell].colspan; i++) {
+	    if (me->sumcols[i].len > 0) {
+		spanlen += me->sumcols[i].len;
+		if (i > icell)
+		    spanlen++;
+	    }
+	    spanlend = HTMAX(spanlend,
+			     me->sumcols[i + 1].pos - me->sumcols[icell].pos);
+	}
+	if (spanlend)
+	    spanlend--;
+	if (spanlend > spanlen)
+	    spanlen = spanlend;
+	/* @@@ could overcount? */
+	if (len > spanlen)
+	    me->maxlen += (len - spanlen);
+    } else if (len > me->sumcols[icell].len) {
+	if (me->sumcols[icell + 1].colspan >= -1)
+	    me->maxlen += (len - me->sumcols[icell].len);
+	me->sumcols[icell].len = len;
+    }
+
+    if (len > 0) {
+	update_sumcols0(me->sumcols, lastrow, pos, len,
+			icell, lastrow->cells[icell].colspan,
+			me->allocated_sumcols);
+	me->maxpos = me->sumcols[me->allocated_sumcols - 1].pos;
+    }
+
+    if ((end_td & TRST_ENDCELL_MASK) == TRST_ENDCELL_LINEBREAK) {
+	lastrow->ended = ROW_ended_by_splitline;
+	lastrow->content |= BELIEVE_OFFSET;
+	lastrow->offset = offset;
+    }
+#ifdef EXP_NESTED_TABLES	/* maxlen may already include contribution of a cell in this column */
+    if (nested_tables) {
+	if (me->maxlen > MAX_STBL_POS)
+	    return -1;
+    } else
+#endif
+    {
+	if (me->maxlen + (xlen - len) > MAX_STBL_POS)
+	    return -1;
+    }
+    if (me->maxpos > /* @@@ max. line length we can accept */ MAX_STBL_POS)
+	return -1;
+
+    if (lineno != lastrow->Line) {
+	/* @@@ Do something here?  Or is it taken care of in
+	   Stbl_finishCellInRow ? */
+    }
+
+    return 0;
+}
+
+/*
+ * Returns -1 on error, otherwise 0.
+ */
+int Stbl_addColInfo(STable_info *me, int colspan,
+		    short alignment,
+		    BOOL isgroup)
+{
+    STable_cellinfo *sumcols, *sumcol;
+    int i, icolinfo;
+
+    CTRACE2(TRACE_TRST,
+	    (tfp, "TRST:Stbl_addColInfo(cs=%d, al=%d, isgroup=%d)\n",
+	     colspan, alignment, (int) isgroup));
+    if (isgroup) {
+	if (me->pending_colgroup_next > me->ncolinfo)
+	    me->ncolinfo = me->pending_colgroup_next;
+	me->pending_colgroup_next = me->ncolinfo + colspan;
+	if (me->ncolinfo > 0)
+	    me->sumcols[me->ncolinfo - 1].cLine = EOCOLG;
+	me->pending_colgroup_align = alignment;
+    } else {
+	for (i = me->pending_colgroup_next - 1;
+	     i >= me->ncolinfo + colspan; i--)
+	    me->sumcols[i].alignment = HT_ALIGN_NONE;
+	me->pending_colgroup_next = me->ncolinfo + colspan;
+    }
+    icolinfo = me->ncolinfo;
+    if (!isgroup)
+	me->ncolinfo += colspan;
+
+    {
+	int growby = 0;
+
+	while (icolinfo + colspan + 1 > me->allocated_sumcols + growby)
+	    growby += CELLS_GROWBY;
+	if (growby) {
+	    if (me->allocated_sumcols == 0) {
+		sumcols = typecallocn(STable_cellinfo, (unsigned) growby);
+	    } else {
+		sumcols = typeRealloc(STable_cellinfo, me->sumcols,
+				        (unsigned) (me->allocated_sumcols + growby));
+
+		for (i = 0; sumcols && i < growby; i++) {
+		    sumcol = sumcols + me->allocated_sumcols + i;
+		    sumcol->pos = sumcols[me->allocated_sumcols - 1].pos;
+		    sumcol->len = 0;
+		    sumcol->colspan = 0;
+		    sumcol->cLine = 0;
+		}
+	    }
+	    if (sumcols) {
+		me->allocated_sumcols += growby;
+		me->sumcols = sumcols;
+	    } else {
+		return -1;
+	    }
+	}
+    }
+
+    if (alignment == HT_ALIGN_NONE)
+	alignment = me->pending_colgroup_align;
+    for (i = icolinfo; i < icolinfo + colspan; i++) {
+	me->sumcols[i].alignment = alignment;
+    }
+    return 0;
+}
+
+/*
+ * Returns -1 on error, otherwise 0.
+ */
+int Stbl_finishColGroup(STable_info *me)
+{
+    CTRACE2(TRACE_TRST, (tfp, "TRST:Stbl_finishColGroup()\n"));
+    if (me->pending_colgroup_next >= me->ncolinfo) {
+	me->ncolinfo = me->pending_colgroup_next;
+	if (me->ncolinfo > 0)
+	    me->sumcols[me->ncolinfo - 1].cLine = EOCOLG;
+    }
+    me->pending_colgroup_next = 0;
+    me->pending_colgroup_align = HT_ALIGN_NONE;
+    return 0;
+}
+
+int Stbl_addRowGroup(STable_info *me, short alignment)
+{
+    CTRACE2(TRACE_TRST, (tfp, "TRST:Stbl_addRowGroup()\n"));
+    Stbl_cancelRowSpans(me);
+    me->rowgroup_align = alignment;
+    return 0;			/* that's all! */
+}
+
+int Stbl_finishTABLE(STable_info *me)
+{
+    STable_states *s = &me->s;
+    int i;
+    int curpos = 0;
+
+    CTRACE2(TRACE_TRST, (tfp, "TRST:Stbl_finishTABLE()\n"));
+    if (!me || me->nrows <= 0 || me->ncols <= 0) {
+	return -1;
+    }
+    if (me->nrows > 0 && me->rows[me->nrows - 1].ncells > 0) {
+	if (s->pending_len > 0)
+	    me->rows[me->nrows - 1].cells[
+					     me->rows[me->nrows - 1].ncells - 1
+		].len = s->pending_len;
+	s->pending_len = 0;
+    }
+    Stbl_finishRowInTable(me);
+    /* take into account offsets on multi-line cells.
+       XXX We cannot do it honestly, since two cells on the same row may
+       participate in multi-line table entries, and we preserve only
+       one offset per row.  This implementation may ignore
+       horizontal offsets for the last row of a multirow table entry.  */
+    for (i = 0; i < me->nrows - 1; i++) {
+	int j = i + 1, leading = i, non_empty = 0;
+	STable_rowinfo *nextrow = me->rows + j;
+	int minoffset, have_offsets;
+	int foundcell = -1, max_width;
+
+	if ((nextrow->content & (IS_CONTINUATION_OF_CELL | HAS_BEG_OF_CELL | BELIEVE_OFFSET))
+	    != (IS_CONTINUATION_OF_CELL | BELIEVE_OFFSET))
+	    continue;		/* Not a continuation line */
+	minoffset = nextrow[-1].offset;		/* Line before first continuation */
+	CTRACE2(TRACE_TRST, (tfp,
+			     "TRST:Stbl_finishTABLE, l=%d, offset=%d, ended=%u.\n",
+			     i, nextrow[-1].offset, nextrow[-1].ended));
+
+	/* Find the common part of the requested offsets */
+	while (j < me->nrows
+	       && ((nextrow->content &
+		    (IS_CONTINUATION_OF_CELL
+		     | HAS_BEG_OF_CELL
+		     | BELIEVE_OFFSET))
+		   == (IS_CONTINUATION_OF_CELL | BELIEVE_OFFSET))) {
+	    if (minoffset > nextrow->offset)
+		minoffset = nextrow->offset;
+	    CTRACE2(TRACE_TRST,
+		    (tfp,
+		     "TRST:Stbl_finishTABLE, l=%d, offset=%d, ended=%u.\n",
+		     j, nextrow->offset, nextrow[-1].ended));
+	    nextrow++;
+	    j++;
+	}
+	i = j - 1;		/* Continue after this line */
+	/* Cancel the common part of offsets */
+	j = leading;		/* Restart */
+	nextrow = me->rows + j;	/* Line before first continuation */
+	have_offsets = 0;
+	nextrow->content |= OFFSET_IS_VALID_LAST_CELL;
+	while (j <= i) {	/* A continuation line */
+	    nextrow->offset -= minoffset;
+	    nextrow->content |= OFFSET_IS_VALID;
+	    if (nextrow->offset)
+		have_offsets = 1;
+	    nextrow++;
+	    j++;
+	}
+	if (!have_offsets)
+	    continue;		/* No offsets to deal with */
+
+	/* Find the cell number */
+	foundcell = -1;
+	j = leading + 1;	/* Restart */
+	nextrow = me->rows + j;	/* First continuation line */
+	while (foundcell == -1 && j <= i) {	/* A continuation line */
+	    int curcell = -1;
+
+	    while (foundcell == -1 && ++curcell < nextrow->ncells)
+		if (nextrow->cells[curcell].len)
+		    foundcell = curcell, non_empty = j;
+	    nextrow++;
+	    j++;
+	}
+	if (foundcell == -1)	/* Can it happen? */
+	    continue;
+	/* Find the max width */
+	max_width = 0;
+	j = leading;		/* Restart */
+	nextrow = me->rows + j;	/* Include the pre-continuation line */
+	while (j <= i) {	/* A continuation line */
+	    if (nextrow->ncells > foundcell) {
+		int curwid = nextrow->cells[foundcell].len + nextrow->offset;
+
+		if (curwid > max_width)
+		    max_width = curwid;
+	    }
+	    nextrow++;
+	    j++;
+	}
+	/* Update the widths */
+	j = non_empty;		/* Restart from the first nonempty */
+	nextrow = me->rows + j;
+	/* Register the increase of the width */
+	update_sumcols0(me->sumcols, me->rows + non_empty,
+			0 /* width only */ , max_width,
+			foundcell, nextrow->cells[foundcell].colspan,
+			me->allocated_sumcols);
+	j = leading;		/* Restart from pre-continuation */
+	nextrow = me->rows + j;
+	while (j <= i) {	/* A continuation line */
+	    if (nextrow->ncells > foundcell)
+		nextrow->cells[foundcell].len = max_width;
+	    nextrow++;
+	    j++;
+	}
+    }				/* END of Offsets processing */
+
+    for (i = 0; i < me->ncols; i++) {
+	if (me->sumcols[i].pos < curpos) {
+	    me->sumcols[i].pos = curpos;
+	} else {
+	    curpos = me->sumcols[i].pos;
+	}
+	if (me->sumcols[i].len > 0) {
+	    curpos += me->sumcols[i].len;
+	}
+    }
+    /* need to recheck curpos: though it is checked each time a cell
+       is added, sometimes the result is ignored, as in split_line(). */
+    return (curpos > MAX_STBL_POS ? -1 : me->ncols);
+}
+
+short Stbl_getAlignment(STable_info *me)
+{
+    return (short) (me ? me->alignment : HT_ALIGN_NONE);
+}
+
+static int get_fixup_positions(STable_rowinfo *me, int *oldpos,
+			       int *newpos,
+			       STable_cellinfo *sumcols)
+{
+    int i = 0, ip = 0;
+    int next_i, newlen;
+    int ninserts;
+
+    if (!me)
+	return -1;
+    while (i < me->ncells) {
+	int offset;
+
+	next_i = i + HTMAX(1, me->cells[i].colspan);
+	if (me->cells[i].cLine != me->Line) {
+	    if (me->cells[i].cLine > me->Line)
+		break;
+	    i = next_i;
+	    continue;
+	}
+	oldpos[ip] = me->cells[i].pos;
+	if ((me->content & OFFSET_IS_VALID)
+	    && (i == me->ncells - 1
+		|| !((me->content & OFFSET_IS_VALID_LAST_CELL))))
+	    offset = me->offset;
+	else
+	    offset = 0;
+	newpos[ip] = sumcols[i].pos + offset;
+	if ((me->cells[i].alignment == HT_CENTER ||
+	     me->cells[i].alignment == HT_RIGHT) &&
+	    me->cells[i].len > 0) {
+	    newlen = sumcols[next_i].pos - newpos[ip] - 1;
+	    newlen = HTMAX(newlen, sumcols[i].len);
+	    if (me->cells[i].len < newlen) {
+		if (me->cells[i].alignment == HT_RIGHT) {
+		    newpos[ip] += newlen - me->cells[i].len;
+		} else {
+		    newpos[ip] += (newlen - me->cells[i].len) / 2;
+		}
+	    }
+	}
+	ip++;
+	i = next_i;
+    }
+    ninserts = ip;
+    return ninserts;
+}
+
+/*
+ * Returns -1 if we have no row for this lineno, or for other error,
+ *	    0 or greater (number of oldpos/newpos pairs) if we have
+ *	      a table row.
+ */
+int Stbl_getFixupPositions(STable_info *me, int lineno,
+			   int *oldpos,
+			   int *newpos)
+{
+    STable_rowinfo *row;
+    int j;
+    int ninserts = -1;
+
+    if (!me || !me->nrows)
+	return -1;
+    for (j = 0; j < me->nrows; j++) {
+	row = me->rows + j;
+	if (row->Line == lineno) {
+	    ninserts = get_fixup_positions(row, oldpos, newpos,
+					   me->sumcols);
+	    break;
+	}
+    }
+    return ninserts;
+}
+
+int Stbl_getStartLine(STable_info *me)
+{
+    if (!me)
+	return -1;
+    else
+	return me->startline;
+}
+
+#ifdef EXP_NESTED_TABLES
+
+int Stbl_getStartLineDeep(STable_info *me)
+{
+    if (!me)
+	return -1;
+    while (me->enclosing)
+	me = me->enclosing;
+    return me->startline;
+}
+
+void Stbl_update_enclosing(STable_info *me, int max_width,
+			   int last_lineno)
+{
+    int l;
+
+    if (!me || !me->enclosing || !max_width)
+	return;
+    CTRACE2(TRACE_TRST,
+	    (tfp, "TRST:Stbl_update_enclosing, width=%d, lines=%d...%d.\n",
+	     max_width, me->startline, last_lineno));
+    for (l = me->startline; l <= last_lineno; l++) {
+	/* Fake <BR> in appropriate positions */
+	if (Stbl_finishCellInTable(me->enclosing,
+				   TRST_ENDCELL_LINEBREAK,
+				   l,
+				   0,
+				   max_width) < 0) {
+	    /* It is not handy to let the caller delete me->enclosing,
+	       and it does not buy us anything.  Do it directly. */
+	    STable_info *stbl = me->enclosing;
+
+	    CTRACE2(TRACE_TRST, (tfp,
+				 "TRST:Stbl_update_enclosing: width too large, aborting enclosing\n"));
+	    me->enclosing = 0;
+	    while (stbl) {
+		STable_info *enclosing = stbl->enclosing;
+
+		Stbl_free(stbl);
+		stbl = enclosing;
+	    }
+	    break;
+	}
+    }
+    return;
+}
+
+void Stbl_set_enclosing(STable_info *me, STable_info *enclosing, struct _TextAnchor *enclosing_last_anchor_before_stbl)
+{
+    if (!me)
+	return;
+    me->enclosing = enclosing;
+    me->enclosing_last_anchor_before_stbl = enclosing_last_anchor_before_stbl;
+}
+
+STable_info *Stbl_get_enclosing(STable_info *me)
+{
+    if (!me)
+	return 0;
+    return me->enclosing;
+}
+
+struct _TextAnchor *Stbl_get_last_anchor_before(STable_info *me)
+{
+    if (!me)
+	return 0;
+    return me->enclosing_last_anchor_before_stbl;
+}
+#endif
diff --git a/src/TRSTable.h b/src/TRSTable.h
new file mode 100644
index 00000000..ef11cd54
--- /dev/null
+++ b/src/TRSTable.h
@@ -0,0 +1,49 @@
+#ifndef TRSTABLE_H
+#define TRSTABLE_H
+
+#include <HTUtils.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* TRST_MAXCOLSPAN and TRST_MAXCOLSPAN are defined in userdefs.h */ typedef struct _STable_info STable_info;
+    extern STable_info *Stbl_startTABLE(short);
+    extern int Stbl_finishTABLE(STable_info *);
+    extern void Stbl_free(STable_info *);
+    extern int Stbl_addRowToTable(STable_info *, int, int);
+    extern int Stbl_addCellToTable(STable_info *, int, int, int, int, int,
+				   int, int);
+    extern int Stbl_finishCellInTable(STable_info *, int, int, int, int);
+    extern int Stbl_addColInfo(STable_info *, int, short, BOOL);
+    extern int Stbl_finishColGroup(STable_info *);
+    extern int Stbl_addRowGroup(STable_info *, short);
+
+#define TRST_ENDCELL_ENDTD	1
+#define TRST_ENDCELL_LINEBREAK	0
+#define TRST_ENDCELL_MASK	1
+#define TRST_FAKING_CELLS	2
+#define Stbl_lineBreak(stbl,l,off,pos) Stbl_finishCellInTable(stbl, TRST_ENDCELL_LINEBREAK, l, off, pos)
+
+    extern int Stbl_getStartLine(STable_info *);
+    extern int Stbl_getFixupPositions(STable_info *me, int lineno,
+				      int *oldpos,
+				      int *newpos);
+    extern short Stbl_getAlignment(STable_info *);
+
+#ifdef EXP_NESTED_TABLES
+    extern void Stbl_update_enclosing(STable_info *me, int max_width,
+				      int last_lineno);
+    struct _TextAnchor;
+    extern void Stbl_set_enclosing(STable_info *me, STable_info *encl, struct _TextAnchor *last_anchor);
+    extern STable_info *Stbl_get_enclosing(STable_info *me);
+    extern struct _TextAnchor *Stbl_get_last_anchor_before(STable_info *me);
+    extern int Stbl_getStartLineDeep(STable_info *);
+
+#else
+#define Stbl_getStartLineDeep(t) Stbl_getStartLine(t)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* TRSTABLE_H */
diff --git a/src/UCAuto.c b/src/UCAuto.c
new file mode 100644
index 00000000..37b736f3
--- /dev/null
+++ b/src/UCAuto.c
@@ -0,0 +1,820 @@
+/*
+ * $LynxId: UCAuto.c,v 1.43 2010/05/02 22:23:54 tom Exp $
+ *
+ *  This file contains code for changing the Linux console mode.
+ *  Currently some names for font files are hardwired in here.
+ *  You have to change this code if it needs accommodation for your
+ *  system (or get the required files...).
+ *
+ *  Depending on the Display Character Set switched to, and the previous
+ *  one as far as it is known, system("setfont ...") and/or output of
+ *  escape sequences to switch console mode are done.  Curses will be
+ *  temporarily suspended while that happens.
+ *
+ *  NOTE that the setfont calls will also affect all other virtual consoles.
+ *
+ *  Any ideas how to do this for other systems?
+ */
+
+#include <HTUtils.h>
+#include <LYUtils.h>
+
+#include <UCMap.h>
+#include <UCDefs.h>
+#include <UCAuto.h>
+#include <LYGlobalDefs.h>
+#include <LYStrings.h>
+#include <LYClean.h>
+#include <LYLeaks.h>
+#include <LYCharSets.h>
+
+#ifdef EXP_CHARTRANS_AUTOSWITCH
+
+#include <HTFile.h>
+#include <www_wait.h>
+
+#ifdef LINUX
+#include <sysexits.h>		/* EX_DATAERR, etc. */
+#endif
+
+#  ifdef CAN_SWITCH_DISPLAY_CHARSET
+char *charset_switch_rules;
+char *charsets_directory;
+int auto_other_display_charset = -1;
+int codepages[2];
+int real_charsets[2] =
+{-1, -1};			/* Non "auto-" charsets for the cps */
+int switch_display_charsets;
+
+#  endif
+
+#ifdef HAVE_USE_LEGACY_CODING
+static int original_coding = 0;
+#endif
+
+#  ifdef __EMX__
+/* If we "just include" <os2.h>, BOOLEAN conflicts. */
+#  define BOOLEAN OS2_BOOLEAN	/* This file doesn't use it, conflicts */
+#  define INCL_VIO		/* I want some Vio functions.. */
+#  define INCL_DOSPROCESS	/* TIB PIB. */
+#  define INCL_DOSNLS		/* DosQueryCp. */
+#  include <os2.h>		/* Misc stuff.. */
+#  include <os2thunk.h>		/* 32 bit to 16 bit pointer conv */
+#  endif
+
+#ifdef LINUX
+typedef enum {
+    Is_Unset,
+    Is_Set,
+    Dunno,
+    Dont_Care
+} TGen_state_t;
+
+/*
+ * List the states the console has been set to via SCS (select character-set).
+ */
+typedef enum {
+    GN_Blat1,			/* Latin-1 */
+    GN_0decgraf,		/* VT100 graphics */
+    GN_Ucp437,			/* PC -> PC */
+    GN_Kuser,			/* user-defined */
+    GN_dunno,
+    GN_dontCare
+} TTransT_t;
+
+static char *T_font_fn = NULL;	/* font filename */
+static char *T_umap_fn = NULL;	/* unicode-map filename */
+
+#define NOOUTPUT "2>/dev/null >/dev/null"
+
+/*
+ * Return the configured path of the setfont/consolechars program.
+ */
+static const char *GetSetfontPath(void)
+{
+    return HTGetProgramPath(ppSETFONT);
+}
+
+/*
+ * setfont and consolechars have different options and available data.
+ */
+static BOOL isSetFont(void)
+{
+    const char *program = GetSetfontPath();
+    const char *slash = strrchr(program, '/');
+    const char *leaf = (slash ? slash + 1 : program);
+
+    return (BOOL) !strcmp(leaf, "setfont");
+}
+
+/*
+ * Here are the differences in options which affect lynx:
+ */
+#define setfont_u()    (isSetFont() ? "-u "  : "--sfm ")
+#define setfont_o()    (isSetFont() ? "-o "  : "--old-font-raw ")
+#define setfont_ou()   (isSetFont() ? "-ou " : "--old-sfm ")
+#define console_font() (isSetFont() ? ""     : "--font ")
+
+/*
+ * call_setfont - execute "setfont" command via system()
+ * returns:	 0  ok (as far as we know)
+ *		-1  error (assume font and umap are not loaded)
+ *		 1  error with umap (assume font loaded but umap empty)
+ */
+static int call_setfont(const char *font,
+			const char *fnsuffix,
+			const char *umap)
+{
+    const char *program = GetSetfontPath();
+    char *T_setfont_cmd = NULL;
+    int rv;
+
+    /*
+     * console-data package has only a few unicode maps.
+     */
+    if (!isSetFont())
+	umap = 0;
+
+    if ((font && T_font_fn && !strcmp(font, T_font_fn))
+	&& (umap && T_umap_fn && !strcmp(umap, T_umap_fn))) {
+	/*
+	 * No need to repeat.
+	 */
+	return 0;
+    }
+    if (font)
+	StrAllocCopy(T_font_fn, font);
+    if (umap)
+	StrAllocCopy(T_umap_fn, umap);
+
+    if (!*fnsuffix)
+	fnsuffix = "";
+
+    if (non_empty(umap) && non_empty(font)) {
+	HTSprintf0(&T_setfont_cmd, "%s %s%s%s %s%s %s",
+		   program,
+		   console_font(), font, fnsuffix,
+		   setfont_u(), umap,
+		   NOOUTPUT);
+    } else if (non_empty(font)) {
+	HTSprintf0(&T_setfont_cmd, "%s %s%s%s %s",
+		   program,
+		   console_font(), font, fnsuffix,
+		   NOOUTPUT);
+    } else if (non_empty(umap)) {
+	HTSprintf0(&T_setfont_cmd, "%s %s%s %s",
+		   program,
+		   setfont_u(), umap,
+		   NOOUTPUT);
+    }
+
+    if (T_setfont_cmd) {
+	CTRACE((tfp, "Changing font: '%s'\n", T_setfont_cmd));
+	rv = LYSystem(T_setfont_cmd);
+	FREE(T_setfont_cmd);
+	if (rv) {
+	    CTRACE((tfp, "call_setfont: system returned %d (0x%x)!\n",
+		    rv, rv));
+	    if (rv == -1 || WIFSIGNALED(rv) || !WIFEXITED(rv)) {
+		return -1;
+	    } else if ((WEXITSTATUS(rv) == EX_DATAERR ||
+			WEXITSTATUS(rv) == EX_NOINPUT) &&
+		       non_empty(umap)) {
+		/*
+		 * Check if the font was loaded ok but something was wrong with
+		 * the umap file.
+		 */
+		return 1;
+	    } else {
+		return -1;
+	    }
+	}
+    }
+    return 0;
+}
+
+static void write_esc(const char *p)
+{
+    int fd = open("/dev/tty", O_WRONLY);
+
+    if (fd >= 0) {
+	IGNORE_RC(write(fd, p, strlen(p)));
+	close(fd);
+    }
+}
+
+static int nonempty_file(const char *p)
+{
+    struct stat sb;
+
+    return (stat(p, &sb) == 0 &&
+	    S_ISREG(sb.st_mode) &&
+	    (sb.st_size != 0));
+}
+
+static BOOL on_console(void)
+{
+    if ((x_display != NULL) ||
+	LYgetXDisplay() != NULL) {
+	/*
+	 * We won't do anything in an xterm.  Better that way...
+	 */
+	return FALSE;
+    }
+    return TRUE;
+}
+
+/*
+ * This is the thing that actually gets called from display_page().
+ */
+void UCChangeTerminalCodepage(int newcs,
+			      LYUCcharset *p)
+{
+    const char *program = GetSetfontPath();
+    static int lastcs = -1;
+    static const char *lastname = NULL;
+    static TTransT_t lastTransT = GN_dunno;
+    static TGen_state_t lastUtf = Dunno;
+    static TGen_state_t lastHasUmap = Dunno;
+
+    static char *old_font = NULL;
+    static char *old_umap = NULL;
+
+    const char *name;
+    TTransT_t TransT = GN_dunno;
+    TGen_state_t Utf = Dunno;
+    TGen_state_t HasUmap = Dunno;
+
+    char *tmpbuf1 = NULL;
+    char *tmpbuf2 = NULL;
+    int status = 0;
+
+    if (!on_console())
+	return;
+
+#ifdef HAVE_USE_LEGACY_CODING
+    if (newcs < 0) {
+	use_legacy_coding(original_coding);
+    } else {
+	original_coding = use_legacy_coding(2);
+    }
+#endif
+
+    /*
+     * Restore the original character set.
+     */
+    if (newcs < 0 || p == 0) {
+	if (non_empty(old_font) &&
+	    non_empty(old_umap)) {
+
+	    if (nonempty_file(old_font)) {
+		if (nonempty_file(old_umap)) {
+		    HTSprintf0(&tmpbuf1, "%s %s%s %s%s %s",
+			       program,
+			       console_font(), old_font,
+			       setfont_u(), old_umap,
+			       NOOUTPUT);
+		} else {
+		    HTSprintf0(&tmpbuf1, "%s %s%s %s",
+			       program,
+			       console_font(), old_font,
+			       NOOUTPUT);
+		}
+		CTRACE((tfp, "Restoring font: '%s'\n", tmpbuf1));
+		status = LYSystem(tmpbuf1);
+		if (status != 0) {
+		    CTRACE((tfp, "...system returned %d (0x%x)\n", status, status));
+		}
+		FREE(tmpbuf1);
+	    }
+	}
+	if (newcs < 0 && p == 0) {
+	    if (old_font) {
+		LYRemoveTemp(old_font);
+		FREE(old_font);
+	    }
+	    if (old_umap) {
+		LYRemoveTemp(old_umap);
+		FREE(old_umap);
+	    }
+	    if (status == 0) {
+		FREE(T_font_fn);
+		FREE(T_umap_fn);
+	    }
+	}
+	return;
+    } else if (lastcs < 0 && old_umap == 0 && old_font == 0) {
+	FILE *fp1;
+	FILE *fp2 = NULL;
+
+	if ((old_font = typecallocn(char, LY_MAXPATH)) != 0)
+	      old_umap = typecallocn(char, LY_MAXPATH);
+
+	if (old_font == NULL)
+	    outofmem(__FILE__, "UCChangeTerminalCodepage");
+
+	assert(old_font != NULL);
+
+	if ((fp1 = LYOpenTemp(old_font, ".fnt", BIN_W)) != 0)
+	    fp2 = LYOpenTemp(old_umap, ".uni", BIN_W);
+
+	if (fp1 && fp2) {
+	    size_t nlen;
+	    int rv;
+
+	    HTSprintf0(&tmpbuf1, "%s %s%s %s%s %s",
+		       program,
+		       setfont_o(), old_font,
+		       setfont_ou(), old_umap,
+		       NOOUTPUT);
+
+	    CTRACE((tfp, "Saving font: '%s'\n", tmpbuf1));
+	    rv = LYSystem(tmpbuf1);
+	    if (rv != 0) {
+		CTRACE((tfp, "...system returned %d (0x%x)\n", rv, rv));
+	    }
+	    FREE(tmpbuf1);
+	    LYCloseTempFP(fp1);
+	    LYCloseTempFP(fp2);
+
+	    /* free up a few bytes */
+	    if ((nlen = strlen(old_font) + 1) < LY_MAXPATH)
+		old_font = typeRealloc(char, old_font, nlen);
+
+	    if ((nlen = strlen(old_umap) + 1) < LY_MAXPATH)
+		old_umap = typeRealloc(char, old_umap, nlen);
+	} else {
+	    if (fp1)
+		LYRemoveTemp(old_font);
+	    if (fp2)
+		LYRemoveTemp(old_umap);
+	    FREE(old_font);
+	    FREE(old_umap);
+	}
+    }
+
+    name = p->MIMEname;
+
+    /*
+     * Font sizes are currently hardwired here.
+     */
+#define SUFF1 ".f16"
+#define SUFF2 "-16.psf"
+#define SUFF3 "-8x16"
+#define SUFF4 "8x16"
+#define SUFF5 ".cp -16"
+#define SUFF6 "_8x16"
+
+    /* NOTE: `!!umap not in kbd!!' comments below means that the *.uni file
+     * is not found in kbd package.  Reference Debian Package: kbd-data,
+     * Version: 0.96a-14.  They should be located elsewhere or generated.
+     * Also some cpNNN fonts used below are not in the kbd-data.  - kw
+     */
+
+    if (!strncmp(name, "iso-8859-1", 10) &&
+	(!name[10] || !isdigit(UCH(name[10])))) {
+	if ((lastHasUmap == Is_Set) && !strcmp(lastname, "cp850")) {
+	    /*
+	     * cp850 already contains all latin1 characters.
+	     */
+	    if (lastTransT != GN_Blat1) {
+		TransT = GN_Blat1;
+	    }
+	} else {
+	    /*
+	     * "setfont lat1u-16.psf -u lat1u.uni"
+	     */
+	    status = call_setfont("lat1u", SUFF2, "lat1u.uni");
+	    HasUmap = Is_Set;
+	    if (lastTransT != GN_Blat1) {
+		TransT = GN_Blat1;
+	    }
+	}
+	Utf = Is_Unset;
+    } else if (!strcmp(name, "iso-8859-2")) {
+	/*
+	 * "setfont iso02.f16 -u iso02.uni"
+	 */
+	status = call_setfont("iso02", SUFF1, "iso02.uni");
+	TransT = GN_Kuser;
+	HasUmap = Is_Set;
+	Utf = Is_Unset;
+    } else if (!strcmp(name, "iso-8859-15")) {
+	/*
+	 * "setfont lat0-16.psf"
+	 */
+	status = call_setfont("lat0", SUFF2, NULL);
+	TransT = GN_Blat1;	/* bogus! */
+	HasUmap = Dunno;	/* distributed lat0 files have bogus map data! */
+	Utf = Is_Unset;
+    } else if (!strncmp(name, "iso-8859-", 9)) {
+	if (strlen(name) <= 10 || !isdigit(UCH(name[10])))
+	    HTSprintf0(&tmpbuf1, "iso0%s", &name[9]);
+	else
+	    HTSprintf0(&tmpbuf1, "iso%s", &name[9]);
+	HTSprintf0(&tmpbuf2, "%s.uni", tmpbuf1);
+	/*
+	 * "setfont iso0N.f16 -u iso0N.uni"
+	 */
+	status = call_setfont(tmpbuf1, SUFF1, tmpbuf2);
+	FREE(tmpbuf1);
+	FREE(tmpbuf2);
+	TransT = GN_Kuser;
+	HasUmap = Is_Set;
+	Utf = Is_Unset;
+    } else if (!strcmp(name, "koi8-r")) {
+	/*
+	 * "setfont koi8-8x16"
+	 * !!umap not in kbd!!
+	 */
+	status = call_setfont("koi8", SUFF3, "koi8r.uni");
+	TransT = GN_Kuser;
+	HasUmap = Is_Set;
+	Utf = Is_Unset;
+    } else if (!strcmp(name, "koi8-u")) {
+	/*
+	 * "setfont koi8u_8x16"
+	 * !!umap not in kbd!!
+	 */
+	status = call_setfont("koi8u", SUFF6, "koi8u.uni");
+	TransT = GN_Kuser;
+	HasUmap = Is_Set;
+	Utf = Is_Unset;
+    } else if (!strcmp(name, "cp437")) {
+	/*
+	 * "setfont default8x16 -u cp437.uni"
+	 */
+	status = call_setfont("default", SUFF4, "cp437.uni");
+	if (lastTransT == GN_Kuser || lastTransT == GN_Ucp437)
+	    TransT = GN_dontCare;
+	else
+	    TransT = GN_Ucp437;
+	HasUmap = Is_Set;
+	Utf = Is_Unset;
+    } else if (!strcmp(name, "cp850")) {
+	/*
+	 * "setfont cp850-8x16 -u cp850.uni"
+	 * !!umap not in kbd!!
+	 */
+	status = call_setfont("cp850", SUFF3, "cp850.uni");
+	TransT = GN_Kuser;
+	HasUmap = Is_Set;
+	Utf = Is_Unset;
+    } else if (!strcmp(name, "cp866") ||
+	       !strcmp(name, "cp852") ||
+	       !strcmp(name, "cp862")) {	/* MS-Kermit has these files */
+	HTSprintf0(&tmpbuf2, "%s.uni", name);
+	/*
+	 * "setfont cpNNN.f16"
+	 * !!umap not in kbd!!
+	 */
+	status = call_setfont(name, SUFF1, tmpbuf2);
+	FREE(tmpbuf2);
+	TransT = GN_Kuser;
+	HasUmap = Is_Set;
+	Utf = Is_Unset;
+    } else if (!strcmp(name, "cp737")) {
+	/*
+	 * "setfont cp737.cp"
+	 * !!umap not in kbd!!
+	 */
+	if (isSetFont()) {
+	    status = call_setfont("737", SUFF5, "cp737.uni");
+	} else {
+	    status = call_setfont("greek", "", "cp737.uni");
+	}
+	TransT = GN_Kuser;
+	HasUmap = Is_Set;
+	Utf = Is_Unset;
+    } else if (!strcmp(name, "cp857")) {
+	status = call_setfont("cp857", SUFF3, "cp857.uni");
+	TransT = GN_Kuser;
+	HasUmap = Is_Set;
+	Utf = Is_Unset;
+    } else if (!strcmp(name, "x-transparent")) {
+	Utf = Dont_Care;
+    } else if (!strcmp(name, "us-ascii")) {
+	Utf = Dont_Care;
+    } else if (!strncmp(name, "mnem", 4)) {
+	Utf = Dont_Care;
+    }
+
+    if (status == 1)
+	HasUmap = Is_Unset;
+    else if (status < 0) {
+	if (HasUmap == Is_Set)
+	    HasUmap = Dunno;
+	name = "unknown-8bit";
+    }
+
+    if (TransT != lastTransT) {
+	if (TransT == GN_Blat1) {
+	    /*
+	     * Switch Linux console to lat1 table.
+	     */
+	    write_esc("\033(B");
+	} else if (TransT == GN_0decgraf) {
+	    write_esc("\033(0");
+	} else if (TransT == GN_Ucp437) {
+	    /*
+	     * Switch Linux console to 437 table?
+	     */
+	    write_esc("\033(U");
+	} else if (TransT == GN_Kuser) {
+	    /*
+	     * Switch Linux console to user table.
+	     */
+	    write_esc("\033(K");
+	}
+	if (TransT != GN_dunno && TransT != GN_dontCare) {
+	    lastTransT = TransT;
+	}
+    }
+
+    if (HasUmap != Dont_Care && HasUmap != Dunno)
+	lastHasUmap = HasUmap;
+
+    if (p->enc == UCT_ENC_UTF8) {
+	if (lastUtf != Is_Set) {
+	    Utf = Is_Set;
+	    /*
+	     * Turn Linux console UTF8 mode ON.
+	     */
+	    write_esc("\033%G");
+	    lastUtf = Utf;
+	}
+	return;
+    } else if (lastUtf == Is_Set && Utf != Dont_Care) {
+	Utf = Is_Unset;
+	/*
+	 * Turn Linux console UTF8 mode OFF.
+	 */
+	write_esc("\033%@");
+	lastUtf = Utf;
+    }
+
+    if (Utf != Dont_Care && Utf != Dunno)
+	lastUtf = Utf;
+
+    lastcs = newcs;
+    lastname = name;
+}
+
+#else /* Not LINUX: */
+/*
+ * This is the thing that actually gets called from display_page().
+ */
+void UCChangeTerminalCodepage(int newcs,
+			      LYUCcharset *p)
+{
+#ifdef __EMX__
+    int res = 0;
+
+#ifdef HAVE_USE_LEGACY_CODING
+    if (newcs < 0) {
+	use_legacy_coding(original_coding);
+    } else {
+	original_coding = use_legacy_coding(2);
+    }
+#endif
+
+    if (newcs < 0)
+	newcs = auto_display_charset;
+    res = Switch_Display_Charset(newcs, SWITCH_DISPLAY_CHARSET_REALLY);
+    CTRACE((tfp,
+	    "UCChangeTerminalCodepage: Switch_Display_Charset(%d) returned %d\n",
+	    newcs, res));
+#else
+    CTRACE((tfp, "UCChangeTerminalCodepage: Called, but not implemented!"));
+#endif
+}
+#endif /* LINUX */
+
+#ifdef CAN_SWITCH_DISPLAY_CHARSET
+
+int Find_Best_Display_Charset(int ord)
+{
+    const char *name = LYCharSet_UC[ord].MIMEname;
+    char *s = charset_switch_rules, *r;
+    char buf[160];
+    static int lowercase;
+    int n = strlen(name), source = 1;
+
+    if (!s || !n)
+	return ord;
+    if (!lowercase++)
+	LYLowerCase(charset_switch_rules);
+    while (1) {
+	while (*s && strchr(" \t,", *s))
+	    s++;		/* Go to start of a name or ':' */
+	if (!*s && source)
+	    return ord;		/* OK to find nothing */
+	if (!*s) {
+	    sprintf(buf, "No destination for '%.80s' in CHARSET_SWITCH_RULES",
+		    name);
+	    HTInfoMsg(buf);
+	    return ord;
+	}
+	if (*s == ':') {
+	    /* Before the replacement name */
+	    while (*s && strchr(" \t:", *s))
+		s++;		/* Go to the replacement */
+	    /* At start of the replacement name */
+	    r = s;
+	    while (*s && !strchr(" \t,:", *s))
+		s++;		/* Skip the replacement */
+	    if (source)
+		continue;
+	    break;
+	}
+	/* At start of the source name */
+	if (source && !strnicmp(name, s, n) && strchr(" \t,", s[n])) {	/* Found! */
+	    source = 0;
+	    s += n;
+	    continue;		/* Look for the replacement */
+	}
+	while (*s && !strchr(" \t,:", *s))
+	    s++;		/* Skip the other source names */
+    }
+    /* Here r point to the replacement, s to the end of the replacement. */
+    if (s >= r + sizeof(buf)) {
+	HTInfoMsg(gettext("Charset name in CHARSET_SWITCH_RULES too long"));
+	return ord;
+    }
+    strncpy(buf, r, s - r);
+    buf[s - r] = '\0';
+    n = UCGetLYhndl_byMIME(buf);
+    if (n < 0) {
+	sprintf(buf, "Unknown charset name '%.*s' in CHARSET_SWITCH_RULES",
+		s - r, r);
+	HTInfoMsg(buf);
+	return ord;
+    }
+    return n;
+}
+
+#  ifdef __EMX__
+/* Switch display for the best fit for LYCharSet_UC[ord].
+   If really is MAYBE, the switch is tentative only, another switch may happen
+   before the actual display.
+
+   Returns the charset we switched to.  */
+static int _Switch_Display_Charset(int ord, enum switch_display_charset_t really)
+{
+    const char *name;
+    unsigned short cp;
+    static int font_loaded_for = -1, old_h, old_w;
+    int rc, ord1;
+    UCHAR msgbuf[MAXPATHLEN + 80];
+
+    CTRACE((tfp, "_Switch_Display_Charset(cp=%d, really=%d).\n", ord, really));
+    /* Do not trust current_char_set unless REALLY, we fake it if MAYBE! */
+    if (ord == current_char_set && really == SWITCH_DISPLAY_CHARSET_MAYBE)
+	return ord;
+    if (ord == auto_other_display_charset
+	|| ord == auto_display_charset || ord == font_loaded_for) {
+	if (really == SWITCH_DISPLAY_CHARSET_MAYBE)
+	    return ord;		/* Report success, to avoid flicker, switch later */
+    } else			/* Currently supports only koi8-r to cp866 translation */
+	ord = Find_Best_Display_Charset(ord);
+
+    /* Ignore sizechange unless the font is loaded */
+    if (ord != font_loaded_for && really == SWITCH_DISPLAY_CHARSET_RESIZE)
+	return ord;
+
+    if (ord == real_charsets[0] || ord == real_charsets[1]) {
+	ord1 = (ord == real_charsets[1]
+		? auto_other_display_charset : auto_display_charset);
+	if (really == SWITCH_DISPLAY_CHARSET_MAYBE)
+	    return ord;		/* Can switch later, report success to avoid flicker */
+    } else
+	ord1 = ord;
+    if (ord == current_char_set && really == SWITCH_DISPLAY_CHARSET_MAYBE)
+	return ord;
+
+    name = LYCharSet_UC[ord1].MIMEname;
+    if (ord1 == auto_other_display_charset || ord1 == auto_display_charset) {
+      retry:
+	rc = VioSetCp(0, codepages[ord1 == auto_other_display_charset], 0);
+	if (rc == 0)
+	    goto report;
+      err:
+	sprintf(msgbuf, "Can't change to '%s': err=%#x=%d", name, rc, rc);
+	HTInfoMsg(msgbuf);
+	return -1;
+    }
+
+    /* Not a "prepared" codepage.  Need to load the user font. */
+    if (charsets_directory) {
+	TIB *tib;		/* Can't load font in a windowed-VIO */
+	PIB *pib;
+	VIOFONTINFO f[2];
+	VIOFONTINFO *font;
+	UCHAR b[1 << 17];
+	UCHAR *buf = b;
+	UCHAR fnamebuf[MAXPATHLEN];
+	FILE *file;
+	APIRET rc;
+	long i, j;
+
+	/* 0 means a FS protected-mode session */
+	if (font_loaded_for == -1	/* Did not try it yet */
+	    && (DosGetInfoBlocks(&tib, &pib) || pib->pib_ultype != 0)) {
+	    ord = ord1 = auto_display_charset;
+	    goto retry;
+	}
+	/* Should not cross 64K boundaries: */
+	font = f;
+	if (((((ULONG) (char *) f) + sizeof(*font)) & 0xFFFF) < sizeof(*font))
+	    font++;
+	if (((ULONG) buf) & 0xFFFF)
+	    buf += 0x10000 - (((ULONG) buf) & 0xFFFF);
+	font->cb = sizeof(*font);	/* How large is this structure */
+	font->type = 0;		/* Not the BIOS, the loaded font. */
+	font->cbData = 65535;	/* How large is my buffer? */
+	font->pbData = _emx_32to16(buf);	/* Wants an 16:16 pointer */
+
+	rc = VioGetFont(font, 0);	/* Retrieve data for current font */
+	if (rc) {
+	    sprintf(msgbuf, "Can't fetch current font info: err=%#x=%d", rc, rc);
+	    HTInfoMsg(msgbuf);
+	    ord = ord1 = auto_display_charset;
+	    goto retry;
+	}
+	if (ord1 == font_loaded_for
+	    && old_h == font->cyCell && old_w == font->cxCell) {
+	    /* The same as the previous font */
+	    if ((rc = VioSetCp(0, -1, 0)))	/* -1: User font */
+		goto err;
+	    goto report;
+	}
+	sprintf(fnamebuf, "%s/%dx%d/%s.fnt",
+		charsets_directory, font->cyCell, font->cxCell, name);
+	file = fopen(fnamebuf, BIN_R);
+	if (!file) {
+	    sprintf(msgbuf, "Can't open font file '%s'", fnamebuf);
+	    HTInfoMsg(msgbuf);
+	    ord = ord1 = auto_display_charset;
+	    goto retry;
+	}
+	i = ftell(file);
+	fseek(file, 0, SEEK_END);
+	if (ftell(file) - i != font->cbData) {
+	    fclose(file);
+	    sprintf(msgbuf, "Mismatch of size of font file '%s'", fnamebuf);
+	    HTAlert(msgbuf);
+	    ord = ord1 = auto_display_charset;
+	    goto retry;
+	}
+	fseek(file, i, SEEK_SET);
+	fread(buf, 1, font->cbData, file);
+	fclose(file);
+	rc = VioSetFont(font, 0);	/* Put it all back.. */
+	if (rc) {
+	    sprintf(msgbuf, "Can't set font: err=%#x=%d", rc, rc);
+	    HTInfoMsg(msgbuf);
+	    ord = ord1 = auto_display_charset;
+	    font_loaded_for = -1;
+	    goto retry;
+	}
+	font_loaded_for = ord1;
+	old_h = font->cyCell;
+	old_w = font->cxCell;
+    } else {
+	ord = ord1 = auto_display_charset;
+	goto retry;
+    }
+  report:
+    CTRACE((tfp, "Display font set to '%s'.\n", name));
+    return ord;
+}
+#  endif			/* __EMX__ */
+
+int Switch_Display_Charset(const int ord, const enum switch_display_charset_t really)
+{
+    int prev = current_char_set;
+    int res;
+    static int repeated;
+
+    if (!switch_display_charsets)
+	return 0;
+    res = _Switch_Display_Charset(ord, really);
+    if (res < 0 || prev == res)	/* No change */
+	return 0;
+    /* Register the change */
+    current_char_set = res;
+    HTMLUseCharacterSet(current_char_set);
+    return 1;
+}
+#endif /* CAN_SWITCH_DISPLAY_CHARSET */
+
+#else /* EXP_CHARTRANS_AUTOSWITCH not defined: */
+/*
+ * This is the thing that actually gets called from display_page().
+ */
+void UCChangeTerminalCodepage(int newcs GCC_UNUSED,
+			      LYUCcharset *p GCC_UNUSED)
+{
+    CTRACE((tfp, "UCChangeTerminalCodepage: Called, but not implemented!"));
+}
+#endif /* EXP_CHARTRANS_AUTOSWITCH */
diff --git a/src/UCAuto.h b/src/UCAuto.h
new file mode 100644
index 00000000..98d2243f
--- /dev/null
+++ b/src/UCAuto.h
@@ -0,0 +1,14 @@
+#ifndef UCAUTO_H
+#define UCAUTO_H
+
+#include <UCDefs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    extern void UCChangeTerminalCodepage(int newcs, LYUCcharset *p);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* UCAUTO_H */
diff --git a/src/UCAux.c b/src/UCAux.c
new file mode 100644
index 00000000..92a4d51b
--- /dev/null
+++ b/src/UCAux.c
@@ -0,0 +1,627 @@
+/*
+ * $LynxId: UCAux.c,v 1.41 2010/04/29 09:10:58 tom Exp $
+ */
+#include <HTUtils.h>
+
+#include <HTCJK.h>
+#include <UCMap.h>
+#include <UCDefs.h>
+#include <HTStream.h>
+#include <UCAux.h>
+#include <LYCharSets.h>
+#include <LYCurses.h>
+#include <LYStrings.h>
+
+BOOL UCCanUniTranslateFrom(int from)
+{
+    if (from < 0)
+	return NO;
+#ifndef EXP_JAPANESEUTF8_SUPPORT
+    if (LYCharSet_UC[from].enc == UCT_ENC_CJK)
+	return NO;
+#endif
+    if (!strcmp(LYCharSet_UC[from].MIMEname, "x-transparent"))
+	return NO;
+
+    /* others YES */
+    return YES;
+}
+
+BOOL UCCanTranslateUniTo(int to)
+{
+    if (to < 0)
+	return NO;
+/*???
+    if (!strcmp(LYCharSet_UC[to].MIMEname, "x-transparent"))
+       return NO;
+*/
+
+    return YES;			/* well at least some characters... */
+}
+
+BOOL UCCanTranslateFromTo(int from,
+			  int to)
+{
+    if (from == to)
+	return YES;
+    if (from < 0 || to < 0)
+	return NO;
+    if (from == LATIN1)
+	return UCCanTranslateUniTo(to);
+    if (to == LATIN1 || LYCharSet_UC[to].enc == UCT_ENC_UTF8)
+	return UCCanUniTranslateFrom(from);
+    {
+	const char *fromname = LYCharSet_UC[from].MIMEname;
+	const char *toname = LYCharSet_UC[to].MIMEname;
+
+	if (!strcmp(fromname, "x-transparent") ||
+	    !strcmp(toname, "x-transparent")) {
+	    return YES;		/* ??? */
+	} else if (!strcmp(fromname, "us-ascii")) {
+	    return YES;
+	}
+	if (LYCharSet_UC[from].enc == UCT_ENC_CJK) {
+	    /*
+	     * CJK mode may be off (i.e., !IS_CJK_TTY) because the current
+	     * document is not CJK, but the check may be for capability in
+	     * relation to another document, for which CJK mode might be turned
+	     * on when retrieved.  Thus, when the from charset is CJK, check if
+	     * the to charset is CJK, and return NO or YES in relation to that. 
+	     * - FM
+	     */
+	    if (LYCharSet_UC[to].enc != UCT_ENC_CJK)
+		return NO;
+	    if ((!strcmp(toname, "euc-jp") ||
+		 !strcmp(toname, "shift_jis")) &&
+		(!strcmp(fromname, "euc-jp") ||
+		 !strcmp(fromname, "shift_jis")))
+		return YES;
+	    /*
+	     * The euc-cn and euc-kr charsets were handled by the (from == to)
+	     * above, so we need not check those.  - FM
+	     */
+	    return NO;
+	}
+    }
+    return YES;			/* others YES */
+}
+
+/*
+ *  Returns YES if no translation necessary (because
+ *  charsets are equal, are equivalent, etc.).
+ */
+BOOL UCNeedNotTranslate(int from,
+			int to)
+{
+    const char *fromname;
+    const char *toname;
+
+    if (from == to)
+	return YES;
+    if (from < 0)
+	return NO;		/* ??? */
+    if (LYCharSet_UC[from].enc == UCT_ENC_7BIT) {
+	return YES;		/* Only 7bit chars. */
+    }
+    fromname = LYCharSet_UC[from].MIMEname;
+    if (!strcmp(fromname, "x-transparent") ||
+	!strcmp(fromname, "us-ascii")) {
+	return YES;
+    }
+    if (to < 0)
+	return NO;		/* ??? */
+    if (to == LATIN1) {
+	if (LYCharSet_UC[from].codepoints & (UCT_CP_SUBSETOF_LAT1))
+	    return YES;
+    }
+    toname = LYCharSet_UC[to].MIMEname;
+    if (!strcmp(toname, "x-transparent")) {
+	return YES;
+    }
+    if (LYCharSet_UC[to].enc == UCT_ENC_UTF8) {
+	return NO;
+    }
+    if (from == LATIN1) {
+	if (LYCharSet_UC[from].codepoints & (UCT_CP_SUPERSETOF_LAT1))
+	    return YES;
+    }
+    if (LYCharSet_UC[from].enc == UCT_ENC_CJK) {
+	if (!IS_CJK_TTY)	/* Use that global flag, for now. */
+	    return NO;
+	if (HTCJK == JAPANESE &&
+	    (!strcmp(fromname, "euc-jp") ||
+	     !strcmp(fromname, "shift_jis")))
+	    return YES;		/* translate internally by lynx, no unicode */
+	return NO;		/* If not handled by (from == to) above. */
+    }
+    return NO;
+}
+
+/*
+ *  The idea here is that any stage of the stream pipe which is interested
+ *  in some charset dependent processing will call this function.
+ *  Given input and output charsets, this function will set various flags
+ *  in a UCTransParams structure that _suggest_ to the caller what to do.
+ *
+ *  Should be called once when a stage starts processing text (and the
+ *  input and output charsets are known), or whenever one of input or
+ *  output charsets has changed (e.g., by SGML.c stage after HTML.c stage
+ *  has processed a META tag).
+ *  The global flags (LYRawMode, HTPassEightBitRaw etc.) are currently
+ *  not taken into account here (except for HTCJK, somewhat), it's still
+ *  up to the caller to do something about them. - KW
+ */
+void UCSetTransParams(UCTransParams * pT, int cs_in,
+		      const LYUCcharset *p_in,
+		      int cs_out,
+		      const LYUCcharset *p_out)
+{
+    CTRACE((tfp, "UCSetTransParams: from %s(%d) to %s(%d)\n",
+	    p_in->MIMEname, UCGetLYhndl_byMIME(p_in->MIMEname),
+	    p_out->MIMEname, UCGetLYhndl_byMIME(p_out->MIMEname)));
+
+    /*
+     * Initialize this element to FALSE, and set it TRUE below if we're dealing
+     * with VISCII.  - FM
+     */
+    pT->trans_C0_to_uni = FALSE;
+
+    /*
+     * The "transparent" display character set is a "super raw mode".  - FM
+     */
+    pT->transp = (BOOL) (!strcmp(p_in->MIMEname, "x-transparent") ||
+			 !strcmp(p_out->MIMEname, "x-transparent"));
+
+    /*
+     * UCS-2 is handled as a special case in SGML_write().
+     */
+    pT->ucs_mode = 0;
+
+    if (pT->transp) {
+	/*
+	 * Set up the structure for "transparent".  - FM
+	 */
+	pT->do_cjk = FALSE;
+	pT->decode_utf8 = FALSE;
+	pT->output_utf8 = FALSE;	/* We may, but won't know about it. - KW */
+	pT->do_8bitraw = TRUE;
+	pT->use_raw_char_in = TRUE;
+	pT->strip_raw_char_in = FALSE;
+	pT->pass_160_173_raw = TRUE;
+	pT->repl_translated_C0 = (BOOL) (p_out->enc == UCT_ENC_8BIT_C0);
+	pT->trans_C0_to_uni = (BOOL) (p_in->enc == UCT_ENC_8BIT_C0 ||
+				      p_out->enc == UCT_ENC_8BIT_C0);
+    } else {
+	/*
+	 * Initialize local flags.  - FM
+	 */
+	BOOL intm_ucs = FALSE;
+	BOOL use_ucs = FALSE;
+
+	/*
+	 * Set this element if we want to treat the input as CJK.  - FM
+	 */
+	pT->do_cjk = (BOOL) ((p_in->enc == UCT_ENC_CJK) && IS_CJK_TTY);
+	/*
+	 * Set these elements based on whether we are dealing with UTF-8.  - FM
+	 */
+	pT->decode_utf8 = (BOOL) (p_in->enc == UCT_ENC_UTF8);
+	pT->output_utf8 = (BOOL) (p_out->enc == UCT_ENC_UTF8);
+	if (pT->do_cjk) {
+	    /*
+	     * Set up the structure for a CJK input with
+	     * a CJK output (IS_CJK_TTY).  - FM
+	     */
+	    pT->trans_to_uni = FALSE;
+	    pT->do_8bitraw = FALSE;
+	    pT->pass_160_173_raw = TRUE;
+	    pT->use_raw_char_in = FALSE;	/* Not used for CJK. - KW */
+	    pT->repl_translated_C0 = FALSE;
+	    pT->trans_from_uni = FALSE;		/* Not used for CJK. - KW */
+	} else {
+	    /*
+	     * Set up for all other charset combinations.  The intm_ucs flag is
+	     * set TRUE if the input charset is iso-8859-1 or UTF-8, or largely
+	     * equivalent to them, i.e., if we have UCS without having to do a
+	     * table translation.
+	     */
+	    intm_ucs = (BOOL) (cs_in == LATIN1 || pT->decode_utf8 ||
+			       (p_in->codepoints &
+				(UCT_CP_SUBSETOF_LAT1 | UCT_CP_SUBSETOF_UCS2)));
+	    /*
+	     * pT->trans_to_uni is set TRUE if we do not have that as input
+	     * already, and we can translate to Unicode.  Note that UTF-8
+	     * always is converted to Unicode in functions that use the
+	     * transformation structure, so it is treated as already Unicode
+	     * here.
+	     */
+	    pT->trans_to_uni = (BOOL) (!intm_ucs &&
+				       UCCanUniTranslateFrom(cs_in));
+	    /*
+	     * We set this if we are translating to Unicode and what normally
+	     * are low value control characters in fact are encoding octets for
+	     * the input charset (presently, this applies to VISCII).  - FM
+	     */
+	    pT->trans_C0_to_uni = (BOOL) (pT->trans_to_uni &&
+					  p_in->enc == UCT_ENC_8BIT_C0);
+	    /*
+	     * We set this, presently, for VISCII.  - FM
+	     */
+	    pT->repl_translated_C0 = (BOOL) (p_out->enc == UCT_ENC_8BIT_C0);
+	    /*
+	     * Currently unused for any charset combination.
+	     * Should always be FALSE
+	     */
+	    pT->strip_raw_char_in = FALSE;
+	    /*
+	     * use_ucs should be set TRUE if we have or will create Unicode
+	     * values for input octets or UTF multibytes.  - FM
+	     */
+	    use_ucs = (BOOL) (intm_ucs || pT->trans_to_uni);
+	    /*
+	     * This is set TRUE if use_ucs was set FALSE.  It is complementary
+	     * to the HTPassEightBitRaw flag, which is set TRUE or FALSE
+	     * elsewhere based on the raw mode setting in relation to the
+	     * current Display Character Set.  - FM
+	     */
+	    pT->do_8bitraw = (BOOL) (!use_ucs);
+	    /*
+	     * This is set TRUE when 160 and 173 should not be treated as nbsp
+	     * and shy, respectively.  - FM
+	     */
+	    pT->pass_160_173_raw = (BOOL) (!use_ucs &&
+					   !(p_in->like8859 & UCT_R_8859SPECL));
+	    /*
+	     * This is set when the input and output charsets match, and they
+	     * are not ones which should go through a Unicode translation
+	     * process anyway.  - FM
+	     */
+	    pT->use_raw_char_in = (BOOL) (!pT->output_utf8 &&
+					  cs_in == cs_out &&
+					  !pT->trans_C0_to_uni);
+	    /*
+	     * This should be set TRUE when we expect to have done translation
+	     * to Unicode or had the equivalent as input, can translate it to
+	     * our output charset, and normally want to do so.  The latter
+	     * depends on the pT->do_8bitraw and pT->use_raw_char_in values set
+	     * above, but also on HTPassEightBitRaw in any functions which use
+	     * the transformation structure..  - FM
+	     */
+	    pT->trans_from_uni = (BOOL) (use_ucs && !pT->do_8bitraw &&
+					 !pT->use_raw_char_in &&
+					 UCCanTranslateUniTo(cs_out));
+	}
+    }
+}
+
+/*
+ *  This function initializes the transformation
+ *  structure by setting all its elements to
+ *  FALSE. - KW
+ */
+void UCTransParams_clear(UCTransParams * pT)
+{
+    pT->transp = FALSE;
+    pT->do_cjk = FALSE;
+    pT->decode_utf8 = FALSE;
+    pT->output_utf8 = FALSE;
+    pT->do_8bitraw = FALSE;
+    pT->use_raw_char_in = FALSE;
+    pT->strip_raw_char_in = FALSE;
+    pT->pass_160_173_raw = FALSE;
+    pT->trans_to_uni = FALSE;
+    pT->trans_C0_to_uni = FALSE;
+    pT->repl_translated_C0 = FALSE;
+    pT->trans_from_uni = FALSE;
+}
+
+/*
+ * If terminal is in UTF-8 mode, it probably cannot understand box drawing
+ * chars as the 8-bit (n)curses handles them.  (This may also be true for other
+ * display character sets, but isn't currently checked.) In that case set the
+ * chars for horizontal and vertical drawing chars to displayable ASCII chars
+ * if '0' was requested.  They'll stay as they are otherwise.  -KW, TD
+ *
+ * If we're able to obtain a character set based on the locale settings,
+ * assume that the user has setup $TERM and the fonts already so line-drawing
+ * works.
+ */
+void UCSetBoxChars(int cset,
+		   int *pvert_out,
+		   int *phori_out,
+		   int vert_in,
+		   int hori_in)
+{
+    BOOL fix_lines = FALSE;
+
+    if (cset >= 0) {
+#ifndef WIDEC_CURSES
+	if (LYCharSet_UC[cset].enc == UCT_ENC_UTF8) {
+	    fix_lines = TRUE;
+	}
+#endif
+	/*
+	 * If we've identified a charset that works, require it.
+	 * This is important if we have loaded a font, which would
+	 * confuse curses.
+	 */
+	/* US-ASCII vs Latin-1 is safe (usually) */
+	if ((cset == US_ASCII
+	     || cset == LATIN1)
+	    && (linedrawing_char_set == US_ASCII
+		|| linedrawing_char_set == LATIN1)) {
+#if (defined(FANCY_CURSES) && defined(A_ALTCHARSET)) || defined(USE_SLANG)
+	    vert_in = 0;
+	    hori_in = 0;
+#else
+	    ;
+#endif
+	}
+#ifdef EXP_CHARTRANS_AUTOSWITCH
+#if defined(NCURSES_VERSION) || defined(HAVE_TIGETSTR)
+	else {
+	    static BOOL first = TRUE;
+	    static int last_cset = -99;
+	    static BOOL last_result = TRUE;
+	    /* *INDENT-OFF* */
+	    static struct {
+		int mapping;
+		int internal;
+		int external;
+	    } table[] = {
+		{ 'j', 0x2518, 0 }, /* BOX DRAWINGS LIGHT UP AND LEFT */
+		{ 'k', 0x2510, 0 }, /* BOX DRAWINGS LIGHT DOWN AND LEFT */
+		{ 'l', 0x250c, 0 }, /* BOX DRAWINGS LIGHT DOWN AND RIGHT */
+		{ 'm', 0x2514, 0 }, /* BOX DRAWINGS LIGHT UP AND RIGHT */
+		{ 'n', 0x253c, 0 }, /* BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */
+		{ 'q', 0x2500, 0 }, /* BOX DRAWINGS LIGHT HORIZONTAL */
+		{ 't', 0x251c, 0 }, /* BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
+		{ 'u', 0x2524, 0 }, /* BOX DRAWINGS LIGHT VERTICAL AND LEFT */
+		{ 'v', 0x2534, 0 }, /* BOX DRAWINGS LIGHT UP AND HORIZONTAL */
+		{ 'w', 0x252c, 0 }, /* BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */
+		{ 'x', 0x2502, 0 }, /* BOX DRAWINGS LIGHT VERTICAL */
+	    };
+	    /* *INDENT-ON* */
+
+	    unsigned n;
+
+	    if (first) {
+		char *map = tigetstr("acsc");
+
+		if (map != 0) {
+		    CTRACE((tfp, "build terminal line-drawing map\n"));
+		    while (map[0] != 0 && map[1] != 0) {
+			for (n = 0; n < TABLESIZE(table); ++n) {
+			    if (table[n].mapping == map[0]) {
+				table[n].external = UCH(map[1]);
+				CTRACE((tfp, "  map[%c] %#x -> %#x\n",
+					table[n].mapping,
+					table[n].internal,
+					table[n].external));
+				break;
+			    }
+			}
+			map += 2;
+		    }
+		}
+		first = FALSE;
+	    }
+
+	    if (cset == last_cset) {
+		fix_lines = last_result;
+	    } else if (cset == UTF8_handle) {
+		last_result = FALSE;
+		last_cset = cset;
+	    } else {
+		CTRACE((tfp, "check terminal line-drawing map\n"));
+		for (n = 0; n < TABLESIZE(table); ++n) {
+		    int test = UCTransUniChar(table[n].internal, cset);
+
+		    if (test != table[n].external) {
+			CTRACE((tfp,
+				"line-drawing map %c mismatch (have %#x, want %#x)\n",
+				table[n].mapping,
+				test, table[n].external));
+			fix_lines = TRUE;
+			break;
+		    }
+		}
+		last_result = fix_lines;
+		last_cset = cset;
+	    }
+	}
+#else
+	else if (cset != linedrawing_char_set && linedrawing_char_set >= 0) {
+	    fix_lines = TRUE;
+	}
+#endif
+#endif
+    }
+    if (fix_lines) {
+	if (!vert_in)
+	    vert_in = '|';
+	if (!hori_in)
+	    hori_in = '-';
+    }
+    *pvert_out = vert_in;
+    *phori_out = hori_in;
+}
+
+/*
+ *  Given an output target HTStream* (can also be a HTStructured* via
+ *  typecast), the target stream's put_character method, and a Unicode
+ *  character,  CPutUtf8_charstring() will either output the UTF8
+ *  encoding of the Unicode and return YES, or do nothing and return
+ *  NO (if conversion would be unnecessary or the Unicode character is
+ *  considered invalid).
+ *
+ *  [Could be used more generally, but is currently only used for &#nnnnn
+ *  stuff - generation of UTF8 from 8-bit encoded charsets not yet done
+ *  by SGML.c etc.]
+ */
+#define PUTC(ch) ((*myPutc)(target, (char)(ch)))
+#define PUTC2(ch) ((*myPutc)(target,(char)(0x80|(0x3f &(ch)))))
+
+BOOL UCPutUtf8_charstring(HTStream *target, putc_func_t * myPutc, long code)
+{
+    if (code < 128)
+	return NO;		/* indicate to caller we didn't handle it */
+    else if (code < 0x800L) {
+	PUTC(0xc0 | (code >> 6));
+	PUTC2(code);
+    } else if (code < 0x10000L) {
+	PUTC(0xe0 | (code >> 12));
+	PUTC2(code >> 6);
+	PUTC2(code);
+    } else if (code < 0x200000L) {
+	PUTC(0xf0 | (code >> 18));
+	PUTC2(code >> 12);
+	PUTC2(code >> 6);
+	PUTC2(code);
+    } else if (code < 0x4000000L) {
+	PUTC(0xf8 | (code >> 24));
+	PUTC2(code >> 18);
+	PUTC2(code >> 12);
+	PUTC2(code >> 6);
+	PUTC2(code);
+    } else if (code <= 0x7fffffffL) {
+	PUTC(0xfc | (code >> 30));
+	PUTC2(code >> 24);
+	PUTC2(code >> 18);
+	PUTC2(code >> 12);
+	PUTC2(code >> 6);
+	PUTC2(code);
+    } else
+	return NO;
+    return YES;
+}
+
+/*
+ *  This function converts a Unicode (UCode_t) value
+ *  to a multibyte UTF-8 character, which is loaded
+ *  into the buffer received as an argument.  The
+ *  buffer should be large enough to hold at least
+ *  seven characters (but should be declared as 8
+ *  to minimize byte alignment problems with some
+ *  compilers). - FM
+ */
+BOOL UCConvertUniToUtf8(UCode_t code, char *buffer)
+{
+    char *ch = buffer;
+
+    if (!ch)
+	return NO;
+
+    if (code <= 0 || code > 0x7fffffffL) {
+	*ch = '\0';
+	return NO;
+    }
+
+    if (code < 0x800L) {
+	*ch++ = (char) (0xc0 | (code >> 6));
+	*ch++ = (char) (0x80 | (0x3f & (code)));
+	*ch = '\0';
+    } else if (code < 0x10000L) {
+	*ch++ = (char) (0xe0 | (code >> 12));
+	*ch++ = (char) (0x80 | (0x3f & (code >> 6)));
+	*ch++ = (char) (0x80 | (0x3f & (code)));
+	*ch = '\0';
+    } else if (code < 0x200000L) {
+	*ch++ = (char) (0xf0 | (code >> 18));
+	*ch++ = (char) (0x80 | (0x3f & (code >> 12)));
+	*ch++ = (char) (0x80 | (0x3f & (code >> 6)));
+	*ch++ = (char) (0x80 | (0x3f & (code)));
+	*ch = '\0';
+    } else if (code < 0x4000000L) {
+	*ch++ = (char) (0xf8 | (code >> 24));
+	*ch++ = (char) (0x80 | (0x3f & (code >> 18)));
+	*ch++ = (char) (0x80 | (0x3f & (code >> 12)));
+	*ch++ = (char) (0x80 | (0x3f & (code >> 6)));
+	*ch++ = (char) (0x80 | (0x3f & (code)));
+	*ch = '\0';
+    } else {
+	*ch++ = (char) (0xfc | (code >> 30));
+	*ch++ = (char) (0x80 | (0x3f & (code >> 24)));
+	*ch++ = (char) (0x80 | (0x3f & (code >> 18)));
+	*ch++ = (char) (0x80 | (0x3f & (code >> 12)));
+	*ch++ = (char) (0x80 | (0x3f & (code >> 6)));
+	*ch++ = (char) (0x80 | (0x3f & (code)));
+	*ch = '\0';
+    }
+    return YES;
+}
+
+/*
+ * Get UCS character code for one character from UTF-8 encoded string.
+ *
+ * On entry:
+ *	*ppuni should point to beginning of UTF-8 encoding character
+ * On exit:
+ *	*ppuni is advanced to point to the last byte of UTF-8 sequence,
+ *		if there was a valid one; otherwise unchanged.
+ * returns the UCS value
+ * returns negative value on error (invalid UTF-8 sequence)
+ */
+UCode_t UCGetUniFromUtf8String(char **ppuni)
+{
+    UCode_t uc_out = 0;
+    char *p = *ppuni;
+    int utf_count, i;
+
+    if (!(**ppuni & 0x80))
+	return (UCode_t) **ppuni;	/* ASCII range character */
+    else if (!(**ppuni & 0x40))
+	return (-1);		/* not a valid UTF-8 start */
+    if ((*p & 0xe0) == 0xc0) {
+	utf_count = 1;
+    } else if ((*p & 0xf0) == 0xe0) {
+	utf_count = 2;
+    } else if ((*p & 0xf8) == 0xf0) {
+	utf_count = 3;
+    } else if ((*p & 0xfc) == 0xf8) {
+	utf_count = 4;
+    } else if ((*p & 0xfe) == 0xfc) {
+	utf_count = 5;
+    } else {			/* garbage */
+	return (-1);
+    }
+    for (p = *ppuni, i = 0; i < utf_count; i++) {
+	if ((*(++p) & 0xc0) != 0x80)
+	    return (-1);
+    }
+    p = *ppuni;
+    switch (utf_count) {
+    case 1:
+	uc_out = (((*p & 0x1f) << 6) |
+		  (*(p + 1) & 0x3f));
+	break;
+    case 2:
+	uc_out = (((((*p & 0x0f) << 6) |
+		    (*(p + 1) & 0x3f)) << 6) |
+		  (*(p + 2) & 0x3f));
+	break;
+    case 3:
+	uc_out = (((((((*p & 0x07) << 6) |
+		      (*(p + 1) & 0x3f)) << 6) |
+		    (*(p + 2) & 0x3f)) << 6) |
+		  (*(p + 3) & 0x3f));
+	break;
+    case 4:
+	uc_out = (((((((((*p & 0x03) << 6) |
+			(*(p + 1) & 0x3f)) << 6) |
+		      (*(p + 2) & 0x3f)) << 6) |
+		    (*(p + 3) & 0x3f)) << 6) |
+		  (*(p + 4) & 0x3f));
+	break;
+    case 5:
+	uc_out = (((((((((((*p & 0x01) << 6) |
+			  (*(p + 1) & 0x3f)) << 6) |
+			(*(p + 2) & 0x3f)) << 6) |
+		      (*(p + 3) & 0x3f)) << 6) |
+		    (*(p + 4) & 0x3f)) << 6) |
+		  (*(p + 5) & 0x3f));
+	break;
+    }
+    *ppuni = p + utf_count;
+    return uc_out;
+}
diff --git a/src/UCdomap.c b/src/UCdomap.c
new file mode 100644
index 00000000..6c7bcffd
--- /dev/null
+++ b/src/UCdomap.c
@@ -0,0 +1,2484 @@
+/*
+ * $LynxId: UCdomap.c,v 1.78 2010/04/29 09:16:49 tom Exp $
+ *
+ *  UCdomap.c
+ *  =========
+ *
+ * This is a Lynx chartrans engine, its external calls are in UCMap.h
+ *
+ * Derived from code in the Linux kernel console driver.
+ * The GNU Public Licence therefore applies, see
+ * the file COPYING in the top-level directory
+ * which should come with every Lynx distribution.
+ *
+ *  [ original comment: - KW ]
+ *
+ * Mapping from internal code (such as Latin-1 or Unicode or IBM PC code)
+ * to font positions.
+ *
+ * aeb, 950210
+ */
+#include <HTUtils.h>
+#include <HTMLDTD.h>
+
+#include <LYGlobalDefs.h>
+#include <UCdomap.h>
+#include <UCMap.h>
+#include <UCAux.h>
+#include <UCDefs.h>
+#include <LYCharSets.h>
+#include <LYStrings.h>
+#include <LYUtils.h>
+
+#if defined(USE_LOCALE_CHARSET) && defined(HAVE_LANGINFO_CODESET)
+#include <langinfo.h>
+#endif
+
+#ifdef EXP_JAPANESEUTF8_SUPPORT
+#include <iconv.h>
+#endif
+
+#include <LYLeaks.h>
+
+/*
+ * Include chartrans tables:
+ */
+#include <cp1250_uni.h>		/* WinLatin2 (cp1250)   */
+#include <cp1251_uni.h>		/* WinCyrillic (cp1251) */
+#include <cp1252_uni.h>		/* WinLatin1 (cp1252)   */
+#include <cp1253_uni.h>		/* WinGreek (cp1253)    */
+#include <cp1255_uni.h>		/* WinHebrew (cp1255)   */
+#include <cp1256_uni.h>		/* WinArabic (cp1256)   */
+#include <cp1257_uni.h>		/* WinBaltRim (cp1257)  */
+#include <cp437_uni.h>		/* DosLatinUS (cp437)   */
+#include <cp737_uni.h>		/* DosGreek (cp737)     */
+#include <cp775_uni.h>		/* DosBaltRim (cp775)   */
+#include <cp850_uni.h>		/* DosLatin1 (cp850)    */
+#include <cp852_uni.h>		/* DosLatin2 (cp852)    */
+#include <cp857_uni.h>		/* DosTurkish (cp857)   */
+#include <cp862_uni.h>		/* DosHebrew (cp862)    */
+#include <cp864_uni.h>		/* DosArabic (cp864)    */
+#include <cp866_uni.h>		/* DosCyrillic (cp866)  */
+#include <cp869_uni.h>		/* DosGreek2 (cp869)    */
+#include <def7_uni.h>		/* 7 bit approximations */
+#include <dmcs_uni.h>		/* DEC Multinational    */
+#include <hp_uni.h>		/* HP Roman8            */
+#include <iso01_uni.h>		/* ISO Latin 1          */
+#include <iso02_uni.h>		/* ISO Latin 2          */
+#include <iso03_uni.h>		/* ISO Latin 3          */
+#include <iso04_uni.h>		/* ISO Latin 4          */
+#include <iso05_uni.h>		/* ISO 8859-5 Cyrillic  */
+#include <iso06_uni.h>		/* ISO 8859-6 Arabic    */
+#include <iso07_uni.h>		/* ISO 8859-7 Greek     */
+#include <iso08_uni.h>		/* ISO 8859-8 Hebrew    */
+#include <iso09_uni.h>		/* ISO 8859-9 (Latin 5) */
+#include <iso10_uni.h>		/* ISO 8859-10          */
+#include <iso13_uni.h>		/* ISO 8859-13 (Latin 7) */
+#include <iso14_uni.h>		/* ISO 8859-14 (Latin 8) */
+#include <iso15_uni.h>		/* ISO 8859-15 (Latin 9) */
+#include <koi8r_uni.h>		/* KOI8-R Cyrillic      */
+#include <mac_uni.h>		/* Macintosh (8 bit)    */
+#include <mnem2_suni.h>		/* RFC 1345 Mnemonic    */
+#include <next_uni.h>		/* NeXT character set   */
+#include <rfc_suni.h>		/* RFC 1345 w/o Intro   */
+/* #include <utf8_uni.h> *//* UNICODE UTF 8        */
+#include <viscii_uni.h>		/* Vietnamese (VISCII)  */
+#include <cp866u_uni.h>		/* Ukrainian Cyrillic (866) */
+#include <koi8u_uni.h>		/* Ukrainian Cyrillic (koi8-u */
+#include <pt154_uni.h>		/* Cyrillic-Asian (PT154) */
+
+#ifdef CAN_AUTODETECT_DISPLAY_CHARSET
+int auto_display_charset = -1;
+#endif
+
+static const char *UC_GNsetMIMEnames[4] =
+{
+    "iso-8859-1", "x-dec-graphics", "cp437", "x-transparent"
+};
+
+static int UC_GNhandles[4] =
+{
+    -1, -1, -1, -1
+};
+
+/*
+ * Some of the code below, and some of the comments, are left in for
+ * historical reasons.  Not all those tables below are currently
+ * really needed (and what with all those hardwired codepoints),
+ * but let's keep them around for now.  They may come in handy if we
+ * decide to make more extended use of the mechanisms (including e.g.
+ * for chars < 127...).  - KW
+ */
+
+static u16 translations[][256] =
+{
+    /*
+     * 8-bit Latin-1 mapped to Unicode -- trivial mapping.
+     */
+    {
+	0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+	0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+	0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+	0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
+	0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+	0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+	0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+	0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+	0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+	0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+	0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+	0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+	0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+	0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+	0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+	0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f,
+	0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
+	0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
+	0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
+	0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
+	0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
+	0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
+	0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
+	0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
+	0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
+	0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+	0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
+	0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
+	0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
+	0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+	0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
+	0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
+    },
+    /*
+     * VT100 graphics mapped to Unicode.
+     */
+    {
+	0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+	0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+	0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+	0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
+	0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+	0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+	0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+	0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+	0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+	0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+	0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+	0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x00a0,
+	0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1,
+	0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0xf800,
+	0xf801, 0x2500, 0xf803, 0xf804, 0x251c, 0x2524, 0x2534, 0x252c,
+	0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x007f,
+	0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
+	0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f,
+	0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
+	0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f,
+	0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7,
+	0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af,
+	0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
+	0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,
+	0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7,
+	0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,
+	0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7,
+	0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,
+	0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7,
+	0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,
+	0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7,
+	0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff
+    },
+    /*
+     * IBM Codepage 437 mapped to Unicode.
+     */
+    {
+	0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022,
+	0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b, 0x263c,
+	0x25ba, 0x25c4, 0x2195, 0x203c, 0x00b6, 0x00a7, 0x25ac, 0x21a8,
+	0x2191, 0x2193, 0x2192, 0x2190, 0x221f, 0x2194, 0x25b2, 0x25bc,
+	0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+	0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+	0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+	0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,
+	0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
+	0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+	0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
+	0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,
+	0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+	0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+	0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+	0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2302,
+	0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7,
+	0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5,
+	0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9,
+	0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192,
+	0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba,
+	0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb,
+	0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+	0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510,
+	0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f,
+	0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567,
+	0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b,
+	0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580,
+	0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4,
+	0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229,
+	0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
+	0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0
+    },
+    /*
+     * User mapping -- default to codes for direct font mapping.
+     */
+    {
+	0xf000, 0xf001, 0xf002, 0xf003, 0xf004, 0xf005, 0xf006, 0xf007,
+	0xf008, 0xf009, 0xf00a, 0xf00b, 0xf00c, 0xf00d, 0xf00e, 0xf00f,
+	0xf010, 0xf011, 0xf012, 0xf013, 0xf014, 0xf015, 0xf016, 0xf017,
+	0xf018, 0xf019, 0xf01a, 0xf01b, 0xf01c, 0xf01d, 0xf01e, 0xf01f,
+	0xf020, 0xf021, 0xf022, 0xf023, 0xf024, 0xf025, 0xf026, 0xf027,
+	0xf028, 0xf029, 0xf02a, 0xf02b, 0xf02c, 0xf02d, 0xf02e, 0xf02f,
+	0xf030, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, 0xf037,
+	0xf038, 0xf039, 0xf03a, 0xf03b, 0xf03c, 0xf03d, 0xf03e, 0xf03f,
+	0xf040, 0xf041, 0xf042, 0xf043, 0xf044, 0xf045, 0xf046, 0xf047,
+	0xf048, 0xf049, 0xf04a, 0xf04b, 0xf04c, 0xf04d, 0xf04e, 0xf04f,
+	0xf050, 0xf051, 0xf052, 0xf053, 0xf054, 0xf055, 0xf056, 0xf057,
+	0xf058, 0xf059, 0xf05a, 0xf05b, 0xf05c, 0xf05d, 0xf05e, 0xf05f,
+	0xf060, 0xf061, 0xf062, 0xf063, 0xf064, 0xf065, 0xf066, 0xf067,
+	0xf068, 0xf069, 0xf06a, 0xf06b, 0xf06c, 0xf06d, 0xf06e, 0xf06f,
+	0xf070, 0xf071, 0xf072, 0xf073, 0xf074, 0xf075, 0xf076, 0xf077,
+	0xf078, 0xf079, 0xf07a, 0xf07b, 0xf07c, 0xf07d, 0xf07e, 0xf07f,
+	0xf080, 0xf081, 0xf082, 0xf083, 0xf084, 0xf085, 0xf086, 0xf087,
+	0xf088, 0xf089, 0xf08a, 0xf08b, 0xf08c, 0xf08d, 0xf08e, 0xf08f,
+	0xf090, 0xf091, 0xf092, 0xf093, 0xf094, 0xf095, 0xf096, 0xf097,
+	0xf098, 0xf099, 0xf09a, 0xf09b, 0xf09c, 0xf09d, 0xf09e, 0xf09f,
+	0xf0a0, 0xf0a1, 0xf0a2, 0xf0a3, 0xf0a4, 0xf0a5, 0xf0a6, 0xf0a7,
+	0xf0a8, 0xf0a9, 0xf0aa, 0xf0ab, 0xf0ac, 0xf0ad, 0xf0ae, 0xf0af,
+	0xf0b0, 0xf0b1, 0xf0b2, 0xf0b3, 0xf0b4, 0xf0b5, 0xf0b6, 0xf0b7,
+	0xf0b8, 0xf0b9, 0xf0ba, 0xf0bb, 0xf0bc, 0xf0bd, 0xf0be, 0xf0bf,
+	0xf0c0, 0xf0c1, 0xf0c2, 0xf0c3, 0xf0c4, 0xf0c5, 0xf0c6, 0xf0c7,
+	0xf0c8, 0xf0c9, 0xf0ca, 0xf0cb, 0xf0cc, 0xf0cd, 0xf0ce, 0xf0cf,
+	0xf0d0, 0xf0d1, 0xf0d2, 0xf0d3, 0xf0d4, 0xf0d5, 0xf0d6, 0xf0d7,
+	0xf0d8, 0xf0d9, 0xf0da, 0xf0db, 0xf0dc, 0xf0dd, 0xf0de, 0xf0df,
+	0xf0e0, 0xf0e1, 0xf0e2, 0xf0e3, 0xf0e4, 0xf0e5, 0xf0e6, 0xf0e7,
+	0xf0e8, 0xf0e9, 0xf0ea, 0xf0eb, 0xf0ec, 0xf0ed, 0xf0ee, 0xf0ef,
+	0xf0f0, 0xf0f1, 0xf0f2, 0xf0f3, 0xf0f4, 0xf0f5, 0xf0f6, 0xf0f7,
+	0xf0f8, 0xf0f9, 0xf0fa, 0xf0fb, 0xf0fc, 0xf0fd, 0xf0fe, 0xf0ff
+    }
+};
+static u16 *UC_translate = NULL;
+
+static struct UC_charset UCInfo[MAXCHARSETS];
+
+/*
+ * The standard kernel character-to-font mappings are not invertible
+ * -- this is just a best effort.
+ */
+#define MAX_GLYPH 512		/* Max possible glyph value */
+
+static unsigned char *inv_translate = NULL;
+static unsigned char inv_norm_transl[MAX_GLYPH];
+static unsigned char *inverse_translations[4] =
+{NULL, NULL, NULL, NULL};
+
+static void set_inverse_transl(int i);
+static u16 *set_translate(int m);
+static int UC_valid_UC_charset(int UC_charset_hndl);
+static void UC_con_set_trans(int UC_charset_in_hndl, int Gn, int update_flag);
+static int con_insert_unipair(u16 unicode, u16 fontpos, int fordefault);
+static int con_insert_unipair_str(u16 unicode, const char *replace_str, int fordefault);
+static void con_clear_unimap(int fordefault);
+static void con_clear_unimap_str(int fordefault);
+static void con_set_default_unimap(void);
+static int UC_con_set_unimap(int UC_charset_out_hndl, int update_flag);
+static int UC_con_set_unimap_str(u16 ct, struct unipair_str *list, int fordefault);
+static int conv_uni_to_pc(long ucs, int usedefault);
+static int conv_uni_to_str(char *outbuf, int buflen, long ucs, int usedefault);
+static void UCconsole_map_init(void);
+static int UC_MapGN(int UChndl, int update_flag);
+static int UC_FindGN_byMIME(const char *UC_MIMEcharset);
+static void UCreset_allocated_LYCharSets(void);
+static const char **UC_setup_LYCharSets_repl(int UC_charset_in_hndl, unsigned lowest8);
+static int UC_Register_with_LYCharSets(int s,
+				       const char *UC_MIMEcharset,
+				       const char *UC_LYNXcharset,
+				       int lowest_eightbit);
+
+#ifdef LY_FIND_LEAKS
+static void UCfree_allocated_LYCharSets(void);
+static void UCcleanup_mem(void);
+#endif
+
+static int default_UChndl = -1;
+
+static void set_inverse_transl(int i)
+{
+    int j, glyph;
+    u16 *p = translations[i];
+    unsigned char *q = inverse_translations[i];
+
+    if (!q) {
+	/*
+	 * Slightly messy to avoid calling kmalloc too early.
+	 */
+	q = inverse_translations[i] = ((i == LAT1_MAP) ?
+				       inv_norm_transl :
+				       (unsigned char *) malloc(MAX_GLYPH));
+	if (!q)
+	    return;
+    }
+    for (j = 0; j < MAX_GLYPH; j++)
+	q[j] = 0;
+
+    for (j = 0; j < E_TABSZ; j++) {
+	glyph = conv_uni_to_pc(p[j], 0);
+	if (glyph >= 0 && glyph < MAX_GLYPH && q[glyph] < 32) {
+	    /*
+	     * Prefer '-' above SHY etc.
+	     */
+	    q[glyph] = UCH(j);
+	}
+    }
+}
+
+static u16 *set_translate(int m)
+{
+    if (!inverse_translations[m])
+	set_inverse_transl(m);
+    inv_translate = inverse_translations[m];
+    return translations[m];
+}
+
+static int UC_valid_UC_charset(int UC_charset_hndl)
+{
+    return (UC_charset_hndl >= 0 && UC_charset_hndl < UCNumCharsets);
+}
+
+static void UC_con_set_trans(int UC_charset_in_hndl,
+			     int Gn,
+			     int update_flag)
+{
+    int i, j;
+    const u16 *p;
+    u16 *ptrans;
+
+    if (!UC_valid_UC_charset(UC_charset_in_hndl)) {
+	CTRACE((tfp, "UC_con_set_trans: Invalid charset handle %d.\n",
+		UC_charset_in_hndl));
+	return;
+    }
+    ptrans = translations[Gn];
+    p = UCInfo[UC_charset_in_hndl].unitable;
+#if(0)
+    if (p == UC_current_unitable) {	/* test whether pointers are equal */
+	return;			/* nothing to be done */
+    }
+    /*
+     * The font is always 256 characters - so far.
+     */
+    con_clear_unimap();
+#endif
+    for (i = 0; i < 256; i++) {
+	if ((j = UCInfo[UC_charset_in_hndl].unicount[i])) {
+	    ptrans[i] = *p;
+	    for (; j; j--) {
+		p++;
+	    }
+	} else {
+	    ptrans[i] = 0xfffd;
+	}
+    }
+    if (update_flag) {
+	set_inverse_transl(Gn);	/* Update inverse translation for this one */
+    }
+}
+
+/*
+ * Unicode -> current font conversion
+ *
+ * A font has at most 512 chars, usually 256.
+ * But one font position may represent several Unicode chars.
+ * A hashtable is somewhat of a pain to deal with, so use a
+ * "paged table" instead.  Simulation has shown the memory cost of
+ * this 3-level paged table scheme to be comparable to a hash table.
+ */
+static int hashtable_contents_valid = 0;	/* Use ASCII-only mode for bootup */
+static int hashtable_str_contents_valid = 0;
+
+static u16 **uni_pagedir[32] =
+{
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static char ***uni_pagedir_str[32] =
+{
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const u16 *UC_current_unitable = NULL;
+static struct unimapdesc_str *UC_current_unitable_str = NULL;
+
+/*
+ * Keep a second set of structures for the translation designated
+ * as "default" - kw
+ */
+static int unidefault_contents_valid = 0;	/* Use ASCII-only mode for bootup */
+static int unidefault_str_contents_valid = 0;
+
+static u16 **unidefault_pagedir[32] =
+{
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+static char ***unidefault_pagedir_str[32] =
+{
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+
+static const u16 *UC_default_unitable = 0;
+static const struct unimapdesc_str *UC_default_unitable_str = 0;
+
+static int con_insert_unipair(u16 unicode, u16 fontpos, int fordefault)
+{
+    int i, n;
+    u16 **p1, *p2;
+
+    if (fordefault)
+	p1 = unidefault_pagedir[n = unicode >> 11];
+    else
+	p1 = uni_pagedir[n = unicode >> 11];
+    if (!p1) {
+	p1 = (u16 * *)malloc(32 * sizeof(u16 *));
+	if (fordefault)
+	    unidefault_pagedir[n] = p1;
+	else
+	    uni_pagedir[n] = p1;
+	if (!p1)
+	    return ucError;
+
+	for (i = 0; i < 32; i++) {
+	    p1[i] = NULL;
+	}
+    }
+
+    if (!(p2 = p1[n = (unicode >> 6) & 0x1f])) {
+	p2 = p1[n] = (u16 *) malloc(64 * sizeof(u16));
+	if (!p2)
+	    return ucError;
+
+	for (i = 0; i < 64; i++) {
+	    p2[i] = 0xffff;	/* No glyph for this character (yet) */
+	}
+    }
+
+    p2[unicode & 0x3f] = fontpos;
+
+    return 0;
+}
+
+static int con_insert_unipair_str(u16 unicode, const char *replace_str,
+				  int fordefault)
+{
+    int i, n;
+    char ***p1;
+    const char **p2;
+
+    if (fordefault)
+	p1 = unidefault_pagedir_str[n = unicode >> 11];
+    else
+	p1 = uni_pagedir_str[n = unicode >> 11];
+    if (!p1) {
+	p1 = (char ***) malloc(32 * sizeof(char **));
+
+	if (fordefault)
+	    unidefault_pagedir_str[n] = p1;
+	else
+	    uni_pagedir_str[n] = p1;
+	if (!p1)
+	    return ucError;
+
+	for (i = 0; i < 32; i++) {
+	    p1[i] = NULL;
+	}
+    }
+
+    n = ((unicode >> 6) & 0x1f);
+    if (!p1[n]) {
+	p1[n] = (char **) malloc(64 * sizeof(char *));
+
+	if (!p1[n])
+	    return ucError;
+
+	p2 = (const char **) p1[n];
+	for (i = 0; i < 64; i++) {
+	    p2[i] = NULL;	/* No replace string this character (yet) */
+	}
+    }
+    p2 = (const char **) p1[n];
+
+    p2[unicode & 0x3f] = replace_str;
+
+    return 0;
+}
+
+/*
+ * ui arg was a leftover, deleted.  - KW
+ */
+static void con_clear_unimap(int fordefault)
+{
+    int i, j;
+    u16 **p1;
+
+    if (fordefault) {
+	for (i = 0; i < 32; i++) {
+	    if ((p1 = unidefault_pagedir[i]) != NULL) {
+		for (j = 0; j < 32; j++) {
+		    FREE(p1[j]);
+		}
+		FREE(p1);
+	    }
+	    unidefault_pagedir[i] = NULL;
+	}
+
+	unidefault_contents_valid = 1;
+    } else {
+	for (i = 0; i < 32; i++) {
+	    if ((p1 = uni_pagedir[i]) != NULL) {
+		for (j = 0; j < 32; j++) {
+		    FREE(p1[j]);
+		}
+		FREE(p1);
+	    }
+	    uni_pagedir[i] = NULL;
+	}
+
+	hashtable_contents_valid = 1;
+    }
+}
+
+static void con_clear_unimap_str(int fordefault)
+{
+    int i, j;
+    char ***p1;
+
+    if (fordefault) {
+	for (i = 0; i < 32; i++) {
+	    if ((p1 = unidefault_pagedir_str[i]) != NULL) {
+		for (j = 0; j < 32; j++) {
+		    FREE(p1[j]);
+		}
+		FREE(p1);
+	    }
+	    unidefault_pagedir_str[i] = NULL;
+	}
+
+	unidefault_str_contents_valid = 1;	/* ??? probably no use... */
+    } else {
+	for (i = 0; i < 32; i++) {
+	    if ((p1 = uni_pagedir_str[i]) != NULL) {
+		for (j = 0; j < 32; j++) {
+		    FREE(p1[j]);
+		}
+		FREE(p1);
+	    }
+	    uni_pagedir_str[i] = NULL;
+	}
+
+	hashtable_str_contents_valid = 1;	/* ??? probably no use... */
+    }
+}
+
+/*
+ * Loads the unimap for the hardware font, as defined in uni_hash.tbl.
+ * The representation used was the most compact I could come up
+ * with.  This routine is executed at sys_setup time, and when the
+ * PIO_FONTRESET ioctl is called.
+ */
+static void con_set_default_unimap(void)
+{
+    int i, j;
+    const u16 *p;
+
+    /*
+     * The default font is always 256 characters.
+     */
+    con_clear_unimap(1);
+
+    p = dfont_unitable;
+    for (i = 0; i < 256; i++) {
+	for (j = dfont_unicount[i]; j; j--) {
+	    con_insert_unipair(*(p++), (u16) i, 1);
+	}
+    }
+
+    UC_default_unitable = dfont_unitable;
+
+    con_clear_unimap_str(1);
+    UC_con_set_unimap_str(dfont_replacedesc.entry_ct, repl_map, 1);
+    UC_default_unitable_str = &dfont_replacedesc;
+}
+
+int UCNumCharsets = 0;
+
+int UCLYhndl_HTFile_for_unspec = -1;
+int UCLYhndl_HTFile_for_unrec = -1;
+int UCLYhndl_for_unspec = -1;
+int UCLYhndl_for_unrec = -1;
+
+/* easy to type, will initialize later */
+int LATIN1 = -1;		/* UCGetLYhndl_byMIME("iso-8859-1") */
+int US_ASCII = -1;		/* UCGetLYhndl_byMIME("us-ascii")   */
+int UTF8_handle = -1;		/* UCGetLYhndl_byMIME("utf-8")      */
+int TRANSPARENT = -1;		/* UCGetLYhndl_byMIME("x-transparent")  */
+
+static int UC_con_set_unimap(int UC_charset_out_hndl,
+			     int update_flag)
+{
+    int i, j;
+    const u16 *p;
+
+    if (!UC_valid_UC_charset(UC_charset_out_hndl)) {
+	CTRACE((tfp, "UC_con_set_unimap: Invalid charset handle %d.\n",
+		UC_charset_out_hndl));
+	return ucError;
+    }
+
+    p = UCInfo[UC_charset_out_hndl].unitable;
+    if (p == UC_current_unitable) {	/* test whether pointers are equal */
+	return update_flag;	/* nothing to be done */
+    }
+    UC_current_unitable = p;
+
+    /*
+     * The font is always 256 characters - so far.
+     */
+    con_clear_unimap(0);
+
+    for (i = 0; i < 256; i++) {
+	for (j = UCInfo[UC_charset_out_hndl].unicount[i]; j; j--) {
+	    con_insert_unipair(*(p++), (u16) i, 0);
+	}
+    }
+
+    if (update_flag) {
+	for (i = 0; i <= 3; i++) {
+	    set_inverse_transl(i);	/* Update all inverse translations */
+	}
+    }
+
+    return 0;
+}
+
+static int UC_con_set_unimap_str(u16 ct, struct unipair_str *list,
+				 int fordefault)
+{
+    int err = 0, err1;
+
+    while (ct--) {
+	if ((err1 = con_insert_unipair_str(list->unicode,
+					   list->replace_str,
+					   fordefault)) != 0) {
+	    err = err1;
+	}
+	list++;
+    }
+
+    /*
+     * No inverse translations for replacement strings!
+     */
+    if (!err) {
+	if (fordefault)
+	    unidefault_str_contents_valid = 1;
+	else
+	    hashtable_str_contents_valid = 1;
+    }
+
+    return err;
+}
+
+static int conv_uni_to_pc(long ucs,
+			  int usedefault)
+{
+    int h;
+    u16 **p1, *p2;
+
+    /*
+     * Only 16-bit codes supported at this time.
+     */
+    if (ucs > 0xffff) {
+	/*
+	 * U+FFFD:  REPLACEMENT CHARACTER.
+	 */
+	ucs = 0xfffd;
+    } else if (ucs < 0x20 || ucs >= 0xfffe) {
+	/*
+	 * Not a printable character.
+	 */
+	return ucError;
+    } else if (ucs == 0xfeff || (ucs >= 0x200b && ucs <= 0x200f)) {
+	/*
+	 * Zero-width space.
+	 */
+	return ucZeroWidth;
+    } else if ((ucs & ~UNI_DIRECT_MASK) == UNI_DIRECT_BASE) {
+	/*
+	 * UNI_DIRECT_BASE indicates the start of the region in the
+	 * User Zone which always has a 1:1 mapping to the currently
+	 * loaded font.  The UNI_DIRECT_MASK indicates the bit span
+	 * of the region.
+	 */
+	return (ucs & UNI_DIRECT_MASK);
+    }
+
+    if (usedefault) {
+	if (!unidefault_contents_valid)
+	    return ucInvalidHash;
+	p1 = unidefault_pagedir[ucs >> 11];
+    } else {
+	if (!hashtable_contents_valid)
+	    return ucInvalidHash;
+	p1 = uni_pagedir[ucs >> 11];
+    }
+
+    if (p1 &&
+	(p2 = p1[(ucs >> 6) & 0x1f]) &&
+	(h = p2[ucs & 0x3f]) < MAX_GLYPH) {
+	return h;
+    }
+
+    /*
+     * Not found.
+     */
+    return ucNotFound;
+}
+
+/*
+ * Note:  contents of outbuf is not changes for negative return value!
+ */
+static int conv_uni_to_str(char *outbuf,
+			   int buflen,
+			   long ucs,
+			   int usedefault)
+{
+    char *h;
+    char ***p1, **p2;
+
+    /*
+     * Only 16-bit codes supported at this time.
+     */
+    if (ucs > 0xffff) {
+	/*
+	 * U+FFFD:  REPLACEMENT CHARACTER.
+	 */
+	ucs = 0xfffd;
+	/*
+	 * Maybe the following two cases should be allowed here??  - KW
+	 */
+    } else if (ucs < 0x20 || ucs >= 0xfffe) {
+	/*
+	 * Not a printable character.
+	 */
+	return ucError;
+    } else if (ucs == 0xfeff || (ucs >= 0x200b && ucs <= 0x200f)) {
+	/*
+	 * Zero-width space.
+	 */
+	return ucZeroWidth;
+    }
+
+    if (usedefault) {
+	if (!unidefault_str_contents_valid)
+	    return ucInvalidHash;
+	p1 = unidefault_pagedir_str[ucs >> 11];
+    } else {
+	if (!hashtable_str_contents_valid)
+	    return ucInvalidHash;
+	p1 = uni_pagedir_str[ucs >> 11];
+    }
+
+    if (p1 &&
+	(p2 = p1[(ucs >> 6) & 0x1f]) &&
+	(h = p2[ucs & 0x3f])) {
+	strncpy(outbuf, h, (size_t) (buflen - 1));
+	return 1;		/* ok ! */
+    }
+
+    /*
+     * Not found.
+     */
+    return ucNotFound;
+}
+
+int UCInitialized = 0;
+
+/*
+ * [ original comment:  - KW ]
+ * This is called at sys_setup time, after memory and the console are
+ * initialized.  It must be possible to call kmalloc(..., GFP_KERNEL)
+ * from this function, hence the call from sys_setup.
+ */
+static void UCconsole_map_init(void)
+{
+    con_set_default_unimap();
+    UCInitialized = 1;
+}
+
+/*
+ * OK now, finally, some stuff that is more specifically for Lynx:  - KW
+ */
+int UCTransUniChar(long unicode,
+		   int charset_out)
+{
+    int rc = 0;
+    int UChndl_out;
+    int isdefault, trydefault = 0;
+    const u16 *ut;
+
+    if ((UChndl_out = LYCharSet_UC[charset_out].UChndl) < 0) {
+	if (LYCharSet_UC[charset_out].codepage < 0) {
+	    if (unicode < 128) {
+		rc = (int) unicode;
+	    } else {
+		rc = LYCharSet_UC[charset_out].codepage;
+	    }
+	    return rc;
+	}
+	if ((UChndl_out = default_UChndl) < 0) {
+	    return ucCannotOutput;
+	}
+	isdefault = 1;
+    } else {
+	isdefault = UCInfo[UChndl_out].replacedesc.isdefault;
+	trydefault = UCInfo[UChndl_out].replacedesc.trydefault;
+    }
+
+    if (!isdefault) {
+	ut = UCInfo[UChndl_out].unitable;
+	if (ut != UC_current_unitable) {
+	    rc = UC_con_set_unimap(UChndl_out, 1);
+	    if (rc < 0) {
+		return rc;
+	    }
+	}
+	rc = conv_uni_to_pc(unicode, 0);
+	if (rc >= 0) {
+	    return rc;
+	}
+    }
+    if (isdefault || trydefault) {
+	rc = conv_uni_to_pc(unicode, 1);
+	if (rc >= 0) {
+	    return rc;
+	}
+    }
+    if (!isdefault && (rc == ucNotFound)) {
+	rc = conv_uni_to_pc(0xfffd, 0);
+    }
+    if ((isdefault || trydefault) && (rc == ucNotFound)) {
+	rc = conv_uni_to_pc(0xfffd, 1);
+    }
+    return rc;
+}
+
+/*
+ * Returns string length, or negative value for error.
+ */
+int UCTransUniCharStr(char *outbuf,
+		      int buflen,
+		      long unicode,
+		      int charset_out,
+		      int chk_single_flag)
+{
+    int rc = ucUnknown, src = 0;
+    int UChndl_out;
+    int isdefault, trydefault = 0;
+    struct unimapdesc_str *repl;
+    const u16 *ut;
+
+    if (buflen < 2)
+	return ucBufferTooSmall;
+
+    if ((UChndl_out = LYCharSet_UC[charset_out].UChndl) < 0) {
+	if (LYCharSet_UC[charset_out].codepage < 0)
+	    return LYCharSet_UC[charset_out].codepage;
+	if ((UChndl_out = default_UChndl) < 0)
+	    return ucCannotOutput;
+	isdefault = 1;
+    } else {
+	isdefault = UCInfo[UChndl_out].replacedesc.isdefault;
+	trydefault = UCInfo[UChndl_out].replacedesc.trydefault;
+    }
+
+    if (chk_single_flag) {
+	if (!isdefault) {
+	    ut = UCInfo[UChndl_out].unitable;
+	    if (ut != UC_current_unitable) {
+		src = UC_con_set_unimap(UChndl_out, 1);
+		if (src < 0) {
+		    return src;
+		}
+	    }
+	}
+	src = conv_uni_to_pc(unicode, isdefault);
+	if (src >= 32) {
+	    outbuf[0] = (char) src;
+	    outbuf[1] = '\0';
+	    return 1;
+	}
+    }
+
+    repl = &(UCInfo[UChndl_out].replacedesc);
+    if (!isdefault) {
+	if (repl != UC_current_unitable_str) {
+	    con_clear_unimap_str(0);
+	    (void) UC_con_set_unimap_str(repl->entry_ct, repl->entries, 0);
+	    UC_current_unitable_str = repl;
+	}
+	rc = conv_uni_to_str(outbuf, buflen, unicode, 0);
+	if (rc >= 0)
+	    return (int) strlen(outbuf);
+    }
+    if (trydefault && chk_single_flag) {
+	src = conv_uni_to_pc(unicode, 1);
+	if (src >= 32) {
+	    outbuf[0] = (char) src;
+	    outbuf[1] = '\0';
+	    return 1;
+	}
+    }
+    if (isdefault || trydefault) {
+#ifdef EXP_JAPANESEUTF8_SUPPORT
+	if (LYCharSet_UC[charset_out].codepage == 0 &&
+	    LYCharSet_UC[charset_out].codepoints == 0) {
+	    iconv_t cd;
+	    char str[3], *pin, *pout;
+	    size_t inleft, outleft;
+	    char *tocode = NULL;
+
+	    str[0] = (char) (unicode >> 8);
+	    str[1] = (char) (unicode & 0xFF);
+	    str[2] = 0;
+	    pin = str;
+	    inleft = 2;
+	    pout = outbuf;
+	    outleft = (size_t) buflen;
+	    /*
+	     * Try TRANSLIT first, since it is an extension which can provide
+	     * translations when there is no available exact translation to
+	     * the target character set.
+	     */
+	    HTSprintf0(&tocode, "%s//TRANSLIT", LYCharSet_UC[charset_out].MIMEname);
+	    cd = iconv_open(tocode, "UTF-16BE");
+	    if (cd == (iconv_t) - 1) {
+		/*
+		 * Try again, without TRANSLIT
+		 */
+		HTSprintf0(&tocode, "%s", LYCharSet_UC[charset_out].MIMEname);
+		cd = iconv_open(tocode, "UTF-16BE");
+
+		if (cd == (iconv_t) - 1) {
+		    CTRACE((tfp,
+			    "Warning: Cannot transcode form charset %s to %s!\n",
+			    "UTF-16BE", tocode));
+		}
+	    }
+	    FREE(tocode);
+
+	    if (cd != (iconv_t) - 1) {
+		rc = (int) iconv(cd, (ICONV_CONST char **) &pin, &inleft,
+				 &pout, &outleft);
+		iconv_close(cd);
+		if ((pout - outbuf) == 3) {
+		    CTRACE((tfp,
+			    "It seems to be a JIS X 0201 code(%ld). Not supported.\n", unicode));
+		    pin = str;
+		    inleft = 2;
+		    pout = outbuf;
+		    outleft = (size_t) buflen;
+		} else if (rc >= 0) {
+		    *pout = '\0';
+		    return (int) strlen(outbuf);
+		}
+	    }
+	}
+#endif
+	rc = conv_uni_to_str(outbuf, buflen, unicode, 1);
+	if (rc >= 0)
+	    return (int) strlen(outbuf);
+    }
+    if (rc == ucNotFound) {
+	if (!isdefault)
+	    rc = conv_uni_to_str(outbuf, buflen, 0xfffd, 0);
+	if ((rc == ucNotFound) && (isdefault || trydefault))
+	    rc = conv_uni_to_str(outbuf, buflen, 0xfffd, 1);
+	if (rc >= 0)
+	    return (int) strlen(outbuf);
+    }
+    if (chk_single_flag && src == ucNotFound) {
+	if (!isdefault)
+	    rc = conv_uni_to_pc(0xfffd, 0);
+	if ((rc == ucNotFound) && (isdefault || trydefault))
+	    rc = conv_uni_to_pc(0xfffd, 1);
+	if (rc >= 32) {
+	    outbuf[0] = (char) rc;
+	    outbuf[1] = '\0';
+	    return 1;
+	}
+	return rc;
+    }
+    return ucNotFound;
+}
+
+static int UC_lastautoGN = 0;
+
+static int UC_MapGN(int UChndl,
+		    int update_flag)
+{
+    int i, Gn, found, lasthndl;
+
+    found = 0;
+    Gn = -1;
+    for (i = 0; i < 4 && Gn < 0; i++) {
+	if (UC_GNhandles[i] < 0) {
+	    Gn = i;
+	} else if (UC_GNhandles[i] == UChndl) {
+	    Gn = i;
+	    found = 1;
+	}
+    }
+    if (found)
+	return Gn;
+    if (Gn >= 0) {
+	UCInfo[UChndl].GN = Gn;
+	UC_GNhandles[Gn] = UChndl;
+    } else {
+	if (UC_lastautoGN == GRAF_MAP) {
+	    Gn = IBMPC_MAP;
+	} else {
+	    Gn = GRAF_MAP;
+	}
+	UC_lastautoGN = Gn;
+	lasthndl = UC_GNhandles[Gn];
+	UCInfo[lasthndl].GN = -1;
+	UCInfo[UChndl].GN = Gn;
+	UC_GNhandles[Gn] = UChndl;
+    }
+    CTRACE((tfp, "UC_MapGN: Using %d <- %d (%s)\n",
+	    Gn, UChndl, UCInfo[UChndl].MIMEname));
+    UC_con_set_trans(UChndl, Gn, update_flag);
+    return Gn;
+}
+
+int UCTransChar(char ch_in,
+		int charset_in,
+		int charset_out)
+{
+    int unicode, Gn;
+    int rc = ucNotFound;
+    int UChndl_in, UChndl_out;
+    int isdefault, trydefault = 0;
+    const u16 *ut;
+    int upd = 0;
+
+    if (charset_in == charset_out)
+	return UCH(ch_in);
+    if (charset_in < 0)
+	return ucCannotConvert;
+    if ((UChndl_in = LYCharSet_UC[charset_in].UChndl) < 0)
+	return ucCannotConvert;
+    if ((UChndl_out = LYCharSet_UC[charset_out].UChndl) < 0) {
+	if (LYCharSet_UC[charset_out].codepage < 0)
+	    return LYCharSet_UC[charset_out].codepage;
+	if ((UChndl_out = default_UChndl) < 0)
+	    return ucCannotOutput;
+	isdefault = 1;
+    } else {
+	isdefault = UCInfo[UChndl_out].replacedesc.isdefault;
+	trydefault = UCInfo[UChndl_out].replacedesc.trydefault;
+    }
+    if (!UCInfo[UChndl_in].num_uni)
+	return ucCannotConvert;
+    if ((Gn = UCInfo[UChndl_in].GN) < 0) {
+	Gn = UC_MapGN(UChndl_in, 0);
+	upd = 1;
+    }
+
+    ut = UCInfo[UChndl_out].unitable;
+    if (!isdefault) {
+	if (ut == UC_current_unitable) {
+	    if (upd) {
+		set_inverse_transl(Gn);
+	    }
+	} else {
+	    rc = UC_con_set_unimap(UChndl_out, 1);
+	    if (rc > 0) {
+		set_inverse_transl(Gn);
+	    } else if (rc < 0) {
+		return rc;
+	    }
+	}
+    }
+    UC_translate = set_translate(Gn);
+    unicode = UC_translate[UCH(ch_in)];
+    if (!isdefault) {
+	rc = conv_uni_to_pc(unicode, 0);
+	if (rc >= 0)
+	    return rc;
+    }
+    if ((rc == ucNotFound) && (isdefault || trydefault)) {
+	rc = conv_uni_to_pc(unicode, 1);
+    }
+    if ((rc == ucNotFound) && !isdefault) {
+	rc = conv_uni_to_pc(0xfffd, 0);
+    }
+    if ((rc == ucNotFound) && (isdefault || trydefault)) {
+	rc = conv_uni_to_pc(0xfffd, 1);
+    }
+    return rc;
+}
+
+#ifdef EXP_JAPANESEUTF8_SUPPORT
+long int UCTransJPToUni(char *inbuf,
+			int buflen,
+			int charset_in)
+{
+    char outbuf[3], *pin, *pout;
+    size_t ilen, olen;
+    iconv_t cd;
+
+    pin = inbuf;
+    pout = outbuf;
+    ilen = 2;
+    olen = (size_t) buflen;
+
+    cd = iconv_open("UTF-16BE", LYCharSet_UC[charset_in].MIMEname);
+    (void) iconv(cd, (ICONV_CONST char **) &pin, &ilen, &pout, &olen);
+    iconv_close(cd);
+    if ((ilen == 0) && (olen == 0)) {
+	return (((unsigned char) outbuf[0]) << 8) + (unsigned char) outbuf[1];
+    }
+    return ucCannotConvert;
+}
+#endif
+
+/*
+ * Translate a character to Unicode.  If additional bytes are needed, this
+ * returns ucNeedMore, based on its internal state.  To reset the state,
+ * call this with charset_in < 0.
+ */
+long int UCTransToUni(char ch_in,
+		      int charset_in)
+{
+    static char buffer[10];
+    static unsigned inx = 0;
+
+    int unicode, Gn;
+    unsigned char ch_iu = UCH(ch_in);
+    int UChndl_in;
+
+    /*
+     * Reset saved-state.
+     */
+    if (charset_in < 0) {
+	inx = 0;
+	return ucCannotConvert;
+    } else if (charset_in == LATIN1) {
+	return ch_iu;
+    } else if (charset_in == UTF8_handle) {
+	if (is8bits(ch_in)) {
+	    unsigned need;
+	    char *ptr;
+
+	    buffer[inx++] = (char) ch_iu;
+	    buffer[inx] = '\0';
+	    need = utf8_length(TRUE, buffer);
+	    if (need && (need + 1) == inx) {
+		inx = 0;
+		ptr = buffer;
+		return UCGetUniFromUtf8String(&ptr);
+	    } else if (inx < sizeof(buffer) - 1) {
+		return ucNeedMore;
+	    } else {
+		inx = 0;
+	    }
+	} else {
+	    inx = 0;
+	}
+    }
+#ifdef EXP_JAPANESEUTF8_SUPPORT
+    if ((strcmp(LYCharSet_UC[charset_in].MIMEname, "shift_jis") == 0) ||
+	(strcmp(LYCharSet_UC[charset_in].MIMEname, "euc-jp") == 0)) {
+	char obuffer[3], *pin, *pout;
+	size_t ilen, olen;
+	iconv_t cd;
+
+	pin = buffer;
+	pout = obuffer;
+	ilen = olen = 2;
+	if (strcmp(LYCharSet_UC[charset_in].MIMEname, "shift_jis") == 0) {
+	    if (inx == 0) {
+		if (IS_SJIS_HI1(ch_iu) ||
+		    IS_SJIS_HI2(ch_iu)) {
+		    buffer[0] = ch_in;
+		    inx = 1;
+		    return ucNeedMore;
+		}
+	    } else {
+		if (IS_SJIS_LO(ch_iu)) {
+		    buffer[1] = ch_in;
+		    buffer[2] = 0;
+
+		    cd = iconv_open("UTF-16BE", "Shift_JIS");
+		    (void) iconv(cd, (ICONV_CONST char **) &pin, &ilen, &pout, &olen);
+		    iconv_close(cd);
+		    inx = 0;
+		    if ((ilen == 0) && (olen == 0)) {
+			return (UCH(obuffer[0]) << 8) + UCH(obuffer[1]);
+		    }
+		}
+	    }
+	}
+	if (strcmp(LYCharSet_UC[charset_in].MIMEname, "euc-jp") == 0) {
+	    if (inx == 0) {
+		if (IS_EUC_HI(ch_iu)) {
+		    buffer[0] = ch_in;
+		    inx = 1;
+		    return ucNeedMore;
+		}
+	    } else {
+		if (IS_EUC_LOX(ch_iu)) {
+		    buffer[1] = ch_in;
+		    buffer[2] = 0;
+
+		    cd = iconv_open("UTF-16BE", "EUC-JP");
+		    (void) iconv(cd, (ICONV_CONST char **) &pin, &ilen, &pout, &olen);
+		    iconv_close(cd);
+		    inx = 0;
+		    if ((ilen == 0) && (olen == 0)) {
+			return (UCH(obuffer[0]) << 8) + UCH(obuffer[1]);
+		    }
+		}
+	    }
+	}
+	inx = 0;
+    }
+#endif
+    if (ch_iu < 128 && ch_iu >= 32)
+	return ch_iu;
+
+    if (ch_iu < 32 &&
+	LYCharSet_UC[charset_in].enc != UCT_ENC_8BIT_C0) {
+	/*
+	 * Don't translate C0 chars except for specific charsets.
+	 */
+	return ch_iu;
+    } else if ((UChndl_in = LYCharSet_UC[charset_in].UChndl) < 0) {
+	return ucCannotConvert;
+    } else if (!UCInfo[UChndl_in].num_uni) {
+	return ucCannotConvert;
+    }
+
+    if ((Gn = UCInfo[UChndl_in].GN) < 0) {
+	Gn = UC_MapGN(UChndl_in, 1);
+    }
+
+    UC_translate = set_translate(Gn);
+    unicode = UC_translate[ch_iu];
+
+    return unicode;
+}
+
+int UCReverseTransChar(char ch_out,
+		       int charset_in,
+		       int charset_out)
+{
+    int Gn;
+    int rc = ucError;
+    int UChndl_in, UChndl_out;
+    int isdefault;
+    int i_ch = UCH(ch_out);
+    const u16 *ut;
+
+    if (charset_in == charset_out)
+	return UCH(ch_out);
+    if (charset_in < 0)
+	return ucCannotConvert;
+    if ((UChndl_in = LYCharSet_UC[charset_in].UChndl) < 0)
+	return ucCannotConvert;
+    if (!UCInfo[UChndl_in].num_uni)
+	return ucCannotConvert;
+    if (charset_out < 0)
+	return ucCannotOutput;
+    if ((UChndl_out = LYCharSet_UC[charset_out].UChndl) < 0) {
+	if (LYCharSet_UC[charset_out].codepage < 0)
+	    return LYCharSet_UC[charset_out].codepage;
+	if ((UChndl_out = default_UChndl) < 0)
+	    return ucCannotOutput;
+	isdefault = 1;
+    } else {
+	isdefault = UCInfo[UChndl_out].replacedesc.isdefault;
+    }
+
+    if (!isdefault) {
+	/*
+	 * Try to use the inverse table if charset_out is not equivalent
+	 * to using just the default table.  If it is, it should have
+	 * just ASCII chars and trying to back-translate those should
+	 * not give anything but themselves.  - kw
+	 */
+	ut = UCInfo[UChndl_out].unitable;
+	if (ut == UC_current_unitable) {
+	    if ((Gn = UCInfo[UChndl_in].GN) < 0) {
+		Gn = UC_MapGN(UChndl_in, 1);
+	    }
+	    UC_translate = set_translate(Gn);
+	    if (inv_translate)
+		rc = inv_translate[i_ch];
+	    if (rc >= 32) {
+		return rc;
+	    }
+	}
+    }
+    return UCTransChar(ch_out, charset_out, charset_in);
+}
+
+/*
+ * Returns string length, or negative value for error.
+ */
+int UCTransCharStr(char *outbuf,
+		   int buflen,
+		   char ch_in,
+		   int charset_in,
+		   int charset_out,
+		   int chk_single_flag)
+{
+    int unicode, Gn;
+    int rc = ucUnknown, src = 0;
+    int UChndl_in, UChndl_out;
+    int isdefault, trydefault = 0;
+    struct unimapdesc_str *repl;
+    const u16 *ut;
+    int upd = 0;
+
+    if (buflen < 2)
+	return ucBufferTooSmall;
+    if (chk_single_flag && charset_in == charset_out) {
+	outbuf[0] = ch_in;
+	outbuf[1] = '\0';
+	return 1;
+    }
+    if (charset_in < 0)
+	return ucCannotConvert;
+    if ((UChndl_in = LYCharSet_UC[charset_in].UChndl) < 0)
+	return ucCannotConvert;
+    if (!UCInfo[UChndl_in].num_uni)
+	return ucCannotConvert;
+    if ((UChndl_out = LYCharSet_UC[charset_out].UChndl) < 0) {
+	if (LYCharSet_UC[charset_out].codepage < 0)
+	    return LYCharSet_UC[charset_out].codepage;
+	if ((UChndl_out = default_UChndl) < 0)
+	    return ucCannotOutput;
+	isdefault = 1;
+    } else {
+	isdefault = UCInfo[UChndl_out].replacedesc.isdefault;
+	trydefault = UCInfo[UChndl_out].replacedesc.trydefault;
+    }
+    if ((Gn = UCInfo[UChndl_in].GN) < 0) {
+	Gn = UC_MapGN(UChndl_in, !chk_single_flag);
+	upd = chk_single_flag;
+    }
+
+    UC_translate = set_translate(Gn);
+    unicode = UC_translate[UCH(ch_in)];
+
+    if (chk_single_flag) {
+	if (!isdefault) {
+	    ut = UCInfo[UChndl_out].unitable;
+	    if (ut == UC_current_unitable) {
+		if (upd)
+		    set_inverse_transl(Gn);
+	    } else {
+		src = UC_con_set_unimap(UChndl_out, 1);
+		if (src > 0) {
+		    set_inverse_transl(Gn);
+		} else if (src < 0) {
+		    return src;
+		}
+	    }
+	}
+	src = conv_uni_to_pc(unicode, isdefault);
+	if (src >= 32) {
+	    outbuf[0] = (char) src;
+	    outbuf[1] = '\0';
+	    return 1;
+	}
+    }
+
+    repl = &(UCInfo[UChndl_out].replacedesc);
+    if (!isdefault) {
+	if (repl != UC_current_unitable_str) {
+	    con_clear_unimap_str(0);
+	    (void) UC_con_set_unimap_str(repl->entry_ct, repl->entries, 0);
+	    UC_current_unitable_str = repl;
+	}
+	rc = conv_uni_to_str(outbuf, buflen, unicode, 0);
+	if (rc >= 0)
+	    return (int) strlen(outbuf);
+    }
+    if (trydefault && chk_single_flag) {
+	src = conv_uni_to_pc(unicode, 1);
+	if (src >= 32) {
+	    outbuf[0] = (char) src;
+	    outbuf[1] = '\0';
+	    return 1;
+	}
+    }
+    if (isdefault || trydefault) {
+	rc = conv_uni_to_str(outbuf, buflen, unicode, 1);
+	if (rc >= 0)
+	    return (int) strlen(outbuf);
+    }
+    if (rc == ucNotFound) {
+	if (!isdefault)
+	    rc = conv_uni_to_str(outbuf, buflen, 0xfffd, 0);
+	if ((rc == ucNotFound) && (isdefault || trydefault))
+	    rc = conv_uni_to_str(outbuf, buflen, 0xfffd, 1);
+	if (rc >= 0)
+	    return (int) strlen(outbuf);
+    }
+    if (chk_single_flag && src == ucNotFound) {
+	if (!isdefault)
+	    rc = conv_uni_to_pc(0xfffd, 0);
+	if ((rc == ucNotFound) && (isdefault || trydefault))
+	    rc = conv_uni_to_pc(0xfffd, 1);
+	if (rc >= 32) {
+	    outbuf[0] = (char) rc;
+	    outbuf[1] = '\0';
+	    return 1;
+	} else if (rc <= 0) {
+	    outbuf[0] = '\0';
+	    return rc;
+	}
+	return rc;
+    }
+    return ucNotFound;
+}
+
+static int UC_FindGN_byMIME(const char *UC_MIMEcharset)
+{
+    int i;
+
+    for (i = 0; i < 4; i++) {
+	if (!strcmp(UC_MIMEcharset, UC_GNsetMIMEnames[i])) {
+	    return i;
+	}
+    }
+    return ucError;
+}
+
+int UCGetRawUniMode_byLYhndl(int i)
+{
+    if (i < 0)
+	return 0;
+    return LYCharSet_UC[i].enc;
+}
+
+/*
+ * Construct a new charset name, given prefix and codepage.  This introduces
+ * potentially unchecked recursion into UCGetLYhntl_byMIME if neither the "cp"
+ * nor "windows-" prefixes are configured, so we check it here.
+ */
+static int getLYhndl_byCP(const char *prefix,
+			  const char *codepage)
+{
+    static int nested;
+    int result = ucError;
+
+    if (!nested++) {
+	char *cptmp = NULL;
+
+	StrAllocCopy(cptmp, prefix);
+	StrAllocCat(cptmp, codepage);
+	result = UCGetLYhndl_byMIME(cptmp);
+	FREE(cptmp);
+    }
+    nested--;
+    return result;
+}
+
+/*
+ * Get Lynx internal charset handler from MIME name,
+ * return -1 if we got NULL or did not recognize value.
+ * According to RFC, MIME headers should match case-insensitively.
+ */
+int UCGetLYhndl_byMIME(const char *value)
+{
+    int i;
+    int LYhndl = -1;
+
+    if (!value || !(*value)) {
+	CTRACE((tfp,
+		"UCGetLYhndl_byMIME: NULL argument instead of MIME name.\n"));
+	return ucError;
+    }
+
+    for (i = 0;
+	 (i < MAXCHARSETS && i < LYNumCharsets &&
+	  LYchar_set_names[i]); i++) {
+	if (LYCharSet_UC[i].MIMEname &&
+	    !strcasecomp(value, LYCharSet_UC[i].MIMEname)) {
+	    return i;
+	}
+    }
+
+    /*
+     * Not yet found, try synonyms.  - FM
+     */
+#if !NO_CHARSET_utf_8
+    if (!strcasecomp(value, "unicode-1-1-utf-8") ||
+	!strcasecomp(value, "utf8")) {
+	/*
+	 * Treat these as synonyms for the IANA registered name.  - FM
+	 */
+	return UCGetLYhndl_byMIME("utf-8");
+    }
+#endif
+    if (!strncasecomp(value, "iso", 3) && !strncmp(value + 3, "8859", 4)) {
+	return getLYhndl_byCP("iso-", value + 3);
+    }
+#if !NO_CHARSET_euc_jp
+    if (!strcasecomp(value, "x-euc-jp") ||
+	!strcasecomp(value, "eucjp")) {
+	return UCGetLYhndl_byMIME("euc-jp");
+    }
+#endif
+#if !NO_CHARSET_shift_jis
+    if ((!strcasecomp(value, "x-shift-jis")) ||
+	(!strcasecomp(value, "x-sjis")) ||
+	(!strcasecomp(value, "pck"))) {
+	return UCGetLYhndl_byMIME("shift_jis");
+    }
+#endif
+#if !NO_CHARSET_euc_kr
+    if (!strcasecomp(value, "iso-2022-kr")) {
+	return UCGetLYhndl_byMIME("euc-kr");
+    }
+#endif
+#if !NO_CHARSET_euc_cn
+    if (!strcasecomp(value, "gb2312") ||
+	!strncasecomp(value, "cn-gb", 5) ||
+	!strcasecomp(value, "iso-2022-cn")) {
+	return UCGetLYhndl_byMIME("euc-cn");
+    }
+#endif
+#if !NO_CHARSET_big5
+    if (!strcasecomp(value, "cn-big5")) {
+	return UCGetLYhndl_byMIME("big5");
+    }
+#endif
+#if !NO_CHARSET_macintosh
+    if (!strcasecomp(value, "x-mac-roman") ||
+	!strcasecomp(value, "mac-roman")) {
+	return UCGetLYhndl_byMIME("macintosh");
+    }
+#endif
+#if !NO_CHARSET_next
+    if (!strcasecomp(value, "x-next") ||
+	!strcasecomp(value, "nextstep") ||
+	!strcasecomp(value, "x-nextstep")) {
+	return UCGetLYhndl_byMIME("next");
+    }
+#endif
+#if !NO_CHARSET_windows_1252
+    if (!strcasecomp(value, "iso-8859-1-windows-3.1-latin-1") ||
+	!strcasecomp(value, "cp1252") ||
+	!strcasecomp(value, "cp-1252") ||
+	!strcasecomp(value, "ibm1252") ||
+	!strcasecomp(value, "iso-8859-1-windows-3.0-latin-1")) {
+	/*
+	 * Treat these as synonyms for windows-1252, which is more
+	 * commonly used than the IANA registered name.  - FM
+	 */
+	return UCGetLYhndl_byMIME("windows-1252");
+    }
+#endif
+#if !NO_CHARSET_windows_1251
+    if (!strcasecomp(value, "ansi-1251")) {
+	return UCGetLYhndl_byMIME("windows-1251");
+    }
+#endif
+#if !NO_CHARSET_windows_1250
+    if (!strcasecomp(value, "iso-8859-2-windows-latin-2") ||
+	!strcasecomp(value, "cp1250") ||
+	!strcasecomp(value, "cp-1250") ||
+	!strcasecomp(value, "ibm1250")) {
+	/*
+	 * Treat these as synonyms for windows-1250.  - FM
+	 */
+	return UCGetLYhndl_byMIME("windows-1250");
+    }
+#endif
+    if ((!strncasecomp(value, "ibm", 3) ||
+	 !strncasecomp(value, "cp-", 3)) &&
+	isdigit(UCH(value[3])) &&
+	isdigit(UCH(value[4])) &&
+	isdigit(UCH(value[5]))) {
+	/*
+	 * For "ibmNNN<...>" or "cp-NNN", try "cpNNN<...>"
+	 * if not yet found.  - KW & FM
+	 */
+	if ((LYhndl = getLYhndl_byCP("cp", value + 3)) >= 0)
+	    return LYhndl;
+	/*
+	 * Try windows-NNN<...> if not yet found.  - FM
+	 */
+	return getLYhndl_byCP("windows-", value + 3);
+    }
+    if (!strncasecomp(value, "windows-", 8) &&
+	isdigit(UCH(value[8])) &&
+	isdigit(UCH(value[9])) &&
+	isdigit(UCH(value[10]))) {
+	/*
+	 * For "windows-NNN<...>", try "cpNNN<...>" - FM
+	 */
+	return getLYhndl_byCP("cp", value + 8);
+    }
+#if !NO_CHARSET_koi8_r
+    if (!strcasecomp(value, "koi-8")) {		/* accentsoft bugosity */
+	return UCGetLYhndl_byMIME("koi8-r");
+    }
+#endif
+    if (!strcasecomp(value, "ANSI_X3.4-1968")) {
+	return US_ASCII;
+    }
+    /* no more synonyms if come here... */
+
+    CTRACE((tfp, "UCGetLYhndl_byMIME: unrecognized MIME name \"%s\"\n", value));
+    return ucError;		/* returns -1 if no charset found by that MIME name */
+}
+
+/*
+ * Function UC_setup_LYCharSets_repl() tries to set up a subtable in
+ * LYCharSets[] appropriate for this new charset, for compatibility with the
+ * "old method".  Maybe not nice (maybe not even necessary any more), but it
+ * works (as far as it goes..).
+ *
+ * We try to be conservative and only allocate new memory for this if needed.
+ * If not needed, just point to SevenBitApproximations[i].  [Could do the same
+ * for ISO_Latin1[] if it's identical to that, but would make it even *more*
+ * messy than it already is...] This the only function in this file that knows,
+ * or cares, about the HTMLDTD or details of LYCharSets[] subtables (and
+ * therefore somewhat violates the idea that this file should be independent of
+ * those).  As in other places, we rely on ISO_Latin1 being the *first* table
+ * in LYCharSets.  - KW
+ */
+
+/*
+ * We need to remember which ones were allocated and which are static.
+ */
+static const char **remember_allocated_LYCharSets[MAXCHARSETS];
+
+static void UCreset_allocated_LYCharSets(void)
+{
+    int i = 0;
+
+    for (; i < MAXCHARSETS; i++) {
+	remember_allocated_LYCharSets[i] = NULL;
+    }
+}
+
+#ifdef LY_FIND_LEAKS
+static void UCfree_allocated_LYCharSets(void)
+{
+    int i = 0;
+
+    for (; i < MAXCHARSETS; i++) {
+	if (remember_allocated_LYCharSets[i] != NULL) {
+	    FREE(remember_allocated_LYCharSets[i]);
+	}
+    }
+}
+#endif
+
+static const char **UC_setup_LYCharSets_repl(int UC_charset_in_hndl,
+					     unsigned lowest8)
+{
+    const char **ISO_Latin1 = LYCharSets[0];
+    const char **p;
+    char **prepl;
+    const u16 *pp;
+    const char **tp;
+    const char *s7;
+    const char *s8;
+    size_t i;
+    int j, changed;
+    u16 k;
+    u8 *ti;
+
+    /*
+     * Create a temporary table for reverse lookup of latin1 codes:
+     */
+    tp = (const char **) malloc(96 * sizeof(char *));
+
+    if (!tp)
+	return NULL;
+    for (i = 0; i < 96; i++)
+	tp[i] = NULL;
+    ti = (u8 *) malloc(96 * sizeof(u8));
+    if (!ti) {
+	FREE(tp);
+	return NULL;
+    }
+    for (i = 0; i < 96; i++)
+	ti[i] = 0;
+
+    pp = UCInfo[UC_charset_in_hndl].unitable;
+
+    /*
+     * Determine if we have any mapping of a Unicode in the range 160-255
+     * to an allowed code point > 0x80 in our new charset...
+     * Store any mappings found in ti[].
+     */
+    if (UCInfo[UC_charset_in_hndl].num_uni > 0) {
+	for (i = 0; i < 256; i++) {
+	    if ((j = UCInfo[UC_charset_in_hndl].unicount[i])) {
+		if ((k = *pp) >= 160 && k < 256 && i >= lowest8) {
+		    ti[k - 160] = UCH(i);
+		}
+		for (; j; j--) {
+		    pp++;
+		}
+	    }
+	}
+    } {
+	u16 ct;
+	struct unipair_str *list;
+
+	/*
+	 * Determine if we have any mapping of a Unicode in the range
+	 * 160-255 to a replacement string for our new charset...
+	 * Store any mappings found in tp[].
+	 */
+	ct = UCInfo[UC_charset_in_hndl].replacedesc.entry_ct;
+	list = UCInfo[UC_charset_in_hndl].replacedesc.entries;
+	while (ct--) {
+	    if ((k = list->unicode) >= 160 && k < 256) {
+		tp[k - 160] = list->replace_str;
+	    }
+	    list++;
+	}
+    }
+    /*
+     * Now allocate a new table compatible with LYCharSets[]
+     * and with the HTMLDTD for entities.
+     * We don't know yet whether we'll keep it around.
+     */
+    prepl = (char **) malloc(HTML_dtd.number_of_entities * sizeof(char *));
+
+    if (!prepl) {
+	FREE(tp);
+	FREE(ti);
+	return 0;
+    }
+
+    p = (const char **) prepl;
+    changed = 0;
+    for (i = 0; i < HTML_dtd.number_of_entities; i++, p++) {
+	/*
+	 * For each of those entities, we check what the "old method"
+	 * ISO_Latin1[] mapping does with them.  If it is nothing we
+	 * want to use, just point to the SevenBitApproximations[] string.
+	 */
+	s7 = SevenBitApproximations[i];
+	s8 = ISO_Latin1[i];
+	*p = s7;
+	if (s8 && UCH(*s8) >= 160 && s8[1] == '\0') {
+	    /*
+	     * We have an entity that is mapped to
+	     * one valid eightbit latin1 char.
+	     */
+	    if (ti[UCH(*s8) - 160] >= UCH(lowest8) &&
+		!(s7[0] == ti[UCH(*s8) - 160] &&
+		  s7[1] == '\0')) {
+		/*
+		 * ...which in turn is mapped, by our "new method",
+		 * to another valid eightbit char for this new
+		 * charset:  either to itself...
+		 */
+		if (ti[UCH(*s8) - 160] == UCH(*s8)) {
+		    *p = s8;
+		} else {
+		    /*
+		     * make those 1-char strings
+		     * into HTAtoms, so they will be cleaned up
+		     * at exit...  all for the sake of preventing
+		     * memory leaks, sigh.
+		     */
+		    static char dummy[2];	/* one char dummy string */
+
+		    dummy[0] = (char) ti[UCH(*s8) - 160];
+		    *p = HTAtom_name(HTAtom_for(dummy));
+		}
+		changed = 1;
+	    } else if (tp[UCH(*s8) - 160] &&
+		       strcmp(s7, tp[UCH(*s8) - 160])) {
+		/*
+		 * ...or which is mapped, by our "new method",
+		 * to a replacement string for this new charset.
+		 */
+		*p = tp[UCH(*s8) - 160];
+		changed = 1;
+	    }
+	}
+    }
+    FREE(tp);
+    FREE(ti);
+    if (!changed) {
+	FREE(prepl);
+	return NULL;
+    }
+    return (const char **) prepl;
+}
+
+/*
+ * "New method" meets "Old method" ...
+ */
+static int UC_Register_with_LYCharSets(int s,
+				       const char *UC_MIMEcharset,
+				       const char *UC_LYNXcharset,
+				       int lowest_eightbit)
+{
+    int i, LYhndl, found;
+    const char **repl;
+
+    LYhndl = -1;
+    if (LYNumCharsets == 0) {
+	/*
+	 * Initialize here; so whoever changes
+	 * LYCharSets.c doesn't have to count...
+	 */
+	for (i = 0; (i < MAXCHARSETS) && LYchar_set_names[i]; i++) {
+	    LYNumCharsets = i + 1;
+	}
+    }
+
+    /*
+     * Search by MIME name, (LYchar_set_names may differ...)
+     */
+    for (i = 0; i < MAXCHARSETS && LYchar_set_names[i] && LYhndl < 0; i++) {
+	if (LYCharSet_UC[i].MIMEname &&
+	    !strcmp(UC_MIMEcharset, LYCharSet_UC[i].MIMEname)) {
+	    LYhndl = i;
+	}
+    }
+
+    if (LYhndl < 0) {		/* not found */
+	found = 0;
+	if (LYNumCharsets >= MAXCHARSETS) {
+	    CTRACE((tfp,
+		    "UC_Register_with_LYCharSets: Too many.  Ignoring %s/%s.",
+		    UC_MIMEcharset, UC_LYNXcharset));
+	    return ucError;
+	}
+	/*
+	 * Add to LYCharSets.c lists.
+	 */
+	LYhndl = LYNumCharsets;
+	LYNumCharsets++;
+	LYlowest_eightbit[LYhndl] = 999;
+	LYCharSets[LYhndl] = SevenBitApproximations;
+	/*
+	 * Hmm, try to be conservative here.
+	 */
+	LYchar_set_names[LYhndl] = UC_LYNXcharset;
+	LYchar_set_names[LYhndl + 1] = NULL;
+	/*
+	 * Terminating NULL may be looked for by Lynx code.
+	 */
+    } else {
+	found = 1;
+    }
+    LYCharSet_UC[LYhndl].UChndl = s;
+    /*
+     * Can we just copy the pointer?  Hope so...
+     */
+    LYCharSet_UC[LYhndl].MIMEname = UC_MIMEcharset;
+    LYCharSet_UC[LYhndl].enc = UCInfo[s].enc;
+    LYCharSet_UC[LYhndl].codepage = UCInfo[s].codepage;
+
+    /*
+     * @@@ We really SHOULD get more info from the table files,
+     * and set relevant flags in the LYCharSet_UC[] entry with
+     * that info...  For now, let's try it without.  - KW
+     */
+    if (lowest_eightbit < LYlowest_eightbit[LYhndl]) {
+	LYlowest_eightbit[LYhndl] = lowest_eightbit;
+    } else if (lowest_eightbit > LYlowest_eightbit[LYhndl]) {
+	UCInfo[s].lowest_eight = LYlowest_eightbit[LYhndl];
+    }
+
+    if (!found && LYhndl > 0) {
+	repl = UC_setup_LYCharSets_repl(s, (unsigned) UCInfo[s].lowest_eight);
+	if (repl) {
+	    LYCharSets[LYhndl] = repl;
+	    /*
+	     * Remember to FREE at exit.
+	     */
+	    remember_allocated_LYCharSets[LYhndl] = repl;
+	}
+    }
+    return LYhndl;
+}
+
+/*
+ * This only sets up the structure - no initialization of the tables
+ * is done here yet.
+ */
+void UC_Charset_Setup(const char *UC_MIMEcharset,
+		      const char *UC_LYNXcharset,
+		      const u8 * unicount,
+		      const u16 * unitable,
+		      int nnuni,
+		      struct unimapdesc_str replacedesc,
+		      int lowest_eight,
+		      int UC_rawuni,
+		      int codepage)
+{
+    int s, Gn;
+    int i, status = 0, found;
+
+    /*
+     * Get (new?) slot.
+     */
+    found = -1;
+    for (i = 0; i < UCNumCharsets && found < 0; i++) {
+	if (!strcmp(UCInfo[i].MIMEname, UC_MIMEcharset)) {
+	    found = i;
+	}
+    }
+    if (found >= 0) {
+	s = found;
+    } else {
+	if (UCNumCharsets >= MAXCHARSETS) {
+	    CTRACE((tfp, "UC_Charset_Setup: Too many.  Ignoring %s/%s.",
+		    UC_MIMEcharset, UC_LYNXcharset));
+	    return;
+	}
+	s = UCNumCharsets;
+	UCInfo[s].MIMEname = UC_MIMEcharset;
+    }
+    UCInfo[s].LYNXname = UC_LYNXcharset;
+    UCInfo[s].unicount = unicount;
+    UCInfo[s].unitable = unitable;
+    UCInfo[s].num_uni = nnuni;
+    UCInfo[s].replacedesc = replacedesc;
+    if (replacedesc.isdefault) {
+	default_UChndl = s;
+    }
+    Gn = UC_FindGN_byMIME(UC_MIMEcharset);
+    if (Gn >= 0)
+	UC_GNhandles[Gn] = s;
+    UCInfo[s].GN = Gn;
+    if (UC_rawuni == UCT_ENC_UTF8)
+	lowest_eight = 128;	/* cheat here */
+    UCInfo[s].lowest_eight = lowest_eight;
+    UCInfo[s].enc = UC_rawuni;
+    UCInfo[s].codepage = codepage;
+    UCInfo[s].LYhndl = UC_Register_with_LYCharSets(s,
+						   UC_MIMEcharset,
+						   UC_LYNXcharset,
+						   lowest_eight);
+    CTRACE2(TRACE_CFG, (tfp, "registered charset %d mime \"%s\" lynx \"%s\"\n",
+			s, UC_MIMEcharset, UC_LYNXcharset));
+    UCInfo[s].uc_status = status;
+    if (found < 0)
+	UCNumCharsets++;
+    return;
+}
+
+/*
+ * UC_NoUctb_Register_with_LYCharSets, UC_Charset_NoUctb_Setup -
+ * Alternative functions for adding character set info to the lists
+ * kept in LYCharSets.c.
+ *
+ * These are for character sets without any real tables of their own.
+ * We don't keep an entry in UCinfo[] for them.
+ */
+static int UC_NoUctb_Register_with_LYCharSets(const char *UC_MIMEcharset,
+					      const char *UC_LYNXcharset,
+					      int lowest_eightbit,
+					      int UC_rawuni,
+					      int codepage)
+{
+    int i, LYhndl = -1;
+
+    if (LYNumCharsets == 0) {
+	/*
+	 * Initialize here; so whoever changes
+	 * LYCharSets.c doesn't have to count...
+	 */
+	for (i = 0; (i < MAXCHARSETS) && LYchar_set_names[i]; i++) {
+	    LYNumCharsets = i + 1;
+	}
+    }
+
+    /*
+     * Search by MIME name, (LYchar_set_names may differ...)
+     * ignore if already present!
+     */
+    for (i = 0; i < MAXCHARSETS && LYchar_set_names[i] && LYhndl < 0; i++) {
+	if (LYCharSet_UC[i].MIMEname &&
+	    !strcmp(UC_MIMEcharset, LYCharSet_UC[i].MIMEname)) {
+	    return ucError;
+	}
+    }
+
+    /* not found */
+    if (LYNumCharsets >= MAXCHARSETS) {
+	CTRACE((tfp,
+		"UC_NoUctb_Register_with_LYCharSets: Too many.  Ignoring %s/%s.",
+		UC_MIMEcharset, UC_LYNXcharset));
+	return ucError;
+    }
+    /*
+     * Add to LYCharSets.c lists.
+     */
+    LYhndl = LYNumCharsets;
+    LYNumCharsets++;
+    LYlowest_eightbit[LYhndl] = lowest_eightbit;
+    LYCharSets[LYhndl] = SevenBitApproximations;
+    LYchar_set_names[LYhndl] = UC_LYNXcharset;
+    LYchar_set_names[LYhndl + 1] = NULL;
+    /*
+     * Terminating NULL may be looked for by Lynx code.
+     */
+
+    LYCharSet_UC[LYhndl].UChndl = -1;	/* no corresponding UChndl ! */
+    LYCharSet_UC[LYhndl].MIMEname = UC_MIMEcharset;
+    LYCharSet_UC[LYhndl].enc = UC_rawuni;
+    LYCharSet_UC[LYhndl].codepage = codepage;
+
+    /*
+     * @@@ We really SHOULD get more info from the table files,
+     * and set relevant flags in the LYCharSet_UC[] entry with
+     * that info...  For now, let's try it without.  - KW
+     */
+
+    return LYhndl;
+}
+
+/*
+ * A wrapper for the previous function.
+ */
+static void UC_Charset_NoUctb_Setup(const char *UC_MIMEcharset,
+				    const char *UC_LYNXcharset,
+				    int trydefault,
+				    int lowest_eight,
+				    int UC_rawuni,
+				    int codepage)
+{
+    int i;
+
+    /*
+     * Ignore completely if already in slot.
+     */
+    for (i = 0; i < UCNumCharsets; i++) {
+	if (!strcmp(UCInfo[i].MIMEname, UC_MIMEcharset)) {
+	    return;
+	}
+    }
+    if (UC_rawuni == UCT_ENC_UTF8)
+	lowest_eight = 128;	/* cheat here */
+    /* 'codepage' doubles as a flag for 'do not try any table
+     * lookup, not even default' when negative.  The value will
+     * be returned immediately by UCTrans* functions.
+     */
+    if (!trydefault && codepage == 0)
+	codepage = ucCannotOutput;	/* if not already set; any negative should do. */
+    UC_NoUctb_Register_with_LYCharSets(UC_MIMEcharset,
+				       UC_LYNXcharset,
+				       lowest_eight,
+				       UC_rawuni,
+				       codepage);
+    return;
+}
+
+#ifdef LY_FIND_LEAKS
+static void UCcleanup_mem(void)
+{
+    int i;
+
+    UCfree_allocated_LYCharSets();
+    con_clear_unimap_str(0);
+    con_clear_unimap_str(1);
+    con_clear_unimap(0);
+    con_clear_unimap(1);
+    for (i = 1; i < 4; i++) {	/* first one is static! */
+	FREE(inverse_translations[i]);
+    }
+}
+#endif /* LY_FIND_LEAKS */
+
+#ifdef EXP_CHARTRANS_AUTOSWITCH
+#ifdef CAN_AUTODETECT_DISPLAY_CHARSET
+#  ifdef __EMX__
+static int CpOrdinal(const unsigned long cp, const int other)
+{
+    char lyName[80];
+    char myMimeName[80];
+    char *mimeName, *mName = NULL, *lName = NULL;
+    int s, i, exists = 0, ret;
+
+    CTRACE((tfp, "CpOrdinal(cp=%lu, other=%d).\n", cp, other));
+    sprintf(myMimeName, "auto%s-cp%lu", (other ? "2" : ""), cp);
+    mimeName = myMimeName + 5 + (other != 0);
+    sprintf(lyName, "AutoDetect%s (cp%lu)",
+	    (other ? "-2" : ""), cp);
+    /* Find slot. */
+    s = -1;
+    for (i = 0; i < UCNumCharsets; i++) {
+	if (!strcmp(UCInfo[i].LYNXname, lyName))
+	    return UCGetLYhndl_byMIME(myMimeName);
+	else if (!strcasecomp(UCInfo[i].MIMEname, mimeName))
+	    s = i;
+    }
+    if (s < 0)
+	return ucError;
+    /* Store the "real" charset info */
+    real_charsets[other != 0] = UCGetLYhndl_byMIME(mimeName);
+    /* Duplicate the record. */
+    StrAllocCopy(mName, myMimeName);
+    StrAllocCopy(lName, lyName);
+    UC_Charset_Setup(mName, lName,
+		     UCInfo[s].unicount, UCInfo[s].unitable,
+		     UCInfo[s].num_uni, UCInfo[s].replacedesc,
+		     UCInfo[s].lowest_eight, UCInfo[s].enc,
+		     UCInfo[s].codepage);
+    ret = UCGetLYhndl_byMIME(myMimeName);
+    CTRACE((tfp, "Found %i.\n", ret));
+    return ret;
+}
+#  endif			/* __EMX__ */
+#endif /* CAN_AUTODETECT_DISPLAY_CHARSET */
+#endif /* EXP_CHARTRANS_AUTOSWITCH */
+
+void UCInit(void)
+{
+
+    UCreset_allocated_LYCharSets();
+#ifdef LY_FIND_LEAKS
+    atexit(UCcleanup_mem);
+#endif
+    UCconsole_map_init();
+
+    /*
+     * The order of charset names visible in Lynx Options menu correspond to
+     * the order of lines below, except the first two described in LYCharSet.c
+     *
+     * Entries whose comment is marked with *** are declared in UCdomap.h,
+     * others are based on the included tables - UCdomap.c, near the top.
+     */
+
+    UC_CHARSET_SETUP_iso_8859_1;	/* ISO Latin 1          */
+    UC_CHARSET_SETUP_iso_8859_15;	/* ISO 8859-15 (Latin 9) */
+    UC_CHARSET_SETUP_cp850;	/* DosLatin1 (cp850)    */
+    UC_CHARSET_SETUP_windows_1252;	/* WinLatin1 (cp1252)   */
+    UC_CHARSET_SETUP_cp437;	/* DosLatinUS (cp437)   */
+
+    UC_CHARSET_SETUP_dec_mcs;	/* DEC Multinational    */
+    UC_CHARSET_SETUP_macintosh;	/* Macintosh (8 bit)    */
+    UC_CHARSET_SETUP_next;	/* NeXT character set   */
+    UC_CHARSET_SETUP_hp_roman8;	/* HP Roman8            */
+
+    UC_CHARSET_SETUP_euc_cn;		  /*** Chinese		    */
+    UC_CHARSET_SETUP_euc_jp;		  /*** Japanese (EUC_JP)    */
+    UC_CHARSET_SETUP_shift_jis;		  /*** Japanese (Shift_JIS) */
+    UC_CHARSET_SETUP_euc_kr;		  /*** Korean		    */
+    UC_CHARSET_SETUP_big5;		  /*** Taipei (Big5)	    */
+
+    UC_CHARSET_SETUP_viscii;	/* Vietnamese (VISCII)  */
+    UC_CHARSET_SETUP;		/* us-ascii *//* 7 bit approximations */
+
+    UC_CHARSET_SETUP_x_transparent;	  /*** Transparent	  */
+
+    UC_CHARSET_SETUP_iso_8859_2;	/* ISO Latin 2          */
+    UC_CHARSET_SETUP_cp852;	/* DosLatin2 (cp852)    */
+    UC_CHARSET_SETUP_windows_1250;	/* WinLatin2 (cp1250)   */
+
+    UC_CHARSET_SETUP_iso_8859_3;	/* ISO Latin 3          */
+    UC_CHARSET_SETUP_iso_8859_4;	/* ISO Latin 4          */
+    UC_CHARSET_SETUP_iso_8859_13;	/* ISO 8859-13 Baltic Rim */
+    UC_CHARSET_SETUP_cp775;	/* DosBaltRim (cp775)   */
+    UC_CHARSET_SETUP_windows_1257;	/* WinBaltRim (cp1257)  */
+    UC_CHARSET_SETUP_iso_8859_5;	/* ISO 8859-5 Cyrillic  */
+    UC_CHARSET_SETUP_cp866;	/* DosCyrillic (cp866)  */
+    UC_CHARSET_SETUP_windows_1251;	/* WinCyrillic (cp1251) */
+    UC_CHARSET_SETUP_koi8_r;	/* KOI8-R Cyrillic      */
+    UC_CHARSET_SETUP_iso_8859_6;	/* ISO 8869-6 Arabic    */
+    UC_CHARSET_SETUP_cp864;	/* DosArabic (cp864)    */
+    UC_CHARSET_SETUP_windows_1256;	/* WinArabic (cp1256)   */
+    UC_CHARSET_SETUP_iso_8859_14;	/* ISO 8859-14 Celtic   */
+    UC_CHARSET_SETUP_iso_8859_7;	/* ISO 8859-7 Greek     */
+    UC_CHARSET_SETUP_cp737;	/* DosGreek (cp737)     */
+    UC_CHARSET_SETUP_cp869;	/* DosGreek2 (cp869)    */
+    UC_CHARSET_SETUP_windows_1253;	/* WinGreek (cp1253)    */
+    UC_CHARSET_SETUP_iso_8859_8;	/* ISO 8859-8 Hebrew    */
+    UC_CHARSET_SETUP_cp862;	/* DosHebrew (cp862)    */
+    UC_CHARSET_SETUP_windows_1255;	/* WinHebrew (cp1255)   */
+    UC_CHARSET_SETUP_iso_8859_9;	/* ISO 8859-9 (Latin 5) */
+    UC_CHARSET_SETUP_cp857;	/* DosTurkish (cp857) */
+    UC_CHARSET_SETUP_iso_8859_10;	/* ISO 8859-10 North European */
+
+    UC_CHARSET_SETUP_utf_8;		  /*** UNICODE UTF-8	  */
+    UC_CHARSET_SETUP_mnemonic_ascii_0;	/* RFC 1345 w/o Intro   */
+    UC_CHARSET_SETUP_mnemonic;	/* RFC 1345 Mnemonic    */
+    UC_CHARSET_SETUP_cp866u;	/* Ukrainian Cyrillic (866) */
+    UC_CHARSET_SETUP_koi8_u;	/* Ukrainian Cyrillic (koi8-u) */
+    UC_CHARSET_SETUP_ptcp154;	/* Cyrillic-Asian (PT154) */
+
+#ifdef EXP_CHARTRANS_AUTOSWITCH
+#ifdef CAN_AUTODETECT_DISPLAY_CHARSET
+#  ifdef __EMX__
+    {
+	unsigned long lst[3];
+	unsigned long len, rc;
+
+	rc = DosQueryCp(sizeof(lst), lst, &len);
+	if (rc == 0) {
+	    if (len >= 1)
+		auto_display_charset = CpOrdinal(lst[0], 0);
+#    ifdef CAN_SWITCH_DISPLAY_CHARSET
+	    if (len >= 3) {
+		codepages[0] = lst[0];
+		codepages[1] = (lst[0] == lst[1] ? lst[2] : lst[1]);
+		auto_other_display_charset = CpOrdinal(codepages[1], 1);
+	    }
+#    endif
+	} else {
+	    CTRACE((tfp, "DosQueryCp() returned %#lx=%lu.\n", rc, rc));
+	}
+    }
+#  endif
+#endif
+#endif
+
+/*
+ * To add synonyms for any charset name check function UCGetLYhndl_byMIME in
+ * this file.
+ */
+
+/* for coding/performance - easy to type: */
+    LATIN1 = UCGetLYhndl_byMIME("iso-8859-1");
+    US_ASCII = UCGetLYhndl_byMIME("us-ascii");
+    UTF8_handle = UCGetLYhndl_byMIME("utf-8");
+    TRANSPARENT = UCGetLYhndl_byMIME("x-transparent");
+}
+
+/*
+ * Safe variant of UCGetLYhndl_byMIME, with blind recovery from typo in user
+ * input:  lynx.cfg, userdefs.h, command line switches.
+ */
+int safeUCGetLYhndl_byMIME(const char *value)
+{
+    int i = UCGetLYhndl_byMIME(value);
+
+    if (i == -1) {		/* was user's typo or not yet recognized value */
+	i = LATIN1;		/* error recovery? */
+	CTRACE((tfp, "safeUCGetLYhndl_byMIME: ISO-8859-1 assumed.\n"));
+    }
+
+    return (i);
+}
+
+#ifdef USE_LOCALE_CHARSET
+
+#if defined(USE_LOCALE_CHARSET) && !defined(HAVE_LANGINFO_CODESET)
+/*
+ * This is a quick-and-dirty emulator of the nl_langinfo(CODESET)
+ * function defined in the Single Unix Specification for those systems
+ * (FreeBSD, etc.) that don't have one yet. It behaves as if it had
+ * been called after setlocale(LC_CTYPE, ""), that is it looks at
+ * the locale environment variables.
+ *
+ * http://www.opengroup.org/onlinepubs/7908799/xsh/langinfo.h.html
+ *
+ * Please extend it as needed and suggest improvements to the author.
+ * This emulator will hopefully become redundant soon as
+ * nl_langinfo(CODESET) becomes more widely implemented.
+ *
+ * Since the proposed Li18nux encoding name registry is still not mature,
+ * the output follows the MIME registry where possible:
+ *
+ *   http://www.iana.org/assignments/character-sets
+ *
+ * A possible autoconf test for the availability of nl_langinfo(CODESET)
+ * can be found in
+ *
+ *   http://www.cl.cam.ac.uk/~mgk25/unicode.html#activate
+ *
+ * Markus.Kuhn@cl.cam.ac.uk -- 2002-03-11
+ * Permission to use, copy, modify, and distribute this software
+ * for any purpose and without fee is hereby granted. The author
+ * disclaims all warranties with regard to this software.
+ *
+ * Latest version:
+ *
+ *   http://www.cl.cam.ac.uk/~mgk25/ucs/langinfo.c
+ */
+
+/*
+#include "langinfo.h"
+*/
+typedef int nl_item;
+
+#define CODESET 1
+
+#define C_CODESET "US-ASCII"	/* Return this as the encoding of the
+				 * C/POSIX locale. Could as well one day
+				 * become "UTF-8". */
+
+#define digit(x) ((x) >= '0' && (x) <= '9')
+
+static char buf[16];
+
+static char *nl_langinfo(nl_item item)
+{
+    char *l, *p;
+
+    if (item != CODESET)
+	return NULL;
+
+    if (((l = LYGetEnv("LC_ALL")) != 0) ||
+	((l = LYGetEnv("LC_CTYPE")) != 0) ||
+	((l = LYGetEnv("LANG")) != 0)) {
+	/* check standardized locales */
+	if (!strcmp(l, "C") || !strcmp(l, "POSIX"))
+	    return C_CODESET;
+	/* check for encoding name fragment */
+	if (strstr(l, "UTF") || strstr(l, "utf"))
+	    return "UTF-8";
+	if ((p = strstr(l, "8859-"))) {
+	    memcpy(buf, "ISO-8859-\0\0", 12);
+	    p += 5;
+	    if (digit(*p)) {
+		buf[9] = *p++;
+		if (digit(*p))
+		    buf[10] = *p++;
+		return buf;
+	    }
+	}
+	if (strstr(l, "KOI8-R"))
+	    return "KOI8-R";
+	if (strstr(l, "KOI8-U"))
+	    return "KOI8-U";
+	if (strstr(l, "620"))
+	    return "TIS-620";
+	if (strstr(l, "2312"))
+	    return "GB2312";
+	if (strstr(l, "HKSCS"))
+	    return "Big5HKSCS";	/* no MIME charset */
+	if (strstr(l, "Big5") || strstr(l, "BIG5"))
+	    return "Big5";
+	if (strstr(l, "GBK"))
+	    return "GBK";	/* no MIME charset */
+	if (strstr(l, "18030"))
+	    return "GB18030";	/* no MIME charset */
+	if (strstr(l, "Shift_JIS") || strstr(l, "SJIS"))
+	    return "Shift_JIS";
+	/* check for conclusive modifier */
+	if (strstr(l, "euro"))
+	    return "ISO-8859-15";
+	/* check for language (and perhaps country) codes */
+	if (strstr(l, "zh_TW"))
+	    return "Big5";
+	if (strstr(l, "zh_HK"))
+	    return "Big5HKSCS";	/* no MIME charset */
+	if (strstr(l, "zh"))
+	    return "GB2312";
+	if (strstr(l, "ja"))
+	    return "EUC-JP";
+	if (strstr(l, "ko"))
+	    return "EUC-KR";
+	if (strstr(l, "ru"))
+	    return "KOI8-R";
+	if (strstr(l, "uk"))
+	    return "KOI8-U";
+	if (strstr(l, "pl") || strstr(l, "hr") ||
+	    strstr(l, "hu") || strstr(l, "cs") ||
+	    strstr(l, "sk") || strstr(l, "sl"))
+	    return "ISO-8859-2";
+	if (strstr(l, "eo") || strstr(l, "mt"))
+	    return "ISO-8859-3";
+	if (strstr(l, "el"))
+	    return "ISO-8859-7";
+	if (strstr(l, "he"))
+	    return "ISO-8859-8";
+	if (strstr(l, "tr"))
+	    return "ISO-8859-9";
+	if (strstr(l, "th"))
+	    return "TIS-620";	/* or ISO-8859-11 */
+	if (strstr(l, "lt"))
+	    return "ISO-8859-13";
+	if (strstr(l, "cy"))
+	    return "ISO-8859-14";
+	if (strstr(l, "ro"))
+	    return "ISO-8859-2";	/* or ISO-8859-16 */
+	if (strstr(l, "am") || strstr(l, "vi"))
+	    return "UTF-8";
+	/* Send me further rules if you like, but don't forget that we are
+	 * *only* interested in locale naming conventions on platforms
+	 * that do not already provide an nl_langinfo(CODESET) implementation. */
+	return "ISO-8859-1";	/* should perhaps be "UTF-8" instead */
+    }
+    return C_CODESET;
+}
+#endif /* defined(USE_LOCALE_CHARSET) && !defined(HAVE_LANGINFO_CODESET) */
+
+/*
+ * If LYLocaleCharset is true, use the current locale to lookup a MIME name
+ * that corresponds, and use that as the display charset.  This feature is
+ * experimental because while nl_langinfo(CODESET) itself is standardized,
+ * the return values and their relationship to the locale value is not.
+ * GNU libiconv happens to give useful values, but other implementations are
+ * not guaranteed to do this.
+ *
+ * Not all Linux versions provide useful information.  GNU libc 2.2 returns
+ *	"ANSI_X3.4-1968"
+ * whether locale is POSIX or en_US.UTF-8.
+ *
+ * Another possible thing to investigate is the locale_charset() function
+ * provided in libiconv 1.5.1.
+ */
+void LYFindLocaleCharset(void)
+{
+    BOOL found = FALSE;
+    char *name;
+
+    CTRACE((tfp, "LYFindLocaleCharset(%d)\n", LYLocaleCharset));
+    name = nl_langinfo(CODESET);
+
+    if (name != 0) {
+	int value = UCGetLYhndl_byMIME(name);
+
+	if (value >= 0) {
+	    found = TRUE;
+	    linedrawing_char_set = value;
+	    CTRACE((tfp, "Found name \"%s\" -> %d\n", name, value));
+	} else {
+	    CTRACE((tfp, "Cannot find a handle for MIME name \"%s\"\n", name));
+	}
+    } else {
+	CTRACE((tfp, "Cannot find a MIME name for locale\n"));
+    }
+
+    if (found && LYLocaleCharset) {
+	current_char_set = linedrawing_char_set;
+    }
+}
+#endif /* USE_LOCALE_CHARSET */
diff --git a/src/UCdomap.h b/src/UCdomap.h
new file mode 100644
index 00000000..1a2f00e8
--- /dev/null
+++ b/src/UCdomap.h
@@ -0,0 +1,178 @@
+#ifndef UCDOMAP_H
+#define UCDOMAP_H
+
+#ifndef HTUTILS_H
+#include <HTUtils.h>
+#endif
+
+#ifndef ALL_CHARSETS
+#define ALL_CHARSETS 1
+#endif
+
+#include <UCkd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * [old comments:  - KW ]
+ * consolemap.h
+ *
+ * Interface between console.c, selection.c and UCmap.c
+ */
+#define LAT1_MAP 0
+#define GRAF_MAP 1
+#define IBMPC_MAP 2
+#define USER_MAP 3
+/*
+ * Some conventions I try to follow (loosely):
+ *	[a-z]* only internal, names from linux driver code.
+ *	UC_* to be only known internally.
+ *	UC[A-Z]* to be exported to other parts of Lynx. -KW
+ */ extern void UC_Charset_Setup(const char *UC_MIMEcharset,
+				 const char *UC_LYNXcharset,
+				 const u8 * unicount,
+				 const u16 * unitable,
+				 int nnuni,
+				 struct unimapdesc_str replacedesc,
+				 int lowest_eight,
+				 int UC_rawuni,
+				 int codepage);
+
+    struct UC_charset {
+	const char *MIMEname;
+	const char *LYNXname;
+	const u8 *unicount;
+	const u16 *unitable;
+	int num_uni;
+	struct unimapdesc_str replacedesc;
+	int uc_status;
+	int LYhndl;
+	int GN;
+	int lowest_eight;
+	int enc;
+	int codepage;		/* codepage number, used by OS/2 font-switching code */
+    };
+
+    extern int UCNumCharsets;
+    extern int UCInitialized;
+
+    extern void UCInit(void);
+
+/*
+ * INSTRUCTIONS for adding new character sets which do not have Unicode tables.
+ *
+ * Several #defines below are declarations for charsets which need no tables
+ * for mapping to Unicode - CJK multibytes, x-transparent, UTF8 - Lynx takes
+ * care of them internally.
+ *
+ * The declaration's format is kept in chrtrans/XXX_uni.h - keep this in mind
+ * when changing ucmaketbl.c, see also UC_Charset_Setup() above for details.
+ */
+
+    /*
+     * There is no strict correlation for the next five, since the transfer
+     * charset gets decoded into Display Char Set by the CJK code (separate from
+     * Unicode mechanism).  For now we use the MIME name that describes what is
+     * output to the terminal.  - KW
+     */
+
+/*----------------------------------------------------------------------------*/
+
+#ifndef NO_CHARSET_euc_cn
+#define NO_CHARSET_euc_cn !ALL_CHARSETS
+#endif
+
+#if NO_CHARSET_euc_cn
+#define UC_CHARSET_SETUP_euc_cn	/* nothing */
+#else
+#define UC_CHARSET_SETUP_euc_cn UC_Charset_NoUctb_Setup("euc-cn","Chinese",\
+       1, 128,UCT_ENC_CJK,0)
+#endif
+
+/*----------------------------------------------------------------------------*/
+
+#ifndef NO_CHARSET_euc_jp
+#define NO_CHARSET_euc_jp !ALL_CHARSETS
+#endif
+
+#if NO_CHARSET_euc_jp
+#define UC_CHARSET_SETUP_euc_jp	/* nothing */
+#else
+#define UC_CHARSET_SETUP_euc_jp UC_Charset_NoUctb_Setup("euc-jp","Japanese (EUC-JP)",\
+       1, 128,UCT_ENC_CJK,0)
+#endif
+
+/*----------------------------------------------------------------------------*/
+
+#ifndef NO_CHARSET_shift_jis
+#define NO_CHARSET_shift_jis !ALL_CHARSETS
+#endif
+
+#if NO_CHARSET_shift_jis
+#define UC_CHARSET_SETUP_shift_jis	/* nothing */
+#else
+#define UC_CHARSET_SETUP_shift_jis UC_Charset_NoUctb_Setup("shift_jis","Japanese (Shift_JIS)",\
+       1, 128,UCT_ENC_CJK,0)
+#endif
+
+/*----------------------------------------------------------------------------*/
+
+#ifndef NO_CHARSET_euc_kr
+#define NO_CHARSET_euc_kr !ALL_CHARSETS
+#endif
+
+#if NO_CHARSET_euc_kr
+#define UC_CHARSET_SETUP_euc_kr	/* nothing */
+#else
+#define UC_CHARSET_SETUP_euc_kr UC_Charset_NoUctb_Setup("euc-kr","Korean",\
+       1, 128,UCT_ENC_CJK,0)
+#endif
+
+/*----------------------------------------------------------------------------*/
+
+#ifndef NO_CHARSET_big5
+#define NO_CHARSET_big5 !ALL_CHARSETS
+#endif
+
+#if NO_CHARSET_big5
+#define UC_CHARSET_SETUP_big5	/* nothing */
+#else
+#define UC_CHARSET_SETUP_big5 UC_Charset_NoUctb_Setup("big5","Taipei (Big5)",\
+       1, 128,UCT_ENC_CJK,0)
+#endif
+
+/*----------------------------------------------------------------------------*/
+
+    /*
+     * Placeholder for non-translation mode.  - FM
+     */
+
+#ifndef NO_CHARSET_x_transparent
+#define NO_CHARSET_x_transparent !ALL_CHARSETS
+#endif
+
+#if NO_CHARSET_x_transparent
+#define UC_CHARSET_SETUP_x_transparent	/* nothing */
+#else
+#define UC_CHARSET_SETUP_x_transparent UC_Charset_NoUctb_Setup("x-transparent","Transparent",\
+       0, 128,UCT_ENC_8BIT,0)
+#endif
+
+/*----------------------------------------------------------------------------*/
+
+#ifndef NO_CHARSET_utf_8
+#define NO_CHARSET_utf_8 !ALL_CHARSETS
+#endif
+
+#if NO_CHARSET_utf_8
+#define UC_CHARSET_SETUP_utf_8	/* nothing */
+#else
+#define UC_CHARSET_SETUP_utf_8 UC_Charset_NoUctb_Setup("utf-8","UNICODE (UTF-8)",\
+       0, 128,UCT_ENC_UTF8,-4)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* UCDOMAP_H */
diff --git a/src/Xsystem.c b/src/Xsystem.c
new file mode 100644
index 00000000..fa3e6b96
--- /dev/null
+++ b/src/Xsystem.c
@@ -0,0 +1,584 @@
+/* $LynxId: Xsystem.c,v 1.23 2009/03/10 15:21:42 tom Exp $
+ *	like system("cmd") but return with exit code of "cmd"
+ *	for Turbo-C/MS-C/LSI-C
+ *  This code is in the public domain.
+ *
+ * @Log: xsystem.c,v @
+ *
+ * Revision 1.14  1997/10/17 (Fri) 16:28:24  senshu
+ * *** for Win32 version ***
+ *
+ * Revision 1.13  1992/02/24  06:59:13  serow
+ * *** empty log message ***
+ *
+ * Revision 1.12  1991/04/09  08:48:20  serow
+ * ignore new line at command line tail
+ *
+ * Revision 1.11  1991/03/12  07:12:50  serow
+ * CMDLINE
+ *
+ * Revision 1.10  91/02/24  05:10:14  serow
+ * 2>&1
+ *
+ * Revision 1.9  91/02/22  07:01:17  serow
+ * NEAR for ms-c
+ *
+ */
+#include <LYUtils.h>
+#include <LYStrings.h>
+#include <LYGlobalDefs.h>
+
+#ifdef DOSPATH
+#include <io.h>
+#else
+extern char *mktemp(char *);
+#endif
+
+#ifndef USECMDLINE
+#define USECMDLINE	0
+#endif
+
+#ifndef TRUE
+#define TRUE	1
+#define FALSE	0
+#endif
+
+#define	TABLESIZE(v)	(sizeof(v)/sizeof(v[0]))
+
+#define STR_MAX 512		/* MAX command line */
+
+#define isk1(c)  ((0x81 <= UCH(c) && UCH(c) <= 0x9F) || (0xE0 <= UCH(c) && UCH(c) <= 0xFC))
+#define isq(c)   ((c) == '"')
+#define isspc(c) ((c) == ' ' || (c) == '\t')
+#define issep(c) (isspc(c) || (c) == '"' || (c) == '\'' || (c) == '<' || (c) == '>' || (c) == 0)
+#define issep2(c) (issep(c) || (c) == '.' || (c) == '\\' || (c) == '/')
+#define isdeg(c) ('0' <= (c) && (c) <= '9')
+
+#ifndef NEAR
+#if 0				/* MS-C */
+#define NEAR	_near
+#else
+#define NEAR
+#endif
+#endif
+
+typedef struct _proc {
+    struct _proc *next;
+    char *line;
+    char *cmd;
+    char *arg;
+    char *inf;
+    int infmod;
+    char *outf;
+    int outfmod;
+    int ored[10];
+    int sred[10];
+} PRO;
+
+static PRO *p1 = 0;
+
+static char *NEAR xmalloc(size_t n)
+{
+    char *bp;
+
+    if ((bp = typecallocn(char, n)) == 0) {
+	write(2, "xsystem: Out of memory.!\n", 25);
+	exit_immediately(EXIT_FAILURE);
+    }
+    return bp;
+}
+
+static char *NEAR xrealloc(void *p, size_t n)
+{
+    char *bp;
+
+    if ((bp = realloc(p, n)) == (char *) 0) {
+	write(2, "xsystem: Out of memory!.\n", 25);
+	exit_immediately(EXIT_FAILURE);
+    }
+    return bp;
+}
+
+static int NEAR is_builtin_command(char *s)
+{
+    static char *cmdtab[] =
+    {
+	"dir", "type", "rem", "ren", "rename", "erase", "del",
+	"copy", "pause", "date", "time", "ver", "vol", "label",
+	"cd", "chdir", "md", "mkdir", "rd", "rmdir", "break",
+	"verify", "set", "prompt", "path", "exit", "ctty", "echo",
+	"if", "for", "cls", "goto", "shift"
+	,"start"		/* start is NT only */
+    };
+    int i, l, lc, count;
+
+    l = strlen(s);
+    count = TABLESIZE(cmdtab);
+    count--;
+#ifdef WIN_EX
+    if (system_is_NT)
+	count++;
+#endif
+    for (i = 0; i < count; i++) {
+	if (strcasecomp(s, cmdtab[i]) == 0)
+	    return 1;
+	lc = strlen(cmdtab[i]);
+	if (lc < l && strnicmp(s, cmdtab[i], lc) == 0 && issep2(s[lc]))
+	    return 1;
+    }
+    return 0;
+}
+
+static int NEAR getswchar(void)
+{
+    int result;
+
+#ifdef __WIN32__
+    result = '/';
+#else
+    union REGS reg;
+
+    reg.x.ax = 0x3700;
+    intdos(&reg, &reg);
+    result = reg.h.dl;
+#endif
+    return result;
+}
+
+static int NEAR csystem(PRO * p, int flag)
+{
+    char *cmp;
+    char SW[3];
+    int rc;
+
+    if ((cmp = LYGetEnv("COMSPEC")) == 0)
+	return -2;
+    SW[0] = (char) getswchar();
+    SW[1] = 'c';
+    SW[2] = 0;
+    rc = spawnl(flag, cmp, cmp, SW, p->cmd, p->arg, (char *) 0);
+    return rc < 0 ? -2 : rc;
+}
+
+static PRO *NEAR pars1c(char *s)
+{
+    PRO *pp;
+    char *fnp;
+    int ms, mi;
+    int fs, fi, inpf;
+    int q;
+
+    pp = (PRO *) xmalloc(sizeof(PRO));
+    for (q = 0; q < (int) TABLESIZE(pp->ored); q++)
+	pp->ored[q] = q;
+    while (isspc(*s))
+	s++;
+    pp->line = strdup(s);
+    pp->cmd = xmalloc(ms = 8);
+    mi = 0;
+    while (!issep(*s)) {
+	if (mi >= ms - 1)
+	    pp->cmd = xrealloc(pp->cmd, ms += 8);
+	pp->cmd[mi++] = *s++;
+    }
+    pp->cmd[mi] = 0;
+    q = 0;
+    pp->arg = xmalloc(ms = 32);
+    if (isspc(*s))
+	s++;
+    mi = 0;
+    while (*s) {
+	if (mi >= ms - 1) {
+	    pp->arg = xrealloc(pp->arg, ms += 32);
+	}
+	if (q == 0) {
+	    inpf = 0;
+	    if ((mi == 0 || isspc(s[-1])) &&
+		isdeg(s[0]) && s[1] == '>' &&
+		s[2] == '&' && isdeg(s[3])) {
+
+		pp->ored[s[0] & 15] = s[3] & 15;
+		s += 4;
+		continue;
+	    } else if (s[0] == '<') {
+		if (pp->inf == 0) {
+		    pp->infmod = O_RDONLY;
+		}
+		inpf = 1;
+	    } else if (s[0] == '>' && s[1] == '>') {
+		if (pp->outf == 0) {
+		    pp->outfmod = O_WRONLY | O_CREAT | O_APPEND;
+		}
+		s++;
+	    } else if (s[0] == '>') {
+		if (pp->outf == 0) {
+		    pp->outfmod = O_WRONLY | O_CREAT | O_TRUNC;
+		}
+	    } else {
+		if (*s == '"')
+		    q = !q;
+		pp->arg[mi++] = *s++;
+		continue;
+	    }
+	    fnp = xmalloc(fs = 16);
+	    fi = 0;
+	    s++;
+	    while (isspc(*s))
+		s++;
+	    while (!issep(*s)) {
+		if (fi >= fs - 1)
+		    fnp = xrealloc(fnp, fs += 16);
+		fnp[fi++] = *s++;
+	    }
+	    fnp[fi] = 0;
+	    if (inpf) {
+		if (pp->inf == 0)
+		    pp->inf = fnp;
+	    } else {
+		if (pp->outf == 0)
+		    pp->outf = fnp;
+	    }
+	} else if (s[0] == '"') {
+	    q = !q;
+	    pp->arg[mi++] = *s++;
+	} else {
+	    pp->arg[mi++] = *s++;
+	}
+    }
+    pp->arg[mi] = 0;
+    return pp;
+}
+
+static PRO *NEAR pars(char *s)
+{
+    char *lb;
+    int li, ls, q;
+    int c;
+    PRO *pp = 0;
+
+    lb = xmalloc(ls = STR_MAX);	/* about */
+    li = q = 0;
+    p1 = 0;
+
+    for (;;) {
+	c = *s++;
+	if (li >= ls - 2)
+	    lb = xrealloc(lb, ls += STR_MAX);
+	if (isk1(c) && *s) {
+	    lb[li++] = (char) c;
+	    lb[li++] = *s++;
+	} else if ((!q && c == '|') || c == 0 || (c == '\n' && *s == 0)) {
+	    lb[li++] = 0;
+	    if (p1 == 0) {
+		pp = p1 = pars1c(lb);
+	    } else {
+		pp->next = pars1c(lb);
+		pp = pp->next;
+	    }
+	    li = 0;
+	    if (c == 0 || (c == '\n' && *s == 0))
+		break;
+	} else if (c == '"') {
+	    q = !q;
+	    lb[li++] = (char) c;
+	} else {
+	    lb[li++] = (char) c;
+	}
+    }
+    free(lb);
+    return p1;
+}
+
+static int NEAR try3(char *cnm, PRO * p, int flag)
+{
+    char cmdb[STR_MAX];
+    int rc;
+
+    sprintf(cmdb, "%.*s.com", sizeof(cmdb) - 5, cnm);
+    if ((rc = open(cmdb, O_RDONLY)) >= 0) {
+	close(rc);
+	return spawnl(flag, cmdb, cmdb, p->arg, (char *) 0);
+    }
+    sprintf(cmdb, "%.*s.exe", sizeof(cmdb) - 5, cnm);
+    if ((rc = open(cmdb, O_RDONLY)) >= 0) {
+	close(rc);
+	return spawnl(flag, cmdb, cmdb, p->arg, (char *) 0);
+    }
+    sprintf(cmdb, "%.*s.bat", sizeof(cmdb) - 5, cnm);
+    if ((rc = open(cmdb, O_RDONLY)) >= 0) {
+	close(rc);
+	return csystem(p, flag);
+    }
+    return -1;
+}
+
+static int NEAR prog_go(PRO * p, int flag)
+{
+    char *s;
+    char *extp = 0;
+    char cmdb[STR_MAX];
+    char *ep;
+    int rc, lc = 0, cmd_len;
+
+    cmd_len = strlen(p->cmd);
+
+    s = p->cmd + cmd_len - 1;
+    while (cmd_len && (*s != '\\') && (*s != '/') && (*s != ':')) {
+	if (*s == '.')
+	    extp = s;
+	cmd_len--;
+	s--;
+    }
+
+    if (is_builtin_command(p->cmd) || (extp && strcasecomp(extp, ".bat") == 0))
+	return csystem(p, flag);
+
+    if (s < p->cmd) {		/* cmd has no PATH nor Drive */
+	ep = LYGetEnv("PATH");
+	LYstrncpy(cmdb, p->cmd, sizeof(cmdb) - 1);
+	for (;;) {
+	    if (extp) {		/* has extension */
+		if ((rc = open(cmdb, O_RDONLY)) >= 0) {
+		    close(rc);
+		    rc = spawnl(flag, cmdb, cmdb, p->arg, (char *) 0);
+		}
+	    } else {
+		rc = try3(cmdb, p, flag);
+	    }
+	    if (rc >= 0)
+		return rc;
+
+	    if (ep && *ep) {
+		int i;
+
+		for (i = 0; *ep != ';' && *ep != '\0'; ep++, i++)
+		    lc = cmdb[i] = *ep;
+		if (*ep == ';')
+		    ep++;
+		if (i > 0 && lc != ':' && lc != '\\' && lc != '/')
+		    cmdb[i++] = '\\';
+		cmdb[i] = 0;
+		LYstrncpy(cmdb + i, p->cmd, sizeof(cmdb) - 1 - i);
+	    } else {
+		if (rc == -2)
+		    return rc;
+		return -1;
+	    }
+	}
+    } else {			/* has PATH or Drive */
+	if (extp) {		/* has extension */
+	    if ((rc = open(p->cmd, O_RDONLY)) >= 0) {
+		close(rc);
+		return spawnl(flag, p->cmd, p->cmd, p->arg, (char *) 0);
+	    }
+	    return -1;
+	} else {
+	    return try3(p->cmd, p, flag);
+	}
+    }
+}
+
+static char *NEAR tmpf(char *tp)
+{
+    char tplate[STR_MAX];
+    char *ev;
+    int i;
+
+    if ((ev = LYGetEnv("TMP")) != 0) {
+	LYstrncpy(tplate, ev, sizeof(tplate) - 2 - strlen(tp));
+	i = strlen(ev);
+	if (i && ev[i - 1] != '\\' && ev[i - 1] != '/')
+	    strcat(tplate, "\\");
+    } else {
+	tplate[0] = 0;
+    }
+    strcat(tplate, tp);
+    return strdup(mktemp(tplate));
+}
+
+static int NEAR redopen(char *fn, int md, int sfd)
+{
+    int rc;
+    int fd;
+
+    if ((fd = open(fn, md, 0666)) != -1) {
+	if (md & O_APPEND)
+	    lseek(fd, 0L, SEEK_END);
+	rc = dup(sfd);
+	if (fd != sfd) {
+	    dup2(fd, sfd);
+	    close(fd);
+	}
+	return rc;
+    }
+    return -1;
+}
+
+static int NEAR redclose(int fd, int sfd)
+{
+    if (fd != -1) {
+	dup2(fd, sfd);
+	close(fd);
+    }
+    return -1;
+}
+
+static void NEAR redswitch(PRO * p)
+{
+    int d;
+
+    for (d = 0; d < (int) TABLESIZE(p->ored); d++) {
+	if (d != p->ored[d]) {
+	    p->sred[d] = dup(d);
+	    dup2(p->ored[d], d);
+	}
+    }
+}
+
+static void NEAR redunswitch(PRO * p)
+{
+    int d;
+
+    for (d = 0; d < (int) TABLESIZE(p->ored); d++) {
+	if (d != p->ored[d]) {
+	    dup2(p->sred[d], d);
+	    close(p->sred[d]);
+	}
+    }
+}
+
+int xsystem(char *cmd)
+{
+    PRO *p, *pn;
+    char *pof, *pif, *pxf;
+    int psstdin, psstdout;
+    int rdstdin, rdstdout;
+    int rc = 0;
+
+#if USECMDLINE
+    static char *cmdline = 0;
+#endif
+
+#ifdef SH_EX			/* 1997/11/01 (Sat) 10:04:03 add by JH7AYN */
+    pif = cmd;
+    while (*pif++) {
+	if (*pif == '\r') {
+	    *pif = '\0';
+	    break;
+	} else if (*pif == '\n') {
+	    *pif = '\0';
+	    break;
+	}
+    }
+#endif
+
+    pof = pif = pxf = 0;
+    p = pars(cmd);
+    pof = tmpf("p1XXXXXX");
+    pif = tmpf("p2XXXXXX");
+    psstdin = psstdout = rdstdin = rdstdout = -1;
+    while (p) {
+#if USECMDLINE
+	if (!LYGetEnv("NOCMDLINE")) {
+	    cmdline = xmalloc(strlen(p->cmd) + strlen(p->arg) + 10);
+	    sprintf(cmdline, "CMDLINE=%s %s", p->cmd, p->arg);
+	    putenv(cmdline);
+	}
+#endif
+	if (p->next)
+	    psstdout = redopen(pof, O_WRONLY | O_CREAT | O_TRUNC, 1);
+	if (p->inf)
+	    rdstdin = redopen(p->inf, p->infmod, 0);
+	if (p->outf)
+	    rdstdout = redopen(p->outf, p->outfmod, 1);
+	redswitch(p);
+	rc = prog_go(p, P_WAIT);
+	redunswitch(p);
+	rdstdin = redclose(rdstdin, 0);
+	rdstdout = redclose(rdstdout, 1);
+	psstdout = redclose(psstdout, 1);
+	psstdin = redclose(psstdin, 0);
+	if ((p = p->next) != 0) {
+	    pxf = pif;
+	    pif = pof;
+	    pof = pxf;
+	    psstdin = redopen(pif, O_RDONLY, 0);
+	}
+    }
+    unlink(pif);
+    free(pif);
+    unlink(pof);
+    free(pof);
+    for (pn = p = p1; p; p = pn) {
+	pn = p->next;
+	if (p->line)
+	    free(p->line);
+	if (p->cmd)
+	    free(p->cmd);
+	if (p->arg)
+	    free(p->arg);
+	if (p->inf)
+	    free(p->inf);
+	if (p->outf)
+	    free(p->outf);
+	free(p);
+    }
+    if (rc == -2)
+	return 127;
+    return rc < 0 ? 0xFF00 : rc;
+}
+
+int exec_command(char *cmd, int wait_flag GCC_UNUSED)
+{
+    int rc;
+
+#if defined(__MINGW32__)
+    rc = system(cmd);
+#else
+    PRO *p;
+    char *pif;
+    int cmd_str;
+
+    pif = cmd;
+    while (*pif == ' ')
+	pif++;
+
+    cmd = pif;
+    cmd_str = TRUE;
+
+    while (*pif++) {
+	if (*pif == '\r') {
+	    *pif = '\0';
+	    break;
+	} else if (*pif == '\n') {
+	    *pif = '\0';
+	    break;
+	} else if (cmd_str) {
+	    if (*pif == '/')
+		*pif = '\\';
+	} else if (cmd_str) {
+	    if (*pif == ' ')
+		cmd_str = FALSE;
+	}
+    }
+    p = pars(cmd);
+
+    if (wait_flag)
+	rc = prog_go(p, P_WAIT);
+    else
+	rc = prog_go(p, P_NOWAIT);
+
+#endif
+    return rc;
+}
+
+#ifdef TEST
+void main()
+{
+    char line_buff[STR_MAX];
+
+    while (gets(line_buff)) {
+	printf("\nreturn %04X\n", xsystem(line_buff));
+    }
+}
+#endif /* TEST */
diff --git a/src/chrtrans/README.format b/src/chrtrans/README.format
new file mode 100644
index 00000000..7437b503
--- /dev/null
+++ b/src/chrtrans/README.format
@@ -0,0 +1,138 @@
+Some notes on the format of table files used here.
+(See README.tables for what to do with them.)
+
+The format is derived from stuff in the console driver of the
+Linux kernel (as are the guts of the chartrans machinery).
+THAT DOES NOT MEAN that anything here is Linux specific - it isn't.
+
+[Note that the format may change, this is still somewhat experimental.]
+
+There are four kinds of lines:
+
+Summary example:
+
+  # This line is a comment, the next line is a directive
+  O Brand new Charset!
+  0x41    U+0041 U+0391
+  U+00cd:I'
+
+Description:
+
+a) comment lines start with a '#' character.
+   (trailing comments are allowed on some of the other lines, if in doubt
+   check the examples..)
+
+b) directives:
+   start with a keyword which may be abbreviated to one letter (first
+   letter must be capitalized), followed by space and a value.
+   Currently recognized:
+
+    OptionName
+	The name under which this should appear on the O)ptions screen
+	in the list for Display Character Set
+    MIMEName
+	The name for this charset in MIME syntax (one word with digits
+	and some other non-letters allowed, should be IANA registered)
+    Default
+	If "Y[es]" or "1", this is the default (fallback) translation table,
+	it will be used for Unicode -> 8bit (or 7bit) translation if no
+	translation is found in the specific table.
+    FallBack
+	Whether to use the default table if no translation is found in
+	this table.  Normally fallback is used, "FallBack NO" or "FallBack 0"
+	disables it (actually, other values than "FallBack Y[es]" or
+	"FallBack 1" disable it).
+
+    RawOrEnc
+	a number which flags some special property (encoding) for this
+	charset [see utf8_uni.tbl for example, see UCDefs.h for details].
+
+    Codepage number (IBM specific)
+	used by OS/2 font-switching code.
+
+c) character translation definitions:
+   they look like
+
+   0x41    U+0041 U+0391 ...
+
+   and are used for "forward" translation (mapping this charset to Unicode)
+   AS WELL AS "back" translation (mapping Unicodes to an 8-bit
+   [incl. 7-bit ASCII] code).
+
+   For the "forward" direction, only the first Unicode is used; for
+   "back" translation, all listed Unicodes are mapped to the byte (i.e.
+   code point) on the left.
+
+   The above example line would tell the chartrans mechanism:
+   "For this charset, code position 65 [hex 0x41] contains Unicode
+    U+0041 (LATIN CAPITAL LETTER A).  For translation of Unicodes to
+    this charset, use byte value 65 [hex 0x41] for U+0041 (LATIN CAPITAL
+    LETTER A) as well as for U+0391 (GREEK CAPITAL LETTER ALPHA)."
+
+  [Note that for bytes in the ASCII range 0x00-0x7F, the forward translations
+   will (probably) not be used by Lynx.  It doesn't hurt to list those,
+   too, for completeness.]
+
+   Some other forms are also accepted:
+
+ * Syntax accepted:
+ *	<fontpos>	<unicode> <unicode> ...
+ *	<fontpos>	<unicode range> <unicode range> ...
+ *	<fontpos>	idem
+ *	<range>		idem
+ *	<range>		<unicode range>
+ *
+ * where <unicode range> ::= <unicode>-<unicode>
+ * and <unicode> ::= U+<h><h><h><h>
+ * and <h> ::= <hexadecimal digit>
+ *
+  [Note that <fontpos> _without_ targets assumed notdefined,
+  so tables from ftp.unicode.org need no patching.]
+
+
+d) string replacement definitions:
+
+  They look like
+
+  U+00cd:I'
+
+  which would mean "Replace Unicode U+00cd (LATIN CAPITAL LETTER I WITH
+  ACUTE" with the string (consisting of two character) I' (if no other
+  translation is available)."  Please note that replacement definitions
+  in certain charset table will override ones from the Default table.
+
+  Note that everything after the ':' is currently taken VERBATIM, so
+  careful with trailing blanks etc.  Please use <C replace> syntax below
+  when you need trailing spaces.
+
+ * Syntax accepted:
+ *      <unicode>	:<replace>
+ *      <unicode range>	:<replace>
+ *      <unicode>	"<C replace>"
+ *      <unicode range>	"<C replace>"
+ *
+ * where <unicode range> ::= <unicode>-<unicode>
+ * and <unicode> ::= U+<h><h><h><h>
+ * and <h> ::= <hexadecimal digit>
+ * and <replace> any string not containing '\n' or '\0', taken verbatim
+ * and <C replace> any string, with backslash having the usual C meaning.
+
+Motivation:
+
+- It is an extension of the format already in use for Linux (kernel,
+  kbd package), those files can be used with some minimal editing.
+
+- It is easy to convert Unicode tables for other charsets, as they
+  are commonly found on ftp sites etc., to this format - the right
+  sed command should do 99% of the work.
+
+- The format is independent of details of other parts of the Lynx code,
+  unlike the "old" LYCharsets.c mechanism.  The tables don't have to
+  be changed in synch when e.g., new entities are added to the entities.h.
+
+
+Note: the Default "7bit approximation" table can be used for
+case-insensitive search for non-ascii letters if no upper/lower case
+information provided by other means, e.g., locale.  It is assumed that
+upper/lower case letters have their "7bit approximation" images
+in def7_uni.tbl matched case-insensitively.
diff --git a/src/chrtrans/README.tables b/src/chrtrans/README.tables
new file mode 100644
index 00000000..14431dad
--- /dev/null
+++ b/src/chrtrans/README.tables
@@ -0,0 +1,76 @@
+The translation table files in this directory were collected from
+several sources (among them ftp://ftp.unicode.org, Linux kbd package,
+ftp://dkuug.dk/) and are believed to be correct in their mappings,
+but not checked in detail.  The Unicode/UCS2 values
+for some of the RFC 1345 Mnemonic codes are out of date,
+a cleanup and update would be needed for serious use.
+[See also http://czyborra.com/charsets/iso8859.html for codepages survey.]
+
+These changes were made to all of the files used from ftp.unicode.org:
+
+	a) add the MIME name of the charset.
+	b) add a name for the display charset (used on Options screen)
+	c) add the codepage number
+	d) remove lines for control characters 0x00 to 0x1f, 0x7f to 0x9f.
+	e) comment-out ASCII lines 0x20 to 0x7f
+	f) use idem to represent the commented-out lines
+	g) change C-style 0xNNNN constants to Unicode-style U+NNNN.
+
+Other changes include
+
+	h) add code-points to several lines to provide Unicode equivalents
+	i) add extra mappings at the end of the files
+	j) comment-out other one-one mappings in the 0xa0-0xff range.
+
+More translation files can be easily provided (and new character entities
+added to entities.h), this set is just to test whether the system works
+in principle (and also how it behaves with incomplete data...)
+
+See the file README.format for a brief explanation of what's in the
+table files.
+
+The examples have names *_uni or *_suni with a .tbl suffix, but it
+doesn't really matter.  The auxiliary program makeuctb (MAKE UniCode
+TaBle) is used to "compile" them into C header files, which can be
+included by UCdomap.c.
+
+Ideally, this should be taken care of by the Makefiles.  On VMS, use
+build-chrtrans.com to compile and link makeuctb.exe and create the
+set of .h files from the current set of .tbl files.  Thereafter, use
+build-header.com to update particular .h files.
+
+To make a new chartrans table available to Lynx (and thereby make a new
+charset known to Lynx) you currently have to manually edit UCdomap.c, in
+two places:
+
+a) Near the top, you will find a bunch of lines (some may be commented out)
+
+  #include "<fn>.h"
+
+Add or comment out as you wish.  But it is probably safest to leave the
+commonly used ones, referring to "def7_uni.h" and "iso01_uni.h", in place.
+
+b) At the bottom, you will find a bunch of lines (again, some may be
+   commented out by default) of the form
+
+    UC_CHARSET_SETUP_<something>;
+
+which should correspond to the #include lines from a).  Again,
+add or subtract as you wish (but preferably consistent with what you
+did under a)...) [The <something> is derived from the charset's MIME name.
+if in doubt, check the last lines of the corresponding ...uni.h file.]
+
+c) To let make automatically notice when you have changed one of the
+   table files, and automatically regenerate the *uni.h file(s),
+you also have to add any new tables to both src/Makefile *and*
+src/chrtrans/Makefile.  Or, for auto-config, the equivalent files
+named makefile.in before running ./configure, or makefile after running
+./configure.  (That may be inconvenient, but I didn't want to depend
+on features than not all makes may have.)  Note that for recompiling
+Lynx, a `make clean' should not be necessary if you have *only* made
+changes to the files in src/chrtrans.  On VMS, add entries for new
+tables to build-chrtrans.com, but you can update the particular file
+with build-header.com, then use the top directory's build.com and
+answer 'n' to it's prompts about whether to update the WWWlibrary
+and chrtrans modules.
+
diff --git a/src/chrtrans/UCkd.h b/src/chrtrans/UCkd.h
new file mode 100644
index 00000000..f2de902f
--- /dev/null
+++ b/src/chrtrans/UCkd.h
@@ -0,0 +1,54 @@
+#ifndef _UC_KD_H
+#define _UC_KD_H
+
+/*
+ *  NOTE: THE FOLLOWING #define MAY NEED ADJUSTMENT.
+ *  u16 should be an unsigned type of 16 bit length (two octets).
+ *  u8  should be an unsigned type of 8  bit length (one octet).
+ */
+#ifndef u16
+#define u16 unsigned short
+#endif /* u16 */
+
+#ifndef u8
+#define u8 unsigned char
+#endif /* u8 */
+
+typedef char scrnmap_t;
+
+#define		E_TABSZ		256
+
+struct unipair {
+    u16 unicode;
+    u16 fontpos;
+};
+struct unipair_str {
+    u16 unicode;
+    const char *replace_str;
+};
+struct unimapdesc {
+    u16 entry_ct;
+    struct unipair *entries;
+};
+struct unimapdesc_str {
+    u16 entry_ct;
+    struct unipair_str *entries;
+    int isdefault;
+    int trydefault;
+};
+
+#define UNI_DIRECT_BASE 0xF000	/* start of Direct Font Region */
+#define UNI_DIRECT_MASK 0x01FF	/* Direct Font Region bitmask */
+
+#define UC_MAXLEN_ID_APPEND 20
+#define UC_MAXLEN_MIMECSNAME 40
+#define UC_MAXLEN_LYNXCSNAME 40
+#define UC_LEN_LYNXCSNAME 20
+
+#undef  EX_OK			/* may be defined in system headers */
+#define EX_OK		0	/* successful termination */
+#define EX_USAGE	64	/* command line usage error */
+#define EX_DATAERR	65	/* data format error */
+#define EX_NOINPUT	66	/* cannot open input */
+
+#endif /* _UC_KD_H */
diff --git a/src/chrtrans/build-chrtrans.com b/src/chrtrans/build-chrtrans.com
new file mode 100644
index 00000000..c4f88f59
--- /dev/null
+++ b/src/chrtrans/build-chrtrans.com
@@ -0,0 +1,141 @@
+$ v0 = 0
+$ v = f$verify(v0)
+$!			BUILD-CHRTRANS.COM
+$!
+$!   Command file to build MAKEUCTB.EXE on VMS systems
+$!   and then use it to create the chrtrans header files.
+$!
+$!   28-Jun-1997	F.Macrides		macrides@sci.wfeb.edu
+$!	Initial version, for Lynx v2.7.1+fotemods
+$!
+$ ON CONTROL_Y THEN GOTO CLEANUP
+$ ON ERROR THEN GOTO CLEANUP
+$ CHRproc = f$environment("PROCEDURE")
+$ CHRwhere = f$parse(CHRproc,,,"DEVICE") + f$parse(CHRproc,,,"DIRECTORY")
+$!
+$ if p1 .nes. ""
+$   then
+$      CHRcc_opts = "/DEBUG/NOOPT"
+$      CHRlink_opts = "/DEBUG"
+$   else
+$      CHRcc_opts = ""
+$      CHRlink_opts = ""
+$ endif
+$!
+$ Compile_makeuctb:
+$!================
+$ v1 = f$verify(1)
+$!
+$!	Compile the Lynx [.SRC.CHRTRANS]makeuctb module.
+$!
+$  v1 = f$verify(v0)
+$ IF f$getsyi("ARCH_NAME") .eqs. "Alpha" .or. -
+     f$getsyi("ARCH_NAME") .eqs. "IA64" .or. -
+     f$trnlnm("VAXCMSG") .eqs. "DECC$MSG" .or. -
+     f$trnlnm("DECC$CC_DEFAULT") .eqs. "/DECC" .or. -
+     f$trnlnm("DECC$CC_DEFAULT") .eqs. "/VAXC"
+$ THEN
+$  CHRcompiler := "DECC"
+$  v1 = f$verify(1)
+$! DECC:
+$  cc := cc/decc/prefix=all /nomember 'CHRcc_opts'-
+	   /INCLUDE=([],[-],[--],[--.WWW.Library.Implementation]) 
+$  v1 = f$verify(v0)
+$ ELSE
+$  IF f$search("gnu_cc:[000000]gcclib.olb") .nes. ""
+$  THEN
+$   CHRcompiler := "GNUC"
+$   v1 = f$verify(1)
+$! GNUC:
+$   cc := gcc 'CHRcc_opts'/INCLUDE=([],[-],[--],[--.WWW.Library.Implementation]) 
+$   v1 = f$verify(v0)
+$  ELSE
+$   CHRcompiler := "VAXC"
+$   v1 = f$verify(1)
+$! VAXC:
+$   cc := cc 'CHRcc_opts'/INCLUDE=([],[-],[--],[--.WWW.Library.Implementation]) 
+$   v1 = f$verify(v0)
+$  ENDIF
+$ ENDIF
+$!
+$ v1 = f$verify(1)
+$ cc makeuctb
+$ v1 = f$verify(v0)
+$!
+$ Link_makeuctb:
+$!=============
+$ v1 = f$verify(1)
+$!
+$!	Link the Lynx [.SRC.CHRTRANS]makeuctb module.
+$!
+$ IF f$getsyi("ARCH_NAME") .eqs. "IA64"
+$ THEN
+$    optslibs=""
+$ ELSE
+$    optslibs=", sys$disk:[-]''CHRcompiler'.opt/opt"
+$ ENDIF
+$
+$ link/exe=makeuctb.exe'CHRlink_opts' makeuctb 'optslibs
+$ v1 = f$verify(v0)
+$!
+$ Create_headers:
+$!==============
+$ v1 = f$verify(1)
+$!
+$!	Create the Lynx [.SRC.CHRTRANS] header files.
+$!
+$ makeuctb := $'CHRwhere'makeuctb
+$ makeuctb cp1250_uni.tbl
+$ makeuctb cp1251_uni.tbl
+$ makeuctb cp1252_uni.tbl
+$ makeuctb cp1253_uni.tbl
+$ makeuctb cp1255_uni.tbl
+$ makeuctb cp1256_uni.tbl
+$ makeuctb cp1257_uni.tbl
+$ makeuctb cp437_uni.tbl
+$ makeuctb cp737_uni.tbl
+$ makeuctb cp775_uni.tbl
+$ makeuctb cp850_uni.tbl
+$ makeuctb cp852_uni.tbl
+$ makeuctb cp857_uni.tbl
+$ makeuctb cp862_uni.tbl
+$ makeuctb cp864_uni.tbl
+$ makeuctb cp866_uni.tbl
+$ makeuctb cp866u_uni.tbl
+$ makeuctb cp869_uni.tbl
+$ makeuctb def7_uni.tbl
+$ makeuctb dmcs_uni.tbl
+$ makeuctb hp_uni.tbl
+$ makeuctb iso01_uni.tbl
+$ makeuctb iso02_uni.tbl
+$ makeuctb iso03_uni.tbl
+$ makeuctb iso04_uni.tbl
+$ makeuctb iso05_uni.tbl
+$ makeuctb iso06_uni.tbl
+$ makeuctb iso07_uni.tbl
+$ makeuctb iso08_uni.tbl
+$ makeuctb iso09_uni.tbl
+$ makeuctb iso10_uni.tbl
+$ makeuctb iso13_uni.tbl
+$ makeuctb iso14_uni.tbl
+$ makeuctb iso15_uni.tbl
+$ makeuctb koi8r_uni.tbl
+$ makeuctb koi8u_uni.tbl
+$ makeuctb mac_uni.tbl
+$ makeuctb mnem_suni.tbl
+$ makeuctb mnem2_suni.tbl
+$ makeuctb mnem_suni.tbl
+$ makeuctb next_uni.tbl
+$ makeuctb pt154_uni.tbl
+$ makeuctb rfc_suni.tbl
+$ makeuctb utf8_uni.tbl
+$ makeuctb viscii_uni.tbl
+$ v1 = f$verify(v0)
+$ exit
+$!
+$ CLEANUP:
+$    v1 = f$verify(0)
+$    write sys$output "Default directory:"
+$    show default
+$    v1 = f$verify(v)
+$ exit
diff --git a/src/chrtrans/build-header.com b/src/chrtrans/build-header.com
new file mode 100644
index 00000000..ff8a6f52
--- /dev/null
+++ b/src/chrtrans/build-header.com
@@ -0,0 +1,37 @@
+$ v0 = 0
+$ v = f$verify(v0)
+$!			BUILD-HEADER.COM
+$!
+$!   Command file to use MAKEUCTB.EXE on VMS systems for creating
+$!   a chrtrans header (foo.h) file from a table (foo.tbl) file.
+$!   Use the file root as P1, e.g.:
+$!
+$!	$ @build-header iso05_uni
+$!
+$!   will create iso05_uni.h from iso05_uni.tbl.
+$!
+$!   28-Jun-1997	F.Macrides		macrides@sci.wfeb.edu
+$!	Initial version, for Lynx v2.7.1+fotemods
+$!
+$ ON CONTROL_Y THEN GOTO CLEANUP
+$ ON ERROR THEN GOTO CLEANUP
+$ CHRproc = f$environment("PROCEDURE")
+$ CHRwhere = f$parse(CHRproc,,,"DEVICE") + f$parse(CHRproc,,,"DIRECTORY")
+$!
+$ Create_header:
+$!=============
+$ v1 = f$verify(1)
+$!
+$!	Create a Lynx [.SRC.CHRTRANS] header file.
+$!
+$ makeuctb := $'CHRwhere'makeuctb
+$ makeuctb 'P1'.tbl
+$ v1 = f$verify(v0)
+$ exit
+$!
+$ CLEANUP:
+$    v1 = f$verify(v0)
+$    write sys$output "Default directory:"
+$    show default
+$    v1 = f$verify(v)
+$ exit
diff --git a/src/chrtrans/caselower.h b/src/chrtrans/caselower.h
new file mode 100644
index 00000000..5894a3f2
--- /dev/null
+++ b/src/chrtrans/caselower.h
@@ -0,0 +1,738 @@
+/*
+  Lynx uses this info for 8bit case-insensitive user search.
+
+  This table is generated from Unicode Character Database, Version 2.1.5
+  available from ftp.unicode.org, and looks as natural way to get case mapping
+  equivalents for unicodes.  (well, too much characters the cost of 3 Kb only). 
+  Few words from the original README.txt quoted:
+
+UNICODE 2.1 CHARACTER DATABASE
+
+Copyright (c) 1991-1998 Unicode, Inc.
+All Rights reserved.
+
+CASE MAPPINGS
+
+The case mapping is an informative, default mapping. Certain languages, such
+as Turkish, German, French, or Greek may have small deviations from the
+default mappings listed in the Unicode Character Database.
+
+ */
+
+#include <UCkd.h>		/* typedef u16 */
+
+typedef struct {
+    u16 upper;
+    u16 lower;
+} unipair_case;
+
+static const unipair_case unicode_to_lower_case[] =
+/* *INDENT-OFF* */
+{
+  {0x0041, 0x0061},  /* LATIN CAPITAL LETTER A */
+  {0x0042, 0x0062},  /* LATIN CAPITAL LETTER B */
+  {0x0043, 0x0063},  /* LATIN CAPITAL LETTER C */
+  {0x0044, 0x0064},  /* LATIN CAPITAL LETTER D */
+  {0x0045, 0x0065},  /* LATIN CAPITAL LETTER E */
+  {0x0046, 0x0066},  /* LATIN CAPITAL LETTER F */
+  {0x0047, 0x0067},  /* LATIN CAPITAL LETTER G */
+  {0x0048, 0x0068},  /* LATIN CAPITAL LETTER H */
+  {0x0049, 0x0069},  /* LATIN CAPITAL LETTER I */
+  {0x004A, 0x006A},  /* LATIN CAPITAL LETTER J */
+  {0x004B, 0x006B},  /* LATIN CAPITAL LETTER K */
+  {0x004C, 0x006C},  /* LATIN CAPITAL LETTER L */
+  {0x004D, 0x006D},  /* LATIN CAPITAL LETTER M */
+  {0x004E, 0x006E},  /* LATIN CAPITAL LETTER N */
+  {0x004F, 0x006F},  /* LATIN CAPITAL LETTER O */
+  {0x0050, 0x0070},  /* LATIN CAPITAL LETTER P */
+  {0x0051, 0x0071},  /* LATIN CAPITAL LETTER Q */
+  {0x0052, 0x0072},  /* LATIN CAPITAL LETTER R */
+  {0x0053, 0x0073},  /* LATIN CAPITAL LETTER S */
+  {0x0054, 0x0074},  /* LATIN CAPITAL LETTER T */
+  {0x0055, 0x0075},  /* LATIN CAPITAL LETTER U */
+  {0x0056, 0x0076},  /* LATIN CAPITAL LETTER V */
+  {0x0057, 0x0077},  /* LATIN CAPITAL LETTER W */
+  {0x0058, 0x0078},  /* LATIN CAPITAL LETTER X */
+  {0x0059, 0x0079},  /* LATIN CAPITAL LETTER Y */
+  {0x005A, 0x007A},  /* LATIN CAPITAL LETTER Z */
+  {0x00C0, 0x00E0},  /* LATIN CAPITAL LETTER A WITH GRAVE */
+  {0x00C1, 0x00E1},  /* LATIN CAPITAL LETTER A WITH ACUTE */
+  {0x00C2, 0x00E2},  /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX */
+  {0x00C3, 0x00E3},  /* LATIN CAPITAL LETTER A WITH TILDE */
+  {0x00C4, 0x00E4},  /* LATIN CAPITAL LETTER A WITH DIAERESIS */
+  {0x00C5, 0x00E5},  /* LATIN CAPITAL LETTER A WITH RING ABOVE */
+  {0x00C6, 0x00E6},  /* LATIN CAPITAL LETTER AE */
+  {0x00C7, 0x00E7},  /* LATIN CAPITAL LETTER C WITH CEDILLA */
+  {0x00C8, 0x00E8},  /* LATIN CAPITAL LETTER E WITH GRAVE */
+  {0x00C9, 0x00E9},  /* LATIN CAPITAL LETTER E WITH ACUTE */
+  {0x00CA, 0x00EA},  /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX */
+  {0x00CB, 0x00EB},  /* LATIN CAPITAL LETTER E WITH DIAERESIS */
+  {0x00CC, 0x00EC},  /* LATIN CAPITAL LETTER I WITH GRAVE */
+  {0x00CD, 0x00ED},  /* LATIN CAPITAL LETTER I WITH ACUTE */
+  {0x00CE, 0x00EE},  /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX */
+  {0x00CF, 0x00EF},  /* LATIN CAPITAL LETTER I WITH DIAERESIS */
+  {0x00D0, 0x00F0},  /* LATIN CAPITAL LETTER ETH */
+  {0x00D1, 0x00F1},  /* LATIN CAPITAL LETTER N WITH TILDE */
+  {0x00D2, 0x00F2},  /* LATIN CAPITAL LETTER O WITH GRAVE */
+  {0x00D3, 0x00F3},  /* LATIN CAPITAL LETTER O WITH ACUTE */
+  {0x00D4, 0x00F4},  /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX */
+  {0x00D5, 0x00F5},  /* LATIN CAPITAL LETTER O WITH TILDE */
+  {0x00D6, 0x00F6},  /* LATIN CAPITAL LETTER O WITH DIAERESIS */
+  {0x00D8, 0x00F8},  /* LATIN CAPITAL LETTER O WITH STROKE */
+  {0x00D9, 0x00F9},  /* LATIN CAPITAL LETTER U WITH GRAVE */
+  {0x00DA, 0x00FA},  /* LATIN CAPITAL LETTER U WITH ACUTE */
+  {0x00DB, 0x00FB},  /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX */
+  {0x00DC, 0x00FC},  /* LATIN CAPITAL LETTER U WITH DIAERESIS */
+  {0x00DD, 0x00FD},  /* LATIN CAPITAL LETTER Y WITH ACUTE */
+  {0x00DE, 0x00FE},  /* LATIN CAPITAL LETTER THORN */
+  {0x0100, 0x0101},  /* LATIN CAPITAL LETTER A WITH MACRON */
+  {0x0102, 0x0103},  /* LATIN CAPITAL LETTER A WITH BREVE */
+  {0x0104, 0x0105},  /* LATIN CAPITAL LETTER A WITH OGONEK */
+  {0x0106, 0x0107},  /* LATIN CAPITAL LETTER C WITH ACUTE */
+  {0x0108, 0x0109},  /* LATIN CAPITAL LETTER C WITH CIRCUMFLEX */
+  {0x010A, 0x010B},  /* LATIN CAPITAL LETTER C WITH DOT ABOVE */
+  {0x010C, 0x010D},  /* LATIN CAPITAL LETTER C WITH CARON */
+  {0x010E, 0x010F},  /* LATIN CAPITAL LETTER D WITH CARON */
+  {0x0110, 0x0111},  /* LATIN CAPITAL LETTER D WITH STROKE */
+  {0x0112, 0x0113},  /* LATIN CAPITAL LETTER E WITH MACRON */
+  {0x0114, 0x0115},  /* LATIN CAPITAL LETTER E WITH BREVE */
+  {0x0116, 0x0117},  /* LATIN CAPITAL LETTER E WITH DOT ABOVE */
+  {0x0118, 0x0119},  /* LATIN CAPITAL LETTER E WITH OGONEK */
+  {0x011A, 0x011B},  /* LATIN CAPITAL LETTER E WITH CARON */
+  {0x011C, 0x011D},  /* LATIN CAPITAL LETTER G WITH CIRCUMFLEX */
+  {0x011E, 0x011F},  /* LATIN CAPITAL LETTER G WITH BREVE */
+  {0x0120, 0x0121},  /* LATIN CAPITAL LETTER G WITH DOT ABOVE */
+  {0x0122, 0x0123},  /* LATIN CAPITAL LETTER G WITH CEDILLA */
+  {0x0124, 0x0125},  /* LATIN CAPITAL LETTER H WITH CIRCUMFLEX */
+  {0x0126, 0x0127},  /* LATIN CAPITAL LETTER H WITH STROKE */
+  {0x0128, 0x0129},  /* LATIN CAPITAL LETTER I WITH TILDE */
+  {0x012A, 0x012B},  /* LATIN CAPITAL LETTER I WITH MACRON */
+  {0x012C, 0x012D},  /* LATIN CAPITAL LETTER I WITH BREVE */
+  {0x012E, 0x012F},  /* LATIN CAPITAL LETTER I WITH OGONEK */
+  {0x0130, 0x0069},  /* LATIN CAPITAL LETTER I WITH DOT ABOVE */
+  {0x0132, 0x0133},  /* LATIN CAPITAL LIGATURE IJ */
+  {0x0134, 0x0135},  /* LATIN CAPITAL LETTER J WITH CIRCUMFLEX */
+  {0x0136, 0x0137},  /* LATIN CAPITAL LETTER K WITH CEDILLA */
+  {0x0139, 0x013A},  /* LATIN CAPITAL LETTER L WITH ACUTE */
+  {0x013B, 0x013C},  /* LATIN CAPITAL LETTER L WITH CEDILLA */
+  {0x013D, 0x013E},  /* LATIN CAPITAL LETTER L WITH CARON */
+  {0x013F, 0x0140},  /* LATIN CAPITAL LETTER L WITH MIDDLE DOT */
+  {0x0141, 0x0142},  /* LATIN CAPITAL LETTER L WITH STROKE */
+  {0x0143, 0x0144},  /* LATIN CAPITAL LETTER N WITH ACUTE */
+  {0x0145, 0x0146},  /* LATIN CAPITAL LETTER N WITH CEDILLA */
+  {0x0147, 0x0148},  /* LATIN CAPITAL LETTER N WITH CARON */
+  {0x014A, 0x014B},  /* LATIN CAPITAL LETTER ENG */
+  {0x014C, 0x014D},  /* LATIN CAPITAL LETTER O WITH MACRON */
+  {0x014E, 0x014F},  /* LATIN CAPITAL LETTER O WITH BREVE */
+  {0x0150, 0x0151},  /* LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */
+  {0x0152, 0x0153},  /* LATIN CAPITAL LIGATURE OE */
+  {0x0154, 0x0155},  /* LATIN CAPITAL LETTER R WITH ACUTE */
+  {0x0156, 0x0157},  /* LATIN CAPITAL LETTER R WITH CEDILLA */
+  {0x0158, 0x0159},  /* LATIN CAPITAL LETTER R WITH CARON */
+  {0x015A, 0x015B},  /* LATIN CAPITAL LETTER S WITH ACUTE */
+  {0x015C, 0x015D},  /* LATIN CAPITAL LETTER S WITH CIRCUMFLEX */
+  {0x015E, 0x015F},  /* LATIN CAPITAL LETTER S WITH CEDILLA */
+  {0x0160, 0x0161},  /* LATIN CAPITAL LETTER S WITH CARON */
+  {0x0162, 0x0163},  /* LATIN CAPITAL LETTER T WITH CEDILLA */
+  {0x0164, 0x0165},  /* LATIN CAPITAL LETTER T WITH CARON */
+  {0x0166, 0x0167},  /* LATIN CAPITAL LETTER T WITH STROKE */
+  {0x0168, 0x0169},  /* LATIN CAPITAL LETTER U WITH TILDE */
+  {0x016A, 0x016B},  /* LATIN CAPITAL LETTER U WITH MACRON */
+  {0x016C, 0x016D},  /* LATIN CAPITAL LETTER U WITH BREVE */
+  {0x016E, 0x016F},  /* LATIN CAPITAL LETTER U WITH RING ABOVE */
+  {0x0170, 0x0171},  /* LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */
+  {0x0172, 0x0173},  /* LATIN CAPITAL LETTER U WITH OGONEK */
+  {0x0174, 0x0175},  /* LATIN CAPITAL LETTER W WITH CIRCUMFLEX */
+  {0x0176, 0x0177},  /* LATIN CAPITAL LETTER Y WITH CIRCUMFLEX */
+  {0x0178, 0x00FF},  /* LATIN CAPITAL LETTER Y WITH DIAERESIS */
+  {0x0179, 0x017A},  /* LATIN CAPITAL LETTER Z WITH ACUTE */
+  {0x017B, 0x017C},  /* LATIN CAPITAL LETTER Z WITH DOT ABOVE */
+  {0x017D, 0x017E},  /* LATIN CAPITAL LETTER Z WITH CARON */
+  {0x0181, 0x0253},  /* LATIN CAPITAL LETTER B WITH HOOK */
+  {0x0182, 0x0183},  /* LATIN CAPITAL LETTER B WITH TOPBAR */
+  {0x0184, 0x0185},  /* LATIN CAPITAL LETTER TONE SIX */
+  {0x0186, 0x0254},  /* LATIN CAPITAL LETTER OPEN O */
+  {0x0187, 0x0188},  /* LATIN CAPITAL LETTER C WITH HOOK */
+  {0x0189, 0x0256},  /* LATIN CAPITAL LETTER AFRICAN D */
+  {0x018A, 0x0257},  /* LATIN CAPITAL LETTER D WITH HOOK */
+  {0x018B, 0x018C},  /* LATIN CAPITAL LETTER D WITH TOPBAR */
+  {0x018E, 0x01DD},  /* LATIN CAPITAL LETTER REVERSED E */
+  {0x018F, 0x0259},  /* LATIN CAPITAL LETTER SCHWA */
+  {0x0190, 0x025B},  /* LATIN CAPITAL LETTER OPEN E */
+  {0x0191, 0x0192},  /* LATIN CAPITAL LETTER F WITH HOOK */
+  {0x0193, 0x0260},  /* LATIN CAPITAL LETTER G WITH HOOK */
+  {0x0194, 0x0263},  /* LATIN CAPITAL LETTER GAMMA */
+  {0x0196, 0x0269},  /* LATIN CAPITAL LETTER IOTA */
+  {0x0197, 0x0268},  /* LATIN CAPITAL LETTER I WITH STROKE */
+  {0x0198, 0x0199},  /* LATIN CAPITAL LETTER K WITH HOOK */
+  {0x019C, 0x026F},  /* LATIN CAPITAL LETTER TURNED M */
+  {0x019D, 0x0272},  /* LATIN CAPITAL LETTER N WITH LEFT HOOK */
+  {0x019F, 0x0275},  /* LATIN CAPITAL LETTER O WITH MIDDLE TILDE */
+  {0x01A0, 0x01A1},  /* LATIN CAPITAL LETTER O WITH HORN */
+  {0x01A2, 0x01A3},  /* LATIN CAPITAL LETTER OI */
+  {0x01A4, 0x01A5},  /* LATIN CAPITAL LETTER P WITH HOOK */
+  {0x01A7, 0x01A8},  /* LATIN CAPITAL LETTER TONE TWO */
+  {0x01A9, 0x0283},  /* LATIN CAPITAL LETTER ESH */
+  {0x01AC, 0x01AD},  /* LATIN CAPITAL LETTER T WITH HOOK */
+  {0x01AE, 0x0288},  /* LATIN CAPITAL LETTER T WITH RETROFLEX HOOK */
+  {0x01AF, 0x01B0},  /* LATIN CAPITAL LETTER U WITH HORN */
+  {0x01B1, 0x028A},  /* LATIN CAPITAL LETTER UPSILON */
+  {0x01B2, 0x028B},  /* LATIN CAPITAL LETTER V WITH HOOK */
+  {0x01B3, 0x01B4},  /* LATIN CAPITAL LETTER Y WITH HOOK */
+  {0x01B5, 0x01B6},  /* LATIN CAPITAL LETTER Z WITH STROKE */
+  {0x01B7, 0x0292},  /* LATIN CAPITAL LETTER EZH */
+  {0x01B8, 0x01B9},  /* LATIN CAPITAL LETTER EZH REVERSED */
+  {0x01BC, 0x01BD},  /* LATIN CAPITAL LETTER TONE FIVE */
+  {0x01C4, 0x01C6},  /* LATIN CAPITAL LETTER DZ WITH CARON */
+  {0x01C5, 0x01C6},  /* LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON */
+  {0x01C7, 0x01C9},  /* LATIN CAPITAL LETTER LJ */
+  {0x01C8, 0x01C9},  /* LATIN CAPITAL LETTER L WITH SMALL LETTER J */
+  {0x01CA, 0x01CC},  /* LATIN CAPITAL LETTER NJ */
+  {0x01CB, 0x01CC},  /* LATIN CAPITAL LETTER N WITH SMALL LETTER J */
+  {0x01CD, 0x01CE},  /* LATIN CAPITAL LETTER A WITH CARON */
+  {0x01CF, 0x01D0},  /* LATIN CAPITAL LETTER I WITH CARON */
+  {0x01D1, 0x01D2},  /* LATIN CAPITAL LETTER O WITH CARON */
+  {0x01D3, 0x01D4},  /* LATIN CAPITAL LETTER U WITH CARON */
+  {0x01D5, 0x01D6},  /* LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON */
+  {0x01D7, 0x01D8},  /* LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE */
+  {0x01D9, 0x01DA},  /* LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON */
+  {0x01DB, 0x01DC},  /* LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE */
+  {0x01DE, 0x01DF},  /* LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON */
+  {0x01E0, 0x01E1},  /* LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON */
+  {0x01E2, 0x01E3},  /* LATIN CAPITAL LETTER AE WITH MACRON */
+  {0x01E4, 0x01E5},  /* LATIN CAPITAL LETTER G WITH STROKE */
+  {0x01E6, 0x01E7},  /* LATIN CAPITAL LETTER G WITH CARON */
+  {0x01E8, 0x01E9},  /* LATIN CAPITAL LETTER K WITH CARON */
+  {0x01EA, 0x01EB},  /* LATIN CAPITAL LETTER O WITH OGONEK */
+  {0x01EC, 0x01ED},  /* LATIN CAPITAL LETTER O WITH OGONEK AND MACRON */
+  {0x01EE, 0x01EF},  /* LATIN CAPITAL LETTER EZH WITH CARON */
+  {0x01F1, 0x01F3},  /* LATIN CAPITAL LETTER DZ */
+  {0x01F2, 0x01F3},  /* LATIN CAPITAL LETTER D WITH SMALL LETTER Z */
+  {0x01F4, 0x01F5},  /* LATIN CAPITAL LETTER G WITH ACUTE */
+  {0x01FA, 0x01FB},  /* LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE */
+  {0x01FC, 0x01FD},  /* LATIN CAPITAL LETTER AE WITH ACUTE */
+  {0x01FE, 0x01FF},  /* LATIN CAPITAL LETTER O WITH STROKE AND ACUTE */
+  {0x0200, 0x0201},  /* LATIN CAPITAL LETTER A WITH DOUBLE GRAVE */
+  {0x0202, 0x0203},  /* LATIN CAPITAL LETTER A WITH INVERTED BREVE */
+  {0x0204, 0x0205},  /* LATIN CAPITAL LETTER E WITH DOUBLE GRAVE */
+  {0x0206, 0x0207},  /* LATIN CAPITAL LETTER E WITH INVERTED BREVE */
+  {0x0208, 0x0209},  /* LATIN CAPITAL LETTER I WITH DOUBLE GRAVE */
+  {0x020A, 0x020B},  /* LATIN CAPITAL LETTER I WITH INVERTED BREVE */
+  {0x020C, 0x020D},  /* LATIN CAPITAL LETTER O WITH DOUBLE GRAVE */
+  {0x020E, 0x020F},  /* LATIN CAPITAL LETTER O WITH INVERTED BREVE */
+  {0x0210, 0x0211},  /* LATIN CAPITAL LETTER R WITH DOUBLE GRAVE */
+  {0x0212, 0x0213},  /* LATIN CAPITAL LETTER R WITH INVERTED BREVE */
+  {0x0214, 0x0215},  /* LATIN CAPITAL LETTER U WITH DOUBLE GRAVE */
+  {0x0216, 0x0217},  /* LATIN CAPITAL LETTER U WITH INVERTED BREVE */
+  {0x0386, 0x03AC},  /* GREEK CAPITAL LETTER ALPHA WITH TONOS */
+  {0x0388, 0x03AD},  /* GREEK CAPITAL LETTER EPSILON WITH TONOS */
+  {0x0389, 0x03AE},  /* GREEK CAPITAL LETTER ETA WITH TONOS */
+  {0x038A, 0x03AF},  /* GREEK CAPITAL LETTER IOTA WITH TONOS */
+  {0x038C, 0x03CC},  /* GREEK CAPITAL LETTER OMICRON WITH TONOS */
+  {0x038E, 0x03CD},  /* GREEK CAPITAL LETTER UPSILON WITH TONOS */
+  {0x038F, 0x03CE},  /* GREEK CAPITAL LETTER OMEGA WITH TONOS */
+  {0x0391, 0x03B1},  /* GREEK CAPITAL LETTER ALPHA */
+  {0x0392, 0x03B2},  /* GREEK CAPITAL LETTER BETA */
+  {0x0393, 0x03B3},  /* GREEK CAPITAL LETTER GAMMA */
+  {0x0394, 0x03B4},  /* GREEK CAPITAL LETTER DELTA */
+  {0x0395, 0x03B5},  /* GREEK CAPITAL LETTER EPSILON */
+  {0x0396, 0x03B6},  /* GREEK CAPITAL LETTER ZETA */
+  {0x0397, 0x03B7},  /* GREEK CAPITAL LETTER ETA */
+  {0x0398, 0x03B8},  /* GREEK CAPITAL LETTER THETA */
+  {0x0399, 0x03B9},  /* GREEK CAPITAL LETTER IOTA */
+  {0x039A, 0x03BA},  /* GREEK CAPITAL LETTER KAPPA */
+  {0x039B, 0x03BB},  /* GREEK CAPITAL LETTER LAMDA */
+  {0x039C, 0x03BC},  /* GREEK CAPITAL LETTER MU */
+  {0x039D, 0x03BD},  /* GREEK CAPITAL LETTER NU */
+  {0x039E, 0x03BE},  /* GREEK CAPITAL LETTER XI */
+  {0x039F, 0x03BF},  /* GREEK CAPITAL LETTER OMICRON */
+  {0x03A0, 0x03C0},  /* GREEK CAPITAL LETTER PI */
+  {0x03A1, 0x03C1},  /* GREEK CAPITAL LETTER RHO */
+  {0x03A3, 0x03C3},  /* GREEK CAPITAL LETTER SIGMA */
+  {0x03A4, 0x03C4},  /* GREEK CAPITAL LETTER TAU */
+  {0x03A5, 0x03C5},  /* GREEK CAPITAL LETTER UPSILON */
+  {0x03A6, 0x03C6},  /* GREEK CAPITAL LETTER PHI */
+  {0x03A7, 0x03C7},  /* GREEK CAPITAL LETTER CHI */
+  {0x03A8, 0x03C8},  /* GREEK CAPITAL LETTER PSI */
+  {0x03A9, 0x03C9},  /* GREEK CAPITAL LETTER OMEGA */
+  {0x03AA, 0x03CA},  /* GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */
+  {0x03AB, 0x03CB},  /* GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */
+  {0x03E2, 0x03E3},  /* COPTIC CAPITAL LETTER SHEI */
+  {0x03E4, 0x03E5},  /* COPTIC CAPITAL LETTER FEI */
+  {0x03E6, 0x03E7},  /* COPTIC CAPITAL LETTER KHEI */
+  {0x03E8, 0x03E9},  /* COPTIC CAPITAL LETTER HORI */
+  {0x03EA, 0x03EB},  /* COPTIC CAPITAL LETTER GANGIA */
+  {0x03EC, 0x03ED},  /* COPTIC CAPITAL LETTER SHIMA */
+  {0x03EE, 0x03EF},  /* COPTIC CAPITAL LETTER DEI */
+  {0x0401, 0x0451},  /* CYRILLIC CAPITAL LETTER IO */
+  {0x0402, 0x0452},  /* CYRILLIC CAPITAL LETTER DJE */
+  {0x0403, 0x0453},  /* CYRILLIC CAPITAL LETTER GJE */
+  {0x0404, 0x0454},  /* CYRILLIC CAPITAL LETTER UKRAINIAN IE */
+  {0x0405, 0x0455},  /* CYRILLIC CAPITAL LETTER DZE */
+  {0x0406, 0x0456},  /* CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */
+  {0x0407, 0x0457},  /* CYRILLIC CAPITAL LETTER YI */
+  {0x0408, 0x0458},  /* CYRILLIC CAPITAL LETTER JE */
+  {0x0409, 0x0459},  /* CYRILLIC CAPITAL LETTER LJE */
+  {0x040A, 0x045A},  /* CYRILLIC CAPITAL LETTER NJE */
+  {0x040B, 0x045B},  /* CYRILLIC CAPITAL LETTER TSHE */
+  {0x040C, 0x045C},  /* CYRILLIC CAPITAL LETTER KJE */
+  {0x040E, 0x045E},  /* CYRILLIC CAPITAL LETTER SHORT U */
+  {0x040F, 0x045F},  /* CYRILLIC CAPITAL LETTER DZHE */
+  {0x0410, 0x0430},  /* CYRILLIC CAPITAL LETTER A */
+  {0x0411, 0x0431},  /* CYRILLIC CAPITAL LETTER BE */
+  {0x0412, 0x0432},  /* CYRILLIC CAPITAL LETTER VE */
+  {0x0413, 0x0433},  /* CYRILLIC CAPITAL LETTER GHE */
+  {0x0414, 0x0434},  /* CYRILLIC CAPITAL LETTER DE */
+  {0x0415, 0x0435},  /* CYRILLIC CAPITAL LETTER IE */
+  {0x0416, 0x0436},  /* CYRILLIC CAPITAL LETTER ZHE */
+  {0x0417, 0x0437},  /* CYRILLIC CAPITAL LETTER ZE */
+  {0x0418, 0x0438},  /* CYRILLIC CAPITAL LETTER I */
+  {0x0419, 0x0439},  /* CYRILLIC CAPITAL LETTER SHORT I */
+  {0x041A, 0x043A},  /* CYRILLIC CAPITAL LETTER KA */
+  {0x041B, 0x043B},  /* CYRILLIC CAPITAL LETTER EL */
+  {0x041C, 0x043C},  /* CYRILLIC CAPITAL LETTER EM */
+  {0x041D, 0x043D},  /* CYRILLIC CAPITAL LETTER EN */
+  {0x041E, 0x043E},  /* CYRILLIC CAPITAL LETTER O */
+  {0x041F, 0x043F},  /* CYRILLIC CAPITAL LETTER PE */
+  {0x0420, 0x0440},  /* CYRILLIC CAPITAL LETTER ER */
+  {0x0421, 0x0441},  /* CYRILLIC CAPITAL LETTER ES */
+  {0x0422, 0x0442},  /* CYRILLIC CAPITAL LETTER TE */
+  {0x0423, 0x0443},  /* CYRILLIC CAPITAL LETTER U */
+  {0x0424, 0x0444},  /* CYRILLIC CAPITAL LETTER EF */
+  {0x0425, 0x0445},  /* CYRILLIC CAPITAL LETTER HA */
+  {0x0426, 0x0446},  /* CYRILLIC CAPITAL LETTER TSE */
+  {0x0427, 0x0447},  /* CYRILLIC CAPITAL LETTER CHE */
+  {0x0428, 0x0448},  /* CYRILLIC CAPITAL LETTER SHA */
+  {0x0429, 0x0449},  /* CYRILLIC CAPITAL LETTER SHCHA */
+  {0x042A, 0x044A},  /* CYRILLIC CAPITAL LETTER HARD SIGN */
+  {0x042B, 0x044B},  /* CYRILLIC CAPITAL LETTER YERU */
+  {0x042C, 0x044C},  /* CYRILLIC CAPITAL LETTER SOFT SIGN */
+  {0x042D, 0x044D},  /* CYRILLIC CAPITAL LETTER E */
+  {0x042E, 0x044E},  /* CYRILLIC CAPITAL LETTER YU */
+  {0x042F, 0x044F},  /* CYRILLIC CAPITAL LETTER YA */
+  {0x0460, 0x0461},  /* CYRILLIC CAPITAL LETTER OMEGA */
+  {0x0462, 0x0463},  /* CYRILLIC CAPITAL LETTER YAT */
+  {0x0464, 0x0465},  /* CYRILLIC CAPITAL LETTER IOTIFIED E */
+  {0x0466, 0x0467},  /* CYRILLIC CAPITAL LETTER LITTLE YUS */
+  {0x0468, 0x0469},  /* CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS */
+  {0x046A, 0x046B},  /* CYRILLIC CAPITAL LETTER BIG YUS */
+  {0x046C, 0x046D},  /* CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS */
+  {0x046E, 0x046F},  /* CYRILLIC CAPITAL LETTER KSI */
+  {0x0470, 0x0471},  /* CYRILLIC CAPITAL LETTER PSI */
+  {0x0472, 0x0473},  /* CYRILLIC CAPITAL LETTER FITA */
+  {0x0474, 0x0475},  /* CYRILLIC CAPITAL LETTER IZHITSA */
+  {0x0476, 0x0477},  /* CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT */
+  {0x0478, 0x0479},  /* CYRILLIC CAPITAL LETTER UK */
+  {0x047A, 0x047B},  /* CYRILLIC CAPITAL LETTER ROUND OMEGA */
+  {0x047C, 0x047D},  /* CYRILLIC CAPITAL LETTER OMEGA WITH TITLO */
+  {0x047E, 0x047F},  /* CYRILLIC CAPITAL LETTER OT */
+  {0x0480, 0x0481},  /* CYRILLIC CAPITAL LETTER KOPPA */
+  {0x0490, 0x0491},  /* CYRILLIC CAPITAL LETTER GHE WITH UPTURN */
+  {0x0492, 0x0493},  /* CYRILLIC CAPITAL LETTER GHE WITH STROKE */
+  {0x0494, 0x0495},  /* CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK */
+  {0x0496, 0x0497},  /* CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER */
+  {0x0498, 0x0499},  /* CYRILLIC CAPITAL LETTER ZE WITH DESCENDER */
+  {0x049A, 0x049B},  /* CYRILLIC CAPITAL LETTER KA WITH DESCENDER */
+  {0x049C, 0x049D},  /* CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE */
+  {0x049E, 0x049F},  /* CYRILLIC CAPITAL LETTER KA WITH STROKE */
+  {0x04A0, 0x04A1},  /* CYRILLIC CAPITAL LETTER BASHKIR KA */
+  {0x04A2, 0x04A3},  /* CYRILLIC CAPITAL LETTER EN WITH DESCENDER */
+  {0x04A4, 0x04A5},  /* CYRILLIC CAPITAL LIGATURE EN GHE */
+  {0x04A6, 0x04A7},  /* CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK */
+  {0x04A8, 0x04A9},  /* CYRILLIC CAPITAL LETTER ABKHASIAN HA */
+  {0x04AA, 0x04AB},  /* CYRILLIC CAPITAL LETTER ES WITH DESCENDER */
+  {0x04AC, 0x04AD},  /* CYRILLIC CAPITAL LETTER TE WITH DESCENDER */
+  {0x04AE, 0x04AF},  /* CYRILLIC CAPITAL LETTER STRAIGHT U */
+  {0x04B0, 0x04B1},  /* CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE */
+  {0x04B2, 0x04B3},  /* CYRILLIC CAPITAL LETTER HA WITH DESCENDER */
+  {0x04B4, 0x04B5},  /* CYRILLIC CAPITAL LIGATURE TE TSE */
+  {0x04B6, 0x04B7},  /* CYRILLIC CAPITAL LETTER CHE WITH DESCENDER */
+  {0x04B8, 0x04B9},  /* CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE */
+  {0x04BA, 0x04BB},  /* CYRILLIC CAPITAL LETTER SHHA */
+  {0x04BC, 0x04BD},  /* CYRILLIC CAPITAL LETTER ABKHASIAN CHE */
+  {0x04BE, 0x04BF},  /* CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER */
+  {0x04C1, 0x04C2},  /* CYRILLIC CAPITAL LETTER ZHE WITH BREVE */
+  {0x04C3, 0x04C4},  /* CYRILLIC CAPITAL LETTER KA WITH HOOK */
+  {0x04C7, 0x04C8},  /* CYRILLIC CAPITAL LETTER EN WITH HOOK */
+  {0x04CB, 0x04CC},  /* CYRILLIC CAPITAL LETTER KHAKASSIAN CHE */
+  {0x04D0, 0x04D1},  /* CYRILLIC CAPITAL LETTER A WITH BREVE */
+  {0x04D2, 0x04D3},  /* CYRILLIC CAPITAL LETTER A WITH DIAERESIS */
+  {0x04D4, 0x04D5},  /* CYRILLIC CAPITAL LIGATURE A IE */
+  {0x04D6, 0x04D7},  /* CYRILLIC CAPITAL LETTER IE WITH BREVE */
+  {0x04D8, 0x04D9},  /* CYRILLIC CAPITAL LETTER SCHWA */
+  {0x04DA, 0x04DB},  /* CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS */
+  {0x04DC, 0x04DD},  /* CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS */
+  {0x04DE, 0x04DF},  /* CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS */
+  {0x04E0, 0x04E1},  /* CYRILLIC CAPITAL LETTER ABKHASIAN DZE */
+  {0x04E2, 0x04E3},  /* CYRILLIC CAPITAL LETTER I WITH MACRON */
+  {0x04E4, 0x04E5},  /* CYRILLIC CAPITAL LETTER I WITH DIAERESIS */
+  {0x04E6, 0x04E7},  /* CYRILLIC CAPITAL LETTER O WITH DIAERESIS */
+  {0x04E8, 0x04E9},  /* CYRILLIC CAPITAL LETTER BARRED O */
+  {0x04EA, 0x04EB},  /* CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS */
+  {0x04EE, 0x04EF},  /* CYRILLIC CAPITAL LETTER U WITH MACRON */
+  {0x04F0, 0x04F1},  /* CYRILLIC CAPITAL LETTER U WITH DIAERESIS */
+  {0x04F2, 0x04F3},  /* CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE */
+  {0x04F4, 0x04F5},  /* CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS */
+  {0x04F8, 0x04F9},  /* CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS */
+  {0x0531, 0x0561},  /* ARMENIAN CAPITAL LETTER AYB */
+  {0x0532, 0x0562},  /* ARMENIAN CAPITAL LETTER BEN */
+  {0x0533, 0x0563},  /* ARMENIAN CAPITAL LETTER GIM */
+  {0x0534, 0x0564},  /* ARMENIAN CAPITAL LETTER DA */
+  {0x0535, 0x0565},  /* ARMENIAN CAPITAL LETTER ECH */
+  {0x0536, 0x0566},  /* ARMENIAN CAPITAL LETTER ZA */
+  {0x0537, 0x0567},  /* ARMENIAN CAPITAL LETTER EH */
+  {0x0538, 0x0568},  /* ARMENIAN CAPITAL LETTER ET */
+  {0x0539, 0x0569},  /* ARMENIAN CAPITAL LETTER TO */
+  {0x053A, 0x056A},  /* ARMENIAN CAPITAL LETTER ZHE */
+  {0x053B, 0x056B},  /* ARMENIAN CAPITAL LETTER INI */
+  {0x053C, 0x056C},  /* ARMENIAN CAPITAL LETTER LIWN */
+  {0x053D, 0x056D},  /* ARMENIAN CAPITAL LETTER XEH */
+  {0x053E, 0x056E},  /* ARMENIAN CAPITAL LETTER CA */
+  {0x053F, 0x056F},  /* ARMENIAN CAPITAL LETTER KEN */
+  {0x0540, 0x0570},  /* ARMENIAN CAPITAL LETTER HO */
+  {0x0541, 0x0571},  /* ARMENIAN CAPITAL LETTER JA */
+  {0x0542, 0x0572},  /* ARMENIAN CAPITAL LETTER GHAD */
+  {0x0543, 0x0573},  /* ARMENIAN CAPITAL LETTER CHEH */
+  {0x0544, 0x0574},  /* ARMENIAN CAPITAL LETTER MEN */
+  {0x0545, 0x0575},  /* ARMENIAN CAPITAL LETTER YI */
+  {0x0546, 0x0576},  /* ARMENIAN CAPITAL LETTER NOW */
+  {0x0547, 0x0577},  /* ARMENIAN CAPITAL LETTER SHA */
+  {0x0548, 0x0578},  /* ARMENIAN CAPITAL LETTER VO */
+  {0x0549, 0x0579},  /* ARMENIAN CAPITAL LETTER CHA */
+  {0x054A, 0x057A},  /* ARMENIAN CAPITAL LETTER PEH */
+  {0x054B, 0x057B},  /* ARMENIAN CAPITAL LETTER JHEH */
+  {0x054C, 0x057C},  /* ARMENIAN CAPITAL LETTER RA */
+  {0x054D, 0x057D},  /* ARMENIAN CAPITAL LETTER SEH */
+  {0x054E, 0x057E},  /* ARMENIAN CAPITAL LETTER VEW */
+  {0x054F, 0x057F},  /* ARMENIAN CAPITAL LETTER TIWN */
+  {0x0550, 0x0580},  /* ARMENIAN CAPITAL LETTER REH */
+  {0x0551, 0x0581},  /* ARMENIAN CAPITAL LETTER CO */
+  {0x0552, 0x0582},  /* ARMENIAN CAPITAL LETTER YIWN */
+  {0x0553, 0x0583},  /* ARMENIAN CAPITAL LETTER PIWR */
+  {0x0554, 0x0584},  /* ARMENIAN CAPITAL LETTER KEH */
+  {0x0555, 0x0585},  /* ARMENIAN CAPITAL LETTER OH */
+  {0x0556, 0x0586},  /* ARMENIAN CAPITAL LETTER FEH */
+  {0x10A0, 0x10D0},  /* GEORGIAN CAPITAL LETTER AN */
+  {0x10A1, 0x10D1},  /* GEORGIAN CAPITAL LETTER BAN */
+  {0x10A2, 0x10D2},  /* GEORGIAN CAPITAL LETTER GAN */
+  {0x10A3, 0x10D3},  /* GEORGIAN CAPITAL LETTER DON */
+  {0x10A4, 0x10D4},  /* GEORGIAN CAPITAL LETTER EN */
+  {0x10A5, 0x10D5},  /* GEORGIAN CAPITAL LETTER VIN */
+  {0x10A6, 0x10D6},  /* GEORGIAN CAPITAL LETTER ZEN */
+  {0x10A7, 0x10D7},  /* GEORGIAN CAPITAL LETTER TAN */
+  {0x10A8, 0x10D8},  /* GEORGIAN CAPITAL LETTER IN */
+  {0x10A9, 0x10D9},  /* GEORGIAN CAPITAL LETTER KAN */
+  {0x10AA, 0x10DA},  /* GEORGIAN CAPITAL LETTER LAS */
+  {0x10AB, 0x10DB},  /* GEORGIAN CAPITAL LETTER MAN */
+  {0x10AC, 0x10DC},  /* GEORGIAN CAPITAL LETTER NAR */
+  {0x10AD, 0x10DD},  /* GEORGIAN CAPITAL LETTER ON */
+  {0x10AE, 0x10DE},  /* GEORGIAN CAPITAL LETTER PAR */
+  {0x10AF, 0x10DF},  /* GEORGIAN CAPITAL LETTER ZHAR */
+  {0x10B0, 0x10E0},  /* GEORGIAN CAPITAL LETTER RAE */
+  {0x10B1, 0x10E1},  /* GEORGIAN CAPITAL LETTER SAN */
+  {0x10B2, 0x10E2},  /* GEORGIAN CAPITAL LETTER TAR */
+  {0x10B3, 0x10E3},  /* GEORGIAN CAPITAL LETTER UN */
+  {0x10B4, 0x10E4},  /* GEORGIAN CAPITAL LETTER PHAR */
+  {0x10B5, 0x10E5},  /* GEORGIAN CAPITAL LETTER KHAR */
+  {0x10B6, 0x10E6},  /* GEORGIAN CAPITAL LETTER GHAN */
+  {0x10B7, 0x10E7},  /* GEORGIAN CAPITAL LETTER QAR */
+  {0x10B8, 0x10E8},  /* GEORGIAN CAPITAL LETTER SHIN */
+  {0x10B9, 0x10E9},  /* GEORGIAN CAPITAL LETTER CHIN */
+  {0x10BA, 0x10EA},  /* GEORGIAN CAPITAL LETTER CAN */
+  {0x10BB, 0x10EB},  /* GEORGIAN CAPITAL LETTER JIL */
+  {0x10BC, 0x10EC},  /* GEORGIAN CAPITAL LETTER CIL */
+  {0x10BD, 0x10ED},  /* GEORGIAN CAPITAL LETTER CHAR */
+  {0x10BE, 0x10EE},  /* GEORGIAN CAPITAL LETTER XAN */
+  {0x10BF, 0x10EF},  /* GEORGIAN CAPITAL LETTER JHAN */
+  {0x10C0, 0x10F0},  /* GEORGIAN CAPITAL LETTER HAE */
+  {0x10C1, 0x10F1},  /* GEORGIAN CAPITAL LETTER HE */
+  {0x10C2, 0x10F2},  /* GEORGIAN CAPITAL LETTER HIE */
+  {0x10C3, 0x10F3},  /* GEORGIAN CAPITAL LETTER WE */
+  {0x10C4, 0x10F4},  /* GEORGIAN CAPITAL LETTER HAR */
+  {0x10C5, 0x10F5},  /* GEORGIAN CAPITAL LETTER HOE */
+  {0x1E00, 0x1E01},  /* LATIN CAPITAL LETTER A WITH RING BELOW */
+  {0x1E02, 0x1E03},  /* LATIN CAPITAL LETTER B WITH DOT ABOVE */
+  {0x1E04, 0x1E05},  /* LATIN CAPITAL LETTER B WITH DOT BELOW */
+  {0x1E06, 0x1E07},  /* LATIN CAPITAL LETTER B WITH LINE BELOW */
+  {0x1E08, 0x1E09},  /* LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE */
+  {0x1E0A, 0x1E0B},  /* LATIN CAPITAL LETTER D WITH DOT ABOVE */
+  {0x1E0C, 0x1E0D},  /* LATIN CAPITAL LETTER D WITH DOT BELOW */
+  {0x1E0E, 0x1E0F},  /* LATIN CAPITAL LETTER D WITH LINE BELOW */
+  {0x1E10, 0x1E11},  /* LATIN CAPITAL LETTER D WITH CEDILLA */
+  {0x1E12, 0x1E13},  /* LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW */
+  {0x1E14, 0x1E15},  /* LATIN CAPITAL LETTER E WITH MACRON AND GRAVE */
+  {0x1E16, 0x1E17},  /* LATIN CAPITAL LETTER E WITH MACRON AND ACUTE */
+  {0x1E18, 0x1E19},  /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW */
+  {0x1E1A, 0x1E1B},  /* LATIN CAPITAL LETTER E WITH TILDE BELOW */
+  {0x1E1C, 0x1E1D},  /* LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE */
+  {0x1E1E, 0x1E1F},  /* LATIN CAPITAL LETTER F WITH DOT ABOVE */
+  {0x1E20, 0x1E21},  /* LATIN CAPITAL LETTER G WITH MACRON */
+  {0x1E22, 0x1E23},  /* LATIN CAPITAL LETTER H WITH DOT ABOVE */
+  {0x1E24, 0x1E25},  /* LATIN CAPITAL LETTER H WITH DOT BELOW */
+  {0x1E26, 0x1E27},  /* LATIN CAPITAL LETTER H WITH DIAERESIS */
+  {0x1E28, 0x1E29},  /* LATIN CAPITAL LETTER H WITH CEDILLA */
+  {0x1E2A, 0x1E2B},  /* LATIN CAPITAL LETTER H WITH BREVE BELOW */
+  {0x1E2C, 0x1E2D},  /* LATIN CAPITAL LETTER I WITH TILDE BELOW */
+  {0x1E2E, 0x1E2F},  /* LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE */
+  {0x1E30, 0x1E31},  /* LATIN CAPITAL LETTER K WITH ACUTE */
+  {0x1E32, 0x1E33},  /* LATIN CAPITAL LETTER K WITH DOT BELOW */
+  {0x1E34, 0x1E35},  /* LATIN CAPITAL LETTER K WITH LINE BELOW */
+  {0x1E36, 0x1E37},  /* LATIN CAPITAL LETTER L WITH DOT BELOW */
+  {0x1E38, 0x1E39},  /* LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON */
+  {0x1E3A, 0x1E3B},  /* LATIN CAPITAL LETTER L WITH LINE BELOW */
+  {0x1E3C, 0x1E3D},  /* LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW */
+  {0x1E3E, 0x1E3F},  /* LATIN CAPITAL LETTER M WITH ACUTE */
+  {0x1E40, 0x1E41},  /* LATIN CAPITAL LETTER M WITH DOT ABOVE */
+  {0x1E42, 0x1E43},  /* LATIN CAPITAL LETTER M WITH DOT BELOW */
+  {0x1E44, 0x1E45},  /* LATIN CAPITAL LETTER N WITH DOT ABOVE */
+  {0x1E46, 0x1E47},  /* LATIN CAPITAL LETTER N WITH DOT BELOW */
+  {0x1E48, 0x1E49},  /* LATIN CAPITAL LETTER N WITH LINE BELOW */
+  {0x1E4A, 0x1E4B},  /* LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW */
+  {0x1E4C, 0x1E4D},  /* LATIN CAPITAL LETTER O WITH TILDE AND ACUTE */
+  {0x1E4E, 0x1E4F},  /* LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS */
+  {0x1E50, 0x1E51},  /* LATIN CAPITAL LETTER O WITH MACRON AND GRAVE */
+  {0x1E52, 0x1E53},  /* LATIN CAPITAL LETTER O WITH MACRON AND ACUTE */
+  {0x1E54, 0x1E55},  /* LATIN CAPITAL LETTER P WITH ACUTE */
+  {0x1E56, 0x1E57},  /* LATIN CAPITAL LETTER P WITH DOT ABOVE */
+  {0x1E58, 0x1E59},  /* LATIN CAPITAL LETTER R WITH DOT ABOVE */
+  {0x1E5A, 0x1E5B},  /* LATIN CAPITAL LETTER R WITH DOT BELOW */
+  {0x1E5C, 0x1E5D},  /* LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON */
+  {0x1E5E, 0x1E5F},  /* LATIN CAPITAL LETTER R WITH LINE BELOW */
+  {0x1E60, 0x1E61},  /* LATIN CAPITAL LETTER S WITH DOT ABOVE */
+  {0x1E62, 0x1E63},  /* LATIN CAPITAL LETTER S WITH DOT BELOW */
+  {0x1E64, 0x1E65},  /* LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE */
+  {0x1E66, 0x1E67},  /* LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE */
+  {0x1E68, 0x1E69},  /* LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE */
+  {0x1E6A, 0x1E6B},  /* LATIN CAPITAL LETTER T WITH DOT ABOVE */
+  {0x1E6C, 0x1E6D},  /* LATIN CAPITAL LETTER T WITH DOT BELOW */
+  {0x1E6E, 0x1E6F},  /* LATIN CAPITAL LETTER T WITH LINE BELOW */
+  {0x1E70, 0x1E71},  /* LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW */
+  {0x1E72, 0x1E73},  /* LATIN CAPITAL LETTER U WITH DIAERESIS BELOW */
+  {0x1E74, 0x1E75},  /* LATIN CAPITAL LETTER U WITH TILDE BELOW */
+  {0x1E76, 0x1E77},  /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW */
+  {0x1E78, 0x1E79},  /* LATIN CAPITAL LETTER U WITH TILDE AND ACUTE */
+  {0x1E7A, 0x1E7B},  /* LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS */
+  {0x1E7C, 0x1E7D},  /* LATIN CAPITAL LETTER V WITH TILDE */
+  {0x1E7E, 0x1E7F},  /* LATIN CAPITAL LETTER V WITH DOT BELOW */
+  {0x1E80, 0x1E81},  /* LATIN CAPITAL LETTER W WITH GRAVE */
+  {0x1E82, 0x1E83},  /* LATIN CAPITAL LETTER W WITH ACUTE */
+  {0x1E84, 0x1E85},  /* LATIN CAPITAL LETTER W WITH DIAERESIS */
+  {0x1E86, 0x1E87},  /* LATIN CAPITAL LETTER W WITH DOT ABOVE */
+  {0x1E88, 0x1E89},  /* LATIN CAPITAL LETTER W WITH DOT BELOW */
+  {0x1E8A, 0x1E8B},  /* LATIN CAPITAL LETTER X WITH DOT ABOVE */
+  {0x1E8C, 0x1E8D},  /* LATIN CAPITAL LETTER X WITH DIAERESIS */
+  {0x1E8E, 0x1E8F},  /* LATIN CAPITAL LETTER Y WITH DOT ABOVE */
+  {0x1E90, 0x1E91},  /* LATIN CAPITAL LETTER Z WITH CIRCUMFLEX */
+  {0x1E92, 0x1E93},  /* LATIN CAPITAL LETTER Z WITH DOT BELOW */
+  {0x1E94, 0x1E95},  /* LATIN CAPITAL LETTER Z WITH LINE BELOW */
+  {0x1EA0, 0x1EA1},  /* LATIN CAPITAL LETTER A WITH DOT BELOW */
+  {0x1EA2, 0x1EA3},  /* LATIN CAPITAL LETTER A WITH HOOK ABOVE */
+  {0x1EA4, 0x1EA5},  /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE */
+  {0x1EA6, 0x1EA7},  /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE */
+  {0x1EA8, 0x1EA9},  /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE */
+  {0x1EAA, 0x1EAB},  /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE */
+  {0x1EAC, 0x1EAD},  /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW */
+  {0x1EAE, 0x1EAF},  /* LATIN CAPITAL LETTER A WITH BREVE AND ACUTE */
+  {0x1EB0, 0x1EB1},  /* LATIN CAPITAL LETTER A WITH BREVE AND GRAVE */
+  {0x1EB2, 0x1EB3},  /* LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE */
+  {0x1EB4, 0x1EB5},  /* LATIN CAPITAL LETTER A WITH BREVE AND TILDE */
+  {0x1EB6, 0x1EB7},  /* LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW */
+  {0x1EB8, 0x1EB9},  /* LATIN CAPITAL LETTER E WITH DOT BELOW */
+  {0x1EBA, 0x1EBB},  /* LATIN CAPITAL LETTER E WITH HOOK ABOVE */
+  {0x1EBC, 0x1EBD},  /* LATIN CAPITAL LETTER E WITH TILDE */
+  {0x1EBE, 0x1EBF},  /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE */
+  {0x1EC0, 0x1EC1},  /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE */
+  {0x1EC2, 0x1EC3},  /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE */
+  {0x1EC4, 0x1EC5},  /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE */
+  {0x1EC6, 0x1EC7},  /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW */
+  {0x1EC8, 0x1EC9},  /* LATIN CAPITAL LETTER I WITH HOOK ABOVE */
+  {0x1ECA, 0x1ECB},  /* LATIN CAPITAL LETTER I WITH DOT BELOW */
+  {0x1ECC, 0x1ECD},  /* LATIN CAPITAL LETTER O WITH DOT BELOW */
+  {0x1ECE, 0x1ECF},  /* LATIN CAPITAL LETTER O WITH HOOK ABOVE */
+  {0x1ED0, 0x1ED1},  /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE */
+  {0x1ED2, 0x1ED3},  /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE */
+  {0x1ED4, 0x1ED5},  /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE */
+  {0x1ED6, 0x1ED7},  /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE */
+  {0x1ED8, 0x1ED9},  /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW */
+  {0x1EDA, 0x1EDB},  /* LATIN CAPITAL LETTER O WITH HORN AND ACUTE */
+  {0x1EDC, 0x1EDD},  /* LATIN CAPITAL LETTER O WITH HORN AND GRAVE */
+  {0x1EDE, 0x1EDF},  /* LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE */
+  {0x1EE0, 0x1EE1},  /* LATIN CAPITAL LETTER O WITH HORN AND TILDE */
+  {0x1EE2, 0x1EE3},  /* LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW */
+  {0x1EE4, 0x1EE5},  /* LATIN CAPITAL LETTER U WITH DOT BELOW */
+  {0x1EE6, 0x1EE7},  /* LATIN CAPITAL LETTER U WITH HOOK ABOVE */
+  {0x1EE8, 0x1EE9},  /* LATIN CAPITAL LETTER U WITH HORN AND ACUTE */
+  {0x1EEA, 0x1EEB},  /* LATIN CAPITAL LETTER U WITH HORN AND GRAVE */
+  {0x1EEC, 0x1EED},  /* LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE */
+  {0x1EEE, 0x1EEF},  /* LATIN CAPITAL LETTER U WITH HORN AND TILDE */
+  {0x1EF0, 0x1EF1},  /* LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW */
+  {0x1EF2, 0x1EF3},  /* LATIN CAPITAL LETTER Y WITH GRAVE */
+  {0x1EF4, 0x1EF5},  /* LATIN CAPITAL LETTER Y WITH DOT BELOW */
+  {0x1EF6, 0x1EF7},  /* LATIN CAPITAL LETTER Y WITH HOOK ABOVE */
+  {0x1EF8, 0x1EF9},  /* LATIN CAPITAL LETTER Y WITH TILDE */
+  {0x1F08, 0x1F00},  /* GREEK CAPITAL LETTER ALPHA WITH PSILI */
+  {0x1F09, 0x1F01},  /* GREEK CAPITAL LETTER ALPHA WITH DASIA */
+  {0x1F0A, 0x1F02},  /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA */
+  {0x1F0B, 0x1F03},  /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA */
+  {0x1F0C, 0x1F04},  /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA */
+  {0x1F0D, 0x1F05},  /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA */
+  {0x1F0E, 0x1F06},  /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI */
+  {0x1F0F, 0x1F07},  /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI */
+  {0x1F18, 0x1F10},  /* GREEK CAPITAL LETTER EPSILON WITH PSILI */
+  {0x1F19, 0x1F11},  /* GREEK CAPITAL LETTER EPSILON WITH DASIA */
+  {0x1F1A, 0x1F12},  /* GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA */
+  {0x1F1B, 0x1F13},  /* GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA */
+  {0x1F1C, 0x1F14},  /* GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA */
+  {0x1F1D, 0x1F15},  /* GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA */
+  {0x1F28, 0x1F20},  /* GREEK CAPITAL LETTER ETA WITH PSILI */
+  {0x1F29, 0x1F21},  /* GREEK CAPITAL LETTER ETA WITH DASIA */
+  {0x1F2A, 0x1F22},  /* GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA */
+  {0x1F2B, 0x1F23},  /* GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA */
+  {0x1F2C, 0x1F24},  /* GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA */
+  {0x1F2D, 0x1F25},  /* GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA */
+  {0x1F2E, 0x1F26},  /* GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI */
+  {0x1F2F, 0x1F27},  /* GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI */
+  {0x1F38, 0x1F30},  /* GREEK CAPITAL LETTER IOTA WITH PSILI */
+  {0x1F39, 0x1F31},  /* GREEK CAPITAL LETTER IOTA WITH DASIA */
+  {0x1F3A, 0x1F32},  /* GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA */
+  {0x1F3B, 0x1F33},  /* GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA */
+  {0x1F3C, 0x1F34},  /* GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA */
+  {0x1F3D, 0x1F35},  /* GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA */
+  {0x1F3E, 0x1F36},  /* GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI */
+  {0x1F3F, 0x1F37},  /* GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI */
+  {0x1F48, 0x1F40},  /* GREEK CAPITAL LETTER OMICRON WITH PSILI */
+  {0x1F49, 0x1F41},  /* GREEK CAPITAL LETTER OMICRON WITH DASIA */
+  {0x1F4A, 0x1F42},  /* GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA */
+  {0x1F4B, 0x1F43},  /* GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA */
+  {0x1F4C, 0x1F44},  /* GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA */
+  {0x1F4D, 0x1F45},  /* GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA */
+  {0x1F59, 0x1F51},  /* GREEK CAPITAL LETTER UPSILON WITH DASIA */
+  {0x1F5B, 0x1F53},  /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA */
+  {0x1F5D, 0x1F55},  /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA */
+  {0x1F5F, 0x1F57},  /* GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI */
+  {0x1F68, 0x1F60},  /* GREEK CAPITAL LETTER OMEGA WITH PSILI */
+  {0x1F69, 0x1F61},  /* GREEK CAPITAL LETTER OMEGA WITH DASIA */
+  {0x1F6A, 0x1F62},  /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA */
+  {0x1F6B, 0x1F63},  /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA */
+  {0x1F6C, 0x1F64},  /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA */
+  {0x1F6D, 0x1F65},  /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA */
+  {0x1F6E, 0x1F66},  /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI */
+  {0x1F6F, 0x1F67},  /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI */
+  {0x1F88, 0x1F80},  /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI */
+  {0x1F89, 0x1F81},  /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI */
+  {0x1F8A, 0x1F82},  /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI */
+  {0x1F8B, 0x1F83},  /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI */
+  {0x1F8C, 0x1F84},  /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI */
+  {0x1F8D, 0x1F85},  /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI */
+  {0x1F8E, 0x1F86},  /* GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */
+  {0x1F8F, 0x1F87},  /* GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */
+  {0x1F98, 0x1F90},  /* GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI */
+  {0x1F99, 0x1F91},  /* GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI */
+  {0x1F9A, 0x1F92},  /* GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI */
+  {0x1F9B, 0x1F93},  /* GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI */
+  {0x1F9C, 0x1F94},  /* GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI */
+  {0x1F9D, 0x1F95},  /* GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI */
+  {0x1F9E, 0x1F96},  /* GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */
+  {0x1F9F, 0x1F97},  /* GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */
+  {0x1FA8, 0x1FA0},  /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI */
+  {0x1FA9, 0x1FA1},  /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI */
+  {0x1FAA, 0x1FA2},  /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI */
+  {0x1FAB, 0x1FA3},  /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI */
+  {0x1FAC, 0x1FA4},  /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI */
+  {0x1FAD, 0x1FA5},  /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI */
+  {0x1FAE, 0x1FA6},  /* GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI */
+  {0x1FAF, 0x1FA7},  /* GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI */
+  {0x1FB8, 0x1FB0},  /* GREEK CAPITAL LETTER ALPHA WITH VRACHY */
+  {0x1FB9, 0x1FB1},  /* GREEK CAPITAL LETTER ALPHA WITH MACRON */
+  {0x1FBA, 0x1F70},  /* GREEK CAPITAL LETTER ALPHA WITH VARIA */
+  {0x1FBB, 0x1F71},  /* GREEK CAPITAL LETTER ALPHA WITH OXIA */
+  {0x1FBC, 0x1FB3},  /* GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI */
+  {0x1FC8, 0x1F72},  /* GREEK CAPITAL LETTER EPSILON WITH VARIA */
+  {0x1FC9, 0x1F73},  /* GREEK CAPITAL LETTER EPSILON WITH OXIA */
+  {0x1FCA, 0x1F74},  /* GREEK CAPITAL LETTER ETA WITH VARIA */
+  {0x1FCB, 0x1F75},  /* GREEK CAPITAL LETTER ETA WITH OXIA */
+  {0x1FCC, 0x1FC3},  /* GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI */
+  {0x1FD8, 0x1FD0},  /* GREEK CAPITAL LETTER IOTA WITH VRACHY */
+  {0x1FD9, 0x1FD1},  /* GREEK CAPITAL LETTER IOTA WITH MACRON */
+  {0x1FDA, 0x1F76},  /* GREEK CAPITAL LETTER IOTA WITH VARIA */
+  {0x1FDB, 0x1F77},  /* GREEK CAPITAL LETTER IOTA WITH OXIA */
+  {0x1FE8, 0x1FE0},  /* GREEK CAPITAL LETTER UPSILON WITH VRACHY */
+  {0x1FE9, 0x1FE1},  /* GREEK CAPITAL LETTER UPSILON WITH MACRON */
+  {0x1FEA, 0x1F7A},  /* GREEK CAPITAL LETTER UPSILON WITH VARIA */
+  {0x1FEB, 0x1F7B},  /* GREEK CAPITAL LETTER UPSILON WITH OXIA */
+  {0x1FEC, 0x1FE5},  /* GREEK CAPITAL LETTER RHO WITH DASIA */
+  {0x1FF8, 0x1F78},  /* GREEK CAPITAL LETTER OMICRON WITH VARIA */
+  {0x1FF9, 0x1F79},  /* GREEK CAPITAL LETTER OMICRON WITH OXIA */
+  {0x1FFA, 0x1F7C},  /* GREEK CAPITAL LETTER OMEGA WITH VARIA */
+  {0x1FFB, 0x1F7D},  /* GREEK CAPITAL LETTER OMEGA WITH OXIA */
+  {0x1FFC, 0x1FF3},  /* GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI */
+  {0x2160, 0x2170},  /* ROMAN NUMERAL ONE */
+  {0x2161, 0x2171},  /* ROMAN NUMERAL TWO */
+  {0x2162, 0x2172},  /* ROMAN NUMERAL THREE */
+  {0x2163, 0x2173},  /* ROMAN NUMERAL FOUR */
+  {0x2164, 0x2174},  /* ROMAN NUMERAL FIVE */
+  {0x2165, 0x2175},  /* ROMAN NUMERAL SIX */
+  {0x2166, 0x2176},  /* ROMAN NUMERAL SEVEN */
+  {0x2167, 0x2177},  /* ROMAN NUMERAL EIGHT */
+  {0x2168, 0x2178},  /* ROMAN NUMERAL NINE */
+  {0x2169, 0x2179},  /* ROMAN NUMERAL TEN */
+  {0x216A, 0x217A},  /* ROMAN NUMERAL ELEVEN */
+  {0x216B, 0x217B},  /* ROMAN NUMERAL TWELVE */
+  {0x216C, 0x217C},  /* ROMAN NUMERAL FIFTY */
+  {0x216D, 0x217D},  /* ROMAN NUMERAL ONE HUNDRED */
+  {0x216E, 0x217E},  /* ROMAN NUMERAL FIVE HUNDRED */
+  {0x216F, 0x217F},  /* ROMAN NUMERAL ONE THOUSAND */
+  {0x24B6, 0x24D0},  /* CIRCLED LATIN CAPITAL LETTER A */
+  {0x24B7, 0x24D1},  /* CIRCLED LATIN CAPITAL LETTER B */
+  {0x24B8, 0x24D2},  /* CIRCLED LATIN CAPITAL LETTER C */
+  {0x24B9, 0x24D3},  /* CIRCLED LATIN CAPITAL LETTER D */
+  {0x24BA, 0x24D4},  /* CIRCLED LATIN CAPITAL LETTER E */
+  {0x24BB, 0x24D5},  /* CIRCLED LATIN CAPITAL LETTER F */
+  {0x24BC, 0x24D6},  /* CIRCLED LATIN CAPITAL LETTER G */
+  {0x24BD, 0x24D7},  /* CIRCLED LATIN CAPITAL LETTER H */
+  {0x24BE, 0x24D8},  /* CIRCLED LATIN CAPITAL LETTER I */
+  {0x24BF, 0x24D9},  /* CIRCLED LATIN CAPITAL LETTER J */
+  {0x24C0, 0x24DA},  /* CIRCLED LATIN CAPITAL LETTER K */
+  {0x24C1, 0x24DB},  /* CIRCLED LATIN CAPITAL LETTER L */
+  {0x24C2, 0x24DC},  /* CIRCLED LATIN CAPITAL LETTER M */
+  {0x24C3, 0x24DD},  /* CIRCLED LATIN CAPITAL LETTER N */
+  {0x24C4, 0x24DE},  /* CIRCLED LATIN CAPITAL LETTER O */
+  {0x24C5, 0x24DF},  /* CIRCLED LATIN CAPITAL LETTER P */
+  {0x24C6, 0x24E0},  /* CIRCLED LATIN CAPITAL LETTER Q */
+  {0x24C7, 0x24E1},  /* CIRCLED LATIN CAPITAL LETTER R */
+  {0x24C8, 0x24E2},  /* CIRCLED LATIN CAPITAL LETTER S */
+  {0x24C9, 0x24E3},  /* CIRCLED LATIN CAPITAL LETTER T */
+  {0x24CA, 0x24E4},  /* CIRCLED LATIN CAPITAL LETTER U */
+  {0x24CB, 0x24E5},  /* CIRCLED LATIN CAPITAL LETTER V */
+  {0x24CC, 0x24E6},  /* CIRCLED LATIN CAPITAL LETTER W */
+  {0x24CD, 0x24E7},  /* CIRCLED LATIN CAPITAL LETTER X */
+  {0x24CE, 0x24E8},  /* CIRCLED LATIN CAPITAL LETTER Y */
+  {0x24CF, 0x24E9},  /* CIRCLED LATIN CAPITAL LETTER Z */
+  {0xFF21, 0xFF41},  /* FULLWIDTH LATIN CAPITAL LETTER A */
+  {0xFF22, 0xFF42},  /* FULLWIDTH LATIN CAPITAL LETTER B */
+  {0xFF23, 0xFF43},  /* FULLWIDTH LATIN CAPITAL LETTER C */
+  {0xFF24, 0xFF44},  /* FULLWIDTH LATIN CAPITAL LETTER D */
+  {0xFF25, 0xFF45},  /* FULLWIDTH LATIN CAPITAL LETTER E */
+  {0xFF26, 0xFF46},  /* FULLWIDTH LATIN CAPITAL LETTER F */
+  {0xFF27, 0xFF47},  /* FULLWIDTH LATIN CAPITAL LETTER G */
+  {0xFF28, 0xFF48},  /* FULLWIDTH LATIN CAPITAL LETTER H */
+  {0xFF29, 0xFF49},  /* FULLWIDTH LATIN CAPITAL LETTER I */
+  {0xFF2A, 0xFF4A},  /* FULLWIDTH LATIN CAPITAL LETTER J */
+  {0xFF2B, 0xFF4B},  /* FULLWIDTH LATIN CAPITAL LETTER K */
+  {0xFF2C, 0xFF4C},  /* FULLWIDTH LATIN CAPITAL LETTER L */
+  {0xFF2D, 0xFF4D},  /* FULLWIDTH LATIN CAPITAL LETTER M */
+  {0xFF2E, 0xFF4E},  /* FULLWIDTH LATIN CAPITAL LETTER N */
+  {0xFF2F, 0xFF4F},  /* FULLWIDTH LATIN CAPITAL LETTER O */
+  {0xFF30, 0xFF50},  /* FULLWIDTH LATIN CAPITAL LETTER P */
+  {0xFF31, 0xFF51},  /* FULLWIDTH LATIN CAPITAL LETTER Q */
+  {0xFF32, 0xFF52},  /* FULLWIDTH LATIN CAPITAL LETTER R */
+  {0xFF33, 0xFF53},  /* FULLWIDTH LATIN CAPITAL LETTER S */
+  {0xFF34, 0xFF54},  /* FULLWIDTH LATIN CAPITAL LETTER T */
+  {0xFF35, 0xFF55},  /* FULLWIDTH LATIN CAPITAL LETTER U */
+  {0xFF36, 0xFF56},  /* FULLWIDTH LATIN CAPITAL LETTER V */
+  {0xFF37, 0xFF57},  /* FULLWIDTH LATIN CAPITAL LETTER W */
+  {0xFF38, 0xFF58},  /* FULLWIDTH LATIN CAPITAL LETTER X */
+  {0xFF39, 0xFF59},  /* FULLWIDTH LATIN CAPITAL LETTER Y */
+  {0xFF3A, 0xFF5A}   /* FULLWIDTH LATIN CAPITAL LETTER Z */
+};
+/* *INDENT-ON* */
diff --git a/src/chrtrans/cp1250_uni.tbl b/src/chrtrans/cp1250_uni.tbl
new file mode 100644
index 00000000..8a19d555
--- /dev/null
+++ b/src/chrtrans/cp1250_uni.tbl
@@ -0,0 +1,172 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Mwindows-1250
+
+#Name as a Display Charset (used on Options screen)
+OEastern European (windows-1250)
+
+#Codepage number
+C1250
+
+#
+#    Name:     cp1250 to Unicode table
+#    Unicode version: 2.0
+#    Table version: 2.01
+#    Table format:  Format A
+#    Date:          04/15/98
+#
+#    Contact:       cpxlate@microsoft.com
+#
+#    General notes: none
+#
+#    Format: Three tab-separated columns
+#        Column #1 is the cp1250 code (in hex)
+#        Column #2 is the Unicode (in hex as 0xXXXX)
+#        Column #3 is the Unicode name (follows a comment sign, '#')
+#
+#    The entries are in cp1250 order
+#
+##################
+# Lines with more than one Unicode (U+XXXX) value contain additional
+# replacement mappings added for lynx. - kw
+
+0x20-0x7e       idem
+#
+0x80	U+20AC	#EURO SIGN
+0x81	      	#UNDEFINED
+0x82	U+201A	#SINGLE LOW-9 QUOTATION MARK
+0x83	      	#UNDEFINED
+0x84	U+201E	#DOUBLE LOW-9 QUOTATION MARK
+0x85	U+2026	#HORIZONTAL ELLIPSIS
+0x86	U+2020	#DAGGER
+0x87	U+2021	#DOUBLE DAGGER
+0x88	      	#UNDEFINED
+0x89	U+2030	#PER MILLE SIGN
+0x8A	U+0160	U+0428	#LATIN CAPITAL LETTER S WITH CARON
+0x8B	U+2039	#SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+0x8C	U+015A	#LATIN CAPITAL LETTER S WITH ACUTE
+0x8D	U+0164	#LATIN CAPITAL LETTER T WITH CARON
+0x8E	U+017D	U+0416	#LATIN CAPITAL LETTER Z WITH CARON
+0x8F	U+0179	#LATIN CAPITAL LETTER Z WITH ACUTE
+0x90	      	#UNDEFINED
+0x91	U+2018	#LEFT SINGLE QUOTATION MARK
+0x92	U+2019	#RIGHT SINGLE QUOTATION MARK
+0x93	U+201C	#LEFT DOUBLE QUOTATION MARK
+0x94	U+201D	#RIGHT DOUBLE QUOTATION MARK
+0x95	U+2022	#BULLET
+0x96	U+2013	#EN DASH
+0x97	U+2014	#EM DASH
+0x98	      	#UNDEFINED
+0x99	U+2122	#TRADE MARK SIGN
+0x9A	U+0161	U+0448	#LATIN SMALL LETTER S WITH CARON
+0x9B	U+203A	#SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0x9C	U+015B	#LATIN SMALL LETTER S WITH ACUTE
+0x9D	U+0165	#LATIN SMALL LETTER T WITH CARON
+0x9E	U+017E	U+0436	#LATIN SMALL LETTER Z WITH CARON
+0x9F	U+017A	#LATIN SMALL LETTER Z WITH ACUTE
+0xA0	U+00A0	#NO-BREAK SPACE
+0xA1	U+02C7	U+030c	#CARON
+0xA2	U+02D8	U+0306	#BREVE
+0xA3	U+0141	#LATIN CAPITAL LETTER L WITH STROKE
+0xA4	U+00A4	#CURRENCY SIGN
+0xA5	U+0104	#LATIN CAPITAL LETTER A WITH OGONEK
+0xA6	U+00A6	#BROKEN BAR
+0xA7	U+00A7	#SECTION SIGN
+0xA8	U+00A8	U+0308	#DIAERESIS
+0xA9	U+00A9	#COPYRIGHT SIGN
+0xAA	U+015E	#LATIN CAPITAL LETTER S WITH CEDILLA
+0xAB	U+00AB	#LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC	U+00AC	#NOT SIGN
+0xAD	U+00AD	#SOFT HYPHEN
+0xAE	U+00AE	#REGISTERED SIGN
+0xAF	U+017B	#LATIN CAPITAL LETTER Z WITH DOT ABOVE
+0xB0	U+00B0	U+030a	#DEGREE SIGN
+0xB1	U+00B1	#PLUS-MINUS SIGN
+0xB2	U+02DB	U+0328	#OGONEK
+0xB3	U+0142	#LATIN SMALL LETTER L WITH STROKE
+0xB4	U+00B4	#ACUTE ACCENT
+0xB5	U+00B5	U+03bc	#MICRO SIGN
+0xB6	U+00B6	#PILCROW SIGN
+0xB7	U+00B7	#MIDDLE DOT
+0xB8	U+00B8	U+0327	#CEDILLA
+0xB9	U+0105	#LATIN SMALL LETTER A WITH OGONEK
+0xBA	U+015F	#LATIN SMALL LETTER S WITH CEDILLA
+0xBB	U+00BB	#RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC	U+013D	#LATIN CAPITAL LETTER L WITH CARON
+0xBD	U+02DD	U+030b	#DOUBLE ACUTE ACCENT
+0xBE	U+013E	#LATIN SMALL LETTER L WITH CARON
+0xBF	U+017C	#LATIN SMALL LETTER Z WITH DOT ABOVE
+0xC0	U+0154	#LATIN CAPITAL LETTER R WITH ACUTE
+0xC1	U+00C1	#LATIN CAPITAL LETTER A WITH ACUTE
+0xC2	U+00C2	#LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xC3	U+0102	#LATIN CAPITAL LETTER A WITH BREVE
+0xC4	U+00C4	#LATIN CAPITAL LETTER A WITH DIAERESIS
+0xC5	U+0139	#LATIN CAPITAL LETTER L WITH ACUTE
+0xC6	U+0106	#LATIN CAPITAL LETTER C WITH ACUTE
+0xC7	U+00C7	#LATIN CAPITAL LETTER C WITH CEDILLA
+0xC8	U+010C	U+0427	#	LATIN CAPITAL LETTER C WITH CARON
+0xC9	U+00C9	#LATIN CAPITAL LETTER E WITH ACUTE
+0xCA	U+0118	#LATIN CAPITAL LETTER E WITH OGONEK
+0xCB	U+00CB	#LATIN CAPITAL LETTER E WITH DIAERESIS
+0xCC	U+011A	#LATIN CAPITAL LETTER E WITH CARON
+0xCD	U+00CD	#LATIN CAPITAL LETTER I WITH ACUTE
+0xCE	U+00CE	#LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xCF	U+010E	#LATIN CAPITAL LETTER D WITH CARON
+0xD0	U+0110	#LATIN CAPITAL LETTER D WITH STROKE
+0xD1	U+0143	#LATIN CAPITAL LETTER N WITH ACUTE
+0xD2	U+0147	#LATIN CAPITAL LETTER N WITH CARON
+0xD3	U+00D3	#LATIN CAPITAL LETTER O WITH ACUTE
+0xD4	U+00D4	#LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xD5	U+0150	#LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
+0xD6	U+00D6	#LATIN CAPITAL LETTER O WITH DIAERESIS
+0xD7	U+00D7	#MULTIPLICATION SIGN
+0xD8	U+0158	#LATIN CAPITAL LETTER R WITH CARON
+0xD9	U+016E	#LATIN CAPITAL LETTER U WITH RING ABOVE
+0xDA	U+00DA	#LATIN CAPITAL LETTER U WITH ACUTE
+0xDB	U+0170	#LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
+0xDC	U+00DC	#LATIN CAPITAL LETTER U WITH DIAERESIS
+0xDD	U+00DD	#LATIN CAPITAL LETTER Y WITH ACUTE
+0xDE	U+0162	#LATIN CAPITAL LETTER T WITH CEDILLA
+0xDF	U+00DF	#LATIN SMALL LETTER SHARP S
+0xE0	U+0155	#LATIN SMALL LETTER R WITH ACUTE
+0xE1	U+00E1	#LATIN SMALL LETTER A WITH ACUTE
+0xE2	U+00E2	#LATIN SMALL LETTER A WITH CIRCUMFLEX
+0xE3	U+0103	#LATIN SMALL LETTER A WITH BREVE
+0xE4	U+00E4	#LATIN SMALL LETTER A WITH DIAERESIS
+0xE5	U+013A	#LATIN SMALL LETTER L WITH ACUTE
+0xE6	U+0107	#LATIN SMALL LETTER C WITH ACUTE
+0xE7	U+00E7	#LATIN SMALL LETTER C WITH CEDILLA
+0xE8	U+010D	U+02a7	U+0447	#	LATIN SMALL LETTER C WITH CARON
+0xE9	U+00E9	#LATIN SMALL LETTER E WITH ACUTE
+0xEA	U+0119	#LATIN SMALL LETTER E WITH OGONEK
+0xEB	U+00EB	#LATIN SMALL LETTER E WITH DIAERESIS
+0xEC	U+011B	#LATIN SMALL LETTER E WITH CARON
+0xED	U+00ED	#LATIN SMALL LETTER I WITH ACUTE
+0xEE	U+00EE	#LATIN SMALL LETTER I WITH CIRCUMFLEX
+0xEF	U+010F	#LATIN SMALL LETTER D WITH CARON
+0xF0	U+0111	#LATIN SMALL LETTER D WITH STROKE
+0xF1	U+0144	#LATIN SMALL LETTER N WITH ACUTE
+0xF2	U+0148	#LATIN SMALL LETTER N WITH CARON
+0xF3	U+00F3	#LATIN SMALL LETTER O WITH ACUTE
+0xF4	U+00F4	#LATIN SMALL LETTER O WITH CIRCUMFLEX
+0xF5	U+0151	#LATIN SMALL LETTER O WITH DOUBLE ACUTE
+0xF6	U+00F6	#LATIN SMALL LETTER O WITH DIAERESIS
+0xF7	U+00F7	#DIVISION SIGN
+0xF8	U+0159	#LATIN SMALL LETTER R WITH CARON
+0xF9	U+016F	#LATIN SMALL LETTER U WITH RING ABOVE
+0xFA	U+00FA	#LATIN SMALL LETTER U WITH ACUTE
+0xFB	U+0171	#LATIN SMALL LETTER U WITH DOUBLE ACUTE
+0xFC	U+00FC	#LATIN SMALL LETTER U WITH DIAERESIS
+0xFD	U+00FD	#LATIN SMALL LETTER Y WITH ACUTE
+0xFE	U+0163	#LATIN SMALL LETTER T WITH CEDILLA
+0xFF	U+02D9	U+0307	U+0387	#DOT ABOVE
+
+U+2218 " \260 "		# RING OPERATOR
+U+2219 " \225 "		# BULLET OPERATOR
+U+2297 "(\327)"		# CIRCLED TIMES
+U+2299 "(\267)"		# CIRCLED DOT OPERATOR
+U+229A "(\260)"		# CIRCLED RING OPERATOR
+U+22A0 "[\327]"		# SQUARED TIMES
+U+22A1 "[\267]"		# SQUARED DOT OPERATOR
+U+22C5 " \267 "		# DOT OPERATOR
diff --git a/src/chrtrans/cp1251_uni.tbl b/src/chrtrans/cp1251_uni.tbl
new file mode 100644
index 00000000..0d928f9c
--- /dev/null
+++ b/src/chrtrans/cp1251_uni.tbl
@@ -0,0 +1,161 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Mwindows-1251
+
+#Name as a Display Charset (used on Options screen)
+OCyrillic (windows-1251)
+
+#Codepage number
+C1251
+
+#
+#    Name:     cp1251 to Unicode table
+#    Unicode version: 2.0
+#    Table version: 2.01
+#    Table format:  Format A
+#    Date:          04/15/98
+#
+#    Contact:       cpxlate@microsoft.com
+#
+#    General notes: none
+#
+#    Format: Three tab-separated columns
+#        Column #1 is the cp1251 code (in hex)
+#        Column #2 is the Unicode (in hex as 0xXXXX)
+#        Column #3 is the Unicode name (follows a comment sign, '#')
+#
+#    The entries are in cp1251 order
+#
+##################
+
+0x20-0x7e       idem
+#
+0x80	U+0402	#CYRILLIC CAPITAL LETTER DJE
+0x81	U+0403	#CYRILLIC CAPITAL LETTER GJE
+0x82	U+201A	#SINGLE LOW-9 QUOTATION MARK
+0x83	U+0453	#CYRILLIC SMALL LETTER GJE
+0x84	U+201E	#DOUBLE LOW-9 QUOTATION MARK
+0x85	U+2026	#HORIZONTAL ELLIPSIS
+0x86	U+2020	#DAGGER
+0x87	U+2021	#DOUBLE DAGGER
+0x88	U+20AC	#EURO SIGN
+0x89	U+2030	#PER MILLE SIGN
+0x8A	U+0409	#CYRILLIC CAPITAL LETTER LJE
+0x8B	U+2039	#SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+0x8C	U+040A	#CYRILLIC CAPITAL LETTER NJE
+0x8D	U+040C	#CYRILLIC CAPITAL LETTER KJE
+0x8E	U+040B	#CYRILLIC CAPITAL LETTER TSHE
+0x8F	U+040F	#CYRILLIC CAPITAL LETTER DZHE
+0x90	U+0452	#CYRILLIC SMALL LETTER DJE
+0x91	U+2018	#LEFT SINGLE QUOTATION MARK
+0x92	U+2019	#RIGHT SINGLE QUOTATION MARK
+0x93	U+201C	#LEFT DOUBLE QUOTATION MARK
+0x94	U+201D	#RIGHT DOUBLE QUOTATION MARK
+0x95	U+2022	#BULLET
+0x96	U+2013	#EN DASH
+0x97	U+2014	#EM DASH
+0x98	      	#UNDEFINED
+0x99	U+2122	#TRADE MARK SIGN
+0x9A	U+0459	#CYRILLIC SMALL LETTER LJE
+0x9B	U+203A	#SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0x9C	U+045A	#CYRILLIC SMALL LETTER NJE
+0x9D	U+045C	#CYRILLIC SMALL LETTER KJE
+0x9E	U+045B	#CYRILLIC SMALL LETTER TSHE
+0x9F	U+045F	#CYRILLIC SMALL LETTER DZHE
+0xA0	U+00A0	#NO-BREAK SPACE
+0xA1	U+040E	#CYRILLIC CAPITAL LETTER SHORT U
+0xA2	U+045E	#CYRILLIC SMALL LETTER SHORT U
+0xA3	U+0408	#CYRILLIC CAPITAL LETTER JE
+0xA4	U+00A4	#CURRENCY SIGN
+0xA5	U+0490	#CYRILLIC CAPITAL LETTER GHE WITH UPTURN
+0xA6	U+00A6	#BROKEN BAR
+0xA7	U+00A7	#SECTION SIGN
+0xA8	U+0401	#CYRILLIC CAPITAL LETTER IO
+0xA9	U+00A9	#COPYRIGHT SIGN
+0xAA	U+0404	#CYRILLIC CAPITAL LETTER UKRAINIAN IE
+0xAB	U+00AB	#LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC	U+00AC	#NOT SIGN
+0xAD	U+00AD	#SOFT HYPHEN
+0xAE	U+00AE	#REGISTERED SIGN
+0xAF	U+0407	#CYRILLIC CAPITAL LETTER YI
+0xB0	U+00B0	#DEGREE SIGN
+0xB1	U+00B1	#PLUS-MINUS SIGN
+0xB2	U+0406	#CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+0xB3	U+0456	#CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+0xB4	U+0491	#CYRILLIC SMALL LETTER GHE WITH UPTURN
+0xB5	U+00B5	#MICRO SIGN
+0xB6	U+00B6	#PILCROW SIGN
+0xB7	U+00B7	#MIDDLE DOT
+0xB8	U+0451	#CYRILLIC SMALL LETTER IO
+0xB9	U+2116	#NUMERO SIGN
+0xBA	U+0454	#CYRILLIC SMALL LETTER UKRAINIAN IE
+0xBB	U+00BB	#RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC	U+0458	#CYRILLIC SMALL LETTER JE
+0xBD	U+0405	#CYRILLIC CAPITAL LETTER DZE
+0xBE	U+0455	#CYRILLIC SMALL LETTER DZE
+0xBF	U+0457	#CYRILLIC SMALL LETTER YI
+0xC0	U+0410	#CYRILLIC CAPITAL LETTER A
+0xC1	U+0411	#CYRILLIC CAPITAL LETTER BE
+0xC2	U+0412	#CYRILLIC CAPITAL LETTER VE
+0xC3	U+0413	#CYRILLIC CAPITAL LETTER GHE
+0xC4	U+0414	#CYRILLIC CAPITAL LETTER DE
+0xC5	U+0415	#CYRILLIC CAPITAL LETTER IE
+0xC6	U+0416	#CYRILLIC CAPITAL LETTER ZHE
+0xC7	U+0417	#CYRILLIC CAPITAL LETTER ZE
+0xC8	U+0418	#CYRILLIC CAPITAL LETTER I
+0xC9	U+0419	#CYRILLIC CAPITAL LETTER SHORT I
+0xCA	U+041A	#CYRILLIC CAPITAL LETTER KA
+0xCB	U+041B	#CYRILLIC CAPITAL LETTER EL
+0xCC	U+041C	#CYRILLIC CAPITAL LETTER EM
+0xCD	U+041D	#CYRILLIC CAPITAL LETTER EN
+0xCE	U+041E	#CYRILLIC CAPITAL LETTER O
+0xCF	U+041F	#CYRILLIC CAPITAL LETTER PE
+0xD0	U+0420	#CYRILLIC CAPITAL LETTER ER
+0xD1	U+0421	#CYRILLIC CAPITAL LETTER ES
+0xD2	U+0422	#CYRILLIC CAPITAL LETTER TE
+0xD3	U+0423	#CYRILLIC CAPITAL LETTER U
+0xD4	U+0424	#CYRILLIC CAPITAL LETTER EF
+0xD5	U+0425	#CYRILLIC CAPITAL LETTER HA
+0xD6	U+0426	#CYRILLIC CAPITAL LETTER TSE
+0xD7	U+0427	#CYRILLIC CAPITAL LETTER CHE
+0xD8	U+0428	#CYRILLIC CAPITAL LETTER SHA
+0xD9	U+0429	#CYRILLIC CAPITAL LETTER SHCHA
+0xDA	U+042A	#CYRILLIC CAPITAL LETTER HARD SIGN
+0xDB	U+042B	#CYRILLIC CAPITAL LETTER YERU
+0xDC	U+042C	#CYRILLIC CAPITAL LETTER SOFT SIGN
+0xDD	U+042D	#CYRILLIC CAPITAL LETTER E
+0xDE	U+042E	#CYRILLIC CAPITAL LETTER YU
+0xDF	U+042F	#CYRILLIC CAPITAL LETTER YA
+0xE0	U+0430	#CYRILLIC SMALL LETTER A
+0xE1	U+0431	#CYRILLIC SMALL LETTER BE
+0xE2	U+0432	#CYRILLIC SMALL LETTER VE
+0xE3	U+0433	#CYRILLIC SMALL LETTER GHE
+0xE4	U+0434	#CYRILLIC SMALL LETTER DE
+0xE5	U+0435	#CYRILLIC SMALL LETTER IE
+0xE6	U+0436	#CYRILLIC SMALL LETTER ZHE
+0xE7	U+0437	#CYRILLIC SMALL LETTER ZE
+0xE8	U+0438	#CYRILLIC SMALL LETTER I
+0xE9	U+0439	#CYRILLIC SMALL LETTER SHORT I
+0xEA	U+043A	#CYRILLIC SMALL LETTER KA
+0xEB	U+043B	#CYRILLIC SMALL LETTER EL
+0xEC	U+043C	#CYRILLIC SMALL LETTER EM
+0xED	U+043D	#CYRILLIC SMALL LETTER EN
+0xEE	U+043E	#CYRILLIC SMALL LETTER O
+0xEF	U+043F	#CYRILLIC SMALL LETTER PE
+0xF0	U+0440	#CYRILLIC SMALL LETTER ER
+0xF1	U+0441	#CYRILLIC SMALL LETTER ES
+0xF2	U+0442	#CYRILLIC SMALL LETTER TE
+0xF3	U+0443	#CYRILLIC SMALL LETTER U
+0xF4	U+0444	#CYRILLIC SMALL LETTER EF
+0xF5	U+0445	#CYRILLIC SMALL LETTER HA
+0xF6	U+0446	#CYRILLIC SMALL LETTER TSE
+0xF7	U+0447	#CYRILLIC SMALL LETTER CHE
+0xF8	U+0448	#CYRILLIC SMALL LETTER SHA
+0xF9	U+0449	#CYRILLIC SMALL LETTER SHCHA
+0xFA	U+044A	#CYRILLIC SMALL LETTER HARD SIGN
+0xFB	U+044B	#CYRILLIC SMALL LETTER YERU
+0xFC	U+044C	#CYRILLIC SMALL LETTER SOFT SIGN
+0xFD	U+044D	#CYRILLIC SMALL LETTER E
+0xFE	U+044E	#CYRILLIC SMALL LETTER YU
+0xFF	U+044F	#CYRILLIC SMALL LETTER YA
diff --git a/src/chrtrans/cp1252_uni.tbl b/src/chrtrans/cp1252_uni.tbl
new file mode 100644
index 00000000..50ce967c
--- /dev/null
+++ b/src/chrtrans/cp1252_uni.tbl
@@ -0,0 +1,177 @@
+# This file has been modified for lynx (see README.tables)
+
+#Shall this become the "default" translation?
+#There has to be exactly one table marked as "default".
+D0
+#
+#The MIME name of this charset.
+Mwindows-1252
+
+#Name as a Display Charset (used on Options screen)
+OWestern (windows-1252)
+
+#Codepage number
+C1252
+
+#
+#    Name:     cp1252 to Unicode table
+#    Unicode version: 2.0
+#    Table version: 2.01
+#    Table format:  Format A
+#    Date:          04/15/98
+#
+#    Contact:       cpxlate@microsoft.com
+#
+#    General notes: none
+#
+#    Format: Three tab-separated columns
+#        Column #1 is the cp1252 code (in hex)
+#        Column #2 is the Unicode (in hex as 0xXXXX)
+#        Column #3 is the Unicode name (follows a comment sign, '#')
+#
+#    The entries are in cp1252 order
+#
+##################
+# Lines with more than one Unicode (U+XXXX) value contain additional
+# replacement mappings added for lynx. - kw
+
+0x20-0x7e       idem
+#
+0x80	U+20AC	#EURO SIGN
+0x81	      	#UNDEFINED
+0x82	U+201A	#SINGLE LOW-9 QUOTATION MARK
+0x83	U+0192	#LATIN SMALL LETTER F WITH HOOK
+0x84	U+201E	#DOUBLE LOW-9 QUOTATION MARK
+0x85	U+2026	#HORIZONTAL ELLIPSIS
+0x86	U+2020	#DAGGER
+0x87	U+2021	#DOUBLE DAGGER
+0x88	U+02C6	U+0302	#MODIFIER LETTER CIRCUMFLEX ACCENT
+0x89	U+2030	#PER MILLE SIGN
+0x8A	U+0160	#LATIN CAPITAL LETTER S WITH CARON
+0x8B	U+2039	#SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+0x8C	U+0152	#LATIN CAPITAL LIGATURE OE
+0x8D	      	#UNDEFINED
+0x8E	U+017D	#LATIN CAPITAL LETTER Z WITH CARON
+0x8F	      	#UNDEFINED
+0x90	      	#UNDEFINED
+0x91	U+2018	#LEFT SINGLE QUOTATION MARK
+0x92	U+2019	#RIGHT SINGLE QUOTATION MARK
+0x93	U+201C	#LEFT DOUBLE QUOTATION MARK
+0x94	U+201D	U+02dd	U+030b	#RIGHT DOUBLE QUOTATION MARK
+0x95	U+2022	#BULLET
+0x96	U+2013	#EN DASH
+0x97	U+2014	#EM DASH
+0x98	U+02DC	#SMALL TILDE
+0x99	U+2122	#TRADE MARK SIGN
+0x9A	U+0161	#LATIN SMALL LETTER S WITH CARON
+0x9B	U+203A	#SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0x9C	U+0153	#LATIN SMALL LIGATURE OE
+0x9D	      	#UNDEFINED
+0x9E	U+017E	#LATIN SMALL LETTER Z WITH CARON
+0x9F	U+0178	#LATIN CAPITAL LETTER Y WITH DIAERESIS
+0xA0	U+00A0	#NO-BREAK SPACE
+0xA1	U+00A1	#INVERTED EXCLAMATION MARK
+0xA2	U+00A2	#CENT SIGN
+0xA3	U+00A3	#POUND SIGN
+0xA4	U+00A4	#CURRENCY SIGN
+0xA5	U+00A5	#YEN SIGN
+0xA6	U+00A6	#BROKEN BAR
+0xA7	U+00A7	#SECTION SIGN
+0xA8	U+00A8	U+0308	#DIAERESIS
+0xA9	U+00A9	#COPYRIGHT SIGN
+0xAA	U+00AA	#FEMININE ORDINAL INDICATOR
+0xAB	U+00AB	#LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC	U+00AC	#NOT SIGN
+0xAD	U+00AD	#SOFT HYPHEN
+0xAE	U+00AE	#REGISTERED SIGN
+0xAF	U+00AF	U+0304	#MACRON
+0xB0	U+00B0	U+030a	#DEGREE SIGN
+0xB1	U+00B1	#PLUS-MINUS SIGN
+0xB2	U+00B2	#SUPERSCRIPT TWO
+0xB3	U+00B3	#SUPERSCRIPT THREE
+0xB4	U+00B4	#ACUTE ACCENT
+0xB5	U+00B5	U+03bc	#MICRO SIGN
+0xB6	U+00B6	#PILCROW SIGN
+0xB7	U+00B7	U+0307	U+0387	U+2027	#MIDDLE DOT
+0xB8	U+00B8	U+0327	#CEDILLA
+0xB9	U+00B9	#SUPERSCRIPT ONE
+0xBA	U+00BA	#MASCULINE ORDINAL INDICATOR
+0xBB	U+00BB	#RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC	U+00BC	#VULGAR FRACTION ONE QUARTER
+0xBD	U+00BD	#VULGAR FRACTION ONE HALF
+0xBE	U+00BE	#VULGAR FRACTION THREE QUARTERS
+0xBF	U+00BF	#INVERTED QUESTION MARK
+0xC0	U+00C0	#LATIN CAPITAL LETTER A WITH GRAVE
+0xC1	U+00C1	#LATIN CAPITAL LETTER A WITH ACUTE
+0xC2	U+00C2	#LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xC3	U+00C3	#LATIN CAPITAL LETTER A WITH TILDE
+0xC4	U+00C4	#LATIN CAPITAL LETTER A WITH DIAERESIS
+0xC5	U+00C5	#LATIN CAPITAL LETTER A WITH RING ABOVE
+0xC6	U+00C6	#LATIN CAPITAL LETTER AE
+0xC7	U+00C7	#LATIN CAPITAL LETTER C WITH CEDILLA
+0xC8	U+00C8	#LATIN CAPITAL LETTER E WITH GRAVE
+0xC9	U+00C9	#LATIN CAPITAL LETTER E WITH ACUTE
+0xCA	U+00CA	#LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+0xCB	U+00CB	#LATIN CAPITAL LETTER E WITH DIAERESIS
+0xCC	U+00CC	#LATIN CAPITAL LETTER I WITH GRAVE
+0xCD	U+00CD	#LATIN CAPITAL LETTER I WITH ACUTE
+0xCE	U+00CE	#LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xCF	U+00CF	#LATIN CAPITAL LETTER I WITH DIAERESIS
+0xD0	U+00D0	#LATIN CAPITAL LETTER ETH
+0xD1	U+00D1	#LATIN CAPITAL LETTER N WITH TILDE
+0xD2	U+00D2	#LATIN CAPITAL LETTER O WITH GRAVE
+0xD3	U+00D3	#LATIN CAPITAL LETTER O WITH ACUTE
+0xD4	U+00D4	#LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xD5	U+00D5	#LATIN CAPITAL LETTER O WITH TILDE
+0xD6	U+00D6	#LATIN CAPITAL LETTER O WITH DIAERESIS
+0xD7	U+00D7	#MULTIPLICATION SIGN
+0xD8	U+00D8	#LATIN CAPITAL LETTER O WITH STROKE
+0xD9	U+00D9	#LATIN CAPITAL LETTER U WITH GRAVE
+0xDA	U+00DA	#LATIN CAPITAL LETTER U WITH ACUTE
+0xDB	U+00DB	#LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+0xDC	U+00DC	#LATIN CAPITAL LETTER U WITH DIAERESIS
+0xDD	U+00DD	#LATIN CAPITAL LETTER Y WITH ACUTE
+0xDE	U+00DE	#LATIN CAPITAL LETTER THORN
+0xDF	U+00DF	#LATIN SMALL LETTER SHARP S
+0xE0	U+00E0	#LATIN SMALL LETTER A WITH GRAVE
+0xE1	U+00E1	#LATIN SMALL LETTER A WITH ACUTE
+0xE2	U+00E2	#LATIN SMALL LETTER A WITH CIRCUMFLEX
+0xE3	U+00E3	#LATIN SMALL LETTER A WITH TILDE
+0xE4	U+00E4	#LATIN SMALL LETTER A WITH DIAERESIS
+0xE5	U+00E5	#LATIN SMALL LETTER A WITH RING ABOVE
+0xE6	U+00E6	#LATIN SMALL LETTER AE
+0xE7	U+00E7	#LATIN SMALL LETTER C WITH CEDILLA
+0xE8	U+00E8	#LATIN SMALL LETTER E WITH GRAVE
+0xE9	U+00E9	#LATIN SMALL LETTER E WITH ACUTE
+0xEA	U+00EA	#LATIN SMALL LETTER E WITH CIRCUMFLEX
+0xEB	U+00EB	#LATIN SMALL LETTER E WITH DIAERESIS
+0xEC	U+00EC	#LATIN SMALL LETTER I WITH GRAVE
+0xED	U+00ED	#LATIN SMALL LETTER I WITH ACUTE
+0xEE	U+00EE	#LATIN SMALL LETTER I WITH CIRCUMFLEX
+0xEF	U+00EF	#LATIN SMALL LETTER I WITH DIAERESIS
+0xF0	U+00F0	#LATIN SMALL LETTER ETH
+0xF1	U+00F1	#LATIN SMALL LETTER N WITH TILDE
+0xF2	U+00F2	#LATIN SMALL LETTER O WITH GRAVE
+0xF3	U+00F3	#LATIN SMALL LETTER O WITH ACUTE
+0xF4	U+00F4	#LATIN SMALL LETTER O WITH CIRCUMFLEX
+0xF5	U+00F5	#LATIN SMALL LETTER O WITH TILDE
+0xF6	U+00F6	#LATIN SMALL LETTER O WITH DIAERESIS
+0xF7	U+00F7	#DIVISION SIGN
+0xF8	U+00F8	#LATIN SMALL LETTER O WITH STROKE
+0xF9	U+00F9	#LATIN SMALL LETTER U WITH GRAVE
+0xFA	U+00FA	#LATIN SMALL LETTER U WITH ACUTE
+0xFB	U+00FB	#LATIN SMALL LETTER U WITH CIRCUMFLEX
+0xFC	U+00FC	#LATIN SMALL LETTER U WITH DIAERESIS
+0xFD	U+00FD	#LATIN SMALL LETTER Y WITH ACUTE
+0xFE	U+00FE	#LATIN SMALL LETTER THORN
+0xFF	U+00FF	#LATIN SMALL LETTER Y WITH DIAERESIS
+
+U+2218 " \260 "		# RING OPERATOR
+U+2219 " \225 "		# BULLET OPERATOR
+U+221b " ROOT\263 "
+U+2297 "(\327)"		# CIRCLED TIMES
+U+2299 "(\267)"		# CIRCLED DOT OPERATOR
+U+229A "(\260)"		# CIRCLED RING OPERATOR
+U+22A0 "[\327]"		# SQUARED TIMES
+U+22A1 "[\267]"		# SQUARED DOT OPERATOR
+U+22C5 " \267 "		# DOT OPERATOR
diff --git a/src/chrtrans/cp1253_uni.tbl b/src/chrtrans/cp1253_uni.tbl
new file mode 100644
index 00000000..49523d44
--- /dev/null
+++ b/src/chrtrans/cp1253_uni.tbl
@@ -0,0 +1,161 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Mwindows-1253
+
+#Name as a Display Charset (used on Options screen)
+OGreek (windows-1253)
+
+#Codepage number
+C1253
+
+#
+#    Name:     cp1253 to Unicode table
+#    Unicode version: 2.0
+#    Table version: 2.01
+#    Table format:  Format A
+#    Date:          04/15/98
+#
+#    Contact:       cpxlate@microsoft.com
+#
+#    General notes: none
+#
+#    Format: Three tab-separated columns
+#        Column #1 is the cp1253 code (in hex)
+#        Column #2 is the Unicode (in hex as 0xXXXX)
+#        Column #3 is the Unicode name (follows a comment sign, '#')
+#
+#    The entries are in cp1253 order
+#
+##################
+
+0x20-0x7e       idem
+#
+0x80	U+20AC	#EURO SIGN
+0x81		#UNDEFINED
+0x82	U+201A	#SINGLE LOW-9 QUOTATION MARK
+0x83	U+0192	#LATIN SMALL LETTER F WITH HOOK
+0x84	U+201E	#DOUBLE LOW-9 QUOTATION MARK
+0x85	U+2026	#HORIZONTAL ELLIPSIS
+0x86	U+2020	#DAGGER
+0x87	U+2021	#DOUBLE DAGGER
+0x88		#UNDEFINED
+0x89	U+2030	#PER MILLE SIGN
+0x8A		#UNDEFINED
+0x8B	U+2039	#SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+0x8C		#UNDEFINED
+0x8D		#UNDEFINED
+0x8E		#UNDEFINED
+0x8F		#UNDEFINED
+0x90		#UNDEFINED
+0x91	U+2018	U+02bd	#LEFT SINGLE QUOTATION MARK
+0x92	U+2019	U+02bc	#RIGHT SINGLE QUOTATION MARK
+0x93	U+201C	#LEFT DOUBLE QUOTATION MARK
+0x94	U+201D	#RIGHT DOUBLE QUOTATION MARK
+0x95	U+2022	#BULLET
+0x96	U+2013	#EN DASH
+0x97	U+2014	#EM DASH
+0x98		#UNDEFINED
+0x99	U+2122	#TRADE MARK SIGN
+0x9A		#UNDEFINED
+0x9B	U+203A	#SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0x9C		#UNDEFINED
+0x9D		#UNDEFINED
+0x9E		#UNDEFINED
+0x9F		#UNDEFINED
+0xA0	U+00A0	#NO-BREAK SPACE
+0xA1	U+0385	#GREEK DIALYTIKA TONOS
+0xA2	U+0386	#GREEK CAPITAL LETTER ALPHA WITH TONOS
+0xA3	U+00A3	#POUND SIGN
+0xA4	U+00A4	#CURRENCY SIGN
+0xA5	U+00A5	#YEN SIGN
+0xA6	U+00A6	#BROKEN BAR
+0xA7	U+00A7	#SECTION SIGN
+0xA8	U+00A8	#DIAERESIS
+0xA9	U+00A9	#COPYRIGHT SIGN
+0xAA		#UNDEFINED
+0xAB	U+00AB	#LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC	U+00AC	#NOT SIGN
+0xAD	U+00AD	#SOFT HYPHEN
+0xAE	U+00AE	#REGISTERED SIGN
+0xAF	U+2015	#HORIZONTAL BAR
+0xB0	U+00B0	#DEGREE SIGN
+0xB1	U+00B1	#PLUS-MINUS SIGN
+0xB2	U+00B2	#SUPERSCRIPT TWO
+0xB3	U+00B3	#SUPERSCRIPT THREE
+0xB4	U+0384	#GREEK TONOS
+0xB5	U+00B5	#MICRO SIGN
+0xB6	U+00B6	#PILCROW SIGN
+0xB7	U+00B7	#MIDDLE DOT
+0xB8	U+0388	#GREEK CAPITAL LETTER EPSILON WITH TONOS
+0xB9	U+0389	#GREEK CAPITAL LETTER ETA WITH TONOS
+0xBA	U+038A	#GREEK CAPITAL LETTER IOTA WITH TONOS
+0xBB	U+00BB	#RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC	U+038C	#GREEK CAPITAL LETTER OMICRON WITH TONOS
+0xBD	U+00BD	#VULGAR FRACTION ONE HALF
+0xBE	U+038E	#GREEK CAPITAL LETTER UPSILON WITH TONOS
+0xBF	U+038F	#GREEK CAPITAL LETTER OMEGA WITH TONOS
+0xC0	U+0390	#GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+0xC1	U+0391	#GREEK CAPITAL LETTER ALPHA
+0xC2	U+0392	#GREEK CAPITAL LETTER BETA
+0xC3	U+0393	#GREEK CAPITAL LETTER GAMMA
+0xC4	U+0394	#GREEK CAPITAL LETTER DELTA
+0xC5	U+0395	#GREEK CAPITAL LETTER EPSILON
+0xC6	U+0396	#GREEK CAPITAL LETTER ZETA
+0xC7	U+0397	#GREEK CAPITAL LETTER ETA
+0xC8	U+0398	#GREEK CAPITAL LETTER THETA
+0xC9	U+0399	#GREEK CAPITAL LETTER IOTA
+0xCA	U+039A	#GREEK CAPITAL LETTER KAPPA
+0xCB	U+039B	#GREEK CAPITAL LETTER LAMDA
+0xCC	U+039C	#GREEK CAPITAL LETTER MU
+0xCD	U+039D	#GREEK CAPITAL LETTER NU
+0xCE	U+039E	#GREEK CAPITAL LETTER XI
+0xCF	U+039F	#GREEK CAPITAL LETTER OMICRON
+0xD0	U+03A0	#GREEK CAPITAL LETTER PI
+0xD1	U+03A1	#GREEK CAPITAL LETTER RHO
+0xD2		#UNDEFINED
+0xD3	U+03A3	#GREEK CAPITAL LETTER SIGMA
+0xD4	U+03A4	#GREEK CAPITAL LETTER TAU
+0xD5	U+03A5	#GREEK CAPITAL LETTER UPSILON
+0xD6	U+03A6	#GREEK CAPITAL LETTER PHI
+0xD7	U+03A7	#GREEK CAPITAL LETTER CHI
+0xD8	U+03A8	#GREEK CAPITAL LETTER PSI
+0xD9	U+03A9	#GREEK CAPITAL LETTER OMEGA
+0xDA	U+03AA	#GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
+0xDB	U+03AB	#GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
+0xDC	U+03AC	#GREEK SMALL LETTER ALPHA WITH TONOS
+0xDD	U+03AD	#GREEK SMALL LETTER EPSILON WITH TONOS
+0xDE	U+03AE	#GREEK SMALL LETTER ETA WITH TONOS
+0xDF	U+03AF	#GREEK SMALL LETTER IOTA WITH TONOS
+0xE0	U+03B0	#GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+0xE1	U+03B1	#GREEK SMALL LETTER ALPHA
+0xE2	U+03B2	#GREEK SMALL LETTER BETA
+0xE3	U+03B3	#GREEK SMALL LETTER GAMMA
+0xE4	U+03B4	#GREEK SMALL LETTER DELTA
+0xE5	U+03B5	#GREEK SMALL LETTER EPSILON
+0xE6	U+03B6	#GREEK SMALL LETTER ZETA
+0xE7	U+03B7	#GREEK SMALL LETTER ETA
+0xE8	U+03B8	#GREEK SMALL LETTER THETA
+0xE9	U+03B9	#GREEK SMALL LETTER IOTA
+0xEA	U+03BA	#GREEK SMALL LETTER KAPPA
+0xEB	U+03BB	#GREEK SMALL LETTER LAMDA
+0xEC	U+03BC	#GREEK SMALL LETTER MU
+0xED	U+03BD	#GREEK SMALL LETTER NU
+0xEE	U+03BE	#GREEK SMALL LETTER XI
+0xEF	U+03BF	#GREEK SMALL LETTER OMICRON
+0xF0	U+03C0	#GREEK SMALL LETTER PI
+0xF1	U+03C1	#GREEK SMALL LETTER RHO
+0xF2	U+03C2	#GREEK SMALL LETTER FINAL SIGMA
+0xF3	U+03C3	#GREEK SMALL LETTER SIGMA
+0xF4	U+03C4	#GREEK SMALL LETTER TAU
+0xF5	U+03C5	#GREEK SMALL LETTER UPSILON
+0xF6	U+03C6	#GREEK SMALL LETTER PHI
+0xF7	U+03C7	#GREEK SMALL LETTER CHI
+0xF8	U+03C8	#GREEK SMALL LETTER PSI
+0xF9	U+03C9	#GREEK SMALL LETTER OMEGA
+0xFA	U+03CA	#GREEK SMALL LETTER IOTA WITH DIALYTIKA
+0xFB	U+03CB	#GREEK SMALL LETTER UPSILON WITH DIALYTIKA
+0xFC	U+03CC	#GREEK SMALL LETTER OMICRON WITH TONOS
+0xFD	U+03CD	#GREEK SMALL LETTER UPSILON WITH TONOS
+0xFE	U+03CE	#GREEK SMALL LETTER OMEGA WITH TONOS
+0xFF		#UNDEFINED
diff --git a/src/chrtrans/cp1255_uni.tbl b/src/chrtrans/cp1255_uni.tbl
new file mode 100644
index 00000000..3f0af9e1
--- /dev/null
+++ b/src/chrtrans/cp1255_uni.tbl
@@ -0,0 +1,161 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Mwindows-1255
+
+#Name as a Display Charset (used on Options screen).
+OHebrew (windows-1255)
+
+#Codepage number
+C1255
+
+#
+#    Name:     cp1255 to Unicode table
+#    Unicode version: 2.0
+#    Table version: 2.01
+#    Table format:  Format A
+#    Date:          04/15/98
+#
+#    Contact:       cpxlate@microsoft.com
+#
+#    General notes: none
+#
+#    Format: Three tab-separated columns
+#        Column #1 is the cp1255 code (in hex)
+#        Column #2 is the Unicode (in hex as 0xXXXX)
+#        Column #3 is the Unicode name (follows a comment sign, '#')
+#
+#    The entries are in cp1255 order
+#
+##################
+
+0x20-0x7e       idem
+#
+0x80	U+20AC	#EURO SIGN
+0x81	      	#UNDEFINED
+0x82	U+201A	#SINGLE LOW-9 QUOTATION MARK
+0x83	U+0192	#LATIN SMALL LETTER F WITH HOOK
+0x84	U+201E	#DOUBLE LOW-9 QUOTATION MARK
+0x85	U+2026	#HORIZONTAL ELLIPSIS
+0x86	U+2020	#DAGGER
+0x87	U+2021	#DOUBLE DAGGER
+0x88	U+02C6	#MODIFIER LETTER CIRCUMFLEX ACCENT
+0x89	U+2030	#PER MILLE SIGN
+0x8A	      	#UNDEFINED
+0x8B	U+2039	#SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+0x8C	      	#UNDEFINED
+0x8D	      	#UNDEFINED
+0x8E	      	#UNDEFINED
+0x8F	      	#UNDEFINED
+0x90	      	#UNDEFINED
+0x91	U+2018	#LEFT SINGLE QUOTATION MARK
+0x92	U+2019	#RIGHT SINGLE QUOTATION MARK
+0x93	U+201C	#LEFT DOUBLE QUOTATION MARK
+0x94	U+201D	#RIGHT DOUBLE QUOTATION MARK
+0x95	U+2022	#BULLET
+0x96	U+2013	#EN DASH
+0x97	U+2014	#EM DASH
+0x98	U+02DC	#SMALL TILDE
+0x99	U+2122	#TRADE MARK SIGN
+0x9A	      	#UNDEFINED
+0x9B	U+203A	#SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0x9C	      	#UNDEFINED
+0x9D	      	#UNDEFINED
+0x9E	      	#UNDEFINED
+0x9F	      	#UNDEFINED
+0xA0	U+00A0	#NO-BREAK SPACE
+0xA1	U+00A1	#INVERTED EXCLAMATION MARK
+0xA2	U+00A2	#CENT SIGN
+0xA3	U+00A3	#POUND SIGN
+0xA4	U+20AA	#NEW SHEQEL SIGN
+0xA5	U+00A5	#YEN SIGN
+0xA6	U+00A6	#BROKEN BAR
+0xA7	U+00A7	#SECTION SIGN
+0xA8	U+00A8	#DIAERESIS
+0xA9	U+00A9	#COPYRIGHT SIGN
+0xAA	U+00D7	#MULTIPLICATION SIGN
+0xAB	U+00AB	#LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC	U+00AC	#NOT SIGN
+0xAD	U+00AD	#SOFT HYPHEN
+0xAE	U+00AE	#REGISTERED SIGN
+0xAF	U+00AF	#MACRON
+0xB0	U+00B0	#DEGREE SIGN
+0xB1	U+00B1	#PLUS-MINUS SIGN
+0xB2	U+00B2	#SUPERSCRIPT TWO
+0xB3	U+00B3	#SUPERSCRIPT THREE
+0xB4	U+00B4	#ACUTE ACCENT
+0xB5	U+00B5	#MICRO SIGN
+0xB6	U+00B6	#PILCROW SIGN
+0xB7	U+00B7	#MIDDLE DOT
+0xB8	U+00B8	#CEDILLA
+0xB9	U+00B9	#SUPERSCRIPT ONE
+0xBA	U+00F7	#DIVISION SIGN
+0xBB	U+00BB	#RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC	U+00BC	#VULGAR FRACTION ONE QUARTER
+0xBD	U+00BD	#VULGAR FRACTION ONE HALF
+0xBE	U+00BE	#VULGAR FRACTION THREE QUARTERS
+0xBF	U+00BF	#INVERTED QUESTION MARK
+0xC0	U+05B0	#HEBREW POINT SHEVA
+0xC1	U+05B1	#HEBREW POINT HATAF SEGOL
+0xC2	U+05B2	#HEBREW POINT HATAF PATAH
+0xC3	U+05B3	#HEBREW POINT HATAF QAMATS
+0xC4	U+05B4	#HEBREW POINT HIRIQ
+0xC5	U+05B5	#HEBREW POINT TSERE
+0xC6	U+05B6	#HEBREW POINT SEGOL
+0xC7	U+05B7	#HEBREW POINT PATAH
+0xC8	U+05B8	#HEBREW POINT QAMATS
+0xC9	U+05B9	#HEBREW POINT HOLAM
+0xCA		#UNDEFINED
+0xCB	U+05BB	#HEBREW POINT QUBUTS
+0xCC	U+05BC	#HEBREW POINT DAGESH OR MAPIQ
+0xCD	U+05BD	#HEBREW POINT METEG
+0xCE	U+05BE	#HEBREW PUNCTUATION MAQAF
+0xCF	U+05BF	#HEBREW POINT RAFE
+0xD0	U+05C0	#HEBREW PUNCTUATION PASEQ
+0xD1	U+05C1	#HEBREW POINT SHIN DOT
+0xD2	U+05C2	#HEBREW POINT SIN DOT
+0xD3	U+05C3	#HEBREW PUNCTUATION SOF PASUQ
+0xD4	U+05F0	#HEBREW LIGATURE YIDDISH DOUBLE VAV
+0xD5	U+05F1	#HEBREW LIGATURE YIDDISH VAV YOD
+0xD6	U+05F2	#HEBREW LIGATURE YIDDISH DOUBLE YOD
+0xD7	U+05F3	#HEBREW PUNCTUATION GERESH
+0xD8	U+05F4	#HEBREW PUNCTUATION GERSHAYIM
+0xD9	      	#UNDEFINED
+0xDA	      	#UNDEFINED
+0xDB	      	#UNDEFINED
+0xDC	      	#UNDEFINED
+0xDD	      	#UNDEFINED
+0xDE	      	#UNDEFINED
+0xDF	      	#UNDEFINED
+0xE0	U+05D0	#HEBREW LETTER ALEF
+0xE1	U+05D1	#HEBREW LETTER BET
+0xE2	U+05D2	#HEBREW LETTER GIMEL
+0xE3	U+05D3	#HEBREW LETTER DALET
+0xE4	U+05D4	#HEBREW LETTER HE
+0xE5	U+05D5	#HEBREW LETTER VAV
+0xE6	U+05D6	#HEBREW LETTER ZAYIN
+0xE7	U+05D7	#HEBREW LETTER HET
+0xE8	U+05D8	#HEBREW LETTER TET
+0xE9	U+05D9	#HEBREW LETTER YOD
+0xEA	U+05DA	#HEBREW LETTER FINAL KAF
+0xEB	U+05DB	#HEBREW LETTER KAF
+0xEC	U+05DC	#HEBREW LETTER LAMED
+0xED	U+05DD	#HEBREW LETTER FINAL MEM
+0xEE	U+05DE	#HEBREW LETTER MEM
+0xEF	U+05DF	#HEBREW LETTER FINAL NUN
+0xF0	U+05E0	#HEBREW LETTER NUN
+0xF1	U+05E1	#HEBREW LETTER SAMEKH
+0xF2	U+05E2	#HEBREW LETTER AYIN
+0xF3	U+05E3	#HEBREW LETTER FINAL PE
+0xF4	U+05E4	#HEBREW LETTER PE
+0xF5	U+05E5	#HEBREW LETTER FINAL TSADI
+0xF6	U+05E6	#HEBREW LETTER TSADI
+0xF7	U+05E7	#HEBREW LETTER QOF
+0xF8	U+05E8	#HEBREW LETTER RESH
+0xF9	U+05E9	#HEBREW LETTER SHIN
+0xFA	U+05EA	#HEBREW LETTER TAV
+0xFB	      	#UNDEFINED
+0xFC	      	#UNDEFINED
+0xFD	U+200E	#LEFT-TO-RIGHT MARK
+0xFE	U+200F	#RIGHT-TO-LEFT MARK
+0xFF	      	#UNDEFINED
diff --git a/src/chrtrans/cp1256_uni.tbl b/src/chrtrans/cp1256_uni.tbl
new file mode 100644
index 00000000..1ab99c06
--- /dev/null
+++ b/src/chrtrans/cp1256_uni.tbl
@@ -0,0 +1,161 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Mwindows-1256
+
+#Name as a Display Charset (used on Options screen).
+OArabic (windows-1256)
+
+#Codepage number
+C1256
+
+#
+#    Name:     cp1256 to Unicode table
+#    Unicode version: 2.1
+#    Table version: 2.01
+#    Table format:  Format A
+#    Date:          01/5/99
+#
+#    Contact:       cpxlate@microsoft.com
+#
+#    General notes: none
+#
+#    Format: Three tab-separated columns
+#        Column #1 is the cp1256 code (in hex)
+#        Column #2 is the Unicode (in hex as 0xXXXX)
+#        Column #3 is the Unicode name (follows a comment sign, '#')
+#
+#    The entries are in cp1256 order
+#
+##################
+
+0x20-0x7e       idem
+#
+0x80	U+20AC	#EURO SIGN
+0x81	U+067E	#ARABIC LETTER PEH
+0x82	U+201A	#SINGLE LOW-9 QUOTATION MARK
+0x83	U+0192	#LATIN SMALL LETTER F WITH HOOK
+0x84	U+201E	#DOUBLE LOW-9 QUOTATION MARK
+0x85	U+2026	#HORIZONTAL ELLIPSIS
+0x86	U+2020	#DAGGER
+0x87	U+2021	#DOUBLE DAGGER
+0x88	U+02C6	#MODIFIER LETTER CIRCUMFLEX ACCENT
+0x89	U+2030	#PER MILLE SIGN
+0x8A	U+0679	#ARABIC LETTER TTEH
+0x8B	U+2039	#SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+0x8C	U+0152	#LATIN CAPITAL LIGATURE OE
+0x8D	U+0686	#ARABIC LETTER TCHEH
+0x8E	U+0698	#ARABIC LETTER JEH
+0x8F	U+0688	#ARABIC LETTER DDAL
+0x90	U+06AF	#ARABIC LETTER GAF
+0x91	U+2018	#LEFT SINGLE QUOTATION MARK
+0x92	U+2019	#RIGHT SINGLE QUOTATION MARK
+0x93	U+201C	#LEFT DOUBLE QUOTATION MARK
+0x94	U+201D	#RIGHT DOUBLE QUOTATION MARK
+0x95	U+2022	#BULLET
+0x96	U+2013	#EN DASH
+0x97	U+2014	#EM DASH
+0x98	U+06A9	#ARABIC LETTER KEHEH
+0x99	U+2122	#TRADE MARK SIGN
+0x9A	U+0691	#ARABIC LETTER RREH
+0x9B	U+203A	#SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0x9C	U+0153	#LATIN SMALL LIGATURE OE
+0x9D	U+200C	#ZERO WIDTH NON-JOINER
+0x9E	U+200D	#ZERO WIDTH JOINER
+0x9F	U+06BA	#ARABIC LETTER NOON GHUNNA
+0xA0	U+00A0	#NO-BREAK SPACE
+0xA1	U+060C	#ARABIC COMMA
+0xA2	U+00A2	#CENT SIGN
+0xA3	U+00A3	#POUND SIGN
+0xA4	U+00A4	#CURRENCY SIGN
+0xA5	U+00A5	#YEN SIGN
+0xA6	U+00A6	#BROKEN BAR
+0xA7	U+00A7	#SECTION SIGN
+0xA8	U+00A8	#DIAERESIS
+0xA9	U+00A9	#COPYRIGHT SIGN
+0xAA	U+06BE	#ARABIC LETTER HEH DOACHASHMEE
+0xAB	U+00AB	#LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC	U+00AC	#NOT SIGN
+0xAD	U+00AD	#SOFT HYPHEN
+0xAE	U+00AE	#REGISTERED SIGN
+0xAF	U+00AF	#MACRON
+0xB0	U+00B0	#DEGREE SIGN
+0xB1	U+00B1	#PLUS-MINUS SIGN
+0xB2	U+00B2	#SUPERSCRIPT TWO
+0xB3	U+00B3	#SUPERSCRIPT THREE
+0xB4	U+00B4	#ACUTE ACCENT
+0xB5	U+00B5	#MICRO SIGN
+0xB6	U+00B6	#PILCROW SIGN
+0xB7	U+00B7	#MIDDLE DOT
+0xB8	U+00B8	#CEDILLA
+0xB9	U+00B9	#SUPERSCRIPT ONE
+0xBA	U+061B	#ARABIC SEMICOLON
+0xBB	U+00BB	#RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC	U+00BC	#VULGAR FRACTION ONE QUARTER
+0xBD	U+00BD	#VULGAR FRACTION ONE HALF
+0xBE	U+00BE	#VULGAR FRACTION THREE QUARTERS
+0xBF	U+061F	#ARABIC QUESTION MARK
+0xC0	U+06C1	#ARABIC LETTER HEH GOAL
+0xC1	U+0621	#ARABIC LETTER HAMZA
+0xC2	U+0622	#ARABIC LETTER ALEF WITH MADDA ABOVE
+0xC3	U+0623	#ARABIC LETTER ALEF WITH HAMZA ABOVE
+0xC4	U+0624	#ARABIC LETTER WAW WITH HAMZA ABOVE
+0xC5	U+0625	#ARABIC LETTER ALEF WITH HAMZA BELOW
+0xC6	U+0626	#ARABIC LETTER YEH WITH HAMZA ABOVE
+0xC7	U+0627	#ARABIC LETTER ALEF
+0xC8	U+0628	#ARABIC LETTER BEH
+0xC9	U+0629	#ARABIC LETTER TEH MARBUTA
+0xCA	U+062A	#ARABIC LETTER TEH
+0xCB	U+062B	#ARABIC LETTER THEH
+0xCC	U+062C	#ARABIC LETTER JEEM
+0xCD	U+062D	#ARABIC LETTER HAH
+0xCE	U+062E	#ARABIC LETTER KHAH
+0xCF	U+062F	#ARABIC LETTER DAL
+0xD0	U+0630	#ARABIC LETTER THAL
+0xD1	U+0631	#ARABIC LETTER REH
+0xD2	U+0632	#ARABIC LETTER ZAIN
+0xD3	U+0633	#ARABIC LETTER SEEN
+0xD4	U+0634	#ARABIC LETTER SHEEN
+0xD5	U+0635	#ARABIC LETTER SAD
+0xD6	U+0636	#ARABIC LETTER DAD
+0xD7	U+00D7	#MULTIPLICATION SIGN
+0xD8	U+0637	#ARABIC LETTER TAH
+0xD9	U+0638	#ARABIC LETTER ZAH
+0xDA	U+0639	#ARABIC LETTER AIN
+0xDB	U+063A	#ARABIC LETTER GHAIN
+0xDC	U+0640	#ARABIC TATWEEL
+0xDD	U+0641	#ARABIC LETTER FEH
+0xDE	U+0642	#ARABIC LETTER QAF
+0xDF	U+0643	#ARABIC LETTER KAF
+0xE0	U+00E0	#LATIN SMALL LETTER A WITH GRAVE
+0xE1	U+0644	#ARABIC LETTER LAM
+0xE2	U+00E2	#LATIN SMALL LETTER A WITH CIRCUMFLEX
+0xE3	U+0645	#ARABIC LETTER MEEM
+0xE4	U+0646	#ARABIC LETTER NOON
+0xE5	U+0647	#ARABIC LETTER HEH
+0xE6	U+0648	#ARABIC LETTER WAW
+0xE7	U+00E7	#LATIN SMALL LETTER C WITH CEDILLA
+0xE8	U+00E8	#LATIN SMALL LETTER E WITH GRAVE
+0xE9	U+00E9	#LATIN SMALL LETTER E WITH ACUTE
+0xEA	U+00EA	#LATIN SMALL LETTER E WITH CIRCUMFLEX
+0xEB	U+00EB	#LATIN SMALL LETTER E WITH DIAERESIS
+0xEC	U+0649	#ARABIC LETTER ALEF MAKSURA
+0xED	U+064A	#ARABIC LETTER YEH
+0xEE	U+00EE	#LATIN SMALL LETTER I WITH CIRCUMFLEX
+0xEF	U+00EF	#LATIN SMALL LETTER I WITH DIAERESIS
+0xF0	U+064B	#ARABIC FATHATAN
+0xF1	U+064C	#ARABIC DAMMATAN
+0xF2	U+064D	#ARABIC KASRATAN
+0xF3	U+064E	#ARABIC FATHA
+0xF4	U+00F4	#LATIN SMALL LETTER O WITH CIRCUMFLEX
+0xF5	U+064F	#ARABIC DAMMA
+0xF6	U+0650	#ARABIC KASRA
+0xF7	U+00F7	#DIVISION SIGN
+0xF8	U+0651	#ARABIC SHADDA
+0xF9	U+00F9	#LATIN SMALL LETTER U WITH GRAVE
+0xFA	U+0652	#ARABIC SUKUN
+0xFB	U+00FB	#LATIN SMALL LETTER U WITH CIRCUMFLEX
+0xFC	U+00FC	#LATIN SMALL LETTER U WITH DIAERESIS
+0xFD	U+200E	#LEFT-TO-RIGHT MARK
+0xFE	U+200F	#RIGHT-TO-LEFT MARK
+0xFF	U+06D2	#ARABIC LETTER YEH BARREE
diff --git a/src/chrtrans/cp1257_uni.tbl b/src/chrtrans/cp1257_uni.tbl
new file mode 100644
index 00000000..6cd0081d
--- /dev/null
+++ b/src/chrtrans/cp1257_uni.tbl
@@ -0,0 +1,162 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Mwindows-1257
+
+#Name as a Display Charset (used on Options screen)
+OBaltic Rim (windows-1257)
+
+#Codepage number
+C1257
+
+#
+#    Name:     cp1257 to Unicode table
+#    Unicode version: 2.0
+#    Table version: 2.01
+#    Table format:  Format A
+#    Date:          04/15/98
+#
+#    Contact:       cpxlate@microsoft.com
+#
+#    General notes: none
+#
+#    Format: Three tab-separated columns
+#        Column #1 is the cp1257 code (in hex)
+#        Column #2 is the Unicode (in hex as 0xXXXX)
+#        Column #3 is the Unicode name (follows a comment sign, '#')
+#
+#    The entries are in cp1257 order
+#
+##################
+
+0x20-0x7e       idem
+#
+0x80	U+20AC	#EURO SIGN
+0x81	      	#UNDEFINED
+0x82	U+201A	#SINGLE LOW-9 QUOTATION MARK
+0x83	      	#UNDEFINED
+0x84	U+201E	#DOUBLE LOW-9 QUOTATION MARK
+0x85	U+2026	#HORIZONTAL ELLIPSIS
+0x86	U+2020	#DAGGER
+0x87	U+2021	#DOUBLE DAGGER
+0x88	      	#UNDEFINED
+0x89	U+2030	#PER MILLE SIGN
+0x8A	      	#UNDEFINED
+0x8B	U+2039	#SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+0x8C	      	#UNDEFINED
+0x8D	U+00A8	#DIAERESIS
+0x8E	U+02C7	#CARON
+0x8F	U+00B8	#CEDILLA
+0x90	      	#UNDEFINED
+0x91	U+2018	#LEFT SINGLE QUOTATION MARK
+0x92	U+2019	#RIGHT SINGLE QUOTATION MARK
+0x93	U+201C	#LEFT DOUBLE QUOTATION MARK
+0x94	U+201D	#RIGHT DOUBLE QUOTATION MARK
+0x95	U+2022	#BULLET
+0x96	U+2013	#EN DASH
+0x97	U+2014	#EM DASH
+0x98	      	#UNDEFINED
+0x99	U+2122	#TRADE MARK SIGN
+0x9A	      	#UNDEFINED
+0x9B	U+203A	#SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0x9C	      	#UNDEFINED
+0x9D	U+00AF	#MACRON
+0x9E	U+02DB	#OGONEK
+0x9F	      	#UNDEFINED
+0xA0	U+00A0	#NO-BREAK SPACE
+0xA1	      	#UNDEFINED
+0xA2	U+00A2	#CENT SIGN
+0xA3	U+00A3	#POUND SIGN
+0xA4	U+00A4	#CURRENCY SIGN
+0xA5	      	#UNDEFINED
+0xA6	U+00A6	#BROKEN BAR
+0xA7	U+00A7	#SECTION SIGN
+0xA8	U+00D8	#LATIN CAPITAL LETTER O WITH STROKE
+0xA9	U+00A9	#COPYRIGHT SIGN
+0xAA	U+0156	#LATIN CAPITAL LETTER R WITH CEDILLA
+0xAB	U+00AB	#LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC	U+00AC	#NOT SIGN
+0xAD	U+00AD	#SOFT HYPHEN
+0xAE	U+00AE	#REGISTERED SIGN
+0xAF	U+00C6	#LATIN CAPITAL LETTER AE
+0xB0	U+00B0	#DEGREE SIGN
+0xB1	U+00B1	#PLUS-MINUS SIGN
+0xB2	U+00B2	#SUPERSCRIPT TWO
+0xB3	U+00B3	#SUPERSCRIPT THREE
+0xB4	U+00B4	#ACUTE ACCENT
+0xB5	U+00B5	#MICRO SIGN
+0xB6	U+00B6	#PILCROW SIGN
+0xB7	U+00B7	#MIDDLE DOT
+0xB8	U+00F8	#LATIN SMALL LETTER O WITH STROKE
+0xB9	U+00B9	#SUPERSCRIPT ONE
+0xBA	U+0157	#LATIN SMALL LETTER R WITH CEDILLA
+0xBB	U+00BB	#RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC	U+00BC	#VULGAR FRACTION ONE QUARTER
+0xBD	U+00BD	#VULGAR FRACTION ONE HALF
+0xBE	U+00BE	#VULGAR FRACTION THREE QUARTERS
+0xBF	U+00E6	#LATIN SMALL LETTER AE
+0xC0	U+0104	#LATIN CAPITAL LETTER A WITH OGONEK
+0xC1	U+012E	#LATIN CAPITAL LETTER I WITH OGONEK
+0xC2	U+0100	#LATIN CAPITAL LETTER A WITH MACRON
+0xC3	U+0106	#LATIN CAPITAL LETTER C WITH ACUTE
+0xC4	U+00C4	#LATIN CAPITAL LETTER A WITH DIAERESIS
+0xC5	U+00C5	#LATIN CAPITAL LETTER A WITH RING ABOVE
+0xC6	U+0118	#LATIN CAPITAL LETTER E WITH OGONEK
+0xC7	U+0112	#LATIN CAPITAL LETTER E WITH MACRON
+0xC8	U+010C	#LATIN CAPITAL LETTER C WITH CARON
+0xC9	U+00C9	#LATIN CAPITAL LETTER E WITH ACUTE
+0xCA	U+0179	#LATIN CAPITAL LETTER Z WITH ACUTE
+0xCB	U+0116	#LATIN CAPITAL LETTER E WITH DOT ABOVE
+0xCC	U+0122	#LATIN CAPITAL LETTER G WITH CEDILLA
+0xCD	U+0136	#LATIN CAPITAL LETTER K WITH CEDILLA
+0xCE	U+012A	#LATIN CAPITAL LETTER I WITH MACRON
+0xCF	U+013B	#LATIN CAPITAL LETTER L WITH CEDILLA
+0xD0	U+0160	#LATIN CAPITAL LETTER S WITH CARON
+0xD1	U+0143	#LATIN CAPITAL LETTER N WITH ACUTE
+0xD2	U+0145	#LATIN CAPITAL LETTER N WITH CEDILLA
+0xD3	U+00D3	#LATIN CAPITAL LETTER O WITH ACUTE
+0xD4	U+014C	#LATIN CAPITAL LETTER O WITH MACRON
+0xD5	U+00D5	#LATIN CAPITAL LETTER O WITH TILDE
+0xD6	U+00D6	#LATIN CAPITAL LETTER O WITH DIAERESIS
+0xD7	U+00D7	#MULTIPLICATION SIGN
+0xD8	U+0172	#LATIN CAPITAL LETTER U WITH OGONEK
+0xD9	U+0141	#LATIN CAPITAL LETTER L WITH STROKE
+0xDA	U+015A	#LATIN CAPITAL LETTER S WITH ACUTE
+0xDB	U+016A	#LATIN CAPITAL LETTER U WITH MACRON
+0xDC	U+00DC	#LATIN CAPITAL LETTER U WITH DIAERESIS
+0xDD	U+017B	#LATIN CAPITAL LETTER Z WITH DOT ABOVE
+0xDE	U+017D	#LATIN CAPITAL LETTER Z WITH CARON
+0xDF	U+00DF	#LATIN SMALL LETTER SHARP S
+0xE0	U+0105	#LATIN SMALL LETTER A WITH OGONEK
+0xE1	U+012F	#LATIN SMALL LETTER I WITH OGONEK
+0xE2	U+0101	#LATIN SMALL LETTER A WITH MACRON
+0xE3	U+0107	#LATIN SMALL LETTER C WITH ACUTE
+0xE4	U+00E4	#LATIN SMALL LETTER A WITH DIAERESIS
+0xE5	U+00E5	#LATIN SMALL LETTER A WITH RING ABOVE
+0xE6	U+0119	#LATIN SMALL LETTER E WITH OGONEK
+0xE7	U+0113	#LATIN SMALL LETTER E WITH MACRON
+0xE8	U+010D	#LATIN SMALL LETTER C WITH CARON
+0xE9	U+00E9	#LATIN SMALL LETTER E WITH ACUTE
+0xEA	U+017A	#LATIN SMALL LETTER Z WITH ACUTE
+0xEB	U+0117	#LATIN SMALL LETTER E WITH DOT ABOVE
+0xEC	U+0123	#LATIN SMALL LETTER G WITH CEDILLA
+0xED	U+0137	#LATIN SMALL LETTER K WITH CEDILLA
+0xEE	U+012B	#LATIN SMALL LETTER I WITH MACRON
+0xEF	U+013C	#LATIN SMALL LETTER L WITH CEDILLA
+0xF0	U+0161	#LATIN SMALL LETTER S WITH CARON
+0xF1	U+0144	#LATIN SMALL LETTER N WITH ACUTE
+0xF2	U+0146	#LATIN SMALL LETTER N WITH CEDILLA
+0xF3	U+00F3	#LATIN SMALL LETTER O WITH ACUTE
+0xF4	U+014D	#LATIN SMALL LETTER O WITH MACRON
+0xF5	U+00F5	#LATIN SMALL LETTER O WITH TILDE
+0xF6	U+00F6	#LATIN SMALL LETTER O WITH DIAERESIS
+0xF7	U+00F7	#DIVISION SIGN
+0xF8	U+0173	#LATIN SMALL LETTER U WITH OGONEK
+0xF9	U+0142	#LATIN SMALL LETTER L WITH STROKE
+0xFA	U+015B	#LATIN SMALL LETTER S WITH ACUTE
+0xFB	U+016B	#LATIN SMALL LETTER U WITH MACRON
+0xFC	U+00FC	#LATIN SMALL LETTER U WITH DIAERESIS
+0xFD	U+017C	#LATIN SMALL LETTER Z WITH DOT ABOVE
+0xFE	U+017E	#LATIN SMALL LETTER Z WITH CARON
+0xFF	U+02D9	#DOT ABOVE
+
diff --git a/src/chrtrans/cp437_uni.tbl b/src/chrtrans/cp437_uni.tbl
new file mode 100644
index 00000000..4f45ce72
--- /dev/null
+++ b/src/chrtrans/cp437_uni.tbl
@@ -0,0 +1,181 @@
+# This file has been modified for lynx (see README.tables)
+
+#Shall this become the "default" translation?
+#There has to be exactly one table marked as "default".
+D0
+#
+#The MIME name of this charset.
+Mcp437
+
+#Name as a Display Charset (used on Options screen)
+OIBM PC US codepage (cp437)
+
+#Codepage number
+C437
+
+#
+#    Name:     cp437_DOSLatinUS to Unicode table
+#    Unicode version: 2.0
+#    Table version: 2.00
+#    Table format:  Format A
+#    Date:          04/24/96
+#    Authors:       Lori Brownell <loribr@microsoft.com>
+#                   K.D. Chang    <a-kchang@microsoft.com>
+#    General notes: none
+#
+#    Format: Three tab-separated columns
+#        Column #1 is the cp437_DOSLatinUS code (in hex)
+#        Column #2 is the Unicode (in hex as 0xXXXX)
+#        Column #3 is the Unicode name (follows a comment sign, '#')
+#
+#    The entries are in cp437_DosLatinUS order
+#
+# Lines with more than one Unicode (U+XXXX) value contain additional
+# replacement mappings added for lynx. - kw
+#
+#######################################
+
+0x20-0x7f	idem
+#
+0x80	U+00c7	#LATIN CAPITAL LETTER C WITH CEDILLA
+0x81	U+00fc	U+03cb	#LATIN SMALL LETTER U WITH DIAERESIS
+0x82	U+00e9	#LATIN SMALL LETTER E WITH ACUTE
+0x83	U+00e2	#LATIN SMALL LETTER A WITH CIRCUMFLEX
+0x84	U+00e4	#LATIN SMALL LETTER A WITH DIAERESIS
+0x85	U+00e0	#LATIN SMALL LETTER A WITH GRAVE
+0x86	U+00e5	#LATIN SMALL LETTER A WITH RING ABOVE
+0x87	U+00e7	#LATIN SMALL LETTER C WITH CEDILLA
+0x88	U+00ea	#LATIN SMALL LETTER E WITH CIRCUMFLEX
+0x89	U+00eb	#LATIN SMALL LETTER E WITH DIAERESIS
+0x8a	U+00e8	#LATIN SMALL LETTER E WITH GRAVE
+0x8b	U+00ef	U+03ca	#LATIN SMALL LETTER I WITH DIAERESIS
+0x8c	U+00ee	#LATIN SMALL LETTER I WITH CIRCUMFLEX
+0x8d	U+00ec	#LATIN SMALL LETTER I WITH GRAVE
+0x8e	U+00c4	#LATIN CAPITAL LETTER A WITH DIAERESIS
+0x8f	U+00c5	#LATIN CAPITAL LETTER A WITH RING ABOVE
+0x90	U+00c9	U+0388	#LATIN CAPITAL LETTER E WITH ACUTE
+0x91	U+00e6	#LATIN SMALL LIGATURE AE
+0x92	U+00c6	#LATIN CAPITAL LIGATURE AE
+0x93	U+00f4	#LATIN SMALL LETTER O WITH CIRCUMFLEX
+0x94	U+00f6	#LATIN SMALL LETTER O WITH DIAERESIS
+0x95	U+00f2	#LATIN SMALL LETTER O WITH GRAVE
+0x96	U+00fb	#LATIN SMALL LETTER U WITH CIRCUMFLEX
+0x97	U+00f9	#LATIN SMALL LETTER U WITH GRAVE
+0x98	U+00ff	#LATIN SMALL LETTER Y WITH DIAERESIS
+0x99	U+00d6	#LATIN CAPITAL LETTER O WITH DIAERESIS
+0x9a	U+00dc	U+03ab	#LATIN CAPITAL LETTER U WITH DIAERESIS
+0x9b	U+00a2	#CENT SIGN
+0x9c	U+00a3	#POUND SIGN
+0x9d	U+00a5	#YEN SIGN
+0x9e	U+20a7	#PESETA SIGN
+0x9f	U+0192	#LATIN SMALL LETTER F WITH HOOK
+0xa0	U+00e1	U+03ac	#LATIN SMALL LETTER A WITH ACUTE
+0xa1	U+00ed	U+03af	#LATIN SMALL LETTER I WITH ACUTE
+0xa2	U+00f3	U+03cc	#LATIN SMALL LETTER O WITH ACUTE
+0xa3	U+00fa	U+03cd	#LATIN SMALL LETTER U WITH ACUTE
+0xa4	U+00f1	#LATIN SMALL LETTER N WITH TILDE
+0xa5	U+00d1	#LATIN CAPITAL LETTER N WITH TILDE
+0xa6	U+00aa	#FEMININE ORDINAL INDICATOR
+0xa7	U+00ba	#MASCULINE ORDINAL INDICATOR
+0xa8	U+00bf	#INVERTED QUESTION MARK
+0xa9	U+2310	#REVERSED NOT SIGN
+0xaa	U+00ac	#NOT SIGN
+0xab	U+00bd	#VULGAR FRACTION ONE HALF
+0xac	U+00bc	#VULGAR FRACTION ONE QUARTER
+0xad	U+00a1	#INVERTED EXCLAMATION MARK
+0xae	U+00ab	#LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xaf	U+00bb	#RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xb0	U+2591	#LIGHT SHADE
+0xb1	U+2592	#MEDIUM SHADE
+0xb2	U+2593	#DARK SHADE
+0xb3	U+2502	#BOX DRAWINGS LIGHT VERTICAL
+0xb4	U+2524	#BOX DRAWINGS LIGHT VERTICAL AND LEFT
+0xb5	U+2561	#BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
+0xb6	U+2562	#BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
+0xb7	U+2556	#BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
+0xb8	U+2555	#BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
+0xb9	U+2563	#BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+0xba	U+2551	#BOX DRAWINGS DOUBLE VERTICAL
+0xbb	U+2557	#BOX DRAWINGS DOUBLE DOWN AND LEFT
+0xbc	U+255d	#BOX DRAWINGS DOUBLE UP AND LEFT
+0xbd	U+255c	#BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
+0xbe	U+255b	#BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
+0xbf	U+2510	#BOX DRAWINGS LIGHT DOWN AND LEFT
+0xc0	U+2514	#BOX DRAWINGS LIGHT UP AND RIGHT
+0xc1	U+2534	#BOX DRAWINGS LIGHT UP AND HORIZONTAL
+0xc2	U+252c	#BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+0xc3	U+251c	#BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+0xc4	U+2500	#BOX DRAWINGS LIGHT HORIZONTAL
+0xc5	U+253c	#BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+0xc6	U+255e	#BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
+0xc7	U+255f	#BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
+0xc8	U+255a	#BOX DRAWINGS DOUBLE UP AND RIGHT
+0xc9	U+2554	#BOX DRAWINGS DOUBLE DOWN AND RIGHT
+0xca	U+2569	#BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+0xcb	U+2566	#BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+0xcc	U+2560	#BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+0xcd	U+2550	#BOX DRAWINGS DOUBLE HORIZONTAL
+0xce	U+256c	#BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+0xcf	U+2567	#BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
+0xd0	U+2568	#BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
+0xd1	U+2564	#BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
+0xd2	U+2565	#BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
+0xd3	U+2559	#BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
+0xd4	U+2558	#BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
+0xd5	U+2552	#BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
+0xd6	U+2553	#BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
+0xd7	U+256b	#BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
+0xd8	U+256a	#BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
+0xd9	U+2518	#BOX DRAWINGS LIGHT UP AND LEFT
+0xda	U+250c	#BOX DRAWINGS LIGHT DOWN AND RIGHT
+0xdb	U+2588	#FULL BLOCK
+0xdc	U+2584	#LOWER HALF BLOCK
+0xdd	U+258c	#LEFT HALF BLOCK
+0xde	U+2590	#RIGHT HALF BLOCK
+0xdf	U+2580	#UPPER HALF BLOCK
+0xe0	U+03b1	#GREEK SMALL LETTER ALPHA
+0xe1	U+00df	U+03b2	#LATIN SMALL LETTER SHARP S
+0xe2	U+0393	#GREEK CAPITAL LETTER GAMMA
+0xe3	U+03c0	#GREEK SMALL LETTER PI
+0xe4	U+03a3	U+2211	#GREEK CAPITAL LETTER SIGMA
+0xe5	U+03c3	#GREEK SMALL LETTER SIGMA
+0xe6	U+00b5	U+03bc	#MICRO SIGN
+0xe7	U+03c4	#GREEK SMALL LETTER TAU
+0xe8	U+03a6	#GREEK CAPITAL LETTER PHI
+0xe9	U+0398	U+03b8	#GREEK CAPITAL LETTER THETA
+0xea	U+03a9	U+2126	#GREEK CAPITAL LETTER OMEGA
+0xeb	U+03b4	#GREEK SMALL LETTER DELTA
+0xec	U+221e	#INFINITY
+0xed	U+03c6	U+00f8	#GREEK SMALL LETTER PHI
+0xee	U+03b5	U+2208	U+220a	#GREEK SMALL LETTER EPSILON
+0xef	U+2229	#INTERSECTION
+0xf0	U+2261	#IDENTICAL TO
+0xf1	U+00b1	#PLUS-MINUS SIGN
+0xf2	U+2265	U+2267	#GREATER-THAN OR EQUAL TO
+0xf3	U+2264	U+2266	#LESS-THAN OR EQUAL TO
+0xf4	U+2320	U+0283	#TOP HALF INTEGRAL
+0xf5	U+2321	#BOTTOM HALF INTEGRAL
+0xf6	U+00f7	#DIVISION SIGN
+0xf7	U+2248	#ALMOST EQUAL TO
+0xf8	U+00b0	U+030a	#DEGREE SIGN
+0xf9	U+2219	U+0307	U+0387	#BULLET OPERATOR
+0xfa	U+00b7	U+2027	#MIDDLE DOT
+0xfb	U+221a	#SQUARE ROOT
+0xfc	U+207f	#SUPERSCRIPT LATIN SMALL LETTER N
+0xfd	U+00b2	#SUPERSCRIPT TWO
+0xfe	U+25a0	#BLACK SQUARE
+0xff	U+00a0	#NO-BREAK SPACE
+
+U+03ad "\356'"	#:î'
+U+03ae:h'
+U+03cd:u'
+U+03ce:w'
+
+U+2209 " !\356 "
+U+221b " 3\373"
+U+221c " 4\373"
+U+2262 " !\360"
+U+2299 "(\372)"
+U+229a "(\370)"
+U+22a1 "[\372]"
+U+02a7 "t\364"
diff --git a/src/chrtrans/cp737_uni.tbl b/src/chrtrans/cp737_uni.tbl
new file mode 100644
index 00000000..e57e2619
--- /dev/null
+++ b/src/chrtrans/cp737_uni.tbl
@@ -0,0 +1,172 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Mcp737
+
+#Name as a Display Charset (used on Options screen)
+OGreek (cp737)
+
+#Codepage number
+C737
+
+#
+#    Name:     cp737_DOSGreek to Unicode table
+#    Unicode version: 2.0
+#    Table version: 2.00
+#    Table format:  Format A
+#    Date:          04/24/96
+#    Authors:       Lori Brownell <loribr@microsoft.com>
+#                   K.D. Chang    <a-kchang@microsoft.com>
+#    General notes: none
+#
+#    Format: Three tab-separated columns
+#        Column #1 is the cp737_DOSGreek code (in hex)
+#        Column #2 is the Unicode (in hex as 0xXXXX)
+#        Column #3 is the Unicode name (follows a comment sign, '#')
+#
+#    The entries are in cp737_DOSGreek order
+#
+##################
+# Lines with more than one Unicode (U+XXXX) value contain additional
+# replacement mappings added for lynx. - kw
+
+0x20-0x7f       idem
+#
+0x80	U+0391	#GREEK CAPITAL LETTER ALPHA
+0x81	U+0392	#GREEK CAPITAL LETTER BETA
+0x82	U+0393	#GREEK CAPITAL LETTER GAMMA
+0x83	U+0394	#GREEK CAPITAL LETTER DELTA
+0x84	U+0395	#GREEK CAPITAL LETTER EPSILON
+0x85	U+0396	#GREEK CAPITAL LETTER ZETA
+0x86	U+0397	#GREEK CAPITAL LETTER ETA
+0x87	U+0398	#GREEK CAPITAL LETTER THETA
+0x88	U+0399	#GREEK CAPITAL LETTER IOTA
+0x89	U+039a	#GREEK CAPITAL LETTER KAPPA
+0x8a	U+039b	#GREEK CAPITAL LETTER LAMDA
+0x8b	U+039c	#GREEK CAPITAL LETTER MU
+0x8c	U+039d	#GREEK CAPITAL LETTER NU
+0x8d	U+039e	#GREEK CAPITAL LETTER XI
+0x8e	U+039f	#GREEK CAPITAL LETTER OMICRON
+0x8f	U+03a0	#GREEK CAPITAL LETTER PI
+0x90	U+03a1	#GREEK CAPITAL LETTER RHO
+0x91	U+03a3	#GREEK CAPITAL LETTER SIGMA
+0x92	U+03a4	#GREEK CAPITAL LETTER TAU
+0x93	U+03a5	#GREEK CAPITAL LETTER UPSILON
+0x94	U+03a6	#GREEK CAPITAL LETTER PHI
+0x95	U+03a7	#GREEK CAPITAL LETTER CHI
+0x96	U+03a8	#GREEK CAPITAL LETTER PSI
+0x97	U+03a9	#GREEK CAPITAL LETTER OMEGA
+0x98	U+03b1	#GREEK SMALL LETTER ALPHA
+0x99	U+03b2	#GREEK SMALL LETTER BETA
+0x9a	U+03b3	U+0263	#GREEK SMALL LETTER GAMMA
+0x9b	U+03b4	#GREEK SMALL LETTER DELTA
+0x9c	U+03b5	#GREEK SMALL LETTER EPSILON
+0x9d	U+03b6	#GREEK SMALL LETTER ZETA
+0x9e	U+03b7	#GREEK SMALL LETTER ETA
+0x9f	U+03b8	#GREEK SMALL LETTER THETA
+0xa0	U+03b9	U+0131	#GREEK SMALL LETTER IOTA
+0xa1	U+03ba	#GREEK SMALL LETTER KAPPA
+0xa2	U+03bb	#GREEK SMALL LETTER LAMDA
+0xa3	U+03bc	U+00b5	#GREEK SMALL LETTER MU
+0xa4	U+03bd	#GREEK SMALL LETTER NU
+0xa5	U+03be	#GREEK SMALL LETTER XI
+0xa6	U+03bf	#GREEK SMALL LETTER OMICRON
+0xa7	U+03c0	#GREEK SMALL LETTER PI
+0xa8	U+03c1	#GREEK SMALL LETTER RHO
+0xa9	U+03c3	#GREEK SMALL LETTER SIGMA
+0xaa	U+03c2	#GREEK SMALL LETTER FINAL SIGMA
+0xab	U+03c4	#GREEK SMALL LETTER TAU
+0xac	U+03c5	U+028a	#GREEK SMALL LETTER UPSILON
+0xad	U+03c6	#GREEK SMALL LETTER PHI
+0xae	U+03c7	#GREEK SMALL LETTER CHI
+0xaf	U+03c8	#GREEK SMALL LETTER PSI
+0xb0	U+2591	#LIGHT SHADE
+0xb1	U+2592	#MEDIUM SHADE
+0xb2	U+2593	#DARK SHADE
+0xb3	U+2502	#BOX DRAWINGS LIGHT VERTICAL
+0xb4	U+2524	#BOX DRAWINGS LIGHT VERTICAL AND LEFT
+0xb5	U+2561	#BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
+0xb6	U+2562	#BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
+0xb7	U+2556	#BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
+0xb8	U+2555	#BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
+0xb9	U+2563	#BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+0xba	U+2551	#BOX DRAWINGS DOUBLE VERTICAL
+0xbb	U+2557	#BOX DRAWINGS DOUBLE DOWN AND LEFT
+0xbc	U+255d	#BOX DRAWINGS DOUBLE UP AND LEFT
+0xbd	U+255c	#BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
+0xbe	U+255b	#BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
+0xbf	U+2510	#BOX DRAWINGS LIGHT DOWN AND LEFT
+0xc0	U+2514	#BOX DRAWINGS LIGHT UP AND RIGHT
+0xc1	U+2534	#BOX DRAWINGS LIGHT UP AND HORIZONTAL
+0xc2	U+252c	#BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+0xc3	U+251c	#BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+0xc4	U+2500	#BOX DRAWINGS LIGHT HORIZONTAL
+0xc5	U+253c	#BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+0xc6	U+255e	#BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
+0xc7	U+255f	#BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
+0xc8	U+255a	#BOX DRAWINGS DOUBLE UP AND RIGHT
+0xc9	U+2554	#BOX DRAWINGS DOUBLE DOWN AND RIGHT
+0xca	U+2569	#BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+0xcb	U+2566	#BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+0xcc	U+2560	#BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+0xcd	U+2550	#BOX DRAWINGS DOUBLE HORIZONTAL
+0xce	U+256c	#BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+0xcf	U+2567	#BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
+0xd0	U+2568	#BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
+0xd1	U+2564	#BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
+0xd2	U+2565	#BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
+0xd3	U+2559	#BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
+0xd4	U+2558	#BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
+0xd5	U+2552	#BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
+0xd6	U+2553	#BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
+0xd7	U+256b	#BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
+0xd8	U+256a	#BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
+0xd9	U+2518	#BOX DRAWINGS LIGHT UP AND LEFT
+0xda	U+250c	#BOX DRAWINGS LIGHT DOWN AND RIGHT
+0xdb	U+2588	#FULL BLOCK
+0xdc	U+2584	#LOWER HALF BLOCK
+0xdd	U+258c	#LEFT HALF BLOCK
+0xde	U+2590	#RIGHT HALF BLOCK
+0xdf	U+2580	#UPPER HALF BLOCK
+0xe0	U+03c9	#GREEK SMALL LETTER OMEGA
+0xe1	U+03ac	#GREEK SMALL LETTER ALPHA WITH TONOS
+0xe2	U+03ad	#GREEK SMALL LETTER EPSILON WITH TONOS
+0xe3	U+03ae	#GREEK SMALL LETTER ETA WITH TONOS
+0xe4	U+03ca	#GREEK SMALL LETTER IOTA WITH DIALYTIKA
+0xe5	U+03af	#GREEK SMALL LETTER IOTA WITH TONOS
+0xe6	U+03cc	#GREEK SMALL LETTER OMICRON WITH TONOS
+0xe7	U+03cd	#GREEK SMALL LETTER UPSILON WITH TONOS
+0xe8	U+03cb	U+00fc	#GREEK SMALL LETTER UPSILON WITH DIALYTIKA
+0xe9	U+03ce	#GREEK SMALL LETTER OMEGA WITH TONOS
+0xea	U+0386	#GREEK CAPITAL LETTER ALPHA WITH TONOS
+0xeb	U+0388	#GREEK CAPITAL LETTER EPSILON WITH TONOS
+0xec	U+0389	#GREEK CAPITAL LETTER ETA WITH TONOS
+0xed	U+038a	#GREEK CAPITAL LETTER IOTA WITH TONOS
+0xee	U+038c	#GREEK CAPITAL LETTER OMICRON WITH TONOS
+0xef	U+038e	#GREEK CAPITAL LETTER UPSILON WITH TONOS
+0xf0	U+038f	#GREEK CAPITAL LETTER OMEGA WITH TONOS
+0xf1	U+00b1	#PLUS-MINUS SIGN
+0xf2	U+2265	#GREATER-THAN OR EQUAL TO
+0xf3	U+2264	#LESS-THAN OR EQUAL TO
+0xf4	U+03aa	#GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
+0xf5	U+03ab	#GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
+0xf6	U+00f7	#DIVISION SIGN
+0xf7	U+2248	#ALMOST EQUAL TO
+0xf8	U+00b0	#DEGREE SIGN
+0xf9	U+2219	U+0307	U+0387	#BULLET OPERATOR
+0xfa	U+00b7	#MIDDLE DOT
+0xfb	U+221a	#SQUARE ROOT
+0xfc	U+207f	#SUPERSCRIPT LATIN SMALL LETTER N
+0xfd	U+00b2	#SUPERSCRIPT TWO
+0xfe	U+25a0	#BLACK SQUARE
+0xff	U+00a0	#NO-BREAK SPACE
+
+U+2209 " !\234 "
+U+2218 " \370 "		# RING OPERATOR
+U+221b " 3\373"
+U+221c " 4\373"
+U+2299 "(\372)"
+U+229a "(\370)"
+U+22a1 "[\372]"
+U+02a4 "d\235"
+U+2249 "!\367"
diff --git a/src/chrtrans/cp775_uni.tbl b/src/chrtrans/cp775_uni.tbl
new file mode 100644
index 00000000..26a3ff72
--- /dev/null
+++ b/src/chrtrans/cp775_uni.tbl
@@ -0,0 +1,159 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Mcp775
+
+#Name as a Display Charset (used on Options screen)
+OBaltic Rim (cp775)
+
+#Codepage number
+C775
+
+#    Name:     cp775_DOSBaltRim to Unicode table
+#    Unicode version: 2.0
+#    Table version: 2.00
+#    Table format:  Format A
+#    Date:          04/24/96
+#    Authors:       Lori Brownell <loribr@microsoft.com>
+#                   K.D. Chang    <a-kchang@microsoft.com>
+#    General notes: none
+#
+#    Format: Three tab-separated columns
+#        Column #1 is the cp775_DOSBaltRim code (in hex)
+#        Column #2 is the Unicode (in hex as 0xXXXX)
+#        Column #3 is the Unicode name (follows a comment sign, '#')
+#
+#    The entries are in cp775_DOSBaltRim order
+#
+##################
+
+0x20-0x7e       idem
+#
+0x80	U+0106	#LATIN CAPITAL LETTER C WITH ACUTE
+0x81	U+00fc	#LATIN SMALL LETTER U WITH DIAERESIS
+0x82	U+00e9	#LATIN SMALL LETTER E WITH ACUTE
+0x83	U+0101	#LATIN SMALL LETTER A WITH MACRON
+0x84	U+00e4	#LATIN SMALL LETTER A WITH DIAERESIS
+0x85	U+0123	#LATIN SMALL LETTER G WITH CEDILLA
+0x86	U+00e5	#LATIN SMALL LETTER A WITH RING ABOVE
+0x87	U+0107	#LATIN SMALL LETTER C WITH ACUTE
+0x88	U+0142	#LATIN SMALL LETTER L WITH STROKE
+0x89	U+0113	#LATIN SMALL LETTER E WITH MACRON
+0x8a	U+0156	#LATIN CAPITAL LETTER R WITH CEDILLA
+0x8b	U+0157	#LATIN SMALL LETTER R WITH CEDILLA
+0x8c	U+012b	#LATIN SMALL LETTER I WITH MACRON
+0x8d	U+0179	#LATIN CAPITAL LETTER Z WITH ACUTE
+0x8e	U+00c4	#LATIN CAPITAL LETTER A WITH DIAERESIS
+0x8f	U+00c5	#LATIN CAPITAL LETTER A WITH RING ABOVE
+0x90	U+00c9	#LATIN CAPITAL LETTER E WITH ACUTE
+0x91	U+00e6	#LATIN SMALL LIGATURE AE
+0x92	U+00c6	#LATIN CAPITAL LIGATURE AE
+0x93	U+014d	#LATIN SMALL LETTER O WITH MACRON
+0x94	U+00f6	#LATIN SMALL LETTER O WITH DIAERESIS
+0x95	U+0122	#LATIN CAPITAL LETTER G WITH CEDILLA
+0x96	U+00a2	#CENT SIGN
+0x97	U+015a	#LATIN CAPITAL LETTER S WITH ACUTE
+0x98	U+015b	#LATIN SMALL LETTER S WITH ACUTE
+0x99	U+00d6	#LATIN CAPITAL LETTER O WITH DIAERESIS
+0x9a	U+00dc	#LATIN CAPITAL LETTER U WITH DIAERESIS
+0x9b	U+00f8	#LATIN SMALL LETTER O WITH STROKE
+0x9c	U+00a3	#POUND SIGN
+0x9d	U+00d8	#LATIN CAPITAL LETTER O WITH STROKE
+0x9e	U+00d7	#MULTIPLICATION SIGN
+0x9f	U+00a4	#CURRENCY SIGN
+0xa0	U+0100	#LATIN CAPITAL LETTER A WITH MACRON
+0xa1	U+012a	#LATIN CAPITAL LETTER I WITH MACRON
+0xa2	U+00f3	#LATIN SMALL LETTER O WITH ACUTE
+0xa3	U+017b	#LATIN CAPITAL LETTER Z WITH DOT ABOVE
+0xa4	U+017c	#LATIN SMALL LETTER Z WITH DOT ABOVE
+0xa5	U+017a	#LATIN SMALL LETTER Z WITH ACUTE
+0xa6	U+201d	#RIGHT DOUBLE QUOTATION MARK
+0xa7	U+00a6	#BROKEN BAR
+0xa8	U+00a9	#COPYRIGHT SIGN
+0xa9	U+00ae	#REGISTERED SIGN
+0xaa	U+00ac	#NOT SIGN
+0xab	U+00bd	#VULGAR FRACTION ONE HALF
+0xac	U+00bc	#VULGAR FRACTION ONE QUARTER
+0xad	U+0141	#LATIN CAPITAL LETTER L WITH STROKE
+0xae	U+00ab	#LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xaf	U+00bb	#RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xb0	U+2591	#LIGHT SHADE
+0xb1	U+2592	#MEDIUM SHADE
+0xb2	U+2593	#DARK SHADE
+0xb3	U+2502	#BOX DRAWINGS LIGHT VERTICAL
+0xb4	U+2524	#BOX DRAWINGS LIGHT VERTICAL AND LEFT
+0xb5	U+0104	#LATIN CAPITAL LETTER A WITH OGONEK
+0xb6	U+010c	#LATIN CAPITAL LETTER C WITH CARON
+0xb7	U+0118	#LATIN CAPITAL LETTER E WITH OGONEK
+0xb8	U+0116	#LATIN CAPITAL LETTER E WITH DOT ABOVE
+0xb9	U+2563	#BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+0xba	U+2551	#BOX DRAWINGS DOUBLE VERTICAL
+0xbb	U+2557	#BOX DRAWINGS DOUBLE DOWN AND LEFT
+0xbc	U+255d	#BOX DRAWINGS DOUBLE UP AND LEFT
+0xbd	U+012e	#LATIN CAPITAL LETTER I WITH OGONEK
+0xbe	U+0160	#LATIN CAPITAL LETTER S WITH CARON
+0xbf	U+2510	#BOX DRAWINGS LIGHT DOWN AND LEFT
+0xc0	U+2514	#BOX DRAWINGS LIGHT UP AND RIGHT
+0xc1	U+2534	#BOX DRAWINGS LIGHT UP AND HORIZONTAL
+0xc2	U+252c	#BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+0xc3	U+251c	#BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+0xc4	U+2500	#BOX DRAWINGS LIGHT HORIZONTAL
+0xc5	U+253c	#BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+0xc6	U+0172	#LATIN CAPITAL LETTER U WITH OGONEK
+0xc7	U+016a	#LATIN CAPITAL LETTER U WITH MACRON
+0xc8	U+255a	#BOX DRAWINGS DOUBLE UP AND RIGHT
+0xc9	U+2554	#BOX DRAWINGS DOUBLE DOWN AND RIGHT
+0xca	U+2569	#BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+0xcb	U+2566	#BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+0xcc	U+2560	#BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+0xcd	U+2550	#BOX DRAWINGS DOUBLE HORIZONTAL
+0xce	U+256c	#BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+0xcf	U+017d	#LATIN CAPITAL LETTER Z WITH CARON
+0xd0	U+0105	#LATIN SMALL LETTER A WITH OGONEK
+0xd1	U+010d	#LATIN SMALL LETTER C WITH CARON
+0xd2	U+0119	#LATIN SMALL LETTER E WITH OGONEK
+0xd3	U+0117	#LATIN SMALL LETTER E WITH DOT ABOVE
+0xd4	U+012f	#LATIN SMALL LETTER I WITH OGONEK
+0xd5	U+0161	#LATIN SMALL LETTER S WITH CARON
+0xd6	U+0173	#LATIN SMALL LETTER U WITH OGONEK
+0xd7	U+016b	#LATIN SMALL LETTER U WITH MACRON
+0xd8	U+017e	#LATIN SMALL LETTER Z WITH CARON
+0xd9	U+2518	#BOX DRAWINGS LIGHT UP AND LEFT
+0xda	U+250c	#BOX DRAWINGS LIGHT DOWN AND RIGHT
+0xdb	U+2588	#FULL BLOCK
+0xdc	U+2584	#LOWER HALF BLOCK
+0xdd	U+258c	#LEFT HALF BLOCK
+0xde	U+2590	#RIGHT HALF BLOCK
+0xdf	U+2580	#UPPER HALF BLOCK
+0xe0	U+00d3	#LATIN CAPITAL LETTER O WITH ACUTE
+0xe1	U+00df	#LATIN SMALL LETTER SHARP S (GERMAN)
+0xe2	U+014c	#LATIN CAPITAL LETTER O WITH MACRON
+0xe3	U+0143	#LATIN CAPITAL LETTER N WITH ACUTE
+0xe4	U+00f5	#LATIN SMALL LETTER O WITH TILDE
+0xe5	U+00d5	#LATIN CAPITAL LETTER O WITH TILDE
+0xe6	U+00b5	#MICRO SIGN
+0xe7	U+0144	#LATIN SMALL LETTER N WITH ACUTE
+0xe8	U+0136	#LATIN CAPITAL LETTER K WITH CEDILLA
+0xe9	U+0137	#LATIN SMALL LETTER K WITH CEDILLA
+0xea	U+013b	#LATIN CAPITAL LETTER L WITH CEDILLA
+0xeb	U+013c	#LATIN SMALL LETTER L WITH CEDILLA
+0xec	U+0146	#LATIN SMALL LETTER N WITH CEDILLA
+0xed	U+0112	#LATIN CAPITAL LETTER E WITH MACRON
+0xee	U+0145	#LATIN CAPITAL LETTER N WITH CEDILLA
+0xef	U+2019	#RIGHT SINGLE QUOTATION MARK
+0xf0	U+00ad	#SOFT HYPHEN
+0xf1	U+00b1	#PLUS-MINUS SIGN
+0xf2	U+201c	#LEFT DOUBLE QUOTATION MARK
+0xf3	U+00be	#VULGAR FRACTION THREE QUARTERS
+0xf4	U+00b6	#PILCROW SIGN
+0xf5	U+00a7	#SECTION SIGN
+0xf6	U+00f7	#DIVISION SIGN
+0xf7	U+201e	#DOUBLE LOW-9 QUOTATION MARK
+0xf8	U+00b0	#DEGREE SIGN
+0xf9	U+2219	#BULLET OPERATOR
+0xfa	U+00b7	#MIDDLE DOT
+0xfb	U+00b9	#SUPERSCRIPT ONE
+0xfc	U+00b3	#SUPERSCRIPT THREE
+0xfd	U+00b2	#SUPERSCRIPT TWO
+0xfe	U+25a0	#BLACK SQUARE
+0xff	U+00a0	#NO-BREAK SPACE
diff --git a/src/chrtrans/cp850_uni.tbl b/src/chrtrans/cp850_uni.tbl
new file mode 100644
index 00000000..9d05af26
--- /dev/null
+++ b/src/chrtrans/cp850_uni.tbl
@@ -0,0 +1,177 @@
+# This file has been modified for lynx (see README.tables)
+
+#Shall this become the "default" translation?
+#Meaning of that is currently unclear...  It's different
+#from the default input or default output charset...
+#but there has to be exactly one table marked as "default".
+D0
+#
+#The MIME name of this charset.
+Mcp850
+
+#Name as a Display Charset (used on Options screen)
+OWestern (cp850)
+
+#Codepage number
+C850
+
+#
+#    Name:     cp850_DOSLatin1 to Unicode table
+#    Unicode version: 2.0
+#    Table version: 2.00
+#    Table format:  Format A
+#    Date:          04/24/96
+#    Authors:       Lori Brownell <loribr@microsoft.com>
+#                   K.D. Chang    <a-kchang@microsoft.com>
+#    General notes: none
+#
+#    Format: Three tab-separated columns
+#        Column #1 is the cp850_DOSLatin1 code (in hex)
+#        Column #2 is the Unicode (in hex as 0xXXXX)
+#        Column #3 is the Unicode name (follows a comment sign, '#')
+#
+#    The entries are in cp850_DOSLatin1 order
+#
+##################
+# Lines with more than one Unicode (U+XXXX) value contain additional
+# replacement mappings added for lynx. - kw
+
+0x20-0x7e       idem
+#
+0x80	U+00c7	#LATIN CAPITAL LETTER C WITH CEDILLA
+0x81	U+00fc	U+03cb	#LATIN SMALL LETTER U WITH DIAERESIS
+0x82	U+00e9	U+03ad	#LATIN SMALL LETTER E WITH ACUTE
+0x83	U+00e2	#LATIN SMALL LETTER A WITH CIRCUMFLEX
+0x84	U+00e4	#LATIN SMALL LETTER A WITH DIAERESIS
+0x85	U+00e0	#LATIN SMALL LETTER A WITH GRAVE
+0x86	U+00e5	#LATIN SMALL LETTER A WITH RING ABOVE
+0x87	U+00e7	#LATIN SMALL LETTER C WITH CEDILLA
+0x88	U+00ea	#LATIN SMALL LETTER E WITH CIRCUMFLEX
+0x89	U+00eb	#LATIN SMALL LETTER E WITH DIAERESIS
+0x8a	U+00e8	#LATIN SMALL LETTER E WITH GRAVE
+0x8b	U+00ef	U+03ca	#LATIN SMALL LETTER I WITH DIAERESIS
+0x8c	U+00ee	#LATIN SMALL LETTER I WITH CIRCUMFLEX
+0x8d	U+00ec	#LATIN SMALL LETTER I WITH GRAVE
+0x8e	U+00c4	#LATIN CAPITAL LETTER A WITH DIAERESIS
+0x8f	U+00c5	#LATIN CAPITAL LETTER A WITH RING ABOVE
+0x90	U+00c9	U+0388	#LATIN CAPITAL LETTER E WITH ACUTE
+0x91	U+00e6	#LATIN SMALL LIGATURE AE
+0x92	U+00c6	#LATIN CAPITAL LIGATURE AE
+0x93	U+00f4	#LATIN SMALL LETTER O WITH CIRCUMFLEX
+0x94	U+00f6	#LATIN SMALL LETTER O WITH DIAERESIS
+0x95	U+00f2	#LATIN SMALL LETTER O WITH GRAVE
+0x96	U+00fb	#LATIN SMALL LETTER U WITH CIRCUMFLEX
+0x97	U+00f9	#LATIN SMALL LETTER U WITH GRAVE
+0x98	U+00ff	#LATIN SMALL LETTER Y WITH DIAERESIS
+0x99	U+00d6	#LATIN CAPITAL LETTER O WITH DIAERESIS
+0x9a	U+00dc	U+03ab	#LATIN CAPITAL LETTER U WITH DIAERESIS
+0x9b	U+00f8	#LATIN SMALL LETTER O WITH STROKE
+0x9c	U+00a3	#POUND SIGN
+0x9d	U+00d8	#LATIN CAPITAL LETTER O WITH STROKE
+0x9e	U+00d7	#MULTIPLICATION SIGN
+0x9f	U+0192	#LATIN SMALL LETTER F WITH HOOK
+0xa0	U+00e1	U+03ac	#LATIN SMALL LETTER A WITH ACUTE
+0xa1	U+00ed	U+03af	#LATIN SMALL LETTER I WITH ACUTE
+0xa2	U+00f3	U+03cc	#LATIN SMALL LETTER O WITH ACUTE
+0xa3	U+00fa	U+03cd	#LATIN SMALL LETTER U WITH ACUTE
+0xa4	U+00f1	#LATIN SMALL LETTER N WITH TILDE
+0xa5	U+00d1	#LATIN CAPITAL LETTER N WITH TILDE
+0xa6	U+00aa	#FEMININE ORDINAL INDICATOR
+0xa7	U+00ba	#MASCULINE ORDINAL INDICATOR
+0xa8	U+00bf	#INVERTED QUESTION MARK
+0xa9	U+00ae	#REGISTERED SIGN
+0xaa	U+00ac	#NOT SIGN
+0xab	U+00bd	#VULGAR FRACTION ONE HALF
+0xac	U+00bc	#VULGAR FRACTION ONE QUARTER
+0xad	U+00a1	#INVERTED EXCLAMATION MARK
+0xae	U+00ab	#LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xaf	U+00bb	#RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xb0	U+2591	#LIGHT SHADE
+0xb1	U+2592	#MEDIUM SHADE
+0xb2	U+2593	#DARK SHADE
+0xb3	U+2502	#BOX DRAWINGS LIGHT VERTICAL
+0xb4	U+2524	#BOX DRAWINGS LIGHT VERTICAL AND LEFT
+0xb5	U+00c1	U+0386	#LATIN CAPITAL LETTER A WITH ACUTE
+0xb6	U+00c2	#LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xb7	U+00c0	#LATIN CAPITAL LETTER A WITH GRAVE
+0xb8	U+00a9	#COPYRIGHT SIGN
+0xb9	U+2563	#BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+0xba	U+2551	#BOX DRAWINGS DOUBLE VERTICAL
+0xbb	U+2557	#BOX DRAWINGS DOUBLE DOWN AND LEFT
+0xbc	U+255d	#BOX DRAWINGS DOUBLE UP AND LEFT
+0xbd	U+00a2	#CENT SIGN
+0xbe	U+00a5	#YEN SIGN
+0xbf	U+2510	#BOX DRAWINGS LIGHT DOWN AND LEFT
+0xc0	U+2514	#BOX DRAWINGS LIGHT UP AND RIGHT
+0xc1	U+2534	#BOX DRAWINGS LIGHT UP AND HORIZONTAL
+0xc2	U+252c	#BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+0xc3	U+251c	#BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+0xc4	U+2500	#BOX DRAWINGS LIGHT HORIZONTAL
+0xc5	U+253c	#BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+0xc6	U+00e3	#LATIN SMALL LETTER A WITH TILDE
+0xc7	U+00c3	#LATIN CAPITAL LETTER A WITH TILDE
+0xc8	U+255a	#BOX DRAWINGS DOUBLE UP AND RIGHT
+0xc9	U+2554	#BOX DRAWINGS DOUBLE DOWN AND RIGHT
+0xca	U+2569	#BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+0xcb	U+2566	#BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+0xcc	U+2560	#BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+0xcd	U+2550	#BOX DRAWINGS DOUBLE HORIZONTAL
+0xce	U+256c	#BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+0xcf	U+00a4	#CURRENCY SIGN
+0xd0	U+00f0	#LATIN SMALL LETTER ETH
+0xd1	U+00d0	#LATIN CAPITAL LETTER ETH
+0xd2	U+00ca	#LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+0xd3	U+00cb	#LATIN CAPITAL LETTER E WITH DIAERESIS
+0xd4	U+00c8	#LATIN CAPITAL LETTER E WITH GRAVE
+0xd5	U+0131	U+03b9	#LATIN SMALL LETTER DOTLESS I
+0xd6	U+00cd	U+038a	#LATIN CAPITAL LETTER I WITH ACUTE
+0xd7	U+00ce	#LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xd8	U+00cf	U+03aa	#LATIN CAPITAL LETTER I WITH DIAERESIS
+0xd9	U+2518	#BOX DRAWINGS LIGHT UP AND LEFT
+0xda	U+250c	#BOX DRAWINGS LIGHT DOWN AND RIGHT
+0xdb	U+2588	#FULL BLOCK
+0xdc	U+2584	#LOWER HALF BLOCK
+0xdd	U+00a6	#BROKEN BAR
+0xde	U+00cc	#LATIN CAPITAL LETTER I WITH GRAVE
+0xdf	U+2580	#UPPER HALF BLOCK
+0xe0	U+00d3	U+038c	#LATIN CAPITAL LETTER O WITH ACUTE
+0xe1	U+00df	#LATIN SMALL LETTER SHARP S
+0xe2	U+00d4	#LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xe3	U+00d2	#LATIN CAPITAL LETTER O WITH GRAVE
+0xe4	U+00f5	#LATIN SMALL LETTER O WITH TILDE
+0xe5	U+00d5	#LATIN CAPITAL LETTER O WITH TILDE
+0xe6	U+00b5	U+03bc	#MICRO SIGN
+0xe7	U+00fe	#LATIN SMALL LETTER THORN
+0xe8	U+00de	#LATIN CAPITAL LETTER THORN
+0xe9	U+00da	#LATIN CAPITAL LETTER U WITH ACUTE
+0xea	U+00db	#LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+0xeb	U+00d9	#LATIN CAPITAL LETTER U WITH GRAVE
+0xec	U+00fd	#LATIN SMALL LETTER Y WITH ACUTE
+0xed	U+00dd	#LATIN CAPITAL LETTER Y WITH ACUTE
+0xee	U+00af	U+0304	#MACRON
+0xef	U+00b4	U+0301	#ACUTE ACCENT
+0xf0	U+00ad	#SOFT HYPHEN
+0xf1	U+00b1	#PLUS-MINUS SIGN
+0xf2	U+2017	U+0333	#DOUBLE LOW LINE
+0xf3	U+00be	#VULGAR FRACTION THREE QUARTERS
+0xf4	U+00b6	#PILCROW SIGN
+0xf5	U+00a7	#SECTION SIGN
+0xf6	U+00f7	#DIVISION SIGN
+0xf7	U+00b8	U+0327	#CEDILLA
+0xf8	U+00b0	U+030a	#DEGREE SIGN
+0xf9	U+00a8	U+0308	#DIAERESIS
+0xfa	U+00b7	U+0307	U+0387	U+2027	#MIDDLE DOT
+0xfb	U+00b9	#SUPERSCRIPT ONE
+0xfc	U+00b3	#SUPERSCRIPT THREE
+0xfd	U+00b2	#SUPERSCRIPT TWO
+0xfe	U+25a0	#BLACK SQUARE
+0xff	U+00a0	#NO-BREAK SPACE
+
+U+2218 " \370 "		# RING OPERATOR
+U+221b " ROOT\374 "
+U+2297 "(\236)"		# CIRCLED TIMES
+U+2299 "(\372)"		# CIRCLED DOT OPERATOR
+U+229A "(\370)"		# CIRCLED RING OPERATOR
+U+22A0 "[\236]"		# SQUARED TIMES
+U+22A1 "[\372]"		# SQUARED DOT OPERATOR
+U+22C5 " \372 "		# DOT OPERATOR
diff --git a/src/chrtrans/cp852_uni.tbl b/src/chrtrans/cp852_uni.tbl
new file mode 100644
index 00000000..978cca4e
--- /dev/null
+++ b/src/chrtrans/cp852_uni.tbl
@@ -0,0 +1,170 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Mcp852
+
+#Name as a Display Charset (used on Options screen)
+OEastern European (cp852)
+
+#Codepage number
+C852
+
+#
+#    Name:     cp852_DOSLatin2 to Unicode table
+#    Unicode version: 2.0
+#    Table version: 2.00
+#    Table format:  Format A
+#    Date:          04/24/96
+#    Authors:       Lori Brownell <loribr@microsoft.com>
+#                   K.D. Chang    <a-kchang@microsoft.com>
+#    General notes: none
+#
+#    Format: Three tab-separated columns
+#        Column #1 is the cp852_DOSLatin2 code (in hex)
+#        Column #2 is the Unicode (in hex as 0xXXXX)
+#        Column #3 is the Unicode name (follows a comment sign, '#')
+#
+#    The entries are in cp852_DOSLatin2 order
+#
+##################
+# Lines with more than one Unicode (U+XXXX) value contain additional
+# replacement mappings added for lynx. - kw
+
+0x20-0x7e       idem
+#
+0x80	U+00c7	#LATIN CAPITAL LETTER C WITH CEDILLA
+0x81	U+00fc	U+03cb	#LATIN SMALL LETTER U WITH DIAERESIS
+0x82	U+00e9	U+03ad	#LATIN SMALL LETTER E WITH ACUTE
+0x83	U+00e2	#LATIN SMALL LETTER A WITH CIRCUMFLEX
+0x84	U+00e4	#LATIN SMALL LETTER A WITH DIAERESIS
+0x85	U+016f	#LATIN SMALL LETTER U WITH RING ABOVE
+0x86	U+0107	#LATIN SMALL LETTER C WITH ACUTE
+0x87	U+00e7	#LATIN SMALL LETTER C WITH CEDILLA
+0x88	U+0142	#LATIN SMALL LETTER L WITH STROKE
+0x89	U+00eb	#LATIN SMALL LETTER E WITH DIAERESIS
+0x8a	U+0150	#LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
+0x8b	U+0151	#LATIN SMALL LETTER O WITH DOUBLE ACUTE
+0x8c	U+00ee	#LATIN SMALL LETTER I WITH CIRCUMFLEX
+0x8d	U+0179	#LATIN CAPITAL LETTER Z WITH ACUTE
+0x8e	U+00c4	#LATIN CAPITAL LETTER A WITH DIAERESIS
+0x8f	U+0106	#LATIN CAPITAL LETTER C WITH ACUTE
+0x90	U+00c9	U+0388	#LATIN CAPITAL LETTER E WITH ACUTE
+0x91	U+0139	#LATIN CAPITAL LETTER L WITH ACUTE
+0x92	U+013a	#LATIN SMALL LETTER L WITH ACUTE
+0x93	U+00f4	#LATIN SMALL LETTER O WITH CIRCUMFLEX
+0x94	U+00f6	#LATIN SMALL LETTER O WITH DIAERESIS
+0x95	U+013d	#LATIN CAPITAL LETTER L WITH CARON
+0x96	U+013e	#LATIN SMALL LETTER L WITH CARON
+0x97	U+015a	#LATIN CAPITAL LETTER S WITH ACUTE
+0x98	U+015b	#LATIN SMALL LETTER S WITH ACUTE
+0x99	U+00d6	#LATIN CAPITAL LETTER O WITH DIAERESIS
+0x9a	U+00dc	U+03ab	#LATIN CAPITAL LETTER U WITH DIAERESIS
+0x9b	U+0164	#LATIN CAPITAL LETTER T WITH CARON
+0x9c	U+0165	#LATIN SMALL LETTER T WITH CARON
+0x9d	U+0141	#LATIN CAPITAL LETTER L WITH STROKE
+0x9e	U+00d7	#MULTIPLICATION SIGN
+0x9f	U+010d	U+02a7	U+0447	#LATIN SMALL LETTER C WITH CARON
+0xa0	U+00e1	U+03ac	#LATIN SMALL LETTER A WITH ACUTE
+0xa1	U+00ed	U+03af	#LATIN SMALL LETTER I WITH ACUTE
+0xa2	U+00f3	U+03cc	#LATIN SMALL LETTER O WITH ACUTE
+0xa3	U+00fa	U+03cd	#LATIN SMALL LETTER U WITH ACUTE
+0xa4	U+0104	#LATIN CAPITAL LETTER A WITH OGONEK
+0xa5	U+0105	#LATIN SMALL LETTER A WITH OGONEK
+0xa6	U+017d	U+0416	#LATIN CAPITAL LETTER Z WITH CARON
+0xa7	U+017e	U+0436	#LATIN SMALL LETTER Z WITH CARON
+0xa8	U+0118	#LATIN CAPITAL LETTER E WITH OGONEK
+0xa9	U+0119	#LATIN SMALL LETTER E WITH OGONEK
+0xaa	U+00ac	#NOT SIGN
+0xab	U+017a	#LATIN SMALL LETTER Z WITH ACUTE
+0xac	U+010c	U+0427	#LATIN CAPITAL LETTER C WITH CARON
+0xad	U+015f	#LATIN SMALL LETTER S WITH CEDILLA
+0xae	U+00ab	#LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xaf	U+00bb	#RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xb0	U+2591	#LIGHT SHADE
+0xb1	U+2592	#MEDIUM SHADE
+0xb2	U+2593	#DARK SHADE
+0xb3	U+2502	#BOX DRAWINGS LIGHT VERTICAL
+0xb4	U+2524	#BOX DRAWINGS LIGHT VERTICAL AND LEFT
+0xb5	U+00c1	U+0386	#LATIN CAPITAL LETTER A WITH ACUTE
+0xb6	U+00c2	#LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xb7	U+011a	#LATIN CAPITAL LETTER E WITH CARON
+0xb8	U+015e	#LATIN CAPITAL LETTER S WITH CEDILLA
+0xb9	U+2563	#BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+0xba	U+2551	#BOX DRAWINGS DOUBLE VERTICAL
+0xbb	U+2557	#BOX DRAWINGS DOUBLE DOWN AND LEFT
+0xbc	U+255d	#BOX DRAWINGS DOUBLE UP AND LEFT
+0xbd	U+017b	#LATIN CAPITAL LETTER Z WITH DOT ABOVE
+0xbe	U+017c	#LATIN SMALL LETTER Z WITH DOT ABOVE
+0xbf	U+2510	#BOX DRAWINGS LIGHT DOWN AND LEFT
+0xc0	U+2514	#BOX DRAWINGS LIGHT UP AND RIGHT
+0xc1	U+2534	#BOX DRAWINGS LIGHT UP AND HORIZONTAL
+0xc2	U+252c	#BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+0xc3	U+251c	#BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+0xc4	U+2500	#BOX DRAWINGS LIGHT HORIZONTAL
+0xc5	U+253c	#BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+0xc6	U+0102	#LATIN CAPITAL LETTER A WITH BREVE
+0xc7	U+0103	#LATIN SMALL LETTER A WITH BREVE
+0xc8	U+255a	#BOX DRAWINGS DOUBLE UP AND RIGHT
+0xc9	U+2554	#BOX DRAWINGS DOUBLE DOWN AND RIGHT
+0xca	U+2569	#BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+0xcb	U+2566	#BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+0xcc	U+2560	#BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+0xcd	U+2550	#BOX DRAWINGS DOUBLE HORIZONTAL
+0xce	U+256c	#BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+0xcf	U+00a4	#CURRENCY SIGN
+0xd0	U+0111	#LATIN SMALL LETTER D WITH STROKE
+0xd1	U+0110	#LATIN CAPITAL LETTER D WITH STROKE
+0xd2	U+010e	#LATIN CAPITAL LETTER D WITH CARON
+0xd3	U+00cb	#LATIN CAPITAL LETTER E WITH DIAERESIS
+0xd4	U+010f	#LATIN SMALL LETTER D WITH CARON
+0xd5	U+0147	#LATIN CAPITAL LETTER N WITH CARON
+0xd6	U+00cd	U+038a	#LATIN CAPITAL LETTER I WITH ACUTE
+0xd7	U+00ce	#LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xd8	U+011b	#LATIN SMALL LETTER E WITH CARON
+0xd9	U+2518	#BOX DRAWINGS LIGHT UP AND LEFT
+0xda	U+250c	#BOX DRAWINGS LIGHT DOWN AND RIGHT
+0xdb	U+2588	#FULL BLOCK
+0xdc	U+2584	#LOWER HALF BLOCK
+0xdd	U+0162	#LATIN CAPITAL LETTER T WITH CEDILLA
+0xde	U+016e	#LATIN CAPITAL LETTER U WITH RING ABOVE
+0xdf	U+2580	#UPPER HALF BLOCK
+0xe0	U+00d3	U+038c	#LATIN CAPITAL LETTER O WITH ACUTE
+0xe1	U+00df	#LATIN SMALL LETTER SHARP S
+0xe2	U+00d4	#LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xe3	U+0143	#LATIN CAPITAL LETTER N WITH ACUTE
+0xe4	U+0144	#LATIN SMALL LETTER N WITH ACUTE
+0xe5	U+0148	#LATIN SMALL LETTER N WITH CARON
+0xe6	U+0160	U+0428	#LATIN CAPITAL LETTER S WITH CARON
+0xe7	U+0161	U+0448	#LATIN SMALL LETTER S WITH CARON
+0xe8	U+0154	#LATIN CAPITAL LETTER R WITH ACUTE
+0xe9	U+00da	#LATIN CAPITAL LETTER U WITH ACUTE
+0xea	U+0155	#LATIN SMALL LETTER R WITH ACUTE
+0xeb	U+0170	#LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
+0xec	U+00fd	#LATIN SMALL LETTER Y WITH ACUTE
+0xed	U+00dd	#LATIN CAPITAL LETTER Y WITH ACUTE
+0xee	U+0163	#LATIN SMALL LETTER T WITH CEDILLA
+0xef	U+00b4	U+0301	#ACUTE ACCENT
+0xf0	U+00ad	#SOFT HYPHEN
+0xf1	U+02dd	U+030b	#DOUBLE ACUTE ACCENT
+0xf2	U+02db	U+0328	#OGONEK
+0xf3	U+02c7	U+030c	#CARON
+0xf4	U+02d8	U+0306	#BREVE
+0xf5	U+00a7	#SECTION SIGN
+0xf6	U+00f7	#DIVISION SIGN
+0xf7	U+00b8	U+0327	#CEDILLA
+0xf8	U+00b0	U+030a	#DEGREE SIGN
+0xf9	U+00a8	U+0308	#DIAERESIS
+0xfa	U+02d9	U+0307	U+0387	#DOT ABOVE
+0xfb	U+0171	#LATIN SMALL LETTER U WITH DOUBLE ACUTE
+0xfc	U+0158	#LATIN CAPITAL LETTER R WITH CARON
+0xfd	U+0159	#LATIN SMALL LETTER R WITH CARON
+0xfe	U+25a0	#BLACK SQUARE
+0xff	U+00a0	#NO-BREAK SPACE
+
+U+2218 " \370 "		# RING OPERATOR
+U+2297 "(\236)"		# CIRCLED TIMES
+U+2299 "(\372)"		# CIRCLED DOT OPERATOR
+U+229A "(\370)"		# CIRCLED RING OPERATOR
+U+22A0 "[\236]"		# SQUARED TIMES
+U+22A1 "[\372]"		# SQUARED DOT OPERATOR
+U+22C5 " \372 "		# DOT OPERATOR
diff --git a/src/chrtrans/cp857_uni.tbl b/src/chrtrans/cp857_uni.tbl
new file mode 100644
index 00000000..bd309968
--- /dev/null
+++ b/src/chrtrans/cp857_uni.tbl
@@ -0,0 +1,159 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Mcp857
+
+#Name as a Display Charset (used on Options screen).
+OTurkish (cp857)
+
+#Codepage number
+C857
+
+#    Name:     cp857_DOSTurkish to Unicode table
+#    Unicode version: 2.0
+#    Table version: 2.00
+#    Table format:  Format A
+#    Date:          04/24/96
+#    Authors:       Lori Brownell <loribr@microsoft.com>
+#                   K.D. Chang    <a-kchang@microsoft.com>
+#    General notes: none
+#
+#    Format: Three tab-separated columns
+#        Column #1 is the cp857_DOSTurkish code (in hex)
+#        Column #2 is the Unicode (in hex as 0xXXXX)
+#        Column #3 is the Unicode name (follows a comment sign, '#')
+#
+#    The entries are in cp857_DOSTurkish order
+#
+##################
+
+0x20-0x7f       idem
+
+0x80	U+00c7	#LATIN CAPITAL LETTER C WITH CEDILLA
+0x81	U+00fc	#LATIN SMALL LETTER U WITH DIAERESIS
+0x82	U+00e9	#LATIN SMALL LETTER E WITH ACUTE
+0x83	U+00e2	#LATIN SMALL LETTER A WITH CIRCUMFLEX
+0x84	U+00e4	#LATIN SMALL LETTER A WITH DIAERESIS
+0x85	U+00e0	#LATIN SMALL LETTER A WITH GRAVE
+0x86	U+00e5	#LATIN SMALL LETTER A WITH RING ABOVE
+0x87	U+00e7	#LATIN SMALL LETTER C WITH CEDILLA
+0x88	U+00ea	#LATIN SMALL LETTER E WITH CIRCUMFLEX
+0x89	U+00eb	#LATIN SMALL LETTER E WITH DIAERESIS
+0x8a	U+00e8	#LATIN SMALL LETTER E WITH GRAVE
+0x8b	U+00ef	#LATIN SMALL LETTER I WITH DIAERESIS
+0x8c	U+00ee	#LATIN SMALL LETTER I WITH CIRCUMFLEX
+0x8d	U+0131	#LATIN SMALL LETTER DOTLESS I
+0x8e	U+00c4	#LATIN CAPITAL LETTER A WITH DIAERESIS
+0x8f	U+00c5	#LATIN CAPITAL LETTER A WITH RING ABOVE
+0x90	U+00c9	#LATIN CAPITAL LETTER E WITH ACUTE
+0x91	U+00e6	#LATIN SMALL LIGATURE AE
+0x92	U+00c6	#LATIN CAPITAL LIGATURE AE
+0x93	U+00f4	#LATIN SMALL LETTER O WITH CIRCUMFLEX
+0x94	U+00f6	#LATIN SMALL LETTER O WITH DIAERESIS
+0x95	U+00f2	#LATIN SMALL LETTER O WITH GRAVE
+0x96	U+00fb	#LATIN SMALL LETTER U WITH CIRCUMFLEX
+0x97	U+00f9	#LATIN SMALL LETTER U WITH GRAVE
+0x98	U+0130	#LATIN CAPITAL LETTER I WITH DOT ABOVE
+0x99	U+00d6	#LATIN CAPITAL LETTER O WITH DIAERESIS
+0x9a	U+00dc	#LATIN CAPITAL LETTER U WITH DIAERESIS
+0x9b	U+00f8	#LATIN SMALL LETTER O WITH STROKE
+0x9c	U+00a3	#POUND SIGN
+0x9d	U+00d8	#LATIN CAPITAL LETTER O WITH STROKE
+0x9e	U+015e	#LATIN CAPITAL LETTER S WITH CEDILLA
+0x9f	U+015f	#LATIN SMALL LETTER S WITH CEDILLA
+0xa0	U+00e1	#LATIN SMALL LETTER A WITH ACUTE
+0xa1	U+00ed	#LATIN SMALL LETTER I WITH ACUTE
+0xa2	U+00f3	#LATIN SMALL LETTER O WITH ACUTE
+0xa3	U+00fa	#LATIN SMALL LETTER U WITH ACUTE
+0xa4	U+00f1	#LATIN SMALL LETTER N WITH TILDE
+0xa5	U+00d1	#LATIN CAPITAL LETTER N WITH TILDE
+0xa6	U+011e	#LATIN CAPITAL LETTER G WITH BREVE
+0xa7	U+011f	#LATIN SMALL LETTER G WITH BREVE
+0xa8	U+00bf	#INVERTED QUESTION MARK
+0xa9	U+00ae	#REGISTERED SIGN
+0xaa	U+00ac	#NOT SIGN
+0xab	U+00bd	#VULGAR FRACTION ONE HALF
+0xac	U+00bc	#VULGAR FRACTION ONE QUARTER
+0xad	U+00a1	#INVERTED EXCLAMATION MARK
+0xae	U+00ab	#LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xaf	U+00bb	#RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xb0	U+2591	#LIGHT SHADE
+0xb1	U+2592	#MEDIUM SHADE
+0xb2	U+2593	#DARK SHADE
+0xb3	U+2502	#BOX DRAWINGS LIGHT VERTICAL
+0xb4	U+2524	#BOX DRAWINGS LIGHT VERTICAL AND LEFT
+0xb5	U+00c1	#LATIN CAPITAL LETTER A WITH ACUTE
+0xb6	U+00c2	#LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xb7	U+00c0	#LATIN CAPITAL LETTER A WITH GRAVE
+0xb8	U+00a9	#COPYRIGHT SIGN
+0xb9	U+2563	#BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+0xba	U+2551	#BOX DRAWINGS DOUBLE VERTICAL
+0xbb	U+2557	#BOX DRAWINGS DOUBLE DOWN AND LEFT
+0xbc	U+255d	#BOX DRAWINGS DOUBLE UP AND LEFT
+0xbd	U+00a2	#CENT SIGN
+0xbe	U+00a5	#YEN SIGN
+0xbf	U+2510	#BOX DRAWINGS LIGHT DOWN AND LEFT
+0xc0	U+2514	#BOX DRAWINGS LIGHT UP AND RIGHT
+0xc1	U+2534	#BOX DRAWINGS LIGHT UP AND HORIZONTAL
+0xc2	U+252c	#BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+0xc3	U+251c	#BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+0xc4	U+2500	#BOX DRAWINGS LIGHT HORIZONTAL
+0xc5	U+253c	#BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+0xc6	U+00e3	#LATIN SMALL LETTER A WITH TILDE
+0xc7	U+00c3	#LATIN CAPITAL LETTER A WITH TILDE
+0xc8	U+255a	#BOX DRAWINGS DOUBLE UP AND RIGHT
+0xc9	U+2554	#BOX DRAWINGS DOUBLE DOWN AND RIGHT
+0xca	U+2569	#BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+0xcb	U+2566	#BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+0xcc	U+2560	#BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+0xcd	U+2550	#BOX DRAWINGS DOUBLE HORIZONTAL
+0xce	U+256c	#BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+0xcf	U+00a4	#CURRENCY SIGN
+0xd0	U+00ba	#MASCULINE ORDINAL INDICATOR
+0xd1	U+00aa	#FEMININE ORDINAL INDICATOR
+0xd2	U+00ca	#LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+0xd3	U+00cb	#LATIN CAPITAL LETTER E WITH DIAERESIS
+0xd4	U+00c8	#LATIN CAPITAL LETTER E WITH GRAVE
+0xd5		#UNDEFINED
+0xd6	U+00cd	#LATIN CAPITAL LETTER I WITH ACUTE
+0xd7	U+00ce	#LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xd8	U+00cf	#LATIN CAPITAL LETTER I WITH DIAERESIS
+0xd9	U+2518	#BOX DRAWINGS LIGHT UP AND LEFT
+0xda	U+250c	#BOX DRAWINGS LIGHT DOWN AND RIGHT
+0xdb	U+2588	#FULL BLOCK
+0xdc	U+2584	#LOWER HALF BLOCK
+0xdd	U+00a6	#BROKEN BAR
+0xde	U+00cc	#LATIN CAPITAL LETTER I WITH GRAVE
+0xdf	U+2580	#UPPER HALF BLOCK
+0xe0	U+00d3	#LATIN CAPITAL LETTER O WITH ACUTE
+0xe1	U+00df	#LATIN SMALL LETTER SHARP S
+0xe2	U+00d4	#LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xe3	U+00d2	#LATIN CAPITAL LETTER O WITH GRAVE
+0xe4	U+00f5	#LATIN SMALL LETTER O WITH TILDE
+0xe5	U+00d5	#LATIN CAPITAL LETTER O WITH TILDE
+0xe6	U+00b5	#MICRO SIGN
+0xe7		#UNDEFINED
+0xe8	U+00d7	#MULTIPLICATION SIGN
+0xe9	U+00da	#LATIN CAPITAL LETTER U WITH ACUTE
+0xea	U+00db	#LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+0xeb	U+00d9	#LATIN CAPITAL LETTER U WITH GRAVE
+0xec	U+00ec	#LATIN SMALL LETTER I WITH GRAVE
+0xed	U+00ff	#LATIN SMALL LETTER Y WITH DIAERESIS
+0xee	U+00af	#MACRON
+0xef	U+00b4	#ACUTE ACCENT
+0xf0	U+00ad	#SOFT HYPHEN
+0xf1	U+00b1	#PLUS-MINUS SIGN
+0xf2		#UNDEFINED
+0xf3	U+00be	#VULGAR FRACTION THREE QUARTERS
+0xf4	U+00b6	#PILCROW SIGN
+0xf5	U+00a7	#SECTION SIGN
+0xf6	U+00f7	#DIVISION SIGN
+0xf7	U+00b8	#CEDILLA
+0xf8	U+00b0	#DEGREE SIGN
+0xf9	U+00a8	#DIAERESIS
+0xfa	U+00b7	#MIDDLE DOT
+0xfb	U+00b9	#SUPERSCRIPT ONE
+0xfc	U+00b3	#SUPERSCRIPT THREE
+0xfd	U+00b2	#SUPERSCRIPT TWO
+0xfe	U+25a0	#BLACK SQUARE
+0xff	U+00a0	#NO-BREAK SPACE
diff --git a/src/chrtrans/cp862_uni.tbl b/src/chrtrans/cp862_uni.tbl
new file mode 100644
index 00000000..ebf12224
--- /dev/null
+++ b/src/chrtrans/cp862_uni.tbl
@@ -0,0 +1,160 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Mcp862
+
+#Name as a Display Charset (used on Options screen).
+OHebrew (cp862)
+
+#Codepage number
+C862
+
+#    Name:     cp862_DOSHebrew to Unicode table
+#    Unicode version: 2.0
+#    Table version: 2.00
+#    Table format:  Format A
+#    Date:          04/24/96
+#    Authors:       Lori Brownell <loribr@microsoft.com>
+#                   K.D. Chang    <a-kchang@microsoft.com>
+#    General notes: none
+#
+#    Format: Three tab-separated columns
+#        Column #1 is the cp862_DOSHebrew code (in hex)
+#        Column #2 is the Unicode (in hex as 0xXXXX)
+#        Column #3 is the Unicode name (follows a comment sign, '#')
+#
+#    The entries are in cp862_DOSHebrew order
+#
+##################
+
+0x20-0x7f       idem
+#
+0x80	U+05d0	#HEBREW LETTER ALEF
+0x81	U+05d1	#HEBREW LETTER BET
+0x82	U+05d2	#HEBREW LETTER GIMEL
+0x83	U+05d3	#HEBREW LETTER DALET
+0x84	U+05d4	#HEBREW LETTER HE
+0x85	U+05d5	#HEBREW LETTER VAV
+0x86	U+05d6	#HEBREW LETTER ZAYIN
+0x87	U+05d7	#HEBREW LETTER HET
+0x88	U+05d8	#HEBREW LETTER TET
+0x89	U+05d9	#HEBREW LETTER YOD
+0x8a	U+05da	#HEBREW LETTER FINAL KAF
+0x8b	U+05db	#HEBREW LETTER KAF
+0x8c	U+05dc	#HEBREW LETTER LAMED
+0x8d	U+05dd	#HEBREW LETTER FINAL MEM
+0x8e	U+05de	#HEBREW LETTER MEM
+0x8f	U+05df	#HEBREW LETTER FINAL NUN
+0x90	U+05e0	#HEBREW LETTER NUN
+0x91	U+05e1	#HEBREW LETTER SAMEKH
+0x92	U+05e2	#HEBREW LETTER AYIN
+0x93	U+05e3	#HEBREW LETTER FINAL PE
+0x94	U+05e4	#HEBREW LETTER PE
+0x95	U+05e5	#HEBREW LETTER FINAL TSADI
+0x96	U+05e6	#HEBREW LETTER TSADI
+0x97	U+05e7	#HEBREW LETTER QOF
+0x98	U+05e8	#HEBREW LETTER RESH
+0x99	U+05e9	#HEBREW LETTER SHIN
+0x9a	U+05ea	#HEBREW LETTER TAV
+0x9b	U+00a2	#CENT SIGN
+0x9c	U+00a3	#POUND SIGN
+0x9d	U+00a5	#YEN SIGN
+0x9e	U+20a7	#PESETA SIGN
+0x9f	U+0192	#LATIN SMALL LETTER F WITH HOOK
+0xa0	U+00e1	#LATIN SMALL LETTER A WITH ACUTE
+0xa1	U+00ed	#LATIN SMALL LETTER I WITH ACUTE
+0xa2	U+00f3	#LATIN SMALL LETTER O WITH ACUTE
+0xa3	U+00fa	#LATIN SMALL LETTER U WITH ACUTE
+0xa4	U+00f1	#LATIN SMALL LETTER N WITH TILDE
+0xa5	U+00d1	#LATIN CAPITAL LETTER N WITH TILDE
+0xa6	U+00aa	#FEMININE ORDINAL INDICATOR
+0xa7	U+00ba	#MASCULINE ORDINAL INDICATOR
+0xa8	U+00bf	#INVERTED QUESTION MARK
+0xa9	U+2310	#REVERSED NOT SIGN
+0xaa	U+00ac	#NOT SIGN
+0xab	U+00bd	#VULGAR FRACTION ONE HALF
+0xac	U+00bc	#VULGAR FRACTION ONE QUARTER
+0xad	U+00a1	#INVERTED EXCLAMATION MARK
+0xae	U+00ab	#LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xaf	U+00bb	#RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xb0	U+2591	#LIGHT SHADE
+0xb1	U+2592	#MEDIUM SHADE
+0xb2	U+2593	#DARK SHADE
+0xb3	U+2502	#BOX DRAWINGS LIGHT VERTICAL
+0xb4	U+2524	#BOX DRAWINGS LIGHT VERTICAL AND LEFT
+0xb5	U+2561	#BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
+0xb6	U+2562	#BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
+0xb7	U+2556	#BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
+0xb8	U+2555	#BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
+0xb9	U+2563	#BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+0xba	U+2551	#BOX DRAWINGS DOUBLE VERTICAL
+0xbb	U+2557	#BOX DRAWINGS DOUBLE DOWN AND LEFT
+0xbc	U+255d	#BOX DRAWINGS DOUBLE UP AND LEFT
+0xbd	U+255c	#BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
+0xbe	U+255b	#BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
+0xbf	U+2510	#BOX DRAWINGS LIGHT DOWN AND LEFT
+0xc0	U+2514	#BOX DRAWINGS LIGHT UP AND RIGHT
+0xc1	U+2534	#BOX DRAWINGS LIGHT UP AND HORIZONTAL
+0xc2	U+252c	#BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+0xc3	U+251c	#BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+0xc4	U+2500	#BOX DRAWINGS LIGHT HORIZONTAL
+0xc5	U+253c	#BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+0xc6	U+255e	#BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
+0xc7	U+255f	#BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
+0xc8	U+255a	#BOX DRAWINGS DOUBLE UP AND RIGHT
+0xc9	U+2554	#BOX DRAWINGS DOUBLE DOWN AND RIGHT
+0xca	U+2569	#BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+0xcb	U+2566	#BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+0xcc	U+2560	#BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+0xcd	U+2550	#BOX DRAWINGS DOUBLE HORIZONTAL
+0xce	U+256c	#BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+0xcf	U+2567	#BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
+0xd0	U+2568	#BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
+0xd1	U+2564	#BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
+0xd2	U+2565	#BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
+0xd3	U+2559	#BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
+0xd4	U+2558	#BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
+0xd5	U+2552	#BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
+0xd6	U+2553	#BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
+0xd7	U+256b	#BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
+0xd8	U+256a	#BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
+0xd9	U+2518	#BOX DRAWINGS LIGHT UP AND LEFT
+0xda	U+250c	#BOX DRAWINGS LIGHT DOWN AND RIGHT
+0xdb	U+2588	#FULL BLOCK
+0xdc	U+2584	#LOWER HALF BLOCK
+0xdd	U+258c	#LEFT HALF BLOCK
+0xde	U+2590	#RIGHT HALF BLOCK
+0xdf	U+2580	#UPPER HALF BLOCK
+0xe0	U+03b1	#GREEK SMALL LETTER ALPHA
+0xe1	U+00df	#LATIN SMALL LETTER SHARP S (GERMAN)
+0xe2	U+0393	#GREEK CAPITAL LETTER GAMMA
+0xe3	U+03c0	#GREEK SMALL LETTER PI
+0xe4	U+03a3	#GREEK CAPITAL LETTER SIGMA
+0xe5	U+03c3	#GREEK SMALL LETTER SIGMA
+0xe6	U+00b5	#MICRO SIGN
+0xe7	U+03c4	#GREEK SMALL LETTER TAU
+0xe8	U+03a6	#GREEK CAPITAL LETTER PHI
+0xe9	U+0398	#GREEK CAPITAL LETTER THETA
+0xea	U+03a9	#GREEK CAPITAL LETTER OMEGA
+0xeb	U+03b4	#GREEK SMALL LETTER DELTA
+0xec	U+221e	#INFINITY
+0xed	U+03c6	#GREEK SMALL LETTER PHI
+0xee	U+03b5	#GREEK SMALL LETTER EPSILON
+0xef	U+2229	#INTERSECTION
+0xf0	U+2261	#IDENTICAL TO
+0xf1	U+00b1	#PLUS-MINUS SIGN
+0xf2	U+2265	#GREATER-THAN OR EQUAL TO
+0xf3	U+2264	#LESS-THAN OR EQUAL TO
+0xf4	U+2320	#TOP HALF INTEGRAL
+0xf5	U+2321	#BOTTOM HALF INTEGRAL
+0xf6	U+00f7	#DIVISION SIGN
+0xf7	U+2248	#ALMOST EQUAL TO
+0xf8	U+00b0	#DEGREE SIGN
+0xf9	U+2219	#BULLET OPERATOR
+0xfa	U+00b7	#MIDDLE DOT
+0xfb	U+221a	#SQUARE ROOT
+0xfc	U+207f	#SUPERSCRIPT LATIN SMALL LETTER N
+0xfd	U+00b2	#SUPERSCRIPT TWO
+0xfe	U+25a0	#BLACK SQUARE
+0xff	U+00a0	#NO-BREAK SPACE
+
diff --git a/src/chrtrans/cp864_uni.tbl b/src/chrtrans/cp864_uni.tbl
new file mode 100644
index 00000000..65685777
--- /dev/null
+++ b/src/chrtrans/cp864_uni.tbl
@@ -0,0 +1,160 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Mcp864
+
+#Name as a Display Charset (used on Options screen).
+OArabic (cp864)
+
+#Codepage number
+C864
+
+#    Name:     cp864_DOSArabic to Unicode table
+#    Unicode version: 2.0
+#    Table version: 2.00
+#    Table format:  Format A
+#    Date:          04/24/96
+#    Authors:       Lori Brownell <loribr@microsoft.com>
+#                   K.D. Chang    <a-kchang@microsoft.com>
+#    General notes: none
+#
+#    Format: Three tab-separated columns
+#        Column #1 is the cp864_DOSArabic code (in hex)
+#        Column #2 is the Unicode (in hex as 0xXXXX)
+#        Column #3 is the Unicode name (follows a comment sign, '#')
+#
+#    The entries are in cp864_DOSArabic order
+#
+##################
+
+0x20-0x7f       idem
+#
+0x80	U+00b0	#DEGREE SIGN
+0x81	U+00b7	#MIDDLE DOT
+0x82	U+2219	#BULLET OPERATOR
+0x83	U+221a	#SQUARE ROOT
+0x84	U+2592	#MEDIUM SHADE
+0x85	U+2500	#FORMS LIGHT HORIZONTAL
+0x86	U+2502	#FORMS LIGHT VERTICAL
+0x87	U+253c	#FORMS LIGHT VERTICAL AND HORIZONTAL
+0x88	U+2524	#FORMS LIGHT VERTICAL AND LEFT
+0x89	U+252c	#FORMS LIGHT DOWN AND HORIZONTAL
+0x8a	U+251c	#FORMS LIGHT VERTICAL AND RIGHT
+0x8b	U+2534	#FORMS LIGHT UP AND HORIZONTAL
+0x8c	U+2510	#FORMS LIGHT DOWN AND LEFT
+0x8d	U+250c	#FORMS LIGHT DOWN AND RIGHT
+0x8e	U+2514	#FORMS LIGHT UP AND RIGHT
+0x8f	U+2518	#FORMS LIGHT UP AND LEFT
+0x90	U+03b2	#GREEK SMALL BETA
+0x91	U+221e	#INFINITY
+0x92	U+03c6	#GREEK SMALL PHI
+0x93	U+00b1	#PLUS-OR-MINUS SIGN
+0x94	U+00bd	#FRACTION 1/2
+0x95	U+00bc	#FRACTION 1/4
+0x96	U+2248	#ALMOST EQUAL TO
+0x97	U+00ab	#LEFT POINTING GUILLEMET
+0x98	U+00bb	#RIGHT POINTING GUILLEMET
+0x99	U+fef7	#ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE ISOLATED FORM
+0x9a	U+fef8	#ARABIC LIGATURE LAM WITH ALEF WITH HAMZA ABOVE FINAL FORM
+#0x9b		#UNDEFINED
+#0x9c		#UNDEFINED
+0x9d	U+fefb	#ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM
+0x9e	U+fefc	#ARABIC LIGATURE LAM WITH ALEF FINAL FORM
+#0x9f		#UNDEFINED
+0xa0	U+00a0	#NON-BREAKING SPACE
+0xa1	U+00ad	#SOFT HYPHEN
+0xa2	U+fe82	#ARABIC LETTER ALEF WITH MADDA ABOVE FINAL FORM
+0xa3	U+00a3	#POUND SIGN
+0xa4	U+00a4	#CURRENCY SIGN
+0xa5	U+fe84	#ARABIC LETTER ALEF WITH HAMZA ABOVE FINAL FORM
+#0xa6		#UNDEFINED
+#0xa7		#UNDEFINED
+0xa8	U+fe8e	#ARABIC LETTER ALEF FINAL FORM
+0xa9	U+fe8f	#ARABIC LETTER BEH ISOLATED FORM
+0xaa	U+fe95	#ARABIC LETTER TEH ISOLATED FORM
+0xab	U+fe99	#ARABIC LETTER THEH ISOLATED FORM
+0xac	U+060c	#ARABIC COMMA
+0xad	U+fe9d	#ARABIC LETTER JEEM ISOLATED FORM
+0xae	U+fea1	#ARABIC LETTER HAH ISOLATED FORM
+0xaf	U+fea5	#ARABIC LETTER KHAH ISOLATED FORM
+0xb0	U+0660	#ARABIC-INDIC DIGIT ZERO
+0xb1	U+0661	#ARABIC-INDIC DIGIT ONE
+0xb2	U+0662	#ARABIC-INDIC DIGIT TWO
+0xb3	U+0663	#ARABIC-INDIC DIGIT THREE
+0xb4	U+0664	#ARABIC-INDIC DIGIT FOUR
+0xb5	U+0665	#ARABIC-INDIC DIGIT FIVE
+0xb6	U+0666	#ARABIC-INDIC DIGIT SIX
+0xb7	U+0667	#ARABIC-INDIC DIGIT SEVEN
+0xb8	U+0668	#ARABIC-INDIC DIGIT EIGHT
+0xb9	U+0669	#ARABIC-INDIC DIGIT NINE
+0xba	U+fed1	#ARABIC LETTER FEH ISOLATED FORM
+0xbb	U+061b	#ARABIC SEMICOLON
+0xbc	U+feb1	#ARABIC LETTER SEEN ISOLATED FORM
+0xbd	U+feb5	#ARABIC LETTER SHEEN ISOLATED FORM
+0xbe	U+feb9	#ARABIC LETTER SAD ISOLATED FORM
+0xbf	U+061f	#ARABIC QUESTION MARK
+0xc0	U+00a2	#CENT SIGN
+0xc1	U+fe80	#ARABIC LETTER HAMZA ISOLATED FORM
+0xc2	U+fe81	#ARABIC LETTER ALEF WITH MADDA ABOVE ISOLATED FORM
+0xc3	U+fe83	#ARABIC LETTER ALEF WITH HAMZA ABOVE ISOLATED FORM
+0xc4	U+fe85	#ARABIC LETTER WAW WITH HAMZA ABOVE ISOLATED FORM
+0xc5	U+feca	#ARABIC LETTER AIN FINAL FORM
+0xc6	U+fe8b	#ARABIC LETTER YEH WITH HAMZA ABOVE INITIAL FORM
+0xc7	U+fe8d	#ARABIC LETTER ALEF ISOLATED FORM
+0xc8	U+fe91	#ARABIC LETTER BEH INITIAL FORM
+0xc9	U+fe93	#ARABIC LETTER TEH MARBUTA ISOLATED FORM
+0xca	U+fe97	#ARABIC LETTER TEH INITIAL FORM
+0xcb	U+fe9b	#ARABIC LETTER THEH INITIAL FORM
+0xcc	U+fe9f	#ARABIC LETTER JEEM INITIAL FORM
+0xcd	U+fea3	#ARABIC LETTER HAH INITIAL FORM
+0xce	U+fea7	#ARABIC LETTER KHAH INITIAL FORM
+0xcf	U+fea9	#ARABIC LETTER DAL ISOLATED FORM
+0xd0	U+feab	#ARABIC LETTER THAL ISOLATED FORM
+0xd1	U+fead	#ARABIC LETTER REH ISOLATED FORM
+0xd2	U+feaf	#ARABIC LETTER ZAIN ISOLATED FORM
+0xd3	U+feb3	#ARABIC LETTER SEEN INITIAL FORM
+0xd4	U+feb7	#ARABIC LETTER SHEEN INITIAL FORM
+0xd5	U+febb	#ARABIC LETTER SAD INITIAL FORM
+0xd6	U+febf	#ARABIC LETTER DAD INITIAL FORM
+0xd7	U+fec1	#ARABIC LETTER TAH ISOLATED FORM
+0xd8	U+fec5	#ARABIC LETTER ZAH ISOLATED FORM
+0xd9	U+fecb	#ARABIC LETTER AIN INITIAL FORM
+0xda	U+fecf	#ARABIC LETTER GHAIN INITIAL FORM
+0xdb	U+00a6	#BROKEN VERTICAL BAR
+0xdc	U+00ac	#NOT SIGN
+0xdd	U+00f7	#DIVISION SIGN
+0xde	U+00d7	#MULTIPLICATION SIGN
+0xdf	U+fec9	#ARABIC LETTER AIN ISOLATED FORM
+0xe0	U+0640	#ARABIC TATWEEL
+0xe1	U+fed3	#ARABIC LETTER FEH INITIAL FORM
+0xe2	U+fed7	#ARABIC LETTER QAF INITIAL FORM
+0xe3	U+fedb	#ARABIC LETTER KAF INITIAL FORM
+0xe4	U+fedf	#ARABIC LETTER LAM INITIAL FORM
+0xe5	U+fee3	#ARABIC LETTER MEEM INITIAL FORM
+0xe6	U+fee7	#ARABIC LETTER NOON INITIAL FORM
+0xe7	U+feeb	#ARABIC LETTER HEH INITIAL FORM
+0xe8	U+feed	#ARABIC LETTER WAW ISOLATED FORM
+0xe9	U+feef	#ARABIC LETTER ALEF MAKSURA ISOLATED FORM
+0xea	U+fef3	#ARABIC LETTER YEH INITIAL FORM
+0xeb	U+febd	#ARABIC LETTER DAD ISOLATED FORM
+0xec	U+fecc	#ARABIC LETTER AIN MEDIAL FORM
+0xed	U+fece	#ARABIC LETTER GHAIN FINAL FORM
+0xee	U+fecd	#ARABIC LETTER GHAIN ISOLATED FORM
+0xef	U+fee1	#ARABIC LETTER MEEM ISOLATED FORM
+0xf0	U+fe7d	#ARABIC SHADDA MEDIAL FORM
+0xf1	U+0651	#ARABIC SHADDAH
+0xf2	U+fee5	#ARABIC LETTER NOON ISOLATED FORM
+0xf3	U+fee9	#ARABIC LETTER HEH ISOLATED FORM
+0xf4	U+feec	#ARABIC LETTER HEH MEDIAL FORM
+0xf5	U+fef0	#ARABIC LETTER ALEF MAKSURA FINAL FORM
+0xf6	U+fef2	#ARABIC LETTER YEH FINAL FORM
+0xf7	U+fed0	#ARABIC LETTER GHAIN MEDIAL FORM
+0xf8	U+fed5	#ARABIC LETTER QAF ISOLATED FORM
+0xf9	U+fef5	#ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE ISOLATED FORM
+0xfa	U+fef6	#ARABIC LIGATURE LAM WITH ALEF WITH MADDA ABOVE FINAL FORM
+0xfb	U+fedd	#ARABIC LETTER LAM ISOLATED FORM
+0xfc	U+fed9	#ARABIC LETTER KAF ISOLATED FORM
+0xfd	U+fef1	#ARABIC LETTER YEH ISOLATED FORM
+0xfe	U+25a0	#BLACK SQUARE
+#0xff		#UNDEFINED
+
diff --git a/src/chrtrans/cp866_uni.tbl b/src/chrtrans/cp866_uni.tbl
new file mode 100644
index 00000000..029e0250
--- /dev/null
+++ b/src/chrtrans/cp866_uni.tbl
@@ -0,0 +1,159 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Mcp866
+
+#Name as a Display Charset (used on Options screen)
+OCyrillic (cp866)
+
+#Codepage number
+C866
+
+#
+#    Name:     cp866_DOSCyrillicRussian to Unicode table
+#    Unicode version: 2.0
+#    Table version: 2.00
+#    Table format:  Format A
+#    Date:          04/24/96
+#    Authors:       Lori Brownell <loribr@microsoft.com>
+#                   K.D. Chang    <a-kchang@microsoft.com>
+#    General notes: none
+#
+#    Format: Three tab-separated columns
+#        Column #1 is the cp866_DOSCyrillicRussian code (in hex)
+#        Column #2 is the Unicode (in hex as 0xXXXX)
+#        Column #3 is the Unicode name (follows a comment sign, '#')
+#
+#    The entries are in cp866_DOSCyrillicRussian order
+#
+0x20-0x7f       idem
+#
+0x80    U+0410  #CYRILLIC CAPITAL LETTER A
+0x81    U+0411  #CYRILLIC CAPITAL LETTER BE
+0x82    U+0412  #CYRILLIC CAPITAL LETTER VE
+0x83    U+0413  #CYRILLIC CAPITAL LETTER GHE
+0x84    U+0414  #CYRILLIC CAPITAL LETTER DE
+0x85    U+0415  #CYRILLIC CAPITAL LETTER IE
+0x86    U+0416  #CYRILLIC CAPITAL LETTER ZHE
+0x87    U+0417  #CYRILLIC CAPITAL LETTER ZE
+0x88    U+0418  #CYRILLIC CAPITAL LETTER I
+0x89    U+0419  #CYRILLIC CAPITAL LETTER SHORT I
+0x8a    U+041a  #CYRILLIC CAPITAL LETTER KA
+0x8b    U+041b  #CYRILLIC CAPITAL LETTER EL
+0x8c    U+041c  #CYRILLIC CAPITAL LETTER EM
+0x8d    U+041d  #CYRILLIC CAPITAL LETTER EN
+0x8e    U+041e  #CYRILLIC CAPITAL LETTER O
+0x8f    U+041f  #CYRILLIC CAPITAL LETTER PE
+0x90    U+0420  #CYRILLIC CAPITAL LETTER ER
+0x91    U+0421  #CYRILLIC CAPITAL LETTER ES
+0x92    U+0422  #CYRILLIC CAPITAL LETTER TE
+0x93    U+0423  #CYRILLIC CAPITAL LETTER U
+0x94    U+0424  #CYRILLIC CAPITAL LETTER EF
+0x95    U+0425  #CYRILLIC CAPITAL LETTER HA
+0x96    U+0426  #CYRILLIC CAPITAL LETTER TSE
+0x97    U+0427  #CYRILLIC CAPITAL LETTER CHE
+0x98    U+0428  #CYRILLIC CAPITAL LETTER SHA
+0x99    U+0429  #CYRILLIC CAPITAL LETTER SHCHA
+0x9a    U+042a  #CYRILLIC CAPITAL LETTER HARD SIGN
+0x9b    U+042b  #CYRILLIC CAPITAL LETTER YERU
+0x9c    U+042c  #CYRILLIC CAPITAL LETTER SOFT SIGN
+0x9d    U+042d  #CYRILLIC CAPITAL LETTER E
+0x9e    U+042e  #CYRILLIC CAPITAL LETTER YU
+0x9f    U+042f  #CYRILLIC CAPITAL LETTER YA
+0xa0    U+0430  #CYRILLIC SMALL LETTER A
+0xa1    U+0431  #CYRILLIC SMALL LETTER BE
+0xa2    U+0432  #CYRILLIC SMALL LETTER VE
+0xa3    U+0433  #CYRILLIC SMALL LETTER GHE
+0xa4    U+0434  #CYRILLIC SMALL LETTER DE
+0xa5    U+0435  #CYRILLIC SMALL LETTER IE
+0xa6    U+0436  #CYRILLIC SMALL LETTER ZHE
+0xa7    U+0437  #CYRILLIC SMALL LETTER ZE
+0xa8    U+0438  #CYRILLIC SMALL LETTER I
+0xa9    U+0439  #CYRILLIC SMALL LETTER SHORT I
+0xaa    U+043a  #CYRILLIC SMALL LETTER KA
+0xab    U+043b  #CYRILLIC SMALL LETTER EL
+0xac    U+043c  #CYRILLIC SMALL LETTER EM
+0xad    U+043d  #CYRILLIC SMALL LETTER EN
+0xae    U+043e  #CYRILLIC SMALL LETTER O
+0xaf    U+043f  #CYRILLIC SMALL LETTER PE
+0xb0    U+2591  #LIGHT SHADE
+0xb1    U+2592  #MEDIUM SHADE
+0xb2    U+2593  #DARK SHADE
+0xb3    U+2502  #BOX DRAWINGS LIGHT VERTICAL
+0xb4    U+2524  #BOX DRAWINGS LIGHT VERTICAL AND LEFT
+0xb5    U+2561  #BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
+0xb6    U+2562  #BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
+0xb7    U+2556  #BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
+0xb8    U+2555  #BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
+0xb9    U+2563  #BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+0xba    U+2551  #BOX DRAWINGS DOUBLE VERTICAL
+0xbb    U+2557  #BOX DRAWINGS DOUBLE DOWN AND LEFT
+0xbc    U+255d  #BOX DRAWINGS DOUBLE UP AND LEFT
+0xbd    U+255c  #BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
+0xbe    U+255b  #BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
+0xbf    U+2510  #BOX DRAWINGS LIGHT DOWN AND LEFT
+0xc0    U+2514  #BOX DRAWINGS LIGHT UP AND RIGHT
+0xc1    U+2534  #BOX DRAWINGS LIGHT UP AND HORIZONTAL
+0xc2    U+252c  #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+0xc3    U+251c  #BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+0xc4    U+2500  #BOX DRAWINGS LIGHT HORIZONTAL
+0xc5    U+253c  #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+0xc6    U+255e  #BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
+0xc7    U+255f  #BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
+0xc8    U+255a  #BOX DRAWINGS DOUBLE UP AND RIGHT
+0xc9    U+2554  #BOX DRAWINGS DOUBLE DOWN AND RIGHT
+0xca    U+2569  #BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+0xcb    U+2566  #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+0xcc    U+2560  #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+0xcd    U+2550  #BOX DRAWINGS DOUBLE HORIZONTAL
+0xce    U+256c  #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+0xcf    U+2567  #BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
+0xd0    U+2568  #BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
+0xd1    U+2564  #BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
+0xd2    U+2565  #BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
+0xd3    U+2559  #BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
+0xd4    U+2558  #BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
+0xd5    U+2552  #BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
+0xd6    U+2553  #BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
+0xd7    U+256b  #BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
+0xd8    U+256a  #BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
+0xd9    U+2518  #BOX DRAWINGS LIGHT UP AND LEFT
+0xda    U+250c  #BOX DRAWINGS LIGHT DOWN AND RIGHT
+0xdb    U+2588  #FULL BLOCK
+0xdc    U+2584  #LOWER HALF BLOCK
+0xdd    U+258c  #LEFT HALF BLOCK
+0xde    U+2590  #RIGHT HALF BLOCK
+0xdf    U+2580  #UPPER HALF BLOCK
+0xe0    U+0440  #CYRILLIC SMALL LETTER ER
+0xe1    U+0441  #CYRILLIC SMALL LETTER ES
+0xe2    U+0442  #CYRILLIC SMALL LETTER TE
+0xe3    U+0443  #CYRILLIC SMALL LETTER U
+0xe4    U+0444  #CYRILLIC SMALL LETTER EF
+0xe5    U+0445  #CYRILLIC SMALL LETTER HA
+0xe6    U+0446  #CYRILLIC SMALL LETTER TSE
+0xe7    U+0447  #CYRILLIC SMALL LETTER CHE
+0xe8    U+0448  #CYRILLIC SMALL LETTER SHA
+0xe9    U+0449  #CYRILLIC SMALL LETTER SHCHA
+0xea    U+044a  #CYRILLIC SMALL LETTER HARD SIGN
+0xeb    U+044b  #CYRILLIC SMALL LETTER YERU
+0xec    U+044c  #CYRILLIC SMALL LETTER SOFT SIGN
+0xed    U+044d  #CYRILLIC SMALL LETTER E
+0xee    U+044e  #CYRILLIC SMALL LETTER YU
+0xef    U+044f  #CYRILLIC SMALL LETTER YA
+0xf0    U+0401  #CYRILLIC CAPITAL LETTER IO
+0xf1    U+0451  #CYRILLIC SMALL LETTER IO
+0xf2    U+0404  #CYRILLIC CAPITAL LETTER UKRAINIAN IE
+0xf3    U+0454  #CYRILLIC SMALL LETTER UKRAINIAN IE
+0xf4    U+0407  #CYRILLIC CAPITAL LETTER YI
+0xf5    U+0457  #CYRILLIC SMALL LETTER YI
+0xf6    U+040e  #CYRILLIC CAPITAL LETTER SHORT U
+0xf7    U+045e  #CYRILLIC SMALL LETTER SHORT U
+0xf8    U+00b0  #DEGREE SIGN
+0xf9    U+2219  #BULLET OPERATOR
+0xfa    U+00b7  #MIDDLE DOT
+0xfb    U+221a  #SQUARE ROOT
+0xfc    U+2116  #NUMERO SIGN
+0xfd    U+00a4  #CURRENCY SIGN
+0xfe    U+25a0  #BLACK SQUARE
+0xff    U+00a0  #NO-BREAK SPACE
+
diff --git a/src/chrtrans/cp866u_uni.tbl b/src/chrtrans/cp866u_uni.tbl
new file mode 100644
index 00000000..8d2dee0c
--- /dev/null
+++ b/src/chrtrans/cp866u_uni.tbl
@@ -0,0 +1,157 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Mcp866u
+
+#Name as a Display Charset (used on Options screen)
+OUkrainian Cyrillic (cp866u)
+
+#Codepage number
+#?
+
+#
+#    Name:     cp866_DOSCyrillicUkrainian to Unicode table
+#    Unicode version: 2.0
+#    Table version: 2.00
+#    Table format:  Format A
+#    General notes: based on Cyrillic (cp866) table,
+#                   have different mapping in 0xF2-0xF9 region.
+#
+#    Format: Three tab-separated columns
+#        Column #1 is the cp866_DOSCyrillicUkrainian code (in hex)
+#        Column #2 is the Unicode (in hex as 0xXXXX)
+#        Column #3 is the Unicode name (follows a comment sign, '#')
+#
+#    The entries are in cp866_DOSCyrillicUkrainian order
+#
+0x20-0x7f       idem
+#
+0x80    U+0410  #CYRILLIC CAPITAL LETTER A
+0x81    U+0411  #CYRILLIC CAPITAL LETTER BE
+0x82    U+0412  #CYRILLIC CAPITAL LETTER VE
+0x83    U+0413  #CYRILLIC CAPITAL LETTER GHE
+0x84    U+0414  #CYRILLIC CAPITAL LETTER DE
+0x85    U+0415  #CYRILLIC CAPITAL LETTER IE
+0x86    U+0416  #CYRILLIC CAPITAL LETTER ZHE
+0x87    U+0417  #CYRILLIC CAPITAL LETTER ZE
+0x88    U+0418  #CYRILLIC CAPITAL LETTER I
+0x89    U+0419  #CYRILLIC CAPITAL LETTER SHORT I
+0x8a    U+041a  #CYRILLIC CAPITAL LETTER KA
+0x8b    U+041b  #CYRILLIC CAPITAL LETTER EL
+0x8c    U+041c  #CYRILLIC CAPITAL LETTER EM
+0x8d    U+041d  #CYRILLIC CAPITAL LETTER EN
+0x8e    U+041e  #CYRILLIC CAPITAL LETTER O
+0x8f    U+041f  #CYRILLIC CAPITAL LETTER PE
+0x90    U+0420  #CYRILLIC CAPITAL LETTER ER
+0x91    U+0421  #CYRILLIC CAPITAL LETTER ES
+0x92    U+0422  #CYRILLIC CAPITAL LETTER TE
+0x93    U+0423  #CYRILLIC CAPITAL LETTER U
+0x94    U+0424  #CYRILLIC CAPITAL LETTER EF
+0x95    U+0425  #CYRILLIC CAPITAL LETTER HA
+0x96    U+0426  #CYRILLIC CAPITAL LETTER TSE
+0x97    U+0427  #CYRILLIC CAPITAL LETTER CHE
+0x98    U+0428  #CYRILLIC CAPITAL LETTER SHA
+0x99    U+0429  #CYRILLIC CAPITAL LETTER SHCHA
+0x9a    U+042a  #CYRILLIC CAPITAL LETTER HARD SIGN
+0x9b    U+042b  #CYRILLIC CAPITAL LETTER YERU
+0x9c    U+042c  #CYRILLIC CAPITAL LETTER SOFT SIGN
+0x9d    U+042d  #CYRILLIC CAPITAL LETTER E
+0x9e    U+042e  #CYRILLIC CAPITAL LETTER YU
+0x9f    U+042f  #CYRILLIC CAPITAL LETTER YA
+0xa0    U+0430  #CYRILLIC SMALL LETTER A
+0xa1    U+0431  #CYRILLIC SMALL LETTER BE
+0xa2    U+0432  #CYRILLIC SMALL LETTER VE
+0xa3    U+0433  #CYRILLIC SMALL LETTER GHE
+0xa4    U+0434  #CYRILLIC SMALL LETTER DE
+0xa5    U+0435  #CYRILLIC SMALL LETTER IE
+0xa6    U+0436  #CYRILLIC SMALL LETTER ZHE
+0xa7    U+0437  #CYRILLIC SMALL LETTER ZE
+0xa8    U+0438  #CYRILLIC SMALL LETTER I
+0xa9    U+0439  #CYRILLIC SMALL LETTER SHORT I
+0xaa    U+043a  #CYRILLIC SMALL LETTER KA
+0xab    U+043b  #CYRILLIC SMALL LETTER EL
+0xac    U+043c  #CYRILLIC SMALL LETTER EM
+0xad    U+043d  #CYRILLIC SMALL LETTER EN
+0xae    U+043e  #CYRILLIC SMALL LETTER O
+0xaf    U+043f  #CYRILLIC SMALL LETTER PE
+0xb0    U+2591  #LIGHT SHADE
+0xb1    U+2592  #MEDIUM SHADE
+0xb2    U+2593  #DARK SHADE
+0xb3    U+2502  #BOX DRAWINGS LIGHT VERTICAL
+0xb4    U+2524  #BOX DRAWINGS LIGHT VERTICAL AND LEFT
+0xb5    U+2561  #BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
+0xb6    U+2562  #BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
+0xb7    U+2556  #BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
+0xb8    U+2555  #BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
+0xb9    U+2563  #BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+0xba    U+2551  #BOX DRAWINGS DOUBLE VERTICAL
+0xbb    U+2557  #BOX DRAWINGS DOUBLE DOWN AND LEFT
+0xbc    U+255d  #BOX DRAWINGS DOUBLE UP AND LEFT
+0xbd    U+255c  #BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
+0xbe    U+255b  #BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
+0xbf    U+2510  #BOX DRAWINGS LIGHT DOWN AND LEFT
+0xc0    U+2514  #BOX DRAWINGS LIGHT UP AND RIGHT
+0xc1    U+2534  #BOX DRAWINGS LIGHT UP AND HORIZONTAL
+0xc2    U+252c  #BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+0xc3    U+251c  #BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+0xc4    U+2500  #BOX DRAWINGS LIGHT HORIZONTAL
+0xc5    U+253c  #BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+0xc6    U+255e  #BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
+0xc7    U+255f  #BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
+0xc8    U+255a  #BOX DRAWINGS DOUBLE UP AND RIGHT
+0xc9    U+2554  #BOX DRAWINGS DOUBLE DOWN AND RIGHT
+0xca    U+2569  #BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+0xcb    U+2566  #BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+0xcc    U+2560  #BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+0xcd    U+2550  #BOX DRAWINGS DOUBLE HORIZONTAL
+0xce    U+256c  #BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+0xcf    U+2567  #BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
+0xd0    U+2568  #BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
+0xd1    U+2564  #BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
+0xd2    U+2565  #BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
+0xd3    U+2559  #BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
+0xd4    U+2558  #BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
+0xd5    U+2552  #BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
+0xd6    U+2553  #BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
+0xd7    U+256b  #BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
+0xd8    U+256a  #BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
+0xd9    U+2518  #BOX DRAWINGS LIGHT UP AND LEFT
+0xda    U+250c  #BOX DRAWINGS LIGHT DOWN AND RIGHT
+0xdb    U+2588  #FULL BLOCK
+0xdc    U+2584  #LOWER HALF BLOCK
+0xdd    U+258c  #LEFT HALF BLOCK
+0xde    U+2590  #RIGHT HALF BLOCK
+0xdf    U+2580  #UPPER HALF BLOCK
+0xe0    U+0440  #CYRILLIC SMALL LETTER ER
+0xe1    U+0441  #CYRILLIC SMALL LETTER ES
+0xe2    U+0442  #CYRILLIC SMALL LETTER TE
+0xe3    U+0443  #CYRILLIC SMALL LETTER U
+0xe4    U+0444  #CYRILLIC SMALL LETTER EF
+0xe5    U+0445  #CYRILLIC SMALL LETTER HA
+0xe6    U+0446  #CYRILLIC SMALL LETTER TSE
+0xe7    U+0447  #CYRILLIC SMALL LETTER CHE
+0xe8    U+0448  #CYRILLIC SMALL LETTER SHA
+0xe9    U+0449  #CYRILLIC SMALL LETTER SHCHA
+0xea    U+044a  #CYRILLIC SMALL LETTER HARD SIGN
+0xeb    U+044b  #CYRILLIC SMALL LETTER YERU
+0xec    U+044c  #CYRILLIC SMALL LETTER SOFT SIGN
+0xed    U+044d  #CYRILLIC SMALL LETTER E
+0xee    U+044e  #CYRILLIC SMALL LETTER YU
+0xef    U+044f  #CYRILLIC SMALL LETTER YA
+0xf0    U+0401  #CYRILLIC CAPITAL LETTER IO
+0xf1    U+0451  #CYRILLIC SMALL LETTER IO
+0xf2    U+0490  #CYRILLIC CAPITAL LETTER GHE WITH UPTURN
+0xf3    U+0491  #CYRILLIC SMALL LETTER GHE WITH UPTURN
+0xf4    U+0404  #CYRILLIC CAPITAL LETTER UKRAINIAN IE
+0xf5    U+0454  #CYRILLIC SMALL LETTER UKRAINIAN IE
+0xf6    U+0406  #CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+0xf7    U+0456  #CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+0xf8    U+0407  #CYRILLIC CAPITAL LETTER YI
+0xf9    U+0457  #CYRILLIC SMALL LETTER YI
+0xfa    U+00b7  #MIDDLE DOT
+0xfb    U+221a  #SQUARE ROOT
+0xfc    U+2116  #NUMERO SIGN
+0xfd    U+00a4  #CURRENCY SIGN
+0xfe    U+25a0  #BLACK SQUARE
+0xff    U+00a0  #NO-BREAK SPACE
+
diff --git a/src/chrtrans/cp869_uni.tbl b/src/chrtrans/cp869_uni.tbl
new file mode 100644
index 00000000..d662b831
--- /dev/null
+++ b/src/chrtrans/cp869_uni.tbl
@@ -0,0 +1,160 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Mcp869
+
+#Name as a Display Charset (used on Options screen)
+OGreek2 (cp869)
+
+#Codepage number
+C869
+
+#    Name:     cp869_DOSGreek2 to Unicode table
+#    Unicode version: 2.0
+#    Table version: 2.00
+#    Table format:  Format A
+#    Date:          04/24/96
+#    Authors:       Lori Brownell <loribr@microsoft.com>
+#                   K.D. Chang    <a-kchang@microsoft.com>
+#    General notes: none
+#
+#    Format: Three tab-separated columns
+#        Column #1 is the cp869_DOSGreek2 code (in hex)
+#        Column #2 is the Unicode (in hex as 0xXXXX)
+#        Column #3 is the Unicode name (follows a comment sign, '#')
+#
+#    The entries are in cp869_DOSGreek2 order
+#
+##################
+
+0x20-0x7f       idem
+#
+#0x80		#UNDEFINED
+#0x81		#UNDEFINED
+#0x82		#UNDEFINED
+#0x83		#UNDEFINED
+#0x84		#UNDEFINED
+#0x85		#UNDEFINED
+0x86	U+0386	#GREEK CAPITAL LETTER ALPHA WITH TONOS
+#0x87		#UNDEFINED
+0x88	U+00b7	#MIDDLE DOT
+0x89	U+00ac	#NOT SIGN
+0x8a	U+00a6	#BROKEN BAR
+0x8b	U+2018	U+02bd	#LEFT SINGLE QUOTATION MARK
+0x8c	U+2019	U+02bc	#RIGHT SINGLE QUOTATION MARK
+0x8d	U+0388	#GREEK CAPITAL LETTER EPSILON WITH TONOS
+0x8e	U+2015	#HORIZONTAL BAR
+0x8f	U+0389	#GREEK CAPITAL LETTER ETA WITH TONOS
+0x90	U+038a	#GREEK CAPITAL LETTER IOTA WITH TONOS
+0x91	U+03aa	#GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
+0x92	U+038c	#GREEK CAPITAL LETTER OMICRON WITH TONOS
+#0x93		#UNDEFINED
+#0x94		#UNDEFINED
+0x95	U+038e	#GREEK CAPITAL LETTER UPSILON WITH TONOS
+0x96	U+03ab	#GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
+0x97	U+00a9	#COPYRIGHT SIGN
+0x98	U+038f	#GREEK CAPITAL LETTER OMEGA WITH TONOS
+0x99	U+00b2	#SUPERSCRIPT TWO
+0x9a	U+00b3	#SUPERSCRIPT THREE
+0x9b	U+03ac	#GREEK SMALL LETTER ALPHA WITH TONOS
+0x9c	U+00a3	#POUND SIGN
+0x9d	U+03ad	#GREEK SMALL LETTER EPSILON WITH TONOS
+0x9e	U+03ae	#GREEK SMALL LETTER ETA WITH TONOS
+0x9f	U+03af	#GREEK SMALL LETTER IOTA WITH TONOS
+0xa0	U+03ca	#GREEK SMALL LETTER IOTA WITH DIALYTIKA
+0xa1	U+0390	#GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+0xa2	U+03cc	#GREEK SMALL LETTER OMICRON WITH TONOS
+0xa3	U+03cd	#GREEK SMALL LETTER UPSILON WITH TONOS
+0xa4	U+0391	#GREEK CAPITAL LETTER ALPHA
+0xa5	U+0392	#GREEK CAPITAL LETTER BETA
+0xa6	U+0393	#GREEK CAPITAL LETTER GAMMA
+0xa7	U+0394	#GREEK CAPITAL LETTER DELTA
+0xa8	U+0395	#GREEK CAPITAL LETTER EPSILON
+0xa9	U+0396	#GREEK CAPITAL LETTER ZETA
+0xaa	U+0397	#GREEK CAPITAL LETTER ETA
+0xab	U+00bd	#VULGAR FRACTION ONE HALF
+0xac	U+0398	#GREEK CAPITAL LETTER THETA
+0xad	U+0399	#GREEK CAPITAL LETTER IOTA
+0xae	U+00ab	#LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xaf	U+00bb	#RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xb0	U+2591	#LIGHT SHADE
+0xb1	U+2592	#MEDIUM SHADE
+0xb2	U+2593	#DARK SHADE
+0xb3	U+2502	#BOX DRAWINGS LIGHT VERTICAL
+0xb4	U+2524	#BOX DRAWINGS LIGHT VERTICAL AND LEFT
+0xb5	U+039a	#GREEK CAPITAL LETTER KAPPA
+0xb6	U+039b	#GREEK CAPITAL LETTER LAMDA
+0xb7	U+039c	#GREEK CAPITAL LETTER MU
+0xb8	U+039d	#GREEK CAPITAL LETTER NU
+0xb9	U+2563	#BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+0xba	U+2551	#BOX DRAWINGS DOUBLE VERTICAL
+0xbb	U+2557	#BOX DRAWINGS DOUBLE DOWN AND LEFT
+0xbc	U+255d	#BOX DRAWINGS DOUBLE UP AND LEFT
+0xbd	U+039e	#GREEK CAPITAL LETTER XI
+0xbe	U+039f	#GREEK CAPITAL LETTER OMICRON
+0xbf	U+2510	#BOX DRAWINGS LIGHT DOWN AND LEFT
+0xc0	U+2514	#BOX DRAWINGS LIGHT UP AND RIGHT
+0xc1	U+2534	#BOX DRAWINGS LIGHT UP AND HORIZONTAL
+0xc2	U+252c	#BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+0xc3	U+251c	#BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+0xc4	U+2500	#BOX DRAWINGS LIGHT HORIZONTAL
+0xc5	U+253c	#BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+0xc6	U+03a0	#GREEK CAPITAL LETTER PI
+0xc7	U+03a1	#GREEK CAPITAL LETTER RHO
+0xc8	U+255a	#BOX DRAWINGS DOUBLE UP AND RIGHT
+0xc9	U+2554	#BOX DRAWINGS DOUBLE DOWN AND RIGHT
+0xca	U+2569	#BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+0xcb	U+2566	#BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+0xcc	U+2560	#BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+0xcd	U+2550	#BOX DRAWINGS DOUBLE HORIZONTAL
+0xce	U+256c	#BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+0xcf	U+03a3	#GREEK CAPITAL LETTER SIGMA
+0xd0	U+03a4	#GREEK CAPITAL LETTER TAU
+0xd1	U+03a5	#GREEK CAPITAL LETTER UPSILON
+0xd2	U+03a6	#GREEK CAPITAL LETTER PHI
+0xd3	U+03a7	#GREEK CAPITAL LETTER CHI
+0xd4	U+03a8	#GREEK CAPITAL LETTER PSI
+0xd5	U+03a9	#GREEK CAPITAL LETTER OMEGA
+0xd6	U+03b1	#GREEK SMALL LETTER ALPHA
+0xd7	U+03b2	#GREEK SMALL LETTER BETA
+0xd8	U+03b3	#GREEK SMALL LETTER GAMMA
+0xd9	U+2518	#BOX DRAWINGS LIGHT UP AND LEFT
+0xda	U+250c	#BOX DRAWINGS LIGHT DOWN AND RIGHT
+0xdb	U+2588	#FULL BLOCK
+0xdc	U+2584	#LOWER HALF BLOCK
+0xdd	U+03b4	#GREEK SMALL LETTER DELTA
+0xde	U+03b5	#GREEK SMALL LETTER EPSILON
+0xdf	U+2580	#UPPER HALF BLOCK
+0xe0	U+03b6	#GREEK SMALL LETTER ZETA
+0xe1	U+03b7	#GREEK SMALL LETTER ETA
+0xe2	U+03b8	#GREEK SMALL LETTER THETA
+0xe3	U+03b9	#GREEK SMALL LETTER IOTA
+0xe4	U+03ba	#GREEK SMALL LETTER KAPPA
+0xe5	U+03bb	#GREEK SMALL LETTER LAMDA
+0xe6	U+03bc	#GREEK SMALL LETTER MU
+0xe7	U+03bd	#GREEK SMALL LETTER NU
+0xe8	U+03be	#GREEK SMALL LETTER XI
+0xe9	U+03bf	#GREEK SMALL LETTER OMICRON
+0xea	U+03c0	#GREEK SMALL LETTER PI
+0xeb	U+03c1	#GREEK SMALL LETTER RHO
+0xec	U+03c3	#GREEK SMALL LETTER SIGMA
+0xed	U+03c2	#GREEK SMALL LETTER FINAL SIGMA
+0xee	U+03c4	#GREEK SMALL LETTER TAU
+0xef	U+0384	#GREEK TONOS
+0xf0	U+00ad	#SOFT HYPHEN
+0xf1	U+00b1	#PLUS-MINUS SIGN
+0xf2	U+03c5	#GREEK SMALL LETTER UPSILON
+0xf3	U+03c6	#GREEK SMALL LETTER PHI
+0xf4	U+03c7	#GREEK SMALL LETTER CHI
+0xf5	U+00a7	#SECTION SIGN
+0xf6	U+03c8	#GREEK SMALL LETTER PSI
+0xf7	U+0385	#GREEK DIALYTIKA TONOS
+0xf8	U+00b0	#DEGREE SIGN
+0xf9	U+00a8	#DIAERESIS
+0xfa	U+03c9	#GREEK SMALL LETTER OMEGA
+0xfb	U+03cb	#GREEK SMALL LETTER UPSILON WITH DIALYTIKA
+0xfc	U+03b0	#GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+0xfd	U+03ce	#GREEK SMALL LETTER OMEGA WITH TONOS
+0xfe	U+25a0	#BLACK SQUARE
+0xff	U+00a0	#NO-BREAK SPACE
+
diff --git a/src/chrtrans/def7_uni.tbl b/src/chrtrans/def7_uni.tbl
new file mode 100644
index 00000000..366aac83
--- /dev/null
+++ b/src/chrtrans/def7_uni.tbl
@@ -0,0 +1,2943 @@
+# $LynxId: def7_uni.tbl,v 1.28 2008/04/15 19:38:41 Tim.Larson Exp $
+# Default 7bit replacements.
+#
+# This table is very important and should not be excluded from the distribution
+# since this is a default fallback for any 8bit user's "display character set"
+# which (nearly) of 256 chars and could not map a rich Unicode repertoire.
+#
+# Note: there are a few rare replacement strings with trailing spaces
+# which should be enclosed as C strings like "... " to make things obvious
+# (and doing that we should escape \ as \134 and escape " as \" or \042
+# but this is really rare).
+#
+
+#The MIME name of this charset.
+Mus-ascii
+
+# Like any other charset this may be selected as "display character set":
+#Name as a Display Charset (used on Options screen)
+O7 bit approximations (US-ASCII)
+
+# Shall this become the "default" translation table?  YES!
+# There has to be exactly one table marked as "default".
+D1
+
+
+# us-ascii characters should not normally pass here,
+# they are always processed directly but let declare them here:
+0x20-0x7e  idem
+
+
+# NO-BREAK SPACE,
+# should not happen (processed in the code):
+#U+00a0:NS
+U+00a1:!
+U+00a2:-c-
+U+00a3:-L-
+U+00a4:CUR
+U+00a5:YEN
+U+00a6:|
+U+00a7:S:
+U+00a8:"
+# COPYRIGHT SIGN:
+U+00a9:(c)
+U+00aa:-a
+U+00ab:<<
+U+00ac:NOT
+# SOFT HYPHEN,
+# should not happen (processed in the code):
+#U+00ad:-
+# REGISTERED SIGN:
+U+00ae:(R)
+U+00af:-
+U+00b0:DEG
+U+00b1:+-
+U+00b2:^2
+U+00b3:^3
+U+00b4:'
+# My -> u
+U+00b5:u
+U+00b6:P:
+U+00b7:.
+U+00b8:,
+U+00b9:^1
+U+00ba:-o
+U+00bb:>>
+U+00bc: 1/4
+U+00bd: 1/2
+U+00be: 3/4
+U+00bf:?
+0x41	U+00c0-U+00c3
+U+00c4 "Ae"		# &Auml;, not the best choice for some languages.
+U+00c5:AA
+U+00c6:AE
+U+00c7:C,
+0x45	U+00c8-U+00cb
+0x49	U+00cc-U+00cf
+U+00d0:D-
+0x4e	U+00d1
+0x4f	U+00d2-U+00d5
+U+00d6 "Oe"		# &Ouml;, not the best choice for some languages.
+U+00d7: *
+U+00d8:O/
+0x55	U+00d9-U+00db
+U+00dc "Ue"		# &Uuml;, not the best choice for some languages.
+0x59	U+00dd
+U+00de:TH
+U+00df:ss
+U+00e0:`a
+0x61	U+00e1-U+00e3
+U+00e4 "ae"		# &auml;, not the best choice for some languages.
+U+00e5:aa
+U+00e6:ae
+U+00e7:c,
+0x65	U+00e8-U+00eb
+U+00ec:`i
+0x69	U+00ed-U+00ef
+U+00f0:d-
+0x6e	U+00f1
+0x6f	U+00f2-U+00f5
+U+00f6 "oe"		# &ouml;, not the best choice for some languages.
+U+00f7:-:
+U+00f8:o/
+0x75	U+00f9-U+00fb
+U+00fc "ue"		# &uuml;, not the best choice for some languages.
+0x79	U+00fd
+U+00fe:th
+0x79	U+00ff
+# end of latin-1 repertoire
+0x41	U+0100	U+0102	U+0104			# A
+0x61	U+0101	U+0103	U+0105			# a
+0x43	U+0106	U+0108	U+010a	U+010c		# C
+# The following line is an example for mapping several accented versions
+# of small letter 'c' to 'c':
+0x63	U+0107	U+0109	U+010b	U+010d		# c
+0x44	U+010e
+0x64	U+010f
+U+0110:D/
+U+0111:d/
+0x45	U+0112	U+0114	U+0116	U+0118	U+011a	# E
+0x65	U+0113	U+0115	U+0117	U+0119	U+011b	# e
+0x47	U+011c	U+011e	U+0120	U+0122		# G
+0x67	U+011d	U+011f	U+0121	U+0123		# g
+0x48	U+0124
+0x68	U+0125
+U+0126:H/
+0x48	U+0127 #	LATIN SMALL LETTER H BAR	-> H
+0x49	U+0128	U+012a	U+012c	U+012e	U+0130	# I
+0x69	U+0129	U+012b	U+012d	U+012f	U+0131	# i
+U+0132:IJ
+U+0133:ij
+0x4a	U+0134
+0x6a	U+0135
+0x4b	U+0136
+0x6b	U+0137
+U+0138:kk
+0x4c	U+0139	U+013b	U+013d			# L
+0x6c	U+013a	U+013c	U+013e			# l
+U+013f:L.
+U+0140:l.
+U+0141:L/
+U+0142:l/
+0x4e	U+0143	U+0145	U+0147			# N
+0x6e	U+0144	U+0146	U+0148			# n
+U+0149:'n
+U+014a:NG
+0x4e	U+014B #	LATIN SMALL LETTER ENG	-> N
+0x4f	U+014c	U+014e				# O
+0x6f	U+014d	U+014f				# o
+U+0150:O"
+U+0151:o"
+U+0152:OE
+U+0153:oe
+0x52	U+0154	U+0156	U+0158			# R
+0x72	U+0155	U+0157	U+0159			# r
+0x53	U+015a	U+015c	U+015e	U+0160		# S
+0x73	U+015b	U+015d	U+015f	U+0161		# s
+0x54	U+0162	U+0164				# T
+0x74	U+0163	U+0165				# t
+U+0166:T/
+U+0167:t/
+0x55	U+0168	U+016a	U+016c	U+016e	U+0172	# U
+0x75	U+0169	U+016b	U+016d	U+016f	U+0173	# u
+U+0170:U"
+U+0171:u"
+0x57	U+0174
+0x77	U+0175
+0x59	U+0176	U+0178
+0x79	U+0177
+0x5a	U+0179	U+017b	U+017d	U+021d
+0x7a	U+017a	U+017c	U+017e
+U+017f:s1
+U+0187:C2
+U+0188:c2
+U+0191:F2
+U+0192: f
+U+0198:K2
+U+0199:k2
+U+01a0:O9
+U+01a1:o9
+U+01a2:OI
+U+01a3:oi
+U+01a6:yr
+U+01af:U9
+U+01b0:u9
+U+01b5:Z/
+U+01b6:z/
+U+01b7:ED
+0x41	U+01cd
+0x61	U+01ce
+0x49	U+01cf
+0x69	U+01d0
+0x4f	U+01d1
+0x6f	U+01d2
+0x55	U+01d3
+0x75	U+01d4
+U+01d5:U:-
+U+01d6:u:-
+U+01d7:U:'
+U+01d8:u:'
+U+01d9:U:<
+U+01da:u:<
+U+01db:U:!
+U+01dc:u:!
+U+01de:A1
+U+01df:a1
+U+01e0:A7
+U+01e1:a7
+U+01e2:A3
+U+01e3:a3
+U+01e4:G/
+U+01e5:g/
+0x47	U+01e6
+0x67	U+01e7
+0x4b	U+01e8
+0x6b	U+01e9
+0x4f	U+01ea
+0x6f	U+01eb
+U+01ec:O1
+U+01ed:o1
+U+01ee:EZ
+U+01ef:ez
+0x6a	U+01f0
+0x47	U+01f4
+0x67	U+01f5
+U+01fa:AA'
+U+01fb:aa'
+U+01fc:AE'
+U+01fd:ae'
+U+01fe:O/'
+U+01ff:o/'
+U+0200:A!!
+U+0201:a!!
+U+0202:A)
+U+0203:a)
+U+0204:E!!
+U+0205:e!!
+U+0206:E)
+U+0207:e)
+U+0208:I!!
+U+0209:i!!
+U+020a:I)
+U+020b:i)
+U+020c:O!!
+U+020d:o!!
+U+020e:O)
+U+020f:o)
+U+0210:R!!
+U+0211:r!!
+U+0212:R)
+U+0213:r)
+U+0214:U!!
+U+0215:u!!
+U+0216:U)
+U+0217:u)
+
+# IPA symbols, from
+#   Linkname: FAQ: Representing IPA Phonetics in ASCII
+#        URL: http://www.hpl.hp.com/personal/Evan_Kirshenbaum/IPA/faq.html
+#        (corrected in Russian Cyrillic area).
+#        (corrected in Greek area).
+#
+0x41	U+0251 #	LATIN SMALL LETTER SCRIPT A	-> A
+U+0252:A.
+U+0253:b`
+0x4f	U+0254 #	LATIN SMALL LETTER OPEN O	-> O
+U+0256:d.
+U+0257:d`
+U+0258:@<umd>
+0x40	U+0259 #	LATIN SMALL LETTER SCHWA	-> @
+0x52	U+025A #	LATIN SMALL LETTER SCHWA HOOK	-> R
+0x45	U+025B #	LATIN SMALL LETTER EPSILON	-> E
+U+025c:V"
+U+025d:R<umd>
+U+025e:O"
+0x4a	U+025F #	LATIN SMALL LETTER DOTLESS J BAR	-> J
+U+0260:g`
+0x67	U+0261 #	LATIN SMALL LETTER SCRIPT G
+0x47	U+0262 #	LATIN LETTER SMALL CAPITAL G
+0x51	U+0263 #	LATIN SMALL LETTER GAMMA	-> Q
+U+0264:o-
+U+0265:j<rnd>
+U+0266:h<?>
+U+0268:i"
+0x49	U+026A U+0269 # LATIN LETTER SMALL CAPITAL I, LATIN SMALL LETTER IOTA
+0x4c	U+026B #	LATIN SMALL LETTER L WITH MIDDLE TILDE
+0x4c	U+026C #	LATIN SMALL LETTER L BELT
+U+026d:l.
+U+026e:z<lat>
+U+026f:u-
+U+0270:j<vel>
+0x4d	U+0271 #	LATIN SMALL LETTER M HOOK
+U+0273:n.
+U+0274:n"
+U+0275:@.
+U+0276:&.
+0x55	U+0277 #	LATIN SMALL LETTER CLOSED OMEGA	-> U
+0x72	U+0279 #	LATIN SMALL LETTER TURNED R	-> r
+U+027a:*<lat>
+U+027b:r.
+U+027d:*.
+0x2a	U+027E #	LATIN SMALL LETTER FISHHOOK R	-> *
+U+0280:r"
+0x52	U+0280 #	LATIN LETTER SMALL CAPITAL R	-> R
+U+0281:g"
+U+0282:s.
+0x53	U+0283 #	LATIN SMALL LETTER ESH	-> S
+U+0284:J`
+U+0287:t!
+U+0288:t.
+U+0289:u"
+0x55	U+028A #	LATIN SMALL LETTER UPSILON	-> U
+U+028b:r<lbd>
+0x56	U+028C #	LATIN SMALL LETTER TURNED V	-> V
+U+028d:w<vls>
+U+028e:l^
+U+028f:I.
+U+0290:z.
+U+0292:Z
+0x3f	U+0294 #	LATIN SMALL LETTER GLOTTAL STOP	-> ?
+U+0295:H<vcd>
+U+0296:l!
+U+0297:c!
+U+0298:p!
+U+0299:b<trl>
+U+029b:G`
+0x6a	U+029d #	LATIN SMALL LETTER CROSSED-TAIL J
+U+029e:k!
+0x4c	U+029F #	LATIN LETTER SMALL CAPITAL L
+U+02a0:q`
+U+02a4:d3
+U+02a6:ts
+U+02a7:tS
+U+02b0:<h>
+U+02b1:<?>
+0x3b	U+02b2 U+0321
+U+02b3:<r>
+U+02b7:<w>
+U+02bb:;S
+0x60	U+02bc
+U+02c6:^
+U+02c7:'<
+U+02c8:|
+U+02c9:1-
+U+02cb:1!
+0x3a	U+02d0
+U+02d1 ":\\"
+0x2b	U+02d6
+0x2d	U+02d7
+U+02d8:'(
+U+02d9:'.
+U+02da:'0
+U+02db:';
+U+02dc:~
+U+02dd:'"
+U+02e5:_T
+U+02e6:_H
+U+02e7:_M
+U+02e8:_L
+U+02e9:_B
+U+02ec:_v
+U+02ee:''
+0x60	U+0300
+0x27	U+0301
+0x5e	U+0302
+0x7e	U+0303	U+0334
+U+030b:''
+0x7c	U+030d
+U+030e:||
+U+030f:``
+0x2e	U+0322	U+0323
+U+0324:<?>
+U+0325:<o>
+0x2c	U+0326	U+0327
+0x2d	U+0329
+0x5b	U+032a
+U+032b:<w>
+U+0334:<H>
+0x2f	U+0337	U+0338
+U+0340:`
+U+0341:'
+U+0342:~
+U+0344:'%
+U+0345:j3
+U+0347:=
+U+0360:~~
+U+0374:'
+U+0375:,
+U+037a:j3
+U+037e:?%
+U+0384:'*
+U+0385:'%
+# Greek letters
+U+0386:A'
+U+0387:.*
+U+0388:E'
+U+0389:Y%
+U+038a:I'
+U+038c:O'
+U+038e:U%
+U+038f:W%
+U+0390:i3
+U+0391:A
+U+0392:B
+U+0393:G
+U+0394:D
+U+0395:E
+U+0396:Z
+U+0397:Y
+U+0398:TH
+U+0399:I
+U+039a:K
+U+039b:L
+U+039c:M
+U+039d:N
+U+039e:C
+U+039f:O
+U+03a0:P
+U+03a1:R
+U+03a3:S
+U+03a4:T
+U+03a5:U
+U+03a6:F
+U+03a7:X
+U+03a8:Q
+U+03a9:W*
+U+03aa:J
+U+03ab:V*
+U+03ac:a'
+U+03ad:e'
+U+03ae:y%
+U+03af:i'
+U+03b0:u3
+U+03b1:a
+U+03b2:b
+U+03b3:g
+U+03b4:d
+U+03b5:e
+U+03b6:z
+U+03b7:y
+U+03b8:th
+U+03b9:i
+U+03ba:k
+U+03bb:l
+U+03bc:m
+U+03bd:n
+U+03be:c
+U+03bf:o
+U+03c0:p
+U+03c1:r
+U+03c2:*s
+U+03c3:s
+U+03c4:t
+U+03c5:u
+U+03c6:f
+U+03c7:x
+U+03c8:q
+U+03c9:w
+U+03ca:j
+U+03cb:v*
+U+03cc:o'
+U+03cd:u%
+U+03ce:w%
+# Greek symbols
+U+03d0 "beta "
+U+03d1 "theta "
+U+03d2 "upsi "
+U+03d5 "phi "
+U+03d6 "pi "
+U+03d7:k.
+U+03da:T3
+U+03db:t3
+U+03dc:M3
+U+03dd:m3
+U+03de:K3
+U+03df:k3
+U+03e0:P3
+U+03e1:p3
+U+03f0 "kappa "
+U+03f1 "rho "
+U+03f3:J
+U+03f4:'%
+U+03f5:j3
+# Cyrillic capital letters
+U+0402:D%
+U+0403:G%
+U+0404:IE
+U+0405:DS
+U+0406:II
+U+0407:YI
+U+0408:J%
+U+0409:LJ
+U+040a:NJ
+U+040b:Ts
+U+040c:KJ
+U+040e:V%
+U+040f:DZ
+# Russian Cyrillic letters, transliterated
+U+0401:IO
+U+0410:A
+U+0411:B
+U+0412:V
+U+0413:G
+U+0414:D
+U+0415:E
+U+0416:ZH
+U+0417:Z
+U+0418:I
+U+0419:J
+U+041a:K
+U+041b:L
+U+041c:M
+U+041d:N
+U+041e:O
+U+041f:P
+U+0420:R
+U+0421:S
+U+0422:T
+U+0423:U
+U+0424:F
+U+0425:H
+U+0426:C
+U+0427:CH
+U+0428:SH
+U+0429:SCH
+U+042a:"
+U+042b:Y
+U+042c:'
+U+042d:`E
+U+042e:YU
+U+042f:YA
+U+0430:a
+U+0431:b
+U+0432:v
+U+0433:g
+U+0434:d
+U+0435:e
+U+0436:zh
+U+0437:z
+U+0438:i
+U+0439:j
+U+043a:k
+U+043b:l
+U+043c:m
+U+043d:n
+U+043e:o
+U+043f:p
+U+0440:r
+U+0441:s
+U+0442:t
+U+0443:u
+U+0444:f
+U+0445:h
+U+0446:c
+U+0447:ch
+U+0448:sh
+U+0449:sch
+U+044a:"
+U+044b:y
+U+044c:'
+U+044d:`e
+U+044e:yu
+U+044f:ya
+U+0451:io
+# end of Russian Cyrillic letters.
+# Cyrillic small letters (and some archaic)
+U+0452:d%
+U+0453:g%
+U+0454:ie
+U+0455:ds
+U+0456:ii
+U+0457:yi
+U+0458:j%
+U+0459:lj
+U+045a:nj
+U+045b:ts
+U+045c:kj
+U+045e:v%
+U+045f:dz
+U+0462:Y3
+U+0463:y3
+U+046a:O3
+U+046b:o3
+U+0472:F3
+U+0473:f3
+U+0474:V3
+U+0475:v3
+U+0480:C3
+U+0481:c3
+U+0490:G3
+U+0491:g3
+U+04d4:AE
+U+04d5:ae
+# These may make Yiddish slightly more readable, until we have
+# something better.
+
+0x69	U+05b4	# i
+0x61	U+05b7	# a
+0x6f	U+05b8	# o
+0x75	U+05bc	# u
+0x68	U+05bf	# h
+0x3a	U+05c2	# :
+
+0x76	U+05f0	# v
+U+05f1:oy
+U+05f2:ey
+
+# U+05d0:A+
+0x23	U+05d0	# '#'
+
+U+05d1:B+
+U+05d2:G+
+U+05d3:D+
+U+05d4:H+
+U+05d5:W+
+U+05d6:Z+
+U+05d7:X+
+U+05d8:Tj
+U+05d9:J+
+U+05da:K%
+U+05db:K+
+U+05dc:L+
+U+05dd:M%
+U+05de:M+
+U+05df:N%
+U+05e0:N+
+U+05e1:S+
+U+05e2:E+
+U+05e3:P%
+U+05e4:P+
+U+05e5:Zj
+U+05e6:ZJ
+U+05e7:Q+
+U+05e8:R+
+U+05e9:Sh
+U+05ea:T+
+
+U+060c:,+
+U+061b:;+
+U+061f:?+
+U+0621:H'
+U+0622:aM
+U+0623:aH
+U+0624:wH
+U+0625:ah
+U+0626:yH
+U+0627:a+
+U+0628:b+
+U+0629:tm
+U+062a:t+
+U+062b:tk
+U+062c:g+
+U+062d:hk
+U+062e:x+
+U+062f:d+
+U+0630:dk
+U+0631:r+
+U+0632:z+
+U+0633:s+
+U+0634:sn
+U+0635:c+
+U+0636:dd
+U+0637:tj
+U+0638:zH
+U+0639:e+
+U+063a:i+
+U+0640:++
+U+0641:f+
+U+0642:q+
+U+0643:k+
+U+0644:l+
+U+0645:m+
+U+0646:n+
+U+0647:h+
+U+0648:w+
+U+0649:j+
+U+064a:y+
+U+064b::+
+U+064c:"+
+U+064d:=+
+U+064e:/+
+U+064f:'+
+U+0650:1+
+U+0651:3+
+U+0652:0+
+U+0660:0a
+U+0661:1a
+U+0662:2a
+U+0663:3a
+U+0664:4a
+U+0665:5a
+U+0666:6a
+U+0667:7a
+U+0668:8a
+U+0669:9a
+U+0670:aS
+U+067e:p+
+U+0681:hH
+U+0686:tc
+U+0698:zj
+U+06a4:v+
+U+06af:gf
+U+06f0:0a
+U+06f1:1a
+U+06f2:2a
+U+06f3:3a
+U+06f4:4a
+U+06f5:5a
+U+06f6:6a
+U+06f7:7a
+U+06f8:8a
+U+06f9:9a
+
+# Replacement strings for Ethiopic characters
+U+1200:he
+U+1201:hu
+U+1202:hi
+U+1203:ha
+U+1204:hE
+0x68	U+1205	#:h
+U+1206:ho
+U+1208:le
+U+1209:lu
+U+120A:li
+U+120B:la
+U+120C:lE
+0x6c	U+120D	#:l
+U+120E:lo
+U+120F:lWa
+U+1210:He
+U+1211:Hu
+U+1212:Hi
+U+1213:Ha
+U+1214:HE
+0x48	U+1215	#:H
+U+1216:Ho
+U+1217:HWa
+U+1218:me
+U+1219:mu
+U+121A:mi
+U+121B:ma
+U+121C:mE
+0x6d	U+121D	#:m
+U+121E:mo
+U+121F:mWa
+U+1220:`se
+U+1221:`su
+U+1222:`si
+U+1223:`sa
+U+1224:`sE
+U+1225:`s
+U+1226:`so
+U+1227:`sWa
+U+1228:re
+U+1229:ru
+U+122A:ri
+U+122B:ra
+U+122C:rE
+0x72	U+122D	#:r
+U+122E:ro
+U+122F:rWa
+U+1230:se
+U+1231:su
+U+1232:si
+U+1233:sa
+U+1234:sE
+0x73	U+1235	#:s
+U+1236:so
+U+1237:sWa
+U+1238:xe
+U+1239:xu
+U+123A:xi
+U+123B:xa
+U+123C:xE
+U+123D:xa
+U+123E:xo
+U+123F:xWa
+U+1240:qe
+U+1241:qu
+U+1242:qi
+U+1243:qa
+U+1244:qE
+0x71	U+1245	#:q
+U+1246:qo
+U+1248:qWe
+U+124A:qWi
+U+124B:qWa
+U+124C:qWE
+U+124D:qW
+U+1250:Qe
+U+1251:Qu
+U+1252:Qi
+U+1253:Qa
+U+1254:QE
+0x51	U+1255	#:Q
+U+1256:Qo
+U+1258:QWe
+U+125A:QWi
+U+125B:QWa
+U+125C:QWE
+U+125D:QW
+U+1260:be
+U+1261:bu
+U+1262:bi
+U+1263:ba
+U+1264:bE
+0x62	U+1265	#:b
+U+1266:bo
+U+1267:bWa
+U+1268:ve
+U+1269:vu
+U+126A:vi
+U+126B:va
+U+126C:vE
+0x76	U+126D	#:v
+U+126E:vo
+U+126F:vWa
+U+1270:te
+U+1271:tu
+U+1272:ti
+U+1273:ta
+U+1274:tE
+0x74	U+1275	#:t
+U+1276:to
+U+1277:tWa
+U+1278:ce
+U+1279:cu
+U+127A:ci
+U+127B:ca
+U+127C:cE
+0x63	U+127D	#:c
+U+127E:co
+U+127F:cWa
+U+1280:`he
+U+1281:`hu
+U+1282:`hi
+U+1283:`ha
+U+1284:`hE
+U+1285:`h
+U+1286:`ho
+U+1288:hWe
+U+128A:hWi
+U+128B:hWa
+U+128C:hWE
+U+128D:hW
+U+1290:na
+U+1291:nu
+U+1292:ni
+U+1293:na
+U+1294:nE
+0x6e	U+1295	#:n
+U+1296:no
+U+1297:nWa
+U+1298:Ne
+U+1299:Nu
+U+129A:Ni
+U+129B:Na
+U+129C:NE
+0x4e	U+129D	#:N
+U+129E:No
+U+129F:NWa
+0x65	U+12A0	#:e
+0x75	U+12A1	#:u
+0x69	U+12A2	#:i
+0x61	U+12A3	#:a
+0x45	U+12A4	#:E
+0x49	U+12A5	#:I
+0x6f	U+12A6	#:o
+U+12A7:e3
+U+12A8:ke
+U+12A9:ku
+U+12AA:ki
+U+12AB:ka
+U+12AC:kE
+0x6b	U+12AD	#:k
+U+12AE:ko
+U+12B0:kWe
+U+12B2:kWi
+U+12B3:kWa
+U+12B4:kWE
+U+12B5:kW
+U+12B8:Ke
+U+12B9:Ku
+U+12BA:Ki
+U+12BB:Ka
+U+12BC:KE
+0x4b	U+12BD	#:K
+U+12BE:Ko
+U+12C0:KWe
+U+12C2:KWi
+U+12C3:KWa
+U+12C4:KWE
+U+12C5:KW
+U+12C8:we
+U+12C9:wu
+U+12CA:wi
+U+12CB:wa
+U+12CC:wE
+0x77	U+12CD	#:w
+U+12CE:wo
+U+12D0:`e
+U+12D1:`u
+U+12D2:`i
+U+12D3:`a
+U+12D4:`E
+U+12D5:`I
+U+12D6:`o
+U+12D8:ze
+U+12D9:zu
+U+12DA:zi
+U+12DB:za
+U+12DC:zE
+0x7a	U+12DD	#:z
+U+12DE:zo
+U+12DF:zWa
+U+12E0:Ze
+U+12E1:Zu
+U+12E2:Zi
+U+12E3:Za
+U+12E4:ZE
+0x5a	U+12E5	#:Z
+U+12E6:Zo
+U+12E7:ZWa
+U+12E8:ye
+U+12E9:yu
+U+12EA:yi
+U+12EB:ya
+U+12EC:yE
+0x79	U+12ED	#:y
+U+12EE:yo
+U+12EF:yWa
+U+12F0:de
+U+12F1:du
+U+12F2:di
+U+12F3:da
+U+12F4:dE
+0x64	U+12F5	#:d
+U+12F6:do
+U+12F7:dWa
+U+12F8:De
+U+12F9:Du
+U+12FA:Di
+U+12FB:Da
+U+12FC:DE
+0x44	U+12FD	#:D
+U+12FE:Do
+U+12FF:DWa
+U+1300:je
+U+1301:ju
+U+1302:ji
+U+1303:ja
+U+1304:jE
+0x6a	U+1305	#:j
+U+1306:jo
+U+1307:jWa
+U+1308:ga
+U+1309:gu
+U+130A:gi
+U+130B:ga
+U+130C:gE
+0x67	U+130D	#:g
+U+130E:go
+U+1310:gWu
+U+1312:gWi
+U+1313:gWa
+U+1314:gWE
+U+1315:gW
+U+1318:Ge
+U+1319:Gu
+U+131A:Gi
+U+131B:Ga
+U+131C:GE
+0x47	U+131D	#:G
+U+131E:Go
+U+131F:GWa
+U+1320:Te
+U+1321:Tu
+U+1322:Ti
+U+1323:Ta
+U+1324:TE
+0x54	U+1325	#:T
+U+1326:To
+U+1327:TWa
+U+1328:Ce
+U+1329:Ca
+U+132A:Cu
+U+132B:Ca
+U+132C:CE
+0x43	U+132D	#:C
+U+132E:Co
+U+132F:CWa
+U+1330:Pe
+U+1331:Pu
+U+1332:Pi
+U+1333:Pa
+U+1334:PE
+0x50	U+1335	#:P
+U+1336:Po
+U+1337:PWa
+U+1338:SWe
+U+1339:SWu
+U+133A:SWi
+U+133B:SWa
+U+133C:SWE
+U+133D:SW
+U+133E:SWo
+U+133F:SWa
+U+1340:`Sa
+U+1341:`Su
+U+1342:`Si
+U+1343:`Sa
+U+1344:`SE
+U+1345:`S
+U+1346:`So
+U+1348:fa
+U+1349:fu
+U+134A:fi
+U+134B:fa
+U+134C:fE
+0x6f	U+134D	#:f
+U+134E:fo
+U+134F:fWa
+U+1350:pe
+U+1351:pu
+U+1352:pi
+U+1353:pa
+U+1354:pE
+0x70	U+1355	#:p
+U+1356:po
+U+1357:pWa
+U+1358:mYa
+U+1359:rYa
+U+135A:fYa
+# ETHIOPIC SPACE U+1360 mapped to ASCII space
+0x20	U+1360
+0x3a	U+1361	#::
+U+1362:::
+0x2c	U+1363	#:,
+U+1364:;
+U+1365:-:
+U+1366::-
+U+1367:`?
+U+1368::|:
+U+1369:`1
+U+136A:`2
+U+136B:`3
+U+136C:`4
+U+136D:`5
+U+136E:`6
+U+136F:`7
+U+1370:`8
+U+1371:`9
+U+1372:`10
+U+1373:`20
+U+1374:`30
+U+1375:`40
+U+1376:`50
+U+1377:`60
+U+1378:`70
+U+1379:`80
+U+137A:`90
+U+137B:`100
+U+137C:`10000
+
+
+U+1e00:A-0
+U+1e01:a-0
+U+1e02:B.
+U+1e03:b.
+U+1e04:B-.
+U+1e05:b-.
+U+1e06:B_
+U+1e07:b_
+U+1e08:C,'
+U+1e09:c,'
+U+1e0a:D.
+U+1e0b:d.
+U+1e0c:D-.
+U+1e0d:d-.
+U+1e0e:D_
+U+1e0f:d_
+U+1e10:D,
+U+1e11:d,
+U+1e12:D->
+U+1e13:d->
+U+1e14:E-!
+U+1e15:e-!
+U+1e16:E-'
+U+1e17:e-'
+U+1e18:E->
+U+1e19:e->
+U+1e1a:E-?
+U+1e1b:e-?
+U+1e1c:E,(
+U+1e1d:e,(
+U+1e1e:F.
+U+1e1f:f.
+U+1e20:G-
+U+1e21:g-
+U+1e22:H.
+U+1e23:h.
+U+1e24:H-.
+U+1e25:h-.
+U+1e26:H:
+U+1e27:h:
+U+1e28:H,
+U+1e29:h,
+U+1e2a:H-(
+U+1e2b:h-(
+U+1e2c:I-?
+U+1e2d:i-?
+U+1e2e:I:'
+U+1e2f:i:'
+U+1e30:K'
+U+1e31:k'
+U+1e32:K-.
+U+1e33:k-.
+U+1e34:K_
+U+1e35:k_
+U+1e36:L-.
+U+1e37:l-.
+U+1e38:L--.
+U+1e39:l--.
+U+1e3a:L_
+U+1e3b:l_
+U+1e3c:L->
+U+1e3d:l->
+U+1e3e:M'
+U+1e3f:m'
+U+1e40:M.
+U+1e41:m.
+U+1e42:M-.
+U+1e43:m-.
+U+1e44:N.
+U+1e45:n.
+U+1e46:N-.
+U+1e47:n-.
+U+1e48:N_
+U+1e49:n_
+U+1e4a:N->
+U+1e4b:n->
+U+1e4c:O?'
+U+1e4d:o?'
+U+1e4e:O?:
+U+1e4f:o?:
+U+1e50:O-!
+U+1e51:o-!
+U+1e52:O-'
+U+1e53:o-'
+U+1e54:P'
+U+1e55:p'
+U+1e56:P.
+U+1e57:p.
+U+1e58:R.
+U+1e59:r.
+U+1e5a:R-.
+U+1e5b:r-.
+U+1e5c:R--.
+U+1e5d:r--.
+U+1e5e:R_
+U+1e5f:r_
+U+1e60:S.
+U+1e61:s.
+U+1e62:S-.
+U+1e63:s-.
+U+1e64:S'.
+U+1e65:s'.
+U+1e66:S<.
+U+1e67:s<.
+U+1e68:S.-.
+U+1e69:s.-.
+U+1e6a:T.
+U+1e6b:t.
+U+1e6c:T-.
+U+1e6d:t-.
+U+1e6e:T_
+U+1e6f:t_
+U+1e70:T->
+U+1e71:t->
+U+1e72:U--:
+U+1e73:u--:
+U+1e74:U-?
+U+1e75:u-?
+U+1e76:U->
+U+1e77:u->
+U+1e78:U?'
+U+1e79:u?'
+U+1e7a:U-:
+U+1e7b:u-:
+U+1e7c:V?
+U+1e7d:v?
+U+1e7e:V-.
+U+1e7f:v-.
+U+1e80:W!
+U+1e81:w!
+U+1e82:W'
+U+1e83:w'
+U+1e84:W:
+U+1e85:w:
+U+1e86:W.
+U+1e87:w.
+U+1e88:W-.
+U+1e89:w-.
+U+1e8a:X.
+U+1e8b:x.
+U+1e8c:X:
+U+1e8d:x:
+U+1e8e:Y.
+U+1e8f:y.
+U+1e90:Z>
+U+1e91:z>
+U+1e92:Z-.
+U+1e93:z-.
+U+1e94:Z_
+U+1e95:z_
+U+1e96:h_
+U+1e97:t:
+U+1e98:w0
+U+1e99:y0
+U+1ea0:A-.
+U+1ea1:a-.
+U+1ea2:A2
+U+1ea3:a2
+U+1ea4:A>'
+U+1ea5:a>'
+U+1ea6:A>!
+U+1ea7:a>!
+U+1ea8:A>2
+U+1ea9:a>2
+U+1eaa:A>?
+U+1eab:a>?
+U+1eac:A>-.
+U+1ead:a>-.
+U+1eae:A('
+U+1eaf:a('
+U+1eb0:A(!
+U+1eb1:a(!
+U+1eb2:A(2
+U+1eb3:a(2
+U+1eb4:A(?
+U+1eb5:a(?
+U+1eb6:A(-.
+U+1eb7:a(-.
+U+1eb8:E-.
+U+1eb9:e-.
+U+1eba:E2
+U+1ebb:e2
+U+1ebc:E?
+U+1ebd:e?
+U+1ebe:E>'
+U+1ebf:e>'
+U+1ec0:E>!
+U+1ec1:e>!
+U+1ec2:E>2
+U+1ec3:e>2
+U+1ec4:E>?
+U+1ec5:e>?
+U+1ec6:E>-.
+U+1ec7:e>-.
+U+1ec8:I2
+U+1ec9:i2
+U+1eca:I-.
+U+1ecb:i-.
+U+1ecc:O-.
+U+1ecd:o-.
+U+1ece:O2
+U+1ecf:o2
+U+1ed0:O>'
+U+1ed1:o>'
+U+1ed2:O>!
+U+1ed3:o>!
+U+1ed4:O>2
+U+1ed5:o>2
+U+1ed6:O>?
+U+1ed7:o>?
+U+1ed8:O>-.
+U+1ed9:o>-.
+U+1eda:O9'
+U+1edb:o9'
+U+1edc:O9!
+U+1edd:o9!
+U+1ede:O92
+U+1edf:o92
+U+1ee0:O9?
+U+1ee1:o9?
+U+1ee2:O9-.
+U+1ee3:o9-.
+U+1ee4:U-.
+U+1ee5:u-.
+U+1ee6:U2
+U+1ee7:u2
+U+1ee8:U9'
+U+1ee9:u9'
+U+1eea:U9!
+U+1eeb:u9!
+U+1eec:U92
+U+1eed:u92
+U+1eee:U9?
+U+1eef:u9?
+U+1ef0:U9-.
+U+1ef1:u9-.
+U+1ef2:Y!
+U+1ef3:y!
+U+1ef4:Y-.
+U+1ef5:y-.
+U+1ef6:Y2
+U+1ef7:y2
+U+1ef8:Y?
+U+1ef9:y?
+0x61	U+1f00
+U+1f01:ha
+U+1f02:`a
+U+1f03:h`a
+U+1f04:a'
+U+1f05:ha'
+U+1f06:a~
+U+1f07:ha~
+0x41	U+1f08
+U+1f09:hA
+U+1f0a:`A
+U+1f0b:h`A
+U+1f0c:A'
+U+1f0d:hA'
+U+1f0e:A~
+U+1f0f:hA~
+U+1f11:he
+U+1f19:hE
+U+1f31:hi
+U+1f39:hI
+U+1f41:ho
+U+1f49:hO
+U+1f51:hu
+U+1f59:hU
+U+1fbf:,,
+U+1fc0:?*
+U+1fc1:?:
+U+1fcd:,!
+U+1fce:,'
+U+1fcf:?,
+U+1fdd:;!
+U+1fde:;'
+U+1fdf:?;
+U+1fe5:rh
+U+1fec:Rh
+U+1fed:!:
+U+1fef:!*
+U+1ffe:;;
+# General punctuation:
+0x20	U+2000 U+2002	U+2004-U+2009	U+205F	# spaces
+U+2001 "  "
+U+2003 "  "
+U+200e:(->)
+U+200f:(<-)
+U+200a:
+0x2d	U+2010 U+2011 U+2013 U+2015	# hyphen-like
+U+2014 "--"
+U+2016:||
+U+2017:=2
+0x60	U+2018		# left single quotation mark  <`>
+0x27	U+2019-U+201b	# various single quotation marks <'>
+0x22	U+201c-U+201f	# various double quotation marks <">
+U+2020:/-
+U+2021:/=
+U+2022 " o "
+U+2023 " > "
+0x2e	U+2024
+U+2025:..
+U+2026:...
+U+2027:.
+U+2028 "\015"
+U+2029 "\015\012"
+
+# Dont wanna see these:
+# POP DIRECTIONAL FORMATTING      202C
+U+202c:
+# LEFT-TO-RIGHT OVERRIDE  202D
+U+202d:
+
+U+202f ""
+U+2030: 0/00
+U+2031: 0/000
+U+2032:'
+U+2033:''
+U+2034:'''
+U+2035:`
+U+2036:``
+U+2037:```
+U+2038:^
+U+2039:<
+U+203a:>
+U+203b::X
+U+203c:!!
+U+203d:?!
+U+203e:'-
+U+2042:***
+U+2043 " - "
+U+2044:/
+U+2045:[-
+U+2046:-]
+U+2047:??
+U+2048:?!
+U+2049:!?
+U+204b:|P
+U+204e:*
+U+2051:**
+U+2052:./.
+U+2053:~
+U+2056 " .: "
+U+2057:''''
+U+2058 " .:. "
+U+2059 " :.: "
+U+205a " : "
+U+205b " .:. "
+U+205c ":+:"
+U+2044:/
+U+2047:??
+U+2048:?!
+U+2049:!?
+# end of General punctuation.
+U+2070:^0
+U+2074:^4
+U+2075:^5
+U+2076:^6
+U+2077:^7
+U+2078:^8
+U+2079:^9
+U+207a:^+
+U+207b:^-
+U+207c:^=
+U+207d:^(
+U+207e:^)
+U+207f:^n
+U+2080:_0
+U+2081:_1
+U+2082:_2
+U+2083:_3
+U+2084:_4
+U+2085:_5
+U+2086:_6
+U+2087:_7
+U+2088:_8
+U+2089:_9
+U+208a:_+
+U+208b:_-
+U+208c:_=
+U+208d:(
+U+208e:)
+# Old euro currency sign glyph:
+#U+20A0:CE
+U+20a1:C//
+U+20a2:Cr
+U+20a3:Ff
+U+20a4:Li
+U+20a5:m/
+U+20a6:N=
+U+20a7:Pt
+U+20a8:Rs
+U+20a9:W=
+U+20aa:rJ
+U+20ab:d_
+# New euro currency sign glyph:
+U+20AC:EUR
+U+20ad:K-
+U+20ae:T//
+U+20af:Dp
+U+20b1:P=
+U+20b2:G|
+U+20b3:A=
+U+20b5:C|
+U+2100:a/c
+U+2101:a/s
+U+2103:oC
+U+2104:CL
+U+2105:c/o
+U+2106:c/u
+U+2109:oF
+0x67	U+210a
+0x68	U+210e
+U+210f "\134hbar "
+U+2111:Im
+U+2113:l
+U+2116:No.
+U+2117:(P)
+U+2118:P
+U+211C:Re
+U+211e:Rx
+U+2120:(SM)
+U+2121:TEL
+# TRADE MARK SIGN:
+U+2122:(TM)
+U+2125:oz.
+U+2126:Ohm
+0x4b	U+212A	# Kelvin sign - K
+U+212b:Ang.
+U+212E:est.
+0x6f	U+2134
+U+2135 "Aleph "
+U+2136 "Bet "
+U+2137 "Gimel "
+U+2138 "Dalet "
+U+213B: FAX
+U+2153: 1/3
+U+2154: 2/3
+U+2155: 1/5
+U+2156: 2/5
+U+2157: 3/5
+U+2158: 4/5
+U+2159: 1/6
+U+215a: 5/6
+U+215b: 1/8
+U+215c: 3/8
+U+215d: 5/8
+U+215e: 7/8
+U+215f: 1/
+U+2160:I
+U+2161:II
+U+2162:III
+U+2163:IV
+U+2164:V
+U+2165:VI
+U+2166:VII
+U+2167:VIII
+U+2168:IX
+U+2169:X
+U+216a:XI
+U+216b:XII
+U+216c:L
+U+216d:C
+U+216e:D
+U+216f:M
+U+2170:i
+U+2171:ii
+U+2172:iii
+U+2173:iv
+U+2174:v
+U+2175:vi
+U+2176:vii
+U+2177:viii
+U+2178:ix
+U+2179:x
+U+217a:xi
+U+217b:xii
+U+217c:l
+U+217d:c
+U+217e:d
+U+217f:m
+U+2180:1000RCD
+U+2181:5000R
+U+2182:10000R
+# Arrows
+U+2190:<-
+U+2191 "^|"	# upwards arrow "-^"
+U+2192:->
+U+2193 "|v"	# downwards arrow "-v"
+U+2194:<->
+U+2195 "^|v"	# up down arrow "UD"
+U+2196:^\
+U+2197:/^
+U+2198:\v
+U+2199:v/
+U+219a:</-
+U+219b:-/>
+U+219c:<~
+U+219d:~>
+U+219e:<<-
+U+219f:^^|
+U+21a0:->>
+U+21a1:|vv
+U+21a2:<-<
+U+21a3:>->
+U+21a4:<-|
+U+21a5:^|_
+U+21a6:|->
+U+21a8 "^|v_"	# up down arrow with base "UD-"
+U+21ad:<~>
+U+21ae:<-/->
+U+21af:Nv
+U+21b0:<^|
+U+21b1:|^>
+U+21b2:<v|
+U+21b3:|v>
+U+21b4:-v
+U+21B5:RET
+U+21ba:u<
+U+21bb:>u
+U+21bc:<-
+U+21bd:<-
+U+21be:^|
+U+21bf:^|
+U+21c0:->
+U+21c1:->
+U+21c2:|v
+U+21c3:|v
+U+21c4:<=>
+U+21c5:^||v
+U+21c6:<=>
+U+21c7:<<=
+U+21c8:^|^|
+U+21c9:=>>
+U+21ca:|v|v
+U+21cb:<=>
+U+21cc:<=>
+U+21cd:<=/=
+U+21ce:<=/=>
+U+21cf:=/=>
+U+21d0:<=
+U+21d1 "^||"	# upwards double arrow "^^"
+U+21d2:=>
+U+21d3 "||v"	# downwards double arrow "vv"
+U+21d4:<=>
+U+21d5:^||v
+U+21d6:^\\
+U+21d7://^
+U+21d8:\\v
+U+21d9:v//
+U+21da:<-=
+U+21db:=->
+U+21dc:<~
+U+21dd:~>
+U+21de:^|=|
+U+21df:|=|v
+U+21e0:<-
+U+21e1:^:
+U+21e2:->
+U+21e3::v
+U+21e4:|<-
+U+21e5:->|
+U+21e6:<-
+U+21e7:^|
+U+21e8:->
+U+21e9:|v
+U+21ea:^!
+U+21eb:^I
+U+21ec:^-I
+U+21ed:^|I
+U+21ee:^^|
+U+21ef:^^I
+U+21f0:|->
+U+21f2:\v_|
+U+21f3:^|v
+U+21f4:-o>
+U+21f5:|v^|
+U+21f6:=->>>
+U+21f7:<-|-
+U+21f8:-|->
+U+21f9:<-|->
+U+21fa:<-||-
+U+21fb:-||->
+U+21fc:<-||->
+U+21fd:<-
+U+21fe:->
+U+21ff:<->
+U+2200:FA
+U+2201:C
+U+2202:\partial
+U+2203:TE
+U+2204:TDNE
+U+2205:{}
+U+2206:Delta
+U+2207:Nabla
+U+2208:(-
+U+2209:!(-
+U+220a:(-
+U+220b:-)
+U+220c:!-)
+U+220d:-)
+U+220e " qed"
+U+220f:\prod
+U+2211:\sum
+U+2212:-
+U+2213:-/+
+U+2214:.+
+0x2f	U+2215
+U+2216 " - "
+U+2217:*
+U+2218:Ob
+U+2219:sb
+U+221a " SQRT "
+U+221b " ROOT3 "
+U+221c " ROOT4 "
+U+221d:0(
+U+221e:infty
+U+221f:-L
+U+2220:-V
+U+2225:PP
+U+2226 " !PP "
+U+2227:AND
+U+2228:OR
+U+2229:(U
+U+222a:)U
+U+222b "\134int "
+U+222c "\134int\134int "
+U+222d "\134int\134int\134int "
+U+222e:Io
+U+2234:.:
+U+2235::.
+U+2236::R
+U+2237:::
+U+2238:.-
+U+2239:-:
+U+223c "?1"	# Why not use "~" in this and following tilde-like characters?  I'll assume someone more math-literate than I did this, and leave them alone.
+U+223e:CG
+U+2241:!~
+U+2242:-~
+U+2243:?-
+U+2244:!~-
+U+2245:?=
+U+2246:~!=
+U+2247:!~=
+# ALMOST EQUAL TO:
+U+2248:~=
+U+2249 " !~= "
+U+224b:~3
+U+224c:=?
+U+2250:=...
+U+2253:HI
+U+2254::=
+U+2255:=:
+U+2260:!=
+U+2261:=3
+U+2262 " !=3 "
+U+2263:=4
+U+2264:<=
+U+2265:>=
+U+2266:.LE.
+U+2267:.GE.
+U+2268:.LT.NOT.EQ.
+U+2269:.GT.NOT.EQ.
+U+226a:<<
+U+226b:>>
+U+226e:!<
+U+226f:!>
+U+2270:!<=
+U+2271:!>=
+U+2272:<~
+U+2273:>~
+U+2274:!<~
+U+2275:!>~
+U+2276 " <> "
+U+2277 " >< "
+U+2278 " !<> "
+U+2279 " !>< "
+U+2282:(C
+U+2283:)C
+U+2284 " !(C "
+U+2285 " !)C "
+U+2286:(_
+U+2287:)_
+U+2288:!(_
+U+2289:!)_
+U+228a:(!_
+U+228b:)!_
+U+228f:[
+U+2290:]
+U+2291:[_
+U+2292:]_
+U+2295 "(+)"		# CIRCLED PLUS
+U+2296 "(-)"		# CIRCLED MINUS
+U+2297 "(x)"		# CIRCLED TIMES
+U+2298 "(/)"		# CIRCLED DIVISION SLASH
+U+2299 "(.)"		# CIRCLED DOT OPERATOR
+U+229A "(o)"		# CIRCLED RING OPERATOR
+U+229B "(*)"		# CIRCLED ASTERISK OPERATOR
+U+229C "(=)"		# CIRCLED EQUALS
+U+229D "(-)"		# CIRCLED DASH
+U+229E "[+]"		# SQUARED PLUS
+U+229F "[-]"		# SQUARED MINUS
+U+22A0 "[x]"		# SQUARED TIMES
+U+22A1 "[.]"		# SQUARED DOT OPERATOR
+U+22a5:-T
+U+22A7 " MODELS "		# MODELS
+U+22A8 " TRUE "		# TRUE
+U+22A9 " FORCES "		# FORCES
+U+22AC " !PROVES "		# DOES NOT PROVE
+U+22AD " NOT TRUE "		# NOT TRUE
+U+22AE " !FORCES "		# DOES NOT FORCE
+U+22B2 " NORMAL SUBGROUP OF "
+U+22B3 " CONTAINS AS NORMAL SUBGROUP "
+U+22B4 " NORMAL SUBGROUP OF OR EQUAL TO "
+U+22B5 " CONTAINS AS NORMAL SUBGROUP OR EQUAL TO "
+U+22B8 " MULTIMAP "		# MULTIMAP
+U+22BA " INTERCALATE "		# INTERCALATE
+U+22BB " XOR "		# XOR
+U+22BC " NAND "		# NAND
+U+22C5 " DOT "		# DOT OPERATOR
+U+22c6 " STAR "
+U+22d6:<.
+U+22d7:>.
+U+22d8:<<<
+U+22d9:>>>
+U+22da:<=|>
+U+22db:>=|<
+U+22dc:=<
+U+22dd:=>
+U+22e2:![_
+U+22e3:!]_
+U+22e4:[!_
+U+22e5:]!_
+U+22e6:<!~
+U+22e7:>!~
+U+22ee::3
+U+22ef:.3
+U+2302:Eh
+U+2303:^
+U+2304:v
+U+2307:~~
+U+2308:<7
+U+2309:>7
+U+230a:7<
+U+230b:7>
+U+2310:NI
+U+2312:(A
+U+2315:TR
+U+2318:88
+U+231a:(-/)
+U+231b " >i< "
+U+2320:Iu
+U+2321:Il
+U+2322::(
+U+2323::)
+U+2324:|^|
+U+2325 " OPT "
+U+2326:[X>
+U+2327:[X]
+U+2328:[kbd]
+U+2329:</
+U+232a:/>
+U+232b:<X]
+U+2387 " ALT "
+U+2397:<-pp
+U+2398:pp->
+U+2399:[PrSc]
+U+239a:[ClSc]
+U+23ce " CR "
+U+23cf:_^_
+U+23da:GROUND
+U+2423:Vs
+U+2440:1h
+U+2441:3h
+U+2442:2h
+U+2443:4h
+U+2446:1j
+U+2447:2j
+U+2448:3j
+U+2449:4j
+U+2460:1-o
+U+2461:2-o
+U+2462:3-o
+U+2463:4-o
+U+2464:5-o
+U+2465:6-o
+U+2466:7-o
+U+2467:8-o
+U+2468:9-o
+U+2469:10-o
+U+246a:11-o
+U+246b:12-o
+U+246c:13-o
+U+246d:14-o
+U+246e:15-o
+U+246f:16-o
+U+2470:17-o
+U+2471:18-o
+U+2472:19-o
+U+2473:20-o
+U+2474:(1)
+U+2475:(2)
+U+2476:(3)
+U+2477:(4)
+U+2478:(5)
+U+2479:(6)
+U+247a:(7)
+U+247b:(8)
+U+247c:(9)
+U+247d:(10)
+U+247e:(11)
+U+247f:(12)
+U+2480:(13)
+U+2481:(14)
+U+2482:(15)
+U+2483:(16)
+U+2484:(17)
+U+2485:(18)
+U+2486:(19)
+U+2487:(20)
+U+2488:1.
+U+2489:2.
+U+248a:3.
+U+248b:4.
+U+248c:5.
+U+248d:6.
+U+248e:7.
+U+248f:8.
+U+2490:9.
+U+2491:10.
+U+2492:11.
+U+2493:12.
+U+2494:13.
+U+2495:14.
+U+2496:15.
+U+2497:16.
+U+2498:17.
+U+2499:18.
+U+249a:19.
+U+249b:20.
+U+249c:(a)
+U+249d:(b)
+U+249e:(c)
+U+249f:(d)
+U+24a0:(e)
+U+24a1:(f)
+U+24a2:(g)
+U+24a3:(h)
+U+24a4:(i)
+U+24a5:(j)
+U+24a6:(k)
+U+24a7:(l)
+U+24a8:(m)
+U+24a9:(n)
+U+24aa:(o)
+U+24ab:(p)
+U+24ac:(q)
+U+24ad:(r)
+U+24ae:(s)
+U+24af:(t)
+U+24b0:(u)
+U+24b1:(v)
+U+24b2:(w)
+U+24b3:(x)
+U+24b4:(y)
+U+24b5:(z)
+U+24b6:A-o
+U+24b7:B-o
+U+24b8:C-o
+U+24b9:D-o
+U+24ba:E-o
+U+24bb:F-o
+U+24bc:G-o
+U+24bd:H-o
+U+24be:I-o
+U+24bf:J-o
+U+24c0:K-o
+U+24c1:L-o
+U+24c2:M-o
+U+24c3:N-o
+U+24c4:O-o
+U+24c5:P-o
+U+24c6:Q-o
+U+24c7:R-o
+U+24c8:S-o
+U+24c9:T-o
+U+24ca:U-o
+U+24cb:V-o
+U+24cc:W-o
+U+24cd:X-o
+U+24ce:Y-o
+U+24cf:Z-o
+U+24d0:a-o
+U+24d1:b-o
+U+24d2:c-o
+U+24d3:d-o
+U+24d4:e-o
+U+24d5:f-o
+U+24d6:g-o
+U+24d7:h-o
+U+24d8:i-o
+U+24d9:j-o
+U+24da:k-o
+U+24db:l-o
+U+24dc:m-o
+U+24dd:n-o
+U+24de:o-o
+U+24df:p-o
+U+24e0:q-o
+U+24e1:r-o
+U+24e2:s-o
+U+24e3:t-o
+U+24e4:u-o
+U+24e5:v-o
+U+24e6:w-o
+U+24e7:x-o
+U+24e8:y-o
+U+24e9:z-o
+U+24ea:0-o
+U+2500:-
+U+2501:=
+U+2502:|
+U+2503:|
+U+2504:-
+U+2505:=
+U+2506:|
+U+2507:|
+U+2508:-
+U+2509:=
+U+250a:|
+U+250b:|
+0x2b	U+250c-U+256c	# box drawings, use +
+U+2571:/
+U+2572:\
+U+2580:TB
+U+2584:LB
+U+2588:FB
+U+258c:lB
+U+2590:RB
+U+2591:.S
+U+2592::S
+U+2593:?S
+U+25a0:fS
+U+25a1:OS
+U+25a2:RO
+U+25a3:Rr
+U+25a4:RF
+U+25a5:RY
+U+25a6:RH
+U+25a7:RZ
+U+25a8:RK
+U+25a9:RX
+U+25aa:sB
+U+25ac:SR
+U+25ad:Or
+U+25b2:^
+U+25b3:uT
+U+25b6:|>
+U+25b7:Tr
+U+25ba:|>
+U+25bc:v
+U+25bd:dT
+U+25c0:<|
+U+25c1:Tl
+U+25c4:<|
+U+25c6:Db
+U+25c7:Dw
+U+25ca:LZ
+U+25cb:0m
+U+25ce:0o
+U+25cf:0M
+U+25d0:0L
+U+25d1:0R
+U+25d8:Sn
+U+25d9:Ic
+U+25e2:Fd
+U+25e3:Bd
+U+25ef:Ci
+# Miscellaneous Symbols
+U+2600 "SU"	# cf. U+263c
+U+2601:cOo
+U+2602:J
+U+2603:"8"
+U+2605:*
+U+2606:*
+U+2607:<v
+U+2608:Rv
+U+2609 "Sol"	# cf. astrological symbols U+263c - U+2647, star-like U+2600 
+U+260a:Asc.
+U+260b:Desc.
+U+260c:Conj.
+U+260d:Opp.
+U+260e:TEL
+U+260f:tel
+0x58    U+2611  U+2612  # checked ballot boxes -> x
+U+2613:X
+U+2614 "\"J\""	# umbrella with rain drops, quote marks are part of the symbol
+U+2615:Joe
+U+261a:<==
+U+261b:==>
+U+261c:<--
+U+261d:||^
+U+261e:-->
+U+261f:||v
+U+2620 "!X!"	# or "POISON ", cf. U+2621
+U+2621 "!Z!"	# previously "CAUTION " - or is it better to leave it spelled out in English?
+U+2622 "!R!"	# or "RADIOACTIVE ", cf. U+2621
+U+2623 "!B!"	# or "BIOHAZARD ", cf. U+2621
+U+2624 "2TS"	# cf. U+2695
+U+2626:t
+U+2627:XP
+U+2628:t
+U+2629:+
+U+262a:(*
+U+262d:'\,)
+U+262e:(PEACE)
+U+262f:Pd
+U+2630:-HVN-
+U+2631:-LAK-
+U+2632:-FIR-
+U+2633:-THR-
+U+2634:-WND-
+U+2635:-WTR-
+U+2636:-MTN-
+U+2637:-RTH-
+U+2638:*
+U+2639::-(
+U+263a::-)
+U+263b:(-:
+U+263c "su"	# previously "SU" - cf. U+2600, typical to have "dark" character in uppercase, eg. U+260e, U+260f
+U+263d "Lun1"	# Luna, 1st quarter
+U+263e "Lun3"	# Luna, 3rd quarter
+U+263f:Mer
+U+2640 "Ven"	# previously "f." - this section labeled as astrological symbols
+U+2641 "Ter"	# Terra, to go with other Latin names
+U+2642 "Mar"	# previously "m." - this section labeled as astrological symbols
+U+2643:Jup
+U+2644:Sat
+U+2645:Ura
+U+2646:Nep
+U+2647:Plu
+U+2648 "Ari"	# Standard astronomical abbreviation
+U+2649 "Tau"
+U+264a "Gem"
+U+264b "Cnc"
+U+264c "Leo"
+U+264d "Vir"
+U+264e "Lib"
+U+264f "Sco"
+U+2650 "Sgr"
+U+2651 "Cap"
+U+2652 "Aqr"
+U+2653 "Psc"
+U+2654 "k"	# white chess king
+U+2655:q
+U+2656:r
+U+2657:b
+U+2658:n
+U+2659:p
+U+265a "K"	# black chess king
+U+265b:Q
+U+265c:R
+U+265d:B
+U+265e:N
+U+265f:P
+U+2660 "cS"	# black spade suit
+U+2661:ch
+U+2662:cd
+U+2663:cC
+U+2664:cs
+U+2665:cH
+U+2666:cD
+U+2667:cc
+#Musical symbols
+U+2669:d
+U+266a:d`
+U+266b:d-d
+U+266c:d=d
+U+266d:b
+U+266e:N
+U+266f:#
+U+2670:t
+U+2671:t
+#Recycling symbols
+U+2672:/_\
+U+2673:/1\
+U+2674:/2\
+U+2675:/3\
+U+2676:/4\
+U+2677:/5\
+U+2678:/6\
+U+2679:/7\
+U+267a:/_\
+U+267b:/_\
+U+267c:/P\
+U+267d:/p\
+U+267e:(oo)
+U+267f "oL"	# wheelchair symbol
+U+2680:d1
+U+2681:d2
+U+2682:d3
+U+2683:d4
+U+2684:d5
+U+2685:d6
+U+2686:(.)
+U+2687:(:)
+U+2688:((.))
+U+2689:((:))
+U+2690 " f "
+U+2691 " F "
+U+2692:'X`
+U+2693 "+-)"	# anchor (sideways)
+U+2694:,X,
+U+2695 "$"	# cf. U+2624
+U+2696 "^T^"	# scales of justice
+U+269a "}T{"	# staff of Hermes
+U+269b ":*:"	# atom symbol
+U+269c "}|{"	# fleur-de-lis
+U+26a0 "!!!"	# or "WARNING ", cf. U+2621
+U+26a1 "!V!"	# or "VOLTAGE ", cf. U+2621
+U+26a2 "f.f."	# two females, lesbian
+U+26a3 "m.m."	# two males, homosexual
+U+26a4 "m.f."	# male and female, bisexual
+U+26a5 "mf."	# cf. U+26A4
+U+26a6 "xm."	# transgendered male
+U+26a7 "xmf."	# transgendered male/female
+U+26aa:o
+U+26ab:O
+U+26ac:o
+U+26ad:oo
+U+26ae:o|o
+U+26af:o-o
+U+26b0 "/b/"	# buried/coffin
+U+26b1 "/c/"	# cremated/urn
+U+26b2 "n."	# cf. U+26a2 - U+26a7
+# Dingbats
+U+2702:8<
+U+2704:>8
+U+2706:(TEL)
+U+2708:+->-
+U+2709 "[v]"	# envelope
+U+270c:mV,
+0x58	U+2713	U+2714	U+2717	U+2718	# check marks -> x
+U+2715: x
+U+2716: X
+U+2719:+
+U+271a:+
+U+271b:+
+U+271c:+
+U+271d:t
+U+271e:t
+U+271f:t
+U+2720:-X
+0x2a	U+2721	U+272a	U+272b	U+272c	U+272d	U+272e	U+272f	U+2730	U+2731	U+2732	U+2733	U+2734	U+2735	U+2736	U+2737	U+2738	U+2739	U+273a	U+273b	U+273c	U+273d
+0x2a	U+2742	U+2743	U+2744	U+2745	U+2746	U+2747	U+2748	U+2749	U+274a	U+274b
+U+2756:<x>
+U+2758:|
+U+2759:|
+U+275a:|
+U+275b:'
+U+275c:'
+U+275d:"
+U+275e:"
+U+2762:!
+U+2763:!
+U+2765:<3
+U+2768:(
+U+2769:)
+U+276a:(
+U+276b:)
+U+276c:<
+U+276d:>
+U+276e:<
+U+276f:>
+U+2770:<
+U+2771:>
+U+2772:[
+U+2773:]
+U+2774:{
+U+2775:}
+U+2776:((1))
+U+2777:((2))
+U+2778:((3))
+U+2779:((4))
+U+277a:((5))
+U+277b:((6))
+U+277c:((7))
+U+277d:((8))
+U+277e:((9))
+U+277f:((10))
+U+2780:(1)
+U+2781:(2)
+U+2782:(3)
+U+2783:(4)
+U+2784:(5)
+U+2785:(6)
+U+2786:(7)
+U+2787:(8)
+U+2788:(9)
+U+2789:(10)
+U+278a:((1))
+U+278b:((2))
+U+278c:((3))
+U+278d:((4))
+U+278e:((5))
+U+278f:((6))
+U+2790:((7))
+U+2791:((8))
+U+2792:((9))
+U+2793:((10))
+U+2794:->
+U+2798:\v
+U+2799:->
+U+279a:/^
+U+279b:->
+U+279c:->
+U+279d:->
+U+279e:->
+U+279f:->
+U+27a0:->
+U+27a1:->
+U+27a2:>
+U+27a3:>
+U+27a4:>
+U+27a5:->
+U+27a6:->
+U+27a7:->
+U+27a8:->
+U+27a9:->
+U+27aa:->
+U+27ab:->
+U+27ac:->
+U+27ad:->
+U+27ae:->
+U+27af:->
+U+27b0:->
+U+27b2:(->)
+U+27b3:>>->
+U+27b4:vv\v
+U+27b5:>>->
+U+27b6:^^/^
+U+27b7:vv\v
+U+27b8:>>->
+U+27b9:^^/^
+U+27ba:->
+U+27bb:->
+U+27bc:->
+U+27bd:->
+U+27be:->
+# Supplemental Arrows A
+U+27f0:^||||
+U+27f1:||||v
+U+27f2:vO
+U+27f3:Ov
+U+27f4:(+)>
+U+27f5:<---
+U+27f6:--->
+U+27f7:<--->
+U+27f8:<===
+U+27f9:===>
+U+27fa:<===>
+U+27fb:<---|
+U+27fc:|--->
+U+27fd:<===|
+U+27fe:|===>
+U+27ff:~~~>
+# Supplemental Arrows B
+U+2900:-|->>
+U+2901:-||->>
+U+2902:<=|=
+U+2903:=|=>
+U+2904:<=|=>
+U+2905:|->>
+U+2906:<=|
+U+2907:|=>
+U+2908:|-|v
+U+2909:^|-|
+U+290a:^|||
+U+290b:|||v
+U+290c:<- -
+U+290d:- ->
+U+290e:<- - -
+U+290f:- - ->
+U+2910:>- - ->
+U+2911:->
+U+2913:|v_
+U+2914:>-|->
+U+2915:>-||->
+U+2916:>->>
+U+2917:>-|->>
+U+2918:>-||->>
+U+2919:-<
+U+291a:>-
+U+291b:-<<
+U+291c:>>-
+U+291d:<><-
+U+291e:-><>
+U+291f:<><-|
+U+2920:|-><>
+U+2921:^\v
+U+2922:v/^
+U+2923:^\,
+U+2924:,/^
+U+2927:^X^
+U+292b:X
+U+292c:X
+U+292d:Xv^
+U+292e:X^v
+U+292f:X ^
+U+2930:X v
+U+2931:^X^
+U+2932:^X^
+U+2933:~>
+U+2934:-^
+U+2935:-v
+U+2938:)v
+U+2939:(v
+U+2945:->+
+U+2946:<-+
+U+2947:-x->
+U+2948:<-o->
+U+2949:^^|o
+U+294a:<->
+U+294b:<->
+U+294c:^|v
+U+294d:^|v
+U+294e:<->
+U+294f:^|v
+U+2950:<->
+U+2951:^|v
+U+2952:|<-
+U+2953:->|
+U+2955:|v_
+U+2956:|<-
+U+2957:->|
+U+2959:|v_
+U+295a:<-|
+U+295b:|->
+U+295c:^|_
+U+295e:<-|
+U+295f:|->
+U+2960:^|_
+U+2962:<=
+U+2963:^||
+U+2964:=>
+U+2965:||v
+U+2970:=)
+U+2a00 "(.)"
+U+2a01 "(+)"
+U+2a02 "(x)"
+U+2a09: *
+U+2a0c "\134int\134int\134int\134int "
+U+2a2f:x
+U+2a30:.x
+U+2a31:x_
+U+2a33:xx
+U+2a34:(x
+U+2a35:x)
+U+2a37:((x))
+U+2a38:(/)
+U+2a39:/+\
+U+2a3a:/-\
+U+2a3b:/x\
+U+2a74:::=
+U+2a75:==
+U+2a76:===
+U+2a77::=:
+#Miscellaneous Symbols and Arrows
+U+2b00:/^
+U+2b01:^\
+U+2b02:\v
+U+2b03:v/
+U+2b04:<->
+U+2b05:<-
+U+2b06:^|
+U+2b07:|v
+U+2b08:/^
+U+2b09:^\
+U+2b0a:\v
+U+2b0b:v/
+U+2b0c:<->
+U+2b0d:^|v
+U+2b0e:-v
+U+2b0f:-^
+U+2b10:v-
+U+2b11:^-
+# Supplemental punctuation
+U+2e0f:__
+U+2e1e:.~
+U+2e1f:~.
+U+2e28:((
+U+2e29:))
+U+2e2a ":."
+U+2e2b ".:"
+U+2e2c "::"
+U+2e2f:~
+#  CJK area:
+0x20	U+3000	# ideographic space
+U+3001:,_
+U+3002:._
+U+3003:+"
+U+3004:JIS
+U+3005:*_
+U+3006:;_
+U+3007:0_
+U+300a:<+
+U+300b:>+
+U+300c:<'
+U+300d:>'
+U+300e:<"
+U+300f:>"
+U+3010:("
+U+3011:)"
+U+3012:=T
+U+3013:=_
+U+3014:('
+U+3015:)'
+U+3016:(I
+U+3017:)I
+U+301a:[[
+U+301b:]]
+U+301c:-?
+U+3020:=T:)
+0x20	U+303f
+U+3041:A5
+U+3042:a5
+U+3043:I5
+U+3044:i5
+U+3045:U5
+U+3046:u5
+U+3047:E5
+U+3048:e5
+U+3049:O5
+U+304a:o5
+U+304b:ka
+U+304c:ga
+U+304d:ki
+U+304e:gi
+U+304f:ku
+U+3050:gu
+U+3051:ke
+U+3052:ge
+U+3053:ko
+U+3054:go
+U+3055:sa
+U+3056:za
+U+3057:si
+U+3058:zi
+U+3059:su
+U+305a:zu
+U+305b:se
+U+305c:ze
+U+305d:so
+U+305e:zo
+U+305f:ta
+U+3060:da
+U+3061:ti
+U+3062:di
+U+3063:tU
+U+3064:tu
+U+3065:du
+U+3066:te
+U+3067:de
+U+3068:to
+U+3069:do
+U+306a:na
+U+306b:ni
+U+306c:nu
+U+306d:ne
+U+306e:no
+U+306f:ha
+U+3070:ba
+U+3071:pa
+U+3072:hi
+U+3073:bi
+U+3074:pi
+U+3075:hu
+U+3076:bu
+U+3077:pu
+U+3078:he
+U+3079:be
+U+307a:pe
+U+307b:ho
+U+307c:bo
+U+307d:po
+U+307e:ma
+U+307f:mi
+U+3080:mu
+U+3081:me
+U+3082:mo
+U+3083:yA
+U+3084:ya
+U+3085:yU
+U+3086:yu
+U+3087:yO
+U+3088:yo
+U+3089:ra
+U+308a:ri
+U+308b:ru
+U+308c:re
+U+308d:ro
+U+308e:wA
+U+308f:wa
+U+3090:wi
+U+3091:we
+U+3092:wo
+U+3093:n5
+U+3094:vu
+U+309b:"5
+U+309c:05
+U+309d:*5
+U+309e:+5
+U+30a1:a6
+U+30a2:A6
+U+30a3:i6
+U+30a4:I6
+U+30a5:u6
+U+30a6:U6
+U+30a7:e6
+U+30a8:E6
+U+30a9:o6
+U+30aa:O6
+U+30ab:Ka
+U+30ac:Ga
+U+30ad:Ki
+U+30ae:Gi
+U+30af:Ku
+U+30b0:Gu
+U+30b1:Ke
+U+30b2:Ge
+U+30b3:Ko
+U+30b4:Go
+U+30b5:Sa
+U+30b6:Za
+U+30b7:Si
+U+30b8:Zi
+U+30b9:Su
+U+30ba:Zu
+U+30bb:Se
+U+30bc:Ze
+U+30bd:So
+U+30be:Zo
+U+30bf:Ta
+U+30c0:Da
+U+30c1:Ti
+U+30c2:Di
+U+30c3:TU
+U+30c4:Tu
+U+30c5:Du
+U+30c6:Te
+U+30c7:De
+U+30c8:To
+U+30c9:Do
+U+30ca:Na
+U+30cb:Ni
+U+30cc:Nu
+U+30cd:Ne
+U+30ce:No
+U+30cf:Ha
+U+30d0:Ba
+U+30d1:Pa
+U+30d2:Hi
+U+30d3:Bi
+U+30d4:Pi
+U+30d5:Hu
+U+30d6:Bu
+U+30d7:Pu
+U+30d8:He
+U+30d9:Be
+U+30da:Pe
+U+30db:Ho
+U+30dc:Bo
+U+30dd:Po
+U+30de:Ma
+U+30df:Mi
+U+30e0:Mu
+U+30e1:Me
+U+30e2:Mo
+U+30e3:YA
+U+30e4:Ya
+U+30e5:YU
+U+30e6:Yu
+U+30e7:YO
+U+30e8:Yo
+U+30e9:Ra
+U+30ea:Ri
+U+30eb:Ru
+U+30ec:Re
+U+30ed:Ro
+U+30ee:WA
+U+30ef:Wa
+U+30f0:Wi
+U+30f1:We
+U+30f2:Wo
+U+30f3:N6
+U+30f4:Vu
+U+30f5:KA
+U+30f6:KE
+U+30f7:Va
+U+30f8:Vi
+U+30f9:Ve
+U+30fa:Vo
+U+30fb:.6
+U+30fc:-6
+U+30fd:*6
+U+30fe:+6
+U+3105:b4
+U+3106:p4
+U+3107:m4
+U+3108:f4
+U+3109:d4
+U+310a:t4
+U+310b:n4
+U+310c:l4
+U+310d:g4
+U+310e:k4
+U+310f:h4
+U+3110:j4
+U+3111:q4
+U+3112:x4
+U+3113:zh
+U+3114:ch
+U+3115:sh
+U+3116:r4
+U+3117:z4
+U+3118:c4
+U+3119:s4
+U+311a:a4
+U+311b:o4
+U+311c:e4
+U+311d:eh4
+U+311e:ai
+U+311f:ei
+U+3120:au
+U+3121:ou
+U+3122:an
+U+3123:en
+U+3124:aN
+U+3125:eN
+U+3126:er
+U+3127:i4
+U+3128:u4
+U+3129:iu
+U+312a:v4
+U+312b:nG
+U+312c:gn
+U+321c:(JU)
+U+3220:1c
+U+3221:2c
+U+3222:3c
+U+3223:4c
+U+3224:5c
+U+3225:6c
+U+3226:7c
+U+3227:8c
+U+3228:9c
+U+3229:10c
+U+327f:KSC
+U+33c2:am
+U+33d8:pm
+#
+#
+#There are four special ranges of characters that are represented only by
+#their start and end characters <...>
+#
+#   The CJK Ideographs Area (U+4E00 - U+9FFF)
+#   The Hangul Syllables Area (U+AC00 - U+D7A3)
+#   The Surrogates Area (U+D800 - U+DFFF)
+#   The Private Use Area (U+E000 - U+F8FF)
+#
+#
+U+fb00:ff
+U+fb01:fi
+U+fb02:fl
+U+fb03:ffi
+U+fb04:ffl
+U+fb05:St
+U+fb06:st
+U+fe7d:3+;
+U+fe82:aM.
+U+fe84:aH.
+U+fe88:ah.
+U+fe8d:a+-
+U+fe8e:a+.
+U+fe8f:b+-
+U+fe90:b+.
+U+fe91:b+,
+U+fe92:b+;
+U+fe93:tm-
+U+fe94:tm.
+U+fe95:t+-
+U+fe96:t+.
+U+fe97:t+,
+U+fe98:t+;
+U+fe99:tk-
+U+fe9a:tk.
+U+fe9b:tk,
+U+fe9c:tk;
+U+fe9d:g+-
+U+fe9e:g+.
+U+fe9f:g+,
+U+fea0:g+;
+U+fea1:hk-
+U+fea2:hk.
+U+fea3:hk,
+U+fea4:hk;
+U+fea5:x+-
+U+fea6:x+.
+U+fea7:x+,
+U+fea8:x+;
+U+fea9:d+-
+U+feaa:d+.
+U+feab:dk-
+U+feac:dk.
+U+fead:r+-
+U+feae:r+.
+U+feaf:z+-
+U+feb0:z+.
+U+feb1:s+-
+U+feb2:s+.
+U+feb3:s+,
+U+feb4:s+;
+U+feb5:sn-
+U+feb6:sn.
+U+feb7:sn,
+U+feb8:sn;
+U+feb9:c+-
+U+feba:c+.
+U+febb:c+,
+U+febc:c+;
+U+febd:dd-
+U+febe:dd.
+U+febf:dd,
+U+fec0:dd;
+U+fec1:tj-
+U+fec2:tj.
+U+fec3:tj,
+U+fec4:tj;
+U+fec5:zH-
+U+fec6:zH.
+U+fec7:zH,
+U+fec8:zH;
+U+fec9:e+-
+U+feca:e+.
+U+fecb:e+,
+U+fecc:e+;
+U+fecd:i+-
+U+fece:i+.
+U+fecf:i+,
+U+fed0:i+;
+U+fed1:f+-
+U+fed2:f+.
+U+fed3:f+,
+U+fed4:f+;
+U+fed5:q+-
+U+fed6:q+.
+U+fed7:q+,
+U+fed8:q+;
+U+fed9:k+-
+U+feda:k+.
+U+fedb:k+,
+U+fedc:k+;
+U+fedd:l+-
+U+fede:l+.
+U+fedf:l+,
+U+fee0:l+;
+U+fee1:m+-
+U+fee2:m+.
+U+fee3:m+,
+U+fee4:m+;
+U+fee5:n+-
+U+fee6:n+.
+U+fee7:n+,
+U+fee8:n+;
+U+fee9:h+-
+U+feea:h+.
+U+feeb:h+,
+U+feec:h+;
+U+feed:w+-
+U+feee:w+.
+U+feef:j+-
+U+fef0:j+.
+U+fef1:y+-
+U+fef2:y+.
+U+fef3:y+,
+U+fef4:y+;
+U+fef5:lM-
+U+fef6:lM.
+U+fef7:lH-
+U+fef8:lH.
+U+fef9:lh-
+U+fefa:lh.
+U+fefb:la-
+U+fefc:la.
+
+# the reverse byte-order-mark: zero-width non-break space
+U+feff ""
+
+0x21-0x7e	U+ff01-U+ff5e
+0x2e	U+ff61
+0x22	U+ff62	U+ff63
+0x2c	U+ff64
+
+# Symbols for C0 and C1 control characters, in case they get through...
+U+0000:NUL
+U+0001:SH
+U+0002:SX
+U+0003:EX
+U+0004:ET
+U+0005:ENQ
+U+0006:AK
+U+0007:BL
+U+0008:BS
+U+0009:HT
+U+000a:LF
+U+000b:VT
+U+000c:FF
+U+000d:CR
+U+000e:SO
+U+000f:SI
+U+0010:DL
+U+0011:DC1
+U+0012:DC2
+U+0013:DC3
+U+0014:DC4
+U+0015:NAK
+U+0016:SYN
+U+0017:EB
+U+0018:CN
+U+0019:EM
+U+001a:SB
+U+001b:ESC
+U+001c:FS
+U+001d:GS
+U+001e:RS
+U+001f:US
+U+007f:DT
+# Most of these characters (80-9F) may be inflicted on us
+# by MS FrontPages which uses Unicode notation such as &#153;
+# but there are no assigned letters in Unicode 128-159 range.
+# It is assumed in the code that those codepoints are from windows-1252.
+#U+0080:PA
+#U+0081:HO
+#U+0082:BH
+#U+0083:NH
+#U+0084:IN
+#U+0085:NL
+U+0085 "\012"
+#U+0086:SA
+#U+0087:ES
+#U+0088:HS
+#U+0089:HJ
+#U+008a:VS
+#U+008b:PD
+#U+008c:PU
+#U+008d:RI
+#U+008e:SS2
+#U+008f:SS3
+#U+0090:DCS
+#U+0091:P1
+#U+0092:P2
+#U+0093:TS
+#U+0094:CC
+#U+0095:MW
+#U+0096:SG
+#U+0097:EG
+#U+0098:SS
+#U+0099:GC
+#U+009a:SC
+#U+009b:CSI
+#U+009c:ST
+#U+009d:OC
+#U+009e:PM
+#U+009f:AC
+
+# Let's try to show a question mark for character that cannot
+# be shown.  U+fffd is used for invalid characters.
+# It works, but let's stick with UHHH representation. - FM
+#U+fffd "?"
diff --git a/src/chrtrans/dmcs_uni.tbl b/src/chrtrans/dmcs_uni.tbl
new file mode 100644
index 00000000..51072fad
--- /dev/null
+++ b/src/chrtrans/dmcs_uni.tbl
@@ -0,0 +1,233 @@
+#The MIME name of this charset.
+Mdec-mcs
+
+#Name as a Display Charset (used on Options screen)
+ODEC Multinational
+
+#
+#   Name:             DEC Multinational (dec-mcs) [to unicode]
+#   Date:             29 October 1997
+#   Author:           Fote
+#
+#   1999-01-01	various corrections, verified against actual DEC VT220
+#		Christian "naddy" Weisgerber <naddy@mips.rhein-neckar.de>
+#
+##################
+
+#0x20    U+0020  # SPACE
+#0x21    U+0021  # EXCLAMATION MARK
+#0x22    U+0022  # QUOTATION MARK
+#0x23    U+0023  # NUMBER SIGN
+#0x24    U+0024  # DOLLAR SIGN
+#0x25    U+0025  # PERCENT SIGN
+#0x26    U+0026  # AMPERSAND
+#0x27    U+0027  # APOSTROPHE
+#0x28    U+0028  # LEFT PARENTHESIS
+#0x29    U+0029  # RIGHT PARENTHESIS
+#0x2A    U+002A  # ASTERISK
+#0x2B    U+002B  # PLUS SIGN
+#0x2C    U+002C  # COMMA
+#0x2D    U+002D  # HYPHEN-MINUS
+#0x2E    U+002E  # FULL STOP
+#0x2F    U+002F  # SOLIDUS
+#0x30    U+0030  # DIGIT ZERO
+#0x31    U+0031  # DIGIT ONE
+#0x32    U+0032  # DIGIT TWO
+#0x33    U+0033  # DIGIT THREE
+#0x34    U+0034  # DIGIT FOUR
+#0x35    U+0035  # DIGIT FIVE
+#0x36    U+0036  # DIGIT SIX
+#0x37    U+0037  # DIGIT SEVEN
+#0x38    U+0038  # DIGIT EIGHT
+#0x39    U+0039  # DIGIT NINE
+#0x3A    U+003A  # COLON
+#0x3B    U+003B  # SEMICOLON
+#0x3C    U+003C  # LESS-THAN SIGN
+#0x3D    U+003D  # EQUALS SIGN
+#0x3E    U+003E  # GREATER-THAN SIGN
+#0x3F    U+003F  # QUESTION MARK
+#0x40    U+0040  # COMMERCIAL AT
+#0x41    U+0041  # LATIN CAPITAL LETTER A
+#0x42    U+0042  # LATIN CAPITAL LETTER B
+#0x43    U+0043  # LATIN CAPITAL LETTER C
+#0x44    U+0044  # LATIN CAPITAL LETTER D
+#0x45    U+0045  # LATIN CAPITAL LETTER E
+#0x46    U+0046  # LATIN CAPITAL LETTER F
+#0x47    U+0047  # LATIN CAPITAL LETTER G
+#0x48    U+0048  # LATIN CAPITAL LETTER H
+#0x49    U+0049  # LATIN CAPITAL LETTER I
+#0x4A    U+004A  # LATIN CAPITAL LETTER J
+#0x4B    U+004B  # LATIN CAPITAL LETTER K
+#0x4C    U+004C  # LATIN CAPITAL LETTER L
+#0x4D    U+004D  # LATIN CAPITAL LETTER M
+#0x4E    U+004E  # LATIN CAPITAL LETTER N
+#0x4F    U+004F  # LATIN CAPITAL LETTER O
+#0x50    U+0050  # LATIN CAPITAL LETTER P
+#0x51    U+0051  # LATIN CAPITAL LETTER Q
+#0x52    U+0052  # LATIN CAPITAL LETTER R
+#0x53    U+0053  # LATIN CAPITAL LETTER S
+#0x54    U+0054  # LATIN CAPITAL LETTER T
+#0x55    U+0055  # LATIN CAPITAL LETTER U
+#0x56    U+0056  # LATIN CAPITAL LETTER V
+#0x57    U+0057  # LATIN CAPITAL LETTER W
+#0x58    U+0058  # LATIN CAPITAL LETTER X
+#0x59    U+0059  # LATIN CAPITAL LETTER Y
+#0x5A    U+005A  # LATIN CAPITAL LETTER Z
+#0x5B    U+005B  # LEFT SQUARE BRACKET
+#0x5C    U+005C  # REVERSE SOLIDUS
+#0x5D    U+005D  # RIGHT SQUARE BRACKET
+#0x5E    U+005E  # CIRCUMFLEX ACCENT
+#0x5F    U+005F  # LOW LINE
+#0x60    U+0060  # GRAVE ACCENT
+#0x61    U+0061  # LATIN SMALL LETTER A
+#0x62    U+0062  # LATIN SMALL LETTER B
+#0x63    U+0063  # LATIN SMALL LETTER C
+#0x64    U+0064  # LATIN SMALL LETTER D
+#0x65    U+0065  # LATIN SMALL LETTER E
+#0x66    U+0066  # LATIN SMALL LETTER F
+#0x67    U+0067  # LATIN SMALL LETTER G
+#0x68    U+0068  # LATIN SMALL LETTER H
+#0x69    U+0069  # LATIN SMALL LETTER I
+#0x6A    U+006A  # LATIN SMALL LETTER J
+#0x6B    U+006B  # LATIN SMALL LETTER K
+#0x6C    U+006C  # LATIN SMALL LETTER L
+#0x6D    U+006D  # LATIN SMALL LETTER M
+#0x6E    U+006E  # LATIN SMALL LETTER N
+#0x6F    U+006F  # LATIN SMALL LETTER O
+#0x70    U+0070  # LATIN SMALL LETTER P
+#0x71    U+0071  # LATIN SMALL LETTER Q
+#0x72    U+0072  # LATIN SMALL LETTER R
+#0x73    U+0073  # LATIN SMALL LETTER S
+#0x74    U+0074  # LATIN SMALL LETTER T
+#0x75    U+0075  # LATIN SMALL LETTER U
+#0x76    U+0076  # LATIN SMALL LETTER V
+#0x77    U+0077  # LATIN SMALL LETTER W
+#0x78    U+0078  # LATIN SMALL LETTER X
+#0x79    U+0079  # LATIN SMALL LETTER Y
+#0x7A    U+007A  # LATIN SMALL LETTER Z
+#0x7B    U+007B  # LEFT CURLY BRACKET
+#0x7C    U+007C  # VERTICAL LINE
+#0x7D    U+007D  # RIGHT CURLY BRACKET
+#0x7E    U+007E  # TILDE
+#
+0x20-0x7E       idem	# ASCII
+
+0xA1    U+00A1  # inverted exclamation mark (&#161;) - iexcl
+0xA2    U+00A2  # cent sign (&#162;) - cent
+0xA3    U+00A3  # pound sign (&#163;) - pound
+0xA5    U+00A5  # yen sign (&#165;) - yen
+# broken vertical bar (&#166;) - brvbar, brkbar
+U+00A6:|
+0xA7    U+00A7  # section sign (&#167;) - sect
+0xA8    U+00A4  # currency sign (&#164;) - curren
+# spacing diaresis (&#168;) - uml, die
+U+00A8:"
+0xA9    U+00A9  # copyright sign (&#169;) - copy
+0xAA    U+00AA  # feminine ordinal indicator (&#170;) - ordf
+0xAB    U+00AB  # angle quotation mark, left (&#171;) - laquo
+# negation sign (&#172); - not
+U+00AC:NOT
+# soft hyphen (&#173;) - shy
+#U+00AD
+# circled R registered sign (&#174;) - reg
+U+00AE:(R)
+# spacing macron (&#175;) - hibar, macr
+U+00AF:-
+0xB0    U+00B0  # degree sign (&#176;) - deg
+0xB1    U+00B1  # plus-or-minus sign (&#177;) - plusmn
+0xB2    U+00B2  # superscript 2 (&#178;) - sup2
+0xB3    U+00B3  # superscript 3 (&#179;) - sup3
+#spacing acute (&#180;) - acute
+U+00B4:'
+0xB5    U+00B5  # micro sign (&#181;) - micro
+0xB6    U+00B6  # paragraph sign (&#182;) - para
+0xB7    U+00B7 # middle dot (&#183;) - middot
+# spacing cedilla (&#184;) - cedil
+U+00B8:,
+0xB9    U+00B9  # superscript 1 (&#185;) - sup1
+0xBA    U+00BA  # masculine ordinal indicator (&#186;) - ordm
+0xBB    U+00BB  # angle quotation mark, right (&#187;) - raquo
+0xBC    U+00BC  # fraction 1/4 (&#188;) - frac14
+0xBD    U+00BD  # fraction 1/2 (&#189;) - frac12
+# fraction 3/4 (&#190;) - frac34
+U+00BE: 3/4
+0xBF    U+00BF  # inverted question mark (&#191;) - iquest
+0xC0    U+00C0  # capital A, grave accent (&#192;) - Agrave
+0xC1    U+00C1  # capital A, acute accent (&#193;) - Aacute
+0xC2    U+00C2  # capital A, circumflex accent (&#194;) - Acirc
+0xC3    U+00C3  # capital A, tilde (&#195;) - Atilde
+0xC4    U+00C4  # capital A, dieresis or umlaut mark (&#196;) - Auml
+0xC5    U+00C5  # capital A, ring (&#197;) - Aring
+0xC6    U+00C6  # capital AE diphthong (ligature) (&#198;) - AElig
+0xC7    U+00C7  # capital C, cedilla (&#199;) - Ccedil
+0xC8    U+00C8  # capital E, grave accent (&#200;) - Egrave
+0xC9    U+00C9  # capital E, acute accent (&#201;) - Eacute
+0xCA    U+00CA  # capital E, circumflex accent (&#202;) - Ecirc
+0xCB    U+00CB  # capital E, dieresis or umlaut mark (&#203;) - Euml
+0xCC    U+00CC  # capital I, grave accent (&#204;) - Igrave
+0xCD    U+00CD  # capital I, acute accent (&#205;) - Iacute
+0xCE    U+00CE  # capital I, circumflex accent (&#206;) - Icirc
+0xCF    U+00CF  # capital I, dieresis or umlaut mark (&#207;) - Iuml
+# capital Eth, Icelandic (&#208;) - ETH */
+U+00D0:DH
+#      Dj  # capital D with stroke - Dstrok
+0xD1    U+00D1  # capital N, tilde (&#209;) - Ntilde
+0xD2    U+00D2  # capital O, grave accent (&#210;) - Ograve
+0xD3    U+00D3  # capital O, acute accent (&#211;) - Oacute
+0xD4    U+00D4  # capital O, circumflex accent (&#212;) - Ocirc
+0xD5    U+00D5  # capital O, tilde (&#213;) - Otilde
+0xD6    U+00D6  # capital O, dieresis or umlaut mark (&#214;) - Ouml
+0xD7    U+0152  # captial OE ligature (&#338;) - OElig
+# multiplication sign (&#215;) - times
+U+00D7:*
+0xD8    U+00D8  # capital O, slash (&#216;) - Oslash
+0xD9    U+00D9  # capital U, grave accent (&#217;) - Ugrave
+0xDA    U+00DA  # capital U, acute accent (&#218;) - Uacute
+0xDB    U+00DB  # capital U, circumflex accent (&#219;) - Ucirc
+0xDC    U+00DC  # capital U, dieresis or umlaut mark (&#220;) - Uuml
+0xDD    U+0178  # capital Y, dieresis or umlaut mark (&#376;) - Yuml
+# capital Y, acute accent (&#221;) - Yacute
+U+00DD:Y'
+# capital THORN, Icelandic (&#222;) - THORN */
+U+00DE:TH
+0xDF    U+00DF  # small sharp s, German (sz ligature) (&#223;) - szlig
+0xE0    U+00E0  # small a, grave accent (&#224;) - agrave
+0xE1    U+00E1  # small a, acute accent (&#225;) - aacute
+0xE2    U+00E2  # small a, circumflex accent (&#226;) - acirc
+0xE3    U+00E3  # small a, tilde (&#227;) - atilde
+0xE4    U+00E4  # small a, dieresis or umlaut mark (&#228;) - auml
+0xE5    U+00E5  # small a, ring (&#229;) - aring
+0xE6    U+00E6  # small ae diphthong (ligature) (&#230;) - aelig
+0xE7    U+00E7  # small c, cedilla (&#231;) - ccedil
+0xE8    U+00E8  # small e, grave accent (&#232;) - egrave
+0xE9    U+00E9  # small e, acute accent (&#233;) - eacute
+0xEA    U+00EA  # small e, circumflex accent (&#234;) - ecirc
+0xEB    U+00EB  # small e, dieresis or umlaut mark (&#235;) - euml
+0xEC    U+00EC  # small i, grave accent (&#236;) - igrave
+0xED    U+00ED  # small i, acute accent (&#237;) - iacute
+0xEE    U+00EE  # small i, circumflex accent (&#238;) - icirc
+0xEF    U+00EF  # small i, dieresis or umlaut mark (&#239;) - iuml
+# small eth, Icelandic (&#240;) - eth
+U+00F0:dh
+0xF1    U+00F1  # small n, tilde (&#241;) - ntilde
+0xF2    U+00F2  # small o, grave accent (&#242;) - ograve
+0xF3    U+00F3  # small o, acute accent (&#243;) - oacute
+0xF4    U+00F4  # small o, circumflex accent (&#244;) - ocirc
+0xF5    U+00F5  # small o, tilde (&#245;) - otilde
+0xF6    U+00F6  # small o, dieresis or umlaut mark (&#246;) - ouml
+0xF7    U+0153  # small oe ligature (&#339;) - oelig
+# division sign (&#247;) - divide
+U+00F7:/
+0xF8    U+00F8  # small o, slash (&#248;) - oslash
+0xF9    U+00F9  # small u, grave accent (&#249;) - ugrave
+0xFA    U+00FA  # small u, acute accent (&#250;) - uacute
+0xFB    U+00FB  # small u, circumflex accent (&#251;) - ucirc
+0xFC    U+00FC  # small u, dieresis or umlaut mark (&#252;) - uuml
+0xFD    U+00FF  # small y, dieresis or umlaut mark (&#255;) - yuml
+# small y, acute accent (&#253;) - yacute
+U+00FD:y'
+# small thorn, Icelandic (&#254;) - thorn
+U+00FE:th
+#
+# TRADE MARK SIGN
+U+2122:(TM)
diff --git a/src/chrtrans/entities.h b/src/chrtrans/entities.h
new file mode 100644
index 00000000..40816495
--- /dev/null
+++ b/src/chrtrans/entities.h
@@ -0,0 +1,1414 @@
+/*
+ * $LynxId: entities.h,v 1.5 2007/07/31 20:40:07 Tim.Larson Exp $
+ *
+ *     Entity Names to Unicode table
+ *     -----------------------------
+ *
+ *     This is a one-way mapping to Unicode so chartrans implementation
+ *     now process character entities like &nbsp the similar way it handles
+ *     the numeric entities like &#123.
+ *     The only call to this structure is via HTMLGetEntityUCValue().
+ *
+
+Unlike the numeric entities &#234 which may be for any Unicode character, the
+character references should be defined within HTML standards to get a
+compatibility between browsers.
+
+Now we have a choice:  use clean HTML4.0 entities list (and reject everithing
+others), or use a relaxed list with lots of synonyms and new symbols found at
+
+ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/MISC/SGML.TXT
+
+We hold both: #define ENTITIES_HTML40_ONLY for strict version,
+otherwise relaxed.
+
+ */
+
+#include <UCkd.h>		/* typedef u16 */
+typedef struct {
+    const char *name;		/* sorted alphabetically (case-sensitive) */
+    u16 code;
+} UC_entity_info;
+
+static const UC_entity_info unicode_entities[] =
+/* *INDENT-OFF* */
+#ifdef ENTITIES_HTML40_ONLY
+/*********************************************************************
+
+   The full list of character references defined as part of HTML 4.0.
+   http://www.w3.org/TR/PR-html40/sgml/entities.html
+
+   Informal history:
+   * ISO Latin 1 entities for 160-255 range were introduced in HTML 2.0
+   * few important entities were added, including &lt, &gt, &amp.
+   * Greek letters and some math symbols were finally added in HTML 4.0
+
+   Totally 252 entries (Nov 1997 HTML 4.0 draft), it is 1:1 mapping.
+   Please do not add more unless a new HTML version will be released,
+   try the #else table for experiments and fun...
+
+****/
+{
+  {"AElig",      198}, /* latin capital letter AE = latin capital ligature AE, U+00C6 ISOlat1 */
+  {"Aacute",     193}, /* latin capital letter A with acute, U+00C1 ISOlat1 */
+  {"Acirc",      194}, /* latin capital letter A with circumflex, U+00C2 ISOlat1 */
+  {"Agrave",     192}, /* latin capital letter A with grave = latin capital letter A grave, U+00C0 ISOlat1 */
+  {"Alpha",      913}, /* greek capital letter alpha, U+0391 */
+  {"Aring",      197}, /* latin capital letter A with ring above = latin capital letter A ring, U+00C5 ISOlat1 */
+  {"Atilde",     195}, /* latin capital letter A with tilde, U+00C3 ISOlat1 */
+  {"Auml",       196}, /* latin capital letter A with diaeresis, U+00C4 ISOlat1 */
+  {"Beta",       914}, /* greek capital letter beta, U+0392 */
+  {"Ccedil",     199}, /* latin capital letter C with cedilla, U+00C7 ISOlat1 */
+  {"Chi",        935}, /* greek capital letter chi, U+03A7 */
+  {"Dagger",    8225}, /* double dagger, U+2021 ISOpub */
+  {"Delta",      916}, /* greek capital letter delta, U+0394 ISOgrk3 */
+  {"ETH",        208}, /* latin capital letter ETH, U+00D0 ISOlat1 */
+  {"Eacute",     201}, /* latin capital letter E with acute, U+00C9 ISOlat1 */
+  {"Ecirc",      202}, /* latin capital letter E with circumflex, U+00CA ISOlat1 */
+  {"Egrave",     200}, /* latin capital letter E with grave, U+00C8 ISOlat1 */
+  {"Epsilon",    917}, /* greek capital letter epsilon, U+0395 */
+  {"Eta",        919}, /* greek capital letter eta, U+0397 */
+  {"Euml",       203}, /* latin capital letter E with diaeresis, U+00CB ISOlat1 */
+  {"Gamma",      915}, /* greek capital letter gamma, U+0393 ISOgrk3 */
+  {"Iacute",     205}, /* latin capital letter I with acute, U+00CD ISOlat1 */
+  {"Icirc",      206}, /* latin capital letter I with circumflex, U+00CE ISOlat1 */
+  {"Igrave",     204}, /* latin capital letter I with grave, U+00CC ISOlat1 */
+  {"Iota",       921}, /* greek capital letter iota, U+0399 */
+  {"Iuml",       207}, /* latin capital letter I with diaeresis, U+00CF ISOlat1 */
+  {"Kappa",      922}, /* greek capital letter kappa, U+039A */
+  {"Lambda",     923}, /* greek capital letter lambda, U+039B ISOgrk3 */
+  {"Mu",         924}, /* greek capital letter mu, U+039C */
+  {"Ntilde",     209}, /* latin capital letter N with tilde, U+00D1 ISOlat1 */
+  {"Nu",         925}, /* greek capital letter nu, U+039D */
+  {"OElig",      338}, /* latin capital ligature OE, U+0152 ISOlat2 */
+  {"Oacute",     211}, /* latin capital letter O with acute, U+00D3 ISOlat1 */
+  {"Ocirc",      212}, /* latin capital letter O with circumflex, U+00D4 ISOlat1 */
+  {"Ograve",     210}, /* latin capital letter O with grave, U+00D2 ISOlat1 */
+  {"Omega",      937}, /* greek capital letter omega, U+03A9 ISOgrk3 */
+  {"Omicron",    927}, /* greek capital letter omicron, U+039F */
+  {"Oslash",     216}, /* latin capital letter O with stroke = latin capital letter O slash, U+00D8 ISOlat1 */
+  {"Otilde",     213}, /* latin capital letter O with tilde, U+00D5 ISOlat1 */
+  {"Ouml",       214}, /* latin capital letter O with diaeresis, U+00D6 ISOlat1 */
+  {"Phi",        934}, /* greek capital letter phi, U+03A6 ISOgrk3 */
+  {"Pi",         928}, /* greek capital letter pi, U+03A0 ISOgrk3 */
+  {"Prime",     8243}, /* double prime = seconds = inches, U+2033 ISOtech */
+  {"Psi",        936}, /* greek capital letter psi, U+03A8 ISOgrk3 */
+  {"Rho",        929}, /* greek capital letter rho, U+03A1 */
+  {"Scaron",     352}, /* latin capital letter S with caron, U+0160 ISOlat2 */
+/* there is no Sigmaf, and no U+03A2 character either */
+  {"Sigma",      931}, /* greek capital letter sigma, U+03A3 ISOgrk3 */
+  {"THORN",      222}, /* latin capital letter THORN, U+00DE ISOlat1 */
+  {"Tau",        932}, /* greek capital letter tau, U+03A4 */
+  {"Theta",      920}, /* greek capital letter theta, U+0398 ISOgrk3 */
+  {"Uacute",     218}, /* latin capital letter U with acute, U+00DA ISOlat1 */
+  {"Ucirc",      219}, /* latin capital letter U with circumflex, U+00DB ISOlat1 */
+  {"Ugrave",     217}, /* latin capital letter U with grave, U+00D9 ISOlat1 */
+  {"Upsilon",    933}, /* greek capital letter upsilon, U+03A5 ISOgrk3 */
+  {"Uuml",       220}, /* latin capital letter U with diaeresis, U+00DC ISOlat1 */
+  {"Xi",         926}, /* greek capital letter xi, U+039E ISOgrk3 */
+  {"Yacute",     221}, /* latin capital letter Y with acute, U+00DD ISOlat1 */
+  {"Yuml",       376}, /* latin capital letter Y with diaeresis, U+0178 ISOlat2 */
+  {"Zeta",       918}, /* greek capital letter zeta, U+0396 */
+  {"aacute",     225}, /* latin small letter a with acute, U+00E1 ISOlat1 */
+  {"acirc",      226}, /* latin small letter a with circumflex, U+00E2 ISOlat1 */
+  {"acute",      180}, /* acute accent = spacing acute, U+00B4 ISOdia */
+  {"aelig",      230}, /* latin small letter ae = latin small ligature ae, U+00E6 ISOlat1 */
+  {"agrave",     224}, /* latin small letter a with grave = latin small letter a grave, U+00E0 ISOlat1 */
+  {"alefsym",   8501}, /* alef symbol = first transfinite cardinal, U+2135 NEW */
+/* alef symbol is NOT the same as hebrew letter alef, U+05D0 although the same glyph could be used to depict both characters */
+  {"alpha",      945}, /* greek small letter alpha, U+03B1 ISOgrk3 */
+  {"amp",         38}, /* ampersand, U+0026 ISOnum */
+  {"and",       8743}, /* logical and = wedge, U+2227 ISOtech */
+  {"ang",       8736}, /* angle, U+2220 ISOamso */
+  {"aring",      229}, /* latin small letter a with ring above = latin small letter a ring, U+00E5 ISOlat1 */
+  {"asymp",     8776}, /* almost equal to = asymptotic to, U+2248 ISOamsr */
+  {"atilde",     227}, /* latin small letter a with tilde, U+00E3 ISOlat1 */
+  {"auml",       228}, /* latin small letter a with diaeresis, U+00E4 ISOlat1 */
+  {"bdquo",     8222}, /* double low-9 quotation mark, U+201E NEW */
+  {"beta",       946}, /* greek small letter beta, U+03B2 ISOgrk3 */
+  {"brvbar",     166}, /* broken bar = broken vertical bar, U+00A6 ISOnum */
+  {"bull",      8226}, /* bullet = black small circle, U+2022 ISOpub  */
+/* bullet is NOT the same as bullet operator, U+2219 */
+  {"cap",       8745}, /* intersection = cap, U+2229 ISOtech */
+  {"ccedil",     231}, /* latin small letter c with cedilla, U+00E7 ISOlat1 */
+  {"cedil",      184}, /* cedilla = spacing cedilla, U+00B8 ISOdia */
+  {"cent",       162}, /* cent sign, U+00A2 ISOnum */
+  {"chi",        967}, /* greek small letter chi, U+03C7 ISOgrk3 */
+  {"circ",       710}, /* modifier letter circumflex accent, U+02C6 ISOpub */
+  {"clubs",     9827}, /* black club suit = shamrock, U+2663 ISOpub */
+  {"cong",      8773}, /* approximately equal to, U+2245 ISOtech */
+  {"copy",       169}, /* copyright sign, U+00A9 ISOnum */
+  {"crarr",     8629}, /* downwards arrow with corner leftwards = carriage return, U+21B5 NEW */
+  {"cup",       8746}, /* union = cup, U+222A ISOtech */
+  {"curren",     164}, /* currency sign, U+00A4 ISOnum */
+  {"dArr",      8659}, /* downwards double arrow, U+21D3 ISOamsa */
+  {"dagger",    8224}, /* dagger, U+2020 ISOpub */
+  {"darr",      8595}, /* downwards arrow, U+2193 ISOnum */
+  {"deg",        176}, /* degree sign, U+00B0 ISOnum */
+  {"delta",      948}, /* greek small letter delta, U+03B4 ISOgrk3 */
+  {"diams",     9830}, /* black diamond suit, U+2666 ISOpub */
+  {"divide",     247}, /* division sign, U+00F7 ISOnum */
+  {"eacute",     233}, /* latin small letter e with acute, U+00E9 ISOlat1 */
+  {"ecirc",      234}, /* latin small letter e with circumflex, U+00EA ISOlat1 */
+  {"egrave",     232}, /* latin small letter e with grave, U+00E8 ISOlat1 */
+  {"empty",     8709}, /* empty set = null set = diameter, U+2205 ISOamso */
+  {"emsp",      8195}, /* em space, U+2003 ISOpub */
+  {"ensp",      8194}, /* en space, U+2002 ISOpub */
+  {"epsilon",    949}, /* greek small letter epsilon, U+03B5 ISOgrk3 */
+  {"equiv",     8801}, /* identical to, U+2261 ISOtech */
+  {"eta",        951}, /* greek small letter eta, U+03B7 ISOgrk3 */
+  {"eth",        240}, /* latin small letter eth, U+00F0 ISOlat1 */
+  {"euml",       235}, /* latin small letter e with diaeresis, U+00EB ISOlat1 */
+  {"euro",      8364}, /* euro sign, U+20AC NEW */
+  {"exist",     8707}, /* there exists, U+2203 ISOtech */
+  {"fnof",       402}, /* latin small f with hook = function = florin, U+0192 ISOtech */
+  {"forall",    8704}, /* for all, U+2200 ISOtech */
+  {"frac12",     189}, /* vulgar fraction one half = fraction one half, U+00BD ISOnum */
+  {"frac14",     188}, /* vulgar fraction one quarter = fraction one quarter, U+00BC ISOnum */
+  {"frac34",     190}, /* vulgar fraction three quarters = fraction three quarters, U+00BE ISOnum */
+  {"frasl",     8260}, /* fraction slash, U+2044 NEW */
+  {"gamma",      947}, /* greek small letter gamma, U+03B3 ISOgrk3 */
+  {"ge",        8805}, /* greater-than or equal to, U+2265 ISOtech */
+  {"gt",          62}, /* greater-than sign, U+003E ISOnum */
+  {"hArr",      8660}, /* left right double arrow, U+21D4 ISOamsa */
+  {"harr",      8596}, /* left right arrow, U+2194 ISOamsa */
+  {"hearts",    9829}, /* black heart suit = valentine, U+2665 ISOpub */
+  {"hellip",    8230}, /* horizontal ellipsis = three dot leader, U+2026 ISOpub  */
+  {"iacute",     237}, /* latin small letter i with acute, U+00ED ISOlat1 */
+  {"icirc",      238}, /* latin small letter i with circumflex, U+00EE ISOlat1 */
+  {"iexcl",      161}, /* inverted exclamation mark, U+00A1 ISOnum */
+  {"igrave",     236}, /* latin small letter i with grave, U+00EC ISOlat1 */
+  {"image",     8465}, /* blackletter capital I = imaginary part, U+2111 ISOamso */
+  {"infin",     8734}, /* infinity, U+221E ISOtech */
+  {"int",       8747}, /* integral, U+222B ISOtech */
+  {"iota",       953}, /* greek small letter iota, U+03B9 ISOgrk3 */
+  {"iquest",     191}, /* inverted question mark = turned question mark, U+00BF ISOnum */
+  {"isin",      8712}, /* element of, U+2208 ISOtech */
+  {"iuml",       239}, /* latin small letter i with diaeresis, U+00EF ISOlat1 */
+  {"kappa",      954}, /* greek small letter kappa, U+03BA ISOgrk3 */
+  {"lArr",      8656}, /* leftwards double arrow, U+21D0 ISOtech */
+/* Unicode does not say that lArr is the same as the 'is implied by' arrow
+    but also does not have any other character for that function. So ? lArr can
+    be used for 'is implied by' as ISOtech suggests */
+  {"lambda",     955}, /* greek small letter lambda, U+03BB ISOgrk3 */
+  {"lang",      9001}, /* left-pointing angle bracket = bra, U+2329 ISOtech */
+/* lang is NOT the same character as U+003C 'less than' or U+2039 'single left-pointing angle quotation mark' */
+  {"laquo",      171}, /* left-pointing double angle quotation mark = left pointing guillemet, U+00AB ISOnum */
+  {"larr",      8592}, /* leftwards arrow, U+2190 ISOnum */
+  {"lceil",     8968}, /* left ceiling = apl upstile, U+2308 ISOamsc  */
+  {"ldquo",     8220}, /* left double quotation mark, U+201C ISOnum */
+  {"le",        8804}, /* less-than or equal to, U+2264 ISOtech */
+  {"lfloor",    8970}, /* left floor = apl downstile, U+230A ISOamsc  */
+  {"lowast",    8727}, /* asterisk operator, U+2217 ISOtech */
+  {"loz",       9674}, /* lozenge, U+25CA ISOpub */
+  {"lrm",       8206}, /* left-to-right mark, U+200E NEW RFC 2070 */
+  {"lsaquo",    8249}, /* single left-pointing angle quotation mark, U+2039 ISO proposed */
+/* lsaquo is proposed but not yet ISO standardised */
+  {"lsquo",     8216}, /* left single quotation mark, U+2018 ISOnum */
+  {"lt",          60}, /* less-than sign, U+003C ISOnum */
+  {"macr",       175}, /* macron = spacing macron = overline = APL overbar, U+00AF ISOdia */
+  {"mdash",     8212}, /* em dash, U+2014 ISOpub */
+  {"micro",      181}, /* micro sign, U+00B5 ISOnum */
+  {"middot",     183}, /* middle dot = Georgian comma = Greek middle dot, U+00B7 ISOnum */
+  {"minus",     8722}, /* minus sign, U+2212 ISOtech */
+  {"mu",         956}, /* greek small letter mu, U+03BC ISOgrk3 */
+  {"nabla",     8711}, /* nabla = backward difference, U+2207 ISOtech */
+  {"nbsp",       160}, /* no-break space = non-breaking space, U+00A0 ISOnum */
+  {"ndash",     8211}, /* en dash, U+2013 ISOpub */
+  {"ne",        8800}, /* not equal to, U+2260 ISOtech */
+  {"ni",        8715}, /* contains as member, U+220B ISOtech */
+/* should there be a more memorable name than 'ni'? */
+  {"not",        172}, /* not sign = discretionary hyphen, U+00AC ISOnum */
+  {"notin",     8713}, /* not an element of, U+2209 ISOtech */
+  {"nsub",      8836}, /* not a subset of, U+2284 ISOamsn */
+  {"ntilde",     241}, /* latin small letter n with tilde, U+00F1 ISOlat1 */
+  {"nu",         957}, /* greek small letter nu, U+03BD ISOgrk3 */
+  {"oacute",     243}, /* latin small letter o with acute, U+00F3 ISOlat1 */
+  {"ocirc",      244}, /* latin small letter o with circumflex, U+00F4 ISOlat1 */
+  {"oelig",      339}, /* latin small ligature oe, U+0153 ISOlat2 */
+  {"ograve",     242}, /* latin small letter o with grave, U+00F2 ISOlat1 */
+  {"oline",     8254}, /* overline = spacing overscore, U+203E NEW */
+  {"omega",      969}, /* greek small letter omega, U+03C9 ISOgrk3 */
+  {"omicron",    959}, /* greek small letter omicron, U+03BF NEW */
+  {"oplus",     8853}, /* circled plus = direct sum, U+2295 ISOamsb */
+  {"or",        8744}, /* logical or = vee, U+2228 ISOtech */
+  {"ordf",       170}, /* feminine ordinal indicator, U+00AA ISOnum */
+  {"ordm",       186}, /* masculine ordinal indicator, U+00BA ISOnum */
+  {"oslash",     248}, /* latin small letter o with stroke, = latin small letter o slash, U+00F8 ISOlat1 */
+  {"otilde",     245}, /* latin small letter o with tilde, U+00F5 ISOlat1 */
+  {"otimes",    8855}, /* circled times = vector product, U+2297 ISOamsb */
+  {"ouml",       246}, /* latin small letter o with diaeresis, U+00F6 ISOlat1 */
+  {"para",       182}, /* pilcrow sign = paragraph sign, U+00B6 ISOnum */
+  {"part",      8706}, /* partial differential, U+2202 ISOtech  */
+  {"permil",    8240}, /* per mille sign, U+2030 ISOtech */
+  {"perp",      8869}, /* up tack = orthogonal to = perpendicular, U+22A5 ISOtech */
+  {"phi",        966}, /* greek small letter phi, U+03C6 ISOgrk3 */
+  {"pi",         960}, /* greek small letter pi, U+03C0 ISOgrk3 */
+  {"piv",        982}, /* greek pi symbol, U+03D6 ISOgrk3 */
+  {"plusmn",     177}, /* plus-minus sign = plus-or-minus sign, U+00B1 ISOnum */
+  {"pound",      163}, /* pound sign, U+00A3 ISOnum */
+  {"prime",     8242}, /* prime = minutes = feet, U+2032 ISOtech */
+  {"prod",      8719}, /* n-ary product = product sign, U+220F ISOamsb */
+/* prod is NOT the same character as U+03A0 'greek capital letter pi' though the same glyph might be used for both */
+  {"prop",      8733}, /* proportional to, U+221D ISOtech */
+  {"psi",        968}, /* greek small letter psi, U+03C8 ISOgrk3 */
+  {"quot",        34}, /* quotation mark = APL quote, U+0022 ISOnum */
+  {"rArr",      8658}, /* rightwards double arrow, U+21D2 ISOtech */
+/* Unicode does not say this is the 'implies' character but does not have
+     another character with this function so ?
+     rArr can be used for 'implies' as ISOtech suggests */
+  {"radic",     8730}, /* square root = radical sign, U+221A ISOtech */
+  {"rang",      9002}, /* right-pointing angle bracket = ket, U+232A ISOtech */
+/* rang is NOT the same character as U+003E 'greater than' or U+203A 'single right-pointing angle quotation mark' */
+  {"raquo",      187}, /* right-pointing double angle quotation mark = right pointing guillemet, U+00BB ISOnum */
+  {"rarr",      8594}, /* rightwards arrow, U+2192 ISOnum */
+  {"rceil",     8969}, /* right ceiling, U+2309 ISOamsc  */
+  {"rdquo",     8221}, /* right double quotation mark, U+201D ISOnum */
+  {"real",      8476}, /* blackletter capital R = real part symbol, U+211C ISOamso */
+  {"reg",        174}, /* registered sign = registered trade mark sign, U+00AE ISOnum */
+  {"rfloor",    8971}, /* right floor, U+230B ISOamsc  */
+  {"rho",        961}, /* greek small letter rho, U+03C1 ISOgrk3 */
+  {"rlm",       8207}, /* right-to-left mark, U+200F NEW RFC 2070 */
+  {"rsaquo",    8250}, /* single right-pointing angle quotation mark, U+203A ISO proposed */
+/* rsaquo is proposed but not yet ISO standardised */
+  {"rsquo",     8217}, /* right single quotation mark, U+2019 ISOnum */
+  {"sbquo",     8218}, /* single low-9 quotation mark, U+201A NEW */
+  {"scaron",     353}, /* latin small letter s with caron, U+0161 ISOlat2 */
+  {"sdot",      8901}, /* dot operator, U+22C5 ISOamsb */
+/* dot operator is NOT the same character as U+00B7 middle dot */
+  {"sect",       167}, /* section sign, U+00A7 ISOnum */
+  {"shy",        173}, /* soft hyphen = discretionary hyphen, U+00AD ISOnum */
+  {"sigma",      963}, /* greek small letter sigma, U+03C3 ISOgrk3 */
+  {"sigmaf",     962}, /* greek small letter final sigma, U+03C2 ISOgrk3 */
+  {"sim",       8764}, /* tilde operator = varies with = similar to, U+223C ISOtech */
+/* tilde operator is NOT the same character as the tilde, U+007E, although the same glyph might be used to represent both */
+  {"spades",    9824}, /* black spade suit, U+2660 ISOpub */
+/* black here seems to mean filled as opposed to hollow */
+  {"sub",       8834}, /* subset of, U+2282 ISOtech */
+  {"sube",      8838}, /* subset of or equal to, U+2286 ISOtech */
+  {"sum",       8721}, /* n-ary sumation, U+2211 ISOamsb */
+/* sum is NOT the same character as U+03A3 'greek capital letter sigma' though the same glyph might be used for both */
+  {"sup",       8835}, /* superset of, U+2283 ISOtech */
+/* note that nsup, 'not a superset of, U+2283' is not covered by the Symbol
+     font encoding and is not included. Should it be, for symmetry?
+     It is in ISOamsn */
+  {"sup1",       185}, /* superscript one = superscript digit one, U+00B9 ISOnum */
+  {"sup2",       178}, /* superscript two = superscript digit two = squared, U+00B2 ISOnum */
+  {"sup3",       179}, /* superscript three = superscript digit three = cubed, U+00B3 ISOnum */
+  {"supe",      8839}, /* superset of or equal to, U+2287 ISOtech */
+  {"szlig",      223}, /* latin small letter sharp s = ess-zed,  U+00DF ISOlat1 */
+  {"tau",        964}, /* greek small letter tau, U+03C4 ISOgrk3 */
+  {"there4",    8756}, /* therefore, U+2234 ISOtech */
+  {"theta",      952}, /* greek small letter theta, U+03B8 ISOgrk3 */
+  {"thetasym",   977}, /* greek small letter theta symbol, U+03D1 NEW */
+  {"thinsp",    8201}, /* thin space, U+2009 ISOpub */
+  {"thorn",      254}, /* latin small letter thorn with, U+00FE ISOlat1 */
+  {"tilde",      732}, /* small tilde, U+02DC ISOdia */
+  {"times",      215}, /* multiplication sign, U+00D7 ISOnum */
+  {"trade",     8482}, /* trade mark sign, U+2122 ISOnum */
+  {"uArr",      8657}, /* upwards double arrow, U+21D1 ISOamsa */
+  {"uacute",     250}, /* latin small letter u with acute, U+00FA ISOlat1 */
+  {"uarr",      8593}, /* upwards arrow, U+2191 ISOnum */
+  {"ucirc",      251}, /* latin small letter u with circumflex, U+00FB ISOlat1 */
+  {"ugrave",     249}, /* latin small letter u with grave, U+00F9 ISOlat1 */
+  {"uml",        168}, /* diaeresis = spacing diaeresis, U+00A8 ISOdia */
+  {"upsih",      978}, /* greek upsilon with hook symbol, U+03D2 NEW */
+  {"upsilon",    965}, /* greek small letter upsilon, U+03C5 ISOgrk3 */
+  {"uuml",       252}, /* latin small letter u with diaeresis, U+00FC ISOlat1 */
+  {"weierp",    8472}, /* script capital P = power set = Weierstrass p, U+2118 ISOamso */
+  {"xi",         958}, /* greek small letter xi, U+03BE ISOgrk3 */
+  {"yacute",     253}, /* latin small letter y with acute, U+00FD ISOlat1 */
+  {"yen",        165}, /* yen sign = yuan sign, U+00A5 ISOnum */
+  {"yuml",       255}, /* latin small letter y with diaeresis, U+00FF ISOlat1 */
+  {"zeta",       950}, /* greek small letter zeta, U+03B6 ISOgrk3 */
+  {"zwj",       8205}, /* zero width joiner, U+200D NEW RFC 2070 */
+  {"zwnj",      8204}, /* zero width non-joiner, U+200C NEW RFC 2070 */
+};
+
+#else /* not ENTITIES_HTML40_ONLY: */
+/***************************************************************************
+
+This table prepared from ftp://ftp.unicode.org/MAPPINGS/VENDORS/MISC/SGML.TXT
+original comment follows:
+
+
+# Author: John Cowan <cowan@ccil.org>
+# Date: 25 July 1997
+#
+# The following table maps SGML character entities from various
+# public sets (namely, ISOamsa, ISOamsb, ISOamsc, ISOamsn, ISOamso,
+# ISOamsr, ISObox, ISOcyr1, ISOcyr2, ISOdia, ISOgrk1, ISOgrk2,
+# ISOgrk3, ISOgrk4, ISOlat1, ISOlat2, ISOnum, ISOpub, ISOtech,
+# HTMLspecial, HTMLsymbol) to corresponding Unicode characters.
+#
+# The table has four tab-separated columns:
+#	Column 1: SGML character entity name
+#	Column 2: SGML public entity set
+#	Column 3: Unicode 2.0 character code
+#	Column 4: Unicode 2.0 character name (UPPER CASE)
+# Entries which don't have Unicode equivalents have "0x????"
+# in Column 3 and a lower case description (from the public entity
+# set DTD) in Column 4.  The mapping is not reversible, because many
+# distinctions are unified away in Unicode, particularly between
+# mathematical symbols.
+
+
+   We just sort it and move column 2 away (line too long, sorry;
+   look at sgml.html in test/ directory for details).
+
+Changes:
+   * Add few (obsolete) synonyms for compatibility with Lynx/2.5 and up:
+          "brkbar"  for "brvbar" 0x00A6
+          "emdash"  for "mdash" 0x2014
+          "endash"  for "ndash" 0x2013
+          "hibar"  for "macr" 0x00AF
+     BTW, lots of synonyms found in this table, we shouldn't worry about...
+     Totally around 1000 entries.
+
+
+Modified by Jacob Poon <jacob.poon@utoronto.ca>
+
+This table is modified improve support of HTML 4.0 character entity references,
+including Euro symbol support ("euro" 0x20AC).
+
+Known issues:
+
+The original table includes two different definitions of &loz; reference.
+Since HTML 4.0 only uses U+25CA, the U+2727 definition is commented out,
+until there is a good reason to put it back in.
+
+"b.delta" mapping fixed (was 0x03B3 = small gamma).
+
+At the end of the table, there are several unnumbered, commented references.
+These are not defined in HTML 4.0, and will remain so until they are defined
+in future SGML/HTML standards.
+
+The support for obsolete references are for backwards compatibility only.  New
+SGML/HTML documents should not depend on these references just because Lynx can
+display them.
+
+****/
+{
+  {"AElig",	0x00C6},  /* LATIN CAPITAL LETTER AE			   */
+  {"Aacgr",	0x0386},  /* GREEK CAPITAL LETTER ALPHA WITH TONOS	   */
+  {"Aacute",	0x00C1},  /* LATIN CAPITAL LETTER A WITH ACUTE		   */
+  {"Abreve",	0x0102},  /* LATIN CAPITAL LETTER A WITH BREVE		   */
+  {"Acirc",	0x00C2},  /* LATIN CAPITAL LETTER A WITH CIRCUMFLEX	   */
+  {"Acy",	0x0410},  /* CYRILLIC CAPITAL LETTER A			   */
+  {"Agr",	0x0391},  /* GREEK CAPITAL LETTER ALPHA 		   */
+  {"Agrave",	0x00C0},  /* LATIN CAPITAL LETTER A WITH GRAVE		   */
+  {"Alpha",	0x0391},  /* GREEK CAPITAL LETTER ALPHA 		   */
+  {"Amacr",	0x0100},  /* LATIN CAPITAL LETTER A WITH MACRON 	   */
+  {"Aogon",	0x0104},  /* LATIN CAPITAL LETTER A WITH OGONEK 	   */
+  {"Aring",	0x00C5},  /* LATIN CAPITAL LETTER A WITH RING ABOVE	   */
+  {"Atilde",	0x00C3},  /* LATIN CAPITAL LETTER A WITH TILDE		   */
+  {"Auml",	0x00C4},  /* LATIN CAPITAL LETTER A WITH DIAERESIS	   */
+  {"Barwed",	0x2306},  /* PERSPECTIVE				   */
+  {"Bcy",	0x0411},  /* CYRILLIC CAPITAL LETTER BE 		   */
+  {"Beta",	0x0392},  /* GREEK CAPITAL LETTER BETA			   */
+  {"Bgr",	0x0392},  /* GREEK CAPITAL LETTER BETA			   */
+  {"CHcy",	0x0427},  /* CYRILLIC CAPITAL LETTER CHE		   */
+  {"Cacute",	0x0106},  /* LATIN CAPITAL LETTER C WITH ACUTE		   */
+  {"Cap",	0x22D2},  /* DOUBLE INTERSECTION			   */
+  {"Ccaron",	0x010C},  /* LATIN CAPITAL LETTER C WITH CARON		   */
+  {"Ccedil",	0x00C7},  /* LATIN CAPITAL LETTER C WITH CEDILLA	   */
+  {"Ccirc",	0x0108},  /* LATIN CAPITAL LETTER C WITH CIRCUMFLEX	   */
+  {"Cdot",	0x010A},  /* LATIN CAPITAL LETTER C WITH DOT ABOVE	   */
+  {"Chi",	0x03A7},  /* GREEK CAPITAL LETTER CHI			   */
+  {"Cup",	0x22D3},  /* DOUBLE UNION				   */
+  {"DJcy",	0x0402},  /* CYRILLIC CAPITAL LETTER DJE		   */
+  {"DScy",	0x0405},  /* CYRILLIC CAPITAL LETTER DZE		   */
+  {"DZcy",	0x040F},  /* CYRILLIC CAPITAL LETTER DZHE		   */
+  {"Dagger",	0x2021},  /* DOUBLE DAGGER				   */
+  {"Dcaron",	0x010E},  /* LATIN CAPITAL LETTER D WITH CARON		   */
+  {"Dcy",	0x0414},  /* CYRILLIC CAPITAL LETTER DE 		   */
+  {"Delta",	0x0394},  /* GREEK CAPITAL LETTER DELTA 		   */
+  {"Dgr",	0x0394},  /* GREEK CAPITAL LETTER DELTA 		   */
+  {"Dot",	0x00A8},  /* DIAERESIS					   */
+  {"DotDot",	0x20DC},  /* COMBINING FOUR DOTS ABOVE			   */
+  {"Dstrok",	0x0110},  /* LATIN CAPITAL LETTER D WITH STROKE 	   */
+  {"EEacgr",	0x0389},  /* GREEK CAPITAL LETTER ETA WITH TONOS	   */
+  {"EEgr",	0x0397},  /* GREEK CAPITAL LETTER ETA			   */
+  {"ENG",	0x014A},  /* LATIN CAPITAL LETTER ENG			   */
+  {"ETH",	0x00D0},  /* LATIN CAPITAL LETTER ETH			   */
+  {"Eacgr",	0x0388},  /* GREEK CAPITAL LETTER EPSILON WITH TONOS	   */
+  {"Eacute",	0x00C9},  /* LATIN CAPITAL LETTER E WITH ACUTE		   */
+  {"Ecaron",	0x011A},  /* LATIN CAPITAL LETTER E WITH CARON		   */
+  {"Ecirc",	0x00CA},  /* LATIN CAPITAL LETTER E WITH CIRCUMFLEX	   */
+  {"Ecy",	0x042D},  /* CYRILLIC CAPITAL LETTER E			   */
+  {"Edot",	0x0116},  /* LATIN CAPITAL LETTER E WITH DOT ABOVE	   */
+  {"Egr",	0x0395},  /* GREEK CAPITAL LETTER EPSILON		   */
+  {"Egrave",	0x00C8},  /* LATIN CAPITAL LETTER E WITH GRAVE		   */
+  {"Emacr",	0x0112},  /* LATIN CAPITAL LETTER E WITH MACRON 	   */
+  {"Eogon",	0x0118},  /* LATIN CAPITAL LETTER E WITH OGONEK 	   */
+  {"Epsilon",	0x0395},  /* GREEK CAPITAL LETTER EPSILON		   */
+  {"Eta",	0x0397},  /* GREEK CAPITAL LETTER ETA			   */
+  {"Euml",	0x00CB},  /* LATIN CAPITAL LETTER E WITH DIAERESIS	   */
+  {"Fcy",	0x0424},  /* CYRILLIC CAPITAL LETTER EF 		   */
+  {"GJcy",	0x0403},  /* CYRILLIC CAPITAL LETTER GJE		   */
+  {"Gamma",	0x0393},  /* GREEK CAPITAL LETTER GAMMA 		   */
+  {"Gbreve",	0x011E},  /* LATIN CAPITAL LETTER G WITH BREVE		   */
+  {"Gcedil",	0x0122},  /* LATIN CAPITAL LETTER G WITH CEDILLA	   */
+  {"Gcirc",	0x011C},  /* LATIN CAPITAL LETTER G WITH CIRCUMFLEX	   */
+  {"Gcy",	0x0413},  /* CYRILLIC CAPITAL LETTER GHE		   */
+  {"Gdot",	0x0120},  /* LATIN CAPITAL LETTER G WITH DOT ABOVE	   */
+  {"Gg",	0x22D9},  /* VERY MUCH GREATER-THAN			   */
+  {"Ggr",	0x0393},  /* GREEK CAPITAL LETTER GAMMA 		   */
+  {"Gt",	0x226B},  /* MUCH GREATER-THAN				   */
+  {"HARDcy",	0x042A},  /* CYRILLIC CAPITAL LETTER HARD SIGN		   */
+  {"Hcirc",	0x0124},  /* LATIN CAPITAL LETTER H WITH CIRCUMFLEX	   */
+  {"Hstrok",	0x0126},  /* LATIN CAPITAL LETTER H WITH STROKE 	   */
+  {"IEcy",	0x0415},  /* CYRILLIC CAPITAL LETTER IE 		   */
+  {"IJlig",	0x0132},  /* LATIN CAPITAL LIGATURE IJ			   */
+  {"IOcy",	0x0401},  /* CYRILLIC CAPITAL LETTER IO 		   */
+  {"Iacgr",	0x038A},  /* GREEK CAPITAL LETTER IOTA WITH TONOS	   */
+  {"Iacute",	0x00CD},  /* LATIN CAPITAL LETTER I WITH ACUTE		   */
+  {"Icirc",	0x00CE},  /* LATIN CAPITAL LETTER I WITH CIRCUMFLEX	   */
+  {"Icy",	0x0418},  /* CYRILLIC CAPITAL LETTER I			   */
+  {"Idigr",	0x03AA},  /* GREEK CAPITAL LETTER IOTA WITH DIALYTIKA	   */
+  {"Idot",	0x0130},  /* LATIN CAPITAL LETTER I WITH DOT ABOVE	   */
+  {"Igr",	0x0399},  /* GREEK CAPITAL LETTER IOTA			   */
+  {"Igrave",	0x00CC},  /* LATIN CAPITAL LETTER I WITH GRAVE		   */
+  {"Imacr",	0x012A},  /* LATIN CAPITAL LETTER I WITH MACRON 	   */
+  {"Iogon",	0x012E},  /* LATIN CAPITAL LETTER I WITH OGONEK 	   */
+  {"Iota",	0x0399},  /* GREEK CAPITAL LETTER IOTA			   */
+  {"Itilde",	0x0128},  /* LATIN CAPITAL LETTER I WITH TILDE		   */
+  {"Iukcy",	0x0406},  /* CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN*/
+  {"Iuml",	0x00CF},  /* LATIN CAPITAL LETTER I WITH DIAERESIS	   */
+  {"Jcirc",	0x0134},  /* LATIN CAPITAL LETTER J WITH CIRCUMFLEX	   */
+  {"Jcy",	0x0419},  /* CYRILLIC CAPITAL LETTER SHORT I		   */
+  {"Jsercy",	0x0408},  /* CYRILLIC CAPITAL LETTER JE 		   */
+  {"Jukcy",	0x0404},  /* CYRILLIC CAPITAL LETTER UKRAINIAN IE	   */
+  {"KHcy",	0x0425},  /* CYRILLIC CAPITAL LETTER HA 		   */
+  {"KHgr",	0x03A7},  /* GREEK CAPITAL LETTER CHI			   */
+  {"KJcy",	0x040C},  /* CYRILLIC CAPITAL LETTER KJE		   */
+  {"Kappa",	0x039A},  /* GREEK CAPITAL LETTER KAPPA 		   */
+  {"Kcedil",	0x0136},  /* LATIN CAPITAL LETTER K WITH CEDILLA	   */
+  {"Kcy",	0x041A},  /* CYRILLIC CAPITAL LETTER KA 		   */
+  {"Kgr",	0x039A},  /* GREEK CAPITAL LETTER KAPPA 		   */
+  {"LJcy",	0x0409},  /* CYRILLIC CAPITAL LETTER LJE		   */
+  {"Lacute",	0x0139},  /* LATIN CAPITAL LETTER L WITH ACUTE		   */
+  {"Lambda",	0x039B},  /* GREEK CAPITAL LETTER LAMDA 		   */
+  {"Larr",	0x219E},  /* LEFTWARDS TWO HEADED ARROW 		   */
+  {"Lcaron",	0x013D},  /* LATIN CAPITAL LETTER L WITH CARON		   */
+  {"Lcedil",	0x013B},  /* LATIN CAPITAL LETTER L WITH CEDILLA	   */
+  {"Lcy",	0x041B},  /* CYRILLIC CAPITAL LETTER EL 		   */
+  {"Lgr",	0x039B},  /* GREEK CAPITAL LETTER LAMDA 		   */
+  {"Ll",	0x22D8},  /* VERY MUCH LESS-THAN			   */
+  {"Lmidot",	0x013F},  /* LATIN CAPITAL LETTER L WITH MIDDLE DOT	   */
+  {"Lstrok",	0x0141},  /* LATIN CAPITAL LETTER L WITH STROKE 	   */
+  {"Lt",	0x226A},  /* MUCH LESS-THAN				   */
+  {"Mcy",	0x041C},  /* CYRILLIC CAPITAL LETTER EM 		   */
+  {"Mgr",	0x039C},  /* GREEK CAPITAL LETTER MU			   */
+  {"Mu",	0x039C},  /* GREEK CAPITAL LETTER MU			   */
+  {"NJcy",	0x040A},  /* CYRILLIC CAPITAL LETTER NJE		   */
+  {"Nacute",	0x0143},  /* LATIN CAPITAL LETTER N WITH ACUTE		   */
+  {"Ncaron",	0x0147},  /* LATIN CAPITAL LETTER N WITH CARON		   */
+  {"Ncedil",	0x0145},  /* LATIN CAPITAL LETTER N WITH CEDILLA	   */
+  {"Ncy",	0x041D},  /* CYRILLIC CAPITAL LETTER EN 		   */
+  {"Ngr",	0x039D},  /* GREEK CAPITAL LETTER NU			   */
+  {"Ntilde",	0x00D1},  /* LATIN CAPITAL LETTER N WITH TILDE		   */
+  {"Nu",	0x039D},  /* GREEK CAPITAL LETTER NU			   */
+  {"OElig",	0x0152},  /* LATIN CAPITAL LIGATURE OE			   */
+  {"OHacgr",	0x038F},  /* GREEK CAPITAL LETTER OMEGA WITH TONOS	   */
+  {"OHgr",	0x03A9},  /* GREEK CAPITAL LETTER OMEGA 		   */
+  {"Oacgr",	0x038C},  /* GREEK CAPITAL LETTER OMICRON WITH TONOS	   */
+  {"Oacute",	0x00D3},  /* LATIN CAPITAL LETTER O WITH ACUTE		   */
+  {"Ocirc",	0x00D4},  /* LATIN CAPITAL LETTER O WITH CIRCUMFLEX	   */
+  {"Ocy",	0x041E},  /* CYRILLIC CAPITAL LETTER O			   */
+  {"Odblac",	0x0150},  /* LATIN CAPITAL LETTER O WITH DOUBLE ACUTE	   */
+  {"Ogr",	0x039F},  /* GREEK CAPITAL LETTER OMICRON		   */
+  {"Ograve",	0x00D2},  /* LATIN CAPITAL LETTER O WITH GRAVE		   */
+  {"Omacr",	0x014C},  /* LATIN CAPITAL LETTER O WITH MACRON 	   */
+  {"Omega",	0x03A9},  /* GREEK CAPITAL LETTER OMEGA 		   */
+  {"Omicron",	0x039F},  /* GREEK CAPITAL LETTER OMICRON		   */
+  {"Oslash",	0x00D8},  /* LATIN CAPITAL LETTER O WITH STROKE 	   */
+  {"Otilde",	0x00D5},  /* LATIN CAPITAL LETTER O WITH TILDE		   */
+  {"Ouml",	0x00D6},  /* LATIN CAPITAL LETTER O WITH DIAERESIS	   */
+  {"PHgr",	0x03A6},  /* GREEK CAPITAL LETTER PHI			   */
+  {"PSgr",	0x03A8},  /* GREEK CAPITAL LETTER PSI			   */
+  {"Pcy",	0x041F},  /* CYRILLIC CAPITAL LETTER PE 		   */
+  {"Pgr",	0x03A0},  /* GREEK CAPITAL LETTER PI			   */
+  {"Phi",	0x03A6},  /* GREEK CAPITAL LETTER PHI			   */
+  {"Pi",	0x03A0},  /* GREEK CAPITAL LETTER PI			   */
+  {"Prime",	0x2033},  /* DOUBLE PRIME				   */
+  {"Psi",	0x03A8},  /* GREEK CAPITAL LETTER PSI			   */
+  {"Racute",	0x0154},  /* LATIN CAPITAL LETTER R WITH ACUTE		   */
+  {"Rarr",	0x21A0},  /* RIGHTWARDS TWO HEADED ARROW		   */
+  {"Rcaron",	0x0158},  /* LATIN CAPITAL LETTER R WITH CARON		   */
+  {"Rcedil",	0x0156},  /* LATIN CAPITAL LETTER R WITH CEDILLA	   */
+  {"Rcy",	0x0420},  /* CYRILLIC CAPITAL LETTER ER 		   */
+  {"Rgr",	0x03A1},  /* GREEK CAPITAL LETTER RHO			   */
+  {"Rho",	0x03A1},  /* GREEK CAPITAL LETTER RHO			   */
+  {"SHCHcy",	0x0429},  /* CYRILLIC CAPITAL LETTER SHCHA		   */
+  {"SHcy",	0x0428},  /* CYRILLIC CAPITAL LETTER SHA		   */
+  {"SOFTcy",	0x042C},  /* CYRILLIC CAPITAL LETTER SOFT SIGN		   */
+  {"Sacute",	0x015A},  /* LATIN CAPITAL LETTER S WITH ACUTE		   */
+  {"Scaron",	0x0160},  /* LATIN CAPITAL LETTER S WITH CARON		   */
+  {"Scedil",	0x015E},  /* LATIN CAPITAL LETTER S WITH CEDILLA	   */
+  {"Scirc",	0x015C},  /* LATIN CAPITAL LETTER S WITH CIRCUMFLEX	   */
+  {"Scy",	0x0421},  /* CYRILLIC CAPITAL LETTER ES 		   */
+  {"Sgr",	0x03A3},  /* GREEK CAPITAL LETTER SIGMA 		   */
+  {"Sigma",	0x03A3},  /* GREEK CAPITAL LETTER SIGMA 		   */
+  {"Sub",	0x22D0},  /* DOUBLE SUBSET				   */
+  {"Sup",	0x22D1},  /* DOUBLE SUPERSET				   */
+  {"THORN",	0x00DE},  /* LATIN CAPITAL LETTER THORN 		   */
+  {"THgr",	0x0398},  /* GREEK CAPITAL LETTER THETA 		   */
+  {"TSHcy",	0x040B},  /* CYRILLIC CAPITAL LETTER TSHE		   */
+  {"TScy",	0x0426},  /* CYRILLIC CAPITAL LETTER TSE		   */
+  {"Tau",	0x03A4},  /* GREEK CAPITAL LETTER TAU			   */
+  {"Tcaron",	0x0164},  /* LATIN CAPITAL LETTER T WITH CARON		   */
+  {"Tcedil",	0x0162},  /* LATIN CAPITAL LETTER T WITH CEDILLA	   */
+  {"Tcy",	0x0422},  /* CYRILLIC CAPITAL LETTER TE 		   */
+  {"Tgr",	0x03A4},  /* GREEK CAPITAL LETTER TAU			   */
+  {"Theta",	0x0398},  /* GREEK CAPITAL LETTER THETA 		   */
+  {"Tstrok",	0x0166},  /* LATIN CAPITAL LETTER T WITH STROKE 	   */
+  {"Uacgr",	0x038E},  /* GREEK CAPITAL LETTER UPSILON WITH TONOS	   */
+  {"Uacute",	0x00DA},  /* LATIN CAPITAL LETTER U WITH ACUTE		   */
+  {"Ubrcy",	0x040E},  /* CYRILLIC CAPITAL LETTER SHORT U		   */
+  {"Ubreve",	0x016C},  /* LATIN CAPITAL LETTER U WITH BREVE		   */
+  {"Ucirc",	0x00DB},  /* LATIN CAPITAL LETTER U WITH CIRCUMFLEX	   */
+  {"Ucy",	0x0423},  /* CYRILLIC CAPITAL LETTER U			   */
+  {"Udblac",	0x0170},  /* LATIN CAPITAL LETTER U WITH DOUBLE ACUTE	   */
+  {"Udigr",	0x03AB},  /* GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA   */
+  {"Ugr",	0x03A5},  /* GREEK CAPITAL LETTER UPSILON		   */
+  {"Ugrave",	0x00D9},  /* LATIN CAPITAL LETTER U WITH GRAVE		   */
+  {"Umacr",	0x016A},  /* LATIN CAPITAL LETTER U WITH MACRON 	   */
+  {"Uogon",	0x0172},  /* LATIN CAPITAL LETTER U WITH OGONEK 	   */
+  {"Upsi",	0x03A5},  /* GREEK CAPITAL LETTER UPSILON		   */
+  {"Upsilon",	0x03A5},  /* GREEK CAPITAL LETTER UPSILON		   */
+  {"Uring",	0x016E},  /* LATIN CAPITAL LETTER U WITH RING ABOVE	   */
+  {"Utilde",	0x0168},  /* LATIN CAPITAL LETTER U WITH TILDE		   */
+  {"Uuml",	0x00DC},  /* LATIN CAPITAL LETTER U WITH DIAERESIS	   */
+  {"Vcy",	0x0412},  /* CYRILLIC CAPITAL LETTER VE 		   */
+  {"Vdash",	0x22A9},  /* FORCES					   */
+  {"Verbar",	0x2016},  /* DOUBLE VERTICAL LINE			   */
+  {"Vvdash",	0x22AA},  /* TRIPLE VERTICAL BAR RIGHT TURNSTILE	   */
+  {"Wcirc",	0x0174},  /* LATIN CAPITAL LETTER W WITH CIRCUMFLEX	   */
+  {"Xgr",	0x039E},  /* GREEK CAPITAL LETTER XI			   */
+  {"Xi",	0x039E},  /* GREEK CAPITAL LETTER XI			   */
+  {"YAcy",	0x042F},  /* CYRILLIC CAPITAL LETTER YA 		   */
+  {"YIcy",	0x0407},  /* CYRILLIC CAPITAL LETTER YI 		   */
+  {"YUcy",	0x042E},  /* CYRILLIC CAPITAL LETTER YU 		   */
+  {"Yacute",	0x00DD},  /* LATIN CAPITAL LETTER Y WITH ACUTE		   */
+  {"Ycirc",	0x0176},  /* LATIN CAPITAL LETTER Y WITH CIRCUMFLEX	   */
+  {"Ycy",	0x042B},  /* CYRILLIC CAPITAL LETTER YERU		   */
+  {"Yuml",	0x0178},  /* LATIN CAPITAL LETTER Y WITH DIAERESIS	   */
+  {"ZHcy",	0x0416},  /* CYRILLIC CAPITAL LETTER ZHE		   */
+  {"Zacute",	0x0179},  /* LATIN CAPITAL LETTER Z WITH ACUTE		   */
+  {"Zcaron",	0x017D},  /* LATIN CAPITAL LETTER Z WITH CARON		   */
+  {"Zcy",	0x0417},  /* CYRILLIC CAPITAL LETTER ZE 		   */
+  {"Zdot",	0x017B},  /* LATIN CAPITAL LETTER Z WITH DOT ABOVE	   */
+  {"Zeta",	0x0396},  /* GREEK CAPITAL LETTER ZETA			   */
+  {"Zgr",	0x0396},  /* GREEK CAPITAL LETTER ZETA			   */
+  {"aacgr",	0x03AC},  /* GREEK SMALL LETTER ALPHA WITH TONOS	   */
+  {"aacute",	0x00E1},  /* LATIN SMALL LETTER A WITH ACUTE		   */
+  {"abreve",	0x0103},  /* LATIN SMALL LETTER A WITH BREVE		   */
+  {"acirc",	0x00E2},  /* LATIN SMALL LETTER A WITH CIRCUMFLEX	   */
+  {"acute",	0x00B4},  /* ACUTE ACCENT				   */
+  {"acy",	0x0430},  /* CYRILLIC SMALL LETTER A			   */
+  {"aelig",	0x00E6},  /* LATIN SMALL LETTER AE			   */
+  {"agr",	0x03B1},  /* GREEK SMALL LETTER ALPHA			   */
+  {"agrave",	0x00E0},  /* LATIN SMALL LETTER A WITH GRAVE		   */
+  {"alefsym",	0x2135},  /* ALEF SYMBOL				   */
+  {"aleph",	0x2135},  /* ALEF SYMBOL				   */
+  {"alpha",	0x03B1},  /* GREEK SMALL LETTER ALPHA			   */
+  {"amacr",	0x0101},  /* LATIN SMALL LETTER A WITH MACRON		   */
+  {"amalg",	0x2210},  /* N-ARY COPRODUCT				   */
+  {"amp",	0x0026},  /* AMPERSAND					   */
+  {"and",	0x2227},  /* LOGICAL AND				   */
+  {"ang",	0x2220},  /* ANGLE					   */
+  {"ang90",	0x221F},  /* RIGHT ANGLE				   */
+  {"angmsd",	0x2221},  /* MEASURED ANGLE				   */
+  {"angsph",	0x2222},  /* SPHERICAL ANGLE				   */
+  {"angst",	0x212B},  /* ANGSTROM SIGN				   */
+  {"aogon",	0x0105},  /* LATIN SMALL LETTER A WITH OGONEK		   */
+  {"ap",	0x2248},  /* ALMOST EQUAL TO				   */
+  {"ape",	0x224A},  /* ALMOST EQUAL OR EQUAL TO			   */
+  {"apos",	0x02BC},  /* MODIFIER LETTER APOSTROPHE 		   */
+  {"aring",	0x00E5},  /* LATIN SMALL LETTER A WITH RING ABOVE	   */
+  {"ast",	0x002A},  /* ASTERISK					   */
+  {"asymp",	0x2248},  /* ALMOST EQUAL TO				   */
+  {"atilde",	0x00E3},  /* LATIN SMALL LETTER A WITH TILDE		   */
+  {"auml",	0x00E4},  /* LATIN SMALL LETTER A WITH DIAERESIS	   */
+  {"b.Delta",	0x0394},  /* GREEK CAPITAL LETTER DELTA 		   */
+  {"b.Gamma",	0x0393},  /* GREEK CAPITAL LETTER GAMMA 		   */
+  {"b.Lambda",	0x039B},  /* GREEK CAPITAL LETTER LAMDA 		   */
+  {"b.Omega",	0x03A9},  /* GREEK CAPITAL LETTER OMEGA 		   */
+  {"b.Phi",	0x03A6},  /* GREEK CAPITAL LETTER PHI			   */
+  {"b.Pi",	0x03A0},  /* GREEK CAPITAL LETTER PI			   */
+  {"b.Psi",	0x03A8},  /* GREEK CAPITAL LETTER PSI			   */
+  {"b.Sigma",	0x03A3},  /* GREEK CAPITAL LETTER SIGMA 		   */
+  {"b.Theta",	0x0398},  /* GREEK CAPITAL LETTER THETA 		   */
+  {"b.Upsi",	0x03A5},  /* GREEK CAPITAL LETTER UPSILON		   */
+  {"b.Xi",	0x039E},  /* GREEK CAPITAL LETTER XI			   */
+  {"b.alpha",	0x03B1},  /* GREEK SMALL LETTER ALPHA			   */
+  {"b.beta",	0x03B2},  /* GREEK SMALL LETTER BETA			   */
+  {"b.chi",	0x03C7},  /* GREEK SMALL LETTER CHI			   */
+  {"b.delta",	0x03B4},  /* GREEK SMALL LETTER DELTA			   */
+  {"b.epsi",	0x03B5},  /* GREEK SMALL LETTER EPSILON 		   */
+  {"b.epsis",	0x03B5},  /* GREEK SMALL LETTER EPSILON 		   */
+  {"b.epsiv",	0x03B5},  /* GREEK SMALL LETTER EPSILON 		   */
+  {"b.eta",	0x03B7},  /* GREEK SMALL LETTER ETA			   */
+  {"b.gamma",	0x03B3},  /* GREEK SMALL LETTER GAMMA			   */
+  {"b.gammad",	0x03DC},  /* GREEK LETTER DIGAMMA			   */
+  {"b.iota",	0x03B9},  /* GREEK SMALL LETTER IOTA			   */
+  {"b.kappa",	0x03BA},  /* GREEK SMALL LETTER KAPPA			   */
+  {"b.kappav",	0x03F0},  /* GREEK KAPPA SYMBOL 			   */
+  {"b.lambda",	0x03BB},  /* GREEK SMALL LETTER LAMDA			   */
+  {"b.mu",	0x03BC},  /* GREEK SMALL LETTER MU			   */
+  {"b.nu",	0x03BD},  /* GREEK SMALL LETTER NU			   */
+  {"b.omega",	0x03CE},  /* GREEK SMALL LETTER OMEGA WITH TONOS	   */
+  {"b.phis",	0x03C6},  /* GREEK SMALL LETTER PHI			   */
+  {"b.phiv",	0x03D5},  /* GREEK PHI SYMBOL				   */
+  {"b.pi",	0x03C0},  /* GREEK SMALL LETTER PI			   */
+  {"b.piv",	0x03D6},  /* GREEK PI SYMBOL				   */
+  {"b.psi",	0x03C8},  /* GREEK SMALL LETTER PSI			   */
+  {"b.rho",	0x03C1},  /* GREEK SMALL LETTER RHO			   */
+  {"b.rhov",	0x03F1},  /* GREEK RHO SYMBOL				   */
+  {"b.sigma",	0x03C3},  /* GREEK SMALL LETTER SIGMA			   */
+  {"b.sigmav",	0x03C2},  /* GREEK SMALL LETTER FINAL SIGMA		   */
+  {"b.tau",	0x03C4},  /* GREEK SMALL LETTER TAU			   */
+  {"b.thetas",	0x03B8},  /* GREEK SMALL LETTER THETA			   */
+  {"b.thetav",	0x03D1},  /* GREEK THETA SYMBOL 			   */
+  {"b.upsi",	0x03C5},  /* GREEK SMALL LETTER UPSILON 		   */
+  {"b.xi",	0x03BE},  /* GREEK SMALL LETTER XI			   */
+  {"b.zeta",	0x03B6},  /* GREEK SMALL LETTER ZETA			   */
+  {"barwed",	0x22BC},  /* NAND					   */
+  {"bcong",	0x224C},  /* ALL EQUAL TO				   */
+  {"bcy",	0x0431},  /* CYRILLIC SMALL LETTER BE			   */
+  {"bdquo",	0x201E},  /* DOUBLE LOW-9 QUOTATION MARK		   */
+  {"becaus",	0x2235},  /* BECAUSE					   */
+  {"bepsi",	0x220D},  /* SMALL CONTAINS AS MEMBER			   */
+  {"bernou",	0x212C},  /* SCRIPT CAPITAL B				   */
+  {"beta",	0x03B2},  /* GREEK SMALL LETTER BETA			   */
+  {"beth",	0x2136},  /* BET SYMBOL 				   */
+  {"bgr",	0x03B2},  /* GREEK SMALL LETTER BETA			   */
+  {"blank",	0x2423},  /* OPEN BOX					   */
+  {"blk12",	0x2592},  /* MEDIUM SHADE				   */
+  {"blk14",	0x2591},  /* LIGHT SHADE				   */
+  {"blk34",	0x2593},  /* DARK SHADE 				   */
+  {"block",	0x2588},  /* FULL BLOCK 				   */
+  {"bottom",	0x22A5},  /* UP TACK					   */
+  {"bowtie",	0x22C8},  /* BOWTIE					   */
+  {"boxDL",	0x2557},  /* BOX DRAWINGS DOUBLE DOWN AND LEFT		   */
+  {"boxDR",	0x2554},  /* BOX DRAWINGS DOUBLE DOWN AND RIGHT 	   */
+  {"boxDl",	0x2556},  /* BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE	   */
+  {"boxDr",	0x2553},  /* BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE	   */
+  {"boxH",	0x2550},  /* BOX DRAWINGS DOUBLE HORIZONTAL		   */
+  {"boxHD",	0x2566},  /* BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL	   */
+  {"boxHU",	0x2569},  /* BOX DRAWINGS DOUBLE UP AND HORIZONTAL	   */
+  {"boxHd",	0x2564},  /* BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE*/
+  {"boxHu",	0x2567},  /* BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE  */
+  {"boxUL",	0x255D},  /* BOX DRAWINGS DOUBLE UP AND LEFT		   */
+  {"boxUR",	0x255A},  /* BOX DRAWINGS DOUBLE UP AND RIGHT		   */
+  {"boxUl",	0x255C},  /* BOX DRAWINGS UP DOUBLE AND LEFT SINGLE	   */
+  {"boxUr",	0x2559},  /* BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE	   */
+  {"boxV",	0x2551},  /* BOX DRAWINGS DOUBLE VERTICAL		   */
+  {"boxVH",	0x256C},  /* BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL   */
+  {"boxVL",	0x2563},  /* BOX DRAWINGS DOUBLE VERTICAL AND LEFT	   */
+  {"boxVR",	0x2560},  /* BOX DRAWINGS DOUBLE VERTICAL AND RIGHT	   */
+  {"boxVh",	0x256B},  /* BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SI*/
+  {"boxVl",	0x2562},  /* BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE  */
+  {"boxVr",	0x255F},  /* BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE */
+  {"boxdL",	0x2555},  /* BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE	   */
+  {"boxdR",	0x2552},  /* BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE	   */
+  {"boxdl",	0x2510},  /* BOX DRAWINGS LIGHT DOWN AND LEFT		   */
+  {"boxdr",	0x250C},  /* BOX DRAWINGS LIGHT DOWN AND RIGHT		   */
+  {"boxh",	0x2500},  /* BOX DRAWINGS LIGHT HORIZONTAL		   */
+  {"boxhD",	0x2565},  /* BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE*/
+  {"boxhU",	0x2568},  /* BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE  */
+  {"boxhd",	0x252C},  /* BOX DRAWINGS LIGHT DOWN AND HORIZONTAL	   */
+  {"boxhu",	0x2534},  /* BOX DRAWINGS LIGHT UP AND HORIZONTAL	   */
+  {"boxuL",	0x255B},  /* BOX DRAWINGS UP SINGLE AND LEFT DOUBLE	   */
+  {"boxuR",	0x2558},  /* BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE	   */
+  {"boxul",	0x2518},  /* BOX DRAWINGS LIGHT UP AND LEFT		   */
+  {"boxur",	0x2514},  /* BOX DRAWINGS LIGHT UP AND RIGHT		   */
+  {"boxv",	0x2502},  /* BOX DRAWINGS LIGHT VERTICAL		   */
+  {"boxvH",	0x256A},  /* BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DO*/
+  {"boxvL",	0x2561},  /* BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE  */
+  {"boxvR",	0x255E},  /* BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE */
+  {"boxvh",	0x253C},  /* BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL    */
+  {"boxvl",	0x2524},  /* BOX DRAWINGS LIGHT VERTICAL AND LEFT	   */
+  {"boxvr",	0x251C},  /* BOX DRAWINGS LIGHT VERTICAL AND RIGHT	   */
+  {"bprime",	0x2035},  /* REVERSED PRIME				   */
+  {"breve",	0x02D8},  /* BREVE					   */
+  {"brkbar",	0x00A6},  /* obsolete synonym for "brvbar" 0x00A6	   */
+  {"brvbar",	0x00A6},  /* BROKEN BAR 				   */
+  {"bsim",	0x223D},  /* REVERSED TILDE				   */
+  {"bsime",	0x22CD},  /* REVERSED TILDE EQUALS			   */
+  {"bsol",	0x005C},  /* REVERSE SOLIDUS				   */
+  {"bull",	0x2022},  /* BULLET					   */
+  {"bump",	0x224E},  /* GEOMETRICALLY EQUIVALENT TO		   */
+  {"bumpe",	0x224F},  /* DIFFERENCE BETWEEN 			   */
+  {"cacute",	0x0107},  /* LATIN SMALL LETTER C WITH ACUTE		   */
+  {"cap",	0x2229},  /* INTERSECTION				   */
+  {"caret",	0x2041},  /* CARET INSERTION POINT			   */
+  {"caron",	0x02C7},  /* CARON					   */
+  {"ccaron",	0x010D},  /* LATIN SMALL LETTER C WITH CARON		   */
+  {"ccedil",	0x00E7},  /* LATIN SMALL LETTER C WITH CEDILLA		   */
+  {"ccirc",	0x0109},  /* LATIN SMALL LETTER C WITH CIRCUMFLEX	   */
+  {"cdot",	0x010B},  /* LATIN SMALL LETTER C WITH DOT ABOVE	   */
+  {"cedil",	0x00B8},  /* CEDILLA					   */
+  {"cent",	0x00A2},  /* CENT SIGN					   */
+  {"chcy",	0x0447},  /* CYRILLIC SMALL LETTER CHE			   */
+  {"check",	0x2713},  /* CHECK MARK 				   */
+  {"chi",	0x03C7},  /* GREEK SMALL LETTER CHI			   */
+  {"cir",	0x25CB},  /* WHITE CIRCLE				   */
+  {"circ",	0x02C6},  /* MODIFIER LETTER CIRCUMFLEX ACCENT		   */
+  {"cire",	0x2257},  /* RING EQUAL TO				   */
+  {"clubs",	0x2663},  /* BLACK CLUB SUIT				   */
+  {"colon",	0x003A},  /* COLON					   */
+  {"colone",	0x2254},  /* COLON EQUALS				   */
+  {"comma",	0x002C},  /* COMMA					   */
+  {"commat",	0x0040},  /* COMMERCIAL AT				   */
+  {"comp",	0x2201},  /* COMPLEMENT 				   */
+  {"compfn",	0x2218},  /* RING OPERATOR				   */
+  {"cong",	0x2245},  /* APPROXIMATELY EQUAL TO			   */
+  {"conint",	0x222E},  /* CONTOUR INTEGRAL				   */
+  {"coprod",	0x2210},  /* N-ARY COPRODUCT				   */
+  {"copy",	0x00A9},  /* COPYRIGHT SIGN				   */
+  {"copysr",	0x2117},  /* SOUND RECORDING COPYRIGHT			   */
+  {"crarr",	0x21B5},  /* DOWNWARDS ARROW WITH CORNER LEFTWARDS	   */
+  {"cross",	0x2717},  /* BALLOT X					   */
+  {"cuepr",	0x22DE},  /* EQUAL TO OR PRECEDES			   */
+  {"cuesc",	0x22DF},  /* EQUAL TO OR SUCCEEDS			   */
+  {"cularr",	0x21B6},  /* ANTICLOCKWISE TOP SEMICIRCLE ARROW 	   */
+  {"cup",	0x222A},  /* UNION					   */
+  {"cupre",	0x227C},  /* PRECEDES OR EQUAL TO			   */
+  {"curarr",	0x21B7},  /* CLOCKWISE TOP SEMICIRCLE ARROW		   */
+  {"curren",	0x00A4},  /* CURRENCY SIGN				   */
+  {"cuvee",	0x22CE},  /* CURLY LOGICAL OR				   */
+  {"cuwed",	0x22CF},  /* CURLY LOGICAL AND				   */
+  {"dArr",	0x21D3},  /* DOWNWARDS DOUBLE ARROW			   */
+  {"dagger",	0x2020},  /* DAGGER					   */
+  {"daleth",	0x2138},  /* DALET SYMBOL				   */
+  {"darr",	0x2193},  /* DOWNWARDS ARROW				   */
+  {"darr2",	0x21CA},  /* DOWNWARDS PAIRED ARROWS			   */
+  {"dash",	0x2010},  /* HYPHEN					   */
+  {"dashv",	0x22A3},  /* LEFT TACK					   */
+  {"dblac",	0x02DD},  /* DOUBLE ACUTE ACCENT			   */
+  {"dcaron",	0x010F},  /* LATIN SMALL LETTER D WITH CARON		   */
+  {"dcy",	0x0434},  /* CYRILLIC SMALL LETTER DE			   */
+  {"deg",	0x00B0},  /* DEGREE SIGN				   */
+  {"delta",	0x03B4},  /* GREEK SMALL LETTER DELTA			   */
+  {"dgr",	0x03B4},  /* GREEK SMALL LETTER DELTA			   */
+  {"dharl",	0x21C3},  /* DOWNWARDS HARPOON WITH BARB LEFTWARDS	   */
+  {"dharr",	0x21C2},  /* DOWNWARDS HARPOON WITH BARB RIGHTWARDS	   */
+  {"diam",	0x22C4},  /* DIAMOND OPERATOR				   */
+  {"diams",	0x2666},  /* BLACK DIAMOND SUIT 			   */
+  {"die",	0x00A8},  /* DIAERESIS					   */
+  {"divide",	0x00F7},  /* DIVISION SIGN				   */
+  {"divonx",	0x22C7},  /* DIVISION TIMES				   */
+  {"djcy",	0x0452},  /* CYRILLIC SMALL LETTER DJE			   */
+  {"dlarr",	0x2199},  /* SOUTH WEST ARROW				   */
+  {"dlcorn",	0x231E},  /* BOTTOM LEFT CORNER 			   */
+  {"dlcrop",	0x230D},  /* BOTTOM LEFT CROP				   */
+  {"dollar",	0x0024},  /* DOLLAR SIGN				   */
+  {"dot",	0x02D9},  /* DOT ABOVE					   */
+  {"drarr",	0x2198},  /* SOUTH EAST ARROW				   */
+  {"drcorn",	0x231F},  /* BOTTOM RIGHT CORNER			   */
+  {"drcrop",	0x230C},  /* BOTTOM RIGHT CROP				   */
+  {"dscy",	0x0455},  /* CYRILLIC SMALL LETTER DZE			   */
+  {"dstrok",	0x0111},  /* LATIN SMALL LETTER D WITH STROKE		   */
+  {"dtri",	0x25BF},  /* WHITE DOWN-POINTING SMALL TRIANGLE 	   */
+  {"dtrif",	0x25BE},  /* BLACK DOWN-POINTING SMALL TRIANGLE 	   */
+  {"dzcy",	0x045F},  /* CYRILLIC SMALL LETTER DZHE 		   */
+  {"eDot",	0x2251},  /* GEOMETRICALLY EQUAL TO			   */
+  {"eacgr",	0x03AD},  /* GREEK SMALL LETTER EPSILON WITH TONOS	   */
+  {"eacute",	0x00E9},  /* LATIN SMALL LETTER E WITH ACUTE		   */
+  {"ecaron",	0x011B},  /* LATIN SMALL LETTER E WITH CARON		   */
+  {"ecir",	0x2256},  /* RING IN EQUAL TO				   */
+  {"ecirc",	0x00EA},  /* LATIN SMALL LETTER E WITH CIRCUMFLEX	   */
+  {"ecolon",	0x2255},  /* EQUALS COLON				   */
+  {"ecy",	0x044D},  /* CYRILLIC SMALL LETTER E			   */
+  {"edot",	0x0117},  /* LATIN SMALL LETTER E WITH DOT ABOVE	   */
+  {"eeacgr",	0x03AE},  /* GREEK SMALL LETTER ETA WITH TONOS		   */
+  {"eegr",	0x03B7},  /* GREEK SMALL LETTER ETA			   */
+  {"efDot",	0x2252},  /* APPROXIMATELY EQUAL TO OR THE IMAGE OF	   */
+  {"egr",	0x03B5},  /* GREEK SMALL LETTER EPSILON 		   */
+  {"egrave",	0x00E8},  /* LATIN SMALL LETTER E WITH GRAVE		   */
+  {"egs",	0x22DD},  /* EQUAL TO OR GREATER-THAN			   */
+  {"ell",	0x2113},  /* SCRIPT SMALL L				   */
+  {"els",	0x22DC},  /* EQUAL TO OR LESS-THAN			   */
+  {"emacr",	0x0113},  /* LATIN SMALL LETTER E WITH MACRON		   */
+  {"emdash",	0x2014},  /* obsolete synonym for "mdash" 0x2014	   */
+  {"empty",	0x2205},  /* EMPTY SET					   */
+  {"emsp",	0x2003},  /* EM SPACE					   */
+  {"emsp13",	0x2004},  /* THREE-PER-EM SPACE 			   */
+  {"emsp14",	0x2005},  /* FOUR-PER-EM SPACE				   */
+  {"endash",	0x2013},  /* obsolete synonym for "ndash" 0x2013	   */
+  {"eng",	0x014B},  /* LATIN SMALL LETTER ENG			   */
+  {"ensp",	0x2002},  /* EN SPACE					   */
+  {"eogon",	0x0119},  /* LATIN SMALL LETTER E WITH OGONEK		   */
+  {"epsi",	0x03B5},  /* GREEK SMALL LETTER EPSILON 		   */
+  {"epsilon",	0x03B5},  /* GREEK SMALL LETTER EPSILON 		   */
+  {"epsis",	0x220A},  /* SMALL ELEMENT OF				   */
+  {"equals",	0x003D},  /* EQUALS SIGN				   */
+  {"equiv",	0x2261},  /* IDENTICAL TO				   */
+  {"erDot",	0x2253},  /* IMAGE OF OR APPROXIMATELY EQUAL TO 	   */
+  {"esdot",	0x2250},  /* APPROACHES THE LIMIT			   */
+  {"eta",	0x03B7},  /* GREEK SMALL LETTER ETA			   */
+  {"eth",	0x00F0},  /* LATIN SMALL LETTER ETH			   */
+  {"euml",	0x00EB},  /* LATIN SMALL LETTER E WITH DIAERESIS	   */
+  {"euro",	0x20AC},  /* EURO SIGN					   */
+  {"excl",	0x0021},  /* EXCLAMATION MARK				   */
+  {"exist",	0x2203},  /* THERE EXISTS				   */
+  {"fcy",	0x0444},  /* CYRILLIC SMALL LETTER EF			   */
+  {"female",	0x2640},  /* FEMALE SIGN				   */
+  {"ffilig",	0xFB03},  /* LATIN SMALL LIGATURE FFI			   */
+  {"fflig",	0xFB00},  /* LATIN SMALL LIGATURE FF			   */
+  {"ffllig",	0xFB04},  /* LATIN SMALL LIGATURE FFL			   */
+  {"filig",	0xFB01},  /* LATIN SMALL LIGATURE FI			   */
+  {"flat",	0x266D},  /* MUSIC FLAT SIGN				   */
+  {"fllig",	0xFB02},  /* LATIN SMALL LIGATURE FL			   */
+  {"fnof",	0x0192},  /* LATIN SMALL LETTER F WITH HOOK		   */
+  {"forall",	0x2200},  /* FOR ALL					   */
+  {"fork",	0x22D4},  /* PITCHFORK					   */
+  {"frac12",	0x00BD},  /* VULGAR FRACTION ONE HALF			   */
+  {"frac13",	0x2153},  /* VULGAR FRACTION ONE THIRD			   */
+  {"frac14",	0x00BC},  /* VULGAR FRACTION ONE QUARTER		   */
+  {"frac15",	0x2155},  /* VULGAR FRACTION ONE FIFTH			   */
+  {"frac16",	0x2159},  /* VULGAR FRACTION ONE SIXTH			   */
+  {"frac18",	0x215B},  /* VULGAR FRACTION ONE EIGHTH 		   */
+  {"frac23",	0x2154},  /* VULGAR FRACTION TWO THIRDS 		   */
+  {"frac25",	0x2156},  /* VULGAR FRACTION TWO FIFTHS 		   */
+  {"frac34",	0x00BE},  /* VULGAR FRACTION THREE QUARTERS		   */
+  {"frac35",	0x2157},  /* VULGAR FRACTION THREE FIFTHS		   */
+  {"frac38",	0x215C},  /* VULGAR FRACTION THREE EIGHTHS		   */
+  {"frac45",	0x2158},  /* VULGAR FRACTION FOUR FIFTHS		   */
+  {"frac56",	0x215A},  /* VULGAR FRACTION FIVE SIXTHS		   */
+  {"frac58",	0x215D},  /* VULGAR FRACTION FIVE EIGHTHS		   */
+  {"frac78",	0x215E},  /* VULGAR FRACTION SEVEN EIGHTHS		   */
+  {"frasl",	0x2044},  /* FRACTION SLASH				   */
+  {"frown",	0x2322},  /* FROWN					   */
+  {"gE",	0x2267},  /* GREATER-THAN OVER EQUAL TO 		   */
+  {"gacute",	0x01F5},  /* LATIN SMALL LETTER G WITH ACUTE		   */
+  {"gamma",	0x03B3},  /* GREEK SMALL LETTER GAMMA			   */
+  {"gammad",	0x03DC},  /* GREEK LETTER DIGAMMA			   */
+  {"gbreve",	0x011F},  /* LATIN SMALL LETTER G WITH BREVE		   */
+  {"gcedil",	0x0123},  /* LATIN SMALL LETTER G WITH CEDILLA		   */
+  {"gcirc",	0x011D},  /* LATIN SMALL LETTER G WITH CIRCUMFLEX	   */
+  {"gcy",	0x0433},  /* CYRILLIC SMALL LETTER GHE			   */
+  {"gdot",	0x0121},  /* LATIN SMALL LETTER G WITH DOT ABOVE	   */
+  {"ge",	0x2265},  /* GREATER-THAN OR EQUAL TO			   */
+  {"gel",	0x22DB},  /* GREATER-THAN EQUAL TO OR LESS-THAN 	   */
+  {"ges",	0x2265},  /* GREATER-THAN OR EQUAL TO			   */
+  {"ggr",	0x03B3},  /* GREEK SMALL LETTER GAMMA			   */
+  {"gimel",	0x2137},  /* GIMEL SYMBOL				   */
+  {"gjcy",	0x0453},  /* CYRILLIC SMALL LETTER GJE			   */
+  {"gl",	0x2277},  /* GREATER-THAN OR LESS-THAN			   */
+  {"gnE",	0x2269},  /* GREATER-THAN BUT NOT EQUAL TO		   */
+  {"gne",	0x2269},  /* GREATER-THAN BUT NOT EQUAL TO		   */
+  {"gnsim",	0x22E7},  /* GREATER-THAN BUT NOT EQUIVALENT TO 	   */
+  {"grave",	0x0060},  /* GRAVE ACCENT				   */
+  {"gsdot",	0x22D7},  /* GREATER-THAN WITH DOT			   */
+  {"gsim",	0x2273},  /* GREATER-THAN OR EQUIVALENT TO		   */
+  {"gt",	0x003E},  /* GREATER-THAN SIGN				   */
+  {"gvnE",	0x2269},  /* GREATER-THAN BUT NOT EQUAL TO		   */
+  {"hArr",	0x21D4},  /* LEFT RIGHT DOUBLE ARROW			   */
+  {"hairsp",	0x200A},  /* HAIR SPACE 				   */
+  {"half",	0x00BD},  /* VULGAR FRACTION ONE HALF			   */
+  {"hamilt",	0x210B},  /* SCRIPT CAPITAL H				   */
+  {"hardcy",	0x044A},  /* CYRILLIC SMALL LETTER HARD SIGN		   */
+  {"harr",	0x2194},  /* LEFT RIGHT ARROW				   */
+  {"harrw",	0x21AD},  /* LEFT RIGHT WAVE ARROW			   */
+  {"hcirc",	0x0125},  /* LATIN SMALL LETTER H WITH CIRCUMFLEX	   */
+  {"hearts",	0x2665},  /* BLACK HEART SUIT				   */
+  {"hellip",	0x2026},  /* HORIZONTAL ELLIPSIS			   */
+  {"hibar",	0x00AF},  /* obsolete synonym for "macr" 0x00AF 	   */
+  {"horbar",	0x2015},  /* HORIZONTAL BAR				   */
+  {"hstrok",	0x0127},  /* LATIN SMALL LETTER H WITH STROKE		   */
+  {"hybull",	0x2043},  /* HYPHEN BULLET				   */
+  {"hyphen",	0x002D},  /* HYPHEN-MINUS				   */
+  {"iacgr",	0x03AF},  /* GREEK SMALL LETTER IOTA WITH TONOS 	   */
+  {"iacute",	0x00ED},  /* LATIN SMALL LETTER I WITH ACUTE		   */
+  {"icirc",	0x00EE},  /* LATIN SMALL LETTER I WITH CIRCUMFLEX	   */
+  {"icy",	0x0438},  /* CYRILLIC SMALL LETTER I			   */
+  {"idiagr",	0x0390},  /* GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TON*/
+  {"idigr",	0x03CA},  /* GREEK SMALL LETTER IOTA WITH DIALYTIKA	   */
+  {"iecy",	0x0435},  /* CYRILLIC SMALL LETTER IE			   */
+  {"iexcl",	0x00A1},  /* INVERTED EXCLAMATION MARK			   */
+  {"iff",	0x21D4},  /* LEFT RIGHT DOUBLE ARROW			   */
+  {"igr",	0x03B9},  /* GREEK SMALL LETTER IOTA			   */
+  {"igrave",	0x00EC},  /* LATIN SMALL LETTER I WITH GRAVE		   */
+  {"ijlig",	0x0133},  /* LATIN SMALL LIGATURE IJ			   */
+  {"imacr",	0x012B},  /* LATIN SMALL LETTER I WITH MACRON		   */
+  {"image",	0x2111},  /* BLACK-LETTER CAPITAL I			   */
+  {"incare",	0x2105},  /* CARE OF					   */
+  {"infin",	0x221E},  /* INFINITY					   */
+  {"inodot",	0x0131},  /* LATIN SMALL LETTER DOTLESS I		   */
+  {"int",	0x222B},  /* INTEGRAL					   */
+  {"intcal",	0x22BA},  /* INTERCALATE				   */
+  {"iocy",	0x0451},  /* CYRILLIC SMALL LETTER IO			   */
+  {"iogon",	0x012F},  /* LATIN SMALL LETTER I WITH OGONEK		   */
+  {"iota",	0x03B9},  /* GREEK SMALL LETTER IOTA			   */
+  {"iquest",	0x00BF},  /* INVERTED QUESTION MARK			   */
+  {"isin",	0x2208},  /* ELEMENT OF 				   */
+  {"itilde",	0x0129},  /* LATIN SMALL LETTER I WITH TILDE		   */
+  {"iukcy",	0x0456},  /* CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I*/
+  {"iuml",	0x00EF},  /* LATIN SMALL LETTER I WITH DIAERESIS	   */
+  {"jcirc",	0x0135},  /* LATIN SMALL LETTER J WITH CIRCUMFLEX	   */
+  {"jcy",	0x0439},  /* CYRILLIC SMALL LETTER SHORT I		   */
+  {"jsercy",	0x0458},  /* CYRILLIC SMALL LETTER JE			   */
+  {"jukcy",	0x0454},  /* CYRILLIC SMALL LETTER UKRAINIAN IE 	   */
+  {"kappa",	0x03BA},  /* GREEK SMALL LETTER KAPPA			   */
+  {"kappav",	0x03F0},  /* GREEK KAPPA SYMBOL 			   */
+  {"kcedil",	0x0137},  /* LATIN SMALL LETTER K WITH CEDILLA		   */
+  {"kcy",	0x043A},  /* CYRILLIC SMALL LETTER KA			   */
+  {"kgr",	0x03BA},  /* GREEK SMALL LETTER KAPPA			   */
+  {"kgreen",	0x0138},  /* LATIN SMALL LETTER KRA			   */
+  {"khcy",	0x0445},  /* CYRILLIC SMALL LETTER HA			   */
+  {"khgr",	0x03C7},  /* GREEK SMALL LETTER CHI			   */
+  {"kjcy",	0x045C},  /* CYRILLIC SMALL LETTER KJE			   */
+  {"lAarr",	0x21DA},  /* LEFTWARDS TRIPLE ARROW			   */
+  {"lArr",	0x21D0},  /* LEFTWARDS DOUBLE ARROW			   */
+  {"lE",	0x2266},  /* LESS-THAN OVER EQUAL TO			   */
+  {"lacute",	0x013A},  /* LATIN SMALL LETTER L WITH ACUTE		   */
+  {"lagran",	0x2112},  /* SCRIPT CAPITAL L				   */
+  {"lambda",	0x03BB},  /* GREEK SMALL LETTER LAMDA			   */
+  {"lang",	0x2329},  /* LEFT-POINTING ANGLE BRACKET		   */
+  {"laquo",	0x00AB},  /* LEFT-POINTING DOUBLE ANGLE QUOTATION MARK	   */
+  {"larr",	0x2190},  /* LEFTWARDS ARROW				   */
+  {"larr2",	0x21C7},  /* LEFTWARDS PAIRED ARROWS			   */
+  {"larrhk",	0x21A9},  /* LEFTWARDS ARROW WITH HOOK			   */
+  {"larrlp",	0x21AB},  /* LEFTWARDS ARROW WITH LOOP			   */
+  {"larrtl",	0x21A2},  /* LEFTWARDS ARROW WITH TAIL			   */
+  {"lcaron",	0x013E},  /* LATIN SMALL LETTER L WITH CARON		   */
+  {"lcedil",	0x013C},  /* LATIN SMALL LETTER L WITH CEDILLA		   */
+  {"lceil",	0x2308},  /* LEFT CEILING				   */
+  {"lcub",	0x007B},  /* LEFT CURLY BRACKET 			   */
+  {"lcy",	0x043B},  /* CYRILLIC SMALL LETTER EL			   */
+  {"ldot",	0x22D6},  /* LESS-THAN WITH DOT 			   */
+  {"ldquo",	0x201C},  /* LEFT DOUBLE QUOTATION MARK 		   */
+  {"ldquor",	0x201E},  /* DOUBLE LOW-9 QUOTATION MARK		   */
+  {"le",	0x2264},  /* LESS-THAN OR EQUAL TO			   */
+  {"leg",	0x22DA},  /* LESS-THAN EQUAL TO OR GREATER-THAN 	   */
+  {"les",	0x2264},  /* LESS-THAN OR EQUAL TO			   */
+  {"lfloor",	0x230A},  /* LEFT FLOOR 				   */
+  {"lg",	0x2276},  /* LESS-THAN OR GREATER-THAN			   */
+  {"lgr",	0x03BB},  /* GREEK SMALL LETTER LAMDA			   */
+  {"lhard",	0x21BD},  /* LEFTWARDS HARPOON WITH BARB DOWNWARDS	   */
+  {"lharu",	0x21BC},  /* LEFTWARDS HARPOON WITH BARB UPWARDS	   */
+  {"lhblk",	0x2584},  /* LOWER HALF BLOCK				   */
+  {"ljcy",	0x0459},  /* CYRILLIC SMALL LETTER LJE			   */
+  {"lmidot",	0x0140},  /* LATIN SMALL LETTER L WITH MIDDLE DOT	   */
+  {"lnE",	0x2268},  /* LESS-THAN BUT NOT EQUAL TO 		   */
+  {"lne",	0x2268},  /* LESS-THAN BUT NOT EQUAL TO 		   */
+  {"lnsim",	0x22E6},  /* LESS-THAN BUT NOT EQUIVALENT TO		   */
+  {"lowast",	0x2217},  /* ASTERISK OPERATOR				   */
+  {"lowbar",	0x005F},  /* LOW LINE					   */
+  {"loz",	0x25CA},  /* LOZENGE					   */
+/*{"loz",	0x2727},     WHITE FOUR POINTED STAR			   */
+  /* Warning: Duplicated &loz; entry.  HTML 4,0 defines it as U+25CA.	   */
+  {"lozf",	0x2726},  /* BLACK FOUR POINTED STAR			   */
+  {"lpar",	0x0028},  /* LEFT PARENTHESIS				   */
+  {"lrarr2",	0x21C6},  /* LEFTWARDS ARROW OVER RIGHTWARDS ARROW	   */
+  {"lrhar2",	0x21CB},  /* LEFTWARDS HARPOON OVER RIGHTWARDS HARPOON	   */
+  {"lrm",	0x200E},  /* LEFT-TO-RIGHT MARK 			   */
+  {"lsaquo",	0x2039},  /* SINGLE LEFT-POINTING ANGLE QUOTATION MARK	   */
+  {"lsh",	0x21B0},  /* UPWARDS ARROW WITH TIP LEFTWARDS		   */
+  {"lsim",	0x2272},  /* LESS-THAN OR EQUIVALENT TO 		   */
+  {"lsqb",	0x005B},  /* LEFT SQUARE BRACKET			   */
+  {"lsquo",	0x2018},  /* LEFT SINGLE QUOTATION MARK 		   */
+  {"lsquor",	0x201A},  /* SINGLE LOW-9 QUOTATION MARK		   */
+  {"lstrok",	0x0142},  /* LATIN SMALL LETTER L WITH STROKE		   */
+  {"lt",	0x003C},  /* LESS-THAN SIGN				   */
+  {"lthree",	0x22CB},  /* LEFT SEMIDIRECT PRODUCT			   */
+  {"ltimes",	0x22C9},  /* LEFT NORMAL FACTOR SEMIDIRECT PRODUCT	   */
+  {"ltri",	0x25C3},  /* WHITE LEFT-POINTING SMALL TRIANGLE 	   */
+  {"ltrie",	0x22B4},  /* NORMAL SUBGROUP OF OR EQUAL TO		   */
+  {"ltrif",	0x25C2},  /* BLACK LEFT-POINTING SMALL TRIANGLE 	   */
+  {"lvnE",	0x2268},  /* LESS-THAN BUT NOT EQUAL TO 		   */
+  {"macr",	0x00AF},  /* MACRON					   */
+  {"male",	0x2642},  /* MALE SIGN					   */
+  {"malt",	0x2720},  /* MALTESE CROSS				   */
+  {"map",	0x21A6},  /* RIGHTWARDS ARROW FROM BAR			   */
+  {"marker",	0x25AE},  /* BLACK VERTICAL RECTANGLE			   */
+  {"mcy",	0x043C},  /* CYRILLIC SMALL LETTER EM			   */
+  {"mdash",	0x2014},  /* EM DASH					   */
+  {"mgr",	0x03BC},  /* GREEK SMALL LETTER MU			   */
+  {"micro",	0x00B5},  /* MICRO SIGN 				   */
+  {"mid",	0x2223},  /* DIVIDES					   */
+  {"middot",	0x00B7},  /* MIDDLE DOT 				   */
+  {"minus",	0x2212},  /* MINUS SIGN 				   */
+  {"minusb",	0x229F},  /* SQUARED MINUS				   */
+  {"mldr",	0x2026},  /* HORIZONTAL ELLIPSIS			   */
+  {"mnplus",	0x2213},  /* MINUS-OR-PLUS SIGN 			   */
+  {"models",	0x22A7},  /* MODELS					   */
+  {"mu",	0x03BC},  /* GREEK SMALL LETTER MU			   */
+  {"mumap",	0x22B8},  /* MULTIMAP					   */
+  {"nVDash",	0x22AF},  /* NEGATED DOUBLE VERTICAL BAR DOUBLE RIGHT TURNS*/
+  {"nVdash",	0x22AE},  /* DOES NOT FORCE				   */
+  {"nabla",	0x2207},  /* NABLA					   */
+  {"nacute",	0x0144},  /* LATIN SMALL LETTER N WITH ACUTE		   */
+  {"nap",	0x2249},  /* NOT ALMOST EQUAL TO			   */
+  {"napos",	0x0149},  /* LATIN SMALL LETTER N PRECEDED BY APOSTROPHE   */
+  {"natur",	0x266E},  /* MUSIC NATURAL SIGN 			   */
+  {"nbsp",	0x00A0},  /* NO-BREAK SPACE				   */
+  {"ncaron",	0x0148},  /* LATIN SMALL LETTER N WITH CARON		   */
+  {"ncedil",	0x0146},  /* LATIN SMALL LETTER N WITH CEDILLA		   */
+  {"ncong",	0x2247},  /* NEITHER APPROXIMATELY NOR ACTUALLY EQUAL TO   */
+  {"ncy",	0x043D},  /* CYRILLIC SMALL LETTER EN			   */
+  {"ndash",	0x2013},  /* EN DASH					   */
+  {"ne",	0x2260},  /* NOT EQUAL TO				   */
+  {"nearr",	0x2197},  /* NORTH EAST ARROW				   */
+  {"nequiv",	0x2262},  /* NOT IDENTICAL TO				   */
+  {"nexist",	0x2204},  /* THERE DOES NOT EXIST			   */
+  {"nge",	0x2271},  /* NEITHER GREATER-THAN NOR EQUAL TO		   */
+  {"nges",	0x2271},  /* NEITHER GREATER-THAN NOR EQUAL TO		   */
+  {"ngr",	0x03BD},  /* GREEK SMALL LETTER NU			   */
+  {"ngt",	0x226F},  /* NOT GREATER-THAN				   */
+  {"nhArr",	0x21CE},  /* LEFT RIGHT DOUBLE ARROW WITH STROKE	   */
+  {"nharr",	0x21AE},  /* LEFT RIGHT ARROW WITH STROKE		   */
+  {"ni",	0x220B},  /* CONTAINS AS MEMBER 			   */
+  {"njcy",	0x045A},  /* CYRILLIC SMALL LETTER NJE			   */
+  {"nlArr",	0x21CD},  /* LEFTWARDS DOUBLE ARROW WITH STROKE 	   */
+  {"nlarr",	0x219A},  /* LEFTWARDS ARROW WITH STROKE		   */
+  {"nldr",	0x2025},  /* TWO DOT LEADER				   */
+  {"nle",	0x2270},  /* NEITHER LESS-THAN NOR EQUAL TO		   */
+  {"nles",	0x2270},  /* NEITHER LESS-THAN NOR EQUAL TO		   */
+  {"nlt",	0x226E},  /* NOT LESS-THAN				   */
+  {"nltri",	0x22EA},  /* NOT NORMAL SUBGROUP OF			   */
+  {"nltrie",	0x22EC},  /* NOT NORMAL SUBGROUP OF OR EQUAL TO 	   */
+  {"nmid",	0x2224},  /* DOES NOT DIVIDE				   */
+  {"not",	0x00AC},  /* NOT SIGN					   */
+  {"notin",	0x2209},  /* NOT AN ELEMENT OF				   */
+  {"npar",	0x2226},  /* NOT PARALLEL TO				   */
+  {"npr",	0x2280},  /* DOES NOT PRECEDE				   */
+  {"npre",	0x22E0},  /* DOES NOT PRECEDE OR EQUAL			   */
+  {"nrArr",	0x21CF},  /* RIGHTWARDS DOUBLE ARROW WITH STROKE	   */
+  {"nrarr",	0x219B},  /* RIGHTWARDS ARROW WITH STROKE		   */
+  {"nrtri",	0x22EB},  /* DOES NOT CONTAIN AS NORMAL SUBGROUP	   */
+  {"nrtrie",	0x22ED},  /* DOES NOT CONTAIN AS NORMAL SUBGROUP OR EQUAL  */
+  {"nsc",	0x2281},  /* DOES NOT SUCCEED				   */
+  {"nsce",	0x22E1},  /* DOES NOT SUCCEED OR EQUAL			   */
+  {"nsim",	0x2241},  /* NOT TILDE					   */
+  {"nsime",	0x2244},  /* NOT ASYMPTOTICALLY EQUAL TO		   */
+  {"nspar",	0x2226},  /* NOT PARALLEL TO				   */
+  {"nsub",	0x2284},  /* NOT A SUBSET OF				   */
+  {"nsubE",	0x2288},  /* NEITHER A SUBSET OF NOR EQUAL TO		   */
+  {"nsube",	0x2288},  /* NEITHER A SUBSET OF NOR EQUAL TO		   */
+  {"nsup",	0x2285},  /* NOT A SUPERSET OF				   */
+  {"nsupE",	0x2289},  /* NEITHER A SUPERSET OF NOR EQUAL TO 	   */
+  {"nsupe",	0x2289},  /* NEITHER A SUPERSET OF NOR EQUAL TO 	   */
+  {"ntilde",	0x00F1},  /* LATIN SMALL LETTER N WITH TILDE		   */
+  {"nu",	0x03BD},  /* GREEK SMALL LETTER NU			   */
+  {"num",	0x0023},  /* NUMBER SIGN				   */
+  {"numero",	0x2116},  /* NUMERO SIGN				   */
+  {"numsp",	0x2007},  /* FIGURE SPACE				   */
+  {"nvDash",	0x22AD},  /* NOT TRUE					   */
+  {"nvdash",	0x22AC},  /* DOES NOT PROVE				   */
+  {"nwarr",	0x2196},  /* NORTH WEST ARROW				   */
+  {"oS",	0x24C8},  /* CIRCLED LATIN CAPITAL LETTER S		   */
+  {"oacgr",	0x03CC},  /* GREEK SMALL LETTER OMICRON WITH TONOS	   */
+  {"oacute",	0x00F3},  /* LATIN SMALL LETTER O WITH ACUTE		   */
+  {"oast",	0x229B},  /* CIRCLED ASTERISK OPERATOR			   */
+  {"ocir",	0x229A},  /* CIRCLED RING OPERATOR			   */
+  {"ocirc",	0x00F4},  /* LATIN SMALL LETTER O WITH CIRCUMFLEX	   */
+  {"ocy",	0x043E},  /* CYRILLIC SMALL LETTER O			   */
+  {"odash",	0x229D},  /* CIRCLED DASH				   */
+  {"odblac",	0x0151},  /* LATIN SMALL LETTER O WITH DOUBLE ACUTE	   */
+  {"odot",	0x2299},  /* CIRCLED DOT OPERATOR			   */
+  {"oelig",	0x0153},  /* LATIN SMALL LIGATURE OE			   */
+  {"ogon",	0x02DB},  /* OGONEK					   */
+  {"ogr",	0x03BF},  /* GREEK SMALL LETTER OMICRON 		   */
+  {"ograve",	0x00F2},  /* LATIN SMALL LETTER O WITH GRAVE		   */
+  {"ohacgr",	0x03CE},  /* GREEK SMALL LETTER OMEGA WITH TONOS	   */
+  {"ohgr",	0x03C9},  /* GREEK SMALL LETTER OMEGA			   */
+  {"ohm",	0x2126},  /* OHM SIGN					   */
+  {"olarr",	0x21BA},  /* ANTICLOCKWISE OPEN CIRCLE ARROW		   */
+  {"oline",	0x203E},  /* OVERLINE					   */
+  {"omacr",	0x014D},  /* LATIN SMALL LETTER O WITH MACRON		   */
+  {"omega",	0x03C9},  /* GREEK SMALL LETTER OMEGA			   */
+  {"omicron",	0x03BF},  /* GREEK SMALL LETTER OMICRON 		   */
+  {"ominus",	0x2296},  /* CIRCLED MINUS				   */
+  {"oplus",	0x2295},  /* CIRCLED PLUS				   */
+  {"or",	0x2228},  /* LOGICAL OR 				   */
+  {"orarr",	0x21BB},  /* CLOCKWISE OPEN CIRCLE ARROW		   */
+  {"order",	0x2134},  /* SCRIPT SMALL O				   */
+  {"ordf",	0x00AA},  /* FEMININE ORDINAL INDICATOR 		   */
+  {"ordm",	0x00BA},  /* MASCULINE ORDINAL INDICATOR		   */
+  {"oslash",	0x00F8},  /* LATIN SMALL LETTER O WITH STROKE		   */
+  {"osol",	0x2298},  /* CIRCLED DIVISION SLASH			   */
+  {"otilde",	0x00F5},  /* LATIN SMALL LETTER O WITH TILDE		   */
+  {"otimes",	0x2297},  /* CIRCLED TIMES				   */
+  {"ouml",	0x00F6},  /* LATIN SMALL LETTER O WITH DIAERESIS	   */
+  {"par",	0x2225},  /* PARALLEL TO				   */
+  {"para",	0x00B6},  /* PILCROW SIGN				   */
+  {"part",	0x2202},  /* PARTIAL DIFFERENTIAL			   */
+  {"pcy",	0x043F},  /* CYRILLIC SMALL LETTER PE			   */
+  {"percnt",	0x0025},  /* PERCENT SIGN				   */
+  {"period",	0x002E},  /* FULL STOP					   */
+  {"permil",	0x2030},  /* PER MILLE SIGN				   */
+  {"perp",	0x22A5},  /* UP TACK					   */
+  {"pgr",	0x03C0},  /* GREEK SMALL LETTER PI			   */
+  {"phgr",	0x03C6},  /* GREEK SMALL LETTER PHI			   */
+  {"phi",	0x03C6},  /* GREEK SMALL LETTER PHI			   */
+  {"phis",	0x03C6},  /* GREEK SMALL LETTER PHI			   */
+  {"phiv",	0x03D5},  /* GREEK PHI SYMBOL				   */
+  {"phmmat",	0x2133},  /* SCRIPT CAPITAL M				   */
+  {"phone",	0x260E},  /* BLACK TELEPHONE				   */
+  {"pi",	0x03C0},  /* GREEK SMALL LETTER PI			   */
+  {"piv",	0x03D6},  /* GREEK PI SYMBOL				   */
+  {"planck",	0x210F},  /* PLANCK CONSTANT OVER TWO PI		   */
+  {"plus",	0x002B},  /* PLUS SIGN					   */
+  {"plusb",	0x229E},  /* SQUARED PLUS				   */
+  {"plusdo",	0x2214},  /* DOT PLUS					   */
+  {"plusmn",	0x00B1},  /* PLUS-MINUS SIGN				   */
+  {"pound",	0x00A3},  /* POUND SIGN 				   */
+  {"pr",	0x227A},  /* PRECEDES					   */
+  {"pre",	0x227C},  /* PRECEDES OR EQUAL TO			   */
+  {"prime",	0x2032},  /* PRIME					   */
+  {"prnsim",	0x22E8},  /* PRECEDES BUT NOT EQUIVALENT TO		   */
+  {"prod",	0x220F},  /* N-ARY PRODUCT				   */
+  {"prop",	0x221D},  /* PROPORTIONAL TO				   */
+  {"prsim",	0x227E},  /* PRECEDES OR EQUIVALENT TO			   */
+  {"psgr",	0x03C8},  /* GREEK SMALL LETTER PSI			   */
+  {"psi",	0x03C8},  /* GREEK SMALL LETTER PSI			   */
+  {"puncsp",	0x2008},  /* PUNCTUATION SPACE				   */
+  {"quest",	0x003F},  /* QUESTION MARK				   */
+  {"quot",	0x0022},  /* QUOTATION MARK				   */
+  {"rAarr",	0x21DB},  /* RIGHTWARDS TRIPLE ARROW			   */
+  {"rArr",	0x21D2},  /* RIGHTWARDS DOUBLE ARROW			   */
+  {"racute",	0x0155},  /* LATIN SMALL LETTER R WITH ACUTE		   */
+  {"radic",	0x221A},  /* SQUARE ROOT				   */
+  {"rang",	0x232A},  /* RIGHT-POINTING ANGLE BRACKET		   */
+  {"raquo",	0x00BB},  /* RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK    */
+  {"rarr",	0x2192},  /* RIGHTWARDS ARROW				   */
+  {"rarr2",	0x21C9},  /* RIGHTWARDS PAIRED ARROWS			   */
+  {"rarrhk",	0x21AA},  /* RIGHTWARDS ARROW WITH HOOK 		   */
+  {"rarrlp",	0x21AC},  /* RIGHTWARDS ARROW WITH LOOP 		   */
+  {"rarrtl",	0x21A3},  /* RIGHTWARDS ARROW WITH TAIL 		   */
+  {"rarrw",	0x219D},  /* RIGHTWARDS WAVE ARROW			   */
+  {"rcaron",	0x0159},  /* LATIN SMALL LETTER R WITH CARON		   */
+  {"rcedil",	0x0157},  /* LATIN SMALL LETTER R WITH CEDILLA		   */
+  {"rceil",	0x2309},  /* RIGHT CEILING				   */
+  {"rcub",	0x007D},  /* RIGHT CURLY BRACKET			   */
+  {"rcy",	0x0440},  /* CYRILLIC SMALL LETTER ER			   */
+  {"rdquo",	0x201D},  /* RIGHT DOUBLE QUOTATION MARK		   */
+  {"rdquor",	0x201C},  /* LEFT DOUBLE QUOTATION MARK 		   */
+  {"real",	0x211C},  /* BLACK-LETTER CAPITAL R			   */
+  {"rect",	0x25AD},  /* WHITE RECTANGLE				   */
+  {"reg",	0x00AE},  /* REGISTERED SIGN				   */
+  {"rfloor",	0x230B},  /* RIGHT FLOOR				   */
+  {"rgr",	0x03C1},  /* GREEK SMALL LETTER RHO			   */
+  {"rhard",	0x21C1},  /* RIGHTWARDS HARPOON WITH BARB DOWNWARDS	   */
+  {"rharu",	0x21C0},  /* RIGHTWARDS HARPOON WITH BARB UPWARDS	   */
+  {"rho",	0x03C1},  /* GREEK SMALL LETTER RHO			   */
+  {"rhov",	0x03F1},  /* GREEK RHO SYMBOL				   */
+  {"ring",	0x02DA},  /* RING ABOVE 				   */
+  {"rlarr2",	0x21C4},  /* RIGHTWARDS ARROW OVER LEFTWARDS ARROW	   */
+  {"rlhar2",	0x21CC},  /* RIGHTWARDS HARPOON OVER LEFTWARDS HARPOON	   */
+  {"rlm",	0x200F},  /* RIGHT-TO-LEFT MARK 			   */
+  {"rpar",	0x0029},  /* RIGHT PARENTHESIS				   */
+  {"rsaquo",	0x203A},  /* SINGLE RIGHT-POINTING ANGLE QUOTATION MARK    */
+  {"rsh",	0x21B1},  /* UPWARDS ARROW WITH TIP RIGHTWARDS		   */
+  {"rsqb",	0x005D},  /* RIGHT SQUARE BRACKET			   */
+  {"rsquo",	0x2019},  /* RIGHT SINGLE QUOTATION MARK		   */
+  {"rsquor",	0x2018},  /* LEFT SINGLE QUOTATION MARK 		   */
+  {"rthree",	0x22CC},  /* RIGHT SEMIDIRECT PRODUCT			   */
+  {"rtimes",	0x22CA},  /* RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT	   */
+  {"rtri",	0x25B9},  /* WHITE RIGHT-POINTING SMALL TRIANGLE	   */
+  {"rtrie",	0x22B5},  /* CONTAINS AS NORMAL SUBGROUP OR EQUAL TO	   */
+  {"rtrif",	0x25B8},  /* BLACK RIGHT-POINTING SMALL TRIANGLE	   */
+  {"rx",	0x211E},  /* PRESCRIPTION TAKE				   */
+  {"sacute",	0x015B},  /* LATIN SMALL LETTER S WITH ACUTE		   */
+  {"samalg",	0x2210},  /* N-ARY COPRODUCT				   */
+  {"sbquo",	0x201A},  /* SINGLE LOW-9 QUOTATION MARK		   */
+  {"sbsol",	0x005C},  /* REVERSE SOLIDUS				   */
+  {"sc",	0x227B},  /* SUCCEEDS					   */
+  {"scaron",	0x0161},  /* LATIN SMALL LETTER S WITH CARON		   */
+  {"sccue",	0x227D},  /* SUCCEEDS OR EQUAL TO			   */
+  {"sce",	0x227D},  /* SUCCEEDS OR EQUAL TO			   */
+  {"scedil",	0x015F},  /* LATIN SMALL LETTER S WITH CEDILLA		   */
+  {"scirc",	0x015D},  /* LATIN SMALL LETTER S WITH CIRCUMFLEX	   */
+  {"scnsim",	0x22E9},  /* SUCCEEDS BUT NOT EQUIVALENT TO		   */
+  {"scsim",	0x227F},  /* SUCCEEDS OR EQUIVALENT TO			   */
+  {"scy",	0x0441},  /* CYRILLIC SMALL LETTER ES			   */
+  {"sdot",	0x22C5},  /* DOT OPERATOR				   */
+  {"sdotb",	0x22A1},  /* SQUARED DOT OPERATOR			   */
+  {"sect",	0x00A7},  /* SECTION SIGN				   */
+  {"semi",	0x003B},  /* SEMICOLON					   */
+  {"setmn",	0x2216},  /* SET MINUS					   */
+  {"sext",	0x2736},  /* SIX POINTED BLACK STAR			   */
+  {"sfgr",	0x03C2},  /* GREEK SMALL LETTER FINAL SIGMA		   */
+  {"sfrown",	0x2322},  /* FROWN					   */
+  {"sgr",	0x03C3},  /* GREEK SMALL LETTER SIGMA			   */
+  {"sharp",	0x266F},  /* MUSIC SHARP SIGN				   */
+  {"shchcy",	0x0449},  /* CYRILLIC SMALL LETTER SHCHA		   */
+  {"shcy",	0x0448},  /* CYRILLIC SMALL LETTER SHA			   */
+  {"shy",	0x00AD},  /* SOFT HYPHEN				   */
+  {"sigma",	0x03C3},  /* GREEK SMALL LETTER SIGMA			   */
+  {"sigmaf",	0x03C2},  /* GREEK SMALL LETTER FINAL SIGMA		   */
+  {"sigmav",	0x03C2},  /* GREEK SMALL LETTER FINAL SIGMA		   */
+  {"sim",	0x223C},  /* TILDE OPERATOR				   */
+  {"sime",	0x2243},  /* ASYMPTOTICALLY EQUAL TO			   */
+  {"smile",	0x2323},  /* SMILE					   */
+  {"softcy",	0x044C},  /* CYRILLIC SMALL LETTER SOFT SIGN		   */
+  {"sol",	0x002F},  /* SOLIDUS					   */
+  {"spades",	0x2660},  /* BLACK SPADE SUIT				   */
+  {"spar",	0x2225},  /* PARALLEL TO				   */
+  {"sqcap",	0x2293},  /* SQUARE CAP 				   */
+  {"sqcup",	0x2294},  /* SQUARE CUP 				   */
+  {"sqsub",	0x228F},  /* SQUARE IMAGE OF				   */
+  {"sqsube",	0x2291},  /* SQUARE IMAGE OF OR EQUAL TO		   */
+  {"sqsup",	0x2290},  /* SQUARE ORIGINAL OF 			   */
+  {"sqsupe",	0x2292},  /* SQUARE ORIGINAL OF OR EQUAL TO		   */
+  {"squ",	0x25A1},  /* WHITE SQUARE				   */
+  {"square",	0x25A1},  /* WHITE SQUARE				   */
+  {"squf",	0x25AA},  /* BLACK SMALL SQUARE 			   */
+  {"ssetmn",	0x2216},  /* SET MINUS					   */
+  {"ssmile",	0x2323},  /* SMILE					   */
+  {"sstarf",	0x22C6},  /* STAR OPERATOR				   */
+  {"star",	0x2606},  /* WHITE STAR 				   */
+  {"starf",	0x2605},  /* BLACK STAR 				   */
+  {"sub",	0x2282},  /* SUBSET OF					   */
+  {"subE",	0x2286},  /* SUBSET OF OR EQUAL TO			   */
+  {"sube",	0x2286},  /* SUBSET OF OR EQUAL TO			   */
+  {"subnE",	0x228A},  /* SUBSET OF WITH NOT EQUAL TO		   */
+  {"subne",	0x228A},  /* SUBSET OF WITH NOT EQUAL TO		   */
+  {"sum",	0x2211},  /* N-ARY SUMMATION				   */
+  {"sung",	0x266A},  /* EIGHTH NOTE				   */
+  {"sup",	0x2283},  /* SUPERSET OF				   */
+  {"sup1",	0x00B9},  /* SUPERSCRIPT ONE				   */
+  {"sup2",	0x00B2},  /* SUPERSCRIPT TWO				   */
+  {"sup3",	0x00B3},  /* SUPERSCRIPT THREE				   */
+  {"supE",	0x2287},  /* SUPERSET OF OR EQUAL TO			   */
+  {"supe",	0x2287},  /* SUPERSET OF OR EQUAL TO			   */
+  {"supnE",	0x228B},  /* SUPERSET OF WITH NOT EQUAL TO		   */
+  {"supne",	0x228B},  /* SUPERSET OF WITH NOT EQUAL TO		   */
+  {"szlig",	0x00DF},  /* LATIN SMALL LETTER SHARP S 		   */
+  {"target",	0x2316},  /* POSITION INDICATOR 			   */
+  {"tau",	0x03C4},  /* GREEK SMALL LETTER TAU			   */
+  {"tcaron",	0x0165},  /* LATIN SMALL LETTER T WITH CARON		   */
+  {"tcedil",	0x0163},  /* LATIN SMALL LETTER T WITH CEDILLA		   */
+  {"tcy",	0x0442},  /* CYRILLIC SMALL LETTER TE			   */
+  {"tdot",	0x20DB},  /* COMBINING THREE DOTS ABOVE 		   */
+  {"telrec",	0x2315},  /* TELEPHONE RECORDER 			   */
+  {"tgr",	0x03C4},  /* GREEK SMALL LETTER TAU			   */
+  {"there4",	0x2234},  /* THEREFORE					   */
+  {"theta",	0x03B8},  /* GREEK SMALL LETTER THETA			   */
+  {"thetas",	0x03B8},  /* GREEK SMALL LETTER THETA			   */
+  {"thetasym",	0x03D1},  /* GREEK THETA SYMBOL 			   */
+  {"thetav",	0x03D1},  /* GREEK THETA SYMBOL 			   */
+  {"thgr",	0x03B8},  /* GREEK SMALL LETTER THETA			   */
+  {"thinsp",	0x2009},  /* THIN SPACE 				   */
+  {"thkap",	0x2248},  /* ALMOST EQUAL TO				   */
+  {"thksim",	0x223C},  /* TILDE OPERATOR				   */
+  {"thorn",	0x00FE},  /* LATIN SMALL LETTER THORN			   */
+  {"tilde",	0x02DC},  /* SMALL TILDE				   */
+  {"times",	0x00D7},  /* MULTIPLICATION SIGN			   */
+  {"timesb",	0x22A0},  /* SQUARED TIMES				   */
+  {"top",	0x22A4},  /* DOWN TACK					   */
+  {"tprime",	0x2034},  /* TRIPLE PRIME				   */
+  {"trade",	0x2122},  /* TRADE MARK SIGN				   */
+  {"trie",	0x225C},  /* DELTA EQUAL TO				   */
+  {"tscy",	0x0446},  /* CYRILLIC SMALL LETTER TSE			   */
+  {"tshcy",	0x045B},  /* CYRILLIC SMALL LETTER TSHE 		   */
+  {"tstrok",	0x0167},  /* LATIN SMALL LETTER T WITH STROKE		   */
+  {"twixt",	0x226C},  /* BETWEEN					   */
+  {"uArr",	0x21D1},  /* UPWARDS DOUBLE ARROW			   */
+  {"uacgr",	0x03CD},  /* GREEK SMALL LETTER UPSILON WITH TONOS	   */
+  {"uacute",	0x00FA},  /* LATIN SMALL LETTER U WITH ACUTE		   */
+  {"uarr",	0x2191},  /* UPWARDS ARROW				   */
+  {"uarr2",	0x21C8},  /* UPWARDS PAIRED ARROWS			   */
+  {"ubrcy",	0x045E},  /* CYRILLIC SMALL LETTER SHORT U		   */
+  {"ubreve",	0x016D},  /* LATIN SMALL LETTER U WITH BREVE		   */
+  {"ucirc",	0x00FB},  /* LATIN SMALL LETTER U WITH CIRCUMFLEX	   */
+  {"ucy",	0x0443},  /* CYRILLIC SMALL LETTER U			   */
+  {"udblac",	0x0171},  /* LATIN SMALL LETTER U WITH DOUBLE ACUTE	   */
+  {"udiagr",	0x03B0},  /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND */
+  {"udigr",	0x03CB},  /* GREEK SMALL LETTER UPSILON WITH DIALYTIKA	   */
+  {"ugr",	0x03C5},  /* GREEK SMALL LETTER UPSILON 		   */
+  {"ugrave",	0x00F9},  /* LATIN SMALL LETTER U WITH GRAVE		   */
+  {"uharl",	0x21BF},  /* UPWARDS HARPOON WITH BARB LEFTWARDS	   */
+  {"uharr",	0x21BE},  /* UPWARDS HARPOON WITH BARB RIGHTWARDS	   */
+  {"uhblk",	0x2580},  /* UPPER HALF BLOCK				   */
+  {"ulcorn",	0x231C},  /* TOP LEFT CORNER				   */
+  {"ulcrop",	0x230F},  /* TOP LEFT CROP				   */
+  {"umacr",	0x016B},  /* LATIN SMALL LETTER U WITH MACRON		   */
+  {"uml",	0x00A8},  /* DIAERESIS					   */
+  {"uogon",	0x0173},  /* LATIN SMALL LETTER U WITH OGONEK		   */
+  {"uplus",	0x228E},  /* MULTISET UNION				   */
+  {"upsi",	0x03C5},  /* GREEK SMALL LETTER UPSILON 		   */
+  {"upsih",	0x03D2},  /* GREEK UPSILON WITH HOOK SYMBOL		   */
+  {"upsilon",	0x03C5},  /* GREEK SMALL LETTER UPSILON 		   */
+  {"urcorn",	0x231D},  /* TOP RIGHT CORNER				   */
+  {"urcrop",	0x230E},  /* TOP RIGHT CROP				   */
+  {"uring",	0x016F},  /* LATIN SMALL LETTER U WITH RING ABOVE	   */
+  {"utilde",	0x0169},  /* LATIN SMALL LETTER U WITH TILDE		   */
+  {"utri",	0x25B5},  /* WHITE UP-POINTING SMALL TRIANGLE		   */
+  {"utrif",	0x25B4},  /* BLACK UP-POINTING SMALL TRIANGLE		   */
+  {"uuml",	0x00FC},  /* LATIN SMALL LETTER U WITH DIAERESIS	   */
+  {"vArr",	0x21D5},  /* UP DOWN DOUBLE ARROW			   */
+  {"vDash",	0x22A8},  /* TRUE					   */
+  {"varr",	0x2195},  /* UP DOWN ARROW				   */
+  {"vcy",	0x0432},  /* CYRILLIC SMALL LETTER VE			   */
+  {"vdash",	0x22A2},  /* RIGHT TACK 				   */
+  {"veebar",	0x22BB},  /* XOR					   */
+  {"vellip",	0x22EE},  /* VERTICAL ELLIPSIS				   */
+  {"verbar",	0x007C},  /* VERTICAL LINE				   */
+  {"vltri",	0x22B2},  /* NORMAL SUBGROUP OF 			   */
+  {"vprime",	0x2032},  /* PRIME					   */
+  {"vprop",	0x221D},  /* PROPORTIONAL TO				   */
+  {"vrtri",	0x22B3},  /* CONTAINS AS NORMAL SUBGROUP		   */
+  {"vsubnE",	0x228A},  /* SUBSET OF WITH NOT EQUAL TO		   */
+  {"vsubne",	0x228A},  /* SUBSET OF WITH NOT EQUAL TO		   */
+  {"vsupnE",	0x228B},  /* SUPERSET OF WITH NOT EQUAL TO		   */
+  {"vsupne",	0x228B},  /* SUPERSET OF WITH NOT EQUAL TO		   */
+  {"wcirc",	0x0175},  /* LATIN SMALL LETTER W WITH CIRCUMFLEX	   */
+  {"wedgeq",	0x2259},  /* ESTIMATES					   */
+  {"weierp",	0x2118},  /* SCRIPT CAPITAL P				   */
+  {"wreath",	0x2240},  /* WREATH PRODUCT				   */
+  {"xcirc",	0x25CB},  /* WHITE CIRCLE				   */
+  {"xdtri",	0x25BD},  /* WHITE DOWN-POINTING TRIANGLE		   */
+  {"xgr",	0x03BE},  /* GREEK SMALL LETTER XI			   */
+  {"xhArr",	0x2194},  /* LEFT RIGHT ARROW				   */
+  {"xharr",	0x2194},  /* LEFT RIGHT ARROW				   */
+  {"xi",	0x03BE},  /* GREEK SMALL LETTER XI			   */
+  {"xlArr",	0x21D0},  /* LEFTWARDS DOUBLE ARROW			   */
+  {"xrArr",	0x21D2},  /* RIGHTWARDS DOUBLE ARROW			   */
+  {"xutri",	0x25B3},  /* WHITE UP-POINTING TRIANGLE 		   */
+  {"yacute",	0x00FD},  /* LATIN SMALL LETTER Y WITH ACUTE		   */
+  {"yacy",	0x044F},  /* CYRILLIC SMALL LETTER YA			   */
+  {"ycirc",	0x0177},  /* LATIN SMALL LETTER Y WITH CIRCUMFLEX	   */
+  {"ycy",	0x044B},  /* CYRILLIC SMALL LETTER YERU 		   */
+  {"yen",	0x00A5},  /* YEN SIGN					   */
+  {"yicy",	0x0457},  /* CYRILLIC SMALL LETTER YI			   */
+  {"yucy",	0x044E},  /* CYRILLIC SMALL LETTER YU			   */
+  {"yuml",	0x00FF},  /* LATIN SMALL LETTER Y WITH DIAERESIS	   */
+  {"zacute",	0x017A},  /* LATIN SMALL LETTER Z WITH ACUTE		   */
+  {"zcaron",	0x017E},  /* LATIN SMALL LETTER Z WITH CARON		   */
+  {"zcy",	0x0437},  /* CYRILLIC SMALL LETTER ZE			   */
+  {"zdot",	0x017C},  /* LATIN SMALL LETTER Z WITH DOT ABOVE	   */
+  {"zeta",	0x03B6},  /* GREEK SMALL LETTER ZETA			   */
+  {"zgr",	0x03B6},  /* GREEK SMALL LETTER ZETA			   */
+  {"zhcy",	0x0436},  /* CYRILLIC SMALL LETTER ZHE			   */
+  {"zwj",	0x200D},  /* ZERO WIDTH JOINER				   */
+  {"zwnj",	0x200C},  /* ZERO WIDTH NON-JOINER			   */
+/* {"epsiv",	0x????},  variant epsilon			 # ISOgrk3 */
+/* {"fjlig",	0x????},  fj ligature				 # ISOpub  */
+/* {"gEl",	0x????},  greater-than, double equals, less-than # ISOamsr */
+/* {"gap",	0x????},  greater-than, approximately equal to	 # ISOamsr */
+/* {"gnap",	0x????},  greater-than, not approximately equal t# ISOamsn */
+/* {"jnodot",	0x????},  latin small letter dotless j		 # ISOamso */
+/* {"lEg",	0x????},  less-than, double equals, greater-than # ISOamsr */
+/* {"lap",	0x????},  less-than, approximately equal to	 # ISOamsr */
+/* {"lnap",	0x????},  less-than, not approximately equal to  # ISOamsn */
+/* {"lpargt",	0x????},  left parenthesis, greater-than	 # ISOamsc */
+/* {"ngE",	0x????},  not greater-than, double equals	 # ISOamsn */
+/* {"nlE",	0x????},  not less-than, double equals		 # ISOamsn */
+/* {"nsmid",	0x????},  nshortmid				 # ISOamsn */
+/* {"prap",	0x????},  precedes, approximately equal to	 # ISOamsr */
+/* {"prnE",	0x????},  precedes, not double equal		 # ISOamsn */
+/* {"prnap",	0x????},  precedes, not approximately equal to	 # ISOamsn */
+/* {"rpargt",	0x????},  right parenthesis, greater-than	 # ISOamsc */
+/* {"scap",	0x????},  succeeds, approximately equal to	 # ISOamsr */
+/* {"scnE",	0x????},  succeeds, not double equals		 # ISOamsn */
+/* {"scnap",	0x????},  succeeds, not approximately equal to	 # ISOamsn */
+/* {"smid",	0x????},  shortmid				 # ISOamsr */
+};
+
+#endif /* not ENTITIES_HTML40_ONLY */
+/* *INDENT-ON* */
diff --git a/src/chrtrans/hp_uni.tbl b/src/chrtrans/hp_uni.tbl
new file mode 100644
index 00000000..58025511
--- /dev/null
+++ b/src/chrtrans/hp_uni.tbl
@@ -0,0 +1,212 @@
+# The MIME name of this charset.
+Mhp-roman8
+
+# Name as a Display Charset (used on Options screen)
+OHP Roman8
+
+# This is not the default font!
+D0
+
+#
+# Name:		HP Roman8 to Unicode
+# Date:		1999-01-09
+# Authors:	Christian "naddy" Weisgerber <naddy@mips.rhein-neckar.de>
+#		Mapping by Roman Czyborra,
+#		<URL:http://czyborra.com/charsets/codepages.html#HP-Roman8>
+#
+
+0x20-0x7E idem	#	ASCII
+
+#0x20	U+0020	#	SPACE
+#0x21	U+0021	#	EXCLAMATION MARK
+#0x22	U+0022	#	QUOTATION MARK
+#0x23	U+0023	#	NUMBER SIGN
+#0x24	U+0024	#	DOLLAR SIGN
+#0x25	U+0025	#	PERCENT SIGN
+#0x26	U+0026	#	AMPERSAND
+#0x27	U+0027	#	APOSTROPHE
+#0x28	U+0028	#	LEFT PARENTHESIS
+#0x29	U+0029	#	RIGHT PARENTHESIS
+#0x2A	U+002A	#	ASTERISK
+#0x2B	U+002B	#	PLUS SIGN
+#0x2C	U+002C	#	COMMA
+#0x2D	U+002D	#	HYPHEN-MINUS
+#0x2E	U+002E	#	FULL STOP
+#0x2F	U+002F	#	SOLIDUS
+#0x30	U+0030	#	DIGIT ZERO
+#0x31	U+0031	#	DIGIT ONE
+#0x32	U+0032	#	DIGIT TWO
+#0x33	U+0033	#	DIGIT THREE
+#0x34	U+0034	#	DIGIT FOUR
+#0x35	U+0035	#	DIGIT FIVE
+#0x36	U+0036	#	DIGIT SIX
+#0x37	U+0037	#	DIGIT SEVEN
+#0x38	U+0038	#	DIGIT EIGHT
+#0x39	U+0039	#	DIGIT NINE
+#0x3A	U+003A	#	COLON
+#0x3B	U+003B	#	SEMICOLON
+#0x3C	U+003C	#	LESS-THAN SIGN
+#0x3D	U+003D	#	EQUALS SIGN
+#0x3E	U+003E	#	GREATER-THAN SIGN
+#0x3F	U+003F	#	QUESTION MARK
+#0x40	U+0040	#	COMMERCIAL AT
+#0x41	U+0041	#	LATIN CAPITAL LETTER A
+#0x42	U+0042	#	LATIN CAPITAL LETTER B
+#0x43	U+0043	#	LATIN CAPITAL LETTER C
+#0x44	U+0044	#	LATIN CAPITAL LETTER D
+#0x45	U+0045	#	LATIN CAPITAL LETTER E
+#0x46	U+0046	#	LATIN CAPITAL LETTER F
+#0x47	U+0047	#	LATIN CAPITAL LETTER G
+#0x48	U+0048	#	LATIN CAPITAL LETTER H
+#0x49	U+0049	#	LATIN CAPITAL LETTER I
+#0x4A	U+004A	#	LATIN CAPITAL LETTER J
+#0x4B	U+004B	#	LATIN CAPITAL LETTER K
+#0x4C	U+004C	#	LATIN CAPITAL LETTER L
+#0x4D	U+004D	#	LATIN CAPITAL LETTER M
+#0x4E	U+004E	#	LATIN CAPITAL LETTER N
+#0x4F	U+004F	#	LATIN CAPITAL LETTER O
+#0x50	U+0050	#	LATIN CAPITAL LETTER P
+#0x51	U+0051	#	LATIN CAPITAL LETTER Q
+#0x52	U+0052	#	LATIN CAPITAL LETTER R
+#0x53	U+0053	#	LATIN CAPITAL LETTER S
+#0x54	U+0054	#	LATIN CAPITAL LETTER T
+#0x55	U+0055	#	LATIN CAPITAL LETTER U
+#0x56	U+0056	#	LATIN CAPITAL LETTER V
+#0x57	U+0057	#	LATIN CAPITAL LETTER W
+#0x58	U+0058	#	LATIN CAPITAL LETTER X
+#0x59	U+0059	#	LATIN CAPITAL LETTER Y
+#0x5A	U+005A	#	LATIN CAPITAL LETTER Z
+#0x5B	U+005B	#	LEFT SQUARE BRACKET
+#0x5C	U+005C	#	REVERSE SOLIDUS
+#0x5D	U+005D	#	RIGHT SQUARE BRACKET
+#0x5E	U+005E	#	CIRCUMFLEX ACCENT
+#0x5F	U+005F	#	LOW LINE
+#0x60	U+0060	#	GRAVE ACCENT
+#0x61	U+0061	#	LATIN SMALL LETTER A
+#0x62	U+0062	#	LATIN SMALL LETTER B
+#0x63	U+0063	#	LATIN SMALL LETTER C
+#0x64	U+0064	#	LATIN SMALL LETTER D
+#0x65	U+0065	#	LATIN SMALL LETTER E
+#0x66	U+0066	#	LATIN SMALL LETTER F
+#0x67	U+0067	#	LATIN SMALL LETTER G
+#0x68	U+0068	#	LATIN SMALL LETTER H
+#0x69	U+0069	#	LATIN SMALL LETTER I
+#0x6A	U+006A	#	LATIN SMALL LETTER J
+#0x6B	U+006B	#	LATIN SMALL LETTER K
+#0x6C	U+006C	#	LATIN SMALL LETTER L
+#0x6D	U+006D	#	LATIN SMALL LETTER M
+#0x6E	U+006E	#	LATIN SMALL LETTER N
+#0x6F	U+006F	#	LATIN SMALL LETTER O
+#0x70	U+0070	#	LATIN SMALL LETTER P
+#0x71	U+0071	#	LATIN SMALL LETTER Q
+#0x72	U+0072	#	LATIN SMALL LETTER R
+#0x73	U+0073	#	LATIN SMALL LETTER S
+#0x74	U+0074	#	LATIN SMALL LETTER T
+#0x75	U+0075	#	LATIN SMALL LETTER U
+#0x76	U+0076	#	LATIN SMALL LETTER V
+#0x77	U+0077	#	LATIN SMALL LETTER W
+#0x78	U+0078	#	LATIN SMALL LETTER X
+#0x79	U+0079	#	LATIN SMALL LETTER Y
+#0x7A	U+007A	#	LATIN SMALL LETTER Z
+#0x7B	U+007B	#	LEFT CURLY BRACKET
+#0x7C	U+007C	#	VERTICAL LINE
+#0x7D	U+007D	#	RIGHT CURLY BRACKET
+#0x7E	U+007E	#	TILDE
+
+0xA0	U+00A0	#	NO-BREAK SPACE
+0xA1	U+00C0	#	LATIN CAPITAL LETTER A WITH GRAVE
+0xA2	U+00C2	#	LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xA3	U+00C8	#	LATIN CAPITAL LETTER E WITH GRAVE
+0xA4	U+00CA	#	LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+0xA5	U+00CB	#	LATIN CAPITAL LETTER E WITH DIAERESIS
+0xA6	U+00CE	#	LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xA7	U+00CF	#	LATIN CAPITAL LETTER I WITH DIAERESIS
+0xA8	U+00B4	#	ACUTE ACCENT
+0xA9	U+02CB	#	MODIFIER LETTER GRAVE ACCENT
+0xAA	U+02C6	#	MODIFIER LETTER CIRCUMFLEX ACCENT
+0xAB	U+00A8	#	DIAERESIS
+0xAC	U+02DC	#	SMALL TILDE
+0xAD	U+00D9	#	LATIN CAPITAL LETTER U WITH GRAVE
+0xAE	U+00DB	#	LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+0xAF	U+20A4	#	LIRA SIGN
+0xB0	U+00AF	#	MACRON
+0xB1	U+00DD	#	LATIN CAPITAL LETTER Y WITH ACUTE
+0xB2	U+00FD	#	LATIN SMALL LETTER Y WITH ACUTE
+0xB3	U+00B0	#	DEGREE SIGN
+0xB4	U+00C7	#	LATIN CAPITAL LETTER C WITH CEDILLA
+0xB5	U+00E7	#	LATIN SMALL LETTER C WITH CEDILLA
+0xB6	U+00D1	#	LATIN CAPITAL LETTER N WITH TILDE
+0xB7	U+00F1	#	LATIN SMALL LETTER N WITH TILDE
+0xB8	U+00A1	#	INVERTED EXCLAMATION MARK
+0xB9	U+00BF	#	INVERTED QUESTION MARK
+0xBA	U+00A4	#	CURRENCY SIGN
+0xBB	U+00A3	#	POUND SIGN
+0xBC	U+00A5	#	YEN SIGN
+0xBD	U+00A7	#	SECTION SIGN
+0xBE	U+0192	#	LATIN SMALL LETTER F WITH HOOK
+0xBF	U+00A2	#	CENT SIGN
+0xC0	U+00E2	#	LATIN SMALL LETTER A WITH CIRCUMFLEX
+0xC1	U+00EA	#	LATIN SMALL LETTER E WITH CIRCUMFLEX
+0xC2	U+00F4	#	LATIN SMALL LETTER O WITH CIRCUMFLEX
+0xC3	U+00FB	#	LATIN SMALL LETTER U WITH CIRCUMFLEX
+0xC4	U+00E1	#	LATIN SMALL LETTER A WITH ACUTE
+0xC5	U+00E9	#	LATIN SMALL LETTER E WITH ACUTE
+0xC6	U+00F3	#	LATIN SMALL LETTER O WITH ACUTE
+0xC7	U+00FA	#	LATIN SMALL LETTER U WITH ACUTE
+0xC8	U+00E0	#	LATIN SMALL LETTER A WITH GRAVE
+0xC9	U+00E8	#	LATIN SMALL LETTER E WITH GRAVE
+0xCA	U+00F2	#	LATIN SMALL LETTER O WITH GRAVE
+0xCB	U+00F9	#	LATIN SMALL LETTER U WITH GRAVE
+0xCC	U+00E4	#	LATIN SMALL LETTER A WITH DIAERESIS
+0xCD	U+00EB	#	LATIN SMALL LETTER E WITH DIAERESIS
+0xCE	U+00F6	#	LATIN SMALL LETTER O WITH DIAERESIS
+0xCF	U+00FC	#	LATIN SMALL LETTER U WITH DIAERESIS
+0xD0	U+00C5	#	LATIN CAPITAL LETTER A WITH RING ABOVE
+0xD1	U+00EE	#	LATIN SMALL LETTER I WITH CIRCUMFLEX
+0xD2	U+00D8	#	LATIN CAPITAL LETTER O WITH STROKE
+0xD3	U+00C6	#	LATIN CAPITAL LETTER AE
+0xD4	U+00E5	#	LATIN SMALL LETTER A WITH RING ABOVE
+0xD5	U+00ED	#	LATIN SMALL LETTER I WITH ACUTE
+0xD6	U+00F8	#	LATIN SMALL LETTER O WITH STROKE
+0xD7	U+00E6	#	LATIN SMALL LETTER AE
+0xD8	U+00C4	#	LATIN CAPITAL LETTER A WITH DIAERESIS
+0xD9	U+00EC	#	LATIN SMALL LETTER I WITH GRAVE
+0xDA	U+00D6	#	LATIN CAPITAL LETTER O WITH DIAERESIS
+0xDB	U+00DC	#	LATIN CAPITAL LETTER U WITH DIAERESIS
+0xDC	U+00C9	#	LATIN CAPITAL LETTER E WITH ACUTE
+0xDD	U+00EF	#	LATIN SMALL LETTER I WITH DIAERESIS
+0xDE	U+00DF	#	LATIN SMALL LETTER SHARP S
+0xDF	U+00D4	#	LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xE0	U+00C1	#	LATIN CAPITAL LETTER A WITH ACUTE
+0xE1	U+00C3	#	LATIN CAPITAL LETTER A WITH TILDE
+0xE2	U+00E3	#	LATIN SMALL LETTER A WITH TILDE
+0xE3	U+00D0	#	LATIN CAPITAL LETTER ETH
+0xE4	U+00F0	#	LATIN SMALL LETTER ETH
+0xE5	U+00CD	#	LATIN CAPITAL LETTER I WITH ACUTE
+0xE6	U+00CC	#	LATIN CAPITAL LETTER I WITH GRAVE
+0xE7	U+00D3	#	LATIN CAPITAL LETTER O WITH ACUTE
+0xE8	U+00D2	#	LATIN CAPITAL LETTER O WITH GRAVE
+0xE9	U+00D5	#	LATIN CAPITAL LETTER O WITH TILDE
+0xEA	U+00F5	#	LATIN SMALL LETTER O WITH TILDE
+0xEB	U+0160	#	LATIN CAPITAL LETTER S WITH CARON
+0xEC	U+0161	#	LATIN SMALL LETTER S WITH CARON
+0xED	U+00DA	#	LATIN CAPITAL LETTER U WITH ACUTE
+0xEE	U+0178	#	LATIN CAPITAL LETTER Y WITH DIAERESIS
+0xEF	U+00FF	#	LATIN SMALL LETTER Y WITH DIAERESIS
+0xF0	U+00DE	#	LATIN CAPITAL LETTER THORN
+0xF1	U+00FE	#	LATIN SMALL LETTER THORN
+0xF2	U+00B7	#	MIDDLE DOT
+0xF3	U+00B5	#	MICRO SIGN
+0xF4	U+00B6	#	PILCROW SIGN
+0xF5	U+00BE	#	VULGAR FRACTION THREE QUARTERS
+0xF6	U+2014	#	EM DASH
+0xF7	U+00BC	#	VULGAR FRACTION ONE QUARTER
+0xF8	U+00BD	#	VULGAR FRACTION ONE HALF
+0xF9	U+00AA	#	FEMININE ORDINAL INDICATOR
+0xFA	U+00BA	#	MASCULINE ORDINAL INDICATOR
+0xFB	U+00AB	#	LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xFC	U+25A0	#	BLACK SQUARE
+0xFD	U+00BB	#	RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xFE	U+00B1	#	PLUS-MINUS SIGN
+
+## EOF ##
diff --git a/src/chrtrans/iso01_uni.tbl b/src/chrtrans/iso01_uni.tbl
new file mode 100644
index 00000000..95e705e2
--- /dev/null
+++ b/src/chrtrans/iso01_uni.tbl
@@ -0,0 +1,334 @@
+# $LynxId: iso01_uni.tbl,v 1.11 2007/07/31 20:35:04 Tim.Larson Exp $
+# vile:tblmode:
+# This file has been modified for lynx (see README.tables)
+
+#Shall this become the "default" translation?
+#Meaning of that is currently not well defined.  It is different
+#from the default input or default output charset...
+#but there has to be exactly one table marked as "default".
+D0
+#
+#The MIME name of this charset.
+Miso-8859-1
+
+#Name as a Display Charset (used on Options screen)
+OWestern (ISO-8859-1)
+
+#Codepage number
+C819
+
+#
+#	Name:             ISO/IEC 8859-1:1998 to Unicode
+#	Unicode version:  3.0
+#	Table version:    1.0
+#	Table format:     Format A
+#	Date:             1999 July 27
+#	Authors:          Ken Whistler <kenw@sybase.com>
+#
+#	Copyright (c) 1991-1999 Unicode, Inc.  All Rights reserved.
+#
+#	This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
+#	No claims are made as to fitness for any particular purpose.  No
+#	warranties of any kind are expressed or implied.  The recipient
+#	agrees to determine applicability of information provided.  If this
+#	file has been provided on optical media by Unicode, Inc., the sole
+#	remedy for any claim will be exchange of defective media within 90
+#	days of receipt.
+#
+#	Unicode, Inc. hereby grants the right to freely use the information
+#	supplied in this file in the creation of products supporting the
+#	Unicode Standard, and to make copies of this file in any form for
+#	internal or external distribution as long as this notice remains
+#	attached.
+#
+#	General notes:
+#
+#	This table contains the data the Unicode Consortium has on how
+#	ISO/IEC 8859-1:1998 characters map into Unicode.
+#
+#	Format:  Three tab-separated columns
+#		 Column #1 is the ISO/IEC 8859-1 code (in hex as 0xXX)
+#		 Column #2 is the Unicode (in hex as 0xXXXX)
+#		 Column #3 the Unicode name (follows a comment sign, '#')
+#
+#	The entries are in ISO/IEC 8859-1 order.
+#
+#	Version history
+#	1.0 version updates 0.1 version by adding mappings for all
+#	control characters.
+#
+#	Updated versions of this file may be found in:
+#		<ftp://ftp.unicode.org/Public/MAPPINGS/>
+#
+#	Any comments or problems, contact <errata@unicode.org>
+#	Please note that <errata@unicode.org> is an archival address;
+#	notices will be checked, but do not expect an immediate response.
+#
+# Lines with more than one Unicode (U+XXXX) value contain additional
+# replacement mappings added for lynx. - kw
+#
+0x20-0x7e idem
+0xa0-0xff idem  #  iso 8859-1 special: trivial mapping to Unicode
+#
+#0x20	U+0020	#	SPACE
+#0x21	U+0021	#	EXCLAMATION MARK
+#0x22	U+0022	#	QUOTATION MARK
+#0x23	U+0023	#	NUMBER SIGN
+#0x24	U+0024	#	DOLLAR SIGN
+#0x25	U+0025	#	PERCENT SIGN
+#0x26	U+0026	#	AMPERSAND
+#0x27	U+0027	#	APOSTROPHE
+#0x28	U+0028	#	LEFT PARENTHESIS
+#0x29	U+0029	#	RIGHT PARENTHESIS
+#0x2A	U+002A	#	ASTERISK
+#0x2B	U+002B	#	PLUS SIGN
+#0x2C	U+002C	#	COMMA
+#0x2D	U+002D	#	HYPHEN-MINUS
+#0x2E	U+002E	#	FULL STOP
+#0x2F	U+002F	#	SOLIDUS
+#0x30	U+0030	#	DIGIT ZERO
+#0x31	U+0031	#	DIGIT ONE
+#0x32	U+0032	#	DIGIT TWO
+#0x33	U+0033	#	DIGIT THREE
+#0x34	U+0034	#	DIGIT FOUR
+#0x35	U+0035	#	DIGIT FIVE
+#0x36	U+0036	#	DIGIT SIX
+#0x37	U+0037	#	DIGIT SEVEN
+#0x38	U+0038	#	DIGIT EIGHT
+#0x39	U+0039	#	DIGIT NINE
+#0x3A	U+003A	#	COLON
+#0x3B	U+003B	#	SEMICOLON
+#0x3C	U+003C	#	LESS-THAN SIGN
+#0x3D	U+003D	#	EQUALS SIGN
+#0x3E	U+003E	#	GREATER-THAN SIGN
+#0x3F	U+003F	#	QUESTION MARK
+#0x40	U+0040	#	COMMERCIAL AT
+#0x41	U+0041	#	LATIN CAPITAL LETTER A
+#0x42	U+0042	#	LATIN CAPITAL LETTER B
+#0x43	U+0043	#	LATIN CAPITAL LETTER C
+#0x44	U+0044	#	LATIN CAPITAL LETTER D
+#0x45	U+0045	#	LATIN CAPITAL LETTER E
+#0x46	U+0046	#	LATIN CAPITAL LETTER F
+#0x47	U+0047	#	LATIN CAPITAL LETTER G
+#0x48	U+0048	#	LATIN CAPITAL LETTER H
+#0x49	U+0049	#	LATIN CAPITAL LETTER I
+#0x4A	U+004A	#	LATIN CAPITAL LETTER J
+#0x4B	U+004B	#	LATIN CAPITAL LETTER K
+#0x4C	U+004C	#	LATIN CAPITAL LETTER L
+#0x4D	U+004D	#	LATIN CAPITAL LETTER M
+#0x4E	U+004E	#	LATIN CAPITAL LETTER N
+#0x4F	U+004F	#	LATIN CAPITAL LETTER O
+#0x50	U+0050	#	LATIN CAPITAL LETTER P
+#0x51	U+0051	#	LATIN CAPITAL LETTER Q
+#0x52	U+0052	#	LATIN CAPITAL LETTER R
+#0x53	U+0053	#	LATIN CAPITAL LETTER S
+#0x54	U+0054	#	LATIN CAPITAL LETTER T
+#0x55	U+0055	#	LATIN CAPITAL LETTER U
+#0x56	U+0056	#	LATIN CAPITAL LETTER V
+#0x57	U+0057	#	LATIN CAPITAL LETTER W
+#0x58	U+0058	#	LATIN CAPITAL LETTER X
+#0x59	U+0059	#	LATIN CAPITAL LETTER Y
+#0x5A	U+005A	#	LATIN CAPITAL LETTER Z
+#0x5B	U+005B	#	LEFT SQUARE BRACKET
+#0x5C	U+005C	#	REVERSE SOLIDUS
+#0x5D	U+005D	#	RIGHT SQUARE BRACKET
+#0x5E	U+005E	#	CIRCUMFLEX ACCENT
+#0x5F	U+005F	#	LOW LINE
+#0x60	U+0060	#	GRAVE ACCENT
+#0x61	U+0061	#	LATIN SMALL LETTER A
+#0x62	U+0062	#	LATIN SMALL LETTER B
+#0x63	U+0063	#	LATIN SMALL LETTER C
+#0x64	U+0064	#	LATIN SMALL LETTER D
+#0x65	U+0065	#	LATIN SMALL LETTER E
+#0x66	U+0066	#	LATIN SMALL LETTER F
+#0x67	U+0067	#	LATIN SMALL LETTER G
+#0x68	U+0068	#	LATIN SMALL LETTER H
+#0x69	U+0069	#	LATIN SMALL LETTER I
+#0x6A	U+006A	#	LATIN SMALL LETTER J
+#0x6B	U+006B	#	LATIN SMALL LETTER K
+#0x6C	U+006C	#	LATIN SMALL LETTER L
+#0x6D	U+006D	#	LATIN SMALL LETTER M
+#0x6E	U+006E	#	LATIN SMALL LETTER N
+#0x6F	U+006F	#	LATIN SMALL LETTER O
+#0x70	U+0070	#	LATIN SMALL LETTER P
+#0x71	U+0071	#	LATIN SMALL LETTER Q
+#0x72	U+0072	#	LATIN SMALL LETTER R
+#0x73	U+0073	#	LATIN SMALL LETTER S
+#0x74	U+0074	#	LATIN SMALL LETTER T
+#0x75	U+0075	#	LATIN SMALL LETTER U
+#0x76	U+0076	#	LATIN SMALL LETTER V
+#0x77	U+0077	#	LATIN SMALL LETTER W
+#0x78	U+0078	#	LATIN SMALL LETTER X
+#0x79	U+0079	#	LATIN SMALL LETTER Y
+#0x7A	U+007A	#	LATIN SMALL LETTER Z
+#0x7B	U+007B	#	LEFT CURLY BRACKET
+#0x7C	U+007C	#	VERTICAL LINE
+#0x7D	U+007D	#	RIGHT CURLY BRACKET
+#0x7E	U+007E	#	TILDE
+0xA0	U+00A0	U+2007	#	NO-BREAK SPACE
+#0xA1	U+00A1	#	INVERTED EXCLAMATION MARK
+#0xA2	U+00A2	#	CENT SIGN
+#0xA3	U+00A3	#	POUND SIGN
+#0xA4	U+00A4	#	CURRENCY SIGN
+#0xA5	U+00A5	#	YEN SIGN
+#0xA6	U+00A6	#	BROKEN BAR
+#0xA7	U+00A7	#	SECTION SIGN
+0xA8	U+00A8	U+0308	#	DIAERESIS
+#0xA9	U+00A9	#	COPYRIGHT SIGN
+#0xAA	U+00AA	#	FEMININE ORDINAL INDICATOR
+#0xAB	U+00AB	#	LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+#0xAC	U+00AC	#	NOT SIGN
+#0xAD	U+00AD	#	SOFT HYPHEN
+#0xAE	U+00AE	#	REGISTERED SIGN
+0xAF	U+00AF	U+0304	#	MACRON
+0xB0	U+00B0	U+030a	#	DEGREE SIGN
+#0xB1	U+00B1	#	PLUS-MINUS SIGN
+#0xB2	U+00B2	#	SUPERSCRIPT TWO
+#0xB3	U+00B3	#	SUPERSCRIPT THREE
+#0xB4	U+00B4	#	ACUTE ACCENT
+0xB5	U+00B5	U+03bc	#	MICRO SIGN
+#0xB6	U+00B6	#	PILCROW SIGN
+0xB7	U+00B7	U+0307	U+0387	U+2027	#	MIDDLE DOT
+0xB8	U+00B8	U+0327	#	CEDILLA
+#0xB9	U+00B9	#	SUPERSCRIPT ONE
+#0xBA	U+00BA	#	MASCULINE ORDINAL INDICATOR
+#0xBB	U+00BB	#	RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+#0xBC	U+00BC	#	VULGAR FRACTION ONE QUARTER
+#0xBD	U+00BD	#	VULGAR FRACTION ONE HALF
+#0xBE	U+00BE	#	VULGAR FRACTION THREE QUARTERS
+#0xBF	U+00BF	#	INVERTED QUESTION MARK
+#0xC0	U+00C0	#	LATIN CAPITAL LETTER A WITH GRAVE
+#0xC1	U+00C1	#	LATIN CAPITAL LETTER A WITH ACUTE
+#0xC2	U+00C2	#	LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+#0xC3	U+00C3	#	LATIN CAPITAL LETTER A WITH TILDE
+#0xC4	U+00C4	#	LATIN CAPITAL LETTER A WITH DIAERESIS
+#0xC5	U+00C5	#	LATIN CAPITAL LETTER A WITH RING ABOVE
+#0xC6	U+00C6	#	LATIN CAPITAL LETTER AE
+#0xC7	U+00C7	#	LATIN CAPITAL LETTER C WITH CEDILLA
+#0xC8	U+00C8	#	LATIN CAPITAL LETTER E WITH GRAVE
+#0xC9	U+00C9	#	LATIN CAPITAL LETTER E WITH ACUTE
+#0xCA	U+00CA	#	LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+#0xCB	U+00CB	#	LATIN CAPITAL LETTER E WITH DIAERESIS
+#0xCC	U+00CC	#	LATIN CAPITAL LETTER I WITH GRAVE
+#0xCD	U+00CD	#	LATIN CAPITAL LETTER I WITH ACUTE
+#0xCE	U+00CE	#	LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+#0xCF	U+00CF	#	LATIN CAPITAL LETTER I WITH DIAERESIS
+#0xD0	U+00D0	#	LATIN CAPITAL LETTER ETH (Icelandic)
+#0xD1	U+00D1	#	LATIN CAPITAL LETTER N WITH TILDE
+#0xD2	U+00D2	#	LATIN CAPITAL LETTER O WITH GRAVE
+#0xD3	U+00D3	#	LATIN CAPITAL LETTER O WITH ACUTE
+#0xD4	U+00D4	#	LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+#0xD5	U+00D5	#	LATIN CAPITAL LETTER O WITH TILDE
+#0xD6	U+00D6	#	LATIN CAPITAL LETTER O WITH DIAERESIS
+#0xD7	U+00D7	#	MULTIPLICATION SIGN
+#0xD8	U+00D8	#	LATIN CAPITAL LETTER O WITH STROKE
+#0xD9	U+00D9	#	LATIN CAPITAL LETTER U WITH GRAVE
+#0xDA	U+00DA	#	LATIN CAPITAL LETTER U WITH ACUTE
+#0xDB	U+00DB	#	LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+#0xDC	U+00DC	#	LATIN CAPITAL LETTER U WITH DIAERESIS
+#0xDD	U+00DD	#	LATIN CAPITAL LETTER Y WITH ACUTE
+#0xDE	U+00DE	#	LATIN CAPITAL LETTER THORN (Icelandic)
+#0xDF	U+00DF	#	LATIN SMALL LETTER SHARP S (German)
+#0xE0	U+00E0	#	LATIN SMALL LETTER A WITH GRAVE
+#0xE1	U+00E1	#	LATIN SMALL LETTER A WITH ACUTE
+#0xE2	U+00E2	#	LATIN SMALL LETTER A WITH CIRCUMFLEX
+#0xE3	U+00E3	#	LATIN SMALL LETTER A WITH TILDE
+#0xE4	U+00E4	#	LATIN SMALL LETTER A WITH DIAERESIS
+#0xE5	U+00E5	#	LATIN SMALL LETTER A WITH RING ABOVE
+#0xE6	U+00E6	#	LATIN SMALL LETTER AE
+#0xE7	U+00E7	#	LATIN SMALL LETTER C WITH CEDILLA
+#0xE8	U+00E8	#	LATIN SMALL LETTER E WITH GRAVE
+#0xE9	U+00E9	#	LATIN SMALL LETTER E WITH ACUTE
+#0xEA	U+00EA	#	LATIN SMALL LETTER E WITH CIRCUMFLEX
+#0xEB	U+00EB	#	LATIN SMALL LETTER E WITH DIAERESIS
+#0xEC	U+00EC	#	LATIN SMALL LETTER I WITH GRAVE
+#0xED	U+00ED	#	LATIN SMALL LETTER I WITH ACUTE
+#0xEE	U+00EE	#	LATIN SMALL LETTER I WITH CIRCUMFLEX
+#0xEF	U+00EF	#	LATIN SMALL LETTER I WITH DIAERESIS
+#0xF0	U+00F0	#	LATIN SMALL LETTER ETH (Icelandic)
+#0xF1	U+00F1	#	LATIN SMALL LETTER N WITH TILDE
+#0xF2	U+00F2	#	LATIN SMALL LETTER O WITH GRAVE
+#0xF3	U+00F3	#	LATIN SMALL LETTER O WITH ACUTE
+#0xF4	U+00F4	#	LATIN SMALL LETTER O WITH CIRCUMFLEX
+#0xF5	U+00F5	#	LATIN SMALL LETTER O WITH TILDE
+#0xF6	U+00F6	#	LATIN SMALL LETTER O WITH DIAERESIS
+#0xF7	U+00F7	#	DIVISION SIGN
+#0xF8	U+00F8	#	LATIN SMALL LETTER O WITH STROKE
+#0xF9	U+00F9	#	LATIN SMALL LETTER U WITH GRAVE
+#0xFA	U+00FA	#	LATIN SMALL LETTER U WITH ACUTE
+#0xFB	U+00FB	#	LATIN SMALL LETTER U WITH CIRCUMFLEX
+#0xFC	U+00FC	#	LATIN SMALL LETTER U WITH DIAERESIS
+#0xFD	U+00FD	#	LATIN SMALL LETTER Y WITH ACUTE
+#0xFE	U+00FE	#	LATIN SMALL LETTER THORN (Icelandic)
+#0xFF	U+00FF	#	LATIN SMALL LETTER Y WITH DIAERESIS
+
+
+0xd0	U+0110	# Dstrok and ETH are nearly the same...
+
+U+219e "\253-"
+U+21a0 "-\273"
+U+21ab "<-\260"
+U+21ac "\260->"
+U+21b4 "\254v"
+U+21b8 "\257^\134"
+U+21c7 "\253="
+U+21c9 "=\273"
+U+21e0 "<\267\267"
+U+21e2 "\267\267>"
+U+21f1 "|\257^\134"
+U+21f6 "=-\273>"
+U+2218 " \260 "		# RING OPERATOR
+U+221b " ROOT\263 "
+U+2297 "(\327)"		# CIRCLED TIMES
+U+2299 "(\267)"		# CIRCLED DOT OPERATOR
+U+229A "(\260)"		# CIRCLED RING OPERATOR
+U+22A0 "[\327]"		# SQUARED TIMES
+U+22A1 "[\267]"		# SQUARED DOT OPERATOR
+U+22C5 " \267 "		# DOT OPERATOR
+U+2603 "\2508\250"
+U+2609 "(\267)"
+U+2614 "\250J\250"
+U+262d "\264\134,)"
+U+266b "d\257d"
+U+2686 "(\267)"
+U+2688 "((\267))"
+U+2692 "\264X`"
+U+2694 ",\327,"
+U+2697 "\360"
+U+2698 "\316"
+U+2699 "\244"
+U+269b ":\244:"
+U+2701 "8\264"
+U+270c "mV\270"
+U+2721 "\244"
+U+273f "\244"
+U+2740 "\244"
+U+2741 "\244"
+U+2761 "\266"
+U+279f "\267->"
+U+27a0 "\267->"
+U+27a7 "\267>"
+U+2900 "-|-\273"
+U+2901 "-||-\273"
+U+2905 "|-\273"
+U+290c "<-\267"
+U+290d "\267->"
+U+290e "<-\267\267"
+U+290f "\267\267->"
+U+2910 ">\267\267-\273"
+U+2911 "\267\267>"
+U+2912 "\257^|"
+U+2916 ">-\273"
+U+2917 ">-|-\273"
+U+2918 ">-||-\273"
+U+291b "-\253"
+U+291c "\273-"
+U+2923 "^\134\270"
+U+2924 "\270/^"
+U+2942 "-><\267"
+U+2943 "<-\267>"
+U+2944 "\267><-"
+U+2954 "\257^|"
+U+2958 "\257^|"
+U+295d "\257|v"
+U+2961 "\257|v"
diff --git a/src/chrtrans/iso02_uni.tbl b/src/chrtrans/iso02_uni.tbl
new file mode 100644
index 00000000..fe3fc718
--- /dev/null
+++ b/src/chrtrans/iso02_uni.tbl
@@ -0,0 +1,265 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Miso-8859-2
+
+#Name as a Display Charset (used on Options screen)
+OEastern European (ISO-8859-2)
+
+#Codepage number
+C912
+
+#
+#	Name:             ISO 8859-2:1999 to Unicode
+#	Unicode version:  3.0
+#	Table version:    1.0
+#	Table format:     Format A
+#	Date:             1999 July 27
+#	Authors:          Ken Whistler <kenw@sybase.com>
+#
+#	Copyright (c) 1991-1999 Unicode, Inc.  All Rights reserved.
+#
+#	This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
+#	No claims are made as to fitness for any particular purpose.  No
+#	warranties of any kind are expressed or implied.  The recipient
+#	agrees to determine applicability of information provided.  If this
+#	file has been provided on optical media by Unicode, Inc., the sole
+#	remedy for any claim will be exchange of defective media within 90
+#	days of receipt.
+#
+#	Unicode, Inc. hereby grants the right to freely use the information
+#	supplied in this file in the creation of products supporting the
+#	Unicode Standard, and to make copies of this file in any form for
+#	internal or external distribution as long as this notice remains
+#	attached.
+#
+#	General notes:
+#
+#	This table contains the data the Unicode Consortium has on how
+#	ISO/IEC 8859-2:1999 characters map into Unicode.
+#
+#	Format:  Three tab-separated columns
+#		 Column #1 is the ISO/IEC 8859-2 code (in hex as 0xXX)
+#		 Column #2 is the Unicode (in hex as 0xXXXX)
+#		 Column #3 the Unicode name (follows a comment sign, '#')
+#
+#	The entries are in ISO/IEC 8859-2 order.
+#
+#	Version history
+#	1.0 version updates 0.1 version by adding mappings for all
+#	control characters.
+#
+#	Updated versions of this file may be found in:
+#		<ftp://ftp.unicode.org/Public/MAPPINGS/>
+#
+#	Any comments or problems, contact <errata@unicode.org>
+#	Please note that <errata@unicode.org> is an archival address;
+#	notices will be checked, but do not expect an immediate response.
+#
+# Lines with more than one Unicode (U+XXXX) value contain additional
+# replacement mappings added for lynx. - kw
+#
+0x20-0x7e idem
+#
+#0x20	U+0020	#	SPACE
+#0x21	U+0021	#	EXCLAMATION MARK
+#0x22	U+0022	#	QUOTATION MARK
+#0x23	U+0023	#	NUMBER SIGN
+#0x24	U+0024	#	DOLLAR SIGN
+#0x25	U+0025	#	PERCENT SIGN
+#0x26	U+0026	#	AMPERSAND
+#0x27	U+0027	#	APOSTROPHE
+#0x28	U+0028	#	LEFT PARENTHESIS
+#0x29	U+0029	#	RIGHT PARENTHESIS
+#0x2A	U+002A	#	ASTERISK
+#0x2B	U+002B	#	PLUS SIGN
+#0x2C	U+002C	#	COMMA
+#0x2D	U+002D	#	HYPHEN-MINUS
+#0x2E	U+002E	#	FULL STOP
+#0x2F	U+002F	#	SOLIDUS
+#0x30	U+0030	#	DIGIT ZERO
+#0x31	U+0031	#	DIGIT ONE
+#0x32	U+0032	#	DIGIT TWO
+#0x33	U+0033	#	DIGIT THREE
+#0x34	U+0034	#	DIGIT FOUR
+#0x35	U+0035	#	DIGIT FIVE
+#0x36	U+0036	#	DIGIT SIX
+#0x37	U+0037	#	DIGIT SEVEN
+#0x38	U+0038	#	DIGIT EIGHT
+#0x39	U+0039	#	DIGIT NINE
+#0x3A	U+003A	#	COLON
+#0x3B	U+003B	#	SEMICOLON
+#0x3C	U+003C	#	LESS-THAN SIGN
+#0x3D	U+003D	#	EQUALS SIGN
+#0x3E	U+003E	#	GREATER-THAN SIGN
+#0x3F	U+003F	#	QUESTION MARK
+#0x40	U+0040	#	COMMERCIAL AT
+#0x41	U+0041	#	LATIN CAPITAL LETTER A
+#0x42	U+0042	#	LATIN CAPITAL LETTER B
+#0x43	U+0043	#	LATIN CAPITAL LETTER C
+#0x44	U+0044	#	LATIN CAPITAL LETTER D
+#0x45	U+0045	#	LATIN CAPITAL LETTER E
+#0x46	U+0046	#	LATIN CAPITAL LETTER F
+#0x47	U+0047	#	LATIN CAPITAL LETTER G
+#0x48	U+0048	#	LATIN CAPITAL LETTER H
+#0x49	U+0049	#	LATIN CAPITAL LETTER I
+#0x4A	U+004A	#	LATIN CAPITAL LETTER J
+#0x4B	U+004B	#	LATIN CAPITAL LETTER K
+#0x4C	U+004C	#	LATIN CAPITAL LETTER L
+#0x4D	U+004D	#	LATIN CAPITAL LETTER M
+#0x4E	U+004E	#	LATIN CAPITAL LETTER N
+#0x4F	U+004F	#	LATIN CAPITAL LETTER O
+#0x50	U+0050	#	LATIN CAPITAL LETTER P
+#0x51	U+0051	#	LATIN CAPITAL LETTER Q
+#0x52	U+0052	#	LATIN CAPITAL LETTER R
+#0x53	U+0053	#	LATIN CAPITAL LETTER S
+#0x54	U+0054	#	LATIN CAPITAL LETTER T
+#0x55	U+0055	#	LATIN CAPITAL LETTER U
+#0x56	U+0056	#	LATIN CAPITAL LETTER V
+#0x57	U+0057	#	LATIN CAPITAL LETTER W
+#0x58	U+0058	#	LATIN CAPITAL LETTER X
+#0x59	U+0059	#	LATIN CAPITAL LETTER Y
+#0x5A	U+005A	#	LATIN CAPITAL LETTER Z
+#0x5B	U+005B	#	LEFT SQUARE BRACKET
+#0x5C	U+005C	#	REVERSE SOLIDUS
+#0x5D	U+005D	#	RIGHT SQUARE BRACKET
+#0x5E	U+005E	#	CIRCUMFLEX ACCENT
+#0x5F	U+005F	#	LOW LINE
+#0x60	U+0060	#	GRAVE ACCENT
+#0x61	U+0061	#	LATIN SMALL LETTER A
+#0x62	U+0062	#	LATIN SMALL LETTER B
+#0x63	U+0063	#	LATIN SMALL LETTER C
+#0x64	U+0064	#	LATIN SMALL LETTER D
+#0x65	U+0065	#	LATIN SMALL LETTER E
+#0x66	U+0066	#	LATIN SMALL LETTER F
+#0x67	U+0067	#	LATIN SMALL LETTER G
+#0x68	U+0068	#	LATIN SMALL LETTER H
+#0x69	U+0069	#	LATIN SMALL LETTER I
+#0x6A	U+006A	#	LATIN SMALL LETTER J
+#0x6B	U+006B	#	LATIN SMALL LETTER K
+#0x6C	U+006C	#	LATIN SMALL LETTER L
+#0x6D	U+006D	#	LATIN SMALL LETTER M
+#0x6E	U+006E	#	LATIN SMALL LETTER N
+#0x6F	U+006F	#	LATIN SMALL LETTER O
+#0x70	U+0070	#	LATIN SMALL LETTER P
+#0x71	U+0071	#	LATIN SMALL LETTER Q
+#0x72	U+0072	#	LATIN SMALL LETTER R
+#0x73	U+0073	#	LATIN SMALL LETTER S
+#0x74	U+0074	#	LATIN SMALL LETTER T
+#0x75	U+0075	#	LATIN SMALL LETTER U
+#0x76	U+0076	#	LATIN SMALL LETTER V
+#0x77	U+0077	#	LATIN SMALL LETTER W
+#0x78	U+0078	#	LATIN SMALL LETTER X
+#0x79	U+0079	#	LATIN SMALL LETTER Y
+#0x7A	U+007A	#	LATIN SMALL LETTER Z
+#0x7B	U+007B	#	LEFT CURLY BRACKET
+#0x7C	U+007C	#	VERTICAL LINE
+#0x7D	U+007D	#	RIGHT CURLY BRACKET
+#0x7E	U+007E	#	TILDE
+0xA0	U+00A0	#	NO-BREAK SPACE
+0xA1	U+0104	#	LATIN CAPITAL LETTER A WITH OGONEK
+0xA2	U+02D8	U+0306	#	BREVE
+0xA3	U+0141	#	LATIN CAPITAL LETTER L WITH STROKE
+0xA4	U+00A4	#	CURRENCY SIGN
+0xA5	U+013D	#	LATIN CAPITAL LETTER L WITH CARON
+0xA6	U+015A	#	LATIN CAPITAL LETTER S WITH ACUTE
+0xA7	U+00A7	#	SECTION SIGN
+0xA8	U+00A8	U+0308	#	DIAERESIS
+0xA9	U+0160	U+0428	#	LATIN CAPITAL LETTER S WITH CARON
+0xAA	U+015E	#	LATIN CAPITAL LETTER S WITH CEDILLA
+0xAB	U+0164	#	LATIN CAPITAL LETTER T WITH CARON
+0xAC	U+0179	#	LATIN CAPITAL LETTER Z WITH ACUTE
+0xAD	U+00AD	#	SOFT HYPHEN
+0xAE	U+017D	U+0416	#	LATIN CAPITAL LETTER Z WITH CARON
+0xAF	U+017B	#	LATIN CAPITAL LETTER Z WITH DOT ABOVE
+0xB0	U+00B0	U+030a	#	DEGREE SIGN
+0xB1	U+0105	#	LATIN SMALL LETTER A WITH OGONEK
+0xB2	U+02DB	U+0328	#	OGONEK
+0xB3	U+0142	#	LATIN SMALL LETTER L WITH STROKE
+0xB4	U+00B4	#	ACUTE ACCENT
+0xB5	U+013E	#	LATIN SMALL LETTER L WITH CARON
+0xB6	U+015B	#	LATIN SMALL LETTER S WITH ACUTE
+0xB7	U+02C7	U+030c	#	CARON
+0xB8	U+00B8	U+0327	#	CEDILLA
+0xB9	U+0161	U+0448	#	LATIN SMALL LETTER S WITH CARON
+0xBA	U+015F	#	LATIN SMALL LETTER S WITH CEDILLA
+0xBB	U+0165	#	LATIN SMALL LETTER T WITH CARON
+0xBC	U+017A	#	LATIN SMALL LETTER Z WITH ACUTE
+0xBD	U+02DD	U+030b	#	DOUBLE ACUTE ACCENT
+0xBE	U+017E	U+0436	#	LATIN SMALL LETTER Z WITH CARON
+0xBF	U+017C	#	LATIN SMALL LETTER Z WITH DOT ABOVE
+0xC0	U+0154	#	LATIN CAPITAL LETTER R WITH ACUTE
+0xC1	U+00C1	#	LATIN CAPITAL LETTER A WITH ACUTE
+0xC2	U+00C2	#	LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xC3	U+0102	#	LATIN CAPITAL LETTER A WITH BREVE
+0xC4	U+00C4	#	LATIN CAPITAL LETTER A WITH DIAERESIS
+0xC5	U+0139	#	LATIN CAPITAL LETTER L WITH ACUTE
+0xC6	U+0106	#	LATIN CAPITAL LETTER C WITH ACUTE
+0xC7	U+00C7	#	LATIN CAPITAL LETTER C WITH CEDILLA
+0xC8	U+010C	U+0427	#	LATIN CAPITAL LETTER C WITH CARON
+0xC9	U+00C9	#	LATIN CAPITAL LETTER E WITH ACUTE
+0xCA	U+0118	#	LATIN CAPITAL LETTER E WITH OGONEK
+0xCB	U+00CB	#	LATIN CAPITAL LETTER E WITH DIAERESIS
+0xCC	U+011A	#	LATIN CAPITAL LETTER E WITH CARON
+0xCD	U+00CD	#	LATIN CAPITAL LETTER I WITH ACUTE
+0xCE	U+00CE	#	LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xCF	U+010E	#	LATIN CAPITAL LETTER D WITH CARON
+0xD0	U+0110	#	LATIN CAPITAL LETTER D WITH STROKE
+0xD1	U+0143	#	LATIN CAPITAL LETTER N WITH ACUTE
+0xD2	U+0147	#	LATIN CAPITAL LETTER N WITH CARON
+0xD3	U+00D3	#	LATIN CAPITAL LETTER O WITH ACUTE
+0xD4	U+00D4	#	LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xD5	U+0150	#	LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
+0xD6	U+00D6	#	LATIN CAPITAL LETTER O WITH DIAERESIS
+0xD7	U+00D7	U+00b7	#	MULTIPLICATION SIGN
+0xD8	U+0158	#	LATIN CAPITAL LETTER R WITH CARON
+0xD9	U+016E	#	LATIN CAPITAL LETTER U WITH RING ABOVE
+0xDA	U+00DA	#	LATIN CAPITAL LETTER U WITH ACUTE
+0xDB	U+0170	#	LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
+0xDC	U+00DC	#	LATIN CAPITAL LETTER U WITH DIAERESIS
+0xDD	U+00DD	#	LATIN CAPITAL LETTER Y WITH ACUTE
+0xDE	U+0162	#	LATIN CAPITAL LETTER T WITH CEDILLA
+0xDF	U+00DF	#	LATIN SMALL LETTER SHARP S
+0xE0	U+0155	#	LATIN SMALL LETTER R WITH ACUTE
+0xE1	U+00E1	#	LATIN SMALL LETTER A WITH ACUTE
+0xE2	U+00E2	#	LATIN SMALL LETTER A WITH CIRCUMFLEX
+0xE3	U+0103	#	LATIN SMALL LETTER A WITH BREVE
+0xE4	U+00E4	#	LATIN SMALL LETTER A WITH DIAERESIS
+0xE5	U+013A	#	LATIN SMALL LETTER L WITH ACUTE
+0xE6	U+0107	#	LATIN SMALL LETTER C WITH ACUTE
+0xE7	U+00E7	#	LATIN SMALL LETTER C WITH CEDILLA
+0xE8	U+010D	U+02a7	U+0447	#	LATIN SMALL LETTER C WITH CARON
+0xE9	U+00E9	#	LATIN SMALL LETTER E WITH ACUTE
+0xEA	U+0119	#	LATIN SMALL LETTER E WITH OGONEK
+0xEB	U+00EB	#	LATIN SMALL LETTER E WITH DIAERESIS
+0xEC	U+011B	#	LATIN SMALL LETTER E WITH CARON
+0xED	U+00ED	#	LATIN SMALL LETTER I WITH ACUTE
+0xEE	U+00EE	#	LATIN SMALL LETTER I WITH CIRCUMFLEX
+0xEF	U+010F	#	LATIN SMALL LETTER D WITH CARON
+0xF0	U+0111	#	LATIN SMALL LETTER D WITH STROKE
+0xF1	U+0144	#	LATIN SMALL LETTER N WITH ACUTE
+0xF2	U+0148	#	LATIN SMALL LETTER N WITH CARON
+0xF3	U+00F3	#	LATIN SMALL LETTER O WITH ACUTE
+0xF4	U+00F4	#	LATIN SMALL LETTER O WITH CIRCUMFLEX
+0xF5	U+0151	#	LATIN SMALL LETTER O WITH DOUBLE ACUTE
+0xF6	U+00F6	#	LATIN SMALL LETTER O WITH DIAERESIS
+0xF7	U+00F7	#	DIVISION SIGN
+0xF8	U+0159	#	LATIN SMALL LETTER R WITH CARON
+0xF9	U+016F	#	LATIN SMALL LETTER U WITH RING ABOVE
+0xFA	U+00FA	#	LATIN SMALL LETTER U WITH ACUTE
+0xFB	U+0171	#	LATIN SMALL LETTER U WITH DOUBLE ACUTE
+0xFC	U+00FC	#	LATIN SMALL LETTER U WITH DIAERESIS
+0xFD	U+00FD	#	LATIN SMALL LETTER Y WITH ACUTE
+0xFE	U+0163	#	LATIN SMALL LETTER T WITH CEDILLA
+0xFF	U+02D9	U+0307	U+0387	#	DOT ABOVE
+
+
+0xd0	U+00d0	# Dstrok and ETH are nearly the same...
+
+U+2218 " \260 "		# RING OPERATOR
+U+2297 "(\327)"		# CIRCLED TIMES
+U+2299 "(\377)"		# CIRCLED DOT OPERATOR
+U+229A "(\260)"		# CIRCLED RING OPERATOR
+U+22A0 "[\327]"		# SQUARED TIMES
+U+22A1 "[\377]"		# SQUARED DOT OPERATOR
+U+22C5 " \377 "		# DOT OPERATOR
diff --git a/src/chrtrans/iso03_uni.tbl b/src/chrtrans/iso03_uni.tbl
new file mode 100644
index 00000000..aafce8dc
--- /dev/null
+++ b/src/chrtrans/iso03_uni.tbl
@@ -0,0 +1,255 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Miso-8859-3
+
+#Name as a Display Charset (used on Options screen)
+OLatin 3 (ISO-8859-3)
+
+#Codepage number
+C913
+
+#
+#	Name:             ISO/IEC 8859-3:1999 to Unicode
+#	Unicode version:  3.0
+#	Table version:    1.0
+#	Table format:     Format A
+#	Date:             1999 July 27
+#	Authors:          Ken Whistler <kenw@sybase.com>
+#
+#	Copyright (c) 1991-1999 Unicode, Inc.  All Rights reserved.
+#
+#	This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
+#	No claims are made as to fitness for any particular purpose.  No
+#	warranties of any kind are expressed or implied.  The recipient
+#	agrees to determine applicability of information provided.  If this
+#	file has been provided on optical media by Unicode, Inc., the sole
+#	remedy for any claim will be exchange of defective media within 90
+#	days of receipt.
+#
+#	Unicode, Inc. hereby grants the right to freely use the information
+#	supplied in this file in the creation of products supporting the
+#	Unicode Standard, and to make copies of this file in any form for
+#	internal or external distribution as long as this notice remains
+#	attached.
+#
+#	General notes:
+#
+#	This table contains the data the Unicode Consortium has on how
+#	ISO/IEC 8859-3:1999 characters map into Unicode.
+#
+#	Format:  Three tab-separated columns
+#		 Column #1 is the ISO/IEC 8859-3 code (in hex as 0xXX)
+#		 Column #2 is the Unicode (in hex as 0xXXXX)
+#		 Column #3 the Unicode name (follows a comment sign, '#')
+#
+#	The entries are in ISO/IEC 8859-3 order.
+#
+#	Version history
+#	1.0 version updates 0.1 version by adding mappings for all
+#	control characters.
+#
+#	Updated versions of this file may be found in:
+#		<ftp://ftp.unicode.org/Public/MAPPINGS/>
+#
+#	Any comments or problems, contact <errata@unicode.org>
+#	Please note that <errata@unicode.org> is an archival address;
+#	notices will be checked, but do not expect an immediate response.
+#
+0x20-0x7e idem
+#
+#0x20	U+0020	#	SPACE
+#0x21	U+0021	#	EXCLAMATION MARK
+#0x22	U+0022	#	QUOTATION MARK
+#0x23	U+0023	#	NUMBER SIGN
+#0x24	U+0024	#	DOLLAR SIGN
+#0x25	U+0025	#	PERCENT SIGN
+#0x26	U+0026	#	AMPERSAND
+#0x27	U+0027	#	APOSTROPHE
+#0x28	U+0028	#	LEFT PARENTHESIS
+#0x29	U+0029	#	RIGHT PARENTHESIS
+#0x2A	U+002A	#	ASTERISK
+#0x2B	U+002B	#	PLUS SIGN
+#0x2C	U+002C	#	COMMA
+#0x2D	U+002D	#	HYPHEN-MINUS
+#0x2E	U+002E	#	FULL STOP
+#0x2F	U+002F	#	SOLIDUS
+#0x30	U+0030	#	DIGIT ZERO
+#0x31	U+0031	#	DIGIT ONE
+#0x32	U+0032	#	DIGIT TWO
+#0x33	U+0033	#	DIGIT THREE
+#0x34	U+0034	#	DIGIT FOUR
+#0x35	U+0035	#	DIGIT FIVE
+#0x36	U+0036	#	DIGIT SIX
+#0x37	U+0037	#	DIGIT SEVEN
+#0x38	U+0038	#	DIGIT EIGHT
+#0x39	U+0039	#	DIGIT NINE
+#0x3A	U+003A	#	COLON
+#0x3B	U+003B	#	SEMICOLON
+#0x3C	U+003C	#	LESS-THAN SIGN
+#0x3D	U+003D	#	EQUALS SIGN
+#0x3E	U+003E	#	GREATER-THAN SIGN
+#0x3F	U+003F	#	QUESTION MARK
+#0x40	U+0040	#	COMMERCIAL AT
+#0x41	U+0041	#	LATIN CAPITAL LETTER A
+#0x42	U+0042	#	LATIN CAPITAL LETTER B
+#0x43	U+0043	#	LATIN CAPITAL LETTER C
+#0x44	U+0044	#	LATIN CAPITAL LETTER D
+#0x45	U+0045	#	LATIN CAPITAL LETTER E
+#0x46	U+0046	#	LATIN CAPITAL LETTER F
+#0x47	U+0047	#	LATIN CAPITAL LETTER G
+#0x48	U+0048	#	LATIN CAPITAL LETTER H
+#0x49	U+0049	#	LATIN CAPITAL LETTER I
+#0x4A	U+004A	#	LATIN CAPITAL LETTER J
+#0x4B	U+004B	#	LATIN CAPITAL LETTER K
+#0x4C	U+004C	#	LATIN CAPITAL LETTER L
+#0x4D	U+004D	#	LATIN CAPITAL LETTER M
+#0x4E	U+004E	#	LATIN CAPITAL LETTER N
+#0x4F	U+004F	#	LATIN CAPITAL LETTER O
+#0x50	U+0050	#	LATIN CAPITAL LETTER P
+#0x51	U+0051	#	LATIN CAPITAL LETTER Q
+#0x52	U+0052	#	LATIN CAPITAL LETTER R
+#0x53	U+0053	#	LATIN CAPITAL LETTER S
+#0x54	U+0054	#	LATIN CAPITAL LETTER T
+#0x55	U+0055	#	LATIN CAPITAL LETTER U
+#0x56	U+0056	#	LATIN CAPITAL LETTER V
+#0x57	U+0057	#	LATIN CAPITAL LETTER W
+#0x58	U+0058	#	LATIN CAPITAL LETTER X
+#0x59	U+0059	#	LATIN CAPITAL LETTER Y
+#0x5A	U+005A	#	LATIN CAPITAL LETTER Z
+#0x5B	U+005B	#	LEFT SQUARE BRACKET
+#0x5C	U+005C	#	REVERSE SOLIDUS
+#0x5D	U+005D	#	RIGHT SQUARE BRACKET
+#0x5E	U+005E	#	CIRCUMFLEX ACCENT
+#0x5F	U+005F	#	LOW LINE
+#0x60	U+0060	#	GRAVE ACCENT
+#0x61	U+0061	#	LATIN SMALL LETTER A
+#0x62	U+0062	#	LATIN SMALL LETTER B
+#0x63	U+0063	#	LATIN SMALL LETTER C
+#0x64	U+0064	#	LATIN SMALL LETTER D
+#0x65	U+0065	#	LATIN SMALL LETTER E
+#0x66	U+0066	#	LATIN SMALL LETTER F
+#0x67	U+0067	#	LATIN SMALL LETTER G
+#0x68	U+0068	#	LATIN SMALL LETTER H
+#0x69	U+0069	#	LATIN SMALL LETTER I
+#0x6A	U+006A	#	LATIN SMALL LETTER J
+#0x6B	U+006B	#	LATIN SMALL LETTER K
+#0x6C	U+006C	#	LATIN SMALL LETTER L
+#0x6D	U+006D	#	LATIN SMALL LETTER M
+#0x6E	U+006E	#	LATIN SMALL LETTER N
+#0x6F	U+006F	#	LATIN SMALL LETTER O
+#0x70	U+0070	#	LATIN SMALL LETTER P
+#0x71	U+0071	#	LATIN SMALL LETTER Q
+#0x72	U+0072	#	LATIN SMALL LETTER R
+#0x73	U+0073	#	LATIN SMALL LETTER S
+#0x74	U+0074	#	LATIN SMALL LETTER T
+#0x75	U+0075	#	LATIN SMALL LETTER U
+#0x76	U+0076	#	LATIN SMALL LETTER V
+#0x77	U+0077	#	LATIN SMALL LETTER W
+#0x78	U+0078	#	LATIN SMALL LETTER X
+#0x79	U+0079	#	LATIN SMALL LETTER Y
+#0x7A	U+007A	#	LATIN SMALL LETTER Z
+#0x7B	U+007B	#	LEFT CURLY BRACKET
+#0x7C	U+007C	#	VERTICAL LINE
+#0x7D	U+007D	#	RIGHT CURLY BRACKET
+#0x7E	U+007E	#	TILDE
+0xA0	U+00A0	#	NO-BREAK SPACE
+0xA1	U+0126	#	LATIN CAPITAL LETTER H WITH STROKE
+0xA2	U+02D8	#	BREVE
+0xA3	U+00A3	#	POUND SIGN
+0xA4	U+00A4	#	CURRENCY SIGN
+0xA6	U+0124	#	LATIN CAPITAL LETTER H WITH CIRCUMFLEX
+0xA7	U+00A7	#	SECTION SIGN
+0xA8	U+00A8	#	DIAERESIS
+0xA9	U+0130	#	LATIN CAPITAL LETTER I WITH DOT ABOVE
+0xAA	U+015E	#	LATIN CAPITAL LETTER S WITH CEDILLA
+0xAB	U+011E	#	LATIN CAPITAL LETTER G WITH BREVE
+0xAC	U+0134	#	LATIN CAPITAL LETTER J WITH CIRCUMFLEX
+0xAD	U+00AD	#	SOFT HYPHEN
+0xAF	U+017B	#	LATIN CAPITAL LETTER Z WITH DOT ABOVE
+0xB0	U+00B0	#	DEGREE SIGN
+0xB1	U+0127	#	LATIN SMALL LETTER H WITH STROKE
+0xB2	U+00B2	#	SUPERSCRIPT TWO
+0xB3	U+00B3	#	SUPERSCRIPT THREE
+0xB4	U+00B4	#	ACUTE ACCENT
+0xB5	U+00B5	#	MICRO SIGN
+0xB6	U+0125	#	LATIN SMALL LETTER H WITH CIRCUMFLEX
+0xB7	U+00B7	#	MIDDLE DOT
+0xB8	U+00B8	#	CEDILLA
+0xB9	U+0131	#	LATIN SMALL LETTER DOTLESS I
+0xBA	U+015F	#	LATIN SMALL LETTER S WITH CEDILLA
+0xBB	U+011F	#	LATIN SMALL LETTER G WITH BREVE
+0xBC	U+0135	#	LATIN SMALL LETTER J WITH CIRCUMFLEX
+0xBD	U+00BD	#	VULGAR FRACTION ONE HALF
+0xBF	U+017C	#	LATIN SMALL LETTER Z WITH DOT ABOVE
+0xC0	U+00C0	#	LATIN CAPITAL LETTER A WITH GRAVE
+0xC1	U+00C1	#	LATIN CAPITAL LETTER A WITH ACUTE
+0xC2	U+00C2	#	LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xC4	U+00C4	#	LATIN CAPITAL LETTER A WITH DIAERESIS
+0xC5	U+010A	#	LATIN CAPITAL LETTER C WITH DOT ABOVE
+0xC6	U+0108	#	LATIN CAPITAL LETTER C WITH CIRCUMFLEX
+0xC7	U+00C7	#	LATIN CAPITAL LETTER C WITH CEDILLA
+0xC8	U+00C8	#	LATIN CAPITAL LETTER E WITH GRAVE
+0xC9	U+00C9	#	LATIN CAPITAL LETTER E WITH ACUTE
+0xCA	U+00CA	#	LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+0xCB	U+00CB	#	LATIN CAPITAL LETTER E WITH DIAERESIS
+0xCC	U+00CC	#	LATIN CAPITAL LETTER I WITH GRAVE
+0xCD	U+00CD	#	LATIN CAPITAL LETTER I WITH ACUTE
+0xCE	U+00CE	#	LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xCF	U+00CF	#	LATIN CAPITAL LETTER I WITH DIAERESIS
+0xD1	U+00D1	#	LATIN CAPITAL LETTER N WITH TILDE
+0xD2	U+00D2	#	LATIN CAPITAL LETTER O WITH GRAVE
+0xD3	U+00D3	#	LATIN CAPITAL LETTER O WITH ACUTE
+0xD4	U+00D4	#	LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xD5	U+0120	#	LATIN CAPITAL LETTER G WITH DOT ABOVE
+0xD6	U+00D6	#	LATIN CAPITAL LETTER O WITH DIAERESIS
+0xD7	U+00D7	#	MULTIPLICATION SIGN
+0xD8	U+011C	#	LATIN CAPITAL LETTER G WITH CIRCUMFLEX
+0xD9	U+00D9	#	LATIN CAPITAL LETTER U WITH GRAVE
+0xDA	U+00DA	#	LATIN CAPITAL LETTER U WITH ACUTE
+0xDB	U+00DB	#	LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+0xDC	U+00DC	#	LATIN CAPITAL LETTER U WITH DIAERESIS
+0xDD	U+016C	#	LATIN CAPITAL LETTER U WITH BREVE
+0xDE	U+015C	#	LATIN CAPITAL LETTER S WITH CIRCUMFLEX
+0xDF	U+00DF	#	LATIN SMALL LETTER SHARP S
+0xE0	U+00E0	#	LATIN SMALL LETTER A WITH GRAVE
+0xE1	U+00E1	#	LATIN SMALL LETTER A WITH ACUTE
+0xE2	U+00E2	#	LATIN SMALL LETTER A WITH CIRCUMFLEX
+0xE4	U+00E4	#	LATIN SMALL LETTER A WITH DIAERESIS
+0xE5	U+010B	#	LATIN SMALL LETTER C WITH DOT ABOVE
+0xE6	U+0109	#	LATIN SMALL LETTER C WITH CIRCUMFLEX
+0xE7	U+00E7	#	LATIN SMALL LETTER C WITH CEDILLA
+0xE8	U+00E8	#	LATIN SMALL LETTER E WITH GRAVE
+0xE9	U+00E9	#	LATIN SMALL LETTER E WITH ACUTE
+0xEA	U+00EA	#	LATIN SMALL LETTER E WITH CIRCUMFLEX
+0xEB	U+00EB	#	LATIN SMALL LETTER E WITH DIAERESIS
+0xEC	U+00EC	#	LATIN SMALL LETTER I WITH GRAVE
+0xED	U+00ED	#	LATIN SMALL LETTER I WITH ACUTE
+0xEE	U+00EE	#	LATIN SMALL LETTER I WITH CIRCUMFLEX
+0xEF	U+00EF	#	LATIN SMALL LETTER I WITH DIAERESIS
+0xF1	U+00F1	#	LATIN SMALL LETTER N WITH TILDE
+0xF2	U+00F2	#	LATIN SMALL LETTER O WITH GRAVE
+0xF3	U+00F3	#	LATIN SMALL LETTER O WITH ACUTE
+0xF4	U+00F4	#	LATIN SMALL LETTER O WITH CIRCUMFLEX
+0xF5	U+0121	#	LATIN SMALL LETTER G WITH DOT ABOVE
+0xF6	U+00F6	#	LATIN SMALL LETTER O WITH DIAERESIS
+0xF7	U+00F7	#	DIVISION SIGN
+0xF8	U+011D	#	LATIN SMALL LETTER G WITH CIRCUMFLEX
+0xF9	U+00F9	#	LATIN SMALL LETTER U WITH GRAVE
+0xFA	U+00FA	#	LATIN SMALL LETTER U WITH ACUTE
+0xFB	U+00FB	#	LATIN SMALL LETTER U WITH CIRCUMFLEX
+0xFC	U+00FC	#	LATIN SMALL LETTER U WITH DIAERESIS
+0xFD	U+016D	#	LATIN SMALL LETTER U WITH BREVE
+0xFE	U+015D	#	LATIN SMALL LETTER S WITH CIRCUMFLEX
+0xFF	U+02D9	#	DOT ABOVE
+
+
+# unassigned 8859-3 codepoints:
+# 0xa5	unused
+# 0xae	unused
+# 0xbe	unused
+# 0xc3	unused
+# 0xd0	unused
+# 0xe3	unused
+# 0xf0	unused
+
diff --git a/src/chrtrans/iso04_uni.tbl b/src/chrtrans/iso04_uni.tbl
new file mode 100644
index 00000000..a1c3ed06
--- /dev/null
+++ b/src/chrtrans/iso04_uni.tbl
@@ -0,0 +1,252 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Miso-8859-4
+
+#Name as a Display Charset (used on Options screen)
+OLatin 4 (ISO-8859-4)
+
+#Codepage number
+C914
+
+#
+#	Name:             ISO/IEC 8859-4:1998 to Unicode
+#	Unicode version:  3.0
+#	Table version:    1.0
+#	Table format:     Format A
+#	Date:             1999 July 27
+#	Authors:          Ken Whistler <kenw@sybase.com>
+#
+#	Copyright (c) 1991-1999 Unicode, Inc.  All Rights reserved.
+#
+#	This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
+#	No claims are made as to fitness for any particular purpose.  No
+#	warranties of any kind are expressed or implied.  The recipient
+#	agrees to determine applicability of information provided.  If this
+#	file has been provided on optical media by Unicode, Inc., the sole
+#	remedy for any claim will be exchange of defective media within 90
+#	days of receipt.
+#
+#	Unicode, Inc. hereby grants the right to freely use the information
+#	supplied in this file in the creation of products supporting the
+#	Unicode Standard, and to make copies of this file in any form for
+#	internal or external distribution as long as this notice remains
+#	attached.
+#
+#	General notes:
+#
+#	This table contains the data the Unicode Consortium has on how
+#	ISO/IEC 8859-4:1998 characters map into Unicode.
+#
+#	Format:  Three tab-separated columns
+#		 Column #1 is the ISO/IEC 8859-4 code (in hex as 0xXX)
+#		 Column #2 is the Unicode (in hex as 0xXXXX)
+#		 Column #3 the Unicode name (follows a comment sign, '#')
+#
+#	The entries are in ISO/IEC 8859-4 order.
+#
+#	Version history
+#	1.0 version updates 0.1 version by adding mappings for all
+#	control characters.
+#
+#	Updated versions of this file may be found in:
+#		<ftp://ftp.unicode.org/Public/MAPPINGS/>
+#
+#	Any comments or problems, contact <errata@unicode.org>
+#	Please note that <errata@unicode.org> is an archival address;
+#	notices will be checked, but do not expect an immediate response.
+#
+#0x20	U+0020	#	SPACE
+#0x21	U+0021	#	EXCLAMATION MARK
+#0x22	U+0022	#	QUOTATION MARK
+#0x23	U+0023	#	NUMBER SIGN
+#0x24	U+0024	#	DOLLAR SIGN
+#0x25	U+0025	#	PERCENT SIGN
+#0x26	U+0026	#	AMPERSAND
+#0x27	U+0027	#	APOSTROPHE
+#0x28	U+0028	#	LEFT PARENTHESIS
+#0x29	U+0029	#	RIGHT PARENTHESIS
+#0x2A	U+002A	#	ASTERISK
+#0x2B	U+002B	#	PLUS SIGN
+#0x2C	U+002C	#	COMMA
+#0x2D	U+002D	#	HYPHEN-MINUS
+#0x2E	U+002E	#	FULL STOP
+#0x2F	U+002F	#	SOLIDUS
+#0x30	U+0030	#	DIGIT ZERO
+#0x31	U+0031	#	DIGIT ONE
+#0x32	U+0032	#	DIGIT TWO
+#0x33	U+0033	#	DIGIT THREE
+#0x34	U+0034	#	DIGIT FOUR
+#0x35	U+0035	#	DIGIT FIVE
+#0x36	U+0036	#	DIGIT SIX
+#0x37	U+0037	#	DIGIT SEVEN
+#0x38	U+0038	#	DIGIT EIGHT
+#0x39	U+0039	#	DIGIT NINE
+#0x3A	U+003A	#	COLON
+#0x3B	U+003B	#	SEMICOLON
+#0x3C	U+003C	#	LESS-THAN SIGN
+#0x3D	U+003D	#	EQUALS SIGN
+#0x3E	U+003E	#	GREATER-THAN SIGN
+#0x3F	U+003F	#	QUESTION MARK
+#0x40	U+0040	#	COMMERCIAL AT
+#0x41	U+0041	#	LATIN CAPITAL LETTER A
+#0x42	U+0042	#	LATIN CAPITAL LETTER B
+#0x43	U+0043	#	LATIN CAPITAL LETTER C
+#0x44	U+0044	#	LATIN CAPITAL LETTER D
+#0x45	U+0045	#	LATIN CAPITAL LETTER E
+#0x46	U+0046	#	LATIN CAPITAL LETTER F
+#0x47	U+0047	#	LATIN CAPITAL LETTER G
+#0x48	U+0048	#	LATIN CAPITAL LETTER H
+#0x49	U+0049	#	LATIN CAPITAL LETTER I
+#0x4A	U+004A	#	LATIN CAPITAL LETTER J
+#0x4B	U+004B	#	LATIN CAPITAL LETTER K
+#0x4C	U+004C	#	LATIN CAPITAL LETTER L
+#0x4D	U+004D	#	LATIN CAPITAL LETTER M
+#0x4E	U+004E	#	LATIN CAPITAL LETTER N
+#0x4F	U+004F	#	LATIN CAPITAL LETTER O
+#0x50	U+0050	#	LATIN CAPITAL LETTER P
+#0x51	U+0051	#	LATIN CAPITAL LETTER Q
+#0x52	U+0052	#	LATIN CAPITAL LETTER R
+#0x53	U+0053	#	LATIN CAPITAL LETTER S
+#0x54	U+0054	#	LATIN CAPITAL LETTER T
+#0x55	U+0055	#	LATIN CAPITAL LETTER U
+#0x56	U+0056	#	LATIN CAPITAL LETTER V
+#0x57	U+0057	#	LATIN CAPITAL LETTER W
+#0x58	U+0058	#	LATIN CAPITAL LETTER X
+#0x59	U+0059	#	LATIN CAPITAL LETTER Y
+#0x5A	U+005A	#	LATIN CAPITAL LETTER Z
+#0x5B	U+005B	#	LEFT SQUARE BRACKET
+#0x5C	U+005C	#	REVERSE SOLIDUS
+#0x5D	U+005D	#	RIGHT SQUARE BRACKET
+#0x5E	U+005E	#	CIRCUMFLEX ACCENT
+#0x5F	U+005F	#	LOW LINE
+#0x60	U+0060	#	GRAVE ACCENT
+#0x61	U+0061	#	LATIN SMALL LETTER A
+#0x62	U+0062	#	LATIN SMALL LETTER B
+#0x63	U+0063	#	LATIN SMALL LETTER C
+#0x64	U+0064	#	LATIN SMALL LETTER D
+#0x65	U+0065	#	LATIN SMALL LETTER E
+#0x66	U+0066	#	LATIN SMALL LETTER F
+#0x67	U+0067	#	LATIN SMALL LETTER G
+#0x68	U+0068	#	LATIN SMALL LETTER H
+#0x69	U+0069	#	LATIN SMALL LETTER I
+#0x6A	U+006A	#	LATIN SMALL LETTER J
+#0x6B	U+006B	#	LATIN SMALL LETTER K
+#0x6C	U+006C	#	LATIN SMALL LETTER L
+#0x6D	U+006D	#	LATIN SMALL LETTER M
+#0x6E	U+006E	#	LATIN SMALL LETTER N
+#0x6F	U+006F	#	LATIN SMALL LETTER O
+#0x70	U+0070	#	LATIN SMALL LETTER P
+#0x71	U+0071	#	LATIN SMALL LETTER Q
+#0x72	U+0072	#	LATIN SMALL LETTER R
+#0x73	U+0073	#	LATIN SMALL LETTER S
+#0x74	U+0074	#	LATIN SMALL LETTER T
+#0x75	U+0075	#	LATIN SMALL LETTER U
+#0x76	U+0076	#	LATIN SMALL LETTER V
+#0x77	U+0077	#	LATIN SMALL LETTER W
+#0x78	U+0078	#	LATIN SMALL LETTER X
+#0x79	U+0079	#	LATIN SMALL LETTER Y
+#0x7A	U+007A	#	LATIN SMALL LETTER Z
+#0x7B	U+007B	#	LEFT CURLY BRACKET
+#0x7C	U+007C	#	VERTICAL LINE
+#0x7D	U+007D	#	RIGHT CURLY BRACKET
+#0x7E	U+007E	#	TILDE
+0xA0	U+00A0	#	NO-BREAK SPACE
+0xA1	U+0104	#	LATIN CAPITAL LETTER A WITH OGONEK
+0xA2	U+0138	#	LATIN SMALL LETTER KRA
+0xA3	U+0156	#	LATIN CAPITAL LETTER R WITH CEDILLA
+0xA4	U+00A4	#	CURRENCY SIGN
+0xA5	U+0128	#	LATIN CAPITAL LETTER I WITH TILDE
+0xA6	U+013B	#	LATIN CAPITAL LETTER L WITH CEDILLA
+0xA7	U+00A7	#	SECTION SIGN
+0xA8	U+00A8	#	DIAERESIS
+0xA9	U+0160	#	LATIN CAPITAL LETTER S WITH CARON
+0xAA	U+0112	#	LATIN CAPITAL LETTER E WITH MACRON
+0xAB	U+0122	#	LATIN CAPITAL LETTER G WITH CEDILLA
+0xAC	U+0166	#	LATIN CAPITAL LETTER T WITH STROKE
+0xAD	U+00AD	#	SOFT HYPHEN
+0xAE	U+017D	#	LATIN CAPITAL LETTER Z WITH CARON
+0xAF	U+00AF	#	MACRON
+0xB0	U+00B0	#	DEGREE SIGN
+0xB1	U+0105	#	LATIN SMALL LETTER A WITH OGONEK
+0xB2	U+02DB	#	OGONEK
+0xB3	U+0157	#	LATIN SMALL LETTER R WITH CEDILLA
+0xB4	U+00B4	#	ACUTE ACCENT
+0xB5	U+0129	#	LATIN SMALL LETTER I WITH TILDE
+0xB6	U+013C	#	LATIN SMALL LETTER L WITH CEDILLA
+0xB7	U+02C7	#	CARON
+0xB8	U+00B8	#	CEDILLA
+0xB9	U+0161	#	LATIN SMALL LETTER S WITH CARON
+0xBA	U+0113	#	LATIN SMALL LETTER E WITH MACRON
+0xBB	U+0123	#	LATIN SMALL LETTER G WITH CEDILLA
+0xBC	U+0167	#	LATIN SMALL LETTER T WITH STROKE
+0xBD	U+014A	#	LATIN CAPITAL LETTER ENG
+0xBE	U+017E	#	LATIN SMALL LETTER Z WITH CARON
+0xBF	U+014B	#	LATIN SMALL LETTER ENG
+0xC0	U+0100	#	LATIN CAPITAL LETTER A WITH MACRON
+0xC1	U+00C1	#	LATIN CAPITAL LETTER A WITH ACUTE
+0xC2	U+00C2	#	LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xC3	U+00C3	#	LATIN CAPITAL LETTER A WITH TILDE
+0xC4	U+00C4	#	LATIN CAPITAL LETTER A WITH DIAERESIS
+0xC5	U+00C5	#	LATIN CAPITAL LETTER A WITH RING ABOVE
+0xC6	U+00C6	#	LATIN CAPITAL LETTER AE
+0xC7	U+012E	#	LATIN CAPITAL LETTER I WITH OGONEK
+0xC8	U+010C	#	LATIN CAPITAL LETTER C WITH CARON
+0xC9	U+00C9	#	LATIN CAPITAL LETTER E WITH ACUTE
+0xCA	U+0118	#	LATIN CAPITAL LETTER E WITH OGONEK
+0xCB	U+00CB	#	LATIN CAPITAL LETTER E WITH DIAERESIS
+0xCC	U+0116	#	LATIN CAPITAL LETTER E WITH DOT ABOVE
+0xCD	U+00CD	#	LATIN CAPITAL LETTER I WITH ACUTE
+0xCE	U+00CE	#	LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xCF	U+012A	#	LATIN CAPITAL LETTER I WITH MACRON
+0xD0	U+0110	#	LATIN CAPITAL LETTER D WITH STROKE
+0xD1	U+0145	#	LATIN CAPITAL LETTER N WITH CEDILLA
+0xD2	U+014C	#	LATIN CAPITAL LETTER O WITH MACRON
+0xD3	U+0136	#	LATIN CAPITAL LETTER K WITH CEDILLA
+0xD4	U+00D4	#	LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xD5	U+00D5	#	LATIN CAPITAL LETTER O WITH TILDE
+0xD6	U+00D6	#	LATIN CAPITAL LETTER O WITH DIAERESIS
+0xD7	U+00D7	#	MULTIPLICATION SIGN
+0xD8	U+00D8	#	LATIN CAPITAL LETTER O WITH STROKE
+0xD9	U+0172	#	LATIN CAPITAL LETTER U WITH OGONEK
+0xDA	U+00DA	#	LATIN CAPITAL LETTER U WITH ACUTE
+0xDB	U+00DB	#	LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+0xDC	U+00DC	#	LATIN CAPITAL LETTER U WITH DIAERESIS
+0xDD	U+0168	#	LATIN CAPITAL LETTER U WITH TILDE
+0xDE	U+016A	#	LATIN CAPITAL LETTER U WITH MACRON
+0xDF	U+00DF	#	LATIN SMALL LETTER SHARP S
+0xE0	U+0101	#	LATIN SMALL LETTER A WITH MACRON
+0xE1	U+00E1	#	LATIN SMALL LETTER A WITH ACUTE
+0xE2	U+00E2	#	LATIN SMALL LETTER A WITH CIRCUMFLEX
+0xE3	U+00E3	#	LATIN SMALL LETTER A WITH TILDE
+0xE4	U+00E4	#	LATIN SMALL LETTER A WITH DIAERESIS
+0xE5	U+00E5	#	LATIN SMALL LETTER A WITH RING ABOVE
+0xE6	U+00E6	#	LATIN SMALL LETTER AE
+0xE7	U+012F	#	LATIN SMALL LETTER I WITH OGONEK
+0xE8	U+010D	#	LATIN SMALL LETTER C WITH CARON
+0xE9	U+00E9	#	LATIN SMALL LETTER E WITH ACUTE
+0xEA	U+0119	#	LATIN SMALL LETTER E WITH OGONEK
+0xEB	U+00EB	#	LATIN SMALL LETTER E WITH DIAERESIS
+0xEC	U+0117	#	LATIN SMALL LETTER E WITH DOT ABOVE
+0xED	U+00ED	#	LATIN SMALL LETTER I WITH ACUTE
+0xEE	U+00EE	#	LATIN SMALL LETTER I WITH CIRCUMFLEX
+0xEF	U+012B	#	LATIN SMALL LETTER I WITH MACRON
+0xF0	U+0111	#	LATIN SMALL LETTER D WITH STROKE
+0xF1	U+0146	#	LATIN SMALL LETTER N WITH CEDILLA
+0xF2	U+014D	#	LATIN SMALL LETTER O WITH MACRON
+0xF3	U+0137	#	LATIN SMALL LETTER K WITH CEDILLA
+0xF4	U+00F4	#	LATIN SMALL LETTER O WITH CIRCUMFLEX
+0xF5	U+00F5	#	LATIN SMALL LETTER O WITH TILDE
+0xF6	U+00F6	#	LATIN SMALL LETTER O WITH DIAERESIS
+0xF7	U+00F7	#	DIVISION SIGN
+0xF8	U+00F8	#	LATIN SMALL LETTER O WITH STROKE
+0xF9	U+0173	#	LATIN SMALL LETTER U WITH OGONEK
+0xFA	U+00FA	#	LATIN SMALL LETTER U WITH ACUTE
+0xFB	U+00FB	#	LATIN SMALL LETTER U WITH CIRCUMFLEX
+0xFC	U+00FC	#	LATIN SMALL LETTER U WITH DIAERESIS
+0xFD	U+0169	#	LATIN SMALL LETTER U WITH TILDE
+0xFE	U+016B	#	LATIN SMALL LETTER U WITH MACRON
+0xFF	U+02D9	#	DOT ABOVE
+
+
+0xd0	U+00d0	# Dstrok and ETH are nearly the same...
diff --git a/src/chrtrans/iso05_uni.tbl b/src/chrtrans/iso05_uni.tbl
new file mode 100644
index 00000000..afbb213c
--- /dev/null
+++ b/src/chrtrans/iso05_uni.tbl
@@ -0,0 +1,259 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Miso-8859-5
+
+#Name as a Display Charset (used on Options screen)
+OCyrillic (ISO-8859-5)
+
+#Codepage number
+C915
+
+#
+#	Name:             ISO 8859-5:1999 to Unicode
+#	Unicode version:  3.0
+#	Table version:    1.0
+#	Table format:     Format A
+#	Date:             1999 July 27
+#	Authors:          Ken Whistler <kenw@sybase.com>
+#
+#	Copyright (c) 1991-1999 Unicode, Inc.  All Rights reserved.
+#
+#	This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
+#	No claims are made as to fitness for any particular purpose.  No
+#	warranties of any kind are expressed or implied.  The recipient
+#	agrees to determine applicability of information provided.  If this
+#	file has been provided on optical media by Unicode, Inc., the sole
+#	remedy for any claim will be exchange of defective media within 90
+#	days of receipt.
+#
+#	Unicode, Inc. hereby grants the right to freely use the information
+#	supplied in this file in the creation of products supporting the
+#	Unicode Standard, and to make copies of this file in any form for
+#	internal or external distribution as long as this notice remains
+#	attached.
+#
+#	General notes:
+#
+#	This table contains the data the Unicode Consortium has on how
+#	ISO/IEC 8859-5:1999 characters map into Unicode.
+#
+#	Format:  Three tab-separated columns
+#		 Column #1 is the ISO/IEC 8859-5 code (in hex as 0xXX)
+#		 Column #2 is the Unicode (in hex as 0xXXXX)
+#		 Column #3 the Unicode name (follows a comment sign, '#')
+#
+#	The entries are in ISO/IEC 8859-5 order.
+#
+#	Version history
+#	1.0 version updates 0.1 version by adding mappings for all
+#	control characters.
+#
+#	Updated versions of this file may be found in:
+#		<ftp://ftp.unicode.org/Public/MAPPINGS/>
+#
+#	Any comments or problems, contact <errata@unicode.org>
+#	Please note that <errata@unicode.org> is an archival address;
+#	notices will be checked, but do not expect an immediate response.
+#
+# Lines with more than one Unicode (U+XXXX) value contain additional
+# replacement mappings added for lynx. - kw
+#
+0x20-0x7e idem
+#
+#0x20	U+0020	#	SPACE
+#0x21	U+0021	#	EXCLAMATION MARK
+#0x22	U+0022	#	QUOTATION MARK
+#0x23	U+0023	#	NUMBER SIGN
+#0x24	U+0024	#	DOLLAR SIGN
+#0x25	U+0025	#	PERCENT SIGN
+#0x26	U+0026	#	AMPERSAND
+#0x27	U+0027	#	APOSTROPHE
+#0x28	U+0028	#	LEFT PARENTHESIS
+#0x29	U+0029	#	RIGHT PARENTHESIS
+#0x2A	U+002A	#	ASTERISK
+#0x2B	U+002B	#	PLUS SIGN
+#0x2C	U+002C	#	COMMA
+#0x2D	U+002D	#	HYPHEN-MINUS
+#0x2E	U+002E	#	FULL STOP
+#0x2F	U+002F	#	SOLIDUS
+#0x30	U+0030	#	DIGIT ZERO
+#0x31	U+0031	#	DIGIT ONE
+#0x32	U+0032	#	DIGIT TWO
+#0x33	U+0033	#	DIGIT THREE
+#0x34	U+0034	#	DIGIT FOUR
+#0x35	U+0035	#	DIGIT FIVE
+#0x36	U+0036	#	DIGIT SIX
+#0x37	U+0037	#	DIGIT SEVEN
+#0x38	U+0038	#	DIGIT EIGHT
+#0x39	U+0039	#	DIGIT NINE
+#0x3A	U+003A	#	COLON
+#0x3B	U+003B	#	SEMICOLON
+#0x3C	U+003C	#	LESS-THAN SIGN
+#0x3D	U+003D	#	EQUALS SIGN
+#0x3E	U+003E	#	GREATER-THAN SIGN
+#0x3F	U+003F	#	QUESTION MARK
+#0x40	U+0040	#	COMMERCIAL AT
+#0x41	U+0041	#	LATIN CAPITAL LETTER A
+#0x42	U+0042	#	LATIN CAPITAL LETTER B
+#0x43	U+0043	#	LATIN CAPITAL LETTER C
+#0x44	U+0044	#	LATIN CAPITAL LETTER D
+#0x45	U+0045	#	LATIN CAPITAL LETTER E
+#0x46	U+0046	#	LATIN CAPITAL LETTER F
+#0x47	U+0047	#	LATIN CAPITAL LETTER G
+#0x48	U+0048	#	LATIN CAPITAL LETTER H
+#0x49	U+0049	#	LATIN CAPITAL LETTER I
+#0x4A	U+004A	#	LATIN CAPITAL LETTER J
+#0x4B	U+004B	#	LATIN CAPITAL LETTER K
+#0x4C	U+004C	#	LATIN CAPITAL LETTER L
+#0x4D	U+004D	#	LATIN CAPITAL LETTER M
+#0x4E	U+004E	#	LATIN CAPITAL LETTER N
+#0x4F	U+004F	#	LATIN CAPITAL LETTER O
+#0x50	U+0050	#	LATIN CAPITAL LETTER P
+#0x51	U+0051	#	LATIN CAPITAL LETTER Q
+#0x52	U+0052	#	LATIN CAPITAL LETTER R
+#0x53	U+0053	#	LATIN CAPITAL LETTER S
+#0x54	U+0054	#	LATIN CAPITAL LETTER T
+#0x55	U+0055	#	LATIN CAPITAL LETTER U
+#0x56	U+0056	#	LATIN CAPITAL LETTER V
+#0x57	U+0057	#	LATIN CAPITAL LETTER W
+#0x58	U+0058	#	LATIN CAPITAL LETTER X
+#0x59	U+0059	#	LATIN CAPITAL LETTER Y
+#0x5A	U+005A	#	LATIN CAPITAL LETTER Z
+#0x5B	U+005B	#	LEFT SQUARE BRACKET
+#0x5C	U+005C	#	REVERSE SOLIDUS
+#0x5D	U+005D	#	RIGHT SQUARE BRACKET
+#0x5E	U+005E	#	CIRCUMFLEX ACCENT
+#0x5F	U+005F	#	LOW LINE
+#0x60	U+0060	#	GRAVE ACCENT
+#0x61	U+0061	#	LATIN SMALL LETTER A
+#0x62	U+0062	#	LATIN SMALL LETTER B
+#0x63	U+0063	#	LATIN SMALL LETTER C
+#0x64	U+0064	#	LATIN SMALL LETTER D
+#0x65	U+0065	#	LATIN SMALL LETTER E
+#0x66	U+0066	#	LATIN SMALL LETTER F
+#0x67	U+0067	#	LATIN SMALL LETTER G
+#0x68	U+0068	#	LATIN SMALL LETTER H
+#0x69	U+0069	#	LATIN SMALL LETTER I
+#0x6A	U+006A	#	LATIN SMALL LETTER J
+#0x6B	U+006B	#	LATIN SMALL LETTER K
+#0x6C	U+006C	#	LATIN SMALL LETTER L
+#0x6D	U+006D	#	LATIN SMALL LETTER M
+#0x6E	U+006E	#	LATIN SMALL LETTER N
+#0x6F	U+006F	#	LATIN SMALL LETTER O
+#0x70	U+0070	#	LATIN SMALL LETTER P
+#0x71	U+0071	#	LATIN SMALL LETTER Q
+#0x72	U+0072	#	LATIN SMALL LETTER R
+#0x73	U+0073	#	LATIN SMALL LETTER S
+#0x74	U+0074	#	LATIN SMALL LETTER T
+#0x75	U+0075	#	LATIN SMALL LETTER U
+#0x76	U+0076	#	LATIN SMALL LETTER V
+#0x77	U+0077	#	LATIN SMALL LETTER W
+#0x78	U+0078	#	LATIN SMALL LETTER X
+#0x79	U+0079	#	LATIN SMALL LETTER Y
+#0x7A	U+007A	#	LATIN SMALL LETTER Z
+#0x7B	U+007B	#	LEFT CURLY BRACKET
+#0x7C	U+007C	#	VERTICAL LINE
+#0x7D	U+007D	#	RIGHT CURLY BRACKET
+#0x7E	U+007E	#	TILDE
+0xA0	U+00A0	#	NO-BREAK SPACE
+0xA1	U+0401	#	CYRILLIC CAPITAL LETTER IO
+0xA2	U+0402	#	CYRILLIC CAPITAL LETTER DJE
+0xA3	U+0403	#	CYRILLIC CAPITAL LETTER GJE
+0xA4	U+0404	#	CYRILLIC CAPITAL LETTER UKRAINIAN IE
+0xA5	U+0405	#	CYRILLIC CAPITAL LETTER DZE
+0xA6	U+0406	U+0130	#	CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+0xA7	U+0407	U+03AA	#	CYRILLIC CAPITAL LETTER YI
+0xA8	U+0408	#	CYRILLIC CAPITAL LETTER JE
+0xA9	U+0409	#	CYRILLIC CAPITAL LETTER LJE
+0xAA	U+040A	#	CYRILLIC CAPITAL LETTER NJE
+0xAB	U+040B	#	CYRILLIC CAPITAL LETTER TSHE
+0xAC	U+040C	#	CYRILLIC CAPITAL LETTER KJE
+0xAD	U+00AD	#	SOFT HYPHEN
+0xAE	U+040E	#	CYRILLIC CAPITAL LETTER SHORT U
+0xAF	U+040F	#	CYRILLIC CAPITAL LETTER DZHE
+0xB0	U+0410	#	CYRILLIC CAPITAL LETTER A
+0xB1	U+0411	#	CYRILLIC CAPITAL LETTER BE
+0xB2	U+0412	#	CYRILLIC CAPITAL LETTER VE
+0xB3	U+0413	U+0393	#	CYRILLIC CAPITAL LETTER GHE
+0xB4	U+0414	#	CYRILLIC CAPITAL LETTER DE
+0xB5	U+0415	#	CYRILLIC CAPITAL LETTER IE
+0xB6	U+0416	U+017d	#	CYRILLIC CAPITAL LETTER ZHE
+0xB7	U+0417	#	CYRILLIC CAPITAL LETTER ZE
+0xB8	U+0418	#	CYRILLIC CAPITAL LETTER I
+0xB9	U+0419	#	CYRILLIC CAPITAL LETTER SHORT I
+0xBA	U+041A	#	CYRILLIC CAPITAL LETTER KA
+0xBB	U+041B	U+039b	#	CYRILLIC CAPITAL LETTER EL
+0xBC	U+041C	#	CYRILLIC CAPITAL LETTER EM
+0xBD	U+041D	#	CYRILLIC CAPITAL LETTER EN
+0xBE	U+041E	#	CYRILLIC CAPITAL LETTER O
+0xBF	U+041F	U+03a0	#	CYRILLIC CAPITAL LETTER PE
+0xC0	U+0420	#	CYRILLIC CAPITAL LETTER ER
+0xC1	U+0421	#	CYRILLIC CAPITAL LETTER ES
+0xC2	U+0422	#	CYRILLIC CAPITAL LETTER TE
+0xC3	U+0423	#	CYRILLIC CAPITAL LETTER U
+0xC4	U+0424	U+03a6	#	CYRILLIC CAPITAL LETTER EF
+0xC5	U+0425	#	CYRILLIC CAPITAL LETTER HA
+0xC6	U+0426	#	CYRILLIC CAPITAL LETTER TSE
+0xC7	U+0427	U+010c	#	CYRILLIC CAPITAL LETTER CHE
+0xC8	U+0428	U+0160	#	CYRILLIC CAPITAL LETTER SHA
+0xC9	U+0429	#	CYRILLIC CAPITAL LETTER SHCHA
+0xCA	U+042A	#	CYRILLIC CAPITAL LETTER HARD SIGN
+0xCB	U+042B	#	CYRILLIC CAPITAL LETTER YERU
+0xCC	U+042C	#	CYRILLIC CAPITAL LETTER SOFT SIGN
+0xCD	U+042D	#	CYRILLIC CAPITAL LETTER E
+0xCE	U+042E	#	CYRILLIC CAPITAL LETTER YU
+0xCF	U+042F	#	CYRILLIC CAPITAL LETTER YA
+0xD0	U+0430	#	CYRILLIC SMALL LETTER A
+0xD1	U+0431	#	CYRILLIC SMALL LETTER BE
+0xD2	U+0432	#	CYRILLIC SMALL LETTER VE
+0xD3	U+0433	#	CYRILLIC SMALL LETTER GHE
+0xD4	U+0434	#	CYRILLIC SMALL LETTER DE
+0xD5	U+0435	#	CYRILLIC SMALL LETTER IE
+0xD6	U+0436	U+017e	#	CYRILLIC SMALL LETTER ZHE
+0xD7	U+0437	#	CYRILLIC SMALL LETTER ZE
+0xD8	U+0438	#	CYRILLIC SMALL LETTER I
+0xD9	U+0439	#	CYRILLIC SMALL LETTER SHORT I
+0xDA	U+043A	#	CYRILLIC SMALL LETTER KA
+0xDB	U+043B	U+03bb	#	CYRILLIC SMALL LETTER EL
+0xDC	U+043C	#	CYRILLIC SMALL LETTER EM
+0xDD	U+043D	#	CYRILLIC SMALL LETTER EN
+0xDE	U+043E	#	CYRILLIC SMALL LETTER O
+0xDF	U+043F	U+03c0	#	CYRILLIC SMALL LETTER PE
+0xE0	U+0440	#	CYRILLIC SMALL LETTER ER
+0xE1	U+0441	#	CYRILLIC SMALL LETTER ES
+0xE2	U+0442	#	CYRILLIC SMALL LETTER TE
+0xE3	U+0443	#	CYRILLIC SMALL LETTER U
+0xE4	U+0444	U+03c6	#	CYRILLIC SMALL LETTER EF
+0xE5	U+0445	#	CYRILLIC SMALL LETTER HA
+0xE6	U+0446	#	CYRILLIC SMALL LETTER TSE
+0xE7	U+0447	U+010d	#	CYRILLIC SMALL LETTER CHE
+0xE8	U+0448	U+0161	#	CYRILLIC SMALL LETTER SHA
+0xE9	U+0449	#	CYRILLIC SMALL LETTER SHCHA
+0xEA	U+044A	#	CYRILLIC SMALL LETTER HARD SIGN
+0xEB	U+044B	U+0131	#	CYRILLIC SMALL LETTER YERU
+0xEC	U+044C	#	CYRILLIC SMALL LETTER SOFT SIGN
+0xED	U+044D	#	CYRILLIC SMALL LETTER E
+0xEE	U+044E	#	CYRILLIC SMALL LETTER YU
+0xEF	U+044F	#	CYRILLIC SMALL LETTER YA
+0xF0	U+2116	#	NUMERO SIGN
+0xF1	U+0451	#	CYRILLIC SMALL LETTER IO
+0xF2	U+0452	#	CYRILLIC SMALL LETTER DJE
+0xF3	U+0453	#	CYRILLIC SMALL LETTER GJE
+0xF4	U+0454	#	CYRILLIC SMALL LETTER UKRAINIAN IE
+0xF5	U+0455	#	CYRILLIC SMALL LETTER DZE
+0xF6	U+0456	#	CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+0xF7	U+0457	U+03CA	#	CYRILLIC SMALL LETTER YI
+0xF8	U+0458	#	CYRILLIC SMALL LETTER JE
+0xF9	U+0459	#	CYRILLIC SMALL LETTER LJE
+0xFA	U+045A	#	CYRILLIC SMALL LETTER NJE
+0xFB	U+045B	#	CYRILLIC SMALL LETTER TSHE
+0xFC	U+045C	#	CYRILLIC SMALL LETTER KJE
+0xFD	U+00A7	#	SECTION SIGN
+0xFE	U+045E	#	CYRILLIC SMALL LETTER SHORT U
+0xFF	U+045F	#	CYRILLIC SMALL LETTER DZHE
+
+U+0400 "`\265"
+U+040d "`\270"
+U+0450 "`\325"
+U+045d "`\330"
diff --git a/src/chrtrans/iso06_uni.tbl b/src/chrtrans/iso06_uni.tbl
new file mode 100644
index 00000000..e4ef995d
--- /dev/null
+++ b/src/chrtrans/iso06_uni.tbl
@@ -0,0 +1,208 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Miso-8859-6
+
+#Name as a Display Charset (used on Options screen).
+OArabic (ISO-8859-6)
+
+#Codepage number
+C1089
+
+#
+#	Name:             ISO 8859-6:1999 to Unicode
+#	Unicode version:  3.0
+#	Table version:    1.0
+#	Table format:     Format A
+#	Date:             1999 July 27
+#	Authors:          Ken Whistler <kenw@sybase.com>
+#
+#	Copyright (c) 1991-1999 Unicode, Inc.  All Rights reserved.
+#
+#	This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
+#	No claims are made as to fitness for any particular purpose.  No
+#	warranties of any kind are expressed or implied.  The recipient
+#	agrees to determine applicability of information provided.  If this
+#	file has been provided on optical media by Unicode, Inc., the sole
+#	remedy for any claim will be exchange of defective media within 90
+#	days of receipt.
+#
+#	Unicode, Inc. hereby grants the right to freely use the information
+#	supplied in this file in the creation of products supporting the
+#	Unicode Standard, and to make copies of this file in any form for
+#	internal or external distribution as long as this notice remains
+#	attached.
+#
+#	General notes:
+#
+#	This table contains the data the Unicode Consortium has on how
+#	ISO/IEC 8859-6:1999 characters map into Unicode.
+#
+#	Format:  Three tab-separated columns
+#		 Column #1 is the ISO/IEC 8859-6 code (in hex as 0xXX)
+#		 Column #2 is the Unicode (in hex as 0xXXXX)
+#		 Column #3 the Unicode name (follows a comment sign, '#')
+#
+#	The entries are in ISO/IEC 8859-6 order.
+#
+#	Version history
+#	1.0 version updates 0.1 version by adding mappings for all
+#	control characters.
+#	0x30..0x39 remapped to the ASCII digits (U+0030..U+0039) instead
+#	of the Arabic digits (U+0660..U+0669).
+#
+#	Updated versions of this file may be found in:
+#		<ftp://ftp.unicode.org/Public/MAPPINGS/>
+#
+#	Any comments or problems, contact <errata@unicode.org>
+#	Please note that <errata@unicode.org> is an archival address;
+#	notices will be checked, but do not expect an immediate response.
+#
+0x20-0x7e idem
+#
+#0x20	U+0020	#	SPACE
+#0x21	U+0021	#	EXCLAMATION MARK
+#0x22	U+0022	#	QUOTATION MARK
+#0x23	U+0023	#	NUMBER SIGN
+#0x24	U+0024	#	DOLLAR SIGN
+#0x25	U+0025	#	PERCENT SIGN
+#0x26	U+0026	#	AMPERSAND
+#0x27	U+0027	#	APOSTROPHE
+#0x28	U+0028	#	LEFT PARENTHESIS
+#0x29	U+0029	#	RIGHT PARENTHESIS
+#0x2A	U+002A	#	ASTERISK
+#0x2B	U+002B	#	PLUS SIGN
+#0x2C	U+002C	#	COMMA
+#0x2D	U+002D	#	HYPHEN-MINUS
+#0x2E	U+002E	#	FULL STOP
+#0x2F	U+002F	#	SOLIDUS
+#0x30	U+0030	#	DIGIT ZERO
+#0x31	U+0031	#	DIGIT ONE
+#0x32	U+0032	#	DIGIT TWO
+#0x33	U+0033	#	DIGIT THREE
+#0x34	U+0034	#	DIGIT FOUR
+#0x35	U+0035	#	DIGIT FIVE
+#0x36	U+0036	#	DIGIT SIX
+#0x37	U+0037	#	DIGIT SEVEN
+#0x38	U+0038	#	DIGIT EIGHT
+#0x39	U+0039	#	DIGIT NINE
+#0x3A	U+003A	#	COLON
+#0x3B	U+003B	#	SEMICOLON
+#0x3C	U+003C	#	LESS-THAN SIGN
+#0x3D	U+003D	#	EQUALS SIGN
+#0x3E	U+003E	#	GREATER-THAN SIGN
+#0x3F	U+003F	#	QUESTION MARK
+#0x40	U+0040	#	COMMERCIAL AT
+#0x41	U+0041	#	LATIN CAPITAL LETTER A
+#0x42	U+0042	#	LATIN CAPITAL LETTER B
+#0x43	U+0043	#	LATIN CAPITAL LETTER C
+#0x44	U+0044	#	LATIN CAPITAL LETTER D
+#0x45	U+0045	#	LATIN CAPITAL LETTER E
+#0x46	U+0046	#	LATIN CAPITAL LETTER F
+#0x47	U+0047	#	LATIN CAPITAL LETTER G
+#0x48	U+0048	#	LATIN CAPITAL LETTER H
+#0x49	U+0049	#	LATIN CAPITAL LETTER I
+#0x4A	U+004A	#	LATIN CAPITAL LETTER J
+#0x4B	U+004B	#	LATIN CAPITAL LETTER K
+#0x4C	U+004C	#	LATIN CAPITAL LETTER L
+#0x4D	U+004D	#	LATIN CAPITAL LETTER M
+#0x4E	U+004E	#	LATIN CAPITAL LETTER N
+#0x4F	U+004F	#	LATIN CAPITAL LETTER O
+#0x50	U+0050	#	LATIN CAPITAL LETTER P
+#0x51	U+0051	#	LATIN CAPITAL LETTER Q
+#0x52	U+0052	#	LATIN CAPITAL LETTER R
+#0x53	U+0053	#	LATIN CAPITAL LETTER S
+#0x54	U+0054	#	LATIN CAPITAL LETTER T
+#0x55	U+0055	#	LATIN CAPITAL LETTER U
+#0x56	U+0056	#	LATIN CAPITAL LETTER V
+#0x57	U+0057	#	LATIN CAPITAL LETTER W
+#0x58	U+0058	#	LATIN CAPITAL LETTER X
+#0x59	U+0059	#	LATIN CAPITAL LETTER Y
+#0x5A	U+005A	#	LATIN CAPITAL LETTER Z
+#0x5B	U+005B	#	LEFT SQUARE BRACKET
+#0x5C	U+005C	#	REVERSE SOLIDUS
+#0x5D	U+005D	#	RIGHT SQUARE BRACKET
+#0x5E	U+005E	#	CIRCUMFLEX ACCENT
+#0x5F	U+005F	#	LOW LINE
+#0x60	U+0060	#	GRAVE ACCENT
+#0x61	U+0061	#	LATIN SMALL LETTER A
+#0x62	U+0062	#	LATIN SMALL LETTER B
+#0x63	U+0063	#	LATIN SMALL LETTER C
+#0x64	U+0064	#	LATIN SMALL LETTER D
+#0x65	U+0065	#	LATIN SMALL LETTER E
+#0x66	U+0066	#	LATIN SMALL LETTER F
+#0x67	U+0067	#	LATIN SMALL LETTER G
+#0x68	U+0068	#	LATIN SMALL LETTER H
+#0x69	U+0069	#	LATIN SMALL LETTER I
+#0x6A	U+006A	#	LATIN SMALL LETTER J
+#0x6B	U+006B	#	LATIN SMALL LETTER K
+#0x6C	U+006C	#	LATIN SMALL LETTER L
+#0x6D	U+006D	#	LATIN SMALL LETTER M
+#0x6E	U+006E	#	LATIN SMALL LETTER N
+#0x6F	U+006F	#	LATIN SMALL LETTER O
+#0x70	U+0070	#	LATIN SMALL LETTER P
+#0x71	U+0071	#	LATIN SMALL LETTER Q
+#0x72	U+0072	#	LATIN SMALL LETTER R
+#0x73	U+0073	#	LATIN SMALL LETTER S
+#0x74	U+0074	#	LATIN SMALL LETTER T
+#0x75	U+0075	#	LATIN SMALL LETTER U
+#0x76	U+0076	#	LATIN SMALL LETTER V
+#0x77	U+0077	#	LATIN SMALL LETTER W
+#0x78	U+0078	#	LATIN SMALL LETTER X
+#0x79	U+0079	#	LATIN SMALL LETTER Y
+#0x7A	U+007A	#	LATIN SMALL LETTER Z
+#0x7B	U+007B	#	LEFT CURLY BRACKET
+#0x7C	U+007C	#	VERTICAL LINE
+#0x7D	U+007D	#	RIGHT CURLY BRACKET
+#0x7E	U+007E	#	TILDE
+0xA0	U+00A0	#	NO-BREAK SPACE
+0xA4	U+00A4	#	CURRENCY SIGN
+0xAC	U+060C	#	ARABIC COMMA
+0xAD	U+00AD	#	SOFT HYPHEN
+0xBB	U+061B	#	ARABIC SEMICOLON
+0xBF	U+061F	#	ARABIC QUESTION MARK
+0xC1	U+0621	#	ARABIC LETTER HAMZA
+0xC2	U+0622	#	ARABIC LETTER ALEF WITH MADDA ABOVE
+0xC3	U+0623	#	ARABIC LETTER ALEF WITH HAMZA ABOVE
+0xC4	U+0624	#	ARABIC LETTER WAW WITH HAMZA ABOVE
+0xC5	U+0625	#	ARABIC LETTER ALEF WITH HAMZA BELOW
+0xC6	U+0626	#	ARABIC LETTER YEH WITH HAMZA ABOVE
+0xC7	U+0627	#	ARABIC LETTER ALEF
+0xC8	U+0628	#	ARABIC LETTER BEH
+0xC9	U+0629	#	ARABIC LETTER TEH MARBUTA
+0xCA	U+062A	#	ARABIC LETTER TEH
+0xCB	U+062B	#	ARABIC LETTER THEH
+0xCC	U+062C	#	ARABIC LETTER JEEM
+0xCD	U+062D	#	ARABIC LETTER HAH
+0xCE	U+062E	#	ARABIC LETTER KHAH
+0xCF	U+062F	#	ARABIC LETTER DAL
+0xD0	U+0630	#	ARABIC LETTER THAL
+0xD1	U+0631	#	ARABIC LETTER REH
+0xD2	U+0632	#	ARABIC LETTER ZAIN
+0xD3	U+0633	#	ARABIC LETTER SEEN
+0xD4	U+0634	#	ARABIC LETTER SHEEN
+0xD5	U+0635	#	ARABIC LETTER SAD
+0xD6	U+0636	#	ARABIC LETTER DAD
+0xD7	U+0637	#	ARABIC LETTER TAH
+0xD8	U+0638	#	ARABIC LETTER ZAH
+0xD9	U+0639	#	ARABIC LETTER AIN
+0xDA	U+063A	#	ARABIC LETTER GHAIN
+0xE0	U+0640	#	ARABIC TATWEEL
+0xE1	U+0641	#	ARABIC LETTER FEH
+0xE2	U+0642	#	ARABIC LETTER QAF
+0xE3	U+0643	#	ARABIC LETTER KAF
+0xE4	U+0644	#	ARABIC LETTER LAM
+0xE5	U+0645	#	ARABIC LETTER MEEM
+0xE6	U+0646	#	ARABIC LETTER NOON
+0xE7	U+0647	#	ARABIC LETTER HEH
+0xE8	U+0648	#	ARABIC LETTER WAW
+0xE9	U+0649	#	ARABIC LETTER ALEF MAKSURA
+0xEA	U+064A	#	ARABIC LETTER YEH
+0xEB	U+064B	#	ARABIC FATHATAN
+0xEC	U+064C	#	ARABIC DAMMATAN
+0xED	U+064D	#	ARABIC KASRATAN
+0xEE	U+064E	#	ARABIC FATHA
+0xEF	U+064F	#	ARABIC DAMMA
+0xF0	U+0650	#	ARABIC KASRA
+0xF1	U+0651	#	ARABIC SHADDA
+0xF2	U+0652	#	ARABIC SUKUN
diff --git a/src/chrtrans/iso07_uni.tbl b/src/chrtrans/iso07_uni.tbl
new file mode 100644
index 00000000..87372a9e
--- /dev/null
+++ b/src/chrtrans/iso07_uni.tbl
@@ -0,0 +1,275 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Miso-8859-7
+
+#Name as a Display Charset (used on Options screen)
+OGreek (ISO-8859-7)
+
+#Codepage number
+C813
+
+#
+#	Name:             ISO 8859-7:2003 to Unicode
+#	Unicode version:  4.0
+#	Table version:    2.0
+#	Table format:     Format A
+#	Date:             2003-Nov-12
+#	Authors:          Ken Whistler <kenw@sybase.com>
+#
+#	Copyright (c) 1991-2003 Unicode, Inc.  All Rights reserved.
+#
+#	This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
+#	No claims are made as to fitness for any particular purpose.  No
+#	warranties of any kind are expressed or implied.  The recipient
+#	agrees to determine applicability of information provided.  If this
+#	file has been provided on optical media by Unicode, Inc., the sole
+#	remedy for any claim will be exchange of defective media within 90
+#	days of receipt.
+#
+#	Unicode, Inc. hereby grants the right to freely use the information
+#	supplied in this file in the creation of products supporting the
+#	Unicode Standard, and to make copies of this file in any form for
+#	internal or external distribution as long as this notice remains
+#	attached.
+#
+#	General notes:
+#
+#	This table contains the data the Unicode Consortium has on how
+#	ISO 8859-7:2003 characters map into Unicode.
+#
+#	ISO 8859-7:1987 is equivalent to ISO-IR-126, ELOT 928,
+#	and ECMA 118. ISO 8859-7:2003 adds two currency signs 
+#	and one other character not in the earlier standard.
+#
+#	Format:  Three tab-separated columns
+#		 Column #1 is the ISO 8859-7 code (in hex as 0xXX)
+#		 Column #2 is the Unicode (in hex as 0xXXXX)
+#		 Column #3 the Unicode name (follows a comment sign, '#')
+#
+#	The entries are in ISO 8859-7 order.
+#
+#	Version history
+#	1.0 version updates 0.1 version by adding mappings for all
+#	control characters.
+#	Remap 0xA1 to U+2018 (instead of 0x02BD) to match text of 8859-7
+#	Remap 0xA2 to U+2019 (instead of 0x02BC) to match text of 8859-7
+#
+#	2.0 version updates 1.0 version by adding mappings for the
+#	three newly added characters 0xA4, 0xA5, 0xAA.
+#
+#	Updated versions of this file may be found in:
+#		<http://www.unicode.org/Public/MAPPINGS/>
+#
+#	Any comments or problems, contact the Unicode Consortium at:
+#	        <http://www.unicode.org/reporting.html>
+#
+# Lines with more than one Unicode (U+XXXX) value contain additional
+# replacement mappings added for lynx. - kw
+#
+0x20-0x7e idem
+#
+#0x20	U+0020	#	SPACE
+#0x21	U+0021	#	EXCLAMATION MARK
+#0x22	U+0022	#	QUOTATION MARK
+#0x23	U+0023	#	NUMBER SIGN
+#0x24	U+0024	#	DOLLAR SIGN
+#0x25	U+0025	#	PERCENT SIGN
+#0x26	U+0026	#	AMPERSAND
+#0x27	U+0027	#	APOSTROPHE
+#0x28	U+0028	#	LEFT PARENTHESIS
+#0x29	U+0029	#	RIGHT PARENTHESIS
+#0x2A	U+002A	#	ASTERISK
+#0x2B	U+002B	#	PLUS SIGN
+#0x2C	U+002C	#	COMMA
+#0x2D	U+002D	#	HYPHEN-MINUS
+#0x2E	U+002E	#	FULL STOP
+#0x2F	U+002F	#	SOLIDUS
+#0x30	U+0030	#	DIGIT ZERO
+#0x31	U+0031	#	DIGIT ONE
+#0x32	U+0032	#	DIGIT TWO
+#0x33	U+0033	#	DIGIT THREE
+#0x34	U+0034	#	DIGIT FOUR
+#0x35	U+0035	#	DIGIT FIVE
+#0x36	U+0036	#	DIGIT SIX
+#0x37	U+0037	#	DIGIT SEVEN
+#0x38	U+0038	#	DIGIT EIGHT
+#0x39	U+0039	#	DIGIT NINE
+#0x3A	U+003A	#	COLON
+#0x3B	U+003B	#	SEMICOLON
+#0x3C	U+003C	#	LESS-THAN SIGN
+#0x3D	U+003D	#	EQUALS SIGN
+#0x3E	U+003E	#	GREATER-THAN SIGN
+#0x3F	U+003F	#	QUESTION MARK
+#0x40	U+0040	#	COMMERCIAL AT
+#0x41	U+0041	#	LATIN CAPITAL LETTER A
+#0x42	U+0042	#	LATIN CAPITAL LETTER B
+#0x43	U+0043	#	LATIN CAPITAL LETTER C
+#0x44	U+0044	#	LATIN CAPITAL LETTER D
+#0x45	U+0045	#	LATIN CAPITAL LETTER E
+#0x46	U+0046	#	LATIN CAPITAL LETTER F
+#0x47	U+0047	#	LATIN CAPITAL LETTER G
+#0x48	U+0048	#	LATIN CAPITAL LETTER H
+#0x49	U+0049	#	LATIN CAPITAL LETTER I
+#0x4A	U+004A	#	LATIN CAPITAL LETTER J
+#0x4B	U+004B	#	LATIN CAPITAL LETTER K
+#0x4C	U+004C	#	LATIN CAPITAL LETTER L
+#0x4D	U+004D	#	LATIN CAPITAL LETTER M
+#0x4E	U+004E	#	LATIN CAPITAL LETTER N
+#0x4F	U+004F	#	LATIN CAPITAL LETTER O
+#0x50	U+0050	#	LATIN CAPITAL LETTER P
+#0x51	U+0051	#	LATIN CAPITAL LETTER Q
+#0x52	U+0052	#	LATIN CAPITAL LETTER R
+#0x53	U+0053	#	LATIN CAPITAL LETTER S
+#0x54	U+0054	#	LATIN CAPITAL LETTER T
+#0x55	U+0055	#	LATIN CAPITAL LETTER U
+#0x56	U+0056	#	LATIN CAPITAL LETTER V
+#0x57	U+0057	#	LATIN CAPITAL LETTER W
+#0x58	U+0058	#	LATIN CAPITAL LETTER X
+#0x59	U+0059	#	LATIN CAPITAL LETTER Y
+#0x5A	U+005A	#	LATIN CAPITAL LETTER Z
+#0x5B	U+005B	#	LEFT SQUARE BRACKET
+#0x5C	U+005C	#	REVERSE SOLIDUS
+#0x5D	U+005D	#	RIGHT SQUARE BRACKET
+#0x5E	U+005E	#	CIRCUMFLEX ACCENT
+#0x5F	U+005F	#	LOW LINE
+#0x60	U+0060	#	GRAVE ACCENT
+#0x61	U+0061	#	LATIN SMALL LETTER A
+#0x62	U+0062	#	LATIN SMALL LETTER B
+#0x63	U+0063	#	LATIN SMALL LETTER C
+#0x64	U+0064	#	LATIN SMALL LETTER D
+#0x65	U+0065	#	LATIN SMALL LETTER E
+#0x66	U+0066	#	LATIN SMALL LETTER F
+#0x67	U+0067	#	LATIN SMALL LETTER G
+#0x68	U+0068	#	LATIN SMALL LETTER H
+#0x69	U+0069	#	LATIN SMALL LETTER I
+#0x6A	U+006A	#	LATIN SMALL LETTER J
+#0x6B	U+006B	#	LATIN SMALL LETTER K
+#0x6C	U+006C	#	LATIN SMALL LETTER L
+#0x6D	U+006D	#	LATIN SMALL LETTER M
+#0x6E	U+006E	#	LATIN SMALL LETTER N
+#0x6F	U+006F	#	LATIN SMALL LETTER O
+#0x70	U+0070	#	LATIN SMALL LETTER P
+#0x71	U+0071	#	LATIN SMALL LETTER Q
+#0x72	U+0072	#	LATIN SMALL LETTER R
+#0x73	U+0073	#	LATIN SMALL LETTER S
+#0x74	U+0074	#	LATIN SMALL LETTER T
+#0x75	U+0075	#	LATIN SMALL LETTER U
+#0x76	U+0076	#	LATIN SMALL LETTER V
+#0x77	U+0077	#	LATIN SMALL LETTER W
+#0x78	U+0078	#	LATIN SMALL LETTER X
+#0x79	U+0079	#	LATIN SMALL LETTER Y
+#0x7A	U+007A	#	LATIN SMALL LETTER Z
+#0x7B	U+007B	#	LEFT CURLY BRACKET
+#0x7C	U+007C	#	VERTICAL LINE
+#0x7D	U+007D	#	RIGHT CURLY BRACKET
+#0x7E	U+007E	#	TILDE
+0xA0	U+00A0	#	NO-BREAK SPACE
+#
+# The following two changed in ISO 8859:1999
+#
+#   Remap 0xA1 to U+2018 (instead of U+02BD)
+#   Remap 0xA2 to U+2019 (instead of U+02BC)
+#
+# Keep the old ones as primary for now.  Also added old U+037[12]
+# found in existing linux kbd files and in RFC 1345 for compatibility.
+# - kw 1999-10-29
+0xA1	U+02BD	U+2018	U+0371	#	MODIFIER LETTER REVERSED COMMA
+0xA2	U+02BC	U+2019	U+0372	#	MODIFIER LETTER APOSTROPHE
+0xA3	U+00A3	#	POUND SIGN
+0xA4	U+20AC	#	EURO SIGN
+0xA5	U+20AF	#	DRACHMA SIGN
+0xA6	U+00A6	#	BROKEN BAR
+0xA7	U+00A7	#	SECTION SIGN
+0xA8	U+00A8	#	DIAERESIS
+0xA9	U+00A9	#	COPYRIGHT SIGN
+0xAA	U+037A	#	GREEK YPOGEGRAMMENI
+0xAB	U+00AB	#	LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC	U+00AC	#	NOT SIGN
+0xAD	U+00AD	#	SOFT HYPHEN
+0xAF	U+2015	#	HORIZONTAL BAR
+0xB0	U+00B0	#	DEGREE SIGN
+0xB1	U+00B1	#	PLUS-MINUS SIGN
+0xB2	U+00B2	#	SUPERSCRIPT TWO
+0xB3	U+00B3	#	SUPERSCRIPT THREE
+0xB4	U+0384	#	GREEK TONOS
+0xB5	U+0385	#	GREEK DIALYTIKA TONOS
+0xB6	U+0386	U+1fbb	#	GREEK CAPITAL LETTER ALPHA WITH TONOS
+0xB7	U+00B7	U+0307	U+0387	U+2027	#	MIDDLE DOT
+0xB8	U+0388	U+1fc9	#	GREEK CAPITAL LETTER EPSILON WITH TONOS
+0xB9	U+0389	U+1fcb	#	GREEK CAPITAL LETTER ETA WITH TONOS
+0xBA	U+038A	U+1fdb	#	GREEK CAPITAL LETTER IOTA WITH TONOS
+0xBB	U+00BB	#	RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC	U+038C	U+1ff9	#	GREEK CAPITAL LETTER OMICRON WITH TONOS
+0xBD	U+00BD	#	VULGAR FRACTION ONE HALF
+0xBE	U+038E	U+1feb	#	GREEK CAPITAL LETTER UPSILON WITH TONOS
+0xBF	U+038F	U+1ffb	#	GREEK CAPITAL LETTER OMEGA WITH TONOS
+0xC0	U+0390	#	GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+0xC1	U+0391	#	GREEK CAPITAL LETTER ALPHA
+0xC2	U+0392	#	GREEK CAPITAL LETTER BETA
+0xC3	U+0393	U+0413	#	GREEK CAPITAL LETTER GAMMA
+0xC4	U+0394	#	GREEK CAPITAL LETTER DELTA
+0xC5	U+0395	#	GREEK CAPITAL LETTER EPSILON
+0xC6	U+0396	#	GREEK CAPITAL LETTER ZETA
+0xC7	U+0397	#	GREEK CAPITAL LETTER ETA
+0xC8	U+0398	#	GREEK CAPITAL LETTER THETA
+0xC9	U+0399	#	GREEK CAPITAL LETTER IOTA
+0xCA	U+039A	#	GREEK CAPITAL LETTER KAPPA
+0xCB	U+039B	U+041b	#	GREEK CAPITAL LETTER LAMDA
+0xCC	U+039C	#	GREEK CAPITAL LETTER MU
+0xCD	U+039D	#	GREEK CAPITAL LETTER NU
+0xCE	U+039E	#	GREEK CAPITAL LETTER XI
+0xCF	U+039F	#	GREEK CAPITAL LETTER OMICRON
+0xD0	U+03A0	U+041f	#	GREEK CAPITAL LETTER PI
+0xD1	U+03A1	#	GREEK CAPITAL LETTER RHO
+0xD3	U+03A3	#	GREEK CAPITAL LETTER SIGMA
+0xD4	U+03A4	#	GREEK CAPITAL LETTER TAU
+0xD5	U+03A5	#	GREEK CAPITAL LETTER UPSILON
+0xD6	U+03A6	U+0424	#	GREEK CAPITAL LETTER PHI
+0xD7	U+03A7	U+0425	#	GREEK CAPITAL LETTER CHI
+0xD8	U+03A8	#	GREEK CAPITAL LETTER PSI
+0xD9	U+03A9	#	GREEK CAPITAL LETTER OMEGA
+0xDA	U+03AA	#	GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
+0xDB	U+03AB	#	GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
+0xDC	U+03AC	U+1f71	#	GREEK SMALL LETTER ALPHA WITH TONOS
+0xDD	U+03AD	U+1f73	#	GREEK SMALL LETTER EPSILON WITH TONOS
+0xDE	U+03AE	U+1f75	#	GREEK SMALL LETTER ETA WITH TONOS
+0xDF	U+03AF	U+1f77	#	GREEK SMALL LETTER IOTA WITH TONOS
+0xE0	U+03B0	#	GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+0xE1	U+03B1	#	GREEK SMALL LETTER ALPHA
+0xE2	U+03B2	#	GREEK SMALL LETTER BETA
+0xE3	U+03B3	U+0263	#	GREEK SMALL LETTER GAMMA
+0xE4	U+03B4	U+00f0	#	GREEK SMALL LETTER DELTA
+0xE5	U+03B5	#	GREEK SMALL LETTER EPSILON
+0xE6	U+03B6	#	GREEK SMALL LETTER ZETA
+0xE7	U+03B7	#	GREEK SMALL LETTER ETA
+0xE8	U+03B8	#	GREEK SMALL LETTER THETA
+0xE9	U+03B9	U+0131	#	GREEK SMALL LETTER IOTA
+0xEA	U+03BA	#	GREEK SMALL LETTER KAPPA
+0xEB	U+03BB	#	GREEK SMALL LETTER LAMDA
+0xEC	U+03BC	U+00b5	#	GREEK SMALL LETTER MU
+0xED	U+03BD	#	GREEK SMALL LETTER NU
+0xEE	U+03BE	#	GREEK SMALL LETTER XI
+0xEF	U+03BF	#	GREEK SMALL LETTER OMICRON
+0xF0	U+03C0	#	GREEK SMALL LETTER PI
+0xF1	U+03C1	#	GREEK SMALL LETTER RHO
+0xF2	U+03C2	#	GREEK SMALL LETTER FINAL SIGMA
+0xF3	U+03C3	#	GREEK SMALL LETTER SIGMA
+0xF4	U+03C4	#	GREEK SMALL LETTER TAU
+0xF5	U+03C5	U+028a	#	GREEK SMALL LETTER UPSILON
+0xF6	U+03C6	#	GREEK SMALL LETTER PHI
+0xF7	U+03C7	#	GREEK SMALL LETTER CHI
+0xF8	U+03C8	#	GREEK SMALL LETTER PSI
+0xF9	U+03C9	#	GREEK SMALL LETTER OMEGA
+0xFA	U+03CA	#	GREEK SMALL LETTER IOTA WITH DIALYTIKA
+0xFB	U+03CB	U+00fc	#	GREEK SMALL LETTER UPSILON WITH DIALYTIKA
+0xFC	U+03CC	U+1f79	#	GREEK SMALL LETTER OMICRON WITH TONOS
+0xFD	U+03CD	U+1f7b	#	GREEK SMALL LETTER UPSILON WITH TONOS
+0xFE	U+03CE	U+1f7d	#	GREEK SMALL LETTER OMEGA WITH TONOS
+
+U+2218 " \260 "		# RING OPERATOR
+U+2209 " !\345 "
+U+221b " ROOT\263 "
+U+229A "(\260)"		# CIRCLED RING OPERATOR
+U+02a4 "d\346"
+U+20af "\304\361\367"
diff --git a/src/chrtrans/iso08_uni.tbl b/src/chrtrans/iso08_uni.tbl
new file mode 100644
index 00000000..d162396a
--- /dev/null
+++ b/src/chrtrans/iso08_uni.tbl
@@ -0,0 +1,229 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Miso-8859-8
+
+#Name as a Display Charset (used on Options screen).
+OHebrew (ISO-8859-8)
+
+#Codepage number
+C916
+
+#
+#	Name:             ISO/IEC 8859-8:1999 to Unicode
+#	Unicode version:  3.0
+#	Table version:    1.1
+#	Table format:     Format A
+#	Date:             2000-Jan-03
+#	Authors:          Ken Whistler <kenw@sybase.com>
+#
+#	Copyright (c) 1991-1999 Unicode, Inc.  All Rights reserved.
+#
+#	This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
+#	No claims are made as to fitness for any particular purpose.  No
+#	warranties of any kind are expressed or implied.  The recipient
+#	agrees to determine applicability of information provided.  If this
+#	file has been provided on optical media by Unicode, Inc., the sole
+#	remedy for any claim will be exchange of defective media within 90
+#	days of receipt.
+#
+#	Unicode, Inc. hereby grants the right to freely use the information
+#	supplied in this file in the creation of products supporting the
+#	Unicode Standard, and to make copies of this file in any form for
+#	internal or external distribution as long as this notice remains
+#	attached.
+#
+#	General notes:
+#
+#	This table contains the data the Unicode Consortium has on how
+#	ISO/IEC 8859-8:1999 characters map into Unicode.
+#
+#	Format:  Three tab-separated columns
+#		 Column #1 is the ISO/IEC 8859-8 code (in hex as 0xXX)
+#		 Column #2 is the Unicode (in hex as 0xXXXX)
+#		 Column #3 the Unicode name (follows a comment sign, '#')
+#
+#	The entries are in ISO/IEC 8859-8 order.
+#
+#	Version history
+#	1.0 version updates 0.1 version by adding mappings for all
+#	control characters.
+#       1.1 version updates to the published 8859-8:1999, correcting
+#          the mapping of 0xAF and adding mappings for LRM and RLM.
+#
+#	Updated versions of this file may be found in:
+#		<ftp://ftp.unicode.org/Public/MAPPINGS/>
+#
+#	Any comments or problems, contact <errata@unicode.org>
+#	Please note that <errata@unicode.org> is an archival address;
+#	notices will be checked, but do not expect an immediate response.
+#
+0x20-0x7e idem
+#
+#0x20	U+0020	#	SPACE
+#0x21	U+0021	#	EXCLAMATION MARK
+#0x22	U+0022	#	QUOTATION MARK
+#0x23	U+0023	#	NUMBER SIGN
+#0x24	U+0024	#	DOLLAR SIGN
+#0x25	U+0025	#	PERCENT SIGN
+#0x26	U+0026	#	AMPERSAND
+#0x27	U+0027	#	APOSTROPHE
+#0x28	U+0028	#	LEFT PARENTHESIS
+#0x29	U+0029	#	RIGHT PARENTHESIS
+#0x2A	U+002A	#	ASTERISK
+#0x2B	U+002B	#	PLUS SIGN
+#0x2C	U+002C	#	COMMA
+#0x2D	U+002D	#	HYPHEN-MINUS
+#0x2E	U+002E	#	FULL STOP
+#0x2F	U+002F	#	SOLIDUS
+#0x30	U+0030	#	DIGIT ZERO
+#0x31	U+0031	#	DIGIT ONE
+#0x32	U+0032	#	DIGIT TWO
+#0x33	U+0033	#	DIGIT THREE
+#0x34	U+0034	#	DIGIT FOUR
+#0x35	U+0035	#	DIGIT FIVE
+#0x36	U+0036	#	DIGIT SIX
+#0x37	U+0037	#	DIGIT SEVEN
+#0x38	U+0038	#	DIGIT EIGHT
+#0x39	U+0039	#	DIGIT NINE
+#0x3A	U+003A	#	COLON
+#0x3B	U+003B	#	SEMICOLON
+#0x3C	U+003C	#	LESS-THAN SIGN
+#0x3D	U+003D	#	EQUALS SIGN
+#0x3E	U+003E	#	GREATER-THAN SIGN
+#0x3F	U+003F	#	QUESTION MARK
+#0x40	U+0040	#	COMMERCIAL AT
+#0x41	U+0041	#	LATIN CAPITAL LETTER A
+#0x42	U+0042	#	LATIN CAPITAL LETTER B
+#0x43	U+0043	#	LATIN CAPITAL LETTER C
+#0x44	U+0044	#	LATIN CAPITAL LETTER D
+#0x45	U+0045	#	LATIN CAPITAL LETTER E
+#0x46	U+0046	#	LATIN CAPITAL LETTER F
+#0x47	U+0047	#	LATIN CAPITAL LETTER G
+#0x48	U+0048	#	LATIN CAPITAL LETTER H
+#0x49	U+0049	#	LATIN CAPITAL LETTER I
+#0x4A	U+004A	#	LATIN CAPITAL LETTER J
+#0x4B	U+004B	#	LATIN CAPITAL LETTER K
+#0x4C	U+004C	#	LATIN CAPITAL LETTER L
+#0x4D	U+004D	#	LATIN CAPITAL LETTER M
+#0x4E	U+004E	#	LATIN CAPITAL LETTER N
+#0x4F	U+004F	#	LATIN CAPITAL LETTER O
+#0x50	U+0050	#	LATIN CAPITAL LETTER P
+#0x51	U+0051	#	LATIN CAPITAL LETTER Q
+#0x52	U+0052	#	LATIN CAPITAL LETTER R
+#0x53	U+0053	#	LATIN CAPITAL LETTER S
+#0x54	U+0054	#	LATIN CAPITAL LETTER T
+#0x55	U+0055	#	LATIN CAPITAL LETTER U
+#0x56	U+0056	#	LATIN CAPITAL LETTER V
+#0x57	U+0057	#	LATIN CAPITAL LETTER W
+#0x58	U+0058	#	LATIN CAPITAL LETTER X
+#0x59	U+0059	#	LATIN CAPITAL LETTER Y
+#0x5A	U+005A	#	LATIN CAPITAL LETTER Z
+#0x5B	U+005B	#	LEFT SQUARE BRACKET
+#0x5C	U+005C	#	REVERSE SOLIDUS
+#0x5D	U+005D	#	RIGHT SQUARE BRACKET
+#0x5E	U+005E	#	CIRCUMFLEX ACCENT
+#0x5F	U+005F	#	LOW LINE
+#0x60	U+0060	#	GRAVE ACCENT
+#0x61	U+0061	#	LATIN SMALL LETTER A
+#0x62	U+0062	#	LATIN SMALL LETTER B
+#0x63	U+0063	#	LATIN SMALL LETTER C
+#0x64	U+0064	#	LATIN SMALL LETTER D
+#0x65	U+0065	#	LATIN SMALL LETTER E
+#0x66	U+0066	#	LATIN SMALL LETTER F
+#0x67	U+0067	#	LATIN SMALL LETTER G
+#0x68	U+0068	#	LATIN SMALL LETTER H
+#0x69	U+0069	#	LATIN SMALL LETTER I
+#0x6A	U+006A	#	LATIN SMALL LETTER J
+#0x6B	U+006B	#	LATIN SMALL LETTER K
+#0x6C	U+006C	#	LATIN SMALL LETTER L
+#0x6D	U+006D	#	LATIN SMALL LETTER M
+#0x6E	U+006E	#	LATIN SMALL LETTER N
+#0x6F	U+006F	#	LATIN SMALL LETTER O
+#0x70	U+0070	#	LATIN SMALL LETTER P
+#0x71	U+0071	#	LATIN SMALL LETTER Q
+#0x72	U+0072	#	LATIN SMALL LETTER R
+#0x73	U+0073	#	LATIN SMALL LETTER S
+#0x74	U+0074	#	LATIN SMALL LETTER T
+#0x75	U+0075	#	LATIN SMALL LETTER U
+#0x76	U+0076	#	LATIN SMALL LETTER V
+#0x77	U+0077	#	LATIN SMALL LETTER W
+#0x78	U+0078	#	LATIN SMALL LETTER X
+#0x79	U+0079	#	LATIN SMALL LETTER Y
+#0x7A	U+007A	#	LATIN SMALL LETTER Z
+#0x7B	U+007B	#	LEFT CURLY BRACKET
+#0x7C	U+007C	#	VERTICAL LINE
+#0x7D	U+007D	#	RIGHT CURLY BRACKET
+#0x7E	U+007E	#	TILDE
+0xA0	U+00A0	#	NO-BREAK SPACE
+0xA2	U+00A2	#	CENT SIGN
+0xA3	U+00A3	#	POUND SIGN
+0xA4	U+00A4	#	CURRENCY SIGN
+0xA5	U+00A5	#	YEN SIGN
+0xA6	U+00A6	#	BROKEN BAR
+0xA7	U+00A7	#	SECTION SIGN
+0xA8	U+00A8	#	DIAERESIS
+0xA9	U+00A9	#	COPYRIGHT SIGN
+0xAA	U+00D7	#	MULTIPLICATION SIGN
+0xAB	U+00AB	#	LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC	U+00AC	#	NOT SIGN
+0xAD	U+00AD	#	SOFT HYPHEN
+0xAE	U+00AE	#	REGISTERED SIGN
+0xAF	U+00AF	#	MACRON
+0xB0	U+00B0	#	DEGREE SIGN
+0xB1	U+00B1	#	PLUS-MINUS SIGN
+0xB2	U+00B2	#	SUPERSCRIPT TWO
+0xB3	U+00B3	#	SUPERSCRIPT THREE
+0xB4	U+00B4	#	ACUTE ACCENT
+0xB5	U+00B5	#	MICRO SIGN
+0xB6	U+00B6	#	PILCROW SIGN
+0xB7	U+00B7	#	MIDDLE DOT
+0xB8	U+00B8	#	CEDILLA
+0xB9	U+00B9	#	SUPERSCRIPT ONE
+0xBA	U+00F7	#	DIVISION SIGN
+0xBB	U+00BB	#	RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC	U+00BC	#	VULGAR FRACTION ONE QUARTER
+0xBD	U+00BD	#	VULGAR FRACTION ONE HALF
+0xBE	U+00BE	#	VULGAR FRACTION THREE QUARTERS
+0xDF	U+2017	#	DOUBLE LOW LINE
+0xE0	U+05D0	#	HEBREW LETTER ALEF
+0xE1	U+05D1	#	HEBREW LETTER BET
+0xE2	U+05D2	#	HEBREW LETTER GIMEL
+0xE3	U+05D3	#	HEBREW LETTER DALET
+0xE4	U+05D4	#	HEBREW LETTER HE
+0xE5	U+05D5	#	HEBREW LETTER VAV
+0xE6	U+05D6	#	HEBREW LETTER ZAYIN
+0xE7	U+05D7	#	HEBREW LETTER HET
+0xE8	U+05D8	#	HEBREW LETTER TET
+0xE9	U+05D9	#	HEBREW LETTER YOD
+0xEA	U+05DA	#	HEBREW LETTER FINAL KAF
+0xEB	U+05DB	#	HEBREW LETTER KAF
+0xEC	U+05DC	#	HEBREW LETTER LAMED
+0xED	U+05DD	#	HEBREW LETTER FINAL MEM
+0xEE	U+05DE	#	HEBREW LETTER MEM
+0xEF	U+05DF	#	HEBREW LETTER FINAL NUN
+0xF0	U+05E0	#	HEBREW LETTER NUN
+0xF1	U+05E1	#	HEBREW LETTER SAMEKH
+0xF2	U+05E2	#	HEBREW LETTER AYIN
+0xF3	U+05E3	#	HEBREW LETTER FINAL PE
+0xF4	U+05E4	#	HEBREW LETTER PE
+0xF5	U+05E5	#	HEBREW LETTER FINAL TSADI
+0xF6	U+05E6	#	HEBREW LETTER TSADI
+0xF7	U+05E7	#	HEBREW LETTER QOF
+0xF8	U+05E8	#	HEBREW LETTER RESH
+0xF9	U+05E9	#	HEBREW LETTER SHIN
+0xFA	U+05EA	#	HEBREW LETTER TAV
+0xFD	U+200E	#	LEFT-TO-RIGHT MARK
+0xFE	U+200F	#	RIGHT-TO-LEFT MARK
+
+
+#Hebrew points - map to empty string
+U+05B0-U+05C2:
+
+#HEBREW LETTER DOUBLE VAV	#U+05F0:åå
+U+05F0 "\345\345"
+#HEBREW LETTER VAV YOD		#U+05F1:éå
+U+05F1 "\351\345"
+#HEBREW LETTER DOUBLE YOD	#U+05F2:éé
+U+05F2 "\351\351"
+
diff --git a/src/chrtrans/iso09_uni.tbl b/src/chrtrans/iso09_uni.tbl
new file mode 100644
index 00000000..87afe48b
--- /dev/null
+++ b/src/chrtrans/iso09_uni.tbl
@@ -0,0 +1,266 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Miso-8859-9
+
+#Name as a Display Charset (used on Options screen)
+OTurkish (ISO-8859-9)
+
+#Codepage number
+C920
+
+#
+#	Name:             ISO/IEC 8859-9:1999 to Unicode
+#	Unicode version:  3.0
+#	Table version:    1.0
+#	Table format:     Format A
+#	Date:             1999 July 27
+#	Authors:          Ken Whistler <kenw@sybase.com>
+#
+#	Copyright (c) 1991-1999 Unicode, Inc.  All Rights reserved.
+#
+#	This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
+#	No claims are made as to fitness for any particular purpose.  No
+#	warranties of any kind are expressed or implied.  The recipient
+#	agrees to determine applicability of information provided.  If this
+#	file has been provided on magnetic media by Unicode, Inc., the sole
+#	remedy for any claim will be exchange of defective media within 90
+#	days of receipt.
+#
+#	Unicode, Inc. hereby grants the right to freely use the information
+#	supplied in this file in the creation of products supporting the
+#	Unicode Standard, and to make copies of this file in any form for
+#	internal or external distribution as long as this notice remains
+#	attached.
+#
+#	General notes:
+#
+#	This table contains the data the Unicode Consortium has on how
+#	ISO/IEC 8859-9:1999 characters map into Unicode.
+#
+#	Format:  Three tab-separated columns
+#		 Column #1 is the ISO/IEC 8859-9 code (in hex as 0xXX)
+#		 Column #2 is the Unicode (in hex as 0xXXXX)
+#		 Column #3 the Unicode name (follows a comment sign, '#')
+#
+#	The entries are in ISO/IEC 8859-9 order.
+#
+#	ISO/IEC 8859-9 is also equivalent to ISO-IR-148.
+#
+#	Version history
+#	1.0 version updates 0.1 version by adding mappings for all
+#	control characters.
+#
+#	Updated versions of this file may be found in:
+#		<ftp://ftp.unicode.org/Public/MAPPINGS/>
+#
+#	Any comments or problems, contact <errata@unicode.org>
+#	Please note that <errata@unicode.org> is an archival address;
+#	notices will be checked, but do not expect an immediate response.
+#
+# Lines with more than one Unicode (U+XXXX) value contain additional
+# replacement mappings added for lynx. - kw
+#
+0x20-0x7e idem
+0x49	U+042b
+#
+#0x20	U+0020	#	SPACE
+#0x21	U+0021	#	EXCLAMATION MARK
+#0x22	U+0022	#	QUOTATION MARK
+#0x23	U+0023	#	NUMBER SIGN
+#0x24	U+0024	#	DOLLAR SIGN
+#0x25	U+0025	#	PERCENT SIGN
+#0x26	U+0026	#	AMPERSAND
+#0x27	U+0027	#	APOSTROPHE
+#0x28	U+0028	#	LEFT PARENTHESIS
+#0x29	U+0029	#	RIGHT PARENTHESIS
+#0x2A	U+002A	#	ASTERISK
+#0x2B	U+002B	#	PLUS SIGN
+#0x2C	U+002C	#	COMMA
+#0x2D	U+002D	#	HYPHEN-MINUS
+#0x2E	U+002E	#	FULL STOP
+#0x2F	U+002F	#	SOLIDUS
+#0x30	U+0030	#	DIGIT ZERO
+#0x31	U+0031	#	DIGIT ONE
+#0x32	U+0032	#	DIGIT TWO
+#0x33	U+0033	#	DIGIT THREE
+#0x34	U+0034	#	DIGIT FOUR
+#0x35	U+0035	#	DIGIT FIVE
+#0x36	U+0036	#	DIGIT SIX
+#0x37	U+0037	#	DIGIT SEVEN
+#0x38	U+0038	#	DIGIT EIGHT
+#0x39	U+0039	#	DIGIT NINE
+#0x3A	U+003A	#	COLON
+#0x3B	U+003B	#	SEMICOLON
+#0x3C	U+003C	#	LESS-THAN SIGN
+#0x3D	U+003D	#	EQUALS SIGN
+#0x3E	U+003E	#	GREATER-THAN SIGN
+#0x3F	U+003F	#	QUESTION MARK
+#0x40	U+0040	#	COMMERCIAL AT
+#0x41	U+0041	#	LATIN CAPITAL LETTER A
+#0x42	U+0042	#	LATIN CAPITAL LETTER B
+#0x43	U+0043	#	LATIN CAPITAL LETTER C
+#0x44	U+0044	#	LATIN CAPITAL LETTER D
+#0x45	U+0045	#	LATIN CAPITAL LETTER E
+#0x46	U+0046	#	LATIN CAPITAL LETTER F
+#0x47	U+0047	#	LATIN CAPITAL LETTER G
+#0x48	U+0048	#	LATIN CAPITAL LETTER H
+#0x49	U+0049	#	LATIN CAPITAL LETTER I
+#0x4A	U+004A	#	LATIN CAPITAL LETTER J
+#0x4B	U+004B	#	LATIN CAPITAL LETTER K
+#0x4C	U+004C	#	LATIN CAPITAL LETTER L
+#0x4D	U+004D	#	LATIN CAPITAL LETTER M
+#0x4E	U+004E	#	LATIN CAPITAL LETTER N
+#0x4F	U+004F	#	LATIN CAPITAL LETTER O
+#0x50	U+0050	#	LATIN CAPITAL LETTER P
+#0x51	U+0051	#	LATIN CAPITAL LETTER Q
+#0x52	U+0052	#	LATIN CAPITAL LETTER R
+#0x53	U+0053	#	LATIN CAPITAL LETTER S
+#0x54	U+0054	#	LATIN CAPITAL LETTER T
+#0x55	U+0055	#	LATIN CAPITAL LETTER U
+#0x56	U+0056	#	LATIN CAPITAL LETTER V
+#0x57	U+0057	#	LATIN CAPITAL LETTER W
+#0x58	U+0058	#	LATIN CAPITAL LETTER X
+#0x59	U+0059	#	LATIN CAPITAL LETTER Y
+#0x5A	U+005A	#	LATIN CAPITAL LETTER Z
+#0x5B	U+005B	#	LEFT SQUARE BRACKET
+#0x5C	U+005C	#	REVERSE SOLIDUS
+#0x5D	U+005D	#	RIGHT SQUARE BRACKET
+#0x5E	U+005E	#	CIRCUMFLEX ACCENT
+#0x5F	U+005F	#	LOW LINE
+#0x60	U+0060	#	GRAVE ACCENT
+#0x61	U+0061	#	LATIN SMALL LETTER A
+#0x62	U+0062	#	LATIN SMALL LETTER B
+#0x63	U+0063	#	LATIN SMALL LETTER C
+#0x64	U+0064	#	LATIN SMALL LETTER D
+#0x65	U+0065	#	LATIN SMALL LETTER E
+#0x66	U+0066	#	LATIN SMALL LETTER F
+#0x67	U+0067	#	LATIN SMALL LETTER G
+#0x68	U+0068	#	LATIN SMALL LETTER H
+#0x69	U+0069	#	LATIN SMALL LETTER I
+#0x6A	U+006A	#	LATIN SMALL LETTER J
+#0x6B	U+006B	#	LATIN SMALL LETTER K
+#0x6C	U+006C	#	LATIN SMALL LETTER L
+#0x6D	U+006D	#	LATIN SMALL LETTER M
+#0x6E	U+006E	#	LATIN SMALL LETTER N
+#0x6F	U+006F	#	LATIN SMALL LETTER O
+#0x70	U+0070	#	LATIN SMALL LETTER P
+#0x71	U+0071	#	LATIN SMALL LETTER Q
+#0x72	U+0072	#	LATIN SMALL LETTER R
+#0x73	U+0073	#	LATIN SMALL LETTER S
+#0x74	U+0074	#	LATIN SMALL LETTER T
+#0x75	U+0075	#	LATIN SMALL LETTER U
+#0x76	U+0076	#	LATIN SMALL LETTER V
+#0x77	U+0077	#	LATIN SMALL LETTER W
+#0x78	U+0078	#	LATIN SMALL LETTER X
+#0x79	U+0079	#	LATIN SMALL LETTER Y
+#0x7A	U+007A	#	LATIN SMALL LETTER Z
+#0x7B	U+007B	#	LEFT CURLY BRACKET
+#0x7C	U+007C	#	VERTICAL LINE
+#0x7D	U+007D	#	RIGHT CURLY BRACKET
+#0x7E	U+007E	#	TILDE
+0xA0	U+00A0	#	NO-BREAK SPACE
+0xA1	U+00A1	#	INVERTED EXCLAMATION MARK
+0xA2	U+00A2	#	CENT SIGN
+0xA3	U+00A3	#	POUND SIGN
+0xA4	U+00A4	#	CURRENCY SIGN
+0xA5	U+00A5	#	YEN SIGN
+0xA6	U+00A6	#	BROKEN BAR
+0xA7	U+00A7	#	SECTION SIGN
+0xA8	U+00A8	U+0308	#	DIAERESIS
+0xA9	U+00A9	#	COPYRIGHT SIGN
+0xAA	U+00AA	#	FEMININE ORDINAL INDICATOR
+0xAB	U+00AB	#	LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC	U+00AC	#	NOT SIGN
+0xAD	U+00AD	#	SOFT HYPHEN
+0xAE	U+00AE	#	REGISTERED SIGN
+0xAF	U+00AF	U+0304	#	MACRON
+0xB0	U+00B0	U+030a	#	DEGREE SIGN
+0xB1	U+00B1	#	PLUS-MINUS SIGN
+0xB2	U+00B2	#	SUPERSCRIPT TWO
+0xB3	U+00B3	#	SUPERSCRIPT THREE
+0xB4	U+00B4	#	ACUTE ACCENT
+0xB5	U+00B5	U+03bc	#	MICRO SIGN
+0xB6	U+00B6	#	PILCROW SIGN
+0xB7	U+00B7	U+0307	U+0387	#	MIDDLE DOT
+0xB8	U+00B8	U+0327	#	CEDILLA
+0xB9	U+00B9	#	SUPERSCRIPT ONE
+0xBA	U+00BA	#	MASCULINE ORDINAL INDICATOR
+0xBB	U+00BB	#	RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC	U+00BC	#	VULGAR FRACTION ONE QUARTER
+0xBD	U+00BD	#	VULGAR FRACTION ONE HALF
+0xBE	U+00BE	#	VULGAR FRACTION THREE QUARTERS
+0xBF	U+00BF	#	INVERTED QUESTION MARK
+0xC0	U+00C0	#	LATIN CAPITAL LETTER A WITH GRAVE
+0xC1	U+00C1	#	LATIN CAPITAL LETTER A WITH ACUTE
+0xC2	U+00C2	#	LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xC3	U+00C3	#	LATIN CAPITAL LETTER A WITH TILDE
+0xC4	U+00C4	#	LATIN CAPITAL LETTER A WITH DIAERESIS
+0xC5	U+00C5	#	LATIN CAPITAL LETTER A WITH RING ABOVE
+0xC6	U+00C6	#	LATIN CAPITAL LETTER AE
+0xC7	U+00C7	#	LATIN CAPITAL LETTER C WITH CEDILLA
+0xC8	U+00C8	#	LATIN CAPITAL LETTER E WITH GRAVE
+0xC9	U+00C9	#	LATIN CAPITAL LETTER E WITH ACUTE
+0xCA	U+00CA	#	LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+0xCB	U+00CB	#	LATIN CAPITAL LETTER E WITH DIAERESIS
+0xCC	U+00CC	#	LATIN CAPITAL LETTER I WITH GRAVE
+0xCD	U+00CD	#	LATIN CAPITAL LETTER I WITH ACUTE
+0xCE	U+00CE	#	LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xCF	U+00CF	#	LATIN CAPITAL LETTER I WITH DIAERESIS
+0xD0	U+011E	#	LATIN CAPITAL LETTER G WITH BREVE
+0xD1	U+00D1	#	LATIN CAPITAL LETTER N WITH TILDE
+0xD2	U+00D2	#	LATIN CAPITAL LETTER O WITH GRAVE
+0xD3	U+00D3	#	LATIN CAPITAL LETTER O WITH ACUTE
+0xD4	U+00D4	#	LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xD5	U+00D5	#	LATIN CAPITAL LETTER O WITH TILDE
+0xD6	U+00D6	#	LATIN CAPITAL LETTER O WITH DIAERESIS
+0xD7	U+00D7	#	MULTIPLICATION SIGN
+0xD8	U+00D8	#	LATIN CAPITAL LETTER O WITH STROKE
+0xD9	U+00D9	#	LATIN CAPITAL LETTER U WITH GRAVE
+0xDA	U+00DA	#	LATIN CAPITAL LETTER U WITH ACUTE
+0xDB	U+00DB	#	LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+0xDC	U+00DC	#	LATIN CAPITAL LETTER U WITH DIAERESIS
+0xDD	U+0130	U+0418	U+0406	#	LATIN CAPITAL LETTER I WITH DOT ABOVE
+0xDE	U+015E	U+0428	#	LATIN CAPITAL LETTER S WITH CEDILLA
+0xDF	U+00DF	#	LATIN SMALL LETTER SHARP S
+0xE0	U+00E0	#	LATIN SMALL LETTER A WITH GRAVE
+0xE1	U+00E1	#	LATIN SMALL LETTER A WITH ACUTE
+0xE2	U+00E2	#	LATIN SMALL LETTER A WITH CIRCUMFLEX
+0xE3	U+00E3	#	LATIN SMALL LETTER A WITH TILDE
+0xE4	U+00E4	#	LATIN SMALL LETTER A WITH DIAERESIS
+0xE5	U+00E5	#	LATIN SMALL LETTER A WITH RING ABOVE
+0xE6	U+00E6	#	LATIN SMALL LETTER AE
+0xE7	U+00E7	#	LATIN SMALL LETTER C WITH CEDILLA
+0xE8	U+00E8	#	LATIN SMALL LETTER E WITH GRAVE
+0xE9	U+00E9	#	LATIN SMALL LETTER E WITH ACUTE
+0xEA	U+00EA	#	LATIN SMALL LETTER E WITH CIRCUMFLEX
+0xEB	U+00EB	#	LATIN SMALL LETTER E WITH DIAERESIS
+0xEC	U+00EC	#	LATIN SMALL LETTER I WITH GRAVE
+0xED	U+00ED	#	LATIN SMALL LETTER I WITH ACUTE
+0xEE	U+00EE	#	LATIN SMALL LETTER I WITH CIRCUMFLEX
+0xEF	U+00EF	#	LATIN SMALL LETTER I WITH DIAERESIS
+0xF0	U+011F	#	LATIN SMALL LETTER G WITH BREVE
+0xF1	U+00F1	#	LATIN SMALL LETTER N WITH TILDE
+0xF2	U+00F2	#	LATIN SMALL LETTER O WITH GRAVE
+0xF3	U+00F3	#	LATIN SMALL LETTER O WITH ACUTE
+0xF4	U+00F4	#	LATIN SMALL LETTER O WITH CIRCUMFLEX
+0xF5	U+00F5	#	LATIN SMALL LETTER O WITH TILDE
+0xF6	U+00F6	#	LATIN SMALL LETTER O WITH DIAERESIS
+0xF7	U+00F7	#	DIVISION SIGN
+0xF8	U+00F8	#	LATIN SMALL LETTER O WITH STROKE
+0xF9	U+00F9	#	LATIN SMALL LETTER U WITH GRAVE
+0xFA	U+00FA	#	LATIN SMALL LETTER U WITH ACUTE
+0xFB	U+00FB	#	LATIN SMALL LETTER U WITH CIRCUMFLEX
+0xFC	U+00FC	#	LATIN SMALL LETTER U WITH DIAERESIS
+0xFD	U+0131	U+03b9	U+044b	#	LATIN SMALL LETTER DOTLESS I
+0xFE	U+015F	U+0448	#	LATIN SMALL LETTER S WITH CEDILLA
+0xFF	U+00FF	#	LATIN SMALL LETTER Y WITH DIAERESIS
+
+U+2218 " \260 "		# RING OPERATOR
+U+221b " ROOT\263 "
+U+2297 "(\327)"		# CIRCLED TIMES
+U+2299 "(\267)"		# CIRCLED DOT OPERATOR
+U+229A "(\260)"		# CIRCLED RING OPERATOR
+U+22A0 "[\327]"		# SQUARED TIMES
+U+22A1 "[\267]"		# SQUARED DOT OPERATOR
+U+22C5 " \267 "		# DOT OPERATOR
diff --git a/src/chrtrans/iso10_uni.tbl b/src/chrtrans/iso10_uni.tbl
new file mode 100644
index 00000000..4c6605c8
--- /dev/null
+++ b/src/chrtrans/iso10_uni.tbl
@@ -0,0 +1,153 @@
+#
+# Unicode mapping table for the fonts iso10.*
+# [use: unicode_start iso10.f16 iso10]
+#
+#This is not default font!
+D0
+
+#The MIME name of this charset.
+
+Miso-8859-10
+
+#Name as a Display Charset (used on Options screen)
+ONorth European (ISO-8859-10)
+
+# Name:		ISO 8859-10 Latin 6 (1998) to Unicode
+# Date:		2005-12-15
+# Authors:	Thomas E Dickey from 
+#		http://czyborra.com/charsets/iso8859.html
+#		(ISO 8859 Alphabet Soup)
+
+0x20-0x7e	idem
+
+0xa0	U+00a0	# NO-BREAK SPACE
+0xa1	U+0104	# LATIN CAPITAL LETTER A WITH OGONEK
+0xa2	U+0112	# LATIN CAPITAL LETTER E WITH MACRON
+0xa3	U+0122	# LATIN CAPITAL LETTER G WITH CEDILLA
+0xa4	U+012a	# LATIN CAPITAL LETTER I WITH MACRON
+0xa5	U+0128	# LATIN CAPITAL LETTER I WITH TILDE
+0xa6	U+0136	# LATIN CAPITAL LETTER K WITH CEDILLA
+0xa7	U+00a7	# SECTION SIGN
+0xa8	U+013b	# LATIN CAPITAL LETTER L WITH CEDILLA
+0xa9	U+0110	# LATIN CAPITAL LETTER D WITH STROKE
+0xaa	U+0160	# LATIN CAPITAL LETTER S WITH CARON
+0xab	U+0166	# LATIN CAPITAL LETTER T WITH STROKE
+0xac	U+017d	# LATIN CAPITAL LETTER Z WITH CARON
+0xad	U+00ad	# SOFT HYPHEN
+0xae	U+016a	# LATIN CAPITAL LETTER U WITH MACRON
+0xaf	U+014a	# LATIN CAPITAL LETTER ENG
+0xb0	U+00b0	# DEGREE SIGN
+0xb1	U+0105	# LATIN SMALL LETTER A WITH OGONEK
+0xb2	U+0113	# LATIN SMALL LETTER E WITH MACRON
+0xb3	U+0123	# LATIN SMALL LETTER G WITH CEDILLA
+0xb4	U+012b	# LATIN SMALL LETTER I WITH MACRON
+0xb5	U+0129	# LATIN SMALL LETTER I WITH TILDE
+0xb6	U+0137	# LATIN SMALL LETTER K WITH CEDILLA
+0xb7	U+00b7	# MIDDLE DOT
+0xb8	U+013c	# LATIN SMALL LETTER L WITH CEDILLA
+0xb9	U+0111	# LATIN SMALL LETTER D WITH STROKE
+0xba	U+0161	# LATIN SMALL LETTER S WITH CARON
+0xbb	U+0167	# LATIN SMALL LETTER T WITH STROKE
+0xbc	U+017e	# LATIN SMALL LETTER Z WITH CARON
+0xbd	U+2015	# HORIZONTAL BAR
+0xbe	U+016b	# LATIN SMALL LETTER U WITH MACRON
+0xbf	U+014b	# LATIN SMALL LETTER ENG
+0xc0	U+0100	# LATIN CAPITAL LETTER A WITH MACRON
+
+0xc1-0xc6	idem
+
+#0xc1	U+00c1	# LATIN CAPITAL LETTER A WITH ACUTE
+#0xc2	U+00c2	# LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+#0xc3	U+00c3	# LATIN CAPITAL LETTER A WITH TILDE
+#0xc4	U+00c4	# LATIN CAPITAL LETTER A WITH DIAERESIS
+#0xc5	U+00c5	# LATIN CAPITAL LETTER A WITH RING ABOVE
+#0xc6	U+00c6	# LATIN CAPITAL LETTER AE
+
+0xc7	U+012e	# LATIN CAPITAL LETTER I WITH OGONEK
+0xc8	U+010c	# LATIN CAPITAL LETTER C WITH CARON
+0xc9	U+00c9	# LATIN CAPITAL LETTER E WITH ACUTE
+0xca	U+0118	# LATIN CAPITAL LETTER E WITH OGONEK
+0xcb	U+00cb	# LATIN CAPITAL LETTER E WITH DIAERESIS
+0xcc	U+0116	# LATIN CAPITAL LETTER E WITH DOT ABOVE
+
+0xcd-0xd0	idem
+
+#0xcd	U+00cd	# LATIN CAPITAL LETTER I WITH ACUTE
+#0xce	U+00ce	# LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+#0xcf	U+00cf	# LATIN CAPITAL LETTER I WITH DIAERESIS
+#0xd0	U+00d0	# LATIN CAPITAL LETTER ETH
+
+0xd1	U+0145	# LATIN CAPITAL LETTER N WITH CEDILLA
+0xd2	U+014c	# LATIN CAPITAL LETTER O WITH MACRON
+
+0xd3-0xd6	idem
+
+#0xd3	U+00d3	# LATIN CAPITAL LETTER O WITH ACUTE
+#0xd4	U+00d4	# LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+#0xd5	U+00d5	# LATIN CAPITAL LETTER O WITH TILDE
+#0xd6	U+00d6	# LATIN CAPITAL LETTER O WITH DIAERESIS
+
+0xd7	U+0168	# LATIN CAPITAL LETTER U WITH TILDE
+0xd8	U+00d8	# LATIN CAPITAL LETTER O WITH STROKE
+0xd9	U+0172	# LATIN CAPITAL LETTER U WITH OGONEK
+
+0xda-0xde	idem
+
+#0xda	U+00da	# LATIN CAPITAL LETTER U WITH ACUTE
+#0xdb	U+00db	# LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+#0xdc	U+00dc	# LATIN CAPITAL LETTER U WITH DIAERESIS
+#0xdd	U+00dd	# LATIN CAPITAL LETTER Y WITH ACUTE
+#0xde	U+00de	# LATIN CAPITAL LETTER THORN
+
+0xdF	U+00df	# LATIN SMALL LETTER SHARP S
+0xe0	U+0101	# LATIN SMALL LETTER A WITH MACRON
+
+0xe1-0xe6	idem
+
+#0xe1	U+00e1	# LATIN SMALL LETTER A WITH ACUTE
+#0xe2	U+00e2	# LATIN SMALL LETTER A WITH CIRCUMFLEX
+#0xe3	U+00e3	# LATIN SMALL LETTER A WITH TILDE
+#0xe4	U+00e4	# LATIN SMALL LETTER A WITH DIAERESIS
+#0xe5	U+00e5	# LATIN SMALL LETTER A WITH RING ABOVE
+#0xe6	U+00e6	# LATIN SMALL LETTER AE
+
+0xe7	U+012f	# LATIN SMALL LETTER I WITH OGONEK
+0xe8	U+010d	# LATIN SMALL LETTER C WITH CARON
+0xe9	U+00e9	# LATIN SMALL LETTER E WITH ACUTE
+0xea	U+0119	# LATIN SMALL LETTER E WITH OGONEK
+0xeb	U+00eb	# LATIN SMALL LETTER E WITH DIAERESIS
+0xec	U+0117	# LATIN SMALL LETTER E WITH DOT ABOVE
+
+0xed-0xf0	idem
+
+#0xed	U+00ed	# LATIN SMALL LETTER I WITH ACUTE
+#0xee	U+00ee	# LATIN SMALL LETTER I WITH CIRCUMFLEX
+#0xef	U+00ef	# LATIN SMALL LETTER I WITH DIAERESIS
+#0xf0	U+00F0	# LATIN SMALL LETTER ETH
+
+0xf1	U+0146	# LATIN SMALL LETTER N WITH CEDILLA
+0xf2	U+014d	# LATIN SMALL LETTER O WITH MACRON
+
+0xf3-0xf6	idem
+
+#0xf3	U+00f3	# LATIN SMALL LETTER O WITH ACUTE
+#0xf4	U+00f4	# LATIN SMALL LETTER O WITH CIRCUMFLEX
+#0xf5	U+00f5	# LATIN SMALL LETTER O WITH TILDE
+#0xf6	U+00f6	# LATIN SMALL LETTER O WITH DIAERESIS
+
+0xf7	U+0169	# LATIN SMALL LETTER U WITH TILDE
+0xf8	U+00f8	# LATIN SMALL LETTER O WITH STROKE
+0xf9	U+0173	# LATIN SMALL LETTER U WITH OGONEK
+
+0xfa-0xfe	idem
+
+#0xfa	U+00fa	# LATIN SMALL LETTER U WITH ACUTE
+#0xfb	U+00fb	# LATIN SMALL LETTER U WITH CIRCUMFLEX
+#0xfc	U+00fc	# LATIN SMALL LETTER U WITH DIAERESIS
+#0xfd	U+00fd	# LATIN SMALL LETTER Y WITH ACUTE
+#0xfe	U+00fe	# LATIN SMALL LETTER THORN
+
+0xfF	U+0138	# LATIN SMALL LETTER KRA
+
+# TRADE MARK SIGN:
+U+2122:(TM)
diff --git a/src/chrtrans/iso13_uni.tbl b/src/chrtrans/iso13_uni.tbl
new file mode 100644
index 00000000..e51ea23e
--- /dev/null
+++ b/src/chrtrans/iso13_uni.tbl
@@ -0,0 +1,114 @@
+# The MIME name of this charset.
+Miso-8859-13
+
+# Name as a Display Charset (used on Options screen)
+OBaltic Rim (ISO-8859-13)
+
+# This is not the default font!
+D0
+
+#
+# Name:		ISO 8859-13 Latin 7 (1998) to Unicode
+# Date:		2005-12-15
+# Authors:	Thomas E Dickey from 
+#		http://czyborra.com/charsets/iso8859.html
+#		(ISO 8859 Alphabet Soup)
+
+0x20-0x7E idem	#	ASCII
+
+0xA0	U+00A0	# NO-BREAK SPACE
+0xA1	U+201D	# RIGHT DOUBLE QUOTATION MARK
+0xA2	U+00A2	# CENT SIGN
+0xA3	U+00A3	# POUND SIGN
+0xA4	U+00A4	# CURRENCY SIGN
+0xA5	U+201E	# DOUBLE LOW-9 QUOTATION MARK
+0xA6	U+00A6	# BROKEN BAR
+0xA7	U+00A7	# SECTION SIGN
+0xA8	U+00D8	# LATIN CAPITAL LETTER O WITH STROKE
+0xA9	U+00A9	# COPYRIGHT SIGN
+0xAA	U+0156	# LATIN CAPITAL LETTER R WITH CEDILLA
+0xAB	U+00AB	# LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC	U+00AC	# NOT SIGN
+0xAD	U+00AD	# SOFT HYPHEN
+0xAE	U+00AE	# REGISTERED SIGN
+0xAF	U+00C6	# LATIN CAPITAL LETTER AE
+0xB0	U+00B0	# DEGREE SIGN
+0xB1	U+00B1	# PLUS-MINUS SIGN
+0xB2	U+00B2	# SUPERSCRIPT TWO
+0xB3	U+00B3	# SUPERSCRIPT THREE
+0xB4	U+201C	# LEFT DOUBLE QUOTATION MARK
+0xB5	U+00B5	# MICRO SIGN
+0xB6	U+00B6	# PILCROW SIGN
+0xB7	U+00B7	# MIDDLE DOT
+0xB8	U+00F8	# LATIN SMALL LETTER O WITH STROKE
+0xB9	U+00B9	# SUPERSCRIPT ONE
+0xBA	U+0157	# LATIN SMALL LETTER R WITH CEDILLA
+0xBB	U+00BB	# RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC	U+00BC	# VULGAR FRACTION ONE QUARTER
+0xBD	U+00BD	# VULGAR FRACTION ONE HALF
+0xBE	U+00BE	# VULGAR FRACTION THREE QUARTERS
+0xBF	U+00E6	# LATIN SMALL LETTER AE
+0xC0	U+0104	# LATIN CAPITAL LETTER A WITH OGONEK
+0xC1	U+012E	# LATIN CAPITAL LETTER I WITH OGONEK
+0xC2	U+0100	# LATIN CAPITAL LETTER A WITH MACRON
+0xC3	U+0106	# LATIN CAPITAL LETTER C WITH ACUTE
+0xC4	U+00C4	# LATIN CAPITAL LETTER A WITH DIAERESIS
+0xC5	U+00C5	# LATIN CAPITAL LETTER A WITH RING ABOVE
+0xC6	U+0118	# LATIN CAPITAL LETTER E WITH OGONEK
+0xC7	U+0112	# LATIN CAPITAL LETTER E WITH MACRON
+0xC8	U+010C	# LATIN CAPITAL LETTER C WITH CARON
+0xC9	U+00C9	# LATIN CAPITAL LETTER E WITH ACUTE
+0xCA	U+0179	# LATIN CAPITAL LETTER Z WITH ACUTE
+0xCB	U+0116	# LATIN CAPITAL LETTER E WITH DOT ABOVE
+0xCC	U+0122	# LATIN CAPITAL LETTER G WITH CEDILLA
+0xCD	U+0136	# LATIN CAPITAL LETTER K WITH CEDILLA
+0xCE	U+012A	# LATIN CAPITAL LETTER I WITH MACRON
+0xCF	U+013B	# LATIN CAPITAL LETTER L WITH CEDILLA
+0xD0	U+0160	# LATIN CAPITAL LETTER S WITH CARON
+0xD1	U+0143	# LATIN CAPITAL LETTER N WITH ACUTE
+0xD2	U+0145	# LATIN CAPITAL LETTER N WITH CEDILLA
+0xD3	U+00D3	# LATIN CAPITAL LETTER O WITH ACUTE
+0xD4	U+014C	# LATIN CAPITAL LETTER O WITH MACRON
+0xD5	U+00D5	# LATIN CAPITAL LETTER O WITH TILDE
+0xD6	U+00D6	# LATIN CAPITAL LETTER O WITH DIAERESIS
+0xD7	U+00D7	# MULTIPLICATION SIGN
+0xD8	U+0172	# LATIN CAPITAL LETTER U WITH OGONEK
+0xD9	U+0141	# LATIN CAPITAL LETTER L WITH STROKE
+0xDA	U+015A	# LATIN CAPITAL LETTER S WITH ACUTE
+0xDB	U+016A	# LATIN CAPITAL LETTER U WITH MACRON
+0xDC	U+00DC	# LATIN CAPITAL LETTER U WITH DIAERESIS
+0xDD	U+017B	# LATIN CAPITAL LETTER Z WITH DOT ABOVE
+0xDE	U+017D	# LATIN CAPITAL LETTER Z WITH CARON
+0xDF	U+00DF	# LATIN SMALL LETTER SHARP S
+0xE0	U+0105	# LATIN SMALL LETTER A WITH OGONEK
+0xE1	U+012F	# LATIN SMALL LETTER I WITH OGONEK
+0xE2	U+0101	# LATIN SMALL LETTER A WITH MACRON
+0xE3	U+0107	# LATIN SMALL LETTER C WITH ACUTE
+0xE4	U+00E4	# LATIN SMALL LETTER A WITH DIAERESIS
+0xE5	U+00E5	# LATIN SMALL LETTER A WITH RING ABOVE
+0xE6	U+0119	# LATIN SMALL LETTER E WITH OGONEK
+0xE7	U+0113	# LATIN SMALL LETTER E WITH MACRON
+0xE8	U+010D	# LATIN SMALL LETTER C WITH CARON
+0xE9	U+00E9	# LATIN SMALL LETTER E WITH ACUTE
+0xEA	U+017A	# LATIN SMALL LETTER Z WITH ACUTE
+0xEB	U+0117	# LATIN SMALL LETTER E WITH DOT ABOVE
+0xEC	U+0123	# LATIN SMALL LETTER G WITH CEDILLA
+0xED	U+0137	# LATIN SMALL LETTER K WITH CEDILLA
+0xEE	U+012B	# LATIN SMALL LETTER I WITH MACRON
+0xEF	U+013C	# LATIN SMALL LETTER L WITH CEDILLA
+0xF0	U+0161	# LATIN SMALL LETTER S WITH CARON
+0xF1	U+0144	# LATIN SMALL LETTER N WITH ACUTE
+0xF2	U+0146	# LATIN SMALL LETTER N WITH CEDILLA
+0xF3	U+00F3	# LATIN SMALL LETTER O WITH ACUTE
+0xF4	U+014D	# LATIN SMALL LETTER O WITH MACRON
+0xF5	U+00F5	# LATIN SMALL LETTER O WITH TILDE
+0xF6	U+00F6	# LATIN SMALL LETTER O WITH DIAERESIS
+0xF7	U+00F7	# DIVISION SIGN
+0xF8	U+0173	# LATIN SMALL LETTER U WITH OGONEK
+0xF9	U+0142	# LATIN SMALL LETTER L WITH STROKE
+0xFA	U+015B	# LATIN SMALL LETTER S WITH ACUTE
+0xFB	U+016B	# LATIN SMALL LETTER U WITH MACRON
+0xFC	U+00FC	# LATIN SMALL LETTER U WITH DIAERESIS
+0xFD	U+017C	# LATIN SMALL LETTER Z WITH DOT ABOVE
+0xFE	U+017E	# LATIN SMALL LETTER Z WITH CARON
+0xFF	U+2019	# RIGHT SINGLE QUOTATION MARK
diff --git a/src/chrtrans/iso14_uni.tbl b/src/chrtrans/iso14_uni.tbl
new file mode 100644
index 00000000..630a9466
--- /dev/null
+++ b/src/chrtrans/iso14_uni.tbl
@@ -0,0 +1,114 @@
+# The MIME name of this charset.
+Miso-8859-14
+
+# Name as a Display Charset (used on Options screen)
+OCeltic (ISO-8859-14)
+
+# This is not the default font!
+D0
+
+#
+# Name:		ISO 8859-13 Latin 8 (1998) to Unicode
+# Date:		2005-12-15
+# Authors:	Thomas E Dickey from 
+#		http://czyborra.com/charsets/iso8859.html
+#		(ISO 8859 Alphabet Soup)
+
+0x20-0x7E idem	#	ASCII
+
+0xA0	U+00A0	# NO-BREAK SPACE
+0xA1	U+1E02	# LATIN CAPITAL LETTER B WITH DOT ABOVE
+0xA2	U+1E03	# LATIN SMALL LETTER B WITH DOT ABOVE
+0xA3	U+00A3	# POUND SIGN
+0xA4	U+010A	# LATIN CAPITAL LETTER C WITH DOT ABOVE
+0xA5	U+010B	# LATIN SMALL LETTER C WITH DOT ABOVE
+0xA6	U+1E0A	# LATIN CAPITAL LETTER D WITH DOT ABOVE
+0xA7	U+00A7	# SECTION SIGN
+0xA8	U+1E80	# LATIN CAPITAL LETTER W WITH GRAVE
+0xA9	U+00A9	# COPYRIGHT SIGN
+0xAA	U+1E82	# LATIN CAPITAL LETTER W WITH ACUTE
+0xAB	U+1E0B	# LATIN SMALL LETTER D WITH DOT ABOVE
+0xAC	U+1EF2	# LATIN CAPITAL LETTER Y WITH GRAVE
+0xAD	U+00AD	# SOFT HYPHEN
+0xAE	U+00AE	# REGISTERED SIGN
+0xAF	U+0178	# LATIN CAPITAL LETTER Y WITH DIAERESIS
+0xB0	U+1E1E	# LATIN CAPITAL LETTER F WITH DOT ABOVE
+0xB1	U+1E1F	# LATIN SMALL LETTER F WITH DOT ABOVE
+0xB2	U+0120	# LATIN CAPITAL LETTER G WITH DOT ABOVE
+0xB3	U+0121	# LATIN SMALL LETTER G WITH DOT ABOVE
+0xB4	U+1E40	# LATIN CAPITAL LETTER M WITH DOT ABOVE
+0xB5	U+1E41	# LATIN SMALL LETTER M WITH DOT ABOVE
+0xB6	U+00B6	# PILCROW SIGN
+0xB7	U+1E56	# LATIN CAPITAL LETTER P WITH DOT ABOVE
+0xB8	U+1E81	# LATIN SMALL LETTER W WITH GRAVE
+0xB9	U+1E57	# LATIN SMALL LETTER P WITH DOT ABOVE
+0xBA	U+1E83	# LATIN SMALL LETTER W WITH ACUTE
+0xBB	U+1E60	# LATIN CAPITAL LETTER S WITH DOT ABOVE
+0xBC	U+1EF3	# LATIN SMALL LETTER Y WITH GRAVE
+0xBD	U+1E84	# LATIN CAPITAL LETTER W WITH DIAERESIS
+0xBE	U+1E85	# LATIN SMALL LETTER W WITH DIAERESIS
+0xBF	U+1E61	# LATIN SMALL LETTER S WITH DOT ABOVE
+0xC0	U+00C0	# LATIN CAPITAL LETTER A WITH GRAVE
+0xC1	U+00C1	# LATIN CAPITAL LETTER A WITH ACUTE
+0xC2	U+00C2	# LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xC3	U+00C3	# LATIN CAPITAL LETTER A WITH TILDE
+0xC4	U+00C4	# LATIN CAPITAL LETTER A WITH DIAERESIS
+0xC5	U+00C5	# LATIN CAPITAL LETTER A WITH RING ABOVE
+0xC6	U+00C6	# LATIN CAPITAL LETTER AE
+0xC7	U+00C7	# LATIN CAPITAL LETTER C WITH CEDILLA
+0xC8	U+00C8	# LATIN CAPITAL LETTER E WITH GRAVE
+0xC9	U+00C9	# LATIN CAPITAL LETTER E WITH ACUTE
+0xCA	U+00CA	# LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+0xCB	U+00CB	# LATIN CAPITAL LETTER E WITH DIAERESIS
+0xCC	U+00CC	# LATIN CAPITAL LETTER I WITH GRAVE
+0xCD	U+00CD	# LATIN CAPITAL LETTER I WITH ACUTE
+0xCE	U+00CE	# LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xCF	U+00CF	# LATIN CAPITAL LETTER I WITH DIAERESIS
+0xD0	U+0174	# LATIN CAPITAL LETTER W WITH CIRCUMFLEX
+0xD1	U+00D1	# LATIN CAPITAL LETTER N WITH TILDE
+0xD2	U+00D2	# LATIN CAPITAL LETTER O WITH GRAVE
+0xD3	U+00D3	# LATIN CAPITAL LETTER O WITH ACUTE
+0xD4	U+00D4	# LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xD5	U+00D5	# LATIN CAPITAL LETTER O WITH TILDE
+0xD6	U+00D6	# LATIN CAPITAL LETTER O WITH DIAERESIS
+0xD7	U+1E6A	# LATIN CAPITAL LETTER T WITH DOT ABOVE
+0xD8	U+00D8	# LATIN CAPITAL LETTER O WITH STROKE
+0xD9	U+00D9	# LATIN CAPITAL LETTER U WITH GRAVE
+0xDA	U+00DA	# LATIN CAPITAL LETTER U WITH ACUTE
+0xDB	U+00DB	# LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+0xDC	U+00DC	# LATIN CAPITAL LETTER U WITH DIAERESIS
+0xDD	U+00DD	# LATIN CAPITAL LETTER Y WITH ACUTE
+0xDE	U+0176	# LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
+0xDF	U+00DF	# LATIN SMALL LETTER SHARP S
+0xE0	U+00E0	# LATIN SMALL LETTER A WITH GRAVE
+0xE1	U+00E1	# LATIN SMALL LETTER A WITH ACUTE
+0xE2	U+00E2	# LATIN SMALL LETTER A WITH CIRCUMFLEX
+0xE3	U+00E3	# LATIN SMALL LETTER A WITH TILDE
+0xE4	U+00E4	# LATIN SMALL LETTER A WITH DIAERESIS
+0xE5	U+00E5	# LATIN SMALL LETTER A WITH RING ABOVE
+0xE6	U+00E6	# LATIN SMALL LETTER AE
+0xE7	U+00E7	# LATIN SMALL LETTER C WITH CEDILLA
+0xE8	U+00E8	# LATIN SMALL LETTER E WITH GRAVE
+0xE9	U+00E9	# LATIN SMALL LETTER E WITH ACUTE
+0xEA	U+00EA	# LATIN SMALL LETTER E WITH CIRCUMFLEX
+0xEB	U+00EB	# LATIN SMALL LETTER E WITH DIAERESIS
+0xEC	U+00EC	# LATIN SMALL LETTER I WITH GRAVE
+0xED	U+00ED	# LATIN SMALL LETTER I WITH ACUTE
+0xEE	U+00EE	# LATIN SMALL LETTER I WITH CIRCUMFLEX
+0xEF	U+00EF	# LATIN SMALL LETTER I WITH DIAERESIS
+0xF0	U+0175	# LATIN SMALL LETTER W WITH CIRCUMFLEX
+0xF1	U+00F1	# LATIN SMALL LETTER N WITH TILDE
+0xF2	U+00F2	# LATIN SMALL LETTER O WITH GRAVE
+0xF3	U+00F3	# LATIN SMALL LETTER O WITH ACUTE
+0xF4	U+00F4	# LATIN SMALL LETTER O WITH CIRCUMFLEX
+0xF5	U+00F5	# LATIN SMALL LETTER O WITH TILDE
+0xF6	U+00F6	# LATIN SMALL LETTER O WITH DIAERESIS
+0xF7	U+1E6B	# LATIN SMALL LETTER T WITH DOT ABOVE
+0xF8	U+00F8	# LATIN SMALL LETTER O WITH STROKE
+0xF9	U+00F9	# LATIN SMALL LETTER U WITH GRAVE
+0xFA	U+00FA	# LATIN SMALL LETTER U WITH ACUTE
+0xFB	U+00FB	# LATIN SMALL LETTER U WITH CIRCUMFLEX
+0xFC	U+00FC	# LATIN SMALL LETTER U WITH DIAERESIS
+0xFD	U+00FD	# LATIN SMALL LETTER Y WITH ACUTE
+0xFE	U+0177	# LATIN SMALL LETTER Y WITH CIRCUMFLEX
+0xFF	U+00FF	# LATIN SMALL LETTER Y WITH DIAERESIS
diff --git a/src/chrtrans/iso15_uni.tbl b/src/chrtrans/iso15_uni.tbl
new file mode 100644
index 00000000..efb5bf23
--- /dev/null
+++ b/src/chrtrans/iso15_uni.tbl
@@ -0,0 +1,216 @@
+# The MIME name of this charset.
+Miso-8859-15
+
+# Name as a Display Charset (used on Options screen)
+OWestern (ISO-8859-15)
+
+# This is not the default font!
+D0
+
+#Codepage number
+#?
+
+#
+# Name:		ISO 8859-15 Latin 9 (1998) to Unicode
+# Date:		1999-01-01
+# Authors:	Christian "naddy" Weisgerber <naddy@mips.rhein-neckar.de>
+#
+# Remarks:	Latin 9 is identical to Latin 1	except for
+#		code positions A4, A6, A8, B4, B8, BC, BD, BE
+
+0x20-0x7E idem	#	ASCII
+
+#0x20	U+0020	#	SPACE
+#0x21	U+0021	#	EXCLAMATION MARK
+#0x22	U+0022	#	QUOTATION MARK
+#0x23	U+0023	#	NUMBER SIGN
+#0x24	U+0024	#	DOLLAR SIGN
+#0x25	U+0025	#	PERCENT SIGN
+#0x26	U+0026	#	AMPERSAND
+#0x27	U+0027	#	APOSTROPHE
+#0x28	U+0028	#	LEFT PARENTHESIS
+#0x29	U+0029	#	RIGHT PARENTHESIS
+#0x2A	U+002A	#	ASTERISK
+#0x2B	U+002B	#	PLUS SIGN
+#0x2C	U+002C	#	COMMA
+#0x2D	U+002D	#	HYPHEN-MINUS
+#0x2E	U+002E	#	FULL STOP
+#0x2F	U+002F	#	SOLIDUS
+#0x30	U+0030	#	DIGIT ZERO
+#0x31	U+0031	#	DIGIT ONE
+#0x32	U+0032	#	DIGIT TWO
+#0x33	U+0033	#	DIGIT THREE
+#0x34	U+0034	#	DIGIT FOUR
+#0x35	U+0035	#	DIGIT FIVE
+#0x36	U+0036	#	DIGIT SIX
+#0x37	U+0037	#	DIGIT SEVEN
+#0x38	U+0038	#	DIGIT EIGHT
+#0x39	U+0039	#	DIGIT NINE
+#0x3A	U+003A	#	COLON
+#0x3B	U+003B	#	SEMICOLON
+#0x3C	U+003C	#	LESS-THAN SIGN
+#0x3D	U+003D	#	EQUALS SIGN
+#0x3E	U+003E	#	GREATER-THAN SIGN
+#0x3F	U+003F	#	QUESTION MARK
+#0x40	U+0040	#	COMMERCIAL AT
+#0x41	U+0041	#	LATIN CAPITAL LETTER A
+#0x42	U+0042	#	LATIN CAPITAL LETTER B
+#0x43	U+0043	#	LATIN CAPITAL LETTER C
+#0x44	U+0044	#	LATIN CAPITAL LETTER D
+#0x45	U+0045	#	LATIN CAPITAL LETTER E
+#0x46	U+0046	#	LATIN CAPITAL LETTER F
+#0x47	U+0047	#	LATIN CAPITAL LETTER G
+#0x48	U+0048	#	LATIN CAPITAL LETTER H
+#0x49	U+0049	#	LATIN CAPITAL LETTER I
+#0x4A	U+004A	#	LATIN CAPITAL LETTER J
+#0x4B	U+004B	#	LATIN CAPITAL LETTER K
+#0x4C	U+004C	#	LATIN CAPITAL LETTER L
+#0x4D	U+004D	#	LATIN CAPITAL LETTER M
+#0x4E	U+004E	#	LATIN CAPITAL LETTER N
+#0x4F	U+004F	#	LATIN CAPITAL LETTER O
+#0x50	U+0050	#	LATIN CAPITAL LETTER P
+#0x51	U+0051	#	LATIN CAPITAL LETTER Q
+#0x52	U+0052	#	LATIN CAPITAL LETTER R
+#0x53	U+0053	#	LATIN CAPITAL LETTER S
+#0x54	U+0054	#	LATIN CAPITAL LETTER T
+#0x55	U+0055	#	LATIN CAPITAL LETTER U
+#0x56	U+0056	#	LATIN CAPITAL LETTER V
+#0x57	U+0057	#	LATIN CAPITAL LETTER W
+#0x58	U+0058	#	LATIN CAPITAL LETTER X
+#0x59	U+0059	#	LATIN CAPITAL LETTER Y
+#0x5A	U+005A	#	LATIN CAPITAL LETTER Z
+#0x5B	U+005B	#	LEFT SQUARE BRACKET
+#0x5C	U+005C	#	REVERSE SOLIDUS
+#0x5D	U+005D	#	RIGHT SQUARE BRACKET
+#0x5E	U+005E	#	CIRCUMFLEX ACCENT
+#0x5F	U+005F	#	LOW LINE
+#0x60	U+0060	#	GRAVE ACCENT
+#0x61	U+0061	#	LATIN SMALL LETTER A
+#0x62	U+0062	#	LATIN SMALL LETTER B
+#0x63	U+0063	#	LATIN SMALL LETTER C
+#0x64	U+0064	#	LATIN SMALL LETTER D
+#0x65	U+0065	#	LATIN SMALL LETTER E
+#0x66	U+0066	#	LATIN SMALL LETTER F
+#0x67	U+0067	#	LATIN SMALL LETTER G
+#0x68	U+0068	#	LATIN SMALL LETTER H
+#0x69	U+0069	#	LATIN SMALL LETTER I
+#0x6A	U+006A	#	LATIN SMALL LETTER J
+#0x6B	U+006B	#	LATIN SMALL LETTER K
+#0x6C	U+006C	#	LATIN SMALL LETTER L
+#0x6D	U+006D	#	LATIN SMALL LETTER M
+#0x6E	U+006E	#	LATIN SMALL LETTER N
+#0x6F	U+006F	#	LATIN SMALL LETTER O
+#0x70	U+0070	#	LATIN SMALL LETTER P
+#0x71	U+0071	#	LATIN SMALL LETTER Q
+#0x72	U+0072	#	LATIN SMALL LETTER R
+#0x73	U+0073	#	LATIN SMALL LETTER S
+#0x74	U+0074	#	LATIN SMALL LETTER T
+#0x75	U+0075	#	LATIN SMALL LETTER U
+#0x76	U+0076	#	LATIN SMALL LETTER V
+#0x77	U+0077	#	LATIN SMALL LETTER W
+#0x78	U+0078	#	LATIN SMALL LETTER X
+#0x79	U+0079	#	LATIN SMALL LETTER Y
+#0x7A	U+007A	#	LATIN SMALL LETTER Z
+#0x7B	U+007B	#	LEFT CURLY BRACKET
+#0x7C	U+007C	#	VERTICAL LINE
+#0x7D	U+007D	#	RIGHT CURLY BRACKET
+#0x7E	U+007E	#	TILDE
+
+0xA0	U+00A0	#	NO-BREAK SPACE
+0xA1	U+00A1	#	INVERTED EXCLAMATION MARK
+0xA2	U+00A2	#	CENT SIGN
+0xA3	U+00A3	#	POUND SIGN
+0xA4	U+20AC	#	EURO SIGN
+0xA5	U+00A5	#	YEN SIGN
+0xA6	U+0160	#	LATIN CAPITAL LETTER S WITH CARON
+0xA7	U+00A7	#	SECTION SIGN
+0xA8	U+0161	#	LATIN SMALL LETTER S WITH CARON
+0xA9	U+00A9	#	COPYRIGHT SIGN
+0xAA	U+00AA	#	FEMININE ORDINAL INDICATOR
+0xAB	U+00AB	#	LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xAC	U+00AC	#	NOT SIGN
+0xAD	U+00AD	#	SOFT HYPHEN
+0xAE	U+00AE	#	REGISTERED SIGN
+0xAF	U+00AF	#	MACRON
+0xB0	U+00B0	#	DEGREE SIGN
+0xB1	U+00B1	#	PLUS-MINUS SIGN
+0xB2	U+00B2	#	SUPERSCRIPT TWO
+0xB3	U+00B3	#	SUPERSCRIPT THREE
+0xB4	U+017D	#	LATIN CAPITAL LETTER Z WITH CARON
+0xB5	U+00B5	#	MICRO SIGN
+0xB6	U+00B6	#	PILCROW SIGN
+0xB7	U+00B7	#	MIDDLE DOT
+0xB8	U+017D	#	LATIN SMALL LETTER Z WITH CARON
+0xB9	U+00B9	#	SUPERSCRIPT ONE
+0xBA	U+00BA	#	MASCULINE ORDINAL INDICATOR
+0xBB	U+00BB	#	RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xBC	U+0152	#	LATIN CAPITAL LIGATURE OE
+0xBD	U+0153	#	LATIN SMALL LIGATURE OE
+0xBE	U+0178	#	LATIN CAPITAL LETTER Y WITH DIAERESIS
+0xBF	U+00BF	#	INVERTED QUESTION MARK
+0xC0	U+00C0	#	LATIN CAPITAL LETTER A WITH GRAVE
+0xC1	U+00C1	#	LATIN CAPITAL LETTER A WITH ACUTE
+0xC2	U+00C2	#	LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xC3	U+00C3	#	LATIN CAPITAL LETTER A WITH TILDE
+0xC4	U+00C4	#	LATIN CAPITAL LETTER A WITH DIAERESIS
+0xC5	U+00C5	#	LATIN CAPITAL LETTER A WITH RING ABOVE
+0xC6	U+00C6	#	LATIN CAPITAL LETTER AE
+0xC7	U+00C7	#	LATIN CAPITAL LETTER C WITH CEDILLA
+0xC8	U+00C8	#	LATIN CAPITAL LETTER E WITH GRAVE
+0xC9	U+00C9	#	LATIN CAPITAL LETTER E WITH ACUTE
+0xCA	U+00CA	#	LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+0xCB	U+00CB	#	LATIN CAPITAL LETTER E WITH DIAERESIS
+0xCC	U+00CC	#	LATIN CAPITAL LETTER I WITH GRAVE
+0xCD	U+00CD	#	LATIN CAPITAL LETTER I WITH ACUTE
+0xCE	U+00CE	#	LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xCF	U+00CF	#	LATIN CAPITAL LETTER I WITH DIAERESIS
+0xD0	U+00D0	#	LATIN CAPITAL LETTER ETH
+0xD1	U+00D1	#	LATIN CAPITAL LETTER N WITH TILDE
+0xD2	U+00D2	#	LATIN CAPITAL LETTER O WITH GRAVE
+0xD3	U+00D3	#	LATIN CAPITAL LETTER O WITH ACUTE
+0xD4	U+00D4	#	LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xD5	U+00D5	#	LATIN CAPITAL LETTER O WITH TILDE
+0xD6	U+00D6	#	LATIN CAPITAL LETTER O WITH DIAERESIS
+0xD7	U+00D7	#	MULTIPLICATION SIGN
+0xD8	U+00D8	#	LATIN CAPITAL LETTER O WITH STROKE
+0xD9	U+00D9	#	LATIN CAPITAL LETTER U WITH GRAVE
+0xDA	U+00DA	#	LATIN CAPITAL LETTER U WITH ACUTE
+0xDB	U+00DB	#	LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+0xDC	U+00DC	#	LATIN CAPITAL LETTER U WITH DIAERESIS
+0xDD	U+00DD	#	LATIN CAPITAL LETTER Y WITH ACUTE
+0xDE	U+00DE	#	LATIN CAPITAL LETTER THORN
+0xDF	U+00DF	#	LATIN SMALL LETTER SHARP S
+0xE0	U+00E0	#	LATIN SMALL LETTER A WITH GRAVE
+0xE1	U+00E1	#	LATIN SMALL LETTER A WITH ACUTE
+0xE2	U+00E2	#	LATIN SMALL LETTER A WITH CIRCUMFLEX
+0xE3	U+00E3	#	LATIN SMALL LETTER A WITH TILDE
+0xE4	U+00E4	#	LATIN SMALL LETTER A WITH DIAERESIS
+0xE5	U+00E5	#	LATIN SMALL LETTER A WITH RING ABOVE
+0xE6	U+00E6	#	LATIN SMALL LETTER AE
+0xE7	U+00E7	#	LATIN SMALL LETTER C WITH CEDILLA
+0xE8	U+00E8	#	LATIN SMALL LETTER E WITH GRAVE
+0xE9	U+00E9	#	LATIN SMALL LETTER E WITH ACUTE
+0xEA	U+00EA	#	LATIN SMALL LETTER E WITH CIRCUMFLEX
+0xEB	U+00EB	#	LATIN SMALL LETTER E WITH DIAERESIS
+0xEC	U+00EC	#	LATIN SMALL LETTER I WITH GRAVE
+0xED	U+00ED	#	LATIN SMALL LETTER I WITH ACUTE
+0xEE	U+00EE	#	LATIN SMALL LETTER I WITH CIRCUMFLEX
+0xEF	U+00EF	#	LATIN SMALL LETTER I WITH DIAERESIS
+0xF0	U+00F0	#	LATIN SMALL LETTER ETH
+0xF1	U+00F1	#	LATIN SMALL LETTER N WITH TILDE
+0xF2	U+00F2	#	LATIN SMALL LETTER O WITH GRAVE
+0xF3	U+00F3	#	LATIN SMALL LETTER O WITH ACUTE
+0xF4	U+00F4	#	LATIN SMALL LETTER O WITH CIRCUMFLEX
+0xF5	U+00F5	#	LATIN SMALL LETTER O WITH TILDE
+0xF6	U+00F6	#	LATIN SMALL LETTER O WITH DIAERESIS
+0xF7	U+00F7	#	DIVISION SIGN
+0xF8	U+00F8	#	LATIN SMALL LETTER O WITH STROKE
+0xF9	U+00F9	#	LATIN SMALL LETTER U WITH GRAVE
+0xFA	U+00FA	#	LATIN SMALL LETTER U WITH ACUTE
+0xFB	U+00FB	#	LATIN SMALL LETTER U WITH CIRCUMFLEX
+0xFC	U+00FC	#	LATIN SMALL LETTER U WITH DIAERESIS
+0xFD	U+00FD	#	LATIN SMALL LETTER Y WITH ACUTE
+0xFE	U+00FE	#	LATIN SMALL LETTER THORN
+0xFF	U+00FF	#	LATIN SMALL LETTER Y WITH DIAERESIS
+
+## EOF ##
diff --git a/src/chrtrans/jcuken_kb.h b/src/chrtrans/jcuken_kb.h
new file mode 100644
index 00000000..5f42d26d
--- /dev/null
+++ b/src/chrtrans/jcuken_kb.h
@@ -0,0 +1,22 @@
+static LYKbLayout_t kb_layout_jcuken[128] =
+{
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,	/* 00..07 */
+    0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,	/* 08..0F */
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,	/* 10..17 */
+    0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,	/* 18..1F */
+
+    0x0020, 0x0021, 0x042D, 0x002F, 0x0024, 0x003A, 0x002E, 0x044D,	/* 20..27 */
+    0x003F, 0x0025, 0x003B, 0x002B, 0x0431, 0x002D, 0x044E, 0x0451,	/* 28..2F */
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,	/* 30..37 */
+    0x0038, 0x0039, 0x0416, 0x0436, 0x0411, 0x003D, 0x042E, 0x0401,	/* 38..3F */
+
+    0x0022, 0x0424, 0x0418, 0x0421, 0x0412, 0x0423, 0x0410, 0x041F,	/* 40..47 */
+    0x0420, 0x0428, 0x041E, 0x041B, 0x0414, 0x042C, 0x0422, 0x0429,	/* 48..4F */
+    0x0417, 0x0419, 0x041A, 0x042B, 0x0415, 0x0413, 0x041C, 0x0426,	/* 50..57 */
+    0x0427, 0x041D, 0x042F, 0x0445, 0x005C, 0x044A, 0x002C, 0x005F,	/* 58..5F */
+
+    0x0029, 0x0444, 0x0438, 0x0441, 0x0432, 0x0443, 0x0430, 0x043F,	/* 60..67 */
+    0x0440, 0x0448, 0x043E, 0x043B, 0x0434, 0x044C, 0x0442, 0x0449,	/* 68..6F */
+    0x0437, 0x0439, 0x043A, 0x044B, 0x0435, 0x0433, 0x043C, 0x0446,	/* 70..77 */
+    0x0447, 0x043D, 0x044F, 0x0425, 0x007C, 0x042A, 0x0028, 0x007F	/* 78..7F */
+};
diff --git a/src/chrtrans/koi8r_uni.tbl b/src/chrtrans/koi8r_uni.tbl
new file mode 100644
index 00000000..8bf4001a
--- /dev/null
+++ b/src/chrtrans/koi8r_uni.tbl
@@ -0,0 +1,147 @@
+# Options screen name for this character set
+OCyrillic (KOI8-R)
+
+# MIME name for this charset
+Mkoi8-r
+
+#Codepage number
+C878
+
+0x20-0x7f       idem
+# Based on a table received from "Glenn E. Thobe" <thobe@lafn.org>
+# (verified against RFC1489).
+#
+#   Lines with more than one Unicode (U+XXXX) value contain additional
+#   replacement mappings added for lynx. - kw
+#
+#hex unicode # description
+#--- U+---- # ---------------
+0x80 U+2500 # FORMS LIGHT HORIZONTAL
+0x81 U+2502 # FORMS LIGHT VERTICAL
+0x82 U+250C # FORMS LIGHT DOWN AND RIGHT
+0x83 U+2510 # FORMS LIGHT DOWN AND LEFT
+0x84 U+2514 # FORMS LIGHT UP AND RIGHT
+0x85 U+2518 # FORMS LIGHT UP AND LEFT
+0x86 U+251C # FORMS LIGHT VERTICAL AND RIGHT
+0x87 U+2524 # FORMS LIGHT VERTICAL AND LEFT
+0x88 U+252C # FORMS LIGHT DOWN AND HORIZONTAL
+0x89 U+2534 # FORMS LIGHT UP AND HORIZONTAL
+0x8A U+253C # FORMS LIGHT VERTICAL AND HORIZONTAL
+0x8B U+2580 # UPPER HALF BLOCK
+0x8C U+2584 # LOWER HALF BLOCK
+0x8D U+2588 # FULL BLOCK
+0x8E U+258C # LEFT HALF BLOCK
+0x8F U+2590 # RIGHT HALF BLOCK
+0x90 U+2591 # LIGHT SHADE
+0x91 U+2592 # MEDIUM SHADE
+0x92 U+2593 # DARK SHADE
+0x93 U+2320 # TOP HALF INTEGRAL
+0x94 U+25A0 # BLACK SMALL SQUARE
+0x95 U+2219 # BULLET OPERATOR
+0x96 U+221A # SQUARE ROOT
+0x97 U+2248 # ALMOST EQUAL TO
+0x98 U+2264 # LESS THAN OR EQUAL TO
+0x99 U+2265 # GREATER THAN OR EQUAL TO
+0x9A U+00A0 # NON-BREAKING SPACE
+0x9B U+2321 # BOTTOM HALF INTEGRAL
+0x9C U+00B0 # DEGREE SIGN
+0x9D U+00B2 # SUPERSCRIPT DIGIT TWO
+0x9E U+00B7 U+2027	# MIDDLE DOT
+0x9F U+00F7 # DIVISION SIGN
+0xA0 U+2550 # FORMS DOUBLE HORIZONTAL
+0xA1 U+2551 # FORMS DOUBLE VERTICAL
+0xA2 U+2552 # FORMS DOWN SINGLE AND RIGHT DOUBLE
+0xA3 U+0451 # SMA IO
+0xA4 U+2553 # FORMS DOWN DOUBLE AND RIGHT SINGLE
+0xA5 U+2554 # FORMS DOUBLE DOWN AND RIGHT
+0xA6 U+2555 # FORMS DOWN SINGLE AND LEFT DOUBLE
+0xA7 U+2556 # FORMS DOWN DOUBLE AND LEFT SINGLE
+0xA8 U+2557 # FORMS DOUBLE DOWN AND LEFT
+0xA9 U+2558 # FORMS UP SINGLE AND RIGHT DOUBLE
+0xAA U+2559 # FORMS UP DOUBLE AND RIGHT SINGLE
+0xAB U+255A # FORMS DOUBLE UP AND RIGHT
+0xAC U+255B # FORMS UP SINGLE AND LEFT DOUBLE
+0xAD U+255C # FORMS UP DOUBLE AND LEFT SINGLE
+0xAE U+255D # FORMS DOUBLE UP AND LEFT
+0xAF U+255E # FORMS VERTICAL SINGLE AND RIGHT DOUBLE
+0xB0 U+255F # FORMS VERTICAL DOUBLE AND RIGHT SINGLE
+0xB1 U+2560 # FORMS DOUBLE VERTICAL AND RIGHT
+0xB2 U+2561 # FORMS VERTICAL SINGLE AND LEFT DOUBLE
+0xB3 U+0401 # CAP IO
+0xB4 U+2562 # FORMS VERTICAL DOUBLE AND LEFT SINGLE
+0xB5 U+2563 # FORMS DOUBLE VERTICAL AND LEFT
+0xB6 U+2564 # FORMS DOWN SINGLE AND HORIZONTAL DOUBLE
+0xB7 U+2565 # FORMS DOWN DOUBLE AND HORIZONTAL SINGLE
+0xB8 U+2566 # FORMS DOUBLE DOWN AND HORIZONTAL
+0xB9 U+2567 # FORMS UP SINGLE AND HORIZONTAL DOUBLE
+0xBA U+2568 # FORMS UP DOUBLE AND HORIZONTAL SINGLE
+0xBB U+2569 # FORMS DOUBLE UP AND HORIZONTAL
+0xBC U+256A # FORMS VERTICAL SINGLE AND HORIZONTAL DOUBLE
+0xBD U+256B # FORMS VERTICAL DOUBLE AND HORIZONTAL SINGLE
+0xBE U+256C # FORMS DOUBLE VERTICAL AND HORIZONTAL
+0xBF U+00A9 # COPYRIGHT SIGN
+0xC0 U+044E # SMA IU
+0xC1 U+0430 # SMA A
+0xC2 U+0431 # SMA BE
+0xC3 U+0446 # SMA TSE
+0xC4 U+0434 # SMA DE
+0xC5 U+0435 # SMA IE
+0xC6 U+0444 U+03c6	# SMA EF
+0xC7 U+0433 # SMA GE
+0xC8 U+0445 # SMA KHA
+0xC9 U+0438 # SMA II
+0xCA U+0439 # SMA SHORT II
+0xCB U+043A # SMA KA
+0xCC U+043B U+03bb	# SMA EL
+0xCD U+043C # SMA EM
+0xCE U+043D # SMA EN
+0xCF U+043E # SMA O
+0xD0 U+043F U+03c0	# SMA PE
+0xD1 U+044F # SMA IA
+0xD2 U+0440 # SMA ER
+0xD3 U+0441 # SMA ES
+0xD4 U+0442 # SMA TE
+0xD5 U+0443 # SMA U
+0xD6 U+0436 U+017e	# SMA ZHE
+0xD7 U+0432 # SMA VE
+0xD8 U+044C # SMA SOFT SIGN
+0xD9 U+044B U+0131	# SMA YERI
+0xDA U+0437 # SMA ZE
+0xDB U+0448 U+0161	# SMA SHA
+0xDC U+044D # SMA REVERSED E
+0xDD U+0449 # SMA SHCHA
+0xDE U+0447 U+010d	# SMA CHE
+0xDF U+044A # SMA HARD SIGN
+0xE0 U+042E # CAP IU
+0xE1 U+0410 # CAP A
+0xE2 U+0411 # CAP BE
+0xE3 U+0426 # CAP TSE
+0xE4 U+0414 # CAP DE
+0xE5 U+0415 # CAP IE
+0xE6 U+0424 U+03a6	# CAP EF
+0xE7 U+0413 U+0393	# CAP GE
+0xE8 U+0425 # CAP KHA
+0xE9 U+0418 # CAP II
+0xEA U+0419 # CAP SHORT II
+0xEB U+041A # CAP KA
+0xEC U+041B U+039b	# CAP EL
+0xED U+041C # CAP EM
+0xEE U+041D # CAP EN
+0xEF U+041E # CAP O
+0xF0 U+041F U+03a0	# CAP PE
+0xF1 U+042F # CAP IA
+0xF2 U+0420 # CAP ER
+0xF3 U+0421 # CAP ES
+0xF4 U+0422 # CAP TE
+0xF5 U+0423 # CAP U
+0xF6 U+0416 U+017d	# CAP ZHE
+0xF7 U+0412 # CAP VE
+0xF8 U+042C # CAP SOFT SIGN
+0xF9 U+042B # CAP YERI
+0xFA U+0417 # CAP ZE
+0xFB U+0428 U+0160	# CAP SHA
+0xFC U+042D # CAP REVERSED E
+0xFD U+0429 # CAP SHCHA
+0xFE U+0427 U+010c	# CAP CHE
+0xFF U+042A # CAP HARD SIGN
+
diff --git a/src/chrtrans/koi8u_uni.tbl b/src/chrtrans/koi8u_uni.tbl
new file mode 100644
index 00000000..2c13845b
--- /dev/null
+++ b/src/chrtrans/koi8u_uni.tbl
@@ -0,0 +1,154 @@
+# Options screen name for this character set
+OUkrainian Cyrillic (KOI8-U)
+
+# MIME name for this charset
+Mkoi8-u
+
+#Codepage number
+#?
+
+0x20-0x7f       idem
+# Based on a table received from "Denis V. Dmitrienko" <denis@null.net>
+# (verified against RFC2319).
+# KOI8-U home page: <http://www.net.ua/KOI8-U>
+#
+# Quoted from RFC2319:
+#   The upper part of the KOI8-U Character Set contains all Russian
+#   letters defined in KOI8-R and four Ukrainian letters (#164, #180 -
+#   ukr. ie, #166, #182 - ukr. i, #167, #183 - ukr. yi, #173, #189 - ukr.
+#   ghe  with upturn) which locations are compliant with ISO-IR-111.
+#
+#   BOX DRAWINGS elements in the other positions (that are not used by
+#   Ukrainian letters) are the same as in KOI8-R character set.
+#
+#
+#hex unicode # description
+#--- U+---- # ---------------
+0x80 U+2500 # BOX DRAWINGS  LIGHT HORIZONTAL
+0x81 U+2502 # BOX DRAWINGS  LIGHT VERTICAL
+0x82 U+250C # BOX DRAWINGS  LIGHT DOWN AND RIGHT
+0x83 U+2510 # BOX DRAWINGS  LIGHT DOWN AND LEFT
+0x84 U+2514 # BOX DRAWINGS  LIGHT UP AND RIGHT
+0x85 U+2518 # BOX DRAWINGS  LIGHT UP AND LEFT
+0x86 U+251C # BOX DRAWINGS  LIGHT VERTICAL AND RIGHT
+0x87 U+2524 # BOX DRAWINGS  LIGHT VERTICAL AND LEFT
+0x88 U+252C # BOX DRAWINGS  LIGHT DOWN AND HORIZONTAL
+0x89 U+2534 # BOX DRAWINGS  LIGHT UP AND HORIZONTAL
+0x8A U+253C # BOX DRAWINGS  LIGHT VERTICAL AND HORIZONTAL
+0x8B U+2580 # UPPER HALF BLOCK
+0x8C U+2584 # LOWER HALF BLOCK
+0x8D U+2588 # FULL BLOCK
+0x8E U+258C # LEFT HALF BLOCK
+0x8F U+2590 # RIGHT HALF BLOCK
+0x90 U+2591 # LIGHT SHADE
+0x91 U+2592 # MEDIUM SHADE
+0x92 U+2593 # DARK SHADE
+0x93 U+2320 # TOP HALF INTEGRAL
+0x94 U+25A0 # BLACK SQUARE
+0x95 U+2219 # BULLET OPERATOR
+0x96 U+221A # SQUARE ROOT
+0x97 U+2248 # ALMOST EQUAL TO
+0x98 U+2264 # LESS THAN OR EQUAL TO
+0x99 U+2265 # GREATER THAN OR EQUAL TO
+0x9A U+00A0 # NO-BREAK SPACE
+0x9B U+2321 # BOTTOM HALF INTEGRAL
+0x9C U+00B0 # DEGREE SIGN
+0x9D U+00B2 # SUPERSCRIPT TWO
+0x9E U+00B7 # MIDDLE DOT
+0x9F U+00F7 # DIVISION SIGN
+0xA0 U+2550 # BOX DRAWINGS  DOUBLE HORIZONTAL
+0xA1 U+2551 # BOX DRAWINGS  DOUBLE VERTICAL
+0xA2 U+2552 # BOX DRAWINGS  DOWN SINGLE AND RIGHT DOUBLE
+0xA3 U+0451 # CYRILLIC SMALL LETTER IO
+0xA4 U+0454 # CYRILLIC SMALL LETTER UKRAINIAN IE
+0xA5 U+2554 # BOX DRAWINGS  DOUBLE DOWN AND RIGHT
+0xA6 U+0456 # CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+0xA7 U+0457 # CYRILLIC SMALL LETTER YI (UKRAINIAN)
+0xA8 U+2557 # BOX DRAWINGS  DOUBLE DOWN AND LEFT
+0xA9 U+2558 # BOX DRAWINGS  UP SINGLE AND RIGHT DOUBLE
+0xAA U+2559 # BOX DRAWINGS  UP DOUBLE AND RIGHT SINGLE
+0xAB U+255A # BOX DRAWINGS  DOUBLE UP AND RIGHT
+0xAC U+255B # BOX DRAWINGS  UP SINGLE AND LEFT DOUBLE
+0xAD U+0491 # CYRILLIC SMALL LETTER GHE WITH UPTURN
+0xAE U+255D # BOX DRAWINGS  DOUBLE UP AND LEFT
+0xAF U+255E # BOX DRAWINGS  VERTICAL SINGLE AND RIGHT DOUBLE
+0xB0 U+255F # BOX DRAWINGS  VERTICAL DOUBLE AND RIGHT SINGLE
+0xB1 U+2560 # BOX DRAWINGS  DOUBLE VERTICAL AND RIGHT
+0xB2 U+2561 # BOX DRAWINGS  VERTICAL SINGLE AND LEFT DOUBLE
+0xB3 U+0401 # CYRILLIC CAPITAL LETTER IO
+0xB4 U+0404 # CYRILLIC CAPITAL LETTER UKRAINIAN IE
+0xB5 U+2563 # BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+0xB6 U+0406 # CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+0xB7 U+0407 # CYRILLIC CAPITAL LETTER YI (UKRAINIAN)
+0xB8 U+2566 # BOX DRAWINGS  DOUBLE DOWN AND HORIZONTAL
+0xB9 U+2567 # BOX DRAWINGS  UP SINGLE AND HORIZONTAL DOUBLE
+0xBA U+2568 # BOX DRAWINGS  UP DOUBLE AND HORIZONTAL SINGLE
+0xBB U+2569 # BOX DRAWINGS  DOUBLE UP AND HORIZONTAL
+0xBC U+256A # BOX DRAWINGS  VERTICAL SINGLE AND HORIZONTAL DOUBLE
+0xBD U+0490 # CYRILLIC CAPITAL LETTER GHE WITH UPTURN
+0xBE U+256C # BOX DRAWINGS  DOUBLE VERTICAL AND HORIZONTAL
+0xBF U+00A9 # COPYRIGHT SIGN
+0xC0 U+044E # CYRILLIC SMALL LETTER YU
+0xC1 U+0430 # CYRILLIC SMALL LETTER A
+0xC2 U+0431 # CYRILLIC SMALL LETTER BE
+0xC3 U+0446 # CYRILLIC SMALL LETTER TSE
+0xC4 U+0434 # CYRILLIC SMALL LETTER DE
+0xC5 U+0435 # CYRILLIC SMALL LETTER IE
+0xC6 U+0444 # CYRILLIC SMALL LETTER EF
+0xC7 U+0433 # CYRILLIC SMALL LETTER GHE
+0xC8 U+0445 # CYRILLIC SMALL LETTER KHA
+0xC9 U+0438 # CYRILLIC SMALL LETTER I
+0xCA U+0439 # CYRILLIC SMALL LETTER SHORT I
+0xCB U+043A # CYRILLIC SMALL LETTER KA
+0xCC U+043B # CYRILLIC SMALL LETTER EL
+0xCD U+043C # CYRILLIC SMALL LETTER EM
+0xCE U+043D # CYRILLIC SMALL LETTER EN
+0xCF U+043E # CYRILLIC SMALL LETTER O
+0xD0 U+043F # CYRILLIC SMALL LETTER PE
+0xD1 U+044F # CYRILLIC SMALL LETTER YA
+0xD2 U+0440 # CYRILLIC SMALL LETTER ER
+0xD3 U+0441 # CYRILLIC SMALL LETTER ES
+0xD4 U+0442 # CYRILLIC SMALL LETTER TE
+0xD5 U+0443 # CYRILLIC SMALL LETTER U
+0xD6 U+0436 # CYRILLIC SMALL LETTER ZHE
+0xD7 U+0432 # CYRILLIC SMALL LETTER VE
+0xD8 U+044C # CYRILLIC SMALL LETTER SOFT SIGN
+0xD9 U+044B # CYRILLIC SMALL LETTER YERU
+0xDA U+0437 # CYRILLIC SMALL LETTER ZE
+0xDB U+0448 # CYRILLIC SMALL LETTER SHA
+0xDC U+044D # CYRILLIC SMALL LETTER E
+0xDD U+0449 # CYRILLIC SMALL LETTER SHCHA
+0xDE U+0447 # CYRILLIC SMALL LETTER CHE
+0xDF U+044A # CYRILLIC SMALL LETTER HARD SIGN
+0xE0 U+042E # CYRILLIC CAPITAL LETTER YU
+0xE1 U+0410 # CYRILLIC CAPITAL LETTER A
+0xE2 U+0411 # CYRILLIC CAPITAL LETTER BE
+0xE3 U+0426 # CYRILLIC CAPITAL LETTER TSE
+0xE4 U+0414 # CYRILLIC CAPITAL LETTER DE
+0xE5 U+0415 # CYRILLIC CAPITAL LETTER IE
+0xE6 U+0424 # CYRILLIC CAPITAL LETTER EF
+0xE7 U+0413 # CYRILLIC CAPITAL LETTER GHE
+0xE8 U+0425 # CYRILLIC CAPITAL LETTER KHA
+0xE9 U+0418 # CYRILLIC CAPITAL LETTER I
+0xEA U+0419 # CYRILLIC CAPITAL LETTER SHORT I
+0xEB U+041A # CYRILLIC CAPITAL LETTER KA
+0xEC U+041B # CYRILLIC CAPITAL LETTER EL
+0xED U+041C # CYRILLIC CAPITAL LETTER EM
+0xEE U+041D # CYRILLIC CAPITAL LETTER EN
+0xEF U+041E # CYRILLIC CAPITAL LETTER O
+0xF0 U+041F # CYRILLIC CAPITAL LETTER PE
+0xF1 U+042F # CYRILLIC CAPITAL LETTER YA
+0xF2 U+0420 # CYRILLIC CAPITAL LETTER ER
+0xF3 U+0421 # CYRILLIC CAPITAL LETTER ES
+0xF4 U+0422 # CYRILLIC CAPITAL LETTER TE
+0xF5 U+0423 # CYRILLIC CAPITAL LETTER U
+0xF6 U+0416 # CYRILLIC CAPITAL LETTER ZHE
+0xF7 U+0412 # CYRILLIC CAPITAL LETTER VE
+0xF8 U+042C # CYRILLIC CAPITAL LETTER SOFT SIGN
+0xF9 U+042B # CYRILLIC CAPITAL LETTER YERU
+0xFA U+0417 # CYRILLIC CAPITAL LETTER ZE
+0xFB U+0428 # CYRILLIC CAPITAL LETTER SHA
+0xFC U+042D # CYRILLIC CAPITAL LETTER E
+0xFD U+0429 # CYRILLIC CAPITAL LETTER SHCHA
+0xFE U+0427 # CYRILLIC CAPITAL LETTER CHE
+0xFF U+042A # CYRILLIC CAPITAL LETTER HARD SIGN
diff --git a/src/chrtrans/mac_uni.tbl b/src/chrtrans/mac_uni.tbl
new file mode 100644
index 00000000..25647017
--- /dev/null
+++ b/src/chrtrans/mac_uni.tbl
@@ -0,0 +1,284 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Mmacintosh
+
+#Name as a Display Charset (used on Options screen)
+OMacintosh (8 bit)
+
+#
+#    Name:     cp10000_MacRoman to Unicode table
+#    Unicode version: 2.0
+#    Table version: 2.00
+#    Table format:  Format A
+#    Date:          04/24/96
+#    Authors:       Lori Brownell <loribr@microsoft.com>
+#                   K.D. Chang    <a-kchang@microsoft.com>
+#    General notes: none
+#
+#    Format: Three tab-separated columns
+#        Column #1 is the cp10000_MacRoman code (in hex)
+#        Column #2 is the Unicode (in hex as 0xXXXX)
+#        Column #3 is the Unicode name (follows a comment sign, '#')
+#
+#    The entries are in cp10000_MacRoman order
+#
+# Lines with more than one Unicode (U+XXXX) value contain additional
+# replacement mappings added for lynx. - kw
+#
+0x20-0x7f       idem
+#
+#0x20	U+0020	# SPACE
+#0x21	U+0021	# EXCLAMATION MARK
+#0x22	U+0022	# QUOTATION MARK
+#0x23	U+0023	# NUMBER SIGN
+#0x24	U+0024	# DOLLAR SIGN
+#0x25	U+0025	# PERCENT SIGN
+#0x26	U+0026	# AMPERSAND
+#0x27	U+0027	# APOSTROPHE
+#0x28	U+0028	# LEFT PARENTHESIS
+#0x29	U+0029	# RIGHT PARENTHESIS
+#0x2A	U+002A	# ASTERISK
+#0x2B	U+002B	# PLUS SIGN
+#0x2C	U+002C	# COMMA
+#0x2D	U+002D	# HYPHEN-MINUS
+#0x2E	U+002E	# FULL STOP
+#0x2F	U+002F	# SOLIDUS
+#0x30	U+0030	# DIGIT ZERO
+#0x31	U+0031	# DIGIT ONE
+#0x32	U+0032	# DIGIT TWO
+#0x33	U+0033	# DIGIT THREE
+#0x34	U+0034	# DIGIT FOUR
+#0x35	U+0035	# DIGIT FIVE
+#0x36	U+0036	# DIGIT SIX
+#0x37	U+0037	# DIGIT SEVEN
+#0x38	U+0038	# DIGIT EIGHT
+#0x39	U+0039	# DIGIT NINE
+#0x3A	U+003A	# COLON
+#0x3B	U+003B	# SEMICOLON
+#0x3C	U+003C	# LESS-THAN SIGN
+#0x3D	U+003D	# EQUALS SIGN
+#0x3E	U+003E	# GREATER-THAN SIGN
+#0x3F	U+003F	# QUESTION MARK
+#0x40	U+0040	# COMMERCIAL AT
+#0x41	U+0041	# LATIN CAPITAL LETTER A
+#0x42	U+0042	# LATIN CAPITAL LETTER B
+#0x43	U+0043	# LATIN CAPITAL LETTER C
+#0x44	U+0044	# LATIN CAPITAL LETTER D
+#0x45	U+0045	# LATIN CAPITAL LETTER E
+#0x46	U+0046	# LATIN CAPITAL LETTER F
+#0x47	U+0047	# LATIN CAPITAL LETTER G
+#0x48	U+0048	# LATIN CAPITAL LETTER H
+#0x49	U+0049	# LATIN CAPITAL LETTER I
+#0x4A	U+004A	# LATIN CAPITAL LETTER J
+#0x4B	U+004B	# LATIN CAPITAL LETTER K
+#0x4C	U+004C	# LATIN CAPITAL LETTER L
+#0x4D	U+004D	# LATIN CAPITAL LETTER M
+#0x4E	U+004E	# LATIN CAPITAL LETTER N
+#0x4F	U+004F	# LATIN CAPITAL LETTER O
+#0x50	U+0050	# LATIN CAPITAL LETTER P
+#0x51	U+0051	# LATIN CAPITAL LETTER Q
+#0x52	U+0052	# LATIN CAPITAL LETTER R
+#0x53	U+0053	# LATIN CAPITAL LETTER S
+#0x54	U+0054	# LATIN CAPITAL LETTER T
+#0x55	U+0055	# LATIN CAPITAL LETTER U
+#0x56	U+0056	# LATIN CAPITAL LETTER V
+#0x57	U+0057	# LATIN CAPITAL LETTER W
+#0x58	U+0058	# LATIN CAPITAL LETTER X
+#0x59	U+0059	# LATIN CAPITAL LETTER Y
+#0x5A	U+005A	# LATIN CAPITAL LETTER Z
+#0x5B	U+005B	# LEFT SQUARE BRACKET
+#0x5C	U+005C	# REVERSE SOLIDUS
+#0x5D	U+005D	# RIGHT SQUARE BRACKET
+#0x5E	U+005E	# CIRCUMFLEX ACCENT
+#0x5F	U+005F	# LOW LINE
+#0x60	U+0060	# GRAVE ACCENT
+#0x61	U+0061	# LATIN SMALL LETTER A
+#0x62	U+0062	# LATIN SMALL LETTER B
+#0x63	U+0063	# LATIN SMALL LETTER C
+#0x64	U+0064	# LATIN SMALL LETTER D
+#0x65	U+0065	# LATIN SMALL LETTER E
+#0x66	U+0066	# LATIN SMALL LETTER F
+#0x67	U+0067	# LATIN SMALL LETTER G
+#0x68	U+0068	# LATIN SMALL LETTER H
+#0x69	U+0069	# LATIN SMALL LETTER I
+#0x6A	U+006A	# LATIN SMALL LETTER J
+#0x6B	U+006B	# LATIN SMALL LETTER K
+#0x6C	U+006C	# LATIN SMALL LETTER L
+#0x6D	U+006D	# LATIN SMALL LETTER M
+#0x6E	U+006E	# LATIN SMALL LETTER N
+#0x6F	U+006F	# LATIN SMALL LETTER O
+#0x70	U+0070	# LATIN SMALL LETTER P
+#0x71	U+0071	# LATIN SMALL LETTER Q
+#0x72	U+0072	# LATIN SMALL LETTER R
+#0x73	U+0073	# LATIN SMALL LETTER S
+#0x74	U+0074	# LATIN SMALL LETTER T
+#0x75	U+0075	# LATIN SMALL LETTER U
+#0x76	U+0076	# LATIN SMALL LETTER V
+#0x77	U+0077	# LATIN SMALL LETTER W
+#0x78	U+0078	# LATIN SMALL LETTER X
+#0x79	U+0079	# LATIN SMALL LETTER Y
+#0x7A	U+007A	# LATIN SMALL LETTER Z
+#0x7B	U+007B	# LEFT CURLY BRACKET
+#0x7C	U+007C	# VERTICAL LINE
+#0x7D	U+007D	# RIGHT CURLY BRACKET
+#0x7E	U+007E	# TILDE
+0x80	U+00C4	# LATIN CAPITAL LETTER A WITH DIAERESIS
+0x81	U+00C5	# LATIN CAPITAL LETTER A WITH RING ABOVE
+0x82	U+00C7	# LATIN CAPITAL LETTER C WITH CEDILLA
+0x83	U+00C9	# LATIN CAPITAL LETTER E WITH ACUTE
+0x84	U+00D1	# LATIN CAPITAL LETTER N WITH TILDE
+0x85	U+00D6	# LATIN CAPITAL LETTER O WITH DIAERESIS
+0x86	U+00DC	# LATIN CAPITAL LETTER U WITH DIAERESIS
+0x87	U+00E1	# LATIN SMALL LETTER A WITH ACUTE
+0x88	U+00E0	# LATIN SMALL LETTER A WITH GRAVE
+0x89	U+00E2	# LATIN SMALL LETTER A WITH CIRCUMFLEX
+0x8A	U+00E4	# LATIN SMALL LETTER A WITH DIAERESIS
+0x8B	U+00E3	# LATIN SMALL LETTER A WITH TILDE
+0x8C	U+00E5	# LATIN SMALL LETTER A WITH RING ABOVE
+0x8D	U+00E7	# LATIN SMALL LETTER C WITH CEDILLA
+0x8E	U+00E9	# LATIN SMALL LETTER E WITH ACUTE
+0x8F	U+00E8	# LATIN SMALL LETTER E WITH GRAVE
+0x90	U+00EA	# LATIN SMALL LETTER E WITH CIRCUMFLEX
+0x91	U+00EB	# LATIN SMALL LETTER E WITH DIAERESIS
+0x92	U+00ED	# LATIN SMALL LETTER I WITH ACUTE
+0x93	U+00EC	# LATIN SMALL LETTER I WITH GRAVE
+0x94	U+00EE	# LATIN SMALL LETTER I WITH CIRCUMFLEX
+0x95	U+00EF	# LATIN SMALL LETTER I WITH DIAERESIS
+0x96	U+00F1	# LATIN SMALL LETTER N WITH TILDE
+0x97	U+00F3	# LATIN SMALL LETTER O WITH ACUTE
+0x98	U+00F2	# LATIN SMALL LETTER O WITH GRAVE
+0x99	U+00F4	# LATIN SMALL LETTER O WITH CIRCUMFLEX
+0x9A	U+00F6	# LATIN SMALL LETTER O WITH DIAERESIS
+0x9B	U+00F5	# LATIN SMALL LETTER O WITH TILDE
+0x9C	U+00FA	# LATIN SMALL LETTER U WITH ACUTE
+0x9D	U+00F9	# LATIN SMALL LETTER U WITH GRAVE
+0x9E	U+00FB	# LATIN SMALL LETTER U WITH CIRCUMFLEX
+0x9F	U+00FC	# LATIN SMALL LETTER U WITH DIAERESIS
+0xA0	U+2020	# DAGGER
+0xA1	U+00B0	# DEGREE SIGN
+0xA2	U+00A2	# CENT SIGN
+0xA3	U+00A3	# POUND SIGN
+0xA4	U+00A7	# SECTION SIGN
+0xA5	U+2022	# BULLET
+0xA6	U+00B6	# PILCROW SIGN
+0xA7	U+00DF	# LATIN SMALL LETTER SHARP S
+0xA8	U+00AE	# REGISTERED SIGN
+0xA9	U+00A9	# COPYRIGHT SIGN
+0xAA	U+2122	# TRADE MARK SIGN
+0xAB	U+00B4	# ACUTE ACCENT
+0xAC	U+00A8	# DIAERESIS
+0xAD	U+2260	# NOT EQUAL TO
+0xAE	U+00C6	# LATIN CAPITAL LIGATURE AE
+0xAF	U+00D8	# LATIN CAPITAL LETTER O WITH STROKE
+0xB0	U+221E	# INFINITY
+0xB1	U+00B1	# PLUS-MINUS SIGN
+0xB2	U+2264	# LESS-THAN OR EQUAL TO
+0xB3	U+2265	# GREATER-THAN OR EQUAL TO
+0xB4	U+00A5	# YEN SIGN
+0xB5	U+00B5	# MICRO SIGN
+0xB6	U+2202	# PARTIAL DIFFERENTIAL
+0xB7	U+2211	# N-ARY SUMMATION
+0xB8	U+220F	# N-ARY PRODUCT
+0xB9	U+03C0	# GREEK SMALL LETTER PI
+0xBA	U+222B	# INTEGRAL
+0xBB	U+00AA	# FEMININE ORDINAL INDICATOR
+0xBC	U+00BA	# MASCULINE ORDINAL INDICATOR
+0xBD	U+2126	# OHM SIGN
+0xBE	U+00E6	# LATIN SMALL LIGATURE AE
+0xBF	U+00F8	# LATIN SMALL LETTER O WITH STROKE
+0xC0	U+00BF	# INVERTED QUESTION MARK
+0xC1	U+00A1	# INVERTED EXCLAMATION MARK
+0xC2	U+00AC	# NOT SIGN
+0xC3	U+221A	# SQUARE ROOT
+0xC4	U+0192	# LATIN SMALL LETTER F WITH HOOK
+0xC5	U+2248	# ALMOST EQUAL TO
+0xC6	U+2206	# INCREMENT
+0xC7	U+00AB	# LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xC8	U+00BB	# RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xC9	U+2026	# HORIZONTAL ELLIPSIS
+0xCA	U+00A0	# NO-BREAK SPACE
+0xCB	U+00C0	# LATIN CAPITAL LETTER A WITH GRAVE
+0xCC	U+00C3	# LATIN CAPITAL LETTER A WITH TILDE
+0xCD	U+00D5	# LATIN CAPITAL LETTER O WITH TILDE
+0xCE	U+0152	# LATIN CAPITAL LIGATURE OE
+0xCF	U+0153	# LATIN SMALL LIGATURE OE
+0xD0	U+2013	# EN DASH
+0xD1	U+2014	# EM DASH
+0xD2	U+201C	# LEFT DOUBLE QUOTATION MARK
+0xD3	U+201D	# RIGHT DOUBLE QUOTATION MARK
+0xD4	U+2018	# LEFT SINGLE QUOTATION MARK
+0xD5	U+2019	# RIGHT SINGLE QUOTATION MARK
+0xD6	U+00F7	# DIVISION SIGN
+0xD7	U+25CA	# LOZENGE
+0xD8	U+00FF	# LATIN SMALL LETTER Y WITH DIAERESIS
+0xD9	U+0178	# LATIN CAPITAL LETTER Y WITH DIAERESIS
+0xDA	U+2044	# FRACTION SLASH
+0xDB	U+00A4	# CURRENCY SIGN
+0xDC	U+2039	# SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+0xDD	U+203A	# SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0xDE	U+FB01	# LATIN SMALL LIGATURE FI
+0xDF	U+FB02	# LATIN SMALL LIGATURE FL
+0xE0	U+2021	# DOUBLE DAGGER
+0xE1    U+00B7  U+0307	U+0387	U+2027	# MIDDLE DOT
+0xE2	U+201A	# SINGLE LOW-9 QUOTATION MARK
+0xE3	U+201E	# DOUBLE LOW-9 QUOTATION MARK
+0xE4	U+2030	# PER MILLE SIGN
+0xE5	U+00C2	# LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0xE6	U+00CA	# LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+0xE7	U+00C1	# LATIN CAPITAL LETTER A WITH ACUTE
+0xE8	U+00CB	# LATIN CAPITAL LETTER E WITH DIAERESIS
+0xE9	U+00C8	# LATIN CAPITAL LETTER E WITH GRAVE
+0xEA	U+00CD	# LATIN CAPITAL LETTER I WITH ACUTE
+0xEB	U+00CE	# LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0xEC	U+00CF	# LATIN CAPITAL LETTER I WITH DIAERESIS
+0xED	U+00CC	# LATIN CAPITAL LETTER I WITH GRAVE
+0xEE	U+00D3	# LATIN CAPITAL LETTER O WITH ACUTE
+0xEF	U+00D4	# LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0xF0		# UNDEFINED
+0xF1	U+00D2	# LATIN CAPITAL LETTER O WITH GRAVE
+0xF2	U+00DA	# LATIN CAPITAL LETTER U WITH ACUTE
+0xF3	U+00DB	# LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+0xF4	U+00D9	# LATIN CAPITAL LETTER U WITH GRAVE
+0xF5	U+0131	# LATIN SMALL LETTER DOTLESS I
+0xF6	U+02C6	# MODIFIER LETTER CIRCUMFLEX ACCENT
+0xF7	U+02DC	# SMALL TILDE
+0xF8	U+00AF	# MACRON
+0xF9	U+02D8	# BREVE
+0xFA	U+02D9	# DOT ABOVE
+0xFB	U+02DA	# RING ABOVE
+0xFC	U+00B8	# CEDILLA
+0xFD	U+02DD	# DOUBLE ACUTE ACCENT
+0xFE	U+02DB	# OGONEK
+0xFF	U+02C7	# CARON
+#
+# broken vertical bar (&#166;) - brvbar, brkbar
+U+00A6:|
+# superscript 3 (&#179;) - sup3
+U+00B3:^3
+# superscript 2 (&#178;) - sup2
+U+00B2:^2
+# superscript 1 (&#185;) - sup1
+U+00B9:^1
+# fraction 1/4 (&#188;) - frac14
+U+00BC: 1/4
+# fraction 1/2 (&#189;) - frac12
+U+00BD: 1/2
+# fraction 3/4 (&#190;) - frac34
+U+00BE: 3/4
+# capital Eth, Icelandic (&#208;) - ETH
+U+00D0:DH
+#      Dj  # capital D with stroke - Dstrok
+# capital Y, acute accent (&#221;) - Yacute
+U+00DD:Y'
+# capital THORN, Icelandic (&#222;) - THORN
+U+00DE:P
+# multiplication sign (&#215;) - times
+U+00D7:*
+# small eth, Icelandic (&#240;) - eth
+U+00F0:dh
+# small y, acute accent (&#253;) - yacute
+U+00FD:y'
+# small thorn, Icelandic (&#254;) - thorn
+U+00FE:p
+#
diff --git a/src/chrtrans/make-msc.bat b/src/chrtrans/make-msc.bat
new file mode 100644
index 00000000..81d615e0
--- /dev/null
+++ b/src/chrtrans/make-msc.bat
@@ -0,0 +1,6 @@
+@rem $LynxId: make-msc.bat,v 1.6 2008/02/18 00:34:44 tom Exp $

+@echo off

+

+nmake -f makefile.msc %1 %2 %3 %4 %5 %6 %7 %8 %9

+

+if exist makeuctb.exe call makehdrs

diff --git a/src/chrtrans/makefile.bcb b/src/chrtrans/makefile.bcb
new file mode 100644
index 00000000..134a3958
--- /dev/null
+++ b/src/chrtrans/makefile.bcb
@@ -0,0 +1,123 @@
+#
+# Borland C++ IDE generated makefile
+#
+# 1997/11/09 (Sun) 14:29:50
+#
+.AUTODEPEND
+
+
+#
+# Borland C++ tools
+#
+IMPLIB  = Implib
+BCC32   = Bcc32 +BccW32.cfg
+TLINK32 = TLink32
+TLIB    = TLib
+BRC32   = Brc32
+TASM32  = Tasm32
+#
+# macros
+#
+BCB = $(MAKEDIR)/..
+BCC_INC = $(BCB)/INCLUDE
+
+#
+# Options
+#
+
+INCLUDES = -I.;../..;../../WWW/LIBRARY/IMPLEMENTATION;$(BCC_INC)
+DEFS =-DNO_FILIO_H;NO_UNISTD_H;_WINDOWS;DOSPATH
+LNIEAT_dbmakeuctbdexe = -x
+
+#
+# Dependency List
+#
+Dep_char = .\makeuctb.exe
+
+char : BccW32.cfg $(Dep_char)
+	echo MakeNode
+
+Dep_dbmakeuctbdexe = .\makeuctb.obj
+
+.\makeuctb.exe : $(Dep_dbmakeuctbdexe)
+	$(BCC32) makeuctb.obj
+
+###
+.\makeuctb.obj :  makeuctb.c
+	$(BCC32) -P- -c $(DEFS) $(INCLUDES) -o$@ makeuctb.c
+
+# Compiler configuration file
+BccW32.cfg :
+	Copy &&|
+-R
+-v
+-vi
+-H
+-H=lynx.csm
+-w-
+-A-
+-wcpt
+-wrpt
+-wrng
+-w-voi
+-w-ret
+-w-sus
+-w-dup
+-w-big
+-w-ext
+-w-zdi
+-w-bei
+-w-obi
+-w-ofp
+-w-eas
+-w-hid
+-w-ncf
+-w-ibc
+-w-dsz
+-w-nst
+-w-mpc
+-w-mpd
+-w-ntd
+-w-nvf
+-w-hch
+-w-inl
+-w-lin
+-w-lvc
+-w-pia
+-w-def
+-w-nod
+-w-pro
+-w-rvl
+-w-ccc
+-w-aus
+-w-par
+-w-rch
+-w-eff
+-w-ill
+-w-ias
+-w-msg
+-WC
+-Ot
+-d-
+-K
+-a-
+-w-stu
+-wbbf
+-w-dpu
+-wcln
+-wsig
+-wucp
+-g200
+-H-
+-v-
+| $@
+
+clean :
+	-del *_uni.h
+	-del *_suni.h
+	-del *.exe
+	-del *.map
+	-del *.obj
+	-del *.tds
+	-del BccW32.cfg
+	-del /f/s/q *.i
diff --git a/src/chrtrans/makefile.dos b/src/chrtrans/makefile.dos
new file mode 100644
index 00000000..07c141f4
--- /dev/null
+++ b/src/chrtrans/makefile.dos
@@ -0,0 +1,135 @@
+#
+# Makefile for the makeuctb and unicode tables
+# for use with DJGPP.
+#
+# Type make to build makeuctb  and all character translation maps.
+# Type make fontmap to build makeuctb and translation map iso8859-1.
+# Type make makeuctb.exe to build makeuctb only.
+# Type make clean to remove makeuctb and character translation maps.
+# Type make distclean to remove makeuctb, character translation maps
+# and .bak files.
+#
+CFLAGS = $(MCFLAGS)
+
+CC = gcc
+MCFLAGS = -O2 -DDOSPATH \
+-I. \
+-I../../WWW/Library/Implementation \
+-I/djgpp/watt32/inc
+-I../..
+
+.SUFFIXES: .tbl
+#
+# This file contains the font map for the default (hardware) font
+#
+
+FONTMAP_INC = iso01_un.h
+
+TABLES= \
+ cp1250_uni.h \
+ cp1251_uni.h \
+ cp1252_uni.h \
+ cp1253_uni.h \
+ cp1255_uni.h \
+ cp1256_uni.h \
+ cp1257_uni.h \
+ cp437_uni.h \
+ cp737_uni.h \
+ cp775_uni.h \
+ cp850_uni.h \
+ cp852_uni.h \
+ cp857_uni.h \
+ cp862_uni.h \
+ cp864_uni.h \
+ cp866_uni.h \
+ cp866u_uni.h \
+ cp869_uni.h \
+ def7_uni.h \
+ dmcs_uni.h \
+ hp_uni.h \
+ iso01_uni.h \
+ iso02_uni.h \
+ iso03_uni.h \
+ iso04_uni.h \
+ iso05_uni.h \
+ iso06_uni.h \
+ iso07_uni.h \
+ iso08_uni.h \
+ iso09_uni.h \
+ iso10_uni.h \
+ iso13_uni.h \
+ iso14_uni.h \
+ iso15_uni.h \
+ koi8r_uni.h \
+ koi8u_uni.h \
+ mac_uni.h \
+ mnem2_suni.h \
+ mnem_suni.h \
+ next_uni.h \
+ pt154_uni.h \
+ rfc_suni.h \
+ utf8_uni.h \
+ viscii_uni.h
+
+default: $(TABLES)
+
+fontmap: $(FONTMAP_INC)
+
+makeuctb.exe: makeuctb.c UCkd.h
+	$(CC) $(CFLAGS) -o makeuctb.exe makeuctb.c
+	strip makeuctb.exe
+
+.tbl.h:
+	./makeuctb $*.tbl
+
+cp1250_uni.h:		cp1250_uni.tbl		makeuctb.exe
+cp1251_uni.h:		cp1251_uni.tbl		makeuctb.exe
+cp1252_uni.h:		cp1252_uni.tbl		makeuctb.exe
+cp1253_uni.h:		cp1253_uni.tbl		makeuctb.exe
+cp1255_uni.h:		cp1255_uni.tbl		makeuctb.exe
+cp1256_uni.h:		cp1256_uni.tbl		makeuctb.exe
+cp1257_uni.h:		cp1257_uni.tbl		makeuctb.exe
+cp437_uni.h:		cp437_uni.tbl		makeuctb.exe
+cp737_uni.h:		cp737_uni.tbl		makeuctb.exe
+cp775_uni.h:		cp775_uni.tbl		makeuctb.exe
+cp850_uni.h:		cp850_uni.tbl		makeuctb.exe
+cp852_uni.h:		cp852_uni.tbl		makeuctb.exe
+cp857_uni.h:		cp857_uni.tbl		makeuctb.exe
+cp862_uni.h:		cp862_uni.tbl		makeuctb.exe
+cp864_uni.h:		cp864_uni.tbl		makeuctb.exe
+cp866_uni.h:		cp866_uni.tbl		makeuctb.exe
+cp866u_uni.h:		cp866u_uni.tbl		makeuctb.exe
+cp869_uni.h:		cp869_uni.tbl		makeuctb.exe
+def7_uni.h:		def7_uni.tbl		makeuctb.exe
+dmcs_uni.h:		dmcs_uni.tbl		makeuctb.exe
+hp_uni.h:               hp_uni.tbl              makeuctb.exe
+iso01_uni.h:		iso01_uni.tbl		makeuctb.exe
+iso02_uni.h:		iso02_uni.tbl		makeuctb.exe
+iso03_uni.h:		iso03_uni.tbl		makeuctb.exe
+iso04_uni.h:		iso04_uni.tbl		makeuctb.exe
+iso05_uni.h:		iso05_uni.tbl		makeuctb.exe
+iso06_uni.h:		iso06_uni.tbl		makeuctb.exe
+iso07_uni.h:		iso07_uni.tbl		makeuctb.exe
+iso08_uni.h:		iso08_uni.tbl		makeuctb.exe
+iso09_uni.h:		iso09_uni.tbl		makeuctb.exe
+iso10_uni.h:		iso10_uni.tbl		makeuctb.exe
+iso13_uni.h:		iso13_uni.tbl		makeuctb.exe
+iso14_uni.h:		iso14_uni.tbl		makeuctb.exe
+iso15_uni.h:		iso15_uni.tbl		makeuctb.exe
+koi8r_uni.h:		koi8r_uni.tbl		makeuctb.exe
+koi8u_uni.h:		koi8u_uni.tbl		makeuctb.exe
+mac_uni.h:		mac_uni.tbl		makeuctb.exe
+mnem2_suni.h:		mnem2_suni.tbl		makeuctb.exe
+mnem_suni.h:		mnem_suni.tbl		makeuctb.exe
+next_uni.h:		next_uni.tbl		makeuctb.exe
+pt154_uni.h:		pt154_uni.tbl		makeuctb.exe
+rfc_suni.h:		rfc_suni.tbl		makeuctb.exe
+utf8_uni.h:		utf8_uni.tbl		makeuctb.exe
+viscii_uni.h:		viscii_uni.tbl		makeuctb.exe
+
+clean:
+	rm -f makeuctb.exe makeuctb *.o *un.h *u.h *c.h *i.h
+
+distclean: clean
+	-rm -f *.bak
+
diff --git a/src/chrtrans/makefile.in b/src/chrtrans/makefile.in
new file mode 100644
index 00000000..dcf827ec
--- /dev/null
+++ b/src/chrtrans/makefile.in
@@ -0,0 +1,200 @@
+# $LynxId: makefile.in,v 1.36 2010/04/30 00:20:45 tom Exp $
+#
+# Makefile for the makeuctb and unicode tables.
+#
+# This may not yet work for the general case.
+# Only some dependencies included.
+#
+SHELL		= @CONFIG_SHELL@
+
+prefix		= @prefix@
+exec_prefix	= @exec_prefix@
+top_srcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= $(srcdir)
+
+top_builddir	= ../..
+
+CC		= @CC@
+CPP		= @CPP@
+CFLAGS		= @CFLAGS@
+EXTRA_CPPFLAGS	= @EXTRA_CPPFLAGS@
+CPPFLAGS	= @CPPFLAGS@
+
+LIBS		= @LIBS@
+LDFLAGS		= @LDFLAGS@
+
+INTLLIB		= @INTLDIR_MAKE@@INTLLIBS@
+
+WWWINC		= WWW/Library/Implementation
+INTLDIR_CPPFLAGS= @INTLDIR_CPPFLAGS@-I$(top_srcdir)/intl
+
+SITE_DEFS	= # FIXME: set in parent makefile
+
+BUILD_CC	= @BUILD_CC@
+BUILD_CPP	= @BUILD_CPP@
+BUILD_CFLAGS	= @BUILD_CFLAGS@
+BUILD_CPPFLAGS	= @BUILD_CPPFLAGS@ @DEFS@
+
+BUILD_LIBS	= @BUILD_LIBS@ #$(SITE_LIBS)
+BUILD_LDFLAGS	= @BUILD_LDFLAGS@
+
+x		= @EXEEXT@
+o		= .@BUILD_OBJEXT@
+BUILD_EXEEXT	= @BUILD_EXEEXT@
+
+CPP_OPTS	= \
+		-I$(top_builddir) \
+		-I$(top_srcdir)/src \
+		-I$(top_srcdir)/src/chrtrans \
+		-I$(top_srcdir)/$(WWWINC) \
+		-I$(top_srcdir)/ \
+		$(INTLDIR_CPPFLAGS) $(SITE_DEFS) $(BUILD_CPPFLAGS)
+CC_OPTS		= $(CPP_OPTS) $(BUILD_CFLAGS)
+
+LINT		= @LINT@
+LINTOPTS	=
+
+#
+# This file contains the font map for the default (hardware) font
+#
+
+FONTMAP_INC = iso01_uni.h# default, if not set by recursive call
+
+### fastdep: $(FONTMAP_INC)
+
+MAKEUCTB = makeuctb$(BUILD_EXEEXT)
+
+TABLES= \
+ cp1250_uni.h \
+ cp1251_uni.h \
+ cp1252_uni.h \
+ cp1253_uni.h \
+ cp1255_uni.h \
+ cp1256_uni.h \
+ cp1257_uni.h \
+ cp437_uni.h \
+ cp737_uni.h \
+ cp775_uni.h \
+ cp850_uni.h \
+ cp852_uni.h \
+ cp857_uni.h \
+ cp862_uni.h \
+ cp864_uni.h \
+ cp866_uni.h \
+ cp866u_uni.h \
+ cp869_uni.h \
+ def7_uni.h \
+ dmcs_uni.h \
+ hp_uni.h \
+ iso01_uni.h \
+ iso02_uni.h \
+ iso03_uni.h \
+ iso04_uni.h \
+ iso05_uni.h \
+ iso06_uni.h \
+ iso07_uni.h \
+ iso08_uni.h \
+ iso09_uni.h \
+ iso10_uni.h \
+ iso13_uni.h \
+ iso14_uni.h \
+ iso15_uni.h \
+ koi8r_uni.h \
+ koi8u_uni.h \
+ mac_uni.h \
+ mnem2_suni.h \
+ mnem_suni.h \
+ next_uni.h \
+ next_uni.h \
+ pt154_uni.h \
+ rfc_suni.h \
+ utf8_uni.h \
+ viscii_uni.h
+
+default: $(FONTMAP_INC)
+
+tables: $(TABLES)
+
+OBJS		= makeuctb$o
+C_SRC		= $(OBJS:$o=.c)
+
+$(MAKEUCTB) : $(OBJS)
+	$(BUILD_CC) $(CC_OPTS) $(BUILD_LDFLAGS) -o $@ $(OBJS) $(INTLLIB) $(BUILD_LIBS)
+
+makeuctb$o : $(srcdir)/UCkd.h $(srcdir)/makeuctb.c
+
+.SUFFIXES : $o .tbl .i .h
+
+.c$o:
+	@RULE_CC@
+	@ECHO_CC@$(BUILD_CC) $(CC_OPTS) -c $(srcdir)/$*.c
+
+.c.i:
+	@RULE_CC@
+	@ECHO_CC@$(BUILD_CPP) -C $(CPP_OPTS) $(srcdir)/$*.c >$@
+
+.tbl.h:
+	./$(MAKEUCTB) $(srcdir)/$*.tbl $*.h
+
+# table files listed here once again to get the make dependencies
+# right, in case makeuctb was recompiled.
+cp1250_uni.h:		$(srcdir)/cp1250_uni.tbl	$(MAKEUCTB)
+cp1251_uni.h:		$(srcdir)/cp1251_uni.tbl	$(MAKEUCTB)
+cp1252_uni.h:		$(srcdir)/cp1252_uni.tbl	$(MAKEUCTB)
+cp1253_uni.h:		$(srcdir)/cp1253_uni.tbl	$(MAKEUCTB)
+cp1255_uni.h:		$(srcdir)/cp1255_uni.tbl	$(MAKEUCTB)
+cp1256_uni.h:		$(srcdir)/cp1256_uni.tbl	$(MAKEUCTB)
+cp1257_uni.h:		$(srcdir)/cp1257_uni.tbl	$(MAKEUCTB)
+cp437_uni.h:		$(srcdir)/cp437_uni.tbl		$(MAKEUCTB)
+cp737_uni.h:		$(srcdir)/cp737_uni.tbl		$(MAKEUCTB)
+cp775_uni.h:		$(srcdir)/cp775_uni.tbl		$(MAKEUCTB)
+cp850_uni.h:		$(srcdir)/cp850_uni.tbl		$(MAKEUCTB)
+cp852_uni.h:		$(srcdir)/cp852_uni.tbl		$(MAKEUCTB)
+cp857_uni.h:		$(srcdir)/cp857_uni.tbl		$(MAKEUCTB)
+cp862_uni.h:		$(srcdir)/cp862_uni.tbl		$(MAKEUCTB)
+cp864_uni.h:		$(srcdir)/cp864_uni.tbl		$(MAKEUCTB)
+cp866_uni.h:		$(srcdir)/cp866_uni.tbl		$(MAKEUCTB)
+cp866u_uni.h:		$(srcdir)/cp866u_uni.tbl	$(MAKEUCTB)
+cp869_uni.h:		$(srcdir)/cp869_uni.tbl		$(MAKEUCTB)
+def7_uni.h:		$(srcdir)/def7_uni.tbl		$(MAKEUCTB)
+dmcs_uni.h:		$(srcdir)/dmcs_uni.tbl		$(MAKEUCTB)
+hp_uni.h:		$(srcdir)/hp_uni.tbl		$(MAKEUCTB)
+iso01_uni.h:		$(srcdir)/iso01_uni.tbl		$(MAKEUCTB)
+iso02_uni.h:		$(srcdir)/iso02_uni.tbl		$(MAKEUCTB)
+iso03_uni.h:		$(srcdir)/iso03_uni.tbl		$(MAKEUCTB)
+iso04_uni.h:		$(srcdir)/iso04_uni.tbl		$(MAKEUCTB)
+iso05_uni.h:		$(srcdir)/iso05_uni.tbl		$(MAKEUCTB)
+iso06_uni.h:		$(srcdir)/iso06_uni.tbl		$(MAKEUCTB)
+iso07_uni.h:		$(srcdir)/iso07_uni.tbl		$(MAKEUCTB)
+iso08_uni.h:		$(srcdir)/iso08_uni.tbl		$(MAKEUCTB)
+iso09_uni.h:		$(srcdir)/iso09_uni.tbl		$(MAKEUCTB)
+iso10_uni.h:		$(srcdir)/iso10_uni.tbl		$(MAKEUCTB)
+iso13_uni.h:		$(srcdir)/iso13_uni.tbl		$(MAKEUCTB)
+iso14_uni.h:		$(srcdir)/iso14_uni.tbl		$(MAKEUCTB)
+iso15_uni.h:		$(srcdir)/iso15_uni.tbl		$(MAKEUCTB)
+koi8r_uni.h:		$(srcdir)/koi8r_uni.tbl		$(MAKEUCTB)
+koi8u_uni.h:		$(srcdir)/koi8u_uni.tbl		$(MAKEUCTB)
+mac_uni.h:		$(srcdir)/mac_uni.tbl		$(MAKEUCTB)
+mnem2_suni.h:		$(srcdir)/mnem2_suni.tbl	$(MAKEUCTB)
+mnem_suni.h:		$(srcdir)/mnem_suni.tbl		$(MAKEUCTB)
+next_uni.h:		$(srcdir)/next_uni.tbl		$(MAKEUCTB)
+pt154_uni.h:		$(srcdir)/pt154_uni.tbl 	$(MAKEUCTB)
+rfc_suni.h:		$(srcdir)/rfc_suni.tbl		$(MAKEUCTB)
+utf8_uni.h:		$(srcdir)/utf8_uni.tbl		$(MAKEUCTB)
+viscii_uni.h:		$(srcdir)/viscii_uni.tbl	$(MAKEUCTB)
+
+lint:
+	$(LINT) $(LINTOPTS) $(CPP_OPTS) $(C_SRC) 2>&1 |tee $(top_builddir)/lint.chrtrans
+
+clean:
+	rm -f $(MAKEUCTB) *$o *uni.h *uni2.h *.i
+
+distclean: clean
+	-rm -rf obsolete
+	rm -f core *.bak *.sav *~ *.h_old
+
+depend :
+	makedepend -fmakefile -- $(CPP_OPTS) -- $(C_SRC)
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
diff --git a/src/chrtrans/makefile.msc b/src/chrtrans/makefile.msc
new file mode 100644
index 00000000..63a19dd8
--- /dev/null
+++ b/src/chrtrans/makefile.msc
@@ -0,0 +1,136 @@
+#
+# Makefile for Microsoft Visual C++ 4.2 or later
+#
+
+CC       = cl
+LD       = link
+
+INCLUDES = /I "." /I ".." /I "..\.." /I "..\..\WWW\Library\Implementation" /I "..\..\lib"
+DEFS = /D "NDEBUG" /D "__WIN32__" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D "_WIN32" /D "NO_FILEIO_H" /D "NO_UNISTD_H" /D "_WINDOWS" /D "DOSPATH"
+CFLAGS   = /nologo /MT /W3 /GX /O2 /c
+
+LDFLAGS  = /nologo /subsystem:console /incremental:no /machine:I386
+LIBS     = user32.lib wsock32.lib
+
+COMPILE = $(CC) $(CFLAGS) $(INCLUDES) $(DEFS)
+LINK    = $(LD) $(LDFLAGS) /out:$@
+
+
+.SUFFIXES: .tbl
+#
+# This file contains the font map for the default (hardware) font
+#
+
+FONTMAP_INC = iso01_un.h
+
+TABLES= \
+ cp1250_uni.h \
+ cp1251_uni.h \
+ cp1252_uni.h \
+ cp1253_uni.h \
+ cp1255_uni.h \
+ cp1256_uni.h \
+ cp1257_uni.h \
+ cp437_uni.h \
+ cp737_uni.h \
+ cp775_uni.h \
+ cp850_uni.h \
+ cp852_uni.h \
+ cp857_uni.h \
+ cp862_uni.h \
+ cp864_uni.h \
+ cp866_uni.h \
+ cp866u_uni.h \
+ cp869_uni.h \
+ def7_uni.h \
+ dmcs_uni.h \
+ hp_uni.h \
+ iso01_uni.h \
+ iso02_uni.h \
+ iso03_uni.h \
+ iso04_uni.h \
+ iso05_uni.h \
+ iso06_uni.h \
+ iso07_uni.h \
+ iso08_uni.h \
+ iso09_uni.h \
+ iso10_uni.h \
+ iso13_uni.h \
+ iso14_uni.h \
+ iso15_uni.h \
+ koi8r_uni.h \
+ koi8u_uni.h \
+ mac_uni.h \
+ mnem2_suni.h \
+ mnem_suni.h \
+ next_uni.h \
+ pt154_uni.h \
+ rfc_suni.h \
+ utf8_uni.h \
+ viscii_uni.h
+
+default: $(TABLES)
+
+fontmap: $(FONTMAP_INC)
+
+makeuctb.exe : makeuctb.obj
+	$(LINK) makeuctb.obj $(LIBS)
+
+makeuctb.obj :  makeuctb.c
+	$(COMPILE) makeuctb.c
+
+.tbl.h:
+	makeuctb $*.tbl
+
+cp1250_uni.h:		cp1250_uni.tbl		makeuctb.exe
+cp1251_uni.h:		cp1251_uni.tbl		makeuctb.exe
+cp1252_uni.h:		cp1252_uni.tbl		makeuctb.exe
+cp1253_uni.h:		cp1253_uni.tbl		makeuctb.exe
+cp1255_uni.h:		cp1255_uni.tbl		makeuctb.exe
+cp1256_uni.h:		cp1256_uni.tbl		makeuctb.exe
+cp1257_uni.h:		cp1257_uni.tbl		makeuctb.exe
+cp437_uni.h:		cp437_uni.tbl		makeuctb.exe
+cp737_uni.h:		cp737_uni.tbl		makeuctb.exe
+cp775_uni.h:		cp775_uni.tbl		makeuctb.exe
+cp850_uni.h:		cp850_uni.tbl		makeuctb.exe
+cp852_uni.h:		cp852_uni.tbl		makeuctb.exe
+cp857_uni.h:		cp857_uni.tbl		makeuctb.exe
+cp862_uni.h:		cp862_uni.tbl		makeuctb.exe
+cp864_uni.h:		cp864_uni.tbl		makeuctb.exe
+cp866_uni.h:		cp866_uni.tbl		makeuctb.exe
+cp866u_uni.h:		cp866u_uni.tbl		makeuctb.exe
+cp869_uni.h:		cp869_uni.tbl		makeuctb.exe
+def7_uni.h:		def7_uni.tbl		makeuctb.exe
+dmcs_uni.h:		dmcs_uni.tbl		makeuctb.exe
+hp_uni.h:               hp_uni.tbl              makeuctb.exe
+iso01_uni.h:		iso01_uni.tbl		makeuctb.exe
+iso02_uni.h:		iso02_uni.tbl		makeuctb.exe
+iso03_uni.h:		iso03_uni.tbl		makeuctb.exe
+iso04_uni.h:		iso04_uni.tbl		makeuctb.exe
+iso05_uni.h:		iso05_uni.tbl		makeuctb.exe
+iso06_uni.h:		iso06_uni.tbl		makeuctb.exe
+iso07_uni.h:		iso07_uni.tbl		makeuctb.exe
+iso08_uni.h:		iso08_uni.tbl		makeuctb.exe
+iso09_uni.h:		iso09_uni.tbl		makeuctb.exe
+iso10_uni.h:		iso10_uni.tbl		makeuctb.exe
+iso13_uni.h:		iso13_uni.tbl		makeuctb.exe
+iso14_uni.h:		iso14_uni.tbl		makeuctb.exe
+iso15_uni.h:		iso15_uni.tbl		makeuctb.exe
+koi8r_uni.h:		koi8r_uni.tbl		makeuctb.exe
+koi8u_uni.h:		koi8u_uni.tbl		makeuctb.exe
+mac_uni.h:		mac_uni.tbl		makeuctb.exe
+mnem2_suni.h:		mnem2_suni.tbl		makeuctb.exe
+mnem_suni.h:		mnem_suni.tbl		makeuctb.exe
+next_uni.h:		next_uni.tbl		makeuctb.exe
+pt154_uni.h:		pt154_uni.tbl		makeuctb.exe
+rfc_suni.h:		rfc_suni.tbl		makeuctb.exe
+utf8_uni.h:		utf8_uni.tbl		makeuctb.exe
+viscii_uni.h:		viscii_uni.tbl		makeuctb.exe
+
+clean :
+	- erase *.obj
+	- erase *.exe
+	- for %%i in ( $(TABLES) ) do erase %%i
+
+distclean : clean
+	- erase *.bak
diff --git a/src/chrtrans/makehdrs.bat b/src/chrtrans/makehdrs.bat
new file mode 100644
index 00000000..2edbef7c
--- /dev/null
+++ b/src/chrtrans/makehdrs.bat
@@ -0,0 +1,50 @@
+@rem $LynxId: makehdrs.bat,v 1.1 2007/06/28 21:50:29 tom Exp $

+@echo If .tbl files are added or removed you will need to hand edit

+@echo this batch file.

+@echo .

+@echo off

+

+makeuctb cp1250_uni.tbl

+makeuctb cp1251_uni.tbl

+makeuctb cp1252_uni.tbl

+makeuctb cp1253_uni.tbl

+makeuctb cp1255_uni.tbl

+makeuctb cp1256_uni.tbl

+makeuctb cp1257_uni.tbl

+makeuctb cp437_uni.tbl

+makeuctb cp737_uni.tbl

+makeuctb cp775_uni.tbl

+makeuctb cp850_uni.tbl

+makeuctb cp852_uni.tbl

+makeuctb cp857_uni.tbl

+makeuctb cp862_uni.tbl

+makeuctb cp864_uni.tbl

+makeuctb cp866_uni.tbl

+makeuctb cp866u_uni.tbl

+makeuctb cp869_uni.tbl

+makeuctb def7_uni.tbl

+makeuctb dmcs_uni.tbl

+makeuctb hp_uni.tbl

+makeuctb iso01_uni.tbl

+makeuctb iso02_uni.tbl

+makeuctb iso03_uni.tbl

+makeuctb iso04_uni.tbl

+makeuctb iso05_uni.tbl

+makeuctb iso06_uni.tbl

+makeuctb iso07_uni.tbl

+makeuctb iso08_uni.tbl

+makeuctb iso09_uni.tbl

+makeuctb iso10_uni.tbl

+makeuctb iso13_uni.tbl

+makeuctb iso14_uni.tbl

+makeuctb iso15_uni.tbl

+makeuctb koi8r_uni.tbl

+makeuctb koi8u_uni.tbl

+makeuctb mac_uni.tbl

+makeuctb mnem2_suni.tbl

+makeuctb mnem_suni.tbl

+makeuctb next_uni.tbl

+makeuctb pt154_uni.tbl

+makeuctb rfc_suni.tbl

+makeuctb utf8_uni.tbl

+makeuctb viscii_uni.tbl

diff --git a/src/chrtrans/makeuctb.c b/src/chrtrans/makeuctb.c
new file mode 100644
index 00000000..e3db8a51
--- /dev/null
+++ b/src/chrtrans/makeuctb.c
@@ -0,0 +1,893 @@
+/*
+ * $LynxId: makeuctb.c,v 1.43 2010/05/03 00:45:10 tom Exp $
+ *
+ *  makeuctb.c, derived from conmakehash.c   - kw
+ *
+ *    Original comments from conmakehash.c:
+ *
+ *  Create arrays for initializing the kernel folded tables (using a hash
+ *  table turned out to be to limiting...)  Unfortunately we can't simply
+ *  preinitialize the tables at compile time since kfree() cannot accept
+ *  memory not allocated by kmalloc(), and doing our own memory management
+ *  just for this seems like massive overkill.
+ *
+ *  Copyright (C) 1995 H. Peter Anvin
+ *
+ *  This program is a part of the Linux kernel, and may be freely
+ *  copied under the terms of the GNU General Public License (GPL),
+ *  version 2, or at your option any later version.
+ */
+
+#ifndef HAVE_CONFIG_H
+/* override HTUtils.h fallbacks for cross-compiling */
+#undef HAVE_LSTAT
+#undef NO_FILIO_H
+#define HAVE_LSTAT 1
+#define NO_FILIO_H 1
+#endif
+
+#define DONT_USE_GETTEXT
+#define DONT_USE_SOCKS5
+#include <UCDefs.h>
+#include <UCkd.h>
+#include <LYUtils.h>
+
+/*
+ *  Don't try to use LYexit() since this is a standalone file.
+ */
+#ifdef exit
+#undef exit
+#endif /* exit */
+
+#define MAX_FONTLEN 256
+
+/*
+ *  We don't deal with UCS4 here. - KW
+ */
+typedef u16 unicode;
+
+static FILE *chdr = 0;
+
+/*
+ * Since we may be writing the formatted file to stdout, ensure that we flush
+ * everything before leaving, since some old (and a few not-so-old) platforms
+ * do not properly implement POSIX 'exit()'.
+ */
+static void done(int code) GCC_NORETURN;
+
+static void done(int code)
+{
+    if (chdr != 0) {
+	fflush(chdr);
+	fclose(chdr);
+    }
+    fflush(stderr);
+    exit(code);
+}
+
+static void usage(void)
+{
+    static const char *tbl[] =
+    {
+	"Usage: makeuctb [parameters]",
+	"",
+	"Utility to convert .tbl into .h files for Lynx compilation.",
+	"",
+	"Parameters (all are optional):",
+	"  1: the input file (normally {filename}.tbl, but \"-\" for stdin",
+	"  2: the output file (normally {filename}.tbl but \"-\" for stdout",
+	"  3: charset mime name",
+	"  4: charset display name"
+    };
+    unsigned n;
+
+    for (n = 0; n < TABLESIZE(tbl); n++) {
+	fprintf(stderr, "%s\n", tbl[n]);
+    };
+    done(EX_USAGE);
+}
+
+#ifdef USE_ASCII_CTYPES
+int ascii_tolower(int i)
+{
+    if (91 > i && i > 64)
+	return (i + 32);
+    else
+	return i;
+}
+#endif
+
+/* copied from HTString.c, not everybody has strncasecmp */
+int strncasecomp(const char *a, const char *b, int n)
+{
+    const char *p;
+    const char *q;
+
+    for (p = a, q = b;; p++, q++) {
+	int diff;
+
+	if (p == (a + n))
+	    return 0;		/*   Match up to n characters */
+	if (!(*p && *q))
+	    return (*p - *q);
+	diff = TOLOWER(*p) - TOLOWER(*q);
+	if (diff)
+	    return diff;
+    }
+    /*NOTREACHED */
+}
+
+static int getunicode(char **p0)
+{
+    char *p = *p0;
+
+    while (*p == ' ' || *p == '\t')
+	p++;
+
+    if (*p == '-') {
+	return -2;
+    } else if (*p != 'U' || p[1] != '+' ||
+	       !isxdigit(UCH(p[2])) ||
+	       !isxdigit(UCH(p[3])) ||
+	       !isxdigit(UCH(p[4])) ||
+	       !isxdigit(UCH(p[5])) ||
+	       isxdigit(UCH(p[6]))) {
+	return -1;
+    }
+    *p0 = p + 6;
+    return strtol((p + 2), 0, 16);
+}
+
+/*
+ *  Massive overkill, but who cares?
+ */
+static unicode unitable[MAX_FONTLEN][255];
+static int unicount[MAX_FONTLEN];
+
+static struct unimapdesc_str themap_str =
+{0, NULL, 0, 0};
+
+static const char *tblname;
+static const char *hdrname;
+
+static int RawOrEnc = 0;
+static int Raw_found = 0;	/* whether explicit R directive found */
+static int CodePage = 0;
+
+#define MAX_UNIPAIRS 4500
+
+static void addpair_str(char *str, int un)
+{
+    int i = 0;
+
+    if (un <= 0xfffe) {
+	if (!themap_str.entry_ct) {
+	    /*
+	     *  Initialize the map for replacement strings.
+	     */
+	    themap_str.entries = (struct unipair_str *) malloc(MAX_UNIPAIRS
+							       * sizeof(struct unipair_str));
+
+	    if (!themap_str.entries) {
+		fprintf(stderr,
+			"%s: Out of memory\n", tblname);
+		done(EX_DATAERR);
+	    }
+	} else {
+	    /*
+	     *  Check that it isn't a duplicate.
+	     */
+	    for (i = 0; i < themap_str.entry_ct; i++) {
+		if (themap_str.entries[i].unicode == un) {
+		    themap_str.entries[i].replace_str = str;
+		    return;
+		}
+	    }
+	}
+
+	/*
+	 *  Add to list.
+	 */
+	if (themap_str.entry_ct > MAX_UNIPAIRS - 1) {
+	    fprintf(stderr,
+		    "ERROR: Only %d unicode replacement strings permitted!\n",
+		    MAX_UNIPAIRS);
+	    done(EX_DATAERR);
+	}
+	themap_str.entries[themap_str.entry_ct].unicode = un;
+	themap_str.entries[themap_str.entry_ct].replace_str = str;
+	themap_str.entry_ct++;
+    }
+    /* otherwise: ignore */
+}
+
+static void addpair(int fp, int un)
+{
+    int i;
+
+    if (!Raw_found) {		/* enc not (yet) explicitly given with 'R' */
+	if (fp >= 128) {
+	    if (RawOrEnc != UCT_ENC_8BIT && RawOrEnc <= UCT_ENC_8859) {
+		if (fp < 160) {	/* cannot be 8859 */
+		    RawOrEnc = UCT_ENC_8BIT;
+		} else if (fp != 160 && fp != 173) {
+		    RawOrEnc = UCT_ENC_8859;	/* hmmm.. more tests needed? */
+		} else if (unicount[fp] == 0 && fp != un) {
+		    /* first unicode for fp doesn't map to itself */
+		    RawOrEnc = UCT_ENC_8BIT;
+		} else {
+		    RawOrEnc = UCT_ENC_8859;	/* hmmm.. more tests needed? */
+		}
+	    }
+	}
+    }
+    if (un <= 0xfffe) {
+	/*
+	 *  Check that it isn't a duplicate.
+	 */
+	for (i = 0; i < unicount[fp]; i++) {
+	    if (unitable[fp][i] == un) {
+		return;
+	    }
+	}
+
+	/*
+	 *  Add to list.
+	 */
+	if (unicount[fp] > 254) {
+	    fprintf(stderr, "ERROR: Only 255 unicodes/glyph permitted!\n");
+	    done(EX_DATAERR);
+	}
+	unitable[fp][unicount[fp]] = un;
+	unicount[fp]++;
+    }
+    /* otherwise: ignore */
+}
+
+static char this_MIMEcharset[UC_MAXLEN_MIMECSNAME + 1];
+static char this_LYNXcharset[UC_MAXLEN_LYNXCSNAME + 1];
+static char id_append[UC_MAXLEN_ID_APPEND + 1] = "_";
+static int this_isDefaultMap = -1;
+static int useDefaultMap = 1;
+static int lowest_eight = 999;
+
+int main(int argc, char **argv)
+{
+    static const char *first_ifdefs[] =
+    {
+	"/*",
+	" * Compile-in this chunk of code unless we've turned it off specifically",
+	" * or in general (id=%s).",
+	" */",
+	"",
+	"#ifndef INCL_CHARSET%s",
+	"#define INCL_CHARSET%s 1",
+	"",
+	"/*ifdef NO_CHARSET*/",
+	"#ifdef  NO_CHARSET",
+	"#undef  NO_CHARSET",
+	"#endif",
+	"#define NO_CHARSET 0 /* force default to always be active */",
+	"",
+	"/*ifndef NO_CHARSET%s*/",
+	"#ifndef NO_CHARSET%s",
+	"",
+	"#if    ALL_CHARSETS",
+	"#define NO_CHARSET%s 0",
+	"#else",
+	"#define NO_CHARSET%s 1",
+	"#endif",
+	"",
+	"#endif /* ndef(NO_CHARSET%s) */",
+	"",
+	"#if NO_CHARSET%s",
+	"#define UC_CHARSET_SETUP%s /*nothing*/",
+	"#else"
+    };
+    static const char *last_ifdefs[] =
+    {
+	"",
+	"#endif /* NO_CHARSET%s */",
+	"",
+	"#endif /* INCL_CHARSET%s */"
+    };
+
+    FILE *ctbl;
+    char buffer[65536];
+    char *outname = 0;
+    unsigned n;
+    int fontlen;
+    int i, nuni, nent;
+    int fp0 = 0, fp1 = 0, un0, un1;
+    char *p, *p1;
+    char *tbuf, ch;
+
+    if (argc < 2 || argc > 5) {
+	usage();
+    }
+
+    if (!strcmp(argv[1], "-")) {
+	ctbl = stdin;
+	tblname = "stdin";
+    } else {
+	ctbl = fopen(tblname = argv[1], "r");
+	if (!ctbl) {
+	    perror(tblname);
+	    done(EX_NOINPUT);
+	}
+    }
+
+    if (argc > 2) {
+	if (!strcmp(argv[2], "-")) {
+	    chdr = stdout;
+	    hdrname = "stdout";
+	} else {
+	    hdrname = argv[2];
+	}
+    } else if (ctbl == stdin) {
+	chdr = stdout;
+	hdrname = "stdout";
+    } else if ((outname = (char *) malloc(strlen(tblname) + 3)) != 0) {
+	strcpy(outname, tblname);
+	hdrname = outname;
+	if ((p = strrchr(outname, '.')) == 0)
+	    p = outname + strlen(outname);
+	strcpy(p, ".h");
+    } else {
+	perror("malloc");
+	done(EX_NOINPUT);
+    }
+
+    if (chdr == 0) {
+	chdr = fopen(hdrname, "w");
+	if (!chdr) {
+	    perror(hdrname);
+	    done(EX_NOINPUT);
+	}
+    }
+
+    /*
+     *  For now we assume the default font is always 256 characters.
+     */
+    fontlen = 256;
+
+    /*
+     *  Initialize table.
+     */
+    for (i = 0; i < fontlen; i++) {
+	unicount[i] = 0;
+    }
+
+    /*
+     *  Now we comes to the tricky part.  Parse the input table.
+     */
+    while (fgets(buffer, sizeof(buffer), ctbl) != NULL) {
+	if ((p = strchr(buffer, '\n')) != NULL) {
+	    *p = '\0';
+	} else {
+	    fprintf(stderr,
+		    "%s: Warning: line too long or incomplete.\n",
+		    tblname);
+	}
+
+	/*
+	 *  Syntax accepted:
+	 *      <fontpos>       <unicode> <unicode> ...
+	 *      <fontpos>       <unicode range> <unicode range> ...
+	 *      <fontpos>       idem
+	 *      <range>         idem
+	 *      <range>         <unicode range>
+	 *      <unicode>       :<replace>
+	 *      <unicode range> :<replace>
+	 *      <unicode>       "<C replace>"
+	 *      <unicode range> "<C replace>"
+	 *
+	 *  where <range> ::= <fontpos>-<fontpos>
+	 *  and <unicode> ::= U+<h><h><h><h>
+	 *  and <h> ::= <hexadecimal digit>
+	 *  and <replace> any string not containing '\n' or '\0'
+	 *  and <C replace> any string with C backslash escapes.
+	 */
+	p = buffer;
+	while (*p == ' ' || *p == '\t') {
+	    p++;
+	}
+	if (!(*p) || *p == '#') {
+	    /*
+	     *  Skip comment or blank line.
+	     */
+	    continue;
+	}
+
+	switch (*p) {
+	    /*
+	     *  Raw Unicode?  I.e. needs some special
+	     *  processing.  One digit code.
+	     */
+	case 'R':
+	    if (p[1] == 'a' || p[1] == 'A') {
+		buffer[sizeof(buffer) - 1] = '\0';
+		if (!strncasecomp(p, "RawOrEnc", 8)) {
+		    p += 8;
+		}
+	    }
+	    p++;
+	    while (*p == ' ' || *p == '\t') {
+		p++;
+	    }
+	    RawOrEnc = strtol(p, 0, 10);
+	    Raw_found = 1;
+	    continue;
+
+	    /*
+	     *  Is this the default table?
+	     */
+	case 'D':
+	    if (p[1] == 'e' || p[1] == 'E') {
+		buffer[sizeof(buffer) - 1] = '\0';
+		if (!strncasecomp(p, "Default", 7)) {
+		    p += 7;
+		}
+	    }
+	    p++;
+	    while (*p == ' ' || *p == '\t') {
+		p++;
+	    }
+	    this_isDefaultMap = (*p == '1' || TOLOWER(*p) == 'y');
+	    continue;
+
+	    /*
+	     *  Is this the default table?
+	     */
+	case 'F':
+	    if (p[1] == 'a' || p[1] == 'A') {
+		buffer[sizeof(buffer) - 1] = '\0';
+		if (!strncasecomp(p, "FallBack", 8)) {
+		    p += 8;
+		}
+	    }
+	    p++;
+	    while (*p == ' ' || *p == '\t') {
+		p++;
+	    }
+	    useDefaultMap = (*p == '1' || TOLOWER(*p) == 'y');
+	    continue;
+
+	case 'M':
+	    if (p[1] == 'i' || p[1] == 'I') {
+		buffer[sizeof(buffer) - 1] = '\0';
+		if (!strncasecomp(p, "MIMEName", 8)) {
+		    p += 8;
+		}
+	    }
+	    p++;
+	    while (*p == ' ' || *p == '\t') {
+		p++;
+	    }
+	    sscanf(p, "%40s", this_MIMEcharset);
+	    continue;
+
+	    /*
+	     *  Display charset name for options screen.
+	     */
+	case 'O':
+	    if (p[1] == 'p' || p[1] == 'P') {
+		buffer[sizeof(buffer) - 1] = '\0';
+		if (!strncasecomp(p, "OptionName", 10)) {
+		    p += 10;
+		}
+	    }
+	    p++;
+	    while (*p == ' ' || *p == '\t') {
+		p++;
+	    }
+	    for (i = 0; *p && i < UC_MAXLEN_LYNXCSNAME; p++, i++) {
+		this_LYNXcharset[i] = *p;
+	    }
+	    this_LYNXcharset[i] = '\0';
+	    continue;
+
+	    /*
+	     *  Codepage number.  Three or four digit code.
+	     */
+	case 'C':
+	    if (p[1] == 'o' || p[1] == 'O') {
+		buffer[sizeof(buffer) - 1] = '\0';
+		if (!strncasecomp(p, "CodePage", 8)) {
+		    p += 8;
+		}
+	    }
+	    p++;
+	    while (*p == ' ' || *p == '\t') {
+		p++;
+	    }
+	    CodePage = strtol(p, 0, 10);
+	    continue;
+	}
+
+	if (*p == 'U') {
+	    un0 = getunicode(&p);
+	    if (un0 < 0) {
+		fprintf(stderr, "Bad input line: %s\n", buffer);
+		done(EX_DATAERR);
+		fprintf(stderr,
+			"%s: Bad Unicode range corresponding to font position range 0x%x-0x%x\n",
+			tblname, fp0, fp1);
+		done(EX_DATAERR);
+	    }
+	    un1 = un0;
+	    while (*p == ' ' || *p == '\t') {
+		p++;
+	    }
+	    if (*p == '-') {
+		p++;
+		while (*p == ' ' || *p == '\t') {
+		    p++;
+		}
+		un1 = getunicode(&p);
+		if (un1 < 0 || un1 < un0) {
+		    fprintf(stderr,
+			    "%s: Bad Unicode range U+%x-U+%x\n",
+			    tblname, un0, un1);
+		    fprintf(stderr, "Bad input line: %s\n", buffer);
+		    done(EX_DATAERR);
+		}
+		while (*p == ' ' || *p == '\t') {
+		    p++;
+		}
+	    }
+
+	    if (*p != ':' && *p != '"') {
+		fprintf(stderr, "No ':' or '\"' where expected: %s\n",
+			buffer);
+		continue;
+	    }
+
+	    /*
+	     * Allocate a string large enough for the worst-case use in the
+	     * loop using sprintf.
+	     */
+	    tbuf = (char *) malloc(5 * strlen(p));
+
+	    if (!(p1 = tbuf)) {
+		fprintf(stderr, "%s: Out of memory\n", tblname);
+		done(EX_DATAERR);
+	    }
+	    if (*p == '"') {
+		/*
+		 *  Handle "<C replace>".
+		 *  Copy chars verbatim until first '"' not \-escaped or
+		 *  end of buffer.
+		 */
+		int escaped = 0;
+
+		ch = 0;
+		for (++p; *p != '\0'; p++) {
+		    ch = *p;
+		    if (escaped) {
+			escaped = 0;
+		    } else if (ch == '"') {
+			break;
+		    } else if (ch == '\\') {
+			escaped = 1;
+		    }
+		    *p1++ = ch;
+		}
+		if (escaped || ch != '"') {
+		    fprintf(stderr, "Warning: String not terminated: %s\n",
+			    buffer);
+		    if (escaped)
+			*p1++ = '\n';
+		}
+	    } else {
+		/*
+		 *  We had ':'.
+		 */
+		for (++p; *p != '\0'; p++, p1++) {
+		    ch = *p;
+		    if (UCH(ch) < 32 || ch == '\\' || ch == '\"' ||
+			UCH(ch) >= 127) {
+			sprintf(p1, "\\%.3o", UCH(ch));
+			p1 += 3;
+		    } else {
+			*p1 = ch;
+		    }
+		}
+	    }
+	    *p1 = '\0';
+	    for (i = un0; i <= un1; i++) {
+		addpair_str(tbuf, i);
+	    }
+	    continue;
+	}
+
+	/*
+	 *  Input line (after skipping spaces) doesn't start with one
+	 *  of the specially recognized characters, so try to interpret
+	 *  it as starting with a fontpos.
+	 */
+	fp0 = strtol(p, &p1, 0);
+	if (p1 == p) {
+	    fprintf(stderr, "Bad input line: %s\n", buffer);
+	    done(EX_DATAERR);
+	}
+	p = p1;
+
+	while (*p == ' ' || *p == '\t') {
+	    p++;
+	}
+	if (*p == '-') {
+	    p++;
+	    fp1 = strtol(p, &p1, 0);
+	    if (p1 == p) {
+		fprintf(stderr, "Bad input line: %s\n", buffer);
+		done(EX_DATAERR);
+	    }
+	    p = p1;
+	} else {
+	    fp1 = 0;
+	}
+
+	if (fp0 < 0 || fp0 >= fontlen) {
+	    fprintf(stderr,
+		    "%s: Glyph number (0x%x) larger than font length\n",
+		    tblname, fp0);
+	    done(EX_DATAERR);
+	}
+	if (fp1 && (fp1 < fp0 || fp1 >= fontlen)) {
+	    fprintf(stderr,
+		    "%s: Bad end of range (0x%x)\n",
+		    tblname, fp1);
+	    done(EX_DATAERR);
+	}
+
+	if (fp1) {
+	    /*
+	     *  We have a range; expect the word "idem"
+	     *  or a Unicode range of the same length.
+	     */
+	    while (*p == ' ' || *p == '\t') {
+		p++;
+	    }
+	    if (!strncmp(p, "idem", 4)) {
+		for (i = fp0; i <= fp1; i++) {
+		    addpair(i, i);
+		}
+		p += 4;
+	    } else {
+		un0 = getunicode(&p);
+		while (*p == ' ' || *p == '\t') {
+		    p++;
+		}
+		if (*p != '-') {
+		    fprintf(stderr,
+			    "%s: Corresponding to a range of font positions,",
+			    tblname);
+		    fprintf(stderr,
+			    " there should be a Unicode range.\n");
+		    done(EX_DATAERR);
+		}
+		p++;
+		un1 = getunicode(&p);
+		if (un0 < 0 || un1 < 0) {
+		    fprintf(stderr,
+			    "%s: Bad Unicode range corresponding to font position range 0x%x-0x%x\n",
+			    tblname, fp0, fp1);
+		    done(EX_DATAERR);
+		}
+		if (un1 - un0 != fp1 - fp0) {
+		    fprintf(stderr,
+			    "%s: Unicode range U+%x-U+%x not of the same length",
+			    tblname, un0, un1);
+		    fprintf(stderr,
+			    " as font position range 0x%x-0x%x\n",
+			    fp0, fp1);
+		    done(EX_DATAERR);
+		}
+		for (i = fp0; i <= fp1; i++) {
+		    addpair(i, un0 - fp0 + i);
+		}
+	    }
+	} else {
+	    /*
+	     *  No range; expect a list of unicode values
+	     *  or unicode ranges for a single font position,
+	     *  or the word "idem"
+	     */
+	    while (*p == ' ' || *p == '\t') {
+		p++;
+	    }
+	    if (!strncmp(p, "idem", 4)) {
+		addpair(fp0, fp0);
+		p += 4;
+	    }
+	    while ((un0 = getunicode(&p)) >= 0) {
+		addpair(fp0, un0);
+		while (*p == ' ' || *p == '\t') {
+		    p++;
+		}
+		if (*p == '-') {
+		    p++;
+		    un1 = getunicode(&p);
+		    if (un1 < un0) {
+			fprintf(stderr,
+				"%s: Bad Unicode range 0x%x-0x%x\n",
+				tblname, un0, un1);
+			done(EX_DATAERR);
+		    }
+		    for (un0++; un0 <= un1; un0++) {
+			addpair(fp0, un0);
+		    }
+		}
+	    }
+	}
+	while (*p == ' ' || *p == '\t') {
+	    p++;
+	}
+	if (*p && *p != '#') {
+	    fprintf(stderr, "%s: trailing junk (%s) ignored\n", tblname, p);
+	}
+    }
+
+    /*
+     *  Okay, we hit EOF, now output tables.
+     */
+    fclose(ctbl);
+
+    /*
+     *  Compute total size of Unicode list.
+     */
+    nuni = 0;
+    for (i = 0; i < fontlen; i++) {
+	nuni += unicount[i];
+    }
+
+    if (argc > 3) {
+	strncpy(this_MIMEcharset, argv[3], UC_MAXLEN_MIMECSNAME);
+    } else if (this_MIMEcharset[0] == '\0') {
+	strncpy(this_MIMEcharset, tblname, UC_MAXLEN_MIMECSNAME);
+	if ((p = strchr(this_MIMEcharset, '.')) != 0) {
+	    *p = '\0';
+	}
+    }
+    for (p = this_MIMEcharset; *p; p++) {
+	*p = TOLOWER(*p);
+    }
+    if (argc > 4) {
+	strncpy(this_LYNXcharset, argv[4], UC_MAXLEN_LYNXCSNAME);
+    } else if (this_LYNXcharset[0] == '\0') {
+	strncpy(this_LYNXcharset, this_MIMEcharset, UC_MAXLEN_LYNXCSNAME);
+    }
+
+    if (this_isDefaultMap == -1) {
+	this_isDefaultMap = !strncmp(this_MIMEcharset, "iso-8859-1", 10);
+    }
+    fprintf(stderr,
+	    "makeuctb: %s: %stranslation map",
+	    this_MIMEcharset, (this_isDefaultMap ? "default " : ""));
+    if (this_isDefaultMap == 1) {
+	*id_append = '\0';
+    } else {
+	for (i = 0, p = this_MIMEcharset;
+	     *p && (i < UC_MAXLEN_ID_APPEND - 1);
+	     p++, i++) {
+	    id_append[i + 1] = isalnum(UCH(*p)) ? *p : '_';
+	}
+	id_append[i + 1] = '\0';
+    }
+    fprintf(stderr, " (%s).\n", id_append);
+
+    for (n = 0; n < TABLESIZE(first_ifdefs); n++) {
+	fprintf(chdr, first_ifdefs[n], id_append);
+	fprintf(chdr, "\n");
+    }
+
+    fprintf(chdr, "\n\
+/*\n\
+ *  uni_hash.tbl\n\
+ *\n\
+ *  Do not edit this file; it was automatically generated by\n\
+ *\n\
+ *  %s %s\n\
+ *\n\
+ */\n\
+\n\
+static const u8 dfont_unicount%s[%d] = \n\
+{\n\t", argv[0], argv[1], id_append, fontlen);
+
+    for (i = 0; i < fontlen; i++) {
+	if (i >= 128 && unicount[i] > 0 && i < lowest_eight) {
+	    lowest_eight = i;
+	}
+	fprintf(chdr, "%3d", unicount[i]);
+	if (i == (fontlen - 1)) {
+	    fprintf(chdr, "\n};\n");
+	} else if ((i % 8) == 7) {
+	    fprintf(chdr, ",\n\t");
+	} else {
+	    fprintf(chdr, ", ");
+	}
+    }
+
+    /*
+     *  If lowest_eightbit is anything else but 999,
+     *  this can't be 7-bit only.
+     */
+    if (lowest_eight != 999 && !RawOrEnc) {
+	RawOrEnc = UCT_ENC_8BIT;
+    }
+
+    if (nuni) {
+	fprintf(chdr, "\nstatic const u16 dfont_unitable%s[%d] = \n{\n\t",
+		id_append, nuni);
+    } else {
+	fprintf(chdr,
+		"\nstatic const u16 dfont_unitable%s[1] = {0}; /* dummy */\n", id_append);
+    }
+
+    fp0 = 0;
+    nent = 0;
+    for (i = 0; i < nuni; i++) {
+	while (nent >= unicount[fp0]) {
+	    fp0++;
+	    nent = 0;
+	}
+	fprintf(chdr, "0x%04x", unitable[fp0][nent++]);
+	if (i == (nuni - 1)) {
+	    fprintf(chdr, "\n};\n");
+	} else if ((i % 8) == 7) {
+	    fprintf(chdr, ",\n\t");
+	} else {
+	    fprintf(chdr, ", ");
+	}
+    }
+
+    if (themap_str.entry_ct) {
+	fprintf(chdr, "\n\
+static struct unipair_str repl_map%s[%d] = \n\
+{\n\t", id_append, themap_str.entry_ct);
+    } else {
+	fprintf(chdr, "\n\
+/* static struct unipair_str repl_map%s[]; */\n", id_append);
+    }
+
+    for (i = 0; i < themap_str.entry_ct; i++) {
+	fprintf(chdr, "{0x%x,\"%s\"}",
+		themap_str.entries[i].unicode,
+		themap_str.entries[i].replace_str);
+	if (i == (themap_str.entry_ct - 1)) {
+	    fprintf(chdr, "\n};\n");
+	} else if ((i % 4) == 3) {
+	    fprintf(chdr, ",\n\t");
+	} else {
+	    fprintf(chdr, ", ");
+	}
+    }
+    if (themap_str.entry_ct) {
+	fprintf(chdr, "\n\
+static const struct unimapdesc_str dfont_replacedesc%s = {%d,repl_map%s,",
+		id_append, themap_str.entry_ct, id_append);
+    } else {
+	fprintf(chdr, "\n\
+static const struct unimapdesc_str dfont_replacedesc%s = {0,NULL,", id_append);
+    }
+    fprintf(chdr, "%d,%d};\n",
+	    this_isDefaultMap ? 1 : 0,
+	    (useDefaultMap && !this_isDefaultMap) ? 1 : 0
+	);
+
+    fprintf(chdr, "#define UC_CHARSET_SETUP%s UC_Charset_Setup(\
+\"%s\",\\\n\"%s\",\\\n\
+dfont_unicount%s,dfont_unitable%s,%d,\\\n\
+dfont_replacedesc%s,%d,%d,%d)\n",
+	    id_append, this_MIMEcharset, this_LYNXcharset,
+	    id_append, id_append, nuni, id_append, lowest_eight, RawOrEnc, CodePage);
+
+    for (n = 0; n < TABLESIZE(last_ifdefs); n++) {
+	fprintf(chdr, last_ifdefs[n], id_append);
+	fprintf(chdr, "\n");
+    }
+
+    done(EX_OK);
+    return 0;
+}
diff --git a/src/chrtrans/makew32.bat b/src/chrtrans/makew32.bat
new file mode 100644
index 00000000..da476010
--- /dev/null
+++ b/src/chrtrans/makew32.bat
@@ -0,0 +1,13 @@
+@rem $LynxId: makew32.bat,v 1.6 2007/06/28 21:07:24 tom Exp $

+@echo off

+

+if "%1"=="" goto normal

+make -l -f makefile.bcb %1

+goto done

+

+:normal

+make -l -f makefile.bcb

+

+call makehdrs

+

+:done

diff --git a/src/chrtrans/mnem2_suni.tbl b/src/chrtrans/mnem2_suni.tbl
new file mode 100644
index 00000000..3fc122cb
--- /dev/null
+++ b/src/chrtrans/mnem2_suni.tbl
@@ -0,0 +1,1865 @@
+#The MIME name of this charset.
+# (this file was renamed from mnemonic_suni.tbl)
+Mmnemonic
+
+#Name as a Display Charset (used on Options screen)
+O RFC 1345 Mnemonic
+
+# Don't fall back to default table for unicode -> 8bit
+Fallback NO
+
+# U+0020:&SP
+U+0021:!
+U+0022:"
+U+0023:&Nb
+U+0024:&DO
+U+0025:%
+U+0026:&&
+U+0027:'
+U+0028:(
+U+0029:)
+U+002a:*
+U+002b:+
+U+002c:,
+U+002d:-
+U+002e:.
+U+002f:/
+U+0030:0
+U+0031:1
+U+0032:2
+U+0033:3
+U+0034:4
+U+0035:5
+U+0036:6
+U+0037:7
+U+0038:8
+U+0039:9
+U+003a::
+U+003b:;
+U+003c:<
+U+003d:=
+U+003e:>
+U+003f:?
+U+0040:&At
+U+0041:A
+U+0042:B
+U+0043:C
+U+0044:D
+U+0045:E
+U+0046:F
+U+0047:G
+U+0048:H
+U+0049:I
+U+004a:J
+U+004b:K
+U+004c:L
+U+004d:M
+U+004e:N
+U+004f:O
+U+0050:P
+U+0051:Q
+U+0052:R
+U+0053:S
+U+0054:T
+U+0055:U
+U+0056:V
+U+0057:W
+U+0058:X
+U+0059:Y
+U+005a:Z
+U+005b:&<(
+U+005c:&//
+U+005d:&)>
+U+005e:&'>
+U+005f:_
+U+0060:&'!
+U+0061:a
+U+0062:b
+U+0063:c
+U+0064:d
+U+0065:e
+U+0066:f
+U+0067:g
+U+0068:h
+U+0069:i
+U+006a:j
+U+006b:k
+U+006c:l
+U+006d:m
+U+006e:n
+U+006f:o
+U+0070:p
+U+0071:q
+U+0072:r
+U+0073:s
+U+0074:t
+U+0075:u
+U+0076:v
+U+0077:w
+U+0078:x
+U+0079:y
+U+007a:z
+U+007b:&(!
+U+007c:&!!
+U+007d:&!)
+U+007e:&'?
+U+00a0:&NS
+U+00a1:&!I
+U+00a2:&Ct
+U+00a3:&Pd
+U+00a4:&Cu
+U+00a5:&Ye
+U+00a6:&BB
+U+00a7:&SE
+U+00a8:&':
+U+00a9:&Co
+U+00aa:&-a
+U+00ab:&<<
+U+00ac:&NO
+U+00ad:&--
+U+00ae:&Rg
+U+00af:&'m
+U+00b0:&DG
+U+00b1:&+-
+U+00b2:&2S
+U+00b3:&3S
+U+00b4:&''
+U+00b5:&My
+U+00b6:&PI
+U+00b7:&.M
+U+00b8:&',
+U+00b9:&1S
+U+00ba:&-o
+U+00bb:&>>
+U+00bc:&14
+U+00bd:&12
+U+00be:&34
+U+00bf:&?I
+U+00c0:&A!
+U+00c1:&A'
+U+00c2:&A>
+U+00c3:&A?
+U+00c4:&A:
+U+00c5:&AA
+U+00c6:&AE
+U+00c7:&C,
+U+00c8:&E!
+U+00c9:&E'
+U+00ca:&E>
+U+00cb:&E:
+U+00cc:&I!
+U+00cd:&I'
+U+00ce:&I>
+U+00cf:&I:
+U+00d0:&D-
+U+00d1:&N?
+U+00d2:&O!
+U+00d3:&O'
+U+00d4:&O>
+U+00d5:&O?
+U+00d6:&O:
+U+00d7:&*X
+U+00d8:&O/
+U+00d9:&U!
+U+00da:&U'
+U+00db:&U>
+U+00dc:&U:
+U+00dd:&Y'
+U+00de:&TH
+U+00df:&ss
+U+00e0:&a!
+U+00e1:&a'
+U+00e2:&a>
+U+00e3:&a?
+U+00e4:&a:
+U+00e5:&aa
+U+00e6:&ae
+U+00e7:&c,
+U+00e8:&e!
+U+00e9:&e'
+U+00ea:&e>
+U+00eb:&e:
+U+00ec:&i!
+U+00ed:&i'
+U+00ee:&i>
+U+00ef:&i:
+U+00f0:&d-
+U+00f1:&n?
+U+00f2:&o!
+U+00f3:&o'
+U+00f4:&o>
+U+00f5:&o?
+U+00f6:&o:
+U+00f7:&-:
+U+00f8:&o/
+U+00f9:&u!
+U+00fa:&u'
+U+00fb:&u>
+U+00fc:&u:
+U+00fd:&y'
+U+00fe:&th
+U+00ff:&y:
+U+0100:&A-
+U+0101:&a-
+U+0102:&A(
+U+0103:&a(
+U+0104:&A;
+U+0105:&a;
+U+0106:&C'
+U+0107:&c'
+U+0108:&C>
+U+0109:&c>
+U+010a:&C.
+U+010b:&c.
+U+010c:&C<
+U+010d:&c<
+U+010e:&D<
+U+010f:&d<
+U+0110:&D/
+U+0111:&d/
+U+0112:&E-
+U+0113:&e-
+U+0114:&E(
+U+0115:&e(
+U+0116:&E.
+U+0117:&e.
+U+0118:&E;
+U+0119:&e;
+U+011a:&E<
+U+011b:&e<
+U+011c:&G>
+U+011d:&g>
+U+011e:&G(
+U+011f:&g(
+U+0120:&G.
+U+0121:&g.
+U+0122:&G,
+U+0123:&g,
+U+0124:&H>
+U+0125:&h>
+U+0126:&H/
+U+0127:&h/
+U+0128:&I?
+U+0129:&i?
+U+012a:&I-
+U+012b:&i-
+U+012c:&I(
+U+012d:&i(
+U+012e:&I;
+U+012f:&i;
+U+0130:&I.
+U+0131:&i.
+U+0132:&IJ
+U+0133:&ij
+U+0134:&J>
+U+0135:&j>
+U+0136:&K,
+U+0137:&k,
+U+0138:&kk
+U+0139:&L'
+U+013a:&l'
+U+013b:&L,
+U+013c:&l,
+U+013d:&L<
+U+013e:&l<
+U+013f:&L.
+U+0140:&l.
+U+0141:&L/
+U+0142:&l/
+U+0143:&N'
+U+0144:&n'
+U+0145:&N,
+U+0146:&n,
+U+0147:&N<
+U+0148:&n<
+U+0149:&'n
+U+014a:&NG
+U+014b:&ng
+U+014c:&O-
+U+014d:&o-
+U+014e:&O(
+U+014f:&o(
+U+0150:&O"
+U+0151:&o"
+U+0152:&OE
+U+0153:&oe
+U+0154:&R'
+U+0155:&r'
+U+0156:&R,
+U+0157:&r,
+U+0158:&R<
+U+0159:&r<
+U+015a:&S'
+U+015b:&s'
+U+015c:&S>
+U+015d:&s>
+U+015e:&S,
+U+015f:&s,
+U+0160:&S<
+U+0161:&s<
+U+0162:&T,
+U+0163:&t,
+U+0164:&T<
+U+0165:&t<
+U+0166:&T/
+U+0167:&t/
+U+0168:&U?
+U+0169:&u?
+U+016a:&U-
+U+016b:&u-
+U+016c:&U(
+U+016d:&u(
+U+016e:&U0
+U+016f:&u0
+U+0170:&U"
+U+0171:&u"
+U+0172:&U;
+U+0173:&u;
+U+0174:&W>
+U+0175:&w>
+U+0176:&Y>
+U+0177:&y>
+U+0178:&Y:
+U+0179:&Z'
+U+017a:&z'
+U+017b:&Z.
+U+017c:&z.
+U+017d:&Z<
+U+017e:&z<
+U+01a0:&O9
+U+01a1:&o9
+U+01a2:&OI
+U+01a3:&oi
+U+01a6:&yr
+U+01af:&U9
+U+01b0:&u9
+U+01b5:&Z/
+U+01b6:&z/
+U+01b7:&ED
+U+01cd:&A<
+U+01ce:&a<
+U+01cf:&I<
+U+01d0:&i<
+U+01d1:&O<
+U+01d2:&o<
+U+01d3:&U<
+U+01d4:&u<
+U+01d5:&_U:-_
+U+01d6:&_u:-_
+U+01d7:&_U:'_
+U+01d8:&_u:'_
+U+01d9:&_U:<_
+U+01da:&_u:<_
+U+01db:&_U:!_
+U+01dc:&_u:!_
+U+01de:&A1
+U+01df:&a1
+U+01e0:&A7
+U+01e1:&a7
+U+01e2:&A3
+U+01e3:&a3
+U+01e4:&G/
+U+01e5:&g/
+U+01e6:&G<
+U+01e7:&g<
+U+01e8:&K<
+U+01e9:&k<
+U+01ea:&O;
+U+01eb:&o;
+U+01ec:&O1
+U+01ed:&o1
+U+01ee:&EZ
+U+01ef:&ez
+U+01f0:&j<
+U+01f4:&G'
+U+01f5:&g'
+U+01fa:&_AA'_
+U+01fb:&_aa'_
+U+01fc:&_AE'_
+U+01fd:&_ae'_
+U+01fe:&_O/'_
+U+01ff:&_o/'_
+U+02bf:&;S
+U+02c7:&'<
+U+02d8:&'(
+U+02d9:&'.
+U+02da:&'0
+U+02db:&';
+U+02dd:&'"
+U+0386:&A%
+U+0388:&E%
+U+0389:&Y%
+U+038a:&I%
+U+038c:&O%
+U+038e:&U%
+U+038f:&W%
+U+0390:&i3
+U+0391:&A*
+U+0392:&B*
+U+0393:&G*
+U+0394:&D*
+U+0395:&E*
+U+0396:&Z*
+U+0397:&Y*
+U+0398:&H*
+U+0399:&I*
+U+039a:&K*
+U+039b:&L*
+U+039c:&M*
+U+039d:&N*
+U+039e:&C*
+U+039f:&O*
+U+03a0:&P*
+U+03a1:&R*
+U+03a3:&S*
+U+03a4:&T*
+U+03a5:&U*
+U+03a6:&F*
+U+03a7:&X*
+U+03a8:&Q*
+U+03a9:&W*
+U+03aa:&J*
+U+03ab:&V*
+U+03ac:&a%
+U+03ad:&e%
+U+03ae:&y%
+U+03af:&i%
+U+03b0:&u3
+U+03b1:&a*
+U+03b2:&b*
+U+03b3:&g*
+U+03b4:&d*
+U+03b5:&e*
+U+03b6:&z*
+U+03b7:&y*
+U+03b8:&h*
+U+03b9:&i*
+U+03ba:&k*
+U+03bb:&l*
+U+03bc:&m*
+U+03bd:&n*
+U+03be:&c*
+U+03bf:&o*
+U+03c0:&p*
+U+03c1:&r*
+U+03c2:&*s
+U+03c3:&s*
+U+03c4:&t*
+U+03c5:&u*
+U+03c6:&f*
+U+03c7:&x*
+U+03c8:&q*
+U+03c9:&w*
+U+03ca:&j*
+U+03cb:&v*
+U+03cc:&o%
+U+03cd:&u%
+U+03ce:&w%
+U+03d8:&'G
+U+03d9:&,G
+U+03da:&T3
+U+03db:&t3
+U+03dc:&M3
+U+03dd:&m3
+U+03de:&K3
+U+03df:&k3
+U+03e0:&P3
+U+03e1:&p3
+U+03f4:&'%
+U+03f5:&j3
+U+0401:&IO
+U+0402:&D%
+U+0403:&G%
+U+0404:&IE
+U+0405:&DS
+U+0406:&II
+U+0407:&YI
+U+0408:&J%
+U+0409:&LJ
+U+040a:&NJ
+U+040b:&Ts
+U+040c:&KJ
+U+040e:&V%
+U+040f:&DZ
+U+0410:&A=
+U+0411:&B=
+U+0412:&V=
+U+0413:&G=
+U+0414:&D=
+U+0415:&E=
+U+0416:&Z%
+U+0417:&Z=
+U+0418:&I=
+U+0419:&J=
+U+041a:&K=
+U+041b:&L=
+U+041c:&M=
+U+041d:&N=
+U+041e:&O=
+U+041f:&P=
+U+0420:&R=
+U+0421:&S=
+U+0422:&T=
+U+0423:&U=
+U+0424:&F=
+U+0425:&H=
+U+0426:&C=
+U+0427:&C%
+U+0428:&S%
+U+0429:&Sc
+U+042a:&="
+U+042b:&Y=
+U+042c:&%"
+U+042d:&JE
+U+042e:&JU
+U+042f:&JA
+U+0430:&a=
+U+0431:&b=
+U+0432:&v=
+U+0433:&g=
+U+0434:&d=
+U+0435:&e=
+U+0436:&z%
+U+0437:&z=
+U+0438:&i=
+U+0439:&j=
+U+043a:&k=
+U+043b:&l=
+U+043c:&m=
+U+043d:&n=
+U+043e:&o=
+U+043f:&p=
+U+0440:&r=
+U+0441:&s=
+U+0442:&t=
+U+0443:&u=
+U+0444:&f=
+U+0445:&h=
+U+0446:&c=
+U+0447:&c%
+U+0448:&s%
+U+0449:&sc
+U+044a:&='
+U+044b:&y=
+U+044c:&%'
+U+044d:&je
+U+044e:&ju
+U+044f:&ja
+U+0451:&io
+U+0452:&d%
+U+0453:&g%
+U+0454:&ie
+U+0455:&ds
+U+0456:&ii
+U+0457:&yi
+U+0458:&j%
+U+0459:&lj
+U+045a:&nj
+U+045b:&ts
+U+045c:&kj
+U+045e:&v%
+U+045f:&dz
+U+0462:&Y3
+U+0463:&y3
+U+046a:&O3
+U+046b:&o3
+U+0472:&F3
+U+0473:&f3
+U+0474:&V3
+U+0475:&v3
+U+0480:&C3
+U+0481:&c3
+U+0490:&G3
+U+0491:&g3
+U+05d0:&A+
+U+05d1:&B+
+U+05d2:&G+
+U+05d3:&D+
+U+05d4:&H+
+U+05d5:&W+
+U+05d6:&Z+
+U+05d7:&X+
+U+05d8:&Tj
+U+05d9:&J+
+U+05da:&K%
+U+05db:&K+
+U+05dc:&L+
+U+05dd:&M%
+U+05de:&M+
+U+05df:&N%
+U+05e0:&N+
+U+05e1:&S+
+U+05e2:&E+
+U+05e3:&P%
+U+05e4:&P+
+U+05e5:&Zj
+U+05e6:&ZJ
+U+05e7:&Q+
+U+05e8:&R+
+U+05e9:&Sh
+U+05ea:&T+
+U+060c:&,+
+U+061b:&;+
+U+061f:&?+
+U+0621:&H'
+U+0622:&aM
+U+0623:&aH
+U+0624:&wH
+U+0625:&ah
+U+0626:&yH
+U+0627:&a+
+U+0628:&b+
+U+0629:&tm
+U+062a:&t+
+U+062b:&tk
+U+062c:&g+
+U+062d:&hk
+U+062e:&x+
+U+062f:&d+
+U+0630:&dk
+U+0631:&r+
+U+0632:&z+
+U+0633:&s+
+U+0634:&sn
+U+0635:&c+
+U+0636:&dd
+U+0637:&tj
+U+0638:&zH
+U+0639:&e+
+U+063a:&i+
+U+0640:&++
+U+0641:&f+
+U+0642:&q+
+U+0643:&k+
+U+0644:&l+
+U+0645:&m+
+U+0646:&n+
+U+0647:&h+
+U+0648:&w+
+U+0649:&j+
+U+064a:&y+
+U+064b:&:+
+U+064c:&"+
+U+064d:&=+
+U+064e:&/+
+U+064f:&'+
+U+0650:&1+
+U+0651:&3+
+U+0652:&0+
+U+0670:&aS
+U+067e:&p+
+U+06a4:&v+
+U+06af:&gf
+U+06f0:&0a
+U+06f1:&1a
+U+06f2:&2a
+U+06f3:&3a
+U+06f4:&4a
+U+06f5:&5a
+U+06f6:&6a
+U+06f7:&7a
+U+06f8:&8a
+U+06f9:&9a
+U+1e00:&_A-0_
+U+1e01:&_a-0_
+U+1e02:&B.
+U+1e03:&b.
+U+1e04:&_B-._
+U+1e05:&_b-._
+U+1e06:&B_
+U+1e07:&b_
+U+1e08:&_C,'_
+U+1e09:&_c,'_
+U+1e0a:&D.
+U+1e0b:&d.
+U+1e0c:&_D-._
+U+1e0d:&_d-._
+U+1e0e:&D_
+U+1e0f:&d_
+U+1e10:&D,
+U+1e11:&d,
+U+1e12:&_D->_
+U+1e13:&_d->_
+U+1e14:&_E-!_
+U+1e15:&_e-!_
+U+1e16:&_E-'_
+U+1e17:&_e-'_
+U+1e18:&_E->_
+U+1e19:&_e->_
+U+1e1a:&_E-?_
+U+1e1b:&_e-?_
+U+1e1c:&_E,(_
+U+1e1d:&_e,(_
+U+1e1e:&F.
+U+1e1f:&f.
+U+1e20:&G-
+U+1e21:&g-
+U+1e22:&H.
+U+1e23:&h.
+U+1e24:&_H-._
+U+1e25:&_h-._
+U+1e26:&H:
+U+1e27:&h:
+U+1e28:&H,
+U+1e29:&h,
+U+1e2a:&_H-(_
+U+1e2b:&_h-(_
+U+1e2c:&_I-?_
+U+1e2d:&_i-?_
+U+1e2e:&_I:'_
+U+1e2f:&_i:'_
+U+1e30:&K'
+U+1e31:&k'
+U+1e32:&_K-._
+U+1e33:&_k-._
+U+1e34:&K_
+U+1e35:&k_
+U+1e36:&_L-._
+U+1e37:&_l-._
+U+1e38:&_L--._
+U+1e39:&_l--._
+U+1e3a:&L_
+U+1e3b:&l_
+U+1e3c:&_L->_
+U+1e3d:&_l->_
+U+1e3e:&M'
+U+1e3f:&m'
+U+1e40:&M.
+U+1e41:&m.
+U+1e42:&_M-._
+U+1e43:&_m-._
+U+1e44:&N.
+U+1e45:&n.
+U+1e46:&_N-._
+U+1e47:&_n-._
+U+1e48:&N_
+U+1e49:&n_
+U+1e4a:&_N->_
+U+1e4b:&_N->_
+U+1e4c:&_O?'_
+U+1e4d:&_o?'_
+U+1e4e:&_O?:_
+U+1e4f:&_o?:_
+U+1e50:&_O-!_
+U+1e51:&_o-!_
+U+1e52:&_O-'_
+U+1e53:&_o-'_
+U+1e54:&P'
+U+1e55:&p'
+U+1e56:&P.
+U+1e57:&p.
+U+1e58:&R.
+U+1e59:&r.
+U+1e5a:&_R-._
+U+1e5b:&_r-._
+U+1e5c:&_R--._
+U+1e5d:&_r--._
+U+1e5e:&R_
+U+1e5f:&r_
+U+1e60:&S.
+U+1e61:&s.
+U+1e62:&_S-._
+U+1e63:&_s-._
+U+1e64:&_S'._
+U+1e65:&_s'._
+U+1e66:&_S<._
+U+1e67:&_s<._
+U+1e68:&_S.-._
+U+1e69:&_S.-._
+U+1e6a:&T.
+U+1e6b:&t.
+U+1e6c:&_T-._
+U+1e6d:&_t-._
+U+1e6e:&T_
+U+1e6f:&t_
+U+1e70:&_T->_
+U+1e71:&_t->_
+U+1e72:&_U--:_
+U+1e73:&_u--:_
+U+1e74:&_U-?_
+U+1e75:&_u-?_
+U+1e76:&_U->_
+U+1e77:&_u->_
+U+1e78:&_U?'_
+U+1e79:&_u?'_
+U+1e7a:&_U-:_
+U+1e7b:&_u-:_
+U+1e7c:&V?
+U+1e7d:&v?
+U+1e7e:&_V-._
+U+1e7f:&_v-._
+U+1e80:&W!
+U+1e81:&w!
+U+1e82:&W'
+U+1e83:&w'
+U+1e84:&W:
+U+1e85:&w:
+U+1e86:&W.
+U+1e87:&w.
+U+1e88:&_W-._
+U+1e89:&_w-._
+U+1e8a:&X.
+U+1e8b:&x.
+U+1e8c:&X:
+U+1e8d:&x:
+U+1e8e:&Y.
+U+1e8f:&y.
+U+1e90:&Z>
+U+1e91:&z>
+U+1e92:&_Z-._
+U+1e93:&_z-._
+U+1e94:&Z_
+U+1e95:&z_
+U+1e96:&h_
+U+1e97:&t:
+U+1e98:&w0
+U+1e99:&y0
+U+1ea0:&_A-._
+U+1ea1:&_a-._
+U+1ea2:&A2
+U+1ea3:&a2
+U+1ea4:&_A>'_
+U+1ea5:&_a>'_
+U+1ea6:&_A>!_
+U+1ea7:&_a>!_
+U+1ea8:&_A>2_
+U+1ea9:&_a>2_
+U+1eaa:&_A>?_
+U+1eab:&_a>?_
+U+1eac:&_A>-._
+U+1ead:&_a>-._
+U+1eae:&_A('_
+U+1eaf:&_a('_
+U+1eb0:&_A(!_
+U+1eb1:&_a(!_
+U+1eb2:&_A(2_
+U+1eb3:&_a(2_
+U+1eb4:&_A(?_
+U+1eb5:&_a(?_
+U+1eb6:&_A(-._
+U+1eb7:&_a(-._
+U+1eb8:&_E-._
+U+1eb9:&_e-._
+U+1eba:&E2
+U+1ebb:&e2
+U+1ebc:&E?
+U+1ebd:&e?
+U+1ebe:&_E>'_
+U+1ebf:&_e>'_
+U+1ec0:&_E>!_
+U+1ec1:&_e>!_
+U+1ec2:&_E>2_
+U+1ec3:&_e>2_
+U+1ec4:&_E>?_
+U+1ec5:&_e>?_
+U+1ec6:&_E>-._
+U+1ec7:&_e>-._
+U+1ec8:&I2
+U+1ec9:&i2
+U+1eca:&_I-._
+U+1ecb:&_i-._
+U+1ecc:&_O-._
+U+1ecd:&_o-._
+U+1ece:&O2
+U+1ecf:&o2
+U+1ed0:&_O>'_
+U+1ed1:&_o>'_
+U+1ed2:&_O>!_
+U+1ed3:&_o>!_
+U+1ed4:&_O>2_
+U+1ed5:&_o>2_
+U+1ed6:&_O>?_
+U+1ed7:&_o>?_
+U+1ed8:&_O>-._
+U+1ed9:&_o>-._
+U+1eda:&_O9'_
+U+1edb:&_o9'_
+U+1edc:&_O9!_
+U+1edd:&_o9!_
+U+1ede:&_O92_
+U+1edf:&_o92_
+U+1ee0:&_O9?_
+U+1ee1:&_o9?_
+U+1ee2:&_O9-._
+U+1ee3:&_o9-._
+U+1ee4:&_U-._
+U+1ee5:&_u-._
+U+1ee6:&U2
+U+1ee7:&u2
+U+1ee8:&_U9'_
+U+1ee9:&_u9'_
+U+1eea:&_U9!_
+U+1eeb:&_u9!_
+U+1eec:&_U92_
+U+1eed:&_u92_
+U+1eee:&_U9?_
+U+1eef:&_u9?_
+U+1ef0:&_U9-._
+U+1ef1:&_u9-._
+U+1ef2:&Y!
+U+1ef3:&y!
+U+1ef4:&_Y-._
+U+1ef5:&_y-._
+U+1ef6:&Y2
+U+1ef7:&y2
+U+1ef8:&Y?
+U+1ef9:&y?
+U+1f00:&;'
+U+1f01:&,'
+U+1f02:&;!
+U+1f03:&,!
+U+1f04:&?;
+U+1f05:&?,
+U+1f06:&!:
+U+1f07:&?:
+U+2002:&1N
+U+2003:&1M
+U+2004:&3M
+U+2005:&4M
+U+2006:&6M
+U+2009:&1T
+U+200a:&1H
+U+2010:&-1
+U+2013:&-N
+U+2014:&-M
+U+2015:&-3
+U+2016:&!2
+U+2017:&=2
+U+2018:&'6
+U+2019:&'9
+U+201a:&.9
+U+201b:&9'
+U+201c:&"6
+U+201d:&"9
+U+201e:&:9
+U+201f:&9"
+U+2020:&/-
+U+2021:&/=
+U+2025:&..
+U+2030:&%0
+U+2032:&1'
+U+2033:&2'
+U+2034:&3'
+U+2035:&1"
+U+2036:&2"
+U+2037:&3"
+U+2038:&Ca
+U+2039:&<1
+U+203a:&>1
+U+203b:&:X
+U+203c:&_!*2_
+U+203e:&'-
+U+2044:&/f
+U+2070:&0S
+U+2074:&4S
+U+2075:&5S
+U+2076:&6S
+U+2077:&7S
+U+2078:&8S
+U+2079:&9S
+U+207a:&+S
+U+207b:&-S
+U+207c:&=S
+U+207d:&(S
+U+207e:&)S
+U+207f:&nS
+U+2080:&0s
+U+2081:&1s
+U+2082:&2s
+U+2083:&3s
+U+2084:&4s
+U+2085:&5s
+U+2086:&6s
+U+2087:&7s
+U+2088:&8s
+U+2089:&9s
+U+208a:&+s
+U+208b:&-s
+U+208c:&=s
+U+208d:&(s
+U+208e:&)s
+U+20a4:&Li
+U+20a7:&Pt
+U+20a9:&W=
+U+2103:&oC
+U+2105:&co
+U+2109:&oF
+U+2116:&N0
+U+2117:&PO
+U+211e:&Rx
+U+2120:&SM
+U+2122:&TM
+U+2126:&Om
+U+212b:&AO
+U+2153:&13
+U+2154:&23
+U+2155:&15
+U+2156:&25
+U+2157:&35
+U+2158:&45
+U+2159:&16
+U+215a:&56
+U+215b:&18
+U+215c:&38
+U+215d:&58
+U+215e:&78
+U+2160:&1R
+U+2161:&2R
+U+2162:&3R
+U+2163:&4R
+U+2164:&5R
+U+2165:&6R
+U+2166:&7R
+U+2167:&8R
+U+2168:&9R
+U+2169:&aR
+U+216a:&bR
+U+216b:&cR
+U+216c:&_50R_
+U+216d:&_100R_
+U+216e:&_500R_
+U+216f:&_1000R_
+U+2170:&1r
+U+2171:&2r
+U+2172:&3r
+U+2173:&4r
+U+2174:&5r
+U+2175:&6r
+U+2176:&7r
+U+2177:&8r
+U+2178:&9r
+U+2179:&ar
+U+217a:&br
+U+217b:&cr
+U+217c:&_50r_
+U+217d:&_100r_
+U+217e:&_500r_
+U+217f:&_1000r_
+U+2180:&_1000RCD_
+U+2181:&_5000R_
+U+2182:&_10000R_
+U+2190:&<-
+U+2191:&-!
+U+2192:&->
+U+2193:&-v
+U+2194:&<>
+U+2195:&UD
+U+2196:&_<!!_
+U+2197:&_//>_
+U+2198:&_!!>_
+U+2199:&_<//_
+U+21d0:&<=
+U+21d2:&=>
+U+21d4:&==
+U+2200:&FA
+U+2202:&dP
+U+2203:&TE
+U+2205:&/0
+U+2206:&DE
+U+2207:&NB
+U+2208:&(-
+U+220b:&-)
+U+220f:&*P
+U+2211:&+Z
+U+2212:&-2
+U+2213:&-+
+U+2217:&*-
+U+2218:&Ob
+U+2219:&Sb
+U+221a:&RT
+U+221d:&0(
+U+221e:&00
+U+221f:&-L
+U+2220:&-V
+U+2225:&PP
+U+2227:&AN
+U+2228:&OR
+U+2229:&(U
+U+222a:&)U
+U+222b:&In
+U+222c:&DI
+U+222e:&Io
+U+2234:&.:
+U+2235:&:.
+U+2236:&:R
+U+2237:&::
+U+223c:&?1
+U+223e:&CG
+U+2243:&?-
+U+2245:&?=
+U+2248:&?2
+U+224c:&=?
+U+2253:&HI
+U+2260:&!=
+U+2261:&=3
+U+2264:&=<
+U+2265:&>=
+U+226a:&<*
+U+226b:&*>
+U+226e:&!<
+U+226f:&!>
+U+2282:&(C
+U+2283:&)C
+U+2286:&(_
+U+2287:&)_
+U+2299:&0.
+U+229a:&02
+U+22a5:&-T
+U+22c5:&.P
+U+22ee:&:3
+U+22ef:&.3
+U+2302:&Eh
+U+2308:&<7
+U+2309:&>7
+U+230a:&7<
+U+230b:&7>
+U+2310:&NI
+U+2312:&(A
+U+2315:&TR
+U+2320:&Iu
+U+2321:&Il
+U+2329:&</
+U+232a:&/>
+U+2423:&Vs
+U+2440:&1h
+U+2441:&3h
+U+2442:&2h
+U+2443:&4h
+U+2446:&1j
+U+2447:&2j
+U+2448:&3j
+U+2449:&4j
+U+2460:&_1-o_
+U+2461:&_2-o_
+U+2462:&_3-o_
+U+2463:&_4-o_
+U+2464:&_5-o_
+U+2465:&_6-o_
+U+2466:&_7-o_
+U+2467:&_8-o_
+U+2468:&_9-o_
+U+2469:&_10-o_
+U+246a:&_11-o_
+U+246b:&_12-o_
+U+246c:&_13-o_
+U+246d:&_14-o_
+U+246e:&_15-o_
+U+246f:&_16-o_
+U+2470:&_17-o_
+U+2471:&_18-o_
+U+2472:&_19-o_
+U+2473:&_20-o_
+U+2474:&_(1)_
+U+2475:&_(2)_
+U+2476:&_(3)_
+U+2477:&_(4)_
+U+2478:&_(5)_
+U+2479:&_(6)_
+U+247a:&_(7)_
+U+247b:&_(8)_
+U+247c:&_(9)_
+U+247d:&_(10)_
+U+247e:&_(11)_
+U+247f:&_(12)_
+U+2480:&_(13)_
+U+2481:&_(14)_
+U+2482:&_(15)_
+U+2483:&_(16)_
+U+2484:&_(17)_
+U+2485:&_(18)_
+U+2486:&_(19)_
+U+2487:&_(20)_
+U+2488:&1.
+U+2489:&2.
+U+248a:&3.
+U+248b:&4.
+U+248c:&5.
+U+248d:&6.
+U+248e:&7.
+U+248f:&8.
+U+2490:&9.
+U+2491:&_10._
+U+2492:&_11._
+U+2493:&_12._
+U+2494:&_13._
+U+2495:&_14._
+U+2496:&_15._
+U+2497:&_16._
+U+2498:&_17._
+U+2499:&_18._
+U+249a:&_19._
+U+249b:&_20._
+U+249c:&_(a)_
+U+249d:&_(b)_
+U+249e:&_(c)_
+U+249f:&_(d)_
+U+24a0:&_(e)_
+U+24a1:&_(f)_
+U+24a2:&_(g)_
+U+24a3:&_(h)_
+U+24a4:&_(i)_
+U+24a5:&_(j)_
+U+24a6:&_(k)_
+U+24a7:&_(l)_
+U+24a8:&_(m)_
+U+24a9:&_(n)_
+U+24aa:&_(o)_
+U+24ab:&_(p)_
+U+24ac:&_(q)_
+U+24ad:&_(r)_
+U+24ae:&_(s)_
+U+24af:&_(t)_
+U+24b0:&_(u)_
+U+24b1:&_(v)_
+U+24b2:&_(w)_
+U+24b3:&_(x)_
+U+24b4:&_(y)_
+U+24b5:&_(z)_
+U+24b6:&_A-o_
+U+24b7:&_B-o_
+U+24b8:&_C-o_
+U+24b9:&_D-o_
+U+24ba:&_E-o_
+U+24bb:&_F-o_
+U+24bc:&_G-o_
+U+24bd:&_H-o_
+U+24be:&_I-o_
+U+24bf:&_J-o_
+U+24c0:&_K-o_
+U+24c1:&_L-o_
+U+24c2:&_M-o_
+U+24c3:&_N-o_
+U+24c4:&_O-o_
+U+24c5:&_P-o_
+U+24c6:&_Q-o_
+U+24c7:&_R-o_
+U+24c8:&_S-o_
+U+24c9:&_T-o_
+U+24ca:&_U-o_
+U+24cb:&_V-o_
+U+24cc:&_W-o_
+U+24cd:&_X-o_
+U+24ce:&_Y-o_
+U+24cf:&_Z-o_
+U+24d0:&_a-o_
+U+24d1:&_b-o_
+U+24d2:&_c-o_
+U+24d3:&_d-o_
+U+24d4:&_e-o_
+U+24d5:&_f-o_
+U+24d6:&_g-o_
+U+24d7:&_h-o_
+U+24d8:&_i-o_
+U+24d9:&_j-o_
+U+24da:&_k-o_
+U+24db:&_l-o_
+U+24dc:&_m-o_
+U+24dd:&_n-o_
+U+24de:&_o-o_
+U+24df:&_p-o_
+U+24e0:&_q-o_
+U+24e1:&_r-o_
+U+24e2:&_s-o_
+U+24e3:&_t-o_
+U+24e4:&_u-o_
+U+24e5:&_v-o_
+U+24e6:&_w-o_
+U+24e7:&_x-o_
+U+24e8:&_y-o_
+U+24e9:&_z-o_
+U+24ea:&_0-o_
+U+2500:&hh
+U+2501:&HH
+U+2502:&vv
+U+2503:&VV
+U+2504:&3-
+U+2505:&3_
+U+2506:&3!
+U+2507:&3/
+U+2508:&4-
+U+2509:&4_
+U+250a:&4!
+U+250b:&4/
+U+250c:&dr
+U+250d:&dR
+U+250e:&Dr
+U+250f:&DR
+U+2510:&dl
+U+2511:&dL
+U+2512:&Dl
+U+2513:&LD
+U+2514:&ur
+U+2515:&uR
+U+2516:&Ur
+U+2517:&UR
+U+2518:&ul
+U+2519:&uL
+U+251a:&Ul
+U+251b:&UL
+U+251c:&vr
+U+251d:&vR
+U+251e:&_Udr_
+U+251f:&_uDr_
+U+2520:&Vr
+U+2521:&_UdR_
+U+2522:&_uDR_
+U+2523:&VR
+U+2524:&vl
+U+2525:&vL
+U+2526:&_Udl_
+U+2527:&_uDl_
+U+2528:&Vl
+U+2529:&_UdL_
+U+252a:&_uDL_
+U+252b:&VL
+U+252c:&dh
+U+252d:&_dLr_
+U+252e:&_dlR_
+U+252f:&dH
+U+2530:&Dh
+U+2531:&_DLr_
+U+2532:&_DlR_
+U+2533:&DH
+U+2534:&uh
+U+2535:&_uLr_
+U+2536:&_ulR_
+U+2537:&uH
+U+2538:&Uh
+U+2539:&_ULr_
+U+253a:&_UlR_
+U+253b:&UH
+U+253c:&vh
+U+253d:&_vLr_
+U+253e:&_vlR_
+U+253f:&vH
+U+2540:&_Udh_
+U+2541:&_uDh_
+U+2542:&Vh
+U+2543:&_UdLr_
+U+2544:&_UdlR_
+U+2545:&_uDLr_
+U+2546:&_uDlR_
+U+2547:&_UdH_
+U+2548:&_uDH_
+U+2549:&_VLr_
+U+254a:&_VlR_
+U+254b:&VH
+U+2571:&FD
+U+2572:&BD
+U+2580:&TB
+U+2584:&LB
+U+2588:&FB
+U+258c:&lB
+U+2590:&RB
+U+2591:&.S
+U+2592:&:S
+U+2593:&?S
+U+25a0:&fS
+U+25a1:&OS
+U+25a2:&RO
+U+25a3:&Rr
+U+25a4:&RF
+U+25a5:&RY
+U+25a6:&RH
+U+25a7:&RZ
+U+25a8:&RK
+U+25a9:&RX
+U+25aa:&sB
+U+25ac:&SR
+U+25ad:&Or
+U+25b2:&UT
+U+25b3:&uT
+U+25b6:&PR
+U+25b7:&Tr
+U+25bc:&Dt
+U+25bd:&dT
+U+25c0:&PL
+U+25c1:&Tl
+U+25c6:&Db
+U+25c7:&Dw
+U+25ca:&LZ
+U+25cb:&0m
+U+25ce:&0o
+U+25cf:&0M
+U+25d0:&0L
+U+25d1:&0R
+U+25d8:&Sn
+U+25d9:&Ic
+U+25e2:&Fd
+U+25e3:&Bd
+U+2605:&*2
+U+2606:&*1
+U+260e:&_TEL_
+U+260f:&_tel_
+U+261c:&<H
+U+261e:&>H
+U+263a:&0u
+U+263b:&0U
+U+263c:&SU
+U+2640:&Fm
+U+2642:&Ml
+U+2660:&cS
+U+2661:&cH
+U+2662:&cD
+U+2663:&cC
+U+2664:&_cS-_
+U+2665:&_cH-_
+U+2666:&_cD-_
+U+2667:&_cC-_
+U+2669:&Md
+U+266a:&M8
+U+266b:&M2
+U+266c:&_M16_
+U+266d:&Mb
+U+266e:&Mx
+U+266f:&MX
+U+2713:&OK
+U+2717:&XX
+U+2720:&-X
+U+3000:&IS
+U+3001:&,_
+U+3002:&._
+U+3003:&+"
+U+3004:&+_
+U+3005:&*_
+U+3006:&;_
+U+3007:&0_
+U+300a:&<+
+U+300b:&>+
+U+300c:&<'
+U+300d:&>'
+U+300e:&<"
+U+300f:&>"
+U+3010:&("
+U+3011:&)"
+U+3012:&=T
+U+3013:&=_
+U+3014:&('
+U+3015:&)'
+U+3016:&(I
+U+3017:&)I
+U+301c:&-?
+U+3020:&_=T:)_
+U+3041:&A5
+U+3042:&a5
+U+3043:&I5
+U+3044:&i5
+U+3045:&U5
+U+3046:&u5
+U+3047:&E5
+U+3048:&e5
+U+3049:&O5
+U+304a:&o5
+U+304b:&ka
+U+304c:&ga
+U+304d:&ki
+U+304e:&gi
+U+304f:&ku
+U+3050:&gu
+U+3051:&ke
+U+3052:&ge
+U+3053:&ko
+U+3054:&go
+U+3055:&sa
+U+3056:&za
+U+3057:&si
+U+3058:&zi
+U+3059:&su
+U+305a:&zu
+U+305b:&se
+U+305c:&ze
+U+305d:&so
+U+305e:&zo
+U+305f:&ta
+U+3060:&da
+U+3061:&ti
+U+3062:&di
+U+3063:&tU
+U+3064:&tu
+U+3065:&du
+U+3066:&te
+U+3067:&de
+U+3068:&to
+U+3069:&do
+U+306a:&na
+U+306b:&ni
+U+306c:&nu
+U+306d:&ne
+U+306e:&no
+U+306f:&ha
+U+3070:&ba
+U+3071:&pa
+U+3072:&hi
+U+3073:&bi
+U+3074:&pi
+U+3075:&hu
+U+3076:&bu
+U+3077:&pu
+U+3078:&he
+U+3079:&be
+U+307a:&pe
+U+307b:&ho
+U+307c:&bo
+U+307d:&po
+U+307e:&ma
+U+307f:&mi
+U+3080:&mu
+U+3081:&me
+U+3082:&mo
+U+3083:&yA
+U+3084:&ya
+U+3085:&yU
+U+3086:&yu
+U+3087:&yO
+U+3088:&yo
+U+3089:&ra
+U+308a:&ri
+U+308b:&ru
+U+308c:&re
+U+308d:&ro
+U+308e:&wA
+U+308f:&wa
+U+3090:&wi
+U+3091:&we
+U+3092:&wo
+U+3093:&n5
+U+3094:&vu
+U+309b:&"5
+U+309c:&05
+U+309d:&*5
+U+309e:&+5
+U+30a1:&a6
+U+30a2:&A6
+U+30a3:&i6
+U+30a4:&I6
+U+30a5:&u6
+U+30a6:&U6
+U+30a7:&e6
+U+30a8:&E6
+U+30a9:&o6
+U+30aa:&O6
+U+30ab:&Ka
+U+30ac:&Ga
+U+30ad:&Ki
+U+30ae:&Gi
+U+30af:&Ku
+U+30b0:&Gu
+U+30b1:&Ke
+U+30b2:&Ge
+U+30b3:&Ko
+U+30b4:&Go
+U+30b5:&Sa
+U+30b6:&Za
+U+30b7:&Si
+U+30b8:&Zi
+U+30b9:&Su
+U+30ba:&Zu
+U+30bb:&Se
+U+30bc:&Ze
+U+30bd:&So
+U+30be:&Zo
+U+30bf:&Ta
+U+30c0:&Da
+U+30c1:&Ti
+U+30c2:&Di
+U+30c3:&TU
+U+30c4:&Tu
+U+30c5:&Du
+U+30c6:&Te
+U+30c7:&De
+U+30c8:&To
+U+30c9:&Do
+U+30ca:&Na
+U+30cb:&Ni
+U+30cc:&Nu
+U+30cd:&Ne
+U+30ce:&No
+U+30cf:&Ha
+U+30d0:&Ba
+U+30d1:&Pa
+U+30d2:&Hi
+U+30d3:&Bi
+U+30d4:&Pi
+U+30d5:&Hu
+U+30d6:&Bu
+U+30d7:&Pu
+U+30d8:&He
+U+30d9:&Be
+U+30da:&Pe
+U+30db:&Ho
+U+30dc:&Bo
+U+30dd:&Po
+U+30de:&Ma
+U+30df:&Mi
+U+30e0:&Mu
+U+30e1:&Me
+U+30e2:&Mo
+U+30e3:&YA
+U+30e4:&Ya
+U+30e5:&YU
+U+30e6:&Yu
+U+30e7:&YO
+U+30e8:&Yo
+U+30e9:&Ra
+U+30ea:&Ri
+U+30eb:&Ru
+U+30ec:&Re
+U+30ed:&Ro
+U+30ee:&WA
+U+30ef:&Wa
+U+30f0:&Wi
+U+30f1:&We
+U+30f2:&Wo
+U+30f3:&N6
+U+30f4:&Vu
+U+30f5:&KA
+U+30f6:&KE
+U+30f7:&Va
+U+30f8:&Vi
+U+30f9:&Ve
+U+30fa:&Vo
+U+30fb:&.6
+U+30fc:&-6
+U+30fd:&*6
+U+30fe:&+6
+U+3105:&b4
+U+3106:&p4
+U+3107:&m4
+U+3108:&f4
+U+3109:&d4
+U+310a:&t4
+U+310b:&n4
+U+310c:&l4
+U+310d:&g4
+U+310e:&k4
+U+310f:&h4
+U+3110:&j4
+U+3111:&q4
+U+3112:&x4
+U+3113:&zh
+U+3114:&ch
+U+3115:&sh
+U+3116:&r4
+U+3117:&z4
+U+3118:&c4
+U+3119:&s4
+U+311a:&a4
+U+311b:&o4
+U+311c:&e4
+U+311d:&_eh4_
+U+311e:&ai
+U+311f:&ei
+U+3120:&au
+U+3121:&ou
+U+3122:&an
+U+3123:&en
+U+3124:&aN
+U+3125:&eN
+U+3126:&er
+U+3127:&i4
+U+3128:&u4
+U+3129:&iu
+U+312a:&v4
+U+312b:&nG
+U+312c:&gn
+U+321c:&_(JU)_
+U+3220:&1c
+U+3221:&2c
+U+3222:&3c
+U+3223:&4c
+U+3224:&5c
+U+3225:&6c
+U+3226:&7c
+U+3227:&8c
+U+3228:&9c
+U+3229:&_10c_
+U+327f:&_KSC_
+U+fb00:&ff
+U+fb01:&fi
+U+fb02:&fl
+U+fb03:&_ffi_
+U+fb04:&_ffl_
+U+fb05:&ft
+U+fb06:&st
+U+fe7d:&_3+;_
+U+fe82:&_aM._
+U+fe84:&_aH._
+U+fe8d:&_a+-_
+U+fe8e:&_a+._
+U+fe8f:&_b+-_
+U+fe90:&_b+,_
+U+fe91:&_b+;_
+U+fe92:&_b+._
+U+fe93:&_tm-_
+U+fe94:&_tm._
+U+fe95:&_t+-_
+U+fe96:&_t+,_
+U+fe97:&_t+;_
+U+fe98:&_t+._
+U+fe99:&_tk-_
+U+fe9a:&_tk,_
+U+fe9b:&_tk;_
+U+fe9c:&_tk._
+U+fe9d:&_g+-_
+U+fe9e:&_g+,_
+U+fe9f:&_g+;_
+U+fea0:&_g+._
+U+fea1:&_hk-_
+U+fea2:&_hk,_
+U+fea3:&_hk;_
+U+fea4:&_hk._
+U+fea5:&_x+-_
+U+fea6:&_x+,_
+U+fea7:&_x+;_
+U+fea8:&_x+._
+U+fea9:&_d+-_
+U+feaa:&_d+._
+U+feab:&_dk-_
+U+feac:&_dk._
+U+fead:&_r+-_
+U+feae:&_r+._
+U+feaf:&_z+-_
+U+feb0:&_z+._
+U+feb1:&_s+-_
+U+feb2:&_s+,_
+U+feb3:&_s+;_
+U+feb4:&_s+._
+U+feb5:&_sn-_
+U+feb6:&_sn,_
+U+feb7:&_sn;_
+U+feb8:&_sn._
+U+feb9:&_c+-_
+U+feba:&_c+,_
+U+febb:&_c+;_
+U+febc:&_c+._
+U+febd:&_dd-_
+U+febe:&_dd,_
+U+febf:&_dd;_
+U+fec0:&_dd._
+U+fec1:&_tj-_
+U+fec2:&_tj,_
+U+fec3:&_tj;_
+U+fec4:&_tj._
+U+fec5:&_zH-_
+U+fec6:&_zH,_
+U+fec7:&_zH;_
+U+fec8:&_zH._
+U+fec9:&_e+-_
+U+feca:&_e+,_
+U+fecb:&_e+;_
+U+fecc:&_e+._
+U+fecd:&_i+-_
+U+fece:&_i+,_
+U+fecf:&_i+;_
+U+fed0:&_i+._
+U+fed1:&_f+-_
+U+fed2:&_f+,_
+U+fed3:&_f+;_
+U+fed4:&_f+._
+U+fed5:&_q+-_
+U+fed6:&_q+,_
+U+fed7:&_q+;_
+U+fed8:&_q+._
+U+fed9:&_k+-_
+U+feda:&_k+,_
+U+fedb:&_k+;_
+U+fedc:&_k+._
+U+fedd:&_l+-_
+U+fede:&_l+,_
+U+fedf:&_l+;_
+U+fee0:&_l+._
+U+fee1:&_m+-_
+U+fee2:&_m+,_
+U+fee3:&_m+;_
+U+fee4:&_m+._
+U+fee5:&_n+-_
+U+fee6:&_n+,_
+U+fee7:&_n+;_
+U+fee8:&_n+._
+U+fee9:&_h+-_
+U+feea:&_h+,_
+U+feeb:&_h+;_
+U+feec:&_h+._
+U+feed:&_w+-_
+U+feee:&_w+._
+U+feef:&_j+-_
+U+fef0:&_j+._
+U+fef1:&_y+-_
+U+fef2:&_y+,_
+U+fef3:&_y+;_
+U+fef4:&_y+._
+U+fef5:&_lM-_
+U+fef6:&_lM._
+U+fef7:&_lH-_
+U+fef8:&_lH._
+U+fef9:&_lh-_
+U+fefa:&_lh._
+U+fefb:&_la-_
+U+fefc:&_la._
+U+0000:&NU
+U+0001:&SH
+U+0002:&SX
+U+0003:&EX
+U+0004:&ET
+U+0005:&EQ
+U+0006:&AK
+U+0007:&BL
+U+0008:&BS
+U+0009:&HT
+# U+000a:&LF
+U+000b:&VT
+U+000c:&FF
+U+000d:&CR
+U+000e:&SO
+U+000f:&SI
+U+0010:&DL
+U+0011:&D1
+U+0012:&D2
+U+0013:&D3
+U+0014:&D4
+U+0015:&NK
+U+0016:&SY
+U+0017:&EB
+U+0018:&CN
+U+0019:&EM
+U+001a:&SB
+U+001b:&EC
+U+001c:&FS
+U+001d:&GS
+U+001e:&RS
+U+001f:&US
+U+007f:&DT
+U+0080:&PA
+U+0081:&HO
+U+0082:&BH
+U+0083:&NH
+U+0084:&IN
+U+0085:&NL
+U+0086:&SA
+U+0087:&ES
+U+0088:&HS
+U+0089:&HJ
+U+008a:&VS
+U+008b:&PD
+U+008c:&PU
+U+008d:&RI
+U+008e:&S2
+U+008f:&S3
+U+0090:&DC
+U+0091:&P1
+U+0092:&P2
+U+0093:&TS
+U+0094:&CC
+U+0095:&MW
+U+0096:&SG
+U+0097:&EG
+U+0098:&SS
+U+0099:&GC
+U+009a:&SC
+U+009b:&CI
+U+009c:&ST
+U+009d:&OC
+U+009e:&PM
+U+009f:&AC
+# Characters in Private Use Area (e000-f8ff) do not have ussigned numbers
+# according Unicode 2.0
diff --git a/src/chrtrans/mnem_suni.tbl b/src/chrtrans/mnem_suni.tbl
new file mode 100644
index 00000000..02bd8ea8
--- /dev/null
+++ b/src/chrtrans/mnem_suni.tbl
@@ -0,0 +1,1861 @@
+#The MIME name of this charset.
+Mmnem
+
+#Name as a Display Charset (used on Options screen)
+ORFC1345 Mnem
+
+# U+0020: SP
+U+0021:!
+U+0022:"
+U+0023: Nb
+U+0024: DO
+U+0025:%
+U+0026:&&
+U+0027:'
+U+0028:(
+U+0029:)
+U+002a:*
+U+002b:+
+U+002c:,
+U+002d:-
+U+002e:.
+U+002f:/
+U+0030:0
+U+0031:1
+U+0032:2
+U+0033:3
+U+0034:4
+U+0035:5
+U+0036:6
+U+0037:7
+U+0038:8
+U+0039:9
+U+003a::
+U+003b:;
+U+003c:<
+U+003d:=
+U+003e:>
+U+003f:?
+U+0040: At
+U+0041:A
+U+0042:B
+U+0043:C
+U+0044:D
+U+0045:E
+U+0046:F
+U+0047:G
+U+0048:H
+U+0049:I
+U+004a:J
+U+004b:K
+U+004c:L
+U+004d:M
+U+004e:N
+U+004f:O
+U+0050:P
+U+0051:Q
+U+0052:R
+U+0053:S
+U+0054:T
+U+0055:U
+U+0056:V
+U+0057:W
+U+0058:X
+U+0059:Y
+U+005a:Z
+U+005b: <(
+U+005c: //
+U+005d: )>
+U+005e: '>
+U+005f:_
+U+0060: '!
+U+0061:a
+U+0062:b
+U+0063:c
+U+0064:d
+U+0065:e
+U+0066:f
+U+0067:g
+U+0068:h
+U+0069:i
+U+006a:j
+U+006b:k
+U+006c:l
+U+006d:m
+U+006e:n
+U+006f:o
+U+0070:p
+U+0071:q
+U+0072:r
+U+0073:s
+U+0074:t
+U+0075:u
+U+0076:v
+U+0077:w
+U+0078:x
+U+0079:y
+U+007a:z
+U+007b: (!
+U+007c: !!
+U+007d: !)
+U+007e: '?
+U+00a0: NS
+U+00a1: !I
+U+00a2: Ct
+U+00a3: Pd
+U+00a4: Cu
+U+00a5: Ye
+U+00a6: BB
+U+00a7: SE
+U+00a8: ':
+U+00a9: Co
+U+00aa: -a
+U+00ab: <<
+U+00ac: NO
+U+00ad: --
+U+00ae: Rg
+U+00af: 'm
+U+00b0: DG
+U+00b1: +-
+U+00b2: 2S
+U+00b3: 3S
+U+00b4: ''
+U+00b5: My
+U+00b6: PI
+U+00b7: .M
+U+00b8: ',
+U+00b9: 1S
+U+00ba: -o
+U+00bb: >>
+U+00bc: 14
+U+00bd: 12
+U+00be: 34
+U+00bf: ?I
+U+00c0: A!
+U+00c1: A'
+U+00c2: A>
+U+00c3: A?
+U+00c4: A:
+U+00c5: AA
+U+00c6: AE
+U+00c7: C,
+U+00c8: E!
+U+00c9: E'
+U+00ca: E>
+U+00cb: E:
+U+00cc: I!
+U+00cd: I'
+U+00ce: I>
+U+00cf: I:
+U+00d0: D-
+U+00d1: N?
+U+00d2: O!
+U+00d3: O'
+U+00d4: O>
+U+00d5: O?
+U+00d6: O:
+U+00d7: *X
+U+00d8: O/
+U+00d9: U!
+U+00da: U'
+U+00db: U>
+U+00dc: U:
+U+00dd: Y'
+U+00de: TH
+U+00df: ss
+U+00e0: a!
+U+00e1: a'
+U+00e2: a>
+U+00e3: a?
+U+00e4: a:
+U+00e5: aa
+U+00e6: ae
+U+00e7: c,
+U+00e8: e!
+U+00e9: e'
+U+00ea: e>
+U+00eb: e:
+U+00ec: i!
+U+00ed: i'
+U+00ee: i>
+U+00ef: i:
+U+00f0: d-
+U+00f1: n?
+U+00f2: o!
+U+00f3: o'
+U+00f4: o>
+U+00f5: o?
+U+00f6: o:
+U+00f7: -:
+U+00f8: o/
+U+00f9: u!
+U+00fa: u'
+U+00fb: u>
+U+00fc: u:
+U+00fd: y'
+U+00fe: th
+U+00ff: y:
+U+0100: A-
+U+0101: a-
+U+0102: A(
+U+0103: a(
+U+0104: A;
+U+0105: a;
+U+0106: C'
+U+0107: c'
+U+0108: C>
+U+0109: c>
+U+010a: C.
+U+010b: c.
+U+010c: C<
+U+010d: c<
+U+010e: D<
+U+010f: d<
+U+0110: D/
+U+0111: d/
+U+0112: E-
+U+0113: e-
+U+0114: E(
+U+0115: e(
+U+0116: E.
+U+0117: e.
+U+0118: E;
+U+0119: e;
+U+011a: E<
+U+011b: e<
+U+011c: G>
+U+011d: g>
+U+011e: G(
+U+011f: g(
+U+0120: G.
+U+0121: g.
+U+0122: G,
+U+0123: g,
+U+0124: H>
+U+0125: h>
+U+0126: H/
+U+0127: h/
+U+0128: I?
+U+0129: i?
+U+012a: I-
+U+012b: i-
+U+012c: I(
+U+012d: i(
+U+012e: I;
+U+012f: i;
+U+0130: I.
+U+0131: i.
+U+0132: IJ
+U+0133: ij
+U+0134: J>
+U+0135: j>
+U+0136: K,
+U+0137: k,
+U+0138: kk
+U+0139: L'
+U+013a: l'
+U+013b: L,
+U+013c: l,
+U+013d: L<
+U+013e: l<
+U+013f: L.
+U+0140: l.
+U+0141: L/
+U+0142: l/
+U+0143: N'
+U+0144: n'
+U+0145: N,
+U+0146: n,
+U+0147: N<
+U+0148: n<
+U+0149: 'n
+U+014a: NG
+U+014b: ng
+U+014c: O-
+U+014d: o-
+U+014e: O(
+U+014f: o(
+U+0150: O"
+U+0151: o"
+U+0152: OE
+U+0153: oe
+U+0154: R'
+U+0155: r'
+U+0156: R,
+U+0157: r,
+U+0158: R<
+U+0159: r<
+U+015a: S'
+U+015b: s'
+U+015c: S>
+U+015d: s>
+U+015e: S,
+U+015f: s,
+U+0160: S<
+U+0161: s<
+U+0162: T,
+U+0163: t,
+U+0164: T<
+U+0165: t<
+U+0166: T/
+U+0167: t/
+U+0168: U?
+U+0169: u?
+U+016a: U-
+U+016b: u-
+U+016c: U(
+U+016d: u(
+U+016e: U0
+U+016f: u0
+U+0170: U"
+U+0171: u"
+U+0172: U;
+U+0173: u;
+U+0174: W>
+U+0175: w>
+U+0176: Y>
+U+0177: y>
+U+0178: Y:
+U+0179: Z'
+U+017a: z'
+U+017b: Z.
+U+017c: z.
+U+017d: Z<
+U+017e: z<
+U+01a0: O9
+U+01a1: o9
+U+01a2: OI
+U+01a3: oi
+U+01a6: yr
+U+01af: U9
+U+01b0: u9
+U+01b5: Z/
+U+01b6: z/
+U+01b7: ED
+U+01cd: A<
+U+01ce: a<
+U+01cf: I<
+U+01d0: i<
+U+01d1: O<
+U+01d2: o<
+U+01d3: U<
+U+01d4: u<
+U+01d5: _U:-_
+U+01d6: _u:-_
+U+01d7: _U:'_
+U+01d8: _u:'_
+U+01d9: _U:<_
+U+01da: _u:<_
+U+01db: _U:!_
+U+01dc: _u:!_
+U+01de: A1
+U+01df: a1
+U+01e0: A7
+U+01e1: a7
+U+01e2: A3
+U+01e3: a3
+U+01e4: G/
+U+01e5: g/
+U+01e6: G<
+U+01e7: g<
+U+01e8: K<
+U+01e9: k<
+U+01ea: O;
+U+01eb: o;
+U+01ec: O1
+U+01ed: o1
+U+01ee: EZ
+U+01ef: ez
+U+01f0: j<
+U+01f4: G'
+U+01f5: g'
+U+01fa: _AA'_
+U+01fb: _aa'_
+U+01fc: _AE'_
+U+01fd: _ae'_
+U+01fe: _O/'_
+U+01ff: _o/'_
+U+02bf: ;S
+U+02c7: '<
+U+02d8: '(
+U+02d9: '.
+U+02da: '0
+U+02db: ';
+U+02dd: '"
+U+0386: A%
+U+0388: E%
+U+0389: Y%
+U+038a: I%
+U+038c: O%
+U+038e: U%
+U+038f: W%
+U+0390: i3
+U+0391: A*
+U+0392: B*
+U+0393: G*
+U+0394: D*
+U+0395: E*
+U+0396: Z*
+U+0397: Y*
+U+0398: H*
+U+0399: I*
+U+039a: K*
+U+039b: L*
+U+039c: M*
+U+039d: N*
+U+039e: C*
+U+039f: O*
+U+03a0: P*
+U+03a1: R*
+U+03a3: S*
+U+03a4: T*
+U+03a5: U*
+U+03a6: F*
+U+03a7: X*
+U+03a8: Q*
+U+03a9: W*
+U+03aa: J*
+U+03ab: V*
+U+03ac: a%
+U+03ad: e%
+U+03ae: y%
+U+03af: i%
+U+03b0: u3
+U+03b1: a*
+U+03b2: b*
+U+03b3: g*
+U+03b4: d*
+U+03b5: e*
+U+03b6: z*
+U+03b7: y*
+U+03b8: h*
+U+03b9: i*
+U+03ba: k*
+U+03bb: l*
+U+03bc: m*
+U+03bd: n*
+U+03be: c*
+U+03bf: o*
+U+03c0: p*
+U+03c1: r*
+U+03c2: *s
+U+03c3: s*
+U+03c4: t*
+U+03c5: u*
+U+03c6: f*
+U+03c7: x*
+U+03c8: q*
+U+03c9: w*
+U+03ca: j*
+U+03cb: v*
+U+03cc: o%
+U+03cd: u%
+U+03ce: w%
+U+03d8: 'G
+U+03d9: ,G
+U+03da: T3
+U+03db: t3
+U+03dc: M3
+U+03dd: m3
+U+03de: K3
+U+03df: k3
+U+03e0: P3
+U+03e1: p3
+U+03f4: '%
+U+03f5: j3
+U+0401: IO
+U+0402: D%
+U+0403: G%
+U+0404: IE
+U+0405: DS
+U+0406: II
+U+0407: YI
+U+0408: J%
+U+0409: LJ
+U+040a: NJ
+U+040b: Ts
+U+040c: KJ
+U+040e: V%
+U+040f: DZ
+U+0410: A=
+U+0411: B=
+U+0412: V=
+U+0413: G=
+U+0414: D=
+U+0415: E=
+U+0416: Z%
+U+0417: Z=
+U+0418: I=
+U+0419: J=
+U+041a: K=
+U+041b: L=
+U+041c: M=
+U+041d: N=
+U+041e: O=
+U+041f: P=
+U+0420: R=
+U+0421: S=
+U+0422: T=
+U+0423: U=
+U+0424: F=
+U+0425: H=
+U+0426: C=
+U+0427: C%
+U+0428: S%
+U+0429: Sc
+U+042a: ="
+U+042b: Y=
+U+042c: %"
+U+042d: JE
+U+042e: JU
+U+042f: JA
+U+0430: a=
+U+0431: b=
+U+0432: v=
+U+0433: g=
+U+0434: d=
+U+0435: e=
+U+0436: z%
+U+0437: z=
+U+0438: i=
+U+0439: j=
+U+043a: k=
+U+043b: l=
+U+043c: m=
+U+043d: n=
+U+043e: o=
+U+043f: p=
+U+0440: r=
+U+0441: s=
+U+0442: t=
+U+0443: u=
+U+0444: f=
+U+0445: h=
+U+0446: c=
+U+0447: c%
+U+0448: s%
+U+0449: sc
+U+044a: ='
+U+044b: y=
+U+044c: %'
+U+044d: je
+U+044e: ju
+U+044f: ja
+U+0451: io
+U+0452: d%
+U+0453: g%
+U+0454: ie
+U+0455: ds
+U+0456: ii
+U+0457: yi
+U+0458: j%
+U+0459: lj
+U+045a: nj
+U+045b: ts
+U+045c: kj
+U+045e: v%
+U+045f: dz
+U+0462: Y3
+U+0463: y3
+U+046a: O3
+U+046b: o3
+U+0472: F3
+U+0473: f3
+U+0474: V3
+U+0475: v3
+U+0480: C3
+U+0481: c3
+U+0490: G3
+U+0491: g3
+U+05d0: A+
+U+05d1: B+
+U+05d2: G+
+U+05d3: D+
+U+05d4: H+
+U+05d5: W+
+U+05d6: Z+
+U+05d7: X+
+U+05d8: Tj
+U+05d9: J+
+U+05da: K%
+U+05db: K+
+U+05dc: L+
+U+05dd: M%
+U+05de: M+
+U+05df: N%
+U+05e0: N+
+U+05e1: S+
+U+05e2: E+
+U+05e3: P%
+U+05e4: P+
+U+05e5: Zj
+U+05e6: ZJ
+U+05e7: Q+
+U+05e8: R+
+U+05e9: Sh
+U+05ea: T+
+U+060c: ,+
+U+061b: ;+
+U+061f: ?+
+U+0621: H'
+U+0622: aM
+U+0623: aH
+U+0624: wH
+U+0625: ah
+U+0626: yH
+U+0627: a+
+U+0628: b+
+U+0629: tm
+U+062a: t+
+U+062b: tk
+U+062c: g+
+U+062d: hk
+U+062e: x+
+U+062f: d+
+U+0630: dk
+U+0631: r+
+U+0632: z+
+U+0633: s+
+U+0634: sn
+U+0635: c+
+U+0636: dd
+U+0637: tj
+U+0638: zH
+U+0639: e+
+U+063a: i+
+U+0640: ++
+U+0641: f+
+U+0642: q+
+U+0643: k+
+U+0644: l+
+U+0645: m+
+U+0646: n+
+U+0647: h+
+U+0648: w+
+U+0649: j+
+U+064a: y+
+U+064b: :+
+U+064c: "+
+U+064d: =+
+U+064e: /+
+U+064f: '+
+U+0650: 1+
+U+0651: 3+
+U+0652: 0+
+U+0670: aS
+U+067e: p+
+U+06a4: v+
+U+06af: gf
+U+06f0: 0a
+U+06f1: 1a
+U+06f2: 2a
+U+06f3: 3a
+U+06f4: 4a
+U+06f5: 5a
+U+06f6: 6a
+U+06f7: 7a
+U+06f8: 8a
+U+06f9: 9a
+U+1e00: _A-0_
+U+1e01: _a-0_
+U+1e02: B.
+U+1e03: b.
+U+1e04: _B-._
+U+1e05: _b-._
+U+1e06: B_
+U+1e07: b_
+U+1e08: _C,'_
+U+1e09: _c,'_
+U+1e0a: D.
+U+1e0b: d.
+U+1e0c: _D-._
+U+1e0d: _d-._
+U+1e0e: D_
+U+1e0f: d_
+U+1e10: D,
+U+1e11: d,
+U+1e12: _D->_
+U+1e13: _d->_
+U+1e14: _E-!_
+U+1e15: _e-!_
+U+1e16: _E-'_
+U+1e17: _e-'_
+U+1e18: _E->_
+U+1e19: _e->_
+U+1e1a: _E-?_
+U+1e1b: _e-?_
+U+1e1c: _E,(_
+U+1e1d: _e,(_
+U+1e1e: F.
+U+1e1f: f.
+U+1e20: G-
+U+1e21: g-
+U+1e22: H.
+U+1e23: h.
+U+1e24: _H-._
+U+1e25: _h-._
+U+1e26: H:
+U+1e27: h:
+U+1e28: H,
+U+1e29: h,
+U+1e2a: _H-(_
+U+1e2b: _h-(_
+U+1e2c: _I-?_
+U+1e2d: _i-?_
+U+1e2e: _I:'_
+U+1e2f: _i:'_
+U+1e30: K'
+U+1e31: k'
+U+1e32: _K-._
+U+1e33: _k-._
+U+1e34: K_
+U+1e35: k_
+U+1e36: _L-._
+U+1e37: _l-._
+U+1e38: _L--._
+U+1e39: _l--._
+U+1e3a: L_
+U+1e3b: l_
+U+1e3c: _L->_
+U+1e3d: _l->_
+U+1e3e: M'
+U+1e3f: m'
+U+1e40: M.
+U+1e41: m.
+U+1e42: _M-._
+U+1e43: _m-._
+U+1e44: N.
+U+1e45: n.
+U+1e46: _N-._
+U+1e47: _n-._
+U+1e48: N_
+U+1e49: n_
+U+1e4a: _N->_
+U+1e4b: _N->_
+U+1e4c: _O?'_
+U+1e4d: _o?'_
+U+1e4e: _O?:_
+U+1e4f: _o?:_
+U+1e50: _O-!_
+U+1e51: _o-!_
+U+1e52: _O-'_
+U+1e53: _o-'_
+U+1e54: P'
+U+1e55: p'
+U+1e56: P.
+U+1e57: p.
+U+1e58: R.
+U+1e59: r.
+U+1e5a: _R-._
+U+1e5b: _r-._
+U+1e5c: _R--._
+U+1e5d: _r--._
+U+1e5e: R_
+U+1e5f: r_
+U+1e60: S.
+U+1e61: s.
+U+1e62: _S-._
+U+1e63: _s-._
+U+1e64: _S'._
+U+1e65: _s'._
+U+1e66: _S<._
+U+1e67: _s<._
+U+1e68: _S.-._
+U+1e69: _S.-._
+U+1e6a: T.
+U+1e6b: t.
+U+1e6c: _T-._
+U+1e6d: _t-._
+U+1e6e: T_
+U+1e6f: t_
+U+1e70: _T->_
+U+1e71: _t->_
+U+1e72: _U--:_
+U+1e73: _u--:_
+U+1e74: _U-?_
+U+1e75: _u-?_
+U+1e76: _U->_
+U+1e77: _u->_
+U+1e78: _U?'_
+U+1e79: _u?'_
+U+1e7a: _U-:_
+U+1e7b: _u-:_
+U+1e7c: V?
+U+1e7d: v?
+U+1e7e: _V-._
+U+1e7f: _v-._
+U+1e80: W!
+U+1e81: w!
+U+1e82: W'
+U+1e83: w'
+U+1e84: W:
+U+1e85: w:
+U+1e86: W.
+U+1e87: w.
+U+1e88: _W-._
+U+1e89: _w-._
+U+1e8a: X.
+U+1e8b: x.
+U+1e8c: X:
+U+1e8d: x:
+U+1e8e: Y.
+U+1e8f: y.
+U+1e90: Z>
+U+1e91: z>
+U+1e92: _Z-._
+U+1e93: _z-._
+U+1e94: Z_
+U+1e95: z_
+U+1e96: h_
+U+1e97: t:
+U+1e98: w0
+U+1e99: y0
+U+1ea0: _A-._
+U+1ea1: _a-._
+U+1ea2: A2
+U+1ea3: a2
+U+1ea4: _A>'_
+U+1ea5: _a>'_
+U+1ea6: _A>!_
+U+1ea7: _a>!_
+U+1ea8: _A>2_
+U+1ea9: _a>2_
+U+1eaa: _A>?_
+U+1eab: _a>?_
+U+1eac: _A>-._
+U+1ead: _a>-._
+U+1eae: _A('_
+U+1eaf: _a('_
+U+1eb0: _A(!_
+U+1eb1: _a(!_
+U+1eb2: _A(2_
+U+1eb3: _a(2_
+U+1eb4: _A(?_
+U+1eb5: _a(?_
+U+1eb6: _A(-._
+U+1eb7: _a(-._
+U+1eb8: _E-._
+U+1eb9: _e-._
+U+1eba: E2
+U+1ebb: e2
+U+1ebc: E?
+U+1ebd: e?
+U+1ebe: _E>'_
+U+1ebf: _e>'_
+U+1ec0: _E>!_
+U+1ec1: _e>!_
+U+1ec2: _E>2_
+U+1ec3: _e>2_
+U+1ec4: _E>?_
+U+1ec5: _e>?_
+U+1ec6: _E>-._
+U+1ec7: _e>-._
+U+1ec8: I2
+U+1ec9: i2
+U+1eca: _I-._
+U+1ecb: _i-._
+U+1ecc: _O-._
+U+1ecd: _o-._
+U+1ece: O2
+U+1ecf: o2
+U+1ed0: _O>'_
+U+1ed1: _o>'_
+U+1ed2: _O>!_
+U+1ed3: _o>!_
+U+1ed4: _O>2_
+U+1ed5: _o>2_
+U+1ed6: _O>?_
+U+1ed7: _o>?_
+U+1ed8: _O>-._
+U+1ed9: _o>-._
+U+1eda: _O9'_
+U+1edb: _o9'_
+U+1edc: _O9!_
+U+1edd: _o9!_
+U+1ede: _O92_
+U+1edf: _o92_
+U+1ee0: _O9?_
+U+1ee1: _o9?_
+U+1ee2: _O9-._
+U+1ee3: _o9-._
+U+1ee4: _U-._
+U+1ee5: _u-._
+U+1ee6: U2
+U+1ee7: u2
+U+1ee8: _U9'_
+U+1ee9: _u9'_
+U+1eea: _U9!_
+U+1eeb: _u9!_
+U+1eec: _U92_
+U+1eed: _u92_
+U+1eee: _U9?_
+U+1eef: _u9?_
+U+1ef0: _U9-._
+U+1ef1: _u9-._
+U+1ef2: Y!
+U+1ef3: y!
+U+1ef4: _Y-._
+U+1ef5: _y-._
+U+1ef6: Y2
+U+1ef7: y2
+U+1ef8: Y?
+U+1ef9: y?
+U+1f00: ;'
+U+1f01: ,'
+U+1f02: ;!
+U+1f03: ,!
+U+1f04: ?;
+U+1f05: ?,
+U+1f06: !:
+U+1f07: ?:
+U+2002: 1N
+U+2003: 1M
+U+2004: 3M
+U+2005: 4M
+U+2006: 6M
+U+2009: 1T
+U+200a: 1H
+U+2010: -1
+U+2013: -N
+U+2014: -M
+U+2015: -3
+U+2016: !2
+U+2017: =2
+U+2018: '6
+U+2019: '9
+U+201a: .9
+U+201b: 9'
+U+201c: "6
+U+201d: "9
+U+201e: :9
+U+201f: 9"
+U+2020: /-
+U+2021: /=
+U+2025: ..
+U+2030: %0
+U+2032: 1'
+U+2033: 2'
+U+2034: 3'
+U+2035: 1"
+U+2036: 2"
+U+2037: 3"
+U+2038: Ca
+U+2039: <1
+U+203a: >1
+U+203b: :X
+U+203c: _!*2_
+U+203e: '-
+U+2044: /f
+U+2070: 0S
+U+2074: 4S
+U+2075: 5S
+U+2076: 6S
+U+2077: 7S
+U+2078: 8S
+U+2079: 9S
+U+207a: +S
+U+207b: -S
+U+207c: =S
+U+207d: (S
+U+207e: )S
+U+207f: nS
+U+2080: 0s
+U+2081: 1s
+U+2082: 2s
+U+2083: 3s
+U+2084: 4s
+U+2085: 5s
+U+2086: 6s
+U+2087: 7s
+U+2088: 8s
+U+2089: 9s
+U+208a: +s
+U+208b: -s
+U+208c: =s
+U+208d: (s
+U+208e: )s
+U+20a4: Li
+U+20a7: Pt
+U+20a9: W=
+U+2103: oC
+U+2105: co
+U+2109: oF
+U+2116: N0
+U+2117: PO
+U+211e: Rx
+U+2120: SM
+U+2122: TM
+U+2126: Om
+U+212b: AO
+U+2153: 13
+U+2154: 23
+U+2155: 15
+U+2156: 25
+U+2157: 35
+U+2158: 45
+U+2159: 16
+U+215a: 56
+U+215b: 18
+U+215c: 38
+U+215d: 58
+U+215e: 78
+U+2160: 1R
+U+2161: 2R
+U+2162: 3R
+U+2163: 4R
+U+2164: 5R
+U+2165: 6R
+U+2166: 7R
+U+2167: 8R
+U+2168: 9R
+U+2169: aR
+U+216a: bR
+U+216b: cR
+U+216c: _50R_
+U+216d: _100R_
+U+216e: _500R_
+U+216f: _1000R_
+U+2170: 1r
+U+2171: 2r
+U+2172: 3r
+U+2173: 4r
+U+2174: 5r
+U+2175: 6r
+U+2176: 7r
+U+2177: 8r
+U+2178: 9r
+U+2179: ar
+U+217a: br
+U+217b: cr
+U+217c: _50r_
+U+217d: _100r_
+U+217e: _500r_
+U+217f: _1000r_
+U+2180: _1000RCD_
+U+2181: _5000R_
+U+2182: _10000R_
+U+2190: <-
+U+2191: -!
+U+2192: ->
+U+2193: -v
+U+2194: <>
+U+2195: UD
+U+2196: _<!!_
+U+2197: _//>_
+U+2198: _!!>_
+U+2199: _<//_
+U+21d0: <=
+U+21d2: =>
+U+21d4: ==
+U+2200: FA
+U+2202: dP
+U+2203: TE
+U+2205: /0
+U+2206: DE
+U+2207: NB
+U+2208: (-
+U+220b: -)
+U+220f: *P
+U+2211: +Z
+U+2212: -2
+U+2213: -+
+U+2217: *-
+U+2218: Ob
+U+2219: Sb
+U+221a: RT
+U+221d: 0(
+U+221e: 00
+U+221f: -L
+U+2220: -V
+U+2225: PP
+U+2227: AN
+U+2228: OR
+U+2229: (U
+U+222a: )U
+U+222b: In
+U+222c: DI
+U+222e: Io
+U+2234: .:
+U+2235: :.
+U+2236: :R
+U+2237: ::
+U+223c: ?1
+U+223e: CG
+U+2243: ?-
+U+2245: ?=
+U+2248: ?2
+U+224c: =?
+U+2253: HI
+U+2260: !=
+U+2261: =3
+U+2264: =<
+U+2265: >=
+U+226a: <*
+U+226b: *>
+U+226e: !<
+U+226f: !>
+U+2282: (C
+U+2283: )C
+U+2286: (_
+U+2287: )_
+U+2299: 0.
+U+229a: 02
+U+22a5: -T
+U+22c5: .P
+U+22ee: :3
+U+22ef: .3
+U+2302: Eh
+U+2308: <7
+U+2309: >7
+U+230a: 7<
+U+230b: 7>
+U+2310: NI
+U+2312: (A
+U+2315: TR
+U+2320: Iu
+U+2321: Il
+U+2329: </
+U+232a: />
+U+2423: Vs
+U+2440: 1h
+U+2441: 3h
+U+2442: 2h
+U+2443: 4h
+U+2446: 1j
+U+2447: 2j
+U+2448: 3j
+U+2449: 4j
+U+2460: _1-o_
+U+2461: _2-o_
+U+2462: _3-o_
+U+2463: _4-o_
+U+2464: _5-o_
+U+2465: _6-o_
+U+2466: _7-o_
+U+2467: _8-o_
+U+2468: _9-o_
+U+2469: _10-o_
+U+246a: _11-o_
+U+246b: _12-o_
+U+246c: _13-o_
+U+246d: _14-o_
+U+246e: _15-o_
+U+246f: _16-o_
+U+2470: _17-o_
+U+2471: _18-o_
+U+2472: _19-o_
+U+2473: _20-o_
+U+2474: _(1)_
+U+2475: _(2)_
+U+2476: _(3)_
+U+2477: _(4)_
+U+2478: _(5)_
+U+2479: _(6)_
+U+247a: _(7)_
+U+247b: _(8)_
+U+247c: _(9)_
+U+247d: _(10)_
+U+247e: _(11)_
+U+247f: _(12)_
+U+2480: _(13)_
+U+2481: _(14)_
+U+2482: _(15)_
+U+2483: _(16)_
+U+2484: _(17)_
+U+2485: _(18)_
+U+2486: _(19)_
+U+2487: _(20)_
+U+2488: 1.
+U+2489: 2.
+U+248a: 3.
+U+248b: 4.
+U+248c: 5.
+U+248d: 6.
+U+248e: 7.
+U+248f: 8.
+U+2490: 9.
+U+2491: _10._
+U+2492: _11._
+U+2493: _12._
+U+2494: _13._
+U+2495: _14._
+U+2496: _15._
+U+2497: _16._
+U+2498: _17._
+U+2499: _18._
+U+249a: _19._
+U+249b: _20._
+U+249c: _(a)_
+U+249d: _(b)_
+U+249e: _(c)_
+U+249f: _(d)_
+U+24a0: _(e)_
+U+24a1: _(f)_
+U+24a2: _(g)_
+U+24a3: _(h)_
+U+24a4: _(i)_
+U+24a5: _(j)_
+U+24a6: _(k)_
+U+24a7: _(l)_
+U+24a8: _(m)_
+U+24a9: _(n)_
+U+24aa: _(o)_
+U+24ab: _(p)_
+U+24ac: _(q)_
+U+24ad: _(r)_
+U+24ae: _(s)_
+U+24af: _(t)_
+U+24b0: _(u)_
+U+24b1: _(v)_
+U+24b2: _(w)_
+U+24b3: _(x)_
+U+24b4: _(y)_
+U+24b5: _(z)_
+U+24b6: _A-o_
+U+24b7: _B-o_
+U+24b8: _C-o_
+U+24b9: _D-o_
+U+24ba: _E-o_
+U+24bb: _F-o_
+U+24bc: _G-o_
+U+24bd: _H-o_
+U+24be: _I-o_
+U+24bf: _J-o_
+U+24c0: _K-o_
+U+24c1: _L-o_
+U+24c2: _M-o_
+U+24c3: _N-o_
+U+24c4: _O-o_
+U+24c5: _P-o_
+U+24c6: _Q-o_
+U+24c7: _R-o_
+U+24c8: _S-o_
+U+24c9: _T-o_
+U+24ca: _U-o_
+U+24cb: _V-o_
+U+24cc: _W-o_
+U+24cd: _X-o_
+U+24ce: _Y-o_
+U+24cf: _Z-o_
+U+24d0: _a-o_
+U+24d1: _b-o_
+U+24d2: _c-o_
+U+24d3: _d-o_
+U+24d4: _e-o_
+U+24d5: _f-o_
+U+24d6: _g-o_
+U+24d7: _h-o_
+U+24d8: _i-o_
+U+24d9: _j-o_
+U+24da: _k-o_
+U+24db: _l-o_
+U+24dc: _m-o_
+U+24dd: _n-o_
+U+24de: _o-o_
+U+24df: _p-o_
+U+24e0: _q-o_
+U+24e1: _r-o_
+U+24e2: _s-o_
+U+24e3: _t-o_
+U+24e4: _u-o_
+U+24e5: _v-o_
+U+24e6: _w-o_
+U+24e7: _x-o_
+U+24e8: _y-o_
+U+24e9: _z-o_
+U+24ea: _0-o_
+U+2500: hh
+U+2501: HH
+U+2502: vv
+U+2503: VV
+U+2504: 3-
+U+2505: 3_
+U+2506: 3!
+U+2507: 3/
+U+2508: 4-
+U+2509: 4_
+U+250a: 4!
+U+250b: 4/
+U+250c: dr
+U+250d: dR
+U+250e: Dr
+U+250f: DR
+U+2510: dl
+U+2511: dL
+U+2512: Dl
+U+2513: LD
+U+2514: ur
+U+2515: uR
+U+2516: Ur
+U+2517: UR
+U+2518: ul
+U+2519: uL
+U+251a: Ul
+U+251b: UL
+U+251c: vr
+U+251d: vR
+U+251e: _Udr_
+U+251f: _uDr_
+U+2520: Vr
+U+2521: _UdR_
+U+2522: _uDR_
+U+2523: VR
+U+2524: vl
+U+2525: vL
+U+2526: _Udl_
+U+2527: _uDl_
+U+2528: Vl
+U+2529: _UdL_
+U+252a: _uDL_
+U+252b: VL
+U+252c: dh
+U+252d: _dLr_
+U+252e: _dlR_
+U+252f: dH
+U+2530: Dh
+U+2531: _DLr_
+U+2532: _DlR_
+U+2533: DH
+U+2534: uh
+U+2535: _uLr_
+U+2536: _ulR_
+U+2537: uH
+U+2538: Uh
+U+2539: _ULr_
+U+253a: _UlR_
+U+253b: UH
+U+253c: vh
+U+253d: _vLr_
+U+253e: _vlR_
+U+253f: vH
+U+2540: _Udh_
+U+2541: _uDh_
+U+2542: Vh
+U+2543: _UdLr_
+U+2544: _UdlR_
+U+2545: _uDLr_
+U+2546: _uDlR_
+U+2547: _UdH_
+U+2548: _uDH_
+U+2549: _VLr_
+U+254a: _VlR_
+U+254b: VH
+U+2571: FD
+U+2572: BD
+U+2580: TB
+U+2584: LB
+U+2588: FB
+U+258c: lB
+U+2590: RB
+U+2591: .S
+U+2592: :S
+U+2593: ?S
+U+25a0: fS
+U+25a1: OS
+U+25a2: RO
+U+25a3: Rr
+U+25a4: RF
+U+25a5: RY
+U+25a6: RH
+U+25a7: RZ
+U+25a8: RK
+U+25a9: RX
+U+25aa: sB
+U+25ac: SR
+U+25ad: Or
+U+25b2: UT
+U+25b3: uT
+U+25b6: PR
+U+25b7: Tr
+U+25bc: Dt
+U+25bd: dT
+U+25c0: PL
+U+25c1: Tl
+U+25c6: Db
+U+25c7: Dw
+U+25ca: LZ
+U+25cb: 0m
+U+25ce: 0o
+U+25cf: 0M
+U+25d0: 0L
+U+25d1: 0R
+U+25d8: Sn
+U+25d9: Ic
+U+25e2: Fd
+U+25e3: Bd
+U+2605: *2
+U+2606: *1
+U+260e: _TEL_
+U+260f: _tel_
+U+261c: <H
+U+261e: >H
+U+263a: 0u
+U+263b: 0U
+U+263c: SU
+U+2640: Fm
+U+2642: Ml
+U+2660: cS
+U+2661: cH
+U+2662: cD
+U+2663: cC
+U+2664: _cS-_
+U+2665: _cH-_
+U+2666: _cD-_
+U+2667: _cC-_
+U+2669: Md
+U+266a: M8
+U+266b: M2
+U+266c: _M16_
+U+266d: Mb
+U+266e: Mx
+U+266f: MX
+U+2713: OK
+U+2717: XX
+U+2720: -X
+U+3000: IS
+U+3001: ,_
+U+3002: ._
+U+3003: +"
+U+3004: +_
+U+3005: *_
+U+3006: ;_
+U+3007: 0_
+U+300a: <+
+U+300b: >+
+U+300c: <'
+U+300d: >'
+U+300e: <"
+U+300f: >"
+U+3010: ("
+U+3011: )"
+U+3012: =T
+U+3013: =_
+U+3014: ('
+U+3015: )'
+U+3016: (I
+U+3017: )I
+U+301c: -?
+U+3020: _=T:)_
+U+3041: A5
+U+3042: a5
+U+3043: I5
+U+3044: i5
+U+3045: U5
+U+3046: u5
+U+3047: E5
+U+3048: e5
+U+3049: O5
+U+304a: o5
+U+304b: ka
+U+304c: ga
+U+304d: ki
+U+304e: gi
+U+304f: ku
+U+3050: gu
+U+3051: ke
+U+3052: ge
+U+3053: ko
+U+3054: go
+U+3055: sa
+U+3056: za
+U+3057: si
+U+3058: zi
+U+3059: su
+U+305a: zu
+U+305b: se
+U+305c: ze
+U+305d: so
+U+305e: zo
+U+305f: ta
+U+3060: da
+U+3061: ti
+U+3062: di
+U+3063: tU
+U+3064: tu
+U+3065: du
+U+3066: te
+U+3067: de
+U+3068: to
+U+3069: do
+U+306a: na
+U+306b: ni
+U+306c: nu
+U+306d: ne
+U+306e: no
+U+306f: ha
+U+3070: ba
+U+3071: pa
+U+3072: hi
+U+3073: bi
+U+3074: pi
+U+3075: hu
+U+3076: bu
+U+3077: pu
+U+3078: he
+U+3079: be
+U+307a: pe
+U+307b: ho
+U+307c: bo
+U+307d: po
+U+307e: ma
+U+307f: mi
+U+3080: mu
+U+3081: me
+U+3082: mo
+U+3083: yA
+U+3084: ya
+U+3085: yU
+U+3086: yu
+U+3087: yO
+U+3088: yo
+U+3089: ra
+U+308a: ri
+U+308b: ru
+U+308c: re
+U+308d: ro
+U+308e: wA
+U+308f: wa
+U+3090: wi
+U+3091: we
+U+3092: wo
+U+3093: n5
+U+3094: vu
+U+309b: "5
+U+309c: 05
+U+309d: *5
+U+309e: +5
+U+30a1: a6
+U+30a2: A6
+U+30a3: i6
+U+30a4: I6
+U+30a5: u6
+U+30a6: U6
+U+30a7: e6
+U+30a8: E6
+U+30a9: o6
+U+30aa: O6
+U+30ab: Ka
+U+30ac: Ga
+U+30ad: Ki
+U+30ae: Gi
+U+30af: Ku
+U+30b0: Gu
+U+30b1: Ke
+U+30b2: Ge
+U+30b3: Ko
+U+30b4: Go
+U+30b5: Sa
+U+30b6: Za
+U+30b7: Si
+U+30b8: Zi
+U+30b9: Su
+U+30ba: Zu
+U+30bb: Se
+U+30bc: Ze
+U+30bd: So
+U+30be: Zo
+U+30bf: Ta
+U+30c0: Da
+U+30c1: Ti
+U+30c2: Di
+U+30c3: TU
+U+30c4: Tu
+U+30c5: Du
+U+30c6: Te
+U+30c7: De
+U+30c8: To
+U+30c9: Do
+U+30ca: Na
+U+30cb: Ni
+U+30cc: Nu
+U+30cd: Ne
+U+30ce: No
+U+30cf: Ha
+U+30d0: Ba
+U+30d1: Pa
+U+30d2: Hi
+U+30d3: Bi
+U+30d4: Pi
+U+30d5: Hu
+U+30d6: Bu
+U+30d7: Pu
+U+30d8: He
+U+30d9: Be
+U+30da: Pe
+U+30db: Ho
+U+30dc: Bo
+U+30dd: Po
+U+30de: Ma
+U+30df: Mi
+U+30e0: Mu
+U+30e1: Me
+U+30e2: Mo
+U+30e3: YA
+U+30e4: Ya
+U+30e5: YU
+U+30e6: Yu
+U+30e7: YO
+U+30e8: Yo
+U+30e9: Ra
+U+30ea: Ri
+U+30eb: Ru
+U+30ec: Re
+U+30ed: Ro
+U+30ee: WA
+U+30ef: Wa
+U+30f0: Wi
+U+30f1: We
+U+30f2: Wo
+U+30f3: N6
+U+30f4: Vu
+U+30f5: KA
+U+30f6: KE
+U+30f7: Va
+U+30f8: Vi
+U+30f9: Ve
+U+30fa: Vo
+U+30fb: .6
+U+30fc: -6
+U+30fd: *6
+U+30fe: +6
+U+3105: b4
+U+3106: p4
+U+3107: m4
+U+3108: f4
+U+3109: d4
+U+310a: t4
+U+310b: n4
+U+310c: l4
+U+310d: g4
+U+310e: k4
+U+310f: h4
+U+3110: j4
+U+3111: q4
+U+3112: x4
+U+3113: zh
+U+3114: ch
+U+3115: sh
+U+3116: r4
+U+3117: z4
+U+3118: c4
+U+3119: s4
+U+311a: a4
+U+311b: o4
+U+311c: e4
+U+311d: _eh4_
+U+311e: ai
+U+311f: ei
+U+3120: au
+U+3121: ou
+U+3122: an
+U+3123: en
+U+3124: aN
+U+3125: eN
+U+3126: er
+U+3127: i4
+U+3128: u4
+U+3129: iu
+U+312a: v4
+U+312b: nG
+U+312c: gn
+U+321c: _(JU)_
+U+3220: 1c
+U+3221: 2c
+U+3222: 3c
+U+3223: 4c
+U+3224: 5c
+U+3225: 6c
+U+3226: 7c
+U+3227: 8c
+U+3228: 9c
+U+3229: _10c_
+U+327f: _KSC_
+U+fb00: ff
+U+fb01: fi
+U+fb02: fl
+U+fb03: _ffi_
+U+fb04: _ffl_
+U+fb05: ft
+U+fb06: st
+U+fe7d: _3+;_
+U+fe82: _aM._
+U+fe84: _aH._
+U+fe8d: _a+-_
+U+fe8e: _a+._
+U+fe8f: _b+-_
+U+fe90: _b+,_
+U+fe91: _b+;_
+U+fe92: _b+._
+U+fe93: _tm-_
+U+fe94: _tm._
+U+fe95: _t+-_
+U+fe96: _t+,_
+U+fe97: _t+;_
+U+fe98: _t+._
+U+fe99: _tk-_
+U+fe9a: _tk,_
+U+fe9b: _tk;_
+U+fe9c: _tk._
+U+fe9d: _g+-_
+U+fe9e: _g+,_
+U+fe9f: _g+;_
+U+fea0: _g+._
+U+fea1: _hk-_
+U+fea2: _hk,_
+U+fea3: _hk;_
+U+fea4: _hk._
+U+fea5: _x+-_
+U+fea6: _x+,_
+U+fea7: _x+;_
+U+fea8: _x+._
+U+fea9: _d+-_
+U+feaa: _d+._
+U+feab: _dk-_
+U+feac: _dk._
+U+fead: _r+-_
+U+feae: _r+._
+U+feaf: _z+-_
+U+feb0: _z+._
+U+feb1: _s+-_
+U+feb2: _s+,_
+U+feb3: _s+;_
+U+feb4: _s+._
+U+feb5: _sn-_
+U+feb6: _sn,_
+U+feb7: _sn;_
+U+feb8: _sn._
+U+feb9: _c+-_
+U+feba: _c+,_
+U+febb: _c+;_
+U+febc: _c+._
+U+febd: _dd-_
+U+febe: _dd,_
+U+febf: _dd;_
+U+fec0: _dd._
+U+fec1: _tj-_
+U+fec2: _tj,_
+U+fec3: _tj;_
+U+fec4: _tj._
+U+fec5: _zH-_
+U+fec6: _zH,_
+U+fec7: _zH;_
+U+fec8: _zH._
+U+fec9: _e+-_
+U+feca: _e+,_
+U+fecb: _e+;_
+U+fecc: _e+._
+U+fecd: _i+-_
+U+fece: _i+,_
+U+fecf: _i+;_
+U+fed0: _i+._
+U+fed1: _f+-_
+U+fed2: _f+,_
+U+fed3: _f+;_
+U+fed4: _f+._
+U+fed5: _q+-_
+U+fed6: _q+,_
+U+fed7: _q+;_
+U+fed8: _q+._
+U+fed9: _k+-_
+U+feda: _k+,_
+U+fedb: _k+;_
+U+fedc: _k+._
+U+fedd: _l+-_
+U+fede: _l+,_
+U+fedf: _l+;_
+U+fee0: _l+._
+U+fee1: _m+-_
+U+fee2: _m+,_
+U+fee3: _m+;_
+U+fee4: _m+._
+U+fee5: _n+-_
+U+fee6: _n+,_
+U+fee7: _n+;_
+U+fee8: _n+._
+U+fee9: _h+-_
+U+feea: _h+,_
+U+feeb: _h+;_
+U+feec: _h+._
+U+feed: _w+-_
+U+feee: _w+._
+U+feef: _j+-_
+U+fef0: _j+._
+U+fef1: _y+-_
+U+fef2: _y+,_
+U+fef3: _y+;_
+U+fef4: _y+._
+U+fef5: _lM-_
+U+fef6: _lM._
+U+fef7: _lH-_
+U+fef8: _lH._
+U+fef9: _lh-_
+U+fefa: _lh._
+U+fefb: _la-_
+U+fefc: _la._
+U+0000: NU
+U+0001: SH
+U+0002: SX
+U+0003: EX
+U+0004: ET
+U+0005: EQ
+U+0006: AK
+U+0007: BL
+U+0008: BS
+U+0009: HT
+# U+000a: LF
+U+000b: VT
+U+000c: FF
+U+000d: CR
+U+000e: SO
+U+000f: SI
+U+0010: DL
+U+0011: D1
+U+0012: D2
+U+0013: D3
+U+0014: D4
+U+0015: NK
+U+0016: SY
+U+0017: EB
+U+0018: CN
+U+0019: EM
+U+001a: SB
+U+001b: EC
+U+001c: FS
+U+001d: GS
+U+001e: RS
+U+001f: US
+U+007f: DT
+U+0080: PA
+U+0081: HO
+U+0082: BH
+U+0083: NH
+U+0084: IN
+U+0085: NL
+U+0086: SA
+U+0087: ES
+U+0088: HS
+U+0089: HJ
+U+008a: VS
+U+008b: PD
+U+008c: PU
+U+008d: RI
+U+008e: S2
+U+008f: S3
+U+0090: DC
+U+0091: P1
+U+0092: P2
+U+0093: TS
+U+0094: CC
+U+0095: MW
+U+0096: SG
+U+0097: EG
+U+0098: SS
+U+0099: GC
+U+009a: SC
+U+009b: CI
+U+009c: ST
+U+009d: OC
+U+009e: PM
+U+009f: AC
+# Characters in Private Use Area (e000-f8ff) do not have ussigned numbers
+# according Unicode 2.0
diff --git a/src/chrtrans/next_uni.tbl b/src/chrtrans/next_uni.tbl
new file mode 100644
index 00000000..a166ba5c
--- /dev/null
+++ b/src/chrtrans/next_uni.tbl
@@ -0,0 +1,185 @@
+# This file has been modified for lynx (see README.tables)
+
+#The MIME name of this charset.
+Mnext
+
+#Name as a Display Charset (used on Options screen)
+ONeXT character set
+
+#
+#       Name:             NextStep Encoding to Unicode
+#       Unicode version:  1.1
+#       Table version:    0.1
+#       Table format:     Format A
+#       Date:             1999 September 23
+#       Authors:          Rick McGowan
+#
+#       Copyright (c) 1991-1999 Unicode, Inc.  All Rights reserved.
+#
+#	This file is provided as-is by Unicode, Inc. (The Unicode Consortium).
+#	No claims are made as to fitness for any particular purpose.  No
+#	warranties of any kind are expressed or implied.  The recipient
+#	agrees to determine applicability of information provided.  If this
+#	file has been provided on optical media by Unicode, Inc., the sole
+#	remedy for any claim will be exchange of defective media within 90
+#	days of receipt.
+#
+#	Unicode, Inc. hereby grants the right to freely use the information
+#	supplied in this file in the creation of products supporting the
+#	Unicode Standard, and to make copies of this file in any form for
+#	internal or external distribution as long as this notice remains
+#	attached.
+#
+#	General notes:
+#
+#	This table contains the data the Unicode Consortium has on how
+#	NextStep Encoding characters map into Unicode.	Since the first
+#	128 characters (0x0 - 0x7f) are identical to ASCII and Unicode,
+#	this table only maps the NextStep range from 0x80 - 0xFF.
+#
+#	This file is provided for historical reference only and pertains
+#	to NextStep and OpenStep products shipped prior to the aquisition
+#	of NeXT by Apple Computer, Inc.  See http://www.apple.com for
+#	further information.
+#
+#       Format:  Three tab-separated columns
+#                Column #1 is the NextStep code (in hex as 0xXX)
+#                Column #2 is the Unicode (in hex as 0xXXXX)
+#                Column #3 NextStep name, Unicode name (follows a comment sign, '#')
+#
+#       The entries are in NextStep order
+#
+#       Any comments or problems, contact info@unicode.org
+#
+#
+0x20-0x7f       idem
+#
+0x80	U+00a0	# NO-BREAK SPACE
+0x81	U+00c0	# LATIN CAPITAL LETTER A WITH GRAVE
+0x82	U+00c1	# LATIN CAPITAL LETTER A WITH ACUTE
+0x83	U+00c2	# LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+0x84	U+00c3	# LATIN CAPITAL LETTER A WITH TILDE
+0x85	U+00c4	# LATIN CAPITAL LETTER A WITH DIAERESIS
+0x86	U+00c5	# LATIN CAPITAL LETTER A WITH RING
+0x87	U+00c7	# LATIN CAPITAL LETTER C WITH CEDILLA
+0x88	U+00c8	# LATIN CAPITAL LETTER E WITH GRAVE
+0x89	U+00c9	# LATIN CAPITAL LETTER E WITH ACUTE
+0x8a	U+00ca	# LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+0x8b	U+00cb	# LATIN CAPITAL LETTER E WITH DIAERESIS
+0x8c	U+00cc	# LATIN CAPITAL LETTER I WITH GRAVE
+0x8d	U+00cd	# LATIN CAPITAL LETTER I WITH ACUTE
+0x8e	U+00ce	# LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+0x8f	U+00cf	# LATIN CAPITAL LETTER I WITH DIAERESIS
+0x90	U+00d0	# LATIN CAPITAL LETTER ETH
+0x91	U+00d1	# LATIN CAPITAL LETTER N WITH TILDE
+0x92	U+00d2	# LATIN CAPITAL LETTER O WITH GRAVE
+0x93	U+00d3	# LATIN CAPITAL LETTER O WITH ACUTE
+0x94	U+00d4	# LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+0x95	U+00d5	# LATIN CAPITAL LETTER O WITH TILDE
+0x96	U+00d6	# LATIN CAPITAL LETTER O WITH DIAERESIS
+0x97	U+00d9	# LATIN CAPITAL LETTER U WITH GRAVE
+0x98	U+00da	# LATIN CAPITAL LETTER U WITH ACUTE
+0x99	U+00db	# LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+0x9a	U+00dc	# LATIN CAPITAL LETTER U WITH DIAERESIS
+0x9b	U+00dd	# LATIN CAPITAL LETTER Y WITH ACUTE
+0x9c	U+00de	# LATIN CAPITAL LETTER THORN
+0x9d	U+00b5	# MICRO SIGN
+0x9e	U+00d7	# MULTIPLICATION SIGN
+0x9f	U+00f7	# DIVISION SIGN
+0xa0	U+00a9	# COPYRIGHT SIGN
+0xa1	U+00a1	# INVERTED EXCLAMATION MARK
+0xa2	U+00a2	# CENT SIGN
+0xa3	U+00a3	# POUND SIGN
+0xa4	U+2044	# FRACTION SLASH
+0xa5	U+00a5	# YEN SIGN
+0xa6	U+0192	# LATIN SMALL LETTER F WITH HOOK
+0xa7	U+00a7	# SECTION SIGN
+0xa8	U+00a4	# CURRENCY SIGN
+0xa9	U+2019	# RIGHT SINGLE QUOTATION MARK
+0xaa	U+201c	# LEFT DOUBLE QUOTATION MARK
+0xab	U+00ab	# LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xac	U+2039	# SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+0xad	U+203a	# SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0xae	U+fb01	# LATIN SMALL LIGATURE FI
+0xaf	U+fb02	# LATIN SMALL LIGATURE FL
+0xb0	U+00ae	# REGISTERED SIGN
+0xb1	U+2013	# EN DASH
+0xb2	U+2020	# DAGGER
+0xb3	U+2021	# DOUBLE DAGGER
+0xb4	U+00b7	# MIDDLE DOT
+0xb5	U+00a6	# BROKEN BAR
+0xb6	U+00b6	# PILCROW SIGN
+0xb7	U+2022	# BULLET
+0xb8	U+201a	# SINGLE LOW-9 QUOTATION MARK
+0xb9	U+201e	# DOUBLE LOW-9 QUOTATION MARK
+0xba	U+201d	# RIGHT DOUBLE QUOTATION MARK
+0xbb	U+00bb	# RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xbc	U+2026	# HORIZONTAL ELLIPSIS
+0xbd	U+2030	# PER MILLE SIGN
+0xbe	U+00ac	# NOT SIGN
+0xbf	U+00bf	# INVERTED QUESTION MARK
+0xc0	U+00b9	# SUPERSCRIPT ONE
+0xc1	U+02cb	# MODIFIER LETTER GRAVE ACCENT
+0xc2	U+00b4	# ACUTE ACCENT
+0xc3	U+02c6	# MODIFIER LETTER CIRCUMFLEX ACCENT
+0xc4	U+02dc	# SMALL TILDE
+0xc5	U+00af	# MACRON
+0xc6	U+02d8	# BREVE
+0xc7	U+02d9	# DOT ABOVE
+0xc8	U+00a8	# DIAERESIS
+0xc9	U+00b2	# SUPERSCRIPT TWO
+0xca	U+02da	# RING ABOVE
+0xcb	U+00b8	# CEDILLA
+0xcc	U+00b3	# SUPERSCRIPT THREE
+0xcd	U+02dd	# DOUBLE ACUTE ACCENT
+0xce	U+02db	# OGONEK
+0xcf	U+02c7	# CARON
+0xd0	U+2014	# EM DASH
+0xd1	U+00b1	# PLUS-MINUS SIGN
+0xd2	U+00bc	# VULGAR FRACTION ONE QUARTER
+0xd3	U+00bd	# VULGAR FRACTION ONE HALF
+0xd4	U+00be	# VULGAR FRACTION THREE QUARTERS
+0xd5	U+00e0	# LATIN SMALL LETTER A WITH GRAVE
+0xd6	U+00e1	# LATIN SMALL LETTER A WITH ACUTE
+0xd7	U+00e2	# LATIN SMALL LETTER A WITH CIRCUMFLEX
+0xd8	U+00e3	# LATIN SMALL LETTER A WITH TILDE
+0xd9	U+00e4	# LATIN SMALL LETTER A WITH DIAERESIS
+0xda	U+00e5	# LATIN SMALL LETTER A WITH RING ABOVE
+0xdb	U+00e7	# LATIN SMALL LETTER C WITH CEDILLA
+0xdc	U+00e8	# LATIN SMALL LETTER E WITH GRAVE
+0xdd	U+00e9	# LATIN SMALL LETTER E WITH ACUTE
+0xde	U+00ea	# LATIN SMALL LETTER E WITH CIRCUMFLEX
+0xdf	U+00eb	# LATIN SMALL LETTER E WITH DIAERESIS
+0xe0	U+00ec	# LATIN SMALL LETTER I WITH GRAVE
+0xe1	U+00c6	# LATIN CAPITAL LETTER AE
+0xe2	U+00ed	# LATIN SMALL LETTER I WITH ACUTE
+0xe3	U+00aa	# FEMININE ORDINAL INDICATOR
+0xe4	U+00ee	# LATIN SMALL LETTER I WITH CIRCUMFLEX
+0xe5	U+00ef	# LATIN SMALL LETTER I WITH DIAERESIS
+0xe6	U+00f0	# LATIN SMALL LETTER ETH
+0xe7	U+00f1	# LATIN SMALL LETTER N WITH TILDE
+0xe8	U+0141	# LATIN CAPITAL LETTER L WITH STROKE
+0xe9	U+00d8	# LATIN CAPITAL LETTER O WITH STROKE
+0xea	U+0152	# LATIN CAPITAL LIGATURE OE
+0xeb	U+00ba	# MASCULINE ORDINAL INDICATOR
+0xec	U+00f2	# LATIN SMALL LETTER O WITH GRAVE
+0xed	U+00f3	# LATIN SMALL LETTER O WITH ACUTE
+0xee	U+00f4	# LATIN SMALL LETTER O WITH CIRCUMFLEX
+0xef	U+00f5	# LATIN SMALL LETTER O WITH TILDE
+0xf0	U+00f6	# LATIN SMALL LETTER O WITH DIAERESIS
+0xf1	U+00e6	# LATIN SMALL LETTER AE
+0xf2	U+00f9	# LATIN SMALL LETTER U WITH GRAVE
+0xf3	U+00fa	# LATIN SMALL LETTER U WITH ACUTE
+0xf4	U+00fb	# LATIN SMALL LETTER U WITH CIRCUMFLEX
+0xf5	U+0131	# LATIN SMALL LETTER DOTLESS I
+0xf6	U+00fc	# LATIN SMALL LETTER U WITH DIAERESIS
+0xf7	U+00fd	# LATIN SMALL LETTER Y WITH ACUTE
+0xf8	U+0142	# LATIN SMALL LETTER L WITH STROKE
+0xf9	U+00f8	# LATIN SMALL LETTER O WITH STROKE
+0xfa	U+0153	# LATIN SMALL LIGATURE OE
+0xfb	U+00df	# LATIN SMALL LETTER SHARP S
+0xfc	U+00fe	# LATIN SMALL LETTER THORN
+0xfd	U+00ff	# LATIN SMALL LETTER Y WITH DIAERESIS
+#0xfe	U+fffd	# .notdef, REPLACEMENT CHARACTER
+#0xff	U+fffd	# .notdef, REPLACEMENT CHARACTER
+
diff --git a/src/chrtrans/pt154_uni.tbl b/src/chrtrans/pt154_uni.tbl
new file mode 100644
index 00000000..0bacb527
--- /dev/null
+++ b/src/chrtrans/pt154_uni.tbl
@@ -0,0 +1,174 @@
+Mptcp154
+#
+OCyrillic-Asian (PT154)
+#
+C1540
+
+#####
+#
+# Charset aliases:
+# csPTCP154
+# PT154
+# CP154
+# Cyrillic-Asian
+#
+# Suitability for use in MIME text:
+# Yes
+#
+# ISO 10646 equivalency table:
+#    Format: Three tab-separated columns
+#        Column #1 is the Paratype CP154 code (in hex)
+#        Column #2 is the Unicode (in hex as 0xXXXX)
+#        Column #3 is the Unicode name (follows a comment sign, '#')
+#
+#    The entries are in Paratype CP154 order
+#
+#####
+
+0x20-0x7e		 idem
+#
+0x80  U+0496     #       CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER
+0x81  U+0492     #       CYRILLIC CAPITAL LETTER GHE WITH STROKE
+0x82  U+04EE     #       CYRILLIC CAPITAL LETTER U WITH MACRON
+0x83  U+0493     #       CYRILLIC SMALL LETTER GHE WITH STROKE
+0x84  U+201E     #       DOUBLE LOW-9 QUOTATION MARK
+0x85  U+2026     #       HORIZONTAL ELLIPSIS
+0x86  U+04B6     #       CYRILLIC CAPITAL LETTER CHE WITH DESCENDER
+0x87  U+04AE     #       CYRILLIC CAPITAL LETTER STRAIGHT U
+0x88  U+04B2     #       CYRILLIC CAPITAL LETTER HA WITH DESCENDER
+0x89  U+04AF     #       CYRILLIC SMALL LETTER STRAIGHT U
+0x8a  U+04A0     #       CYRILLIC CAPITAL LETTER BASHKIR KA
+0x8b  U+04E2     #       CYRILLIC CAPITAL LETTER I WITH MACRON
+0x8c  U+04A2     #       CYRILLIC CAPITAL LETTER EN WITH DESCENDER
+0x8d  U+049A     #       CYRILLIC CAPITAL LETTER KA WITH DESCENDER
+0x8e  U+04BA     #       CYRILLIC CAPITAL LETTER SHHA
+0x8f  U+04B8     #       CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE
+0x90  U+0497     #       CYRILLIC SMALL LETTER ZHE WITH DESCENDER
+0x91  U+2018     #       LEFT SINGLE QUOTATION MARK
+0x92  U+2019     #       RIGHT SINGLE QUOTATION MARK
+0x93  U+201C     #       LEFT DOUBLE QUOTATION MARK
+0x94  U+201D     #       RIGHT DOUBLE QUOTATION MARK
+0x95  U+2022     #       BULLET
+0x96  U+2013     #       EN DASH
+0x97  U+2014     #       EM DASH
+0x98  U+04B3     #       CYRILLIC SMALL LETTER HA WITH DESCENDER
+0x99  U+04B7     #       CYRILLIC SMALL LETTER CHE WITH DESCENDER
+0x9a  U+04A1     #       CYRILLIC SMALL LETTER BASHKIR KA
+0x9b  U+04E3     #       CYRILLIC SMALL LETTER I WITH MACRON
+0x9c  U+04A3     #       CYRILLIC SMALL LETTER EN WITH DESCENDER
+0x9d  U+049B     #       CYRILLIC SMALL LETTER KA WITH DESCENDER
+0x9e  U+04BB     #       CYRILLIC SMALL LETTER SHHA
+0x9f  U+04B9     #       CYRILLIC SMALL LETTER CHE WITH VERTICAL STROKE
+0xa0  U+00A0     #       NO-BREAK SPACE
+0xa1  U+040E     #       CYRILLIC CAPITAL LETTER SHORT U (Byelorussian)
+0xa2  U+045E     #       CYRILLIC SMALL LETTER SHORT U (Byelorussian)
+0xa3  U+0408     #       CYRILLIC CAPITAL LETTER JE
+0xa4  U+04E8     #       CYRILLIC CAPITAL LETTER BARRED O
+0xa5  U+0498     #       CYRILLIC CAPITAL LETTER ZE WITH DESCENDER
+0xa6  U+04B0     #       CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE
+0xa7  U+00A7     #       SECTION SIGN
+0xa8  U+0401     #       CYRILLIC CAPITAL LETTER IO
+0xa9  U+00A9     #       COPYRIGHT SIGN
+0xaa  U+04D8     #       CYRILLIC CAPITAL LETTER SCHWA
+0xab  U+00AB     #       LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xac  U+00AC     #       NOT SIGN
+0xad  U+04EF     #       CYRILLIC SMALL LETTER U WITH MACRON
+0xae  U+00AE     #       REGISTERED SIGN
+0xaf  U+049C     #       CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE
+0xb0  U+00B0     #       DEGREE SIGN
+0xb1  U+04B1     #       CYRILLIC SMALL LETTER STRAIGHT U WITH STROKE
+0xb2  U+0406     #       CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+0xb3  U+0456     #       CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+0xb4  U+0499     #       CYRILLIC SMALL LETTER ZE WITH DESCENDER
+0xb5  U+04E9     #       CYRILLIC SMALL LETTER BARRED O
+0xb6  U+00B6     #       PILCROW SIGN
+0xb7  U+00B7     #       MIDDLE DOT
+0xb8  U+0451     #       CYRILLIC SMALL LETTER IO
+0xb9  U+2116     #       NUMERO SIGN
+0xba  U+04D9     #       CYRILLIC SMALL LETTER SCHWA
+0xbb  U+00BB     #       RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+0xbc  U+0458     #       CYRILLIC SMALL LETTER JE
+0xbd  U+04AA     #       CYRILLIC CAPITAL LETTER ES WITH DESCENDER
+0xbe  U+04AB     #       CYRILLIC SMALL LETTER ES WITH DESCENDER
+0xbf  U+049D     #       CYRILLIC SMALL LETTER KA WITH VERTICAL STROKE
+0xc0  U+0410     #       CYRILLIC CAPITAL LETTER A
+0xc1  U+0411     #       CYRILLIC CAPITAL LETTER BE
+0xc2  U+0412     #       CYRILLIC CAPITAL LETTER VE
+0xc3  U+0413     #       CYRILLIC CAPITAL LETTER GHE
+0xc4  U+0414     #       CYRILLIC CAPITAL LETTER DE
+0xc5  U+0415     #       CYRILLIC CAPITAL LETTER IE
+0xc6  U+0416     #       CYRILLIC CAPITAL LETTER ZHE
+0xc7  U+0417     #       CYRILLIC CAPITAL LETTER ZE
+0xc8  U+0418     #       CYRILLIC CAPITAL LETTER I
+0xc9  U+0419     #       CYRILLIC CAPITAL LETTER SHORT I
+0xca  U+041A     #       CYRILLIC CAPITAL LETTER KA
+0xcb  U+041B     #       CYRILLIC CAPITAL LETTER EL
+0xcc  U+041C     #       CYRILLIC CAPITAL LETTER EM
+0xcd  U+041D     #       CYRILLIC CAPITAL LETTER EN
+0xce  U+041E     #       CYRILLIC CAPITAL LETTER O
+0xcf  U+041F     #       CYRILLIC CAPITAL LETTER PE
+0xd0  U+0420     #       CYRILLIC CAPITAL LETTER ER
+0xd1  U+0421     #       CYRILLIC CAPITAL LETTER ES
+0xd2  U+0422     #       CYRILLIC CAPITAL LETTER TE
+0xd3  U+0423     #       CYRILLIC CAPITAL LETTER U
+0xd4  U+0424     #       CYRILLIC CAPITAL LETTER EF
+0xd5  U+0425     #       CYRILLIC CAPITAL LETTER HA
+0xd6  U+0426     #       CYRILLIC CAPITAL LETTER TSE
+0xd7  U+0427     #       CYRILLIC CAPITAL LETTER CHE
+0xd8  U+0428     #       CYRILLIC CAPITAL LETTER SHA
+0xd9  U+0429     #       CYRILLIC CAPITAL LETTER SHCHA
+0xda  U+042A     #       CYRILLIC CAPITAL LETTER HARD SIGN
+0xdb  U+042B     #       CYRILLIC CAPITAL LETTER YERU
+0xdc  U+042C     #       CYRILLIC CAPITAL LETTER SOFT SIGN
+0xdd  U+042D     #       CYRILLIC CAPITAL LETTER E
+0xde  U+042E     #       CYRILLIC CAPITAL LETTER YU
+0xdf  U+042F     #       CYRILLIC CAPITAL LETTER YA
+0xe0  U+0430     #       CYRILLIC SMALL LETTER A
+0xe1  U+0431     #       CYRILLIC SMALL LETTER BE
+0xe2  U+0432     #       CYRILLIC SMALL LETTER VE
+0xe3  U+0433     #       CYRILLIC SMALL LETTER GHE
+0xe4  U+0434     #       CYRILLIC SMALL LETTER DE
+0xe5  U+0435     #       CYRILLIC SMALL LETTER IE
+0xe6  U+0436     #       CYRILLIC SMALL LETTER ZHE
+0xe7  U+0437     #       CYRILLIC SMALL LETTER ZE
+0xe8  U+0438     #       CYRILLIC SMALL LETTER I
+0xe9  U+0439     #       CYRILLIC SMALL LETTER SHORT I
+0xea  U+043A     #       CYRILLIC SMALL LETTER KA
+0xeb  U+043B     #       CYRILLIC SMALL LETTER EL
+0xec  U+043C     #       CYRILLIC SMALL LETTER EM
+0xed  U+043D     #       CYRILLIC SMALL LETTER EN
+0xee  U+043E     #       CYRILLIC SMALL LETTER O
+0xef  U+043F     #       CYRILLIC SMALL LETTER PE
+0xf0  U+0440     #       CYRILLIC SMALL LETTER ER
+0xf1  U+0441     #       CYRILLIC SMALL LETTER ES
+0xf2  U+0442     #       CYRILLIC SMALL LETTER TE
+0xf3  U+0443     #       CYRILLIC SMALL LETTER U
+0xf4  U+0444     #       CYRILLIC SMALL LETTER EF
+0xf5  U+0445     #       CYRILLIC SMALL LETTER HA
+0xf6  U+0446     #       CYRILLIC SMALL LETTER TSE
+0xf7  U+0447     #       CYRILLIC SMALL LETTER CHE
+0xf8  U+0448     #       CYRILLIC SMALL LETTER SHA
+0xf9  U+0449     #       CYRILLIC SMALL LETTER SHCHA
+0xfa  U+044A     #       CYRILLIC SMALL LETTER HARD SIGN
+0xfb  U+044B     #       CYRILLIC SMALL LETTER YERU
+0xfc  U+044C     #       CYRILLIC SMALL LETTER SOFT SIGN
+0xfd  U+044D     #       CYRILLIC SMALL LETTER E
+0xfe  U+044E     #       CYRILLIC SMALL LETTER YU
+0xff  U+044F     #       CYRILLIC SMALL LETTER YA
+
+#####
+#
+# Additional information:
+# This charset based on CP1251 with added asian cyrillic symbols.
+#
+# Person & email address to contact for further information:
+# Alexander Uskov
+# InternetDataCenter of KazakhTelecom.
+# e-mail: auskov@idc.kz
+#
+# Intended usage:
+# COMMON
+#
+# (record created 2002-09-27)
+#
+#####
diff --git a/src/chrtrans/rfc_suni.tbl b/src/chrtrans/rfc_suni.tbl
new file mode 100644
index 00000000..65fa17ed
--- /dev/null
+++ b/src/chrtrans/rfc_suni.tbl
@@ -0,0 +1,1958 @@
+# Generated from the mnemonic file found under ftp://dkuug.dk/i18n/
+# then hand-tweaked
+# perl -n -e \
+# 'if (s|<([^ \t]+)>\s+<U([\dA-Z]{4})>\s.*$|U+\L\2\E:\1|) {s|/?(.)|\1|g&&print}'\
+# mnemonic,ds
+
+#The MIME name of this charset.
+Mmnemonic+ascii+0
+
+#Name as a Display Charset (used on Options screen)
+ORFC 1345 w/o Intro
+
+# Don't fall back to default table for unicode -> 8bit
+Fallback NO
+
+U+0020:SP
+U+0021:!
+U+0022:"
+U+0023:Nb
+U+0024:DO
+U+0025:%
+U+0026:&
+U+0027:'
+U+0028:(
+U+0029:)
+U+002a:*
+U+002b:+
+U+002c:,
+U+002d:-
+U+002e:.
+U+002f:/
+U+0030:0
+U+0031:1
+U+0032:2
+U+0033:3
+U+0034:4
+U+0035:5
+U+0036:6
+U+0037:7
+U+0038:8
+U+0039:9
+U+003a::
+U+003b:;
+U+003c:<
+U+003d:=
+U+003e:>
+U+003f:?
+U+0040:At
+U+0041:A
+U+0042:B
+U+0043:C
+U+0044:D
+U+0045:E
+U+0046:F
+U+0047:G
+U+0048:H
+U+0049:I
+U+004a:J
+U+004b:K
+U+004c:L
+U+004d:M
+U+004e:N
+U+004f:O
+U+0050:P
+U+0051:Q
+U+0052:R
+U+0053:S
+U+0054:T
+U+0055:U
+U+0056:V
+U+0057:W
+U+0058:X
+U+0059:Y
+U+005a:Z
+U+005b:<(
+U+005c://
+U+005d:)>
+U+005e:'>
+U+005f:_
+U+0060:'!
+U+0061:a
+U+0062:b
+U+0063:c
+U+0064:d
+U+0065:e
+U+0066:f
+U+0067:g
+U+0068:h
+U+0069:i
+U+006a:j
+U+006b:k
+U+006c:l
+U+006d:m
+U+006e:n
+U+006f:o
+U+0070:p
+U+0071:q
+U+0072:r
+U+0073:s
+U+0074:t
+U+0075:u
+U+0076:v
+U+0077:w
+U+0078:x
+U+0079:y
+U+007a:z
+U+007b:(!
+U+007c:!!
+U+007d:!)
+U+007e:'?
+U+00a0:NS
+U+00a1:!I
+U+00a2:Ct
+U+00a3:Pd
+U+00a4:Cu
+U+00a5:Ye
+U+00a6:BB
+U+00a7:SE
+U+00a8:':
+U+00a9:Co
+U+00aa:-a
+U+00ab:<<
+U+00ac:NO
+U+00ad:--
+U+00ae:Rg
+U+00af:'m
+U+00b0:DG
+U+00b1:+-
+U+00b2:2S
+U+00b3:3S
+U+00b4:''
+U+00b5:My
+U+00b6:PI
+U+00b7:.M
+U+00b8:',
+U+00b9:1S
+U+00ba:-o
+U+00bb:>>
+U+00bc:14
+U+00bd:12
+U+00be:34
+U+00bf:?I
+U+00c0:A!
+U+00c1:A'
+U+00c2:A>
+U+00c3:A?
+U+00c4:A:
+U+00c5:AA
+U+00c6:AE
+U+00c7:C,
+U+00c8:E!
+U+00c9:E'
+U+00ca:E>
+U+00cb:E:
+U+00cc:I!
+U+00cd:I'
+U+00ce:I>
+U+00cf:I:
+U+00d0:D-
+U+00d1:N?
+U+00d2:O!
+U+00d3:O'
+U+00d4:O>
+U+00d5:O?
+U+00d6:O:
+U+00d7:*X
+U+00d8:O/
+U+00d9:U!
+U+00da:U'
+U+00db:U>
+U+00dc:U:
+U+00dd:Y'
+U+00de:TH
+U+00df:ss
+U+00e0:a!
+U+00e1:a'
+U+00e2:a>
+U+00e3:a?
+U+00e4:a:
+U+00e5:aa
+U+00e6:ae
+U+00e7:c,
+U+00e8:e!
+U+00e9:e'
+U+00ea:e>
+U+00eb:e:
+U+00ec:i!
+U+00ed:i'
+U+00ee:i>
+U+00ef:i:
+U+00f0:d-
+U+00f1:n?
+U+00f2:o!
+U+00f3:o'
+U+00f4:o>
+U+00f5:o?
+U+00f6:o:
+U+00f7:-:
+U+00f8:o/
+U+00f9:u!
+U+00fa:u'
+U+00fb:u>
+U+00fc:u:
+U+00fd:y'
+U+00fe:th
+U+00ff:y:
+U+0100:A-
+U+0101:a-
+U+0102:A(
+U+0103:a(
+U+0104:A;
+U+0105:a;
+U+0106:C'
+U+0107:c'
+U+0108:C>
+U+0109:c>
+U+010a:C.
+U+010b:c.
+U+010c:C<
+U+010d:c<
+U+010e:D<
+U+010f:d<
+U+0110:D/
+U+0111:d/
+U+0112:E-
+U+0113:e-
+U+0114:E(
+U+0115:e(
+U+0116:E.
+U+0117:e.
+U+0118:E;
+U+0119:e;
+U+011a:E<
+U+011b:e<
+U+011c:G>
+U+011d:g>
+U+011e:G(
+U+011f:g(
+U+0120:G.
+U+0121:g.
+U+0122:G,
+U+0123:g,
+U+0124:H>
+U+0125:h>
+U+0126:H/
+U+0127:h/
+U+0128:I?
+U+0129:i?
+U+012a:I-
+U+012b:i-
+U+012c:I(
+U+012d:i(
+U+012e:I;
+U+012f:i;
+U+0130:I.
+U+0131:i.
+U+0132:IJ
+U+0133:ij
+U+0134:J>
+U+0135:j>
+U+0136:K,
+U+0137:k,
+U+0138:kk
+U+0139:L'
+U+013a:l'
+U+013b:L,
+U+013c:l,
+U+013d:L<
+U+013e:l<
+U+013f:L.
+U+0140:l.
+U+0141:L/
+U+0142:l/
+U+0143:N'
+U+0144:n'
+U+0145:N,
+U+0146:n,
+U+0147:N<
+U+0148:n<
+U+0149:'n
+U+014a:NG
+U+014b:ng
+U+014c:O-
+U+014d:o-
+U+014e:O(
+U+014f:o(
+U+0150:O"
+U+0151:o"
+U+0152:OE
+U+0153:oe
+U+0154:R'
+U+0155:r'
+U+0156:R,
+U+0157:r,
+U+0158:R<
+U+0159:r<
+U+015a:S'
+U+015b:s'
+U+015c:S>
+U+015d:s>
+U+015e:S,
+U+015f:s,
+U+0160:S<
+U+0161:s<
+U+0162:T,
+U+0163:t,
+U+0164:T<
+U+0165:t<
+U+0166:T/
+U+0167:t/
+U+0168:U?
+U+0169:u?
+U+016a:U-
+U+016b:u-
+U+016c:U(
+U+016d:u(
+U+016e:U0
+U+016f:u0
+U+0170:U"
+U+0171:u"
+U+0172:U;
+U+0173:u;
+U+0174:W>
+U+0175:w>
+U+0176:Y>
+U+0177:y>
+U+0178:Y:
+U+0179:Z'
+U+017a:z'
+U+017b:Z.
+U+017c:z.
+U+017d:Z<
+U+017e:z<
+U+017f:s1
+U+0187:C2
+U+0188:c2
+U+0191:F2
+U+0192:f2
+U+0198:K2
+U+0199:k2
+U+01a0:O9
+U+01a1:o9
+U+01a2:OI
+U+01a3:oi
+U+01a6:yr
+U+01af:U9
+U+01b0:u9
+U+01b5:Z/
+U+01b6:z/
+U+01b7:ED
+U+01cd:A<
+U+01ce:a<
+U+01cf:I<
+U+01d0:i<
+U+01d1:O<
+U+01d2:o<
+U+01d3:U<
+U+01d4:u<
+U+01d5:U:-
+U+01d6:u:-
+U+01d7:U:'
+U+01d8:u:'
+U+01d9:U:<
+U+01da:u:<
+U+01db:U:!
+U+01dc:u:!
+U+01de:A1
+U+01df:a1
+U+01e0:A7
+U+01e1:a7
+U+01e2:A3
+U+01e3:a3
+U+01e4:G/
+U+01e5:g/
+U+01e6:G<
+U+01e7:g<
+U+01e8:K<
+U+01e9:k<
+U+01ea:O;
+U+01eb:o;
+U+01ec:O1
+U+01ed:o1
+U+01ee:EZ
+U+01ef:ez
+U+01f0:j<
+U+01f4:G'
+U+01f5:g'
+U+01fa:AA'
+U+01fb:aa'
+U+01fc:AE'
+U+01fd:ae'
+U+01fe:O/'
+U+01ff:o/'
+U+0200:A!!
+U+0201:a!!
+U+0202:A)
+U+0203:a)
+U+0204:E!!
+U+0205:e!!
+U+0206:E)
+U+0207:e)
+U+0208:I!!
+U+0209:i!!
+U+020a:I)
+U+020b:i)
+U+020c:O!!
+U+020d:o!!
+U+020e:O)
+U+020f:o)
+U+0210:R!!
+U+0211:r!!
+U+0212:R)
+U+0213:r)
+U+0214:U!!
+U+0215:u!!
+U+0216:U)
+U+0217:u)
+U+0292:ed
+U+02bb:;S
+U+02c6:1>
+U+02c7:'<
+U+02c9:1-
+U+02cb:1!
+U+02d8:'(
+U+02d9:'.
+U+02da:'0
+U+02db:';
+U+02dc:1?
+U+02dd:'"
+U+0374:'G
+U+0375:,G
+U+037a:j3
+U+037e:?%
+U+0384:'*
+U+0385:'%
+U+0386:A%
+U+0387:.*
+U+0388:E%
+U+0389:Y%
+U+038a:I%
+U+038c:O%
+U+038e:U%
+U+038f:W%
+U+0390:i3
+U+0391:A*
+U+0392:B*
+U+0393:G*
+U+0394:D*
+U+0395:E*
+U+0396:Z*
+U+0397:Y*
+U+0398:H*
+U+0399:I*
+U+039a:K*
+U+039b:L*
+U+039c:M*
+U+039d:N*
+U+039e:C*
+U+039f:O*
+U+03a0:P*
+U+03a1:R*
+U+03a3:S*
+U+03a4:T*
+U+03a5:U*
+U+03a6:F*
+U+03a7:X*
+U+03a8:Q*
+U+03a9:W*
+U+03aa:J*
+U+03ab:V*
+U+03ac:a%
+U+03ad:e%
+U+03ae:y%
+U+03af:i%
+U+03b0:u3
+U+03b1:a*
+U+03b2:b*
+U+03b3:g*
+U+03b4:d*
+U+03b5:e*
+U+03b6:z*
+U+03b7:y*
+U+03b8:h*
+U+03b9:i*
+U+03ba:k*
+U+03bb:l*
+U+03bc:m*
+U+03bd:n*
+U+03be:c*
+U+03bf:o*
+U+03c0:p*
+U+03c1:r*
+U+03c2:*s
+U+03c3:s*
+U+03c4:t*
+U+03c5:u*
+U+03c6:f*
+U+03c7:x*
+U+03c8:q*
+U+03c9:w*
+U+03ca:j*
+U+03cb:v*
+U+03cc:o%
+U+03cd:u%
+U+03ce:w%
+U+03d0:b3
+U+03da:T3
+U+03db:t3
+U+03dc:M3
+U+03dd:m3
+U+03de:K3
+U+03df:k3
+U+03e0:P3
+U+03e1:p3
+U+0401:IO
+U+0402:D%
+U+0403:G%
+U+0404:IE
+U+0405:DS
+U+0406:II
+U+0407:YI
+U+0408:J%
+U+0409:LJ
+U+040a:NJ
+U+040b:Ts
+U+040c:KJ
+U+040e:V%
+U+040f:DZ
+U+0410:A=
+U+0411:B=
+U+0412:V=
+U+0413:G=
+U+0414:D=
+U+0415:E=
+U+0416:Z%
+U+0417:Z=
+U+0418:I=
+U+0419:J=
+U+041a:K=
+U+041b:L=
+U+041c:M=
+U+041d:N=
+U+041e:O=
+U+041f:P=
+U+0420:R=
+U+0421:S=
+U+0422:T=
+U+0423:U=
+U+0424:F=
+U+0425:H=
+U+0426:C=
+U+0427:C%
+U+0428:S%
+U+0429:Sc
+U+042a:="
+U+042b:Y=
+U+042c:%"
+U+042d:JE
+U+042e:JU
+U+042f:JA
+U+0430:a=
+U+0431:b=
+U+0432:v=
+U+0433:g=
+U+0434:d=
+U+0435:e=
+U+0436:z%
+U+0437:z=
+U+0438:i=
+U+0439:j=
+U+043a:k=
+U+043b:l=
+U+043c:m=
+U+043d:n=
+U+043e:o=
+U+043f:p=
+U+0440:r=
+U+0441:s=
+U+0442:t=
+U+0443:u=
+U+0444:f=
+U+0445:h=
+U+0446:c=
+U+0447:c%
+U+0448:s%
+U+0449:sc
+U+044a:='
+U+044b:y=
+U+044c:%'
+U+044d:je
+U+044e:ju
+U+044f:ja
+U+0451:io
+U+0452:d%
+U+0453:g%
+U+0454:ie
+U+0455:ds
+U+0456:ii
+U+0457:yi
+U+0458:j%
+U+0459:lj
+U+045a:nj
+U+045b:ts
+U+045c:kj
+U+045e:v%
+U+045f:dz
+U+0462:Y3
+U+0463:y3
+U+046a:O3
+U+046b:o3
+U+0472:F3
+U+0473:f3
+U+0474:V3
+U+0475:v3
+U+0480:C3
+U+0481:c3
+U+0490:G3
+U+0491:g3
+U+05d0:A+
+U+05d1:B+
+U+05d2:G+
+U+05d3:D+
+U+05d4:H+
+U+05d5:W+
+U+05d6:Z+
+U+05d7:X+
+U+05d8:Tj
+U+05d9:J+
+U+05da:K%
+U+05db:K+
+U+05dc:L+
+U+05dd:M%
+U+05de:M+
+U+05df:N%
+U+05e0:N+
+U+05e1:S+
+U+05e2:E+
+U+05e3:P%
+U+05e4:P+
+U+05e5:Zj
+U+05e6:ZJ
+U+05e7:Q+
+U+05e8:R+
+U+05e9:Sh
+U+05ea:T+
+U+060c:,+
+U+061b:;+
+U+061f:?+
+U+0621:H'
+U+0622:aM
+U+0623:aH
+U+0624:wH
+U+0625:ah
+U+0626:yH
+U+0627:a+
+U+0628:b+
+U+0629:tm
+U+062a:t+
+U+062b:tk
+U+062c:g+
+U+062d:hk
+U+062e:x+
+U+062f:d+
+U+0630:dk
+U+0631:r+
+U+0632:z+
+U+0633:s+
+U+0634:sn
+U+0635:c+
+U+0636:dd
+U+0637:tj
+U+0638:zH
+U+0639:e+
+U+063a:i+
+U+0640:++
+U+0641:f+
+U+0642:q+
+U+0643:k+
+U+0644:l+
+U+0645:m+
+U+0646:n+
+U+0647:h+
+U+0648:w+
+U+0649:j+
+U+064a:y+
+U+064b::+
+U+064c:"+
+U+064d:=+
+U+064e:/+
+U+064f:'+
+U+0650:1+
+U+0651:3+
+U+0652:0+
+U+0660:0a
+U+0661:1a
+U+0662:2a
+U+0663:3a
+U+0664:4a
+U+0665:5a
+U+0666:6a
+U+0667:7a
+U+0668:8a
+U+0669:9a
+U+0670:aS
+U+067e:p+
+U+0681:hH
+U+0686:tc
+U+0698:zj
+U+06a4:v+
+U+06af:gf
+U+1e00:A-0
+U+1e01:a-0
+U+1e02:B.
+U+1e03:b.
+U+1e04:B-.
+U+1e05:b-.
+U+1e06:B_
+U+1e07:b_
+U+1e08:C,'
+U+1e09:c,'
+U+1e0a:D.
+U+1e0b:d.
+U+1e0c:D-.
+U+1e0d:d-.
+U+1e0e:D_
+U+1e0f:d_
+U+1e10:D,
+U+1e11:d,
+U+1e12:D->
+U+1e13:d->
+U+1e14:E-!
+U+1e15:e-!
+U+1e16:E-'
+U+1e17:e-'
+U+1e18:E->
+U+1e19:e->
+U+1e1a:E-?
+U+1e1b:e-?
+U+1e1c:E,(
+U+1e1d:e,(
+U+1e1e:F.
+U+1e1f:f.
+U+1e20:G-
+U+1e21:g-
+U+1e22:H.
+U+1e23:h.
+U+1e24:H-.
+U+1e25:h-.
+U+1e26:H:
+U+1e27:h:
+U+1e28:H,
+U+1e29:h,
+U+1e2a:H-(
+U+1e2b:h-(
+U+1e2c:I-?
+U+1e2d:i-?
+U+1e2e:I:'
+U+1e2f:i:'
+U+1e30:K'
+U+1e31:k'
+U+1e32:K-.
+U+1e33:k-.
+U+1e34:K_
+U+1e35:k_
+U+1e36:L-.
+U+1e37:l-.
+U+1e38:L--.
+U+1e39:l--.
+U+1e3a:L_
+U+1e3b:l_
+U+1e3c:L->
+U+1e3d:l->
+U+1e3e:M'
+U+1e3f:m'
+U+1e40:M.
+U+1e41:m.
+U+1e42:M-.
+U+1e43:m-.
+U+1e44:N.
+U+1e45:n.
+U+1e46:N-.
+U+1e47:n-.
+U+1e48:N_
+U+1e49:n_
+U+1e4a:N->
+U+1e4b:n->
+U+1e4c:O?'
+U+1e4d:o?'
+U+1e4e:O?:
+U+1e4f:o?:
+U+1e50:O-!
+U+1e51:o-!
+U+1e52:O-'
+U+1e53:o-'
+U+1e54:P'
+U+1e55:p'
+U+1e56:P.
+U+1e57:p.
+U+1e58:R.
+U+1e59:r.
+U+1e5a:R-.
+U+1e5b:r-.
+U+1e5c:R--.
+U+1e5d:r--.
+U+1e5e:R_
+U+1e5f:r_
+U+1e60:S.
+U+1e61:s.
+U+1e62:S-.
+U+1e63:s-.
+U+1e64:S'.
+U+1e65:s'.
+U+1e66:S<.
+U+1e67:s<.
+U+1e68:S.-.
+U+1e69:s.-.
+U+1e6a:T.
+U+1e6b:t.
+U+1e6c:T-.
+U+1e6d:t-.
+U+1e6e:T_
+U+1e6f:t_
+U+1e70:T->
+U+1e71:t->
+U+1e72:U--:
+U+1e73:u--:
+U+1e74:U-?
+U+1e75:u-?
+U+1e76:U->
+U+1e77:u->
+U+1e78:U?'
+U+1e79:u?'
+U+1e7a:U-:
+U+1e7b:u-:
+U+1e7c:V?
+U+1e7d:v?
+U+1e7e:V-.
+U+1e7f:v-.
+U+1e80:W!
+U+1e81:w!
+U+1e82:W'
+U+1e83:w'
+U+1e84:W:
+U+1e85:w:
+U+1e86:W.
+U+1e87:w.
+U+1e88:W-.
+U+1e89:w-.
+U+1e8a:X.
+U+1e8b:x.
+U+1e8c:X:
+U+1e8d:x:
+U+1e8e:Y.
+U+1e8f:y.
+U+1e90:Z>
+U+1e91:z>
+U+1e92:Z-.
+U+1e93:z-.
+U+1e94:Z_
+U+1e95:z_
+U+1e96:h_
+U+1e97:t:
+U+1e98:w0
+U+1e99:y0
+U+1ea0:A-.
+U+1ea1:a-.
+U+1ea2:A2
+U+1ea3:a2
+U+1ea4:A>'
+U+1ea5:a>'
+U+1ea6:A>!
+U+1ea7:a>!
+U+1ea8:A>2
+U+1ea9:a>2
+U+1eaa:A>?
+U+1eab:a>?
+U+1eac:A>-.
+U+1ead:a>-.
+U+1eae:A('
+U+1eaf:a('
+U+1eb0:A(!
+U+1eb1:a(!
+U+1eb2:A(2
+U+1eb3:a(2
+U+1eb4:A(?
+U+1eb5:a(?
+U+1eb6:A(-.
+U+1eb7:a(-.
+U+1eb8:E-.
+U+1eb9:e-.
+U+1eba:E2
+U+1ebb:e2
+U+1ebc:E?
+U+1ebd:e?
+U+1ebe:E>'
+U+1ebf:e>'
+U+1ec0:E>!
+U+1ec1:e>!
+U+1ec2:E>2
+U+1ec3:e>2
+U+1ec4:E>?
+U+1ec5:e>?
+U+1ec6:E>-.
+U+1ec7:e>-.
+U+1ec8:I2
+U+1ec9:i2
+U+1eca:I-.
+U+1ecb:i-.
+U+1ecc:O-.
+U+1ecd:o-.
+U+1ece:O2
+U+1ecf:o2
+U+1ed0:O>'
+U+1ed1:o>'
+U+1ed2:O>!
+U+1ed3:o>!
+U+1ed4:O>2
+U+1ed5:o>2
+U+1ed6:O>?
+U+1ed7:o>?
+U+1ed8:O>-.
+U+1ed9:o>-.
+U+1eda:O9'
+U+1edb:o9'
+U+1edc:O9!
+U+1edd:o9!
+U+1ede:O92
+U+1edf:o92
+U+1ee0:O9?
+U+1ee1:o9?
+U+1ee2:O9-.
+U+1ee3:o9-.
+U+1ee4:U-.
+U+1ee5:u-.
+U+1ee6:U2
+U+1ee7:u2
+U+1ee8:U9'
+U+1ee9:u9'
+U+1eea:U9!
+U+1eeb:u9!
+U+1eec:U92
+U+1eed:u92
+U+1eee:U9?
+U+1eef:u9?
+U+1ef0:U9-.
+U+1ef1:u9-.
+U+1ef2:Y!
+U+1ef3:y!
+U+1ef4:Y-.
+U+1ef5:y-.
+U+1ef6:Y2
+U+1ef7:y2
+U+1ef8:Y?
+U+1ef9:y?
+U+1fbf:,,
+U+1fc0:?*
+U+1fc1:?:
+U+1fcd:,!
+U+1fce:,'
+U+1fcf:?,
+U+1fdd:;!
+U+1fde:;'
+U+1fdf:?;
+U+1fed:!:
+U+1fef:!*
+U+1ffe:;;
+U+2002:1N
+U+2003:1M
+U+2004:3M
+U+2005:4M
+U+2006:6M
+U+200e:LR
+U+200f:RL
+U+2009:1T
+U+200a:1H
+U+2010:-1
+U+2013:-N
+U+2014:-M
+U+2015:-3
+U+2016:!2
+U+2017:=2
+U+2018:'6
+U+2019:'9
+U+201a:.9
+U+201b:9'
+U+201c:"6
+U+201d:"9
+U+201e::9
+U+201f:9"
+U+2020:/-
+U+2021:/=
+U+2022:Sb
+U+2025:..
+U+2026:.3
+U+2030:%0
+U+2032:1'
+U+2033:2'
+U+2034:3'
+U+2035:1"
+U+2036:2"
+U+2037:3"
+U+2038:Ca
+U+2039:<1
+U+203a:>1
+U+203b::X
+U+203c:!*2
+U+203e:'-
+U+2044:/f
+U+2070:0S
+U+2074:4S
+U+2075:5S
+U+2076:6S
+U+2077:7S
+U+2078:8S
+U+2079:9S
+U+207a:+S
+U+207b:-S
+U+207c:=S
+U+207d:(S
+U+207e:)S
+U+207f:nS
+U+2080:0s
+U+2081:1s
+U+2082:2s
+U+2083:3s
+U+2084:4s
+U+2085:5s
+U+2086:6s
+U+2087:7s
+U+2088:8s
+U+2089:9s
+U+208a:+s
+U+208b:-s
+U+208c:=s
+U+208d:(s
+U+208e:)s
+U+20a3:Ff
+U+20a4:Li
+U+20a7:Pt
+U+20a9:W=
+U+2103:oC
+U+2105:co
+U+2109:oF
+U+2116:N0
+U+2117:PO
+U+211e:Rx
+U+2120:SM
+U+2122:TM
+U+2126:Om
+U+212b:AO
+U+2153:13
+U+2154:23
+U+2155:15
+U+2156:25
+U+2157:35
+U+2158:45
+U+2159:16
+U+215a:56
+U+215b:18
+U+215c:38
+U+215d:58
+U+215e:78
+U+2160:1R
+U+2161:2R
+U+2162:3R
+U+2163:4R
+U+2164:5R
+U+2165:6R
+U+2166:7R
+U+2167:8R
+U+2168:9R
+U+2169:aR
+U+216a:bR
+U+216b:cR
+U+216c:50R
+U+216d:100R
+U+216e:500R
+U+216f:1000R
+U+2170:1r
+U+2171:2r
+U+2172:3r
+U+2173:4r
+U+2174:5r
+U+2175:6r
+U+2176:7r
+U+2177:8r
+U+2178:9r
+U+2179:ar
+U+217a:br
+U+217b:cr
+U+217c:50r
+U+217d:100r
+U+217e:500r
+U+217f:1000r
+U+2180:1000RCD
+U+2181:5000R
+U+2182:10000R
+U+2190:<-
+U+2191:-!
+U+2192:->
+U+2193:-v
+U+2194:<>
+U+2195:UD
+U+2196:<!!
+U+2197://>
+U+2198:!!>
+U+2199:<//
+U+21a8:UD-
+U+21c0:>V
+U+21d0:<=
+U+21d2:=>
+U+21d4:==
+U+2200:FA
+U+2202:dP
+U+2203:TE
+U+2205:/0
+U+2206:DE
+U+2207:NB
+U+2208:(-
+U+220b:-)
+U+220f:*P
+U+2211:+Z
+U+2212:-2
+U+2213:-+
+U+2214:.+
+U+2217:*-
+U+2218:Ob
+U+2219:sb
+U+221a:RT
+U+221d:0(
+U+221e:00
+U+221f:-L
+U+2220:-V
+U+2225:PP
+U+2227:AN
+U+2228:OR
+U+2229:(U
+U+222a:)U
+U+222b:In
+U+222c:DI
+U+222e:Io
+U+2234:.:
+U+2235::.
+U+2236::R
+U+2237:::
+U+223c:?1
+U+223e:CG
+U+2243:?-
+U+2245:?=
+U+2248:?2
+U+224c:=?
+U+2253:HI
+U+2260:!=
+U+2261:=3
+U+2264:=<
+U+2265:>=
+U+226a:<*
+U+226b:*>
+U+226e:!<
+U+226f:!>
+U+2282:(C
+U+2283:)C
+U+2286:(_
+U+2287:)_
+U+2299:0.
+U+229a:02
+U+22a5:-T
+U+22c5:.P
+U+22ee::3
+U+2302:Eh
+U+2308:<7
+U+2309:>7
+U+230a:7<
+U+230b:7>
+U+2310:NI
+U+2312:(A
+U+2315:TR
+U+2318:88
+U+2320:Iu
+U+2321:Il
+U+2329:</
+U+232a:/>
+U+2423:Vs
+U+2440:1h
+U+2441:3h
+U+2442:2h
+U+2443:4h
+U+2446:1j
+U+2447:2j
+U+2448:3j
+U+2449:4j
+U+2460:1-o
+U+2461:2-o
+U+2462:3-o
+U+2463:4-o
+U+2464:5-o
+U+2465:6-o
+U+2466:7-o
+U+2467:8-o
+U+2468:9-o
+U+2469:10-o
+U+246a:11-o
+U+246b:12-o
+U+246c:13-o
+U+246d:14-o
+U+246e:15-o
+U+246f:16-o
+U+2470:17-o
+U+2471:18-o
+U+2472:19-o
+U+2473:20-o
+U+2474:(1)
+U+2475:(2)
+U+2476:(3)
+U+2477:(4)
+U+2478:(5)
+U+2479:(6)
+U+247a:(7)
+U+247b:(8)
+U+247c:(9)
+U+247d:(10)
+U+247e:(11)
+U+247f:(12)
+U+2480:(13)
+U+2481:(14)
+U+2482:(15)
+U+2483:(16)
+U+2484:(17)
+U+2485:(18)
+U+2486:(19)
+U+2487:(20)
+U+2488:1.
+U+2489:2.
+U+248a:3.
+U+248b:4.
+U+248c:5.
+U+248d:6.
+U+248e:7.
+U+248f:8.
+U+2490:9.
+U+2491:10.
+U+2492:11.
+U+2493:12.
+U+2494:13.
+U+2495:14.
+U+2496:15.
+U+2497:16.
+U+2498:17.
+U+2499:18.
+U+249a:19.
+U+249b:20.
+U+249c:(a)
+U+249d:(b)
+U+249e:(c)
+U+249f:(d)
+U+24a0:(e)
+U+24a1:(f)
+U+24a2:(g)
+U+24a3:(h)
+U+24a4:(i)
+U+24a5:(j)
+U+24a6:(k)
+U+24a7:(l)
+U+24a8:(m)
+U+24a9:(n)
+U+24aa:(o)
+U+24ab:(p)
+U+24ac:(q)
+U+24ad:(r)
+U+24ae:(s)
+U+24af:(t)
+U+24b0:(u)
+U+24b1:(v)
+U+24b2:(w)
+U+24b3:(x)
+U+24b4:(y)
+U+24b5:(z)
+U+24b6:A-o
+U+24b7:B-o
+U+24b8:C-o
+U+24b9:D-o
+U+24ba:E-o
+U+24bb:F-o
+U+24bc:G-o
+U+24bd:H-o
+U+24be:I-o
+U+24bf:J-o
+U+24c0:K-o
+U+24c1:L-o
+U+24c2:M-o
+U+24c3:N-o
+U+24c4:O-o
+U+24c5:P-o
+U+24c6:Q-o
+U+24c7:R-o
+U+24c8:S-o
+U+24c9:T-o
+U+24ca:U-o
+U+24cb:V-o
+U+24cc:W-o
+U+24cd:X-o
+U+24ce:Y-o
+U+24cf:Z-o
+U+24d0:a-o
+U+24d1:b-o
+U+24d2:c-o
+U+24d3:d-o
+U+24d4:e-o
+U+24d5:f-o
+U+24d6:g-o
+U+24d7:h-o
+U+24d8:i-o
+U+24d9:j-o
+U+24da:k-o
+U+24db:l-o
+U+24dc:m-o
+U+24dd:n-o
+U+24de:o-o
+U+24df:p-o
+U+24e0:q-o
+U+24e1:r-o
+U+24e2:s-o
+U+24e3:t-o
+U+24e4:u-o
+U+24e5:v-o
+U+24e6:w-o
+U+24e7:x-o
+U+24e8:y-o
+U+24e9:z-o
+U+24ea:0-o
+U+2500:hh
+U+2501:HH-
+U+2502:vv
+U+2503:VV-
+U+2504:3-
+U+2505:3_
+U+2506:3!
+U+2507:3/
+U+2508:4-
+U+2509:4_
+U+250a:4!
+U+250b:4/
+U+250c:dr
+U+250d:dR-
+U+250e:Dr-
+U+250f:DR-
+U+2510:dl
+U+2511:dL-
+U+2512:Dl-
+U+2513:LD-
+U+2514:ur
+U+2515:uR-
+U+2516:Ur-
+U+2517:UR-
+U+2518:ul
+U+2519:uL-
+U+251a:Ul-
+U+251b:UL-
+U+251c:vr
+U+251d:vR-
+U+251e:Udr
+U+251f:uDr
+U+2520:Vr-
+U+2521:UdR
+U+2522:uDR
+U+2523:VR-
+U+2524:vl
+U+2525:vL-
+U+2526:Udl
+U+2527:uDl
+U+2528:Vl-
+U+2529:UdL
+U+252a:uDL
+U+252b:VL-
+U+252c:dh
+U+252d:dLr
+U+252e:dlR
+U+252f:dH-
+U+2530:Dh-
+U+2531:DLr
+U+2532:DlR
+U+2533:DH-
+U+2534:uh
+U+2535:uLr
+U+2536:ulR
+U+2537:uH-
+U+2538:Uh-
+U+2539:ULr
+U+253a:UlR
+U+253b:UH-
+U+253c:vh
+U+253d:vLr
+U+253e:vlR
+U+253f:vH-
+U+2540:Udh
+U+2541:uDh
+U+2542:Vh-
+U+2543:UdLr
+U+2544:UdlR
+U+2545:uDLr
+U+2546:uDlR
+U+2547:UdH
+U+2548:uDH
+U+2549:VLr
+U+254a:VlR
+U+254b:VH-
+U+2550:HH
+U+2551:VV
+U+2552:dR
+U+2553:Dr
+U+2554:DR
+U+2555:dL
+U+2556:Dl
+U+2557:LD
+U+2558:uR
+U+2559:Ur
+U+255a:UR
+U+255b:uL
+U+255c:Ul
+U+255d:UL
+U+255e:vR
+U+255f:Vr
+U+2560:VR
+U+2561:vL
+U+2562:Vl
+U+2563:VL
+U+2564:dH
+U+2565:Dh
+U+2566:DH
+U+2567:uH
+U+2568:Uh
+U+2569:UH
+U+256a:vH
+U+256b:Vh
+U+256c:VH
+U+2571:FD
+U+2572:BD
+U+2580:TB
+U+2584:LB
+U+2588:FB
+U+258c:lB
+U+2590:RB
+U+2591:.S
+U+2592::S
+U+2593:?S
+U+25a0:fS
+U+25a1:OS
+U+25a2:RO
+U+25a3:Rr
+U+25a4:RF
+U+25a5:RY
+U+25a6:RH
+U+25a7:RZ
+U+25a8:RK
+U+25a9:RX
+U+25aa:sB
+U+25ac:SR
+U+25ad:Or
+U+25b2:UT
+U+25b3:uT
+U+25b7:Tr
+U+25ba:PR
+U+25bc:Dt
+U+25bd:dT
+U+25c1:Tl
+U+25c4:PL
+U+25c6:Db
+U+25c7:Dw
+U+25ca:LZ
+U+25cb:0m
+U+25ce:0o
+U+25cf:0M
+U+25d0:0L
+U+25d1:0R
+U+25d8:Sn
+U+25d9:Ic
+U+25e2:Fd
+U+25e3:Bd
+U+25ef:Ci
+U+2605:*2
+U+2606:*1
+U+260e:TEL
+U+260f:tel
+U+261c:<H
+U+261e:>H
+U+263a:0u
+U+263b:0U
+U+263c:SU
+U+2640:Fm
+U+2642:Ml
+U+2660:cS
+U+2661:cH
+U+2662:cD
+U+2663:cC
+U+2664:cS-
+U+2665:cH-
+U+2666:cD-
+U+2667:cC-
+U+2669:Md
+U+266a:M8
+U+266b:M2
+U+266c:M16
+U+266d:Mb
+U+266e:Mx
+U+266f:MX
+U+2713:OK
+U+2717:XX
+U+2720:-X
+U+3000:IS
+U+3001:,_
+U+3002:._
+U+3003:+"
+U+3004:JIS
+U+3005:*_
+U+3006:;_
+U+3007:0_
+U+300a:<+
+U+300b:>+
+U+300c:<'
+U+300d:>'
+U+300e:<"
+U+300f:>"
+U+3010:("
+U+3011:)"
+U+3012:=T
+U+3013:=_
+U+3014:('
+U+3015:)'
+U+3016:(I
+U+3017:)I
+U+301c:-?
+U+3020:=T:)
+U+3041:A5
+U+3042:a5
+U+3043:I5
+U+3044:i5
+U+3045:U5
+U+3046:u5
+U+3047:E5
+U+3048:e5
+U+3049:O5
+U+304a:o5
+U+304b:ka
+U+304c:ga
+U+304d:ki
+U+304e:gi
+U+304f:ku
+U+3050:gu
+U+3051:ke
+U+3052:ge
+U+3053:ko
+U+3054:go
+U+3055:sa
+U+3056:za
+U+3057:si
+U+3058:zi
+U+3059:su
+U+305a:zu
+U+305b:se
+U+305c:ze
+U+305d:so
+U+305e:zo
+U+305f:ta
+U+3060:da
+U+3061:ti
+U+3062:di
+U+3063:tU
+U+3064:tu
+U+3065:du
+U+3066:te
+U+3067:de
+U+3068:to
+U+3069:do
+U+306a:na
+U+306b:ni
+U+306c:nu
+U+306d:ne
+U+306e:no
+U+306f:ha
+U+3070:ba
+U+3071:pa
+U+3072:hi
+U+3073:bi
+U+3074:pi
+U+3075:hu
+U+3076:bu
+U+3077:pu
+U+3078:he
+U+3079:be
+U+307a:pe
+U+307b:ho
+U+307c:bo
+U+307d:po
+U+307e:ma
+U+307f:mi
+U+3080:mu
+U+3081:me
+U+3082:mo
+U+3083:yA
+U+3084:ya
+U+3085:yU
+U+3086:yu
+U+3087:yO
+U+3088:yo
+U+3089:ra
+U+308a:ri
+U+308b:ru
+U+308c:re
+U+308d:ro
+U+308e:wA
+U+308f:wa
+U+3090:wi
+U+3091:we
+U+3092:wo
+U+3093:n5
+U+3094:vu
+U+309b:"5
+U+309c:05
+U+309d:*5
+U+309e:+5
+U+30a1:a6
+U+30a2:A6
+U+30a3:i6
+U+30a4:I6
+U+30a5:u6
+U+30a6:U6
+U+30a7:e6
+U+30a8:E6
+U+30a9:o6
+U+30aa:O6
+U+30ab:Ka
+U+30ac:Ga
+U+30ad:Ki
+U+30ae:Gi
+U+30af:Ku
+U+30b0:Gu
+U+30b1:Ke
+U+30b2:Ge
+U+30b3:Ko
+U+30b4:Go
+U+30b5:Sa
+U+30b6:Za
+U+30b7:Si
+U+30b8:Zi
+U+30b9:Su
+U+30ba:Zu
+U+30bb:Se
+U+30bc:Ze
+U+30bd:So
+U+30be:Zo
+U+30bf:Ta
+U+30c0:Da
+U+30c1:Ti
+U+30c2:Di
+U+30c3:TU
+U+30c4:Tu
+U+30c5:Du
+U+30c6:Te
+U+30c7:De
+U+30c8:To
+U+30c9:Do
+U+30ca:Na
+U+30cb:Ni
+U+30cc:Nu
+U+30cd:Ne
+U+30ce:No
+U+30cf:Ha
+U+30d0:Ba
+U+30d1:Pa
+U+30d2:Hi
+U+30d3:Bi
+U+30d4:Pi
+U+30d5:Hu
+U+30d6:Bu
+U+30d7:Pu
+U+30d8:He
+U+30d9:Be
+U+30da:Pe
+U+30db:Ho
+U+30dc:Bo
+U+30dd:Po
+U+30de:Ma
+U+30df:Mi
+U+30e0:Mu
+U+30e1:Me
+U+30e2:Mo
+U+30e3:YA
+U+30e4:Ya
+U+30e5:YU
+U+30e6:Yu
+U+30e7:YO
+U+30e8:Yo
+U+30e9:Ra
+U+30ea:Ri
+U+30eb:Ru
+U+30ec:Re
+U+30ed:Ro
+U+30ee:WA
+U+30ef:Wa
+U+30f0:Wi
+U+30f1:We
+U+30f2:Wo
+U+30f3:N6
+U+30f4:Vu
+U+30f5:KA
+U+30f6:KE
+U+30f7:Va
+U+30f8:Vi
+U+30f9:Ve
+U+30fa:Vo
+U+30fb:.6
+U+30fc:-6
+U+30fd:*6
+U+30fe:+6
+U+3105:b4
+U+3106:p4
+U+3107:m4
+U+3108:f4
+U+3109:d4
+U+310a:t4
+U+310b:n4
+U+310c:l4
+U+310d:g4
+U+310e:k4
+U+310f:h4
+U+3110:j4
+U+3111:q4
+U+3112:x4
+U+3113:zh
+U+3114:ch
+U+3115:sh
+U+3116:r4
+U+3117:z4
+U+3118:c4
+U+3119:s4
+U+311a:a4
+U+311b:o4
+U+311c:e4
+U+311d:eh4
+U+311e:ai
+U+311f:ei
+U+3120:au
+U+3121:ou
+U+3122:an
+U+3123:en
+U+3124:aN
+U+3125:eN
+U+3126:er
+U+3127:i4
+U+3128:u4
+U+3129:iu
+U+312a:v4
+U+312b:nG
+U+312c:gn
+U+321c:(JU)
+U+3220:1c
+U+3221:2c
+U+3222:3c
+U+3223:4c
+U+3224:5c
+U+3225:6c
+U+3226:7c
+U+3227:8c
+U+3228:9c
+U+3229:10c
+U+327f:KSC
+U+33c2:am
+U+33d8:pm
+U+fb00:ff
+U+fb01:fi
+U+fb02:fl
+U+fb03:ffi
+U+fb04:ffl
+U+fb05:St
+U+fb06:st
+U+fe7d:3+;
+U+fe82:aM.
+U+fe84:aH.
+U+fe88:ah.
+U+fe8d:a+-
+U+fe8e:a+.
+U+fe8f:b+-
+U+fe90:b+.
+U+fe91:b+,
+U+fe92:b+;
+U+fe93:tm-
+U+fe94:tm.
+U+fe95:t+-
+U+fe96:t+.
+U+fe97:t+,
+U+fe98:t+;
+U+fe99:tk-
+U+fe9a:tk.
+U+fe9b:tk,
+U+fe9c:tk;
+U+fe9d:g+-
+U+fe9e:g+.
+U+fe9f:g+,
+U+fea0:g+;
+U+fea1:hk-
+U+fea2:hk.
+U+fea3:hk,
+U+fea4:hk;
+U+fea5:x+-
+U+fea6:x+.
+U+fea7:x+,
+U+fea8:x+;
+U+fea9:d+-
+U+feaa:d+.
+U+feab:dk-
+U+feac:dk.
+U+fead:r+-
+U+feae:r+.
+U+feaf:z+-
+U+feb0:z+.
+U+feb1:s+-
+U+feb2:s+.
+U+feb3:s+,
+U+feb4:s+;
+U+feb5:sn-
+U+feb6:sn.
+U+feb7:sn,
+U+feb8:sn;
+U+feb9:c+-
+U+feba:c+.
+U+febb:c+,
+U+febc:c+;
+U+febd:dd-
+U+febe:dd.
+U+febf:dd,
+U+fec0:dd;
+U+fec1:tj-
+U+fec2:tj.
+U+fec3:tj,
+U+fec4:tj;
+U+fec5:zH-
+U+fec6:zH.
+U+fec7:zH,
+U+fec8:zH;
+U+fec9:e+-
+U+feca:e+.
+U+fecb:e+,
+U+fecc:e+;
+U+fecd:i+-
+U+fece:i+.
+U+fecf:i+,
+U+fed0:i+;
+U+fed1:f+-
+U+fed2:f+.
+U+fed3:f+,
+U+fed4:f+;
+U+fed5:q+-
+U+fed6:q+.
+U+fed7:q+,
+U+fed8:q+;
+U+fed9:k+-
+U+feda:k+.
+U+fedb:k+,
+U+fedc:k+;
+U+fedd:l+-
+U+fede:l+.
+U+fedf:l+,
+U+fee0:l+;
+U+fee1:m+-
+U+fee2:m+.
+U+fee3:m+,
+U+fee4:m+;
+U+fee5:n+-
+U+fee6:n+.
+U+fee7:n+,
+U+fee8:n+;
+U+fee9:h+-
+U+feea:h+.
+U+feeb:h+,
+U+feec:h+;
+U+feed:w+-
+U+feee:w+.
+U+feef:j+-
+U+fef0:j+.
+U+fef1:y+-
+U+fef2:y+.
+U+fef3:y+,
+U+fef4:y+;
+U+fef5:lM-
+U+fef6:lM.
+U+fef7:lH-
+U+fef8:lH.
+U+fef9:lh-
+U+fefa:lh.
+U+fefb:la-
+U+fefc:la.
+U+0000:NU
+U+0001:SH
+U+0002:SX
+U+0003:EX
+U+0004:ET
+U+0005:EQ
+U+0006:AK
+U+0007:BL
+U+0008:BS
+U+0009:HT
+U+000a:LF
+U+000b:VT
+U+000c:FF
+U+000d:CR
+U+000e:SO
+U+000f:SI
+U+0010:DL
+U+0011:D1
+U+0012:D2
+U+0013:D3
+U+0014:D4
+U+0015:NK
+U+0016:SY
+U+0017:EB
+U+0018:CN
+U+0019:EM
+U+001a:SB
+U+001b:EC
+U+001c:FS
+U+001d:GS
+U+001e:RS
+U+001f:US
+U+007f:DT
+U+0080:PA
+U+0081:HO
+U+0082:BH
+U+0083:NH
+U+0084:IN
+U+0085:NL
+U+0086:SA
+U+0087:ES
+U+0088:HS
+U+0089:HJ
+U+008a:VS
+U+008b:PD
+U+008c:PU
+U+008d:RI
+U+008e:S2
+U+008f:S3
+U+0090:DC
+U+0091:P1
+U+0092:P2
+U+0093:TS
+U+0094:CC
+U+0095:MW
+U+0096:SG
+U+0097:EG
+U+0098:SS
+U+0099:GC
+U+009a:SC
+U+009b:CI
+U+009c:ST
+U+009d:OC
+U+009e:PM
+U+009f:AC
+# Characters in Private Use Area (e000-f8ff) do not have ussigned numbers
+# according Unicode 2.0
diff --git a/src/chrtrans/rot13_kb.h b/src/chrtrans/rot13_kb.h
new file mode 100644
index 00000000..1df5f321
--- /dev/null
+++ b/src/chrtrans/rot13_kb.h
@@ -0,0 +1,22 @@
+static LYKbLayout_t kb_layout_rot13[128] =
+{
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,	/* 00..07 */
+    0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,	/* 08..0F */
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,	/* 10..17 */
+    0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,	/* 18..1F */
+
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,	/* 20..27 */
+    0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,	/* 28..2F */
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,	/* 30..37 */
+    0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,	/* 38..3F */
+
+    0x0000, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054,	/* 40..48 */
+    0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x0041, 0x0042,	/* 40..4F */
+    0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a,	/* 50..58 */
+    0x004b, 0x004c, 0x004d, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,	/* 50..5F */
+
+    0x0000, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074,	/* 60..68 */
+    0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x0061, 0x0062,	/* 60..6F */
+    0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a,	/* 70..78 */
+    0x006b, 0x006c, 0x006d, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000	/* 70..7F */
+};
diff --git a/src/chrtrans/utf8_uni.tbl b/src/chrtrans/utf8_uni.tbl
new file mode 100644
index 00000000..88ad4925
--- /dev/null
+++ b/src/chrtrans/utf8_uni.tbl
@@ -0,0 +1,35 @@
+#
+# This one is not really much of a "translation table", it mostly just
+# tells Lynx that "utf-8" is Unicode/UCS2 encoded in UTF8.  Note that
+# "unicode-1-1-utf-8" and "utf8" are treated as synonyms.
+#
+#The MIME name of this charset.
+Mutf-8
+
+#Name as a Display Charset (used on Options screen)
+OUNICODE (UTF-8)
+
+# Some kind of raw Unicode?
+# Use 6 for for really "raw" 16bit UCS-2, 7 for UTF-8, ...
+
+# most of these codes currently don't make much sense in a *.tbl file,
+# but for completeness (from UCDefs.h):
+#  #define UCT_ENC_7BIT 0
+#  #define UCT_ENC_8BIT 1
+#  #define UCT_ENC_8859 2
+#  #define UCT_ENC_8BIT_C0 3
+#  #define UCT_ENC_MAYBE2022 4
+#  #define UCT_ENC_CJK 5
+#  #define UCT_ENC_16BIT 6
+#  #define UCT_ENC_UTF8 7
+
+R 7
+
+#Shall this become the "default" translation?
+#There has to be exactly one table marked as "default".
+Default NO
+
+# Don't fall back to default table for unicode -> 8bit
+Fallback NO
+
+0x20-0x7f	idem
diff --git a/src/chrtrans/viscii_uni.tbl b/src/chrtrans/viscii_uni.tbl
new file mode 100644
index 00000000..617f1e1f
--- /dev/null
+++ b/src/chrtrans/viscii_uni.tbl
@@ -0,0 +1,300 @@
+#
+# Unicode mapping table for VISCII 1.1 fonts and charset=viscii,
+# described in RFC 1456.
+# See also <URL:http://www.trichlor.org/vietstd/report/rep92.htm>,
+# also for testing.
+# The 6 characters encoded in the C0 control region should not
+# be passed through to the terminal but be mapped to VIQR strings.
+# THe two changed mappings of MacVISCII are recognized in documents.
+
+# [convert with makeuctb]
+#
+#The MIME name of this charset.
+Mviscii
+
+#Name as a Display Charset (used on Options screen).
+OVietnamese (VISCII)
+
+# Special 'enc' flag to signal that some C0 characters are used.
+# Tables with R3 should properly map the allowed C0 control chars!
+#
+# most of these codes currently don't make much sense in a *.tbl file,
+# but for completeness (from UCDefs.h):
+#  #define UCT_ENC_7BIT 0
+#  #define UCT_ENC_8BIT 1
+#  #define UCT_ENC_8859 2
+#  #define UCT_ENC_8BIT_C0 3
+#  #define UCT_ENC_MAYBE2022 4
+#  #define UCT_ENC_CJK 5
+#  #define UCT_ENC_16BIT 6
+#  #define UCT_ENC_UTF8 7
+
+R 3
+
+#0x00	U+0000
+#0x01	U+0001
+#0x03	U+0003
+#0x04	U+0004
+#0x07	U+0007
+#0x08	U+0008
+0x09	U+0009
+0x0a	U+000a
+#0x0b	U+000b
+0x0c	U+000c
+U+000c " "
+0x0d	U+000d
+#0x0e	U+000e
+#0x0f	U+000f
+#0x10	U+0010
+#0x11	U+0011
+#0x12	U+0012
+#0x13	U+0013
+#0x15	U+0015
+#0x16	U+0016
+#0x17	U+0017
+#0x18	U+0018
+0x1a	U+001a
+U+001a:^Z
+#0x1b	U+001b
+#0x1c	U+001c
+#0x1d	U+001d
+#0x1f	U+001f
+#0x20	U+0020
+#0x21	U+0021
+#0x22	U+0022
+#0x23	U+0023
+#0x24	U+0024
+#0x25	U+0025
+#0x26	U+0026
+#0x27	U+0027
+#0x28	U+0028
+#0x29	U+0029
+#0x2a	U+002a
+#0x2b	U+002b
+#0x2c	U+002c
+#0x2d	U+002d
+#0x2e	U+002e
+#0x2f	U+002f
+#0x30	U+0030
+#0x31	U+0031
+#0x32	U+0032
+#0x33	U+0033
+#0x34	U+0034
+#0x35	U+0035
+#0x36	U+0036
+#0x37	U+0037
+#0x38	U+0038
+#0x39	U+0039
+#0x3a	U+003a
+#0x3b	U+003b
+#0x3c	U+003c
+#0x3d	U+003d
+#0x3e	U+003e
+#0x3f	U+003f
+#0x40	U+0040
+#0x41	U+0041
+#0x42	U+0042
+#0x43	U+0043
+#0x44	U+0044
+#0x45	U+0045
+#0x46	U+0046
+#0x47	U+0047
+#0x48	U+0048
+#0x49	U+0049
+#0x4a	U+004a
+#0x4b	U+004b
+#0x4c	U+004c
+#0x4d	U+004d
+#0x4e	U+004e
+#0x4f	U+004f
+#0x50	U+0050
+#0x51	U+0051
+#0x52	U+0052
+#0x53	U+0053
+#0x54	U+0054
+#0x55	U+0055
+#0x56	U+0056
+#0x57	U+0057
+#0x58	U+0058
+#0x59	U+0059
+#0x5a	U+005a
+#0x5b	U+005b
+#0x5c	U+005c
+#0x5d	U+005d
+#0x5e	U+005e
+#0x5f	U+005f
+#0x60	U+0060
+#0x61	U+0061
+#0x62	U+0062
+#0x63	U+0063
+#0x64	U+0064
+#0x65	U+0065
+#0x66	U+0066
+#0x67	U+0067
+#0x68	U+0068
+#0x69	U+0069
+#0x6a	U+006a
+#0x6b	U+006b
+#0x6c	U+006c
+#0x6d	U+006d
+#0x6e	U+006e
+#0x6f	U+006f
+#0x70	U+0070
+#0x71	U+0071
+#0x72	U+0072
+#0x73	U+0073
+#0x74	U+0074
+#0x75	U+0075
+#0x76	U+0076
+#0x77	U+0077
+#0x78	U+0078
+#0x79	U+0079
+#0x7a	U+007a
+#0x7b	U+007b
+#0x7c	U+007c
+#0x7d	U+007d
+#0x7e	U+007e
+#0x7f	U+007f
+0xc0	U+00c0
+0xc1	U+00c1
+0xc2	U+00c2
+0xc3	U+00c3
+0xc8	U+00c8
+0xc9	U+00c9
+0xca	U+00ca
+0xcc	U+00cc
+0xcd	U+00cd
+0xd2	U+00d2
+0xd3	U+00d3
+0xd4	U+00d4
+0xa0	U+00d5
+0xd9	U+00d9
+0xda	U+00da
+0xdd	U+00dd
+0xe0	U+00e0
+0xe1	U+00e1
+0xe2	U+00e2
+0xe3	U+00e3
+0xe8	U+00e8
+0xe9	U+00e9
+0xea	U+00ea
+0xec	U+00ec
+0xed	U+00ed
+0xf2	U+00f2
+0xf3	U+00f3
+0xf4	U+00f4
+0xf5	U+00f5
+0xf9	U+00f9
+0xfa	U+00fa
+0xfd	U+00fd
+0xc5	U+0102
+0xe5	U+0103
+0xd0	U+0110
+0xf0	U+0111 U+00f0  # "edh" is similar enough to map it here
+0xce	U+0128
+0xee	U+0129
+0x9d	U+0168
+0xfb	U+0169
+0xb4	U+01a0
+0xbd	U+01a1
+0xbf	U+01af
+0xdf	U+01b0
+0x80	U+1ea0
+0xd5	U+1ea1
+0xc4	U+1ea2
+0xe4	U+1ea3
+0x84	U+1ea4
+0xa4	U+1ea5
+0x85	U+1ea6
+0xa5	U+1ea7
+0x86	U+1ea8
+0xa6	U+1ea9
+0x06	U+1eaa
+U+1eaa "\302~"  # A with circumflex (same code as in iso-8859-1) and tilde
+0xe7	U+1eab
+0x87	U+1eac
+0xa7	U+1ead
+0x81	U+1eae
+0xa1	U+1eaf
+0x82	U+1eb0
+0xa2	U+1eb1
+0x02	U+1eb2
+U+1eb2:A(?
+0xc6	U+1eb3
+0x05	U+1eb4
+U+1eb4:A(~
+0xc7	U+1eb5
+0x83	U+1eb6
+0xa3	U+1eb7
+0x89	U+1eb8
+0xa9	U+1eb9
+0xcb	U+1eba
+0xeb	U+1ebb
+0x88	U+1ebc
+0xa8	U+1ebd
+0x8a	U+1ebe
+0xaa	U+1ebf
+0x8b	U+1ec0
+0xab	U+1ec1
+0x8c	U+1ec2
+0xac	U+1ec3
+0x8d	U+1ec4
+0xad	U+1ec5
+0x8e	U+1ec6
+0xae	U+1ec7
+0x9b	U+1ec8
+0xef	U+1ec9
+0x98	U+1eca
+0xb8	U+1ecb
+0x9a	U+1ecc
+0xf7	U+1ecd
+0x99	U+1ece
+0xf6	U+1ecf
+0x8f	U+1ed0
+0xaf	U+1ed1
+0x90	U+1ed2
+0xb0	U+1ed3
+0x91	U+1ed4
+0xb1	U+1ed5
+0x92	U+1ed6
+0xb2	U+1ed7
+0x93	U+1ed8
+0xb5	U+1ed9
+0x95	U+1eda
+0xbe	U+1edb
+0x96	U+1edc
+0xb6	U+1edd
+0x97	U+1ede
+0xb7	U+1edf
+0xb3	U+1ee0
+0xde	U+1ee1
+0x94	U+1ee2
+0xfe	U+1ee3
+0x9e	U+1ee4
+0xf8	U+1ee5
+0x9c	U+1ee6
+0xfc	U+1ee7
+0xba	U+1ee8
+0xd1	U+1ee9
+0xbb	U+1eea
+0xd7	U+1eeb
+0xbc	U+1eec
+0xd8	U+1eed
+0xff	U+1eee
+0xe6	U+1eef
+0xb9	U+1ef0
+0xf1	U+1ef1
+0x9f	U+1ef2
+0xcf	U+1ef3
+0x18	U+1ef4   # MacVISCII
+0x1e	U+1ef4
+U+1ef4:Y.
+0xdc	U+1ef5
+0x17	U+1ef6   # MacVISCII
+0x14	U+1ef6
+U+1ef6:Y?
+0xd6	U+1ef7
+0x19	U+1ef8
+U+1ef8:Y~
+0xdb	U+1ef9
+
diff --git a/src/chrtrans/yawerty_kb.h b/src/chrtrans/yawerty_kb.h
new file mode 100644
index 00000000..8301c818
--- /dev/null
+++ b/src/chrtrans/yawerty_kb.h
@@ -0,0 +1,22 @@
+static LYKbLayout_t kb_layout_yawerty[128] =
+{
+    0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,	/* 00..07 */
+    0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,	/* 08..0F */
+    0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,	/* 10..17 */
+    0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,	/* 18..1F */
+
+    0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,	/* 20..27 */
+    0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,	/* 28..2F */
+    0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,	/* 30..37 */
+    0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,	/* 38..3F */
+
+    0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413,	/* 40..47 */
+    0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e,	/* 48..4F */
+    0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412,	/* 50..57 */
+    0x042c, 0x042b, 0x0417, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a,	/* 58..5F */
+
+    0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433,	/* 60..67 */
+    0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e,	/* 68..6F */
+    0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432,	/* 70..77 */
+    0x044c, 0x044b, 0x0437, 0x0428, 0x042d, 0x0429, 0x0427, 0x0000	/* 78..7F */
+};
diff --git a/src/cmu_tcp.opt b/src/cmu_tcp.opt
new file mode 100644
index 00000000..24e43fe3
--- /dev/null
+++ b/src/cmu_tcp.opt
@@ -0,0 +1 @@
+cmuip_root:[syslib]libcmu/library
diff --git a/src/decc.opt b/src/decc.opt
new file mode 100644
index 00000000..670a21b0
--- /dev/null
+++ b/src/decc.opt
@@ -0,0 +1,2 @@
+sys$library:vaxcrtl/library
+sys$library:vaxccurse/library
diff --git a/src/descrip.mms b/src/descrip.mms
new file mode 100644
index 00000000..6b22b78d
--- /dev/null
+++ b/src/descrip.mms
@@ -0,0 +1,172 @@
+! $LynxId: descrip.mms,v 1.12 2008/06/30 23:50:22 tom Exp $
+!
+!       Make LYNX hypertext browser under VMS
+!       =====================================
+!
+!	NOTE:  Use [.SRC.CHRTRANS]BUILD-CHRTRANS.COM to create the
+!	       chrtrans header files before using this descrip.mms.
+!
+! History:
+!  1/1/93  creation at KU (Lou montulli@ukanaix.cc.ukans.edu).
+!  4/12/93 (seb@lns61.tn.cornell.edu)
+!           modified to support either UCX or MULTINET
+!  12/2/93 modified to support Lynx rewrite
+!  12/13/93 (macrides@sci.wfeb.edu)
+!	     Added conditional compilations for VAXC vs. DECC
+!	     (dependencies not yet specified; this is just a
+!	      "starter", should anyone want to do it well).
+!  10/31/94 RLD Updated for Lynx v2.3.4-VMS, supporting OpenCMU
+!               and TCPWare
+!  11/11/94 RLD Updated for Lynx v2.3.5-VMS
+!  11/18/94 FM Updated for SOCKETSHR/NETLIB
+!  12/07/94 FM Updated for DECC/VAX, VAXC/VAX and DECC/AXP
+!  05/03/95 FM Include /NoMember for DECC (not the default on AXP, and
+!		the code assumes byte alignment).
+!  06/14/95 FM Added LYList.
+!  07/26/95 FM Separated transport (TOPT) and compiler (COPT) option files.
+!  07/29/95 FM Added support for GNUC.
+!  02/29/96 FM Added LYMap.
+!  06/28/97 FM Added UCAuto, UCAux, and UCdomap.
+!  15 Sep 06 (TD)	Cleanup...
+!
+! Instructions:
+!       Use the correct command line for your TCP/IP implementation:
+!
+!	$ MMS                                   for VAXC - MultiNet
+!	$ MMS /Macro = (MULTINET=1)		for VAXC - MultiNet
+!	$ MMS /Macro = (WIN_TCP=1)              for VAXC - Wollongong TCP/IP
+!	$ MMS /Macro = (UCX=1)			for VAXC - UCX
+!	$ MMS /Macro = (CMU_TCP=1)		for VAXC - OpenCMU TCP/IP
+!	$ MMS /Macro = (SOCKETSHR_TCP=1)	for VAXC - SOCKETSHR/NETLIB
+!	$ MMS /Macro = (TCPWARE=1)		for VAXC - TCPWare TCP/IP
+!	$ MMS /Macro = (DECNET=1)		for VAXC - socket emulation over DECnet
+!
+!	$ MMS /Macro = (MULTINET=1, DEC_C=1)	for DECC - MultiNet
+!	$ MMS /Macro = (WIN_TCP=1, DEC_C=1)	for DECC - Wollongong TCP/IP
+!	$ MMS /Macro = (UCX=1, DEC_C=1)		for DECC - UCX
+!	$ MMS /Macro = (CMU_TCP=1, DEC_C=1)	for DECC - OpenCMU TCP/IP
+!	$ MMS /Macro = (SOCKETSHR_TCP=1,DEC_C=1) for DECC - SOCKETSHR/NETLIB
+!	$ MMS /Macro = (TCPWARE=1, DEC_C=1)	for DECC - OpenCMU TCP/IP
+!	$ MMS /Macro = (DECNET=1, DEC_C=1)	for DECC - socket emulation over DECnet
+!
+!	$ MMS /Macro = (MULTINET=1, GNU_C=1)	for GNUC - MultiNet
+!	$ MMS /Macro = (WIN_TCP=1, GNU_C=1)	for GNUC - Wollongong TCP/IP
+!	$ MMS /Macro = (UCX=1, GNU_C=1)		for GNUC - UCX
+!	$ MMS /Macro = (CMU_TCP=1, GNU_C=1)	for GNUC - OpenCMU TCP/IP
+!	$ MMS /Macro = (SOCKETSHR_TCP=1,GNU_C=1) for GNUC - SOCKETSHR/NETLIB
+!	$ MMS /Macro = (TCPWARE=1, GNU_C=1)	for GNUC - OpenCMU TCP/IP
+!	$ MMS /Macro = (DECNET=1, GNU_C=1)	for GNUC - socket emulation over DECnet
+
+OBJS =	DefaultStyle.obj, GridText.obj, HTAlert.obj, HTFWriter.obj, -
+	HTInit.obj, HTML.obj, LYBookmark.obj, LYCgi.obj, LYCharSets.obj, -
+	LYCharUtils.obj, LYClean.obj, LYCookie.obj, LYCurses.obj, -
+	LYDownload.obj, LYEdit.obj, LYEditmap.obj, LYForms.obj, -
+	LYGetFile.obj, LYHistory.obj, LYJump.obj, LYKeymap.obj, -
+	LYLeaks.obj, LYList.obj, LYMail.obj, LYMain.obj, LYMainLoop.obj, -
+	LYMap.obj, LYNews.obj, LYOptions.obj, LYPrint.obj, LYReadCFG.obj, -
+	LYSearch.obj, LYShowInfo.obj, LYStrings.obj, LYTraversal.obj, -
+	LYUpload.obj, LYUtils.obj, LYexit.obj, LYrcFile.obj, TRSTable.obj, -
+	LYmktime.obj, UCAuto.obj, UCAux.obj, UCdomap.obj, parsdate.obj
+
+.ifdef SLANG
+SCREEN_DEF = USE_SLANG
+SCREEN_INC = , SLANG_INC 
+SCREEN_LIB = , SLANG_LIB:slang.olb/lib
+.else
+.ifdef DEC_C
+SCREEN_DEF = __VMS_CURSES
+.endif
+.endif
+
+.ifdef DEC_C
+COMPILER = DECC
+MODEL_DEF = _DECC_V4_SOURCE
+.else
+MODEL_DEF =
+.ifdef GNU_C
+COMPILER = GNUC
+CC = gcc
+.else
+COMPILER = VAXC
+.endif
+.endif
+
+.ifdef WIN_TCP
+NETWORK_DEF = WIN_TCP
+NETWORK_OPT = WIN_TCP
+.else
+.ifdef CMU_TCP
+NETWORK_DEF = CMU_TCP
+NETWORK_OPT = CMU_TCP
+.else
+.ifdef SOCKETSHR_TCP
+NETWORK_DEF = SOCKETSHR_TCP
+NETWORK_OPT = SOCKETSHR_TCP
+.else
+.ifdef UCX
+NETWORK_DEF = UCX
+.ifdef DEC_C
+NETWORK_OPT = UCXSHR
+.else
+NETWORK_OPT = UCXOLB
+.endif
+.else
+.ifdef TCPWARE
+NETWORK_DEF = TCPWARE,UCX
+.ifdef DEC_C
+NETWORK_OPT = TCPWARESHR
+.else
+NETWORK_OPT = TCPWAREOLB
+.endif
+.else
+.ifdef DECnet
+NETWORK_DEF = DECNET
+NETWORK_OPT = DECNET
+.else !  Default to MultiNet
+NETWORK_DEF = MULTINET,__SOCKET_TYPEDEFS
+NETWORK_OPT = MULTINET
+.endif !  DECnet
+.endif !  TCPWARE
+.endif !  UCX
+.endif !  SOCKETSHR_TCP
+.endif !  CMU_TCP
+.endif !  WIN_TCP
+
+COMPILER_DEF = $(MODEL_DEF),$(NETWORK_DEF),$(SCREEN_DEF)
+
+.ifdef DEC_C
+MY_CFLAGS = /decc/Prefix=All/NoMember/Define=(ACCESS_AUTH,$(COMPILER_DEF))
+.else
+MY_CFLAGS = /Define = (ACCESS_AUTH, $(COMPILER_DEF))
+.endif
+
+.if "$(MMS_ARCHNAME)" .eq "IA64"
+TOPT = 
+COPT = 
+.else
+TOPT = ,sys$disk:[]$(NETWORK_OPT).opt/opt
+COPT = ,sys$disk:[]$(COMPILER).opt/opt
+.endif
+
+WWWLIB = [-.WWW.Library.Implementation]WWWLib.olb
+CFLAGS = $(MY_CFLAGS) $(CFLAGS)/Include=([], [-], [.chrtrans], [-.WWW.Library.Implementation]$(SCREEN_INC))
+
+
+lynx :	lynx.exe
+	@ Continue
+
+HDRS = [.chrtrans]iso01_uni.h
+
+lynx.exe :   $(HDRS) $(OBJS) $(WWWLIB)
+	$(LINK) /Executable = Lynx.exe $(OBJS), $(WWWLIB)/lib $(SCREEN_LIB) $(TOPT) $(COPT)
+
+$(HDRS) :
+	set default [.chrtrans]
+	@build-chrtrans
+	set default [-]
+
+clean :
+	- Set Protection = (Owner:RWED) *.*;-1
+	- Purge /NoLog /NoConfirm
+	- Delete /NoConfirm /NoLog *.obj;*
+	- Delete /NoConfirm /NoLog *.exe;*
diff --git a/src/gnuc.opt b/src/gnuc.opt
new file mode 100644
index 00000000..0fe5159b
--- /dev/null
+++ b/src/gnuc.opt
@@ -0,0 +1,3 @@
+gnu_cc:[000000]gcclib/library
+sys$share:vaxcrtl/share
+sys$library:vaxccurse/library
diff --git a/src/makefile.dos b/src/makefile.dos
new file mode 100644
index 00000000..ed0358db
--- /dev/null
+++ b/src/makefile.dos
@@ -0,0 +1,115 @@
+# $LynxId: makefile.dos,v 1.32 2008/06/30 23:53:42 tom Exp $
+
+OBJS= UCdomap.o UCAux.o UCAuto.o \
+LYClean.o LYShowInfo.o LYEdit.o LYStrings.o \
+LYMail.o HTAlert.o GridText.o LYGetFile.o \
+LYMain.o LYMainLoop.o LYCurses.o LYBookmark.o LYmktime.o LYUtils.o \
+LYOptions.o LYReadCFG.o LYSearch.o LYHistory.o LYSession.o \
+LYForms.o LYPrint.o LYrcFile.o LYDownload.o LYNews.o LYKeymap.o \
+HTML.o HTFWriter.o HTInit.o DefaultStyle.o LYLocal.o LYUpload.o \
+LYLeaks.o LYexit.o LYJump.o LYList.o LYCgi.o LYTraversal.o \
+LYEditmap.o LYCharSets.o LYCharUtils.o LYMap.o LYCookie.o LYExtern.o \
+LYStyle.o LYHash.o LYPrettySrc.o TRSTable.o parsdate.o
+
+CFLAGS= -O2 $(MCFLAGS) $(INTLFLAGS) -I. -I..
+
+# comment this line to suppress DIRED support
+DIRED_DEFS = \
+ -DDIRED_SUPPORT \
+ -DOK_UUDECODE \
+ -DOK_TAR \
+ -DOK_GZIP \
+ -DOK_ZIP \
+ -DOK_OVERRIDE
+
+# Use this option to enable optional and *experimental* color style.
+#ENABLE_COLOR_STYLE = -DUSE_COLOR_STYLE
+
+CC = gcc
+
+MCFLAGS = \
+ $(DIRED_DEFS) \
+ $(ENABLE_COLOR_STYLE) \
+ -DACCESS_AUTH \
+ -DCOLOR_CURSES \
+ -DDISP_PARTIAL \
+ -DDOSPATH \
+ -DEXP_ADDRLIST_PAGE \
+ -DEXP_ALT_BINDINGS \
+ -DEXP_NESTED_TABLES \
+ -DUSE_PERSISTENT_COOKIES \
+ -DFANCY_CURSES \
+ -DNOUSERS \
+ -DNO_CUSERID \
+ -DNO_TTYTYPE \
+ -DNO_UTMP \
+ -DPDCURSES \
+ -DUSE_SOURCE_CACHE \
+ -DUSE_EXTERNALS \
+ -DUSE_FILE_UPLOAD \
+ -DUSE_PRETTYSRC \
+ -DUSE_ZLIB \
+ $(SSLFLAGS) \
+ $(SSLINC) \
+ -I./chrtrans \
+ -I../WWW/Library/Implementation \
+ -I/djgpp/pdcur26 \
+ -I/djgpp/watt32/inc
+
+WWWLIB = \
+ ../WWW/Library/djgpp/libwww.a \
+ /djgpp/pdcur26/lib/pdcurses.a
+
+LIBS= -L/djgpp/watt32/lib -lwatt -lz -lwmemu
+
+# Uncomment the following to enable Internationalization.
+#INTLFLAGS = -DHAVE_GETTEXT -DHAVE_LIBINTL_H
+#INTLLIBS= -lintl -liconv
+
+# Uncomment the following to enable SSL.
+#SSLFLAGS = -DUSE_SSL
+#SSLLIB = -lssl -lcrypto
+#SSLINC = -I/djgpp/include/openssl
+
+all: lynx
+
+lynx:   message $(OBJS) $(WWWLIB)
+	@echo "Linking and creating Lynx executable"
+	$(CC) $(CFLAGS) -o lynx.exe  $(OBJS) $(WWWLIB) $(SSLLIB) $(LIBS) $(INTLLIBS)
+	@echo "Welcome to Lynx!"
+
+message:
+	@echo "Compiling Lynx sources"
+
+dbg:	$(OBJS) $(WWWLIB)
+	@echo "Making Lynx code"
+	$(CC) -g $(OBJS) $(CFLAGS) $(WWWLIB) $(LIBS)
+
+lint:
+	lint *.c  > ../lint.out
+
+clean:
+	rm -f lynx.exe core *.[ob]
+
+DefaultStyle.o:	../userdefs.h
+HTFWriter.o:	../userdefs.h
+LYBookmark.o:	../userdefs.h
+LYCharSets.o:	../userdefs.h
+LYCharUtils.o:	../userdefs.h
+LYCookie.o:	../userdefs.h
+LYDownload.o:	../userdefs.h
+LYEditmap.o:	../userdefs.h
+LYExtern.o:	../userdefs.h
+LYGetFile.o:	../userdefs.h
+LYHistory.o:	../userdefs.h
+LYKeymap.o:	../userdefs.h
+LYMain.o:	../userdefs.h
+LYMainLoop.o:	../userdefs.h
+LYOptions.o:	../userdefs.h
+LYReadCFG.o:	../userdefs.h
+LYShowInfo.o:	../userdefs.h
+LYStrings.o:	../userdefs.h
+LYTraversal.o:	../userdefs.h
+LYUtils.o:	../userdefs.h
+LYmktime.o:	../userdefs.h
+parsdate.o:	../userdefs.h
diff --git a/src/makefile.dsl b/src/makefile.dsl
new file mode 100644
index 00000000..64024f7f
--- /dev/null
+++ b/src/makefile.dsl
@@ -0,0 +1,105 @@
+# $LynxId: makefile.dsl,v 1.19 2008/06/30 23:53:42 tom Exp $
+
+OBJS= UCdomap.o UCAux.o UCAuto.o \
+LYClean.o LYShowInfo.o LYEdit.o LYStrings.o \
+LYMail.o HTAlert.o GridText.o LYGetFile.o \
+LYMain.o LYMainLoop.o LYCurses.o LYBookmark.o LYmktime.o LYUtils.o \
+LYOptions.o LYReadCFG.o LYSearch.o LYHistory.o LYSession.o \
+LYForms.o LYPrint.o LYrcFile.o LYDownload.o LYNews.o LYKeymap.o \
+HTML.o HTFWriter.o HTInit.o DefaultStyle.o LYLocal.o LYUpload.o \
+LYLeaks.o LYexit.o LYJump.o LYList.o LYCgi.o LYTraversal.o \
+LYEditmap.o LYCharSets.o LYCharUtils.o LYMap.o LYCookie.o LYExtern.o \
+LYStyle.o LYHash.o LYPrettySrc.o TRSTable.o parsdate.o
+
+CFLAGS= -O2 $(MCFLAGS) $(INTLFLAGS) -I. -I.. $(SLANGINC)
+
+# comment this line to suppress DIRED support
+DIRED_DEFS = \
+ -DDIRED_SUPPORT \
+ -DOK_UUDECODE \
+ -DOK_TAR \
+ -DOK_GZIP \
+ -DOK_ZIP \
+ -DOK_OVERRIDE
+
+CC = gcc
+
+MCFLAGS = \
+ $(DIRED_DEFS) \
+ -DACCESS_AUTH \
+ -DDISP_PARTIAL \
+ -DDJGPP_KEYHANDLER \
+ -DDOSPATH \
+ -DHAVE_POPEN \
+ -DNOUSERS \
+ -DNO_CUSERID \
+ -DNO_TTYTYPE \
+ -DNO_UTMP \
+ -DUSE_EXTERNALS \
+ -DUSE_PRETTYSRC \
+ -DUSE_SLANG \
+ -DUSE_SOURCE_CACHE \
+ -DUSE_ZLIB \
+ $(SSLFLAGS) \
+ $(SSLINC) \
+ -I./chrtrans \
+ -I../WWW/Library/Implementation \
+ -I/dev/env/DJDIR/watt32/inc
+
+WWWLIB = \
+ ../WWW/Library/djgpp/libwww.a \
+ /dev/env/DJDIR/watt32/lib/libwatt.a
+
+LIBS= $(SLANGLIB) -lslang $(SSLLIB) -lz $(INTLLIBS)
+
+# Uncomment the following to enable Internationalization.
+#INTLFLAGS = -DHAVE_GETTEXT -DHAVE_LIBINTL_H
+#INTLLIBS= -lintl -liconv
+
+# Uncomment the following to enable SSL.
+#SSLFLAGS = -DUSE_SSL
+#SSLLIB = -lssl -lcrypto
+#SSLINC = -I/dev/env/DJDIR/include/openssl
+
+all: lynx.exe
+
+lynx.exe:   message $(OBJS) $(WWWLIB)
+	@echo "Linking and creating Lynx executable"
+	$(CC) $(CFLAGS) -o lynx.exe  $(OBJS) $(WWWLIB) $(LIBS)
+	@echo "Welcome to Lynx!"
+
+message:
+	@echo "Compiling Lynx sources"
+
+dbg:	$(OBJS) $(WWWLIB)
+	@echo "Making Lynx code"
+	$(CC) -g $(OBJS) $(CFLAGS) $(WWWLIB) $(LIBS)
+
+lint:
+	lint *.c  > ../lint.out
+
+clean:
+	rm -f lynx.exe core *.[ob]
+
+DefaultStyle.o:	../userdefs.h
+HTFWriter.o:	../userdefs.h
+LYBookmark.o:	../userdefs.h
+LYCharSets.o:	../userdefs.h
+LYCharUtils.o:	../userdefs.h
+LYCookie.o:	../userdefs.h
+LYDownload.o:	../userdefs.h
+LYEditmap.o:	../userdefs.h
+LYExtern.o:	../userdefs.h
+LYGetFile.o:	../userdefs.h
+LYHistory.o:	../userdefs.h
+LYKeymap.o:	../userdefs.h
+LYMain.o:	../userdefs.h
+LYMainLoop.o:	../userdefs.h
+LYOptions.o:	../userdefs.h
+LYReadCFG.o:	../userdefs.h
+LYShowInfo.o:	../userdefs.h
+LYStrings.o:	../userdefs.h
+LYTraversal.o:	../userdefs.h
+LYUtils.o:	../userdefs.h
+LYmktime.o:	../userdefs.h
+parsdate.o:	../userdefs.h
diff --git a/src/makefile.in b/src/makefile.in
new file mode 100644
index 00000000..3c5e9edf
--- /dev/null
+++ b/src/makefile.in
@@ -0,0 +1,228 @@
+# $LynxId: makefile.in,v 1.62 2010/04/30 00:20:41 tom Exp $
+# template-makefile for Lynx src directory
+
+SHELL		= @CONFIG_SHELL@
+CDPATH		= .
+
+@SET_MAKE@
+prefix		= @prefix@
+exec_prefix	= @exec_prefix@
+top_srcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= .:$(srcdir)
+
+top_builddir	= ..
+
+# see po/makefile
+localedir	= @NLS_DATADIR@/locale
+
+# Symbols which the configure script can set in each makefile:
+CC		= @CC@
+CPP		= @CPP@
+CFLAGS		= @CFLAGS@
+DEFS		= @DEFS@
+CHARSET_DEFS	= @CHARSET_DEFS@
+EXTRA_CPPFLAGS	= @EXTRA_CPPFLAGS@
+CPPFLAGS	= @CPPFLAGS@
+
+x		= @EXEEXT@
+o		= .@OBJEXT@
+
+BUILD_CC	= @BUILD_CC@
+BUILD_CPP	= @BUILD_CPP@
+BUILD_CFLAGS	= @BUILD_CFLAGS@
+BUILD_CPPFLAGS	= @BUILD_CPPFLAGS@ @DEFS@
+BUILD_EXEEXT	= @BUILD_EXEEXT@
+
+YACC		= @YACC@
+
+LIBS		= @LIBS@ $(RESOLVLIB) $(WAISLIB) $(SITE_LIBS)
+LDFLAGS		= @EXTRA_LDFLAGS@ @LDFLAGS@
+
+# Symbols inherited from the top-level makefile
+RESOLVLIB	= # FIXME: set in parent makefile
+SITE_DEFS	= # FIXME: set in parent makefile
+SITE_LIBS	= # FIXME: set in parent makefile
+WAISLIB		= # FIXME: set in parent makefile
+
+WWWINC		= WWW/Library/Implementation
+WWWLIB		= $(top_builddir)/WWW/Library/Implementation/libwww.a
+
+INTLLIB		= @INTLLIBS@
+INTLDIR_CPPFLAGS= @INTLDIR_CPPFLAGS@-I$(top_srcdir)/intl
+
+CPP_OPTS	= $(DEFS) $(CHARSET_DEFS) \
+		-DLOCALEDIR=\"$(localedir)\" \
+		-I. \
+		-I$(top_builddir) \
+		-Ichrtrans \
+		-I$(srcdir)/chrtrans \
+		-I$(top_srcdir) \
+		-I$(top_srcdir)/src \
+		-I$(top_srcdir)/$(WWWINC) \
+		$(INTLDIR_CPPFLAGS) $(SITE_DEFS) $(EXTRA_CPPFLAGS) $(CPPFLAGS)
+CC_OPTS		= $(CPP_OPTS) $(CFLAGS)
+
+LINT		= @LINT@
+LINTOPTS	=
+
+COMPRESS_PROG	=@COMPRESS_PROG@
+COMPRESS_EXT	=@COMPRESS_EXT@
+
+CHARTRANS_OBJS	= UCdomap$o UCAux$o UCAuto$o
+OBJS		= \
+	LYebcdic$o \
+	LYClean$o LYShowInfo$o LYEdit$o LYStrings$o LYMail$o \
+	HTAlert$o GridText$o LYGetFile$o LYMain$o LYMainLoop$o \
+	LYCurses$o LYBookmark$o LYmktime$o LYUtils$o LYOptions$o \
+	LYReadCFG$o LYSearch$o LYHistory$o LYForms$o LYPrint$o \
+	LYrcFile$o LYDownload$o LYNews$o LYKeymap$o HTML$o \
+	HTFWriter$o HTInit$o DefaultStyle$o LYUpload$o \
+	LYLeaks$o LYexit$o LYJump$o LYList$o LYCgi$o \
+	LYTraversal$o LYEditmap$o LYCharSets$o LYCharUtils$o \
+	LYMap$o LYCookie$o LYStyle$o LYHash$o LYPrettySrc$o \
+	TRSTable$o parsdate$o $(CHARTRANS_OBJS) @EXTRA_OBJS@ @LIBOBJS@
+
+C_SRC		= $(OBJS:$o=.c)
+
+all: lynx$x
+
+.SUFFIXES : $o .i
+
+# yacc builds .c in target directory, not $(srcdir)
+.c$o:
+	@RULE_CC@
+	@ECHO_CC@$(CC) $(CC_OPTS) -c $<
+
+.c.i:
+	@RULE_CC@
+	@ECHO_CC@$(CPP) -C $(CPP_OPTS) $< >$@
+
+lynx$x:   message do_chartrans_stuff $(top_builddir)/LYHelp.h $(OBJS) $(WWWLIB)
+	@echo "Linking and creating Lynx executable"
+	$(CC) $(CC_OPTS) $(LDFLAGS) -o $@  $(OBJS) $(WWWLIB) $(INTLLIB) $(LDFLAGS) $(LIBS)
+	@echo "Copying Lynx executable into top-level directory"
+	rm -f $(top_builddir)/$@
+	cp $@ $(top_builddir)/
+	@echo "Welcome to Lynx!"
+
+message:
+	@echo "Compiling Lynx sources"
+
+do_chartrans_stuff:
+	-cd chrtrans && $(MAKE) \
+		SITE_DEFS="$(SITE_DEFS)" \
+		BUILD_CFLAGS="$(BUILD_CFLAGS)" \
+		BUILD_CPPFLAGS="$(BUILD_CPPFLAGS)" \
+		BUILD_LDFLAGS="$(BUILD_LDFLAGS)" \
+		BUILD_LIBS="$(BUILD_LIBS)" \
+		BUILD_CC="$(BUILD_CC)" tables
+
+lint:
+	$(LINT) $(LINTOPTS) $(CPP_OPTS) $(C_SRC)  2>&1 |tee $(top_builddir)/lint.lynx
+
+clean:
+	rm -f lynx$x core *.core *.leaks *.i *$o *.bak tags TAGS test_*
+	cd chrtrans && $(MAKE) clean
+
+tags:
+	ctags *.[ch]
+
+distclean: clean
+
+CMN=$(top_srcdir)/WWW/Library/Implementation/
+
+HTFWriter$o :		$(top_srcdir)/userdefs.h
+HTInit$o :		$(top_srcdir)/userdefs.h
+LYCharSets$o :		$(top_srcdir)/userdefs.h
+LYGetFile$o :		$(top_srcdir)/userdefs.h
+LYKeymap$o :		$(top_srcdir)/userdefs.h
+LYLeaks$o :		$(CMN)LYLeaks.h $(CMN)HTString.h
+LYMail$o :		$(top_srcdir)/userdefs.h
+LYMain$o :		$(top_srcdir)/userdefs.h $(top_builddir)/lynx_cfg.h
+LYMainLoop$o :		$(top_srcdir)/userdefs.h
+LYOptions$o :		$(top_srcdir)/userdefs.h
+LYReadCFG$o :		$(top_srcdir)/userdefs.h
+LYShowInfo$o :		$(top_builddir)/cfg_defs.h
+LYTraversal$o :		$(top_srcdir)/userdefs.h
+LYUtils$o :		$(top_srcdir)/userdefs.h
+LYmktime$o :		$(top_srcdir)/userdefs.h
+LYrcFile$o :		$(top_srcdir)/userdefs.h
+
+LYIcon$o:
+	windres -i LYIcon.rc -o LYIcon$o -O coff
+
+CHRTR= chrtrans/
+
+TABLES= \
+ $(CHRTR)cp1250_uni.h \
+ $(CHRTR)cp1251_uni.h \
+ $(CHRTR)cp1252_uni.h \
+ $(CHRTR)cp1253_uni.h \
+ $(CHRTR)cp1255_uni.h \
+ $(CHRTR)cp1256_uni.h \
+ $(CHRTR)cp1257_uni.h \
+ $(CHRTR)cp437_uni.h \
+ $(CHRTR)cp737_uni.h \
+ $(CHRTR)cp775_uni.h \
+ $(CHRTR)cp850_uni.h \
+ $(CHRTR)cp852_uni.h \
+ $(CHRTR)cp862_uni.h \
+ $(CHRTR)cp864_uni.h \
+ $(CHRTR)cp866_uni.h \
+ $(CHRTR)cp869_uni.h \
+ $(CHRTR)def7_uni.h \
+ $(CHRTR)dmcs_uni.h \
+ $(CHRTR)hp_uni.h \
+ $(CHRTR)iso01_uni.h \
+ $(CHRTR)iso02_uni.h \
+ $(CHRTR)iso03_uni.h \
+ $(CHRTR)iso04_uni.h \
+ $(CHRTR)iso05_uni.h \
+ $(CHRTR)iso06_uni.h \
+ $(CHRTR)iso07_uni.h \
+ $(CHRTR)iso08_uni.h \
+ $(CHRTR)iso09_uni.h \
+ $(CHRTR)iso10_uni.h \
+ $(CHRTR)iso15_uni.h \
+ $(CHRTR)koi8r_uni.h \
+ $(CHRTR)mac_uni.h \
+ $(CHRTR)mnem_suni.h \
+ $(CHRTR)mnem2_suni.h \
+ $(CHRTR)next_uni.h \
+ $(CHRTR)rfc_suni.h \
+ $(CHRTR)utf8_uni.h \
+ $(CHRTR)viscii_uni.h
+
+$(TABLES):
+	-cd chrtrans && $(MAKE) tables
+
+UCdomap$o :	UCdomap.c \
+		chrtrans/UCkd.h \
+		chrtrans/makeuctb$(BUILD_EXEEXT) \
+		chrtrans/makeuctb.c \
+	UCdomap.h $(CMN)UCMap.h $(TABLES) $(top_srcdir)/userdefs.h
+
+chrtrans/makeuctb$(BUILD_EXEEXT):
+	cd chrtrans; make makeuctb$(BUILD_EXEEXT)
+
+UCAux$o : UCAux.c $(CMN)UCAux.h $(CMN)UCDefs.h
+LYCookie$o : $(top_srcdir)/userdefs.h
+
+test_mktime.o: $(srcdir)/LYmktime.c
+	$(CC) -o $@ $(CC_OPTS) -DTEST_DRIVER -c $(srcdir)/LYmktime.c
+
+# test-driver for LYmktime
+test_mktime: test_mktime.o parsdate.o LYebcdic.o
+	$(CC) -o $@ $(CC_OPTS) test_mktime.o parsdate.o LYebcdic.o
+
+# update generated source (may be in ".", or srcdir)
+parsdate.c : $(srcdir)/parsdate.y
+	$(YACC)  $(srcdir)/parsdate.y 
+	-rm -f $@
+	mv y.tab.c $@
+
+depend : $(TABLES)
+	makedepend -fmakefile -- $(CC_OPTS) -- $(C_SRC)
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
diff --git a/src/makefile.wsl b/src/makefile.wsl
new file mode 100644
index 00000000..5890ec38
--- /dev/null
+++ b/src/makefile.wsl
@@ -0,0 +1,68 @@
+# $LynxId: makefile.wsl,v 1.15 2008/06/30 23:53:42 tom Exp $
+
+OBJS= UCdomap.o UCAux.o UCAuto.o \
+LYClean.o LYShowInfo.o LYEdit.o LYStrings.o \
+LYMail.o HTAlert.o GridText.o LYGetFile.o \
+LYMain.o LYMainLoop.o LYCurses.o LYBookmark.o LYmktime.o LYUtils.o \
+LYOptions.o LYReadCFG.o LYSearch.o LYHistory.o LYSession.o \
+LYForms.o LYPrint.o LYrcFile.o LYDownload.o LYNews.o LYKeymap.o \
+HTML.o HTFWriter.o HTInit.o DefaultStyle.o LYLocal.o LYUpload.o \
+LYLeaks.o LYexit.o LYJump.o LYList.o LYCgi.o LYTraversal.o \
+LYEditmap.o LYCharSets.o LYCharUtils.o LYMap.o LYCookie.o LYExtern.o \
+LYStyle.o LYHash.o LYPrettySrc.o TRSTable.o parsdate.o
+
+CFLAGS= -O1 $(MCFLAGS) -I. -I.. $(SLANGINC)
+
+CC = gcc
+MCFLAGS = -DDISP_PARTIAL -DUSE_ZLIB -DUSE_EXTERNALS \
+-DUSE_SOURCE_CACHE -DUSE_PRETTYSRC \
+-DUSE_SLANG -DACCESS_AUTH -DNO_CUSERID \
+-DNOUSERS -DDOSPATH -DNO_TTYTYPE -DNO_UTMP -I../WWW/library/implement -I../djgpp/tcplib/include \
+-I./chrtrans -I../djgpp/tcplib/include/tcp
+WWWLIB = ../WWW/library/djgpp/libwww.a ../djgpp/tcplib/obj/libtcp.a
+LIBS= -lslang -lz
+CHRTR= ./chrtrans/
+
+all: lynx.exe
+
+lynx.exe:   message $(OBJS) $(WWWLIB)
+	@echo "Linking and creating Lynx executable"
+	$(CC) $(CFLAGS) -o lynx.exe  $(OBJS) $(WWWLIB) $(SLANGLIB) $(LIBS)
+	@echo "Welcome to Lynx!"
+
+message:
+	@echo "Compiling Lynx sources"
+
+dbg:    $(OBJS) $(WWWLIB)
+	@echo "Making Lynx code"
+	$(CC) $(OBJS) $(CFLAGS) $(WWWLIB) $(SLANGLIB) $(LIBS)
+
+lint:
+	lint *.c  > ../lint.out
+
+clean:
+	rm -f lynx.exe core *.[ob]
+
+DefaultStyle.o:	../userdefs.h
+HTFWriter.o:	../userdefs.h
+LYBookmark.o:	../userdefs.h
+LYCharSets.o:	../userdefs.h
+LYCharUtils.o:	../userdefs.h
+LYCookie.o:	../userdefs.h
+LYDownload.o:	../userdefs.h
+LYEditmap.o:	../userdefs.h
+LYExtern.o:	../userdefs.h
+LYGetFile.o:	../userdefs.h
+LYHistory.o:	../userdefs.h
+LYKeymap.o:	../userdefs.h
+LYMain.o:	../userdefs.h
+LYMainLoop.o:	../userdefs.h
+LYOptions.o:	../userdefs.h
+LYReadCFG.o:	../userdefs.h
+LYReadCFG.o:	../userdefs.h
+LYShowInfo.o:	../userdefs.h
+LYStrings.o:	../userdefs.h
+LYTraversal.o:	../userdefs.h
+LYUtils.o:	../userdefs.h
+LYmktime.o:	../userdefs.h
+parsdate.o:	../userdefs.h
diff --git a/src/mktime.c b/src/mktime.c
new file mode 100644
index 00000000..78b88c2f
--- /dev/null
+++ b/src/mktime.c
@@ -0,0 +1,72 @@
+/*
+ * mktime.c -- converts a struct tm into a time_t
+ *
+ * Copyright (C) 1997 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Written by Philippe De Muyter <phdm@macqel.be>.  */
+
+#include	<time.h>
+
+static time_t mkgmtime(register struct tm *t)
+{
+    register short month, year;
+    register time_t result;
+    static int m_to_d[12] =
+    {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
+
+    month = t->tm_mon;
+    year = t->tm_year + month / 12 + 1900;
+    month %= 12;
+    if (month < 0) {
+	year -= 1;
+	month += 12;
+    }
+    result = (year - 1970) * 365 + m_to_d[month];
+    if (month <= 1)
+	year -= 1;
+    result += (year - 1968) / 4;
+    result -= (year - 1900) / 100;
+    result += (year - 1600) / 400;
+    result += t->tm_mday;
+    result -= 1;
+    result *= 24;
+    result += t->tm_hour;
+    result *= 60;
+    result += t->tm_min;
+    result *= 60;
+    result += t->tm_sec;
+    return (result);
+}
+
+/*
+ *  mktime -- convert tm struct to time_t
+ *		if tm_isdst >= 0 use it, else compute it
+ */
+
+time_t
+mktime(struct tm * t)
+{
+    time_t result;
+
+    tzset();
+    result = mkgmtime(t) + timezone;
+    if (t->tm_isdst > 0
+	|| (t->tm_isdst < 0 && localtime(&result)->tm_isdst))
+	result -= 3600;
+    return (result);
+}
diff --git a/src/multinet.opt b/src/multinet.opt
new file mode 100644
index 00000000..97b420f4
--- /dev/null
+++ b/src/multinet.opt
@@ -0,0 +1 @@
+multinet:multinet_socket_library/share
diff --git a/src/parsdate.c b/src/parsdate.c
new file mode 100644
index 00000000..97578ef3
--- /dev/null
+++ b/src/parsdate.c
@@ -0,0 +1,1519 @@
+#ifndef lint
+static const char yysccsid[] = "@(#)yaccpar	1.9 (Berkeley) 02/21/93";
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#define YYBYACC 1
+#define YYMAJOR 1
+#define YYMINOR 9
+#define YYPATCH 20081224
+
+#define YYEMPTY        (-1)
+#define yyclearin      (yychar = YYEMPTY)
+#define yyerrok        (yyerrflag = 0)
+#define YYRECOVERING() (yyerrflag != 0)
+
+/* compatibility with bison */
+#ifdef YYPARSE_PARAM
+/* compatibility with FreeBSD */
+#ifdef YYPARSE_PARAM_TYPE
+#define YYPARSE_DECL() yyparse(YYPARSE_PARAM_TYPE YYPARSE_PARAM)
+#else
+#define YYPARSE_DECL() yyparse(void *YYPARSE_PARAM)
+#endif
+#else
+#define YYPARSE_DECL() yyparse(void)
+#endif /* YYPARSE_PARAM */
+
+extern int YYPARSE_DECL();
+
+static int yygrowstack(void);
+#define YYPREFIX "yy"
+#line 2 "./parsdate.y"
+/*
+ *  $LynxId: parsdate.c,v 1.8 2009/01/01 22:12:42 tom Exp $
+ *
+ *  This module is adapted and extended from tin, to use for LYmktime().
+ *
+ *  Project   : tin - a Usenet reader
+ *  Module    : parsedate.y
+ *  Author    : S. Bellovin, R. $alz, J. Berets, P. Eggert
+ *  Created   : 1990-08-01
+ *  Updated   : 2008-06-30 (by Thomas Dickey, for Lynx)
+ *  Notes     : This grammar has 8 shift/reduce conflicts.
+ *
+ *              Originally written by Steven M. Bellovin <smb@research.att.com>
+ *              while at the University of North Carolina at Chapel Hill.
+ *              Later tweaked by a couple of people on Usenet.  Completely
+ *              overhauled by Rich $alz <rsalz@osf.org> and Jim Berets
+ *              <jberets@bbn.com> in August, 1990.
+ *
+ *              Further revised (removed obsolete constructs and cleaned up
+ *              timezone names) in August, 1991, by Rich.
+ *              Paul Eggert <eggert@twinsun.com> helped in September 1992.
+ *              Roland Rosenfeld added MET DST code in April 1994.
+ *
+ *  Revision  : 1.13
+ *  Copyright : This code is in the public domain and has no copyright.
+ */
+
+/* SUPPRESS 530 */ /* Empty body for statement */
+/* SUPPRESS 593 on yyerrlab */ /* Label was not used */
+/* SUPPRESS 593 on yynewstate */ /* Label was not used */
+/* SUPPRESS 595 on yypvt */ /* Automatic variable may be used before set */
+
+#undef alloca			/* conflicting def may be set by yacc */
+#include <parsdate.h>
+
+/*
+**  Get the number of elements in a fixed-size array, or a pointer just
+**  past the end of it.
+*/
+#define ENDOF(array)	(&array[ARRAY_SIZE(array)])
+
+#ifdef EBCDIC
+#define TO_ASCII(c)	TOASCII(c)
+#define TO_LOCAL(c)	FROMASCII(c)
+#else
+#define TO_ASCII(c)	(c)
+#define TO_LOCAL(c)	(c)
+#endif
+
+#define IS7BIT(x)		((unsigned) TO_ASCII(x) < 128)
+#define CTYPE(isXXXXX, c)	(IS7BIT(c) && isXXXXX(((unsigned char)c)))
+
+typedef char	*PD_STRING;
+
+extern int date_parse(void);
+
+#define yyparse		date_parse
+#define yylex		date_lex
+#define yyerror		date_error
+
+
+    /* See the LeapYears table in Convert. */
+#define EPOCH		1970
+#define END_OF_TIME	2038
+
+    /* Constants for general time calculations. */
+#define DST_OFFSET	1
+#define SECSPERDAY	(24L * 60L * 60L)
+    /* Readability for TABLE stuff. */
+#define HOUR(x)		(x * 60)
+
+#define LPAREN		'('
+#define RPAREN		')'
+
+
+/*
+**  Daylight-savings mode:  on, off, or not yet known.
+*/
+typedef enum _DSTMODE {
+    DSTon, DSToff, DSTmaybe
+} DSTMODE;
+
+/*
+**  Meridian:  am, pm, or 24-hour style.
+*/
+typedef enum _MERIDIAN {
+    MERam, MERpm, MER24
+} MERIDIAN;
+
+
+/*
+**  Global variables.  We could get rid of most of them by using a yacc
+**  union, but this is more efficient.  (This routine predates the
+**  yacc %union construct.)
+*/
+static char	*yyInput;
+static DSTMODE	yyDSTmode;
+static int	yyHaveDate;
+static int	yyHaveRel;
+static int	yyHaveTime;
+static time_t	yyTimezone;
+static time_t	yyDay;
+static time_t	yyHour;
+static time_t	yyMinutes;
+static time_t	yyMonth;
+static time_t	yySeconds;
+static time_t	yyYear;
+static MERIDIAN	yyMeridian;
+static time_t	yyRelMonth;
+static time_t	yyRelSeconds;
+
+static time_t	ToSeconds(time_t, time_t, time_t, MERIDIAN);
+static time_t	Convert(time_t, time_t, time_t, time_t, time_t, time_t, MERIDIAN, DSTMODE);
+static time_t	DSTcorrect(time_t, time_t);
+static time_t	RelativeMonth(time_t, time_t);
+static int	LookupWord(char	*, int);
+static int	date_lex(void);
+static int	GetTimeInfo(TIMEINFO *Now);
+
+/*
+ * The 'date_error()' function is declared here to work around a defect in
+ * bison 1.22, which redefines 'const' further down in this file, making it
+ * impossible to put a prototype here, and the function later.  We're using
+ * 'const' on the parameter to quiet gcc's -Wwrite-strings warning.
+ */
+/*ARGSUSED*/
+static void
+date_error(const char GCC_UNUSED *s)
+{
+    /*NOTREACHED*/
+}
+
+#line 136 "./parsdate.y"
+typedef union {
+    time_t		Number;
+    enum _MERIDIAN	Meridian;
+} YYSTYPE;
+#line 172 "y.tab.c"
+#define tDAY 257
+#define tDAYZONE 258
+#define tMERIDIAN 259
+#define tMONTH 260
+#define tMONTH_UNIT 261
+#define tSEC_UNIT 262
+#define tSNUMBER 263
+#define tUNUMBER 264
+#define tZONE 265
+#define tDST 266
+#define YYERRCODE 256
+static const short yylhs[] = {                           -1,
+    0,    0,    4,    4,    4,    4,    4,    4,    5,    5,
+    5,    5,    5,    2,    2,    2,    2,    2,    1,    6,
+    6,    6,    6,    6,    6,    6,    6,    6,    7,    8,
+    8,    8,    8,    3,    3,
+};
+static const short yylen[] = {                            2,
+    0,    2,    1,    2,    1,    1,    2,    1,    2,    4,
+    4,    6,    6,    1,    1,    2,    2,    1,    1,    3,
+    5,    2,    4,    2,    3,    5,    6,    3,    9,    2,
+    2,    2,    2,    0,    1,
+};
+static const short yydefred[] = {                         1,
+    0,    0,    0,    0,    0,    2,    0,    5,    0,    8,
+    0,    0,    0,   32,   30,   35,    0,   33,   31,    0,
+    0,    0,    9,    0,   19,    0,   18,    4,    7,    0,
+    0,    0,   25,   28,    0,    0,   16,   17,    0,    0,
+    0,   23,    0,   11,   10,    0,    0,   26,    0,    0,
+   21,    0,   27,   13,   12,    0,    0,   29,
+};
+static const short yydgoto[] = {                          1,
+   27,   28,   23,    6,    7,    8,    9,   10,
+};
+static const short yysindex[] = {                         0,
+ -240,  -41, -256, -227,  -45,    0, -251,    0, -251,    0,
+ -254, -249,  -22,    0,    0,    0, -237,    0,    0, -235,
+ -228, -226,    0, -236,    0, -224,    0,    0,    0, -223,
+  -39, -222,    0,    0,  -58,   -7,    0,    0,  -15, -220,
+ -215,    0, -218,    0,    0, -217, -216,    0, -214, -234,
+    0,   -8,    0,    0,    0, -213, -212,    0,
+};
+static const short yyrindex[] = {                         0,
+    0,    0,    0,    0,    5,    0,   26,    0,   31,    0,
+    0,    0,   11,    0,    0,    0,   37,    0,    0,    0,
+    0,    0,    0,   16,    0,   32,    0,    0,    0,    0,
+    0,    0,    0,    0,    1,   21,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    1,
+    0,    0,    0,    0,    0,    0,    0,    0,
+};
+static const short yygindex[] = {                         0,
+  -17,   44,  -31,    0,    0,    0,    0,    0,
+};
+#define YYTABLESIZE 300
+static const short yytable[] = {                         43,
+   34,   22,   12,   45,   34,   41,   24,   13,   38,   30,
+   22,   25,   21,   26,   31,   15,    2,   44,   55,    3,
+   20,   32,    4,    5,   16,    3,   33,   34,   25,   37,
+    6,   14,   54,   14,   15,   35,   24,   36,   25,   46,
+   39,   42,   47,   48,   49,   50,   51,   52,   53,   56,
+   57,   58,   29,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+   16,    0,    0,    0,   25,    0,    0,    0,    0,    0,
+    0,    0,    0,   16,   17,   18,   19,   20,   11,    0,
+   40,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+    0,    0,    0,    0,    0,    0,    0,   34,   34,    0,
+   34,   34,   34,    0,   34,   34,    0,   22,   34,   34,
+   22,    0,   15,   22,   22,   15,    0,   20,   15,   15,
+   20,    0,    3,   20,   20,    3,    0,    6,   14,    3,
+    6,   14,    0,   24,    6,   14,   24,    0,    0,   24,
+};
+static const short yycheck[] = {                         58,
+    0,   47,   44,   35,    0,   45,  258,  264,   26,  264,
+    0,  263,   58,  265,  264,    0,  257,   35,   50,  260,
+    0,   44,  263,  264,  259,    0,  264,  263,  263,  266,
+    0,    0,   50,  261,  262,  264,    0,  264,  263,   47,
+  264,  264,   58,  264,  260,  264,  264,  264,  263,   58,
+  264,  264,    9,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+  259,   -1,   -1,   -1,  263,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,  259,  260,  261,  262,  263,  260,   -1,
+  260,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,   -1,
+   -1,   -1,   -1,   -1,   -1,   -1,   -1,  257,  258,   -1,
+  260,  257,  258,   -1,  264,  265,   -1,  257,  264,  265,
+  260,   -1,  257,  263,  264,  260,   -1,  257,  263,  264,
+  260,   -1,  257,  263,  264,  260,   -1,  257,  257,  264,
+  260,  260,   -1,  257,  264,  264,  260,   -1,   -1,  263,
+};
+#define YYFINAL 1
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+#define YYMAXTOKEN 266
+#if YYDEBUG
+static const char *yyname[] = {
+
+"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,"','","'-'",0,"'/'",0,0,0,0,0,0,0,0,0,0,"':'",0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"tDAY","tDAYZONE",
+"tMERIDIAN","tMONTH","tMONTH_UNIT","tSEC_UNIT","tSNUMBER","tUNUMBER","tZONE",
+"tDST",
+};
+static const char *yyrule[] = {
+"$accept : spec",
+"spec :",
+"spec : spec item",
+"item : time",
+"item : time zone",
+"item : date",
+"item : both",
+"item : both zone",
+"item : rel",
+"time : tUNUMBER o_merid",
+"time : tUNUMBER ':' tUNUMBER o_merid",
+"time : tUNUMBER ':' tUNUMBER numzone",
+"time : tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid",
+"time : tUNUMBER ':' tUNUMBER ':' tUNUMBER numzone",
+"zone : tZONE",
+"zone : tDAYZONE",
+"zone : tDAYZONE tDST",
+"zone : tZONE numzone",
+"zone : numzone",
+"numzone : tSNUMBER",
+"date : tUNUMBER '/' tUNUMBER",
+"date : tUNUMBER '/' tUNUMBER '/' tUNUMBER",
+"date : tMONTH tUNUMBER",
+"date : tMONTH tUNUMBER ',' tUNUMBER",
+"date : tUNUMBER tMONTH",
+"date : tUNUMBER tMONTH tUNUMBER",
+"date : tDAY ',' tUNUMBER tMONTH tUNUMBER",
+"date : tDAY ',' tUNUMBER '-' tMONTH tSNUMBER",
+"date : tUNUMBER tSNUMBER tSNUMBER",
+"both : tDAY tMONTH tUNUMBER tUNUMBER ':' tUNUMBER ':' tUNUMBER tUNUMBER",
+"rel : tSNUMBER tSEC_UNIT",
+"rel : tUNUMBER tSEC_UNIT",
+"rel : tSNUMBER tMONTH_UNIT",
+"rel : tUNUMBER tMONTH_UNIT",
+"o_merid :",
+"o_merid : tMERIDIAN",
+
+};
+#endif
+#if YYDEBUG
+#include <stdio.h>
+#endif
+
+/* define the initial stack-sizes */
+#ifdef YYSTACKSIZE
+#undef YYMAXDEPTH
+#define YYMAXDEPTH  YYSTACKSIZE
+#else
+#ifdef YYMAXDEPTH
+#define YYSTACKSIZE YYMAXDEPTH
+#else
+#define YYSTACKSIZE 500
+#define YYMAXDEPTH  500
+#endif
+#endif
+
+#define YYINITSTACKSIZE 500
+
+int      yydebug;
+int      yynerrs;
+int      yyerrflag;
+int      yychar;
+short   *yyssp;
+YYSTYPE *yyvsp;
+YYSTYPE  yyval;
+YYSTYPE  yylval;
+
+/* variables for the parser stack */
+static short   *yyss;
+static short   *yysslim;
+static YYSTYPE *yyvs;
+static unsigned yystacksize;
+#line 358 "./parsdate.y"
+
+/*
+**  An entry in the lexical lookup table.
+*/
+typedef struct _TABLE {
+    const char *name;
+    int		type;
+    time_t	value;
+} TABLE;
+
+/* Month and day table. */
+static const TABLE MonthDayTable[] = {
+    { "january",	tMONTH,  1 },
+    { "february",	tMONTH,  2 },
+    { "march",		tMONTH,  3 },
+    { "april",		tMONTH,  4 },
+    { "may",		tMONTH,  5 },
+    { "june",		tMONTH,  6 },
+    { "july",		tMONTH,  7 },
+    { "august",		tMONTH,  8 },
+    { "september",	tMONTH,  9 },
+    { "october",	tMONTH, 10 },
+    { "november",	tMONTH, 11 },
+    { "december",	tMONTH, 12 },
+	/* The value of the day isn't used... */
+    { "sunday",		tDAY, 0 },
+    { "monday",		tDAY, 0 },
+    { "tuesday",	tDAY, 0 },
+    { "wednesday",	tDAY, 0 },
+    { "thursday",	tDAY, 0 },
+    { "friday",		tDAY, 0 },
+    { "saturday",	tDAY, 0 },
+};
+
+/* Time units table. */
+static const TABLE	UnitsTable[] = {
+    { "year",		tMONTH_UNIT,	12 },
+    { "month",		tMONTH_UNIT,	1 },
+    { "week",		tSEC_UNIT,	7 * 24 * 60 * 60 },
+    { "day",		tSEC_UNIT,	1 * 24 * 60 * 60 },
+    { "hour",		tSEC_UNIT,	60 * 60 },
+    { "minute",		tSEC_UNIT,	60 },
+    { "min",		tSEC_UNIT,	60 },
+    { "second",		tSEC_UNIT,	1 },
+    { "sec",		tSEC_UNIT,	1 },
+};
+
+/* Timezone table. */
+static const TABLE	TimezoneTable[] = {
+    { "gmt",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
+    { "ut",	tZONE,     HOUR( 0) },	/* Universal */
+    { "utc",	tZONE,     HOUR( 0) },	/* Universal Coordinated */
+    { "cut",	tZONE,     HOUR( 0) },	/* Coordinated Universal */
+    { "z",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
+    { "wet",	tZONE,     HOUR( 0) },	/* Western European */
+    { "bst",	tDAYZONE,  HOUR( 0) },	/* British Summer */
+    { "nst",	tZONE,     HOUR(3)+30 }, /* Newfoundland Standard */
+    { "ndt",	tDAYZONE,  HOUR(3)+30 }, /* Newfoundland Daylight */
+    { "ast",	tZONE,     HOUR( 4) },	/* Atlantic Standard */
+    { "adt",	tDAYZONE,  HOUR( 4) },	/* Atlantic Daylight */
+    { "est",	tZONE,     HOUR( 5) },	/* Eastern Standard */
+    { "edt",	tDAYZONE,  HOUR( 5) },	/* Eastern Daylight */
+    { "cst",	tZONE,     HOUR( 6) },	/* Central Standard */
+    { "cdt",	tDAYZONE,  HOUR( 6) },	/* Central Daylight */
+    { "mst",	tZONE,     HOUR( 7) },	/* Mountain Standard */
+    { "mdt",	tDAYZONE,  HOUR( 7) },	/* Mountain Daylight */
+    { "pst",	tZONE,     HOUR( 8) },	/* Pacific Standard */
+    { "pdt",	tDAYZONE,  HOUR( 8) },	/* Pacific Daylight */
+    { "yst",	tZONE,     HOUR( 9) },	/* Yukon Standard */
+    { "ydt",	tDAYZONE,  HOUR( 9) },	/* Yukon Daylight */
+    { "akst",	tZONE,     HOUR( 9) },	/* Alaska Standard */
+    { "akdt",	tDAYZONE,  HOUR( 9) },	/* Alaska Daylight */
+    { "hst",	tZONE,     HOUR(10) },	/* Hawaii Standard */
+    { "hast",	tZONE,     HOUR(10) },	/* Hawaii-Aleutian Standard */
+    { "hadt",	tDAYZONE,  HOUR(10) },	/* Hawaii-Aleutian Daylight */
+    { "ces",	tDAYZONE,  -HOUR(1) },	/* Central European Summer */
+    { "cest",	tDAYZONE,  -HOUR(1) },	/* Central European Summer */
+    { "mez",	tZONE,     -HOUR(1) },	/* Middle European */
+    { "mezt",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
+    { "cet",	tZONE,     -HOUR(1) },	/* Central European */
+    { "met",	tZONE,     -HOUR(1) },	/* Middle European */
+/* Additional aliases for MET / MET DST *************************************/
+    { "mez",    tZONE,     -HOUR(1) },  /* Middle European */
+    { "mewt",   tZONE,     -HOUR(1) },  /* Middle European Winter */
+    { "mest",   tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
+    { "mes",    tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
+    { "mesz",   tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
+    { "msz",    tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
+    { "metdst", tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
+/****************************************************************************/
+    { "eet",	tZONE,     -HOUR(2) },	/* Eastern Europe */
+    { "msk",	tZONE,     -HOUR(3) },	/* Moscow Winter */
+    { "msd",	tDAYZONE,  -HOUR(3) },	/* Moscow Summer */
+    { "wast",	tZONE,     -HOUR(8) },	/* West Australian Standard */
+    { "wadt",	tDAYZONE,  -HOUR(8) },	/* West Australian Daylight */
+    { "hkt",	tZONE,     -HOUR(8) },	/* Hong Kong */
+    { "cct",	tZONE,     -HOUR(8) },	/* China Coast */
+    { "jst",	tZONE,     -HOUR(9) },	/* Japan Standard */
+    { "kst",	tZONE,     -HOUR(9) },	/* Korean Standard */
+    { "kdt",	tZONE,     -HOUR(9) },	/* Korean Daylight */
+    { "cast",	tZONE,     -(HOUR(9)+30) }, /* Central Australian Standard */
+    { "cadt",	tDAYZONE,  -(HOUR(9)+30) }, /* Central Australian Daylight */
+    { "east",	tZONE,     -HOUR(10) },	/* Eastern Australian Standard */
+    { "eadt",	tDAYZONE,  -HOUR(10) },	/* Eastern Australian Daylight */
+    { "nzst",	tZONE,     -HOUR(12) },	/* New Zealand Standard */
+    { "nzdt",	tDAYZONE,  -HOUR(12) },	/* New Zealand Daylight */
+
+    /* For completeness we include the following entries. */
+#if	0
+
+    /* Duplicate names.  Either they conflict with a zone listed above
+     * (which is either more likely to be seen or just been in circulation
+     * longer), or they conflict with another zone in this section and
+     * we could not reasonably choose one over the other. */
+    { "fst",	tZONE,     HOUR( 2) },	/* Fernando De Noronha Standard */
+    { "fdt",	tDAYZONE,  HOUR( 2) },	/* Fernando De Noronha Daylight */
+    { "bst",	tZONE,     HOUR( 3) },	/* Brazil Standard */
+    { "est",	tZONE,     HOUR( 3) },	/* Eastern Standard (Brazil) */
+    { "edt",	tDAYZONE,  HOUR( 3) },	/* Eastern Daylight (Brazil) */
+    { "wst",	tZONE,     HOUR( 4) },	/* Western Standard (Brazil) */
+    { "wdt",	tDAYZONE,  HOUR( 4) },	/* Western Daylight (Brazil) */
+    { "cst",	tZONE,     HOUR( 5) },	/* Chile Standard */
+    { "cdt",	tDAYZONE,  HOUR( 5) },	/* Chile Daylight */
+    { "ast",	tZONE,     HOUR( 5) },	/* Acre Standard */
+    { "adt",	tDAYZONE,  HOUR( 5) },	/* Acre Daylight */
+    { "cst",	tZONE,     HOUR( 5) },	/* Cuba Standard */
+    { "cdt",	tDAYZONE,  HOUR( 5) },	/* Cuba Daylight */
+    { "est",	tZONE,     HOUR( 6) },	/* Easter Island Standard */
+    { "edt",	tDAYZONE,  HOUR( 6) },	/* Easter Island Daylight */
+    { "sst",	tZONE,     HOUR(11) },	/* Samoa Standard */
+    { "ist",	tZONE,     -HOUR(2) },	/* Israel Standard */
+    { "idt",	tDAYZONE,  -HOUR(2) },	/* Israel Daylight */
+    { "idt",	tDAYZONE,  -(HOUR(3)+30) }, /* Iran Daylight */
+    { "ist",	tZONE,     -(HOUR(3)+30) }, /* Iran Standard */
+    { "cst",	 tZONE,     -HOUR(8) },	/* China Standard */
+    { "cdt",	 tDAYZONE,  -HOUR(8) },	/* China Daylight */
+    { "sst",	 tZONE,     -HOUR(8) },	/* Singapore Standard */
+
+    /* Dubious (e.g., not in Olson's TIMEZONE package) or obsolete. */
+    { "gst",	tZONE,     HOUR( 3) },	/* Greenland Standard */
+    { "wat",	tZONE,     -HOUR(1) },	/* West Africa */
+    { "at",	tZONE,     HOUR( 2) },	/* Azores */
+    { "gst",	tZONE,     -HOUR(10) },	/* Guam Standard */
+    { "nft",	tZONE,     HOUR(3)+30 }, /* Newfoundland */
+    { "idlw",	tZONE,     HOUR(12) },	/* International Date Line West */
+    { "mewt",	tZONE,     -HOUR(1) },	/* Middle European Winter */
+    { "mest",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
+    { "swt",	tZONE,     -HOUR(1) },	/* Swedish Winter */
+    { "sst",	tDAYZONE,  -HOUR(1) },	/* Swedish Summer */
+    { "fwt",	tZONE,     -HOUR(1) },	/* French Winter */
+    { "fst",	tDAYZONE,  -HOUR(1) },	/* French Summer */
+    { "bt",	tZONE,     -HOUR(3) },	/* Baghdad */
+    { "it",	tZONE,     -(HOUR(3)+30) }, /* Iran */
+    { "zp4",	tZONE,     -HOUR(4) },	/* USSR Zone 3 */
+    { "zp5",	tZONE,     -HOUR(5) },	/* USSR Zone 4 */
+    { "ist",	tZONE,     -(HOUR(5)+30) }, /* Indian Standard */
+    { "zp6",	tZONE,     -HOUR(6) },	/* USSR Zone 5 */
+    { "nst",	tZONE,     -HOUR(7) },	/* North Sumatra */
+    { "sst",	tZONE,     -HOUR(7) },	/* South Sumatra */
+    { "jt",	tZONE,     -(HOUR(7)+30) }, /* Java (3pm in Cronusland!) */
+    { "nzt",	tZONE,     -HOUR(12) },	/* New Zealand */
+    { "idle",	tZONE,     -HOUR(12) },	/* International Date Line East */
+    { "cat",	tZONE,     HOUR(10) },	/* -- expired 1967 */
+    { "nt",	tZONE,     HOUR(11) },	/* -- expired 1967 */
+    { "ahst",	tZONE,     HOUR(10) },	/* -- expired 1983 */
+    { "hdt",	tDAYZONE,  HOUR(10) },	/* -- expired 1986 */
+#endif	/* 0 */
+};
+
+static time_t
+ToSeconds(
+    time_t	Hours,
+    time_t	Minutes,
+    time_t	Seconds,
+    MERIDIAN	Meridian)
+{
+    if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 61)
+	return -1;
+    if (Meridian == MER24) {
+	if (Hours < 0 || Hours > 23)
+	    return -1;
+    } else {
+	if (Hours < 1 || Hours > 12)
+		return -1;
+	if (Hours == 12)
+		Hours = 0;
+	if (Meridian == MERpm)
+		Hours += 12;
+    }
+    return (Hours * 60L + Minutes) * 60L + Seconds;
+}
+
+
+static time_t
+Convert(
+    time_t	Month,
+    time_t	Day,
+    time_t	Year,
+    time_t	Hours,
+    time_t	Minutes,
+    time_t	Seconds,
+    MERIDIAN	Meridian,
+    DSTMODE	dst)
+{
+    static const int	DaysNormal[13] = {
+	0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+    };
+    static const int	DaysLeap[13] = {
+	0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+    };
+    static const int	LeapYears[] = {
+	1972, 1976, 1980, 1984, 1988, 1992, 1996,
+	2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036
+    };
+    const int *yp;
+    const int *mp;
+    int i;
+    time_t Julian;
+    time_t tod;
+
+    if (Year < 0)
+	Year = -Year;
+    if (Year < 70)
+        Year += 2000;
+    if (Year < 100)
+	Year += 1900;
+    if (Year < EPOCH)
+	Year += 100;
+    for (mp = DaysNormal, yp = LeapYears; yp < ENDOF(LeapYears); yp++)
+	if (Year == *yp) {
+	    mp = DaysLeap;
+	    break;
+	}
+    if (Year < EPOCH || Year > END_OF_TIME
+     || Month < 1 || Month > 12
+     /* NOSTRICT */ /* conversion from long may lose accuracy */
+     || Day < 1 || Day > mp[(int)Month]) {
+	return -1;
+    }
+
+    Julian = Day - 1 + (Year - EPOCH) * 365;
+    for (yp = LeapYears; yp < ENDOF(LeapYears); yp++, Julian++) {
+	if (Year <= *yp)
+	    break;
+    }
+    for (i = 1; i < Month; i++)
+	Julian += *++mp;
+    Julian *= SECSPERDAY;
+    Julian += yyTimezone * 60L;
+    if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) {
+	return -1;
+    }
+    Julian += tod;
+    tod = Julian;
+    if (dst == DSTon || (dst == DSTmaybe && localtime(&tod)->tm_isdst))
+	Julian -= DST_OFFSET * 60 * 60;
+    return Julian;
+}
+
+
+static time_t
+DSTcorrect(
+    time_t	Start,
+    time_t	Future)
+{
+    time_t	StartDay;
+    time_t	FutureDay;
+
+    StartDay = (localtime(&Start)->tm_hour + 1) % 24;
+    FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
+    return (Future - Start) + (StartDay - FutureDay) * DST_OFFSET * 60 * 60;
+}
+
+
+static time_t
+RelativeMonth(
+    time_t	Start,
+    time_t	RelMonth)
+{
+    struct tm	*tm;
+    time_t	Month;
+    time_t	Year;
+
+    tm = localtime(&Start);
+    Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
+    Year = Month / 12 + 1900;
+    Month = Month % 12 + 1;
+    return DSTcorrect(Start,
+	    Convert(Month, (time_t)tm->tm_mday, Year,
+		(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
+		MER24, DSTmaybe));
+}
+
+
+static int
+LookupWord(
+    char *buff,
+    int length)
+{
+    char	*p;
+    const char *q;
+    const TABLE *tp;
+    int	c;
+
+    p = buff;
+    c = p[0];
+
+    /* See if we have an abbreviation for a month. */
+    if (length == 3 || (length == 4 && p[3] == '.')) {
+	for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) {
+	    q = tp->name;
+	    if (c == q[0] && p[1] == q[1] && p[2] == q[2]) {
+		yylval.Number = tp->value;
+		return tp->type;
+	    }
+	}
+    } else {
+	for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) {
+	    if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
+		yylval.Number = tp->value;
+		return tp->type;
+	    }
+	}
+    }
+
+    /* Try for a timezone. */
+    for (tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++) {
+	if (c == tp->name[0] && p[1] == tp->name[1]
+	 && strcmp(p, tp->name) == 0) {
+	    yylval.Number = tp->value;
+	    return tp->type;
+	}
+    }
+
+    if (strcmp(buff, "dst") == 0)
+      return tDST;
+
+    /* Try the units table. */
+    for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++) {
+	if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
+	    yylval.Number = tp->value;
+	    return tp->type;
+	}
+    }
+
+    /* Strip off any plural and try the units table again. */
+    if (--length > 0 && p[length] == 's') {
+	p[length] = '\0';
+	for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++) {
+	    if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
+		p[length] = 's';
+		yylval.Number = tp->value;
+		return tp->type;
+	    }
+	}
+	p[length] = 's';
+    }
+    length++;
+
+    /* Drop out any periods. */
+    for (p = buff, q = (PD_STRING)buff; *q; q++) {
+	if (*q != '.')
+	    *p++ = *q;
+    }
+    *p = '\0';
+
+    /* Try the meridians. */
+    if (buff[1] == 'm' && buff[2] == '\0') {
+	if (buff[0] == 'a') {
+	    yylval.Meridian = MERam;
+	    return tMERIDIAN;
+	}
+	if (buff[0] == 'p') {
+	    yylval.Meridian = MERpm;
+	    return tMERIDIAN;
+	}
+    }
+
+    /* If we saw any periods, try the timezones again. */
+    if (p - buff != length) {
+	c = buff[0];
+	for (p = buff, tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++) {
+	    if (c == tp->name[0] && p[1] == tp->name[1]
+	    && strcmp(p, tp->name) == 0) {
+		yylval.Number = tp->value;
+		return tp->type;
+	    }
+	}
+    }
+
+    /* Unknown word -- assume GMT timezone. */
+    yylval.Number = 0;
+    return tZONE;
+}
+
+
+/*
+ * This returns characters as-is (the ones that are not part of some token),
+ * and codes greater than 256 (the token values).
+ *
+ * yacc generates tables that may use the character value.  In particular,
+ * byacc's yycheck[] table contains integer values for the expected codes from
+ * this function, which (unless byacc is run locally) are ASCII codes.
+ *
+ * The TO_LOCAL() function assumes its input is in ASCII, and the output is
+ * whatever native encoding is used on the machine, e.g., EBCDIC.
+ *
+ * The TO_ASCII() function is the inverse of TO_LOCAL().
+ */
+static int
+date_lex(void)
+{
+    int		c;
+    char	*p;
+    char	buff[20];
+    int		sign;
+    int		i;
+    int		nesting;
+
+    for(;;) {
+	/* Get first character after the whitespace. */
+	for(;;) {
+	    while (CTYPE(isspace, *yyInput))
+		yyInput++;
+	    c = *yyInput;
+
+	    /* Ignore RFC 822 comments, typically time zone names. */
+	    if (c != LPAREN)
+		break;
+	    for (nesting = 1;
+		 (c = *++yyInput) != RPAREN || --nesting;
+		 ) {
+		if (c == LPAREN) {
+		    nesting++;
+		} else if (!IS7BIT(c) || c == '\0' || c == '\r'
+		        || (c == '\\'
+			 && ((c = *++yyInput) == '\0'
+			  || !IS7BIT(c)))) {
+		    /* Lexical error: bad comment. */
+		    return '?';
+		}
+	    }
+	    yyInput++;
+	}
+
+	/* A number? */
+	if (CTYPE(isdigit, c) || c == '-' || c == '+') {
+	    if (c == '-' || c == '+') {
+		sign = c == '-' ? -1 : 1;
+		yyInput++;
+		if (!CTYPE(isdigit, *yyInput)) {
+		    /* Return the isolated plus or minus sign. */
+		    --yyInput;
+		    return *yyInput++;
+		}
+	    } else {
+		sign = 0;
+	    }
+	    for (p = buff;
+		 (c = *yyInput++) != '\0' && CTYPE(isdigit, c);
+		 ) {
+		if (p < &buff[sizeof buff - 1])
+		    *p++ = (char) c;
+	    }
+	    *p = '\0';
+	    i = atoi(buff);
+
+	    yyInput--;
+	    yylval.Number = sign < 0 ? -i : i;
+	    return sign ? tSNUMBER : tUNUMBER;
+	}
+
+	/* A word? */
+	if (CTYPE(isalpha, c)) {
+	    for (p = buff;
+		 (c = *yyInput++) == '.' || CTYPE(isalpha, c);
+		 ) {
+		if (p < &buff[sizeof buff - 1])
+		    *p++ = (char) (CTYPE(isupper, c) ? tolower(c) : c);
+	    }
+	    *p = '\0';
+	    yyInput--;
+	    return LookupWord(buff, p - buff);
+	}
+
+	return *yyInput++;
+    }
+}
+
+
+static int
+GetTimeInfo(
+    TIMEINFO		*Now)
+{
+    static time_t	LastTime;
+    static long		LastTzone;
+    struct tm		*tm;
+#if	defined(HAVE_GETTIMEOFDAY)
+    struct timeval	tv;
+#endif	/* defined(HAVE_GETTIMEOFDAY) */
+#if	defined(DONT_HAVE_TM_GMTOFF)
+    struct tm		local;
+    struct tm		gmt;
+#endif	/* !defined(DONT_HAVE_TM_GMTOFF) */
+
+    /* Get the basic time. */
+#if defined(HAVE_GETTIMEOFDAY)
+    if (gettimeofday(&tv, (struct timezone *)NULL) == -1)
+	return -1;
+    Now->time = tv.tv_sec;
+    Now->usec = tv.tv_usec;
+#else
+    /* Can't check for -1 since that might be a time, I guess. */
+    (void)time(&Now->time);
+    Now->usec = 0;
+#endif /* defined(HAVE_GETTIMEOFDAY) */
+
+    /* Now get the timezone if it's been an hour since the last time. */
+    if (Now->time - LastTime > 60 * 60) {
+	LastTime = Now->time;
+	if ((tm = localtime(&Now->time)) == NULL)
+	    return -1;
+#if	defined(DONT_HAVE_TM_GMTOFF)
+	/* To get the timezone, compare localtime with GMT. */
+	local = *tm;
+	if ((tm = gmtime(&Now->time)) == NULL)
+	    return -1;
+	gmt = *tm;
+
+	/* Assume we are never more than 24 hours away. */
+	LastTzone = gmt.tm_yday - local.tm_yday;
+	if (LastTzone > 1)
+	    LastTzone = -24;
+	else if (LastTzone < -1)
+	    LastTzone = 24;
+	else
+	    LastTzone *= 24;
+
+	/* Scale in the hours and minutes; ignore seconds. */
+	LastTzone += gmt.tm_hour - local.tm_hour;
+	LastTzone *= 60;
+	LastTzone += gmt.tm_min - local.tm_min;
+#else
+	LastTzone =  (0 - tm->tm_gmtoff) / 60;
+#endif	/* defined(DONT_HAVE_TM_GMTOFF) */
+    }
+    Now->tzone = LastTzone;
+    return 0;
+}
+
+
+time_t
+parsedate(
+    char		*p,
+    TIMEINFO		*now)
+{
+    struct tm		*tm;
+    TIMEINFO		ti;
+    time_t		Start;
+
+    yyInput = p;
+    if (now == NULL) {
+	now = &ti;
+	(void)GetTimeInfo(&ti);
+    }
+
+    tm = localtime(&now->time);
+    yyYear = tm->tm_year + 1900;
+    yyMonth = tm->tm_mon + 1;
+    yyDay = tm->tm_mday;
+    yyTimezone = now->tzone;
+    if (tm->tm_isdst)                   /* Correct timezone offset for DST */
+      yyTimezone += DST_OFFSET * 60;
+    yyDSTmode = DSTmaybe;
+    yyHour = 0;
+    yyMinutes = 0;
+    yySeconds = 0;
+    yyMeridian = MER24;
+    yyRelSeconds = 0;
+    yyRelMonth = 0;
+    yyHaveDate = 0;
+    yyHaveRel = 0;
+    yyHaveTime = 0;
+
+    if (date_parse() || yyHaveTime > 1 || yyHaveDate > 1)
+	return -1;
+
+    if (yyHaveDate || yyHaveTime) {
+	Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
+		    yyMeridian, yyDSTmode);
+	if (Start < 0)
+	    return -1;
+    } else {
+	Start = now->time;
+	if (!yyHaveRel)
+	    Start -= (tm->tm_hour * 60L + tm->tm_min) * 60L + tm->tm_sec;
+    }
+
+    Start += yyRelSeconds;
+    if (yyRelMonth)
+	Start += RelativeMonth(Start, yyRelMonth);
+
+    /* Have to do *something* with a legitimate -1 so it's distinguishable
+     * from the error return value.  (Alternately could set errno on error.) */
+    return Start == -1 ? 0 : Start;
+}
+#line 989 "y.tab.c"
+/* allocate initial stack or double stack size, up to YYMAXDEPTH */
+static int yygrowstack(void)
+{
+    int i;
+    unsigned newsize;
+    short *newss;
+    YYSTYPE *newvs;
+
+    if ((newsize = yystacksize) == 0)
+        newsize = YYINITSTACKSIZE;
+    else if (newsize >= YYMAXDEPTH)
+        return -1;
+    else if ((newsize *= 2) > YYMAXDEPTH)
+        newsize = YYMAXDEPTH;
+
+    i = yyssp - yyss;
+    newss = (yyss != 0)
+          ? (short *)realloc(yyss, newsize * sizeof(*newss))
+          : (short *)malloc(newsize * sizeof(*newss));
+    if (newss == 0)
+        return -1;
+
+    yyss  = newss;
+    yyssp = newss + i;
+    newvs = (yyvs != 0)
+          ? (YYSTYPE *)realloc(yyvs, newsize * sizeof(*newvs))
+          : (YYSTYPE *)malloc(newsize * sizeof(*newvs));
+    if (newvs == 0)
+        return -1;
+
+    yyvs = newvs;
+    yyvsp = newvs + i;
+    yystacksize = newsize;
+    yysslim = yyss + newsize - 1;
+    return 0;
+}
+
+#define YYABORT  goto yyabort
+#define YYREJECT goto yyabort
+#define YYACCEPT goto yyaccept
+#define YYERROR  goto yyerrlab
+
+int
+YYPARSE_DECL()
+{
+    int yym, yyn, yystate;
+#if YYDEBUG
+    const char *yys;
+
+    if ((yys = getenv("YYDEBUG")) != 0)
+    {
+        yyn = *yys;
+        if (yyn >= '0' && yyn <= '9')
+            yydebug = yyn - '0';
+    }
+#endif
+
+    yynerrs = 0;
+    yyerrflag = 0;
+    yychar = YYEMPTY;
+    yystate = 0;
+
+    if (yyss == NULL && yygrowstack()) goto yyoverflow;
+    yyssp = yyss;
+    yyvsp = yyvs;
+    yystate = 0;
+    *yyssp = 0;
+
+yyloop:
+    if ((yyn = yydefred[yystate]) != 0) goto yyreduce;
+    if (yychar < 0)
+    {
+        if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+        if (yydebug)
+        {
+            yys = 0;
+            if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+            if (!yys) yys = "illegal-symbol";
+            printf("%sdebug: state %d, reading %d (%s)\n",
+                    YYPREFIX, yystate, yychar, yys);
+        }
+#endif
+    }
+    if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&
+            yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+    {
+#if YYDEBUG
+        if (yydebug)
+            printf("%sdebug: state %d, shifting to state %d\n",
+                    YYPREFIX, yystate, yytable[yyn]);
+#endif
+        if (yyssp >= yysslim && yygrowstack())
+        {
+            goto yyoverflow;
+        }
+        yystate = yytable[yyn];
+        *++yyssp = yytable[yyn];
+        *++yyvsp = yylval;
+        yychar = YYEMPTY;
+        if (yyerrflag > 0)  --yyerrflag;
+        goto yyloop;
+    }
+    if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&
+            yyn <= YYTABLESIZE && yycheck[yyn] == yychar)
+    {
+        yyn = yytable[yyn];
+        goto yyreduce;
+    }
+    if (yyerrflag) goto yyinrecovery;
+
+    yyerror("syntax error");
+
+    goto yyerrlab;
+
+yyerrlab:
+    ++yynerrs;
+
+yyinrecovery:
+    if (yyerrflag < 3)
+    {
+        yyerrflag = 3;
+        for (;;)
+        {
+            if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 &&
+                    yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)
+            {
+#if YYDEBUG
+                if (yydebug)
+                    printf("%sdebug: state %d, error recovery shifting\
+ to state %d\n", YYPREFIX, *yyssp, yytable[yyn]);
+#endif
+                if (yyssp >= yysslim && yygrowstack())
+                {
+                    goto yyoverflow;
+                }
+                yystate = yytable[yyn];
+                *++yyssp = yytable[yyn];
+                *++yyvsp = yylval;
+                goto yyloop;
+            }
+            else
+            {
+#if YYDEBUG
+                if (yydebug)
+                    printf("%sdebug: error recovery discarding state %d\n",
+                            YYPREFIX, *yyssp);
+#endif
+                if (yyssp <= yyss) goto yyabort;
+                --yyssp;
+                --yyvsp;
+            }
+        }
+    }
+    else
+    {
+        if (yychar == 0) goto yyabort;
+#if YYDEBUG
+        if (yydebug)
+        {
+            yys = 0;
+            if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+            if (!yys) yys = "illegal-symbol";
+            printf("%sdebug: state %d, error recovery discards token %d (%s)\n",
+                    YYPREFIX, yystate, yychar, yys);
+        }
+#endif
+        yychar = YYEMPTY;
+        goto yyloop;
+    }
+
+yyreduce:
+#if YYDEBUG
+    if (yydebug)
+        printf("%sdebug: state %d, reducing by rule %d (%s)\n",
+                YYPREFIX, yystate, yyn, yyrule[yyn]);
+#endif
+    yym = yylen[yyn];
+    if (yym)
+        yyval = yyvsp[1-yym];
+    else
+        memset(&yyval, 0, sizeof yyval);
+    switch (yyn)
+    {
+case 3:
+#line 154 "./parsdate.y"
+	{
+	    yyHaveTime++;
+#if	defined(lint)
+	    /* I am compulsive about lint natterings... */
+	    if (yyHaveTime == -1) {
+		YYERROR;
+	    }
+#endif	/* defined(lint) */
+	}
+break;
+case 4:
+#line 163 "./parsdate.y"
+	{
+	    yyHaveTime++;
+	    yyTimezone = yyvsp[0].Number;
+	}
+break;
+case 5:
+#line 167 "./parsdate.y"
+	{
+	    yyHaveDate++;
+	}
+break;
+case 6:
+#line 170 "./parsdate.y"
+	{
+	    yyHaveDate++;
+	    yyHaveTime++;
+	}
+break;
+case 7:
+#line 174 "./parsdate.y"
+	{
+	    yyHaveDate++;
+	    yyHaveTime++;
+	    yyTimezone = yyvsp[0].Number;
+	}
+break;
+case 8:
+#line 179 "./parsdate.y"
+	{
+	    yyHaveRel = 1;
+	}
+break;
+case 9:
+#line 184 "./parsdate.y"
+	{
+	    if (yyvsp[-1].Number < 100) {
+		yyHour = yyvsp[-1].Number;
+		yyMinutes = 0;
+	    }
+	    else {
+		yyHour = yyvsp[-1].Number / 100;
+		yyMinutes = yyvsp[-1].Number % 100;
+	    }
+	    yySeconds = 0;
+	    yyMeridian = yyvsp[0].Meridian;
+	}
+break;
+case 10:
+#line 196 "./parsdate.y"
+	{
+	    yyHour = yyvsp[-3].Number;
+	    yyMinutes = yyvsp[-1].Number;
+	    yySeconds = 0;
+	    yyMeridian = yyvsp[0].Meridian;
+	}
+break;
+case 11:
+#line 202 "./parsdate.y"
+	{
+	    yyHour = yyvsp[-3].Number;
+	    yyMinutes = yyvsp[-1].Number;
+	    yyTimezone = yyvsp[0].Number;
+	    yyMeridian = MER24;
+	    yyDSTmode = DSToff;
+	}
+break;
+case 12:
+#line 209 "./parsdate.y"
+	{
+	    yyHour = yyvsp[-5].Number;
+	    yyMinutes = yyvsp[-3].Number;
+	    yySeconds = yyvsp[-1].Number;
+	    yyMeridian = yyvsp[0].Meridian;
+	}
+break;
+case 13:
+#line 215 "./parsdate.y"
+	{
+	    yyHour = yyvsp[-5].Number;
+	    yyMinutes = yyvsp[-3].Number;
+	    yySeconds = yyvsp[-1].Number;
+	    yyTimezone = yyvsp[0].Number;
+	    yyMeridian = MER24;
+	    yyDSTmode = DSToff;
+	}
+break;
+case 14:
+#line 225 "./parsdate.y"
+	{
+	    yyval.Number = yyvsp[0].Number;
+	    yyDSTmode = DSToff;
+	}
+break;
+case 15:
+#line 229 "./parsdate.y"
+	{
+	    yyval.Number = yyvsp[0].Number;
+	    yyDSTmode = DSTon;
+	}
+break;
+case 16:
+#line 233 "./parsdate.y"
+	{
+	    yyTimezone = yyvsp[-1].Number;
+	    yyDSTmode = DSTon;
+	}
+break;
+case 17:
+#line 237 "./parsdate.y"
+	{
+	    /* Only allow "GMT+300" and "GMT-0800" */
+	    if (yyvsp[-1].Number != 0) {
+		YYABORT;
+	    }
+	    yyval.Number = yyvsp[0].Number;
+	    yyDSTmode = DSToff;
+	}
+break;
+case 18:
+#line 245 "./parsdate.y"
+	{
+	    yyval.Number = yyvsp[0].Number;
+	    yyDSTmode = DSToff;
+	}
+break;
+case 19:
+#line 251 "./parsdate.y"
+	{
+	    int	i;
+
+	    /* Unix and GMT and numeric timezones -- a little confusing. */
+	    if ((int)yyvsp[0].Number < 0) {
+		/* Don't work with negative modulus. */
+		yyvsp[0].Number = -(int)yyvsp[0].Number;
+		if (yyvsp[0].Number > 9999 || (i = yyvsp[0].Number % 100) >= 60) {
+			YYABORT;
+		}
+		yyval.Number = (yyvsp[0].Number / 100) * 60 + i;
+	    }
+	    else {
+		if (yyvsp[0].Number > 9999 || (i = yyvsp[0].Number % 100) >= 60) {
+			YYABORT;
+		}
+		yyval.Number = -((yyvsp[0].Number / 100) * 60 + i);
+	    }
+	}
+break;
+case 20:
+#line 272 "./parsdate.y"
+	{
+	    yyMonth = yyvsp[-2].Number;
+	    yyDay = yyvsp[0].Number;
+	}
+break;
+case 21:
+#line 276 "./parsdate.y"
+	{
+	    if (yyvsp[-4].Number > 100) {
+		yyYear = yyvsp[-4].Number;
+		yyMonth = yyvsp[-2].Number;
+		yyDay = yyvsp[0].Number;
+	    }
+	    else {
+		yyMonth = yyvsp[-4].Number;
+		yyDay = yyvsp[-2].Number;
+		yyYear = yyvsp[0].Number;
+	    }
+	}
+break;
+case 22:
+#line 288 "./parsdate.y"
+	{
+	    yyMonth = yyvsp[-1].Number;
+	    yyDay = yyvsp[0].Number;
+	}
+break;
+case 23:
+#line 292 "./parsdate.y"
+	{
+	    yyMonth = yyvsp[-3].Number;
+	    yyDay = yyvsp[-2].Number;
+	    yyYear = yyvsp[0].Number;
+	}
+break;
+case 24:
+#line 297 "./parsdate.y"
+	{
+	    yyDay = yyvsp[-1].Number;
+	    yyMonth = yyvsp[0].Number;
+	}
+break;
+case 25:
+#line 301 "./parsdate.y"
+	{
+	    yyDay = yyvsp[-2].Number;
+	    yyMonth = yyvsp[-1].Number;
+	    yyYear = yyvsp[0].Number;
+	}
+break;
+case 26:
+#line 306 "./parsdate.y"
+	{
+	    yyDay = yyvsp[-2].Number;
+	    yyMonth = yyvsp[-1].Number;
+	    yyYear = yyvsp[0].Number;
+	}
+break;
+case 27:
+#line 311 "./parsdate.y"
+	{
+	    yyDay = yyvsp[-3].Number;
+	    yyMonth = yyvsp[-1].Number;
+	    yyYear = -yyvsp[0].Number;
+	}
+break;
+case 28:
+#line 316 "./parsdate.y"
+	{
+	    yyDay = yyvsp[-2].Number;
+	    yyMonth = -yyvsp[-1].Number;
+	    yyYear = -yyvsp[0].Number;
+	    yyDSTmode = DSToff;	/* assume midnight if no time given */
+	    yyTimezone = 0;	/* Lynx assumes GMT for this format */
+	}
+break;
+case 29:
+#line 325 "./parsdate.y"
+	{
+	    yyMonth = yyvsp[-7].Number;
+	    yyDay = yyvsp[-6].Number;
+	    yyYear = yyvsp[0].Number;
+	    yyHour = yyvsp[-5].Number;
+	    yyMinutes = yyvsp[-3].Number;
+	    yySeconds = yyvsp[-1].Number;
+	}
+break;
+case 30:
+#line 335 "./parsdate.y"
+	{
+	    yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number;
+	}
+break;
+case 31:
+#line 338 "./parsdate.y"
+	{
+	    yyRelSeconds += yyvsp[-1].Number * yyvsp[0].Number;
+	}
+break;
+case 32:
+#line 341 "./parsdate.y"
+	{
+	    yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number;
+	}
+break;
+case 33:
+#line 344 "./parsdate.y"
+	{
+	    yyRelMonth += yyvsp[-1].Number * yyvsp[0].Number;
+	}
+break;
+case 34:
+#line 349 "./parsdate.y"
+	{
+	    yyval.Meridian = MER24;
+	}
+break;
+case 35:
+#line 352 "./parsdate.y"
+	{
+	    yyval.Meridian = yyvsp[0].Meridian;
+	}
+break;
+#line 1460 "y.tab.c"
+    }
+    yyssp -= yym;
+    yystate = *yyssp;
+    yyvsp -= yym;
+    yym = yylhs[yyn];
+    if (yystate == 0 && yym == 0)
+    {
+#if YYDEBUG
+        if (yydebug)
+            printf("%sdebug: after reduction, shifting from state 0 to\
+ state %d\n", YYPREFIX, YYFINAL);
+#endif
+        yystate = YYFINAL;
+        *++yyssp = YYFINAL;
+        *++yyvsp = yyval;
+        if (yychar < 0)
+        {
+            if ((yychar = yylex()) < 0) yychar = 0;
+#if YYDEBUG
+            if (yydebug)
+            {
+                yys = 0;
+                if (yychar <= YYMAXTOKEN) yys = yyname[yychar];
+                if (!yys) yys = "illegal-symbol";
+                printf("%sdebug: state %d, reading %d (%s)\n",
+                        YYPREFIX, YYFINAL, yychar, yys);
+            }
+#endif
+        }
+        if (yychar == 0) goto yyaccept;
+        goto yyloop;
+    }
+    if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&
+            yyn <= YYTABLESIZE && yycheck[yyn] == yystate)
+        yystate = yytable[yyn];
+    else
+        yystate = yydgoto[yym];
+#if YYDEBUG
+    if (yydebug)
+        printf("%sdebug: after reduction, shifting from state %d \
+to state %d\n", YYPREFIX, *yyssp, yystate);
+#endif
+    if (yyssp >= yysslim && yygrowstack())
+    {
+        goto yyoverflow;
+    }
+    *++yyssp = (short) yystate;
+    *++yyvsp = yyval;
+    goto yyloop;
+
+yyoverflow:
+    yyerror("yacc stack overflow");
+
+yyabort:
+    return (1);
+
+yyaccept:
+    return (0);
+}
diff --git a/src/parsdate.h b/src/parsdate.h
new file mode 100644
index 00000000..ab24d318
--- /dev/null
+++ b/src/parsdate.h
@@ -0,0 +1,21 @@
+/* $LynxId: parsdate.h,v 1.1 2008/06/29 23:23:45 tom Exp $ */
+#ifndef PARSDATE_H
+#define PARSDATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <LYUtils.h>
+#define ARRAY_SIZE(array)       ((int) (sizeof(array) / sizeof(array[0])))
+     typedef struct _TIMEINFO {
+	time_t time;
+	long usec;
+	long tzone;
+    } TIMEINFO;
+
+    extern time_t parsedate(char *p, TIMEINFO * now);
+
+#ifdef __cplusplus
+}
+#endif
+#endif				/* PARSDATE_H */
diff --git a/src/parsdate.y b/src/parsdate.y
new file mode 100644
index 00000000..3466fc65
--- /dev/null
+++ b/src/parsdate.y
@@ -0,0 +1,963 @@
+%{
+/*
+ *  $LynxId: parsdate.y,v 1.12 2009/01/01 22:12:25 tom Exp $
+ *
+ *  This module is adapted and extended from tin, to use for LYmktime().
+ *
+ *  Project   : tin - a Usenet reader
+ *  Module    : parsedate.y
+ *  Author    : S. Bellovin, R. $alz, J. Berets, P. Eggert
+ *  Created   : 1990-08-01
+ *  Updated   : 2008-06-30 (by Thomas Dickey, for Lynx)
+ *  Notes     : This grammar has 8 shift/reduce conflicts.
+ *
+ *              Originally written by Steven M. Bellovin <smb@research.att.com>
+ *              while at the University of North Carolina at Chapel Hill.
+ *              Later tweaked by a couple of people on Usenet.  Completely
+ *              overhauled by Rich $alz <rsalz@osf.org> and Jim Berets
+ *              <jberets@bbn.com> in August, 1990.
+ *
+ *              Further revised (removed obsolete constructs and cleaned up
+ *              timezone names) in August, 1991, by Rich.
+ *              Paul Eggert <eggert@twinsun.com> helped in September 1992.
+ *              Roland Rosenfeld added MET DST code in April 1994.
+ *
+ *  Revision  : 1.13
+ *  Copyright : This code is in the public domain and has no copyright.
+ */
+
+/* SUPPRESS 530 */ /* Empty body for statement */
+/* SUPPRESS 593 on yyerrlab */ /* Label was not used */
+/* SUPPRESS 593 on yynewstate */ /* Label was not used */
+/* SUPPRESS 595 on yypvt */ /* Automatic variable may be used before set */
+
+#undef alloca			/* conflicting def may be set by yacc */
+#include <parsdate.h>
+
+/*
+**  Get the number of elements in a fixed-size array, or a pointer just
+**  past the end of it.
+*/
+#define ENDOF(array)	(&array[ARRAY_SIZE(array)])
+
+#ifdef EBCDIC
+#define TO_ASCII(c)	TOASCII(c)
+#define TO_LOCAL(c)	FROMASCII(c)
+#else
+#define TO_ASCII(c)	(c)
+#define TO_LOCAL(c)	(c)
+#endif
+
+#define IS7BIT(x)		((unsigned) TO_ASCII(x) < 128)
+#define CTYPE(isXXXXX, c)	(IS7BIT(c) && isXXXXX(((unsigned char)c)))
+
+typedef char	*PD_STRING;
+
+extern int date_parse(void);
+
+#define yyparse		date_parse
+#define yylex		date_lex
+#define yyerror		date_error
+
+
+    /* See the LeapYears table in Convert. */
+#define EPOCH		1970
+#define END_OF_TIME	2038
+
+    /* Constants for general time calculations. */
+#define DST_OFFSET	1
+#define SECSPERDAY	(24L * 60L * 60L)
+    /* Readability for TABLE stuff. */
+#define HOUR(x)		(x * 60)
+
+#define LPAREN		'('
+#define RPAREN		')'
+
+
+/*
+**  Daylight-savings mode:  on, off, or not yet known.
+*/
+typedef enum _DSTMODE {
+    DSTon, DSToff, DSTmaybe
+} DSTMODE;
+
+/*
+**  Meridian:  am, pm, or 24-hour style.
+*/
+typedef enum _MERIDIAN {
+    MERam, MERpm, MER24
+} MERIDIAN;
+
+
+/*
+**  Global variables.  We could get rid of most of them by using a yacc
+**  union, but this is more efficient.  (This routine predates the
+**  yacc %union construct.)
+*/
+static char	*yyInput;
+static DSTMODE	yyDSTmode;
+static int	yyHaveDate;
+static int	yyHaveRel;
+static int	yyHaveTime;
+static time_t	yyTimezone;
+static time_t	yyDay;
+static time_t	yyHour;
+static time_t	yyMinutes;
+static time_t	yyMonth;
+static time_t	yySeconds;
+static time_t	yyYear;
+static MERIDIAN	yyMeridian;
+static time_t	yyRelMonth;
+static time_t	yyRelSeconds;
+
+static time_t	ToSeconds(time_t, time_t, time_t, MERIDIAN);
+static time_t	Convert(time_t, time_t, time_t, time_t, time_t, time_t, MERIDIAN, DSTMODE);
+static time_t	DSTcorrect(time_t, time_t);
+static time_t	RelativeMonth(time_t, time_t);
+static int	LookupWord(char	*, int);
+static int	date_lex(void);
+static int	GetTimeInfo(TIMEINFO *Now);
+
+/*
+ * The 'date_error()' function is declared here to work around a defect in
+ * bison 1.22, which redefines 'const' further down in this file, making it
+ * impossible to put a prototype here, and the function later.  We're using
+ * 'const' on the parameter to quiet gcc's -Wwrite-strings warning.
+ */
+/*ARGSUSED*/
+static void
+date_error(const char GCC_UNUSED *s)
+{
+    /*NOTREACHED*/
+}
+
+%}
+
+%union {
+    time_t		Number;
+    enum _MERIDIAN	Meridian;
+}
+
+%token	tDAY tDAYZONE tMERIDIAN tMONTH tMONTH_UNIT tSEC_UNIT tSNUMBER
+%token	tUNUMBER tZONE tDST
+
+%type	<Number>	tDAYZONE tMONTH tMONTH_UNIT tSEC_UNIT
+%type	<Number>	tSNUMBER tUNUMBER tZONE numzone zone
+%type	<Meridian>	tMERIDIAN o_merid
+
+%%
+
+spec	: /* NULL */
+	| spec item
+	;
+
+item	: time {
+	    yyHaveTime++;
+#if	defined(lint)
+	    /* I am compulsive about lint natterings... */
+	    if (yyHaveTime == -1) {
+		YYERROR;
+	    }
+#endif	/* defined(lint) */
+	}
+	| time zone {
+	    yyHaveTime++;
+	    yyTimezone = $2;
+	}
+	| date {
+	    yyHaveDate++;
+	}
+	| both {
+	    yyHaveDate++;
+	    yyHaveTime++;
+	}
+	| both zone {
+	    yyHaveDate++;
+	    yyHaveTime++;
+	    yyTimezone = $2;
+	}
+	| rel {
+	    yyHaveRel = 1;
+	}
+	;
+
+time	: tUNUMBER o_merid {
+	    if ($1 < 100) {
+		yyHour = $1;
+		yyMinutes = 0;
+	    }
+	    else {
+		yyHour = $1 / 100;
+		yyMinutes = $1 % 100;
+	    }
+	    yySeconds = 0;
+	    yyMeridian = $2;
+	}
+	| tUNUMBER ':' tUNUMBER o_merid {
+	    yyHour = $1;
+	    yyMinutes = $3;
+	    yySeconds = 0;
+	    yyMeridian = $4;
+	}
+	| tUNUMBER ':' tUNUMBER numzone {
+	    yyHour = $1;
+	    yyMinutes = $3;
+	    yyTimezone = $4;
+	    yyMeridian = MER24;
+	    yyDSTmode = DSToff;
+	}
+	| tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
+	    yyHour = $1;
+	    yyMinutes = $3;
+	    yySeconds = $5;
+	    yyMeridian = $6;
+	}
+	| tUNUMBER ':' tUNUMBER ':' tUNUMBER numzone {
+	    yyHour = $1;
+	    yyMinutes = $3;
+	    yySeconds = $5;
+	    yyTimezone = $6;
+	    yyMeridian = MER24;
+	    yyDSTmode = DSToff;
+	}
+	;
+
+zone	: tZONE {
+	    $$ = $1;
+	    yyDSTmode = DSToff;
+	}
+	| tDAYZONE {
+	    $$ = $1;
+	    yyDSTmode = DSTon;
+	}
+	| tDAYZONE tDST {
+	    yyTimezone = $1;
+	    yyDSTmode = DSTon;
+	}
+	| tZONE numzone {
+	    /* Only allow "GMT+300" and "GMT-0800" */
+	    if ($1 != 0) {
+		YYABORT;
+	    }
+	    $$ = $2;
+	    yyDSTmode = DSToff;
+	}
+	| numzone {
+	    $$ = $1;
+	    yyDSTmode = DSToff;
+	}
+	;
+
+numzone	: tSNUMBER {
+	    int	i;
+
+	    /* Unix and GMT and numeric timezones -- a little confusing. */
+	    if ((int)$1 < 0) {
+		/* Don't work with negative modulus. */
+		$1 = -(int)$1;
+		if ($1 > 9999 || (i = $1 % 100) >= 60) {
+			YYABORT;
+		}
+		$$ = ($1 / 100) * 60 + i;
+	    }
+	    else {
+		if ($1 > 9999 || (i = $1 % 100) >= 60) {
+			YYABORT;
+		}
+		$$ = -(($1 / 100) * 60 + i);
+	    }
+	}
+	;
+
+date	: tUNUMBER '/' tUNUMBER {
+	    yyMonth = $1;
+	    yyDay = $3;
+	}
+	| tUNUMBER '/' tUNUMBER '/' tUNUMBER {
+	    if ($1 > 100) {
+		yyYear = $1;
+		yyMonth = $3;
+		yyDay = $5;
+	    }
+	    else {
+		yyMonth = $1;
+		yyDay = $3;
+		yyYear = $5;
+	    }
+	}
+	| tMONTH tUNUMBER {
+	    yyMonth = $1;
+	    yyDay = $2;
+	}
+	| tMONTH tUNUMBER ',' tUNUMBER {
+	    yyMonth = $1;
+	    yyDay = $2;
+	    yyYear = $4;
+	}
+	| tUNUMBER tMONTH {
+	    yyDay = $1;
+	    yyMonth = $2;
+	}
+	| tUNUMBER tMONTH tUNUMBER {
+	    yyDay = $1;
+	    yyMonth = $2;
+	    yyYear = $3;
+	}
+	| tDAY ',' tUNUMBER tMONTH tUNUMBER {
+	    yyDay = $3;
+	    yyMonth = $4;
+	    yyYear = $5;
+	}
+	| tDAY ',' tUNUMBER '-' tMONTH tSNUMBER {
+	    yyDay = $3;
+	    yyMonth = $5;
+	    yyYear = -$6;
+	}
+	| tUNUMBER tSNUMBER tSNUMBER {
+	    yyDay = $1;
+	    yyMonth = -$2;
+	    yyYear = -$3;
+	    yyDSTmode = DSToff;	/* assume midnight if no time given */
+	    yyTimezone = 0;	/* Lynx assumes GMT for this format */
+	}
+	;
+
+both	: tDAY tMONTH tUNUMBER tUNUMBER ':' tUNUMBER ':' tUNUMBER tUNUMBER {
+	    yyMonth = $2;
+	    yyDay = $3;
+	    yyYear = $9;
+	    yyHour = $4;
+	    yyMinutes = $6;
+	    yySeconds = $8;
+	}
+	;
+
+rel	: tSNUMBER tSEC_UNIT {
+	    yyRelSeconds += $1 * $2;
+	}
+	| tUNUMBER tSEC_UNIT {
+	    yyRelSeconds += $1 * $2;
+	}
+	| tSNUMBER tMONTH_UNIT {
+	    yyRelMonth += $1 * $2;
+	}
+	| tUNUMBER tMONTH_UNIT {
+	    yyRelMonth += $1 * $2;
+	}
+	;
+
+o_merid	: /* NULL */ {
+	    $$ = MER24;
+	}
+	| tMERIDIAN {
+	    $$ = $1;
+	}
+	;
+
+%%
+
+/*
+**  An entry in the lexical lookup table.
+*/
+typedef struct _TABLE {
+    const char *name;
+    int		type;
+    time_t	value;
+} TABLE;
+
+/* Month and day table. */
+static const TABLE MonthDayTable[] = {
+    { "january",	tMONTH,  1 },
+    { "february",	tMONTH,  2 },
+    { "march",		tMONTH,  3 },
+    { "april",		tMONTH,  4 },
+    { "may",		tMONTH,  5 },
+    { "june",		tMONTH,  6 },
+    { "july",		tMONTH,  7 },
+    { "august",		tMONTH,  8 },
+    { "september",	tMONTH,  9 },
+    { "october",	tMONTH, 10 },
+    { "november",	tMONTH, 11 },
+    { "december",	tMONTH, 12 },
+	/* The value of the day isn't used... */
+    { "sunday",		tDAY, 0 },
+    { "monday",		tDAY, 0 },
+    { "tuesday",	tDAY, 0 },
+    { "wednesday",	tDAY, 0 },
+    { "thursday",	tDAY, 0 },
+    { "friday",		tDAY, 0 },
+    { "saturday",	tDAY, 0 },
+};
+
+/* Time units table. */
+static const TABLE	UnitsTable[] = {
+    { "year",		tMONTH_UNIT,	12 },
+    { "month",		tMONTH_UNIT,	1 },
+    { "week",		tSEC_UNIT,	7 * 24 * 60 * 60 },
+    { "day",		tSEC_UNIT,	1 * 24 * 60 * 60 },
+    { "hour",		tSEC_UNIT,	60 * 60 },
+    { "minute",		tSEC_UNIT,	60 },
+    { "min",		tSEC_UNIT,	60 },
+    { "second",		tSEC_UNIT,	1 },
+    { "sec",		tSEC_UNIT,	1 },
+};
+
+/* Timezone table. */
+static const TABLE	TimezoneTable[] = {
+    { "gmt",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
+    { "ut",	tZONE,     HOUR( 0) },	/* Universal */
+    { "utc",	tZONE,     HOUR( 0) },	/* Universal Coordinated */
+    { "cut",	tZONE,     HOUR( 0) },	/* Coordinated Universal */
+    { "z",	tZONE,     HOUR( 0) },	/* Greenwich Mean */
+    { "wet",	tZONE,     HOUR( 0) },	/* Western European */
+    { "bst",	tDAYZONE,  HOUR( 0) },	/* British Summer */
+    { "nst",	tZONE,     HOUR(3)+30 }, /* Newfoundland Standard */
+    { "ndt",	tDAYZONE,  HOUR(3)+30 }, /* Newfoundland Daylight */
+    { "ast",	tZONE,     HOUR( 4) },	/* Atlantic Standard */
+    { "adt",	tDAYZONE,  HOUR( 4) },	/* Atlantic Daylight */
+    { "est",	tZONE,     HOUR( 5) },	/* Eastern Standard */
+    { "edt",	tDAYZONE,  HOUR( 5) },	/* Eastern Daylight */
+    { "cst",	tZONE,     HOUR( 6) },	/* Central Standard */
+    { "cdt",	tDAYZONE,  HOUR( 6) },	/* Central Daylight */
+    { "mst",	tZONE,     HOUR( 7) },	/* Mountain Standard */
+    { "mdt",	tDAYZONE,  HOUR( 7) },	/* Mountain Daylight */
+    { "pst",	tZONE,     HOUR( 8) },	/* Pacific Standard */
+    { "pdt",	tDAYZONE,  HOUR( 8) },	/* Pacific Daylight */
+    { "yst",	tZONE,     HOUR( 9) },	/* Yukon Standard */
+    { "ydt",	tDAYZONE,  HOUR( 9) },	/* Yukon Daylight */
+    { "akst",	tZONE,     HOUR( 9) },	/* Alaska Standard */
+    { "akdt",	tDAYZONE,  HOUR( 9) },	/* Alaska Daylight */
+    { "hst",	tZONE,     HOUR(10) },	/* Hawaii Standard */
+    { "hast",	tZONE,     HOUR(10) },	/* Hawaii-Aleutian Standard */
+    { "hadt",	tDAYZONE,  HOUR(10) },	/* Hawaii-Aleutian Daylight */
+    { "ces",	tDAYZONE,  -HOUR(1) },	/* Central European Summer */
+    { "cest",	tDAYZONE,  -HOUR(1) },	/* Central European Summer */
+    { "mez",	tZONE,     -HOUR(1) },	/* Middle European */
+    { "mezt",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
+    { "cet",	tZONE,     -HOUR(1) },	/* Central European */
+    { "met",	tZONE,     -HOUR(1) },	/* Middle European */
+/* Additional aliases for MET / MET DST *************************************/
+    { "mez",    tZONE,     -HOUR(1) },  /* Middle European */
+    { "mewt",   tZONE,     -HOUR(1) },  /* Middle European Winter */
+    { "mest",   tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
+    { "mes",    tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
+    { "mesz",   tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
+    { "msz",    tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
+    { "metdst", tDAYZONE,  -HOUR(1) },  /* Middle European Summer */
+/****************************************************************************/
+    { "eet",	tZONE,     -HOUR(2) },	/* Eastern Europe */
+    { "msk",	tZONE,     -HOUR(3) },	/* Moscow Winter */
+    { "msd",	tDAYZONE,  -HOUR(3) },	/* Moscow Summer */
+    { "wast",	tZONE,     -HOUR(8) },	/* West Australian Standard */
+    { "wadt",	tDAYZONE,  -HOUR(8) },	/* West Australian Daylight */
+    { "hkt",	tZONE,     -HOUR(8) },	/* Hong Kong */
+    { "cct",	tZONE,     -HOUR(8) },	/* China Coast */
+    { "jst",	tZONE,     -HOUR(9) },	/* Japan Standard */
+    { "kst",	tZONE,     -HOUR(9) },	/* Korean Standard */
+    { "kdt",	tZONE,     -HOUR(9) },	/* Korean Daylight */
+    { "cast",	tZONE,     -(HOUR(9)+30) }, /* Central Australian Standard */
+    { "cadt",	tDAYZONE,  -(HOUR(9)+30) }, /* Central Australian Daylight */
+    { "east",	tZONE,     -HOUR(10) },	/* Eastern Australian Standard */
+    { "eadt",	tDAYZONE,  -HOUR(10) },	/* Eastern Australian Daylight */
+    { "nzst",	tZONE,     -HOUR(12) },	/* New Zealand Standard */
+    { "nzdt",	tDAYZONE,  -HOUR(12) },	/* New Zealand Daylight */
+
+    /* For completeness we include the following entries. */
+#if	0
+
+    /* Duplicate names.  Either they conflict with a zone listed above
+     * (which is either more likely to be seen or just been in circulation
+     * longer), or they conflict with another zone in this section and
+     * we could not reasonably choose one over the other. */
+    { "fst",	tZONE,     HOUR( 2) },	/* Fernando De Noronha Standard */
+    { "fdt",	tDAYZONE,  HOUR( 2) },	/* Fernando De Noronha Daylight */
+    { "bst",	tZONE,     HOUR( 3) },	/* Brazil Standard */
+    { "est",	tZONE,     HOUR( 3) },	/* Eastern Standard (Brazil) */
+    { "edt",	tDAYZONE,  HOUR( 3) },	/* Eastern Daylight (Brazil) */
+    { "wst",	tZONE,     HOUR( 4) },	/* Western Standard (Brazil) */
+    { "wdt",	tDAYZONE,  HOUR( 4) },	/* Western Daylight (Brazil) */
+    { "cst",	tZONE,     HOUR( 5) },	/* Chile Standard */
+    { "cdt",	tDAYZONE,  HOUR( 5) },	/* Chile Daylight */
+    { "ast",	tZONE,     HOUR( 5) },	/* Acre Standard */
+    { "adt",	tDAYZONE,  HOUR( 5) },	/* Acre Daylight */
+    { "cst",	tZONE,     HOUR( 5) },	/* Cuba Standard */
+    { "cdt",	tDAYZONE,  HOUR( 5) },	/* Cuba Daylight */
+    { "est",	tZONE,     HOUR( 6) },	/* Easter Island Standard */
+    { "edt",	tDAYZONE,  HOUR( 6) },	/* Easter Island Daylight */
+    { "sst",	tZONE,     HOUR(11) },	/* Samoa Standard */
+    { "ist",	tZONE,     -HOUR(2) },	/* Israel Standard */
+    { "idt",	tDAYZONE,  -HOUR(2) },	/* Israel Daylight */
+    { "idt",	tDAYZONE,  -(HOUR(3)+30) }, /* Iran Daylight */
+    { "ist",	tZONE,     -(HOUR(3)+30) }, /* Iran Standard */
+    { "cst",	 tZONE,     -HOUR(8) },	/* China Standard */
+    { "cdt",	 tDAYZONE,  -HOUR(8) },	/* China Daylight */
+    { "sst",	 tZONE,     -HOUR(8) },	/* Singapore Standard */
+
+    /* Dubious (e.g., not in Olson's TIMEZONE package) or obsolete. */
+    { "gst",	tZONE,     HOUR( 3) },	/* Greenland Standard */
+    { "wat",	tZONE,     -HOUR(1) },	/* West Africa */
+    { "at",	tZONE,     HOUR( 2) },	/* Azores */
+    { "gst",	tZONE,     -HOUR(10) },	/* Guam Standard */
+    { "nft",	tZONE,     HOUR(3)+30 }, /* Newfoundland */
+    { "idlw",	tZONE,     HOUR(12) },	/* International Date Line West */
+    { "mewt",	tZONE,     -HOUR(1) },	/* Middle European Winter */
+    { "mest",	tDAYZONE,  -HOUR(1) },	/* Middle European Summer */
+    { "swt",	tZONE,     -HOUR(1) },	/* Swedish Winter */
+    { "sst",	tDAYZONE,  -HOUR(1) },	/* Swedish Summer */
+    { "fwt",	tZONE,     -HOUR(1) },	/* French Winter */
+    { "fst",	tDAYZONE,  -HOUR(1) },	/* French Summer */
+    { "bt",	tZONE,     -HOUR(3) },	/* Baghdad */
+    { "it",	tZONE,     -(HOUR(3)+30) }, /* Iran */
+    { "zp4",	tZONE,     -HOUR(4) },	/* USSR Zone 3 */
+    { "zp5",	tZONE,     -HOUR(5) },	/* USSR Zone 4 */
+    { "ist",	tZONE,     -(HOUR(5)+30) }, /* Indian Standard */
+    { "zp6",	tZONE,     -HOUR(6) },	/* USSR Zone 5 */
+    { "nst",	tZONE,     -HOUR(7) },	/* North Sumatra */
+    { "sst",	tZONE,     -HOUR(7) },	/* South Sumatra */
+    { "jt",	tZONE,     -(HOUR(7)+30) }, /* Java (3pm in Cronusland!) */
+    { "nzt",	tZONE,     -HOUR(12) },	/* New Zealand */
+    { "idle",	tZONE,     -HOUR(12) },	/* International Date Line East */
+    { "cat",	tZONE,     HOUR(10) },	/* -- expired 1967 */
+    { "nt",	tZONE,     HOUR(11) },	/* -- expired 1967 */
+    { "ahst",	tZONE,     HOUR(10) },	/* -- expired 1983 */
+    { "hdt",	tDAYZONE,  HOUR(10) },	/* -- expired 1986 */
+#endif	/* 0 */
+};
+
+static time_t
+ToSeconds(
+    time_t	Hours,
+    time_t	Minutes,
+    time_t	Seconds,
+    MERIDIAN	Meridian)
+{
+    if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 61)
+	return -1;
+    if (Meridian == MER24) {
+	if (Hours < 0 || Hours > 23)
+	    return -1;
+    } else {
+	if (Hours < 1 || Hours > 12)
+		return -1;
+	if (Hours == 12)
+		Hours = 0;
+	if (Meridian == MERpm)
+		Hours += 12;
+    }
+    return (Hours * 60L + Minutes) * 60L + Seconds;
+}
+
+
+static time_t
+Convert(
+    time_t	Month,
+    time_t	Day,
+    time_t	Year,
+    time_t	Hours,
+    time_t	Minutes,
+    time_t	Seconds,
+    MERIDIAN	Meridian,
+    DSTMODE	dst)
+{
+    static const int	DaysNormal[13] = {
+	0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+    };
+    static const int	DaysLeap[13] = {
+	0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+    };
+    static const int	LeapYears[] = {
+	1972, 1976, 1980, 1984, 1988, 1992, 1996,
+	2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036
+    };
+    const int *yp;
+    const int *mp;
+    int i;
+    time_t Julian;
+    time_t tod;
+
+    if (Year < 0)
+	Year = -Year;
+    if (Year < 70)
+        Year += 2000;
+    if (Year < 100)
+	Year += 1900;
+    if (Year < EPOCH)
+	Year += 100;
+    for (mp = DaysNormal, yp = LeapYears; yp < ENDOF(LeapYears); yp++)
+	if (Year == *yp) {
+	    mp = DaysLeap;
+	    break;
+	}
+    if (Year < EPOCH || Year > END_OF_TIME
+     || Month < 1 || Month > 12
+     /* NOSTRICT */ /* conversion from long may lose accuracy */
+     || Day < 1 || Day > mp[(int)Month]) {
+	return -1;
+    }
+
+    Julian = Day - 1 + (Year - EPOCH) * 365;
+    for (yp = LeapYears; yp < ENDOF(LeapYears); yp++, Julian++) {
+	if (Year <= *yp)
+	    break;
+    }
+    for (i = 1; i < Month; i++)
+	Julian += *++mp;
+    Julian *= SECSPERDAY;
+    Julian += yyTimezone * 60L;
+    if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) {
+	return -1;
+    }
+    Julian += tod;
+    tod = Julian;
+    if (dst == DSTon || (dst == DSTmaybe && localtime(&tod)->tm_isdst))
+	Julian -= DST_OFFSET * 60 * 60;
+    return Julian;
+}
+
+
+static time_t
+DSTcorrect(
+    time_t	Start,
+    time_t	Future)
+{
+    time_t	StartDay;
+    time_t	FutureDay;
+
+    StartDay = (localtime(&Start)->tm_hour + 1) % 24;
+    FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
+    return (Future - Start) + (StartDay - FutureDay) * DST_OFFSET * 60 * 60;
+}
+
+
+static time_t
+RelativeMonth(
+    time_t	Start,
+    time_t	RelMonth)
+{
+    struct tm	*tm;
+    time_t	Month;
+    time_t	Year;
+
+    tm = localtime(&Start);
+    Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
+    Year = Month / 12 + 1900;
+    Month = Month % 12 + 1;
+    return DSTcorrect(Start,
+	    Convert(Month, (time_t)tm->tm_mday, Year,
+		(time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
+		MER24, DSTmaybe));
+}
+
+
+static int
+LookupWord(
+    char *buff,
+    int length)
+{
+    char	*p;
+    const char *q;
+    const TABLE *tp;
+    int	c;
+
+    p = buff;
+    c = p[0];
+
+    /* See if we have an abbreviation for a month. */
+    if (length == 3 || (length == 4 && p[3] == '.')) {
+	for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) {
+	    q = tp->name;
+	    if (c == q[0] && p[1] == q[1] && p[2] == q[2]) {
+		yylval.Number = tp->value;
+		return tp->type;
+	    }
+	}
+    } else {
+	for (tp = MonthDayTable; tp < ENDOF(MonthDayTable); tp++) {
+	    if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
+		yylval.Number = tp->value;
+		return tp->type;
+	    }
+	}
+    }
+
+    /* Try for a timezone. */
+    for (tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++) {
+	if (c == tp->name[0] && p[1] == tp->name[1]
+	 && strcmp(p, tp->name) == 0) {
+	    yylval.Number = tp->value;
+	    return tp->type;
+	}
+    }
+
+    if (strcmp(buff, "dst") == 0)
+      return tDST;
+
+    /* Try the units table. */
+    for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++) {
+	if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
+	    yylval.Number = tp->value;
+	    return tp->type;
+	}
+    }
+
+    /* Strip off any plural and try the units table again. */
+    if (--length > 0 && p[length] == 's') {
+	p[length] = '\0';
+	for (tp = UnitsTable; tp < ENDOF(UnitsTable); tp++) {
+	    if (c == tp->name[0] && strcmp(p, tp->name) == 0) {
+		p[length] = 's';
+		yylval.Number = tp->value;
+		return tp->type;
+	    }
+	}
+	p[length] = 's';
+    }
+    length++;
+
+    /* Drop out any periods. */
+    for (p = buff, q = (PD_STRING)buff; *q; q++) {
+	if (*q != '.')
+	    *p++ = *q;
+    }
+    *p = '\0';
+
+    /* Try the meridians. */
+    if (buff[1] == 'm' && buff[2] == '\0') {
+	if (buff[0] == 'a') {
+	    yylval.Meridian = MERam;
+	    return tMERIDIAN;
+	}
+	if (buff[0] == 'p') {
+	    yylval.Meridian = MERpm;
+	    return tMERIDIAN;
+	}
+    }
+
+    /* If we saw any periods, try the timezones again. */
+    if (p - buff != length) {
+	c = buff[0];
+	for (p = buff, tp = TimezoneTable; tp < ENDOF(TimezoneTable); tp++) {
+	    if (c == tp->name[0] && p[1] == tp->name[1]
+	    && strcmp(p, tp->name) == 0) {
+		yylval.Number = tp->value;
+		return tp->type;
+	    }
+	}
+    }
+
+    /* Unknown word -- assume GMT timezone. */
+    yylval.Number = 0;
+    return tZONE;
+}
+
+
+/*
+ * This returns characters as-is (the ones that are not part of some token),
+ * and codes greater than 256 (the token values).
+ *
+ * yacc generates tables that may use the character value.  In particular,
+ * byacc's yycheck[] table contains integer values for the expected codes from
+ * this function, which (unless byacc is run locally) are ASCII codes.
+ *
+ * The TO_LOCAL() function assumes its input is in ASCII, and the output is
+ * whatever native encoding is used on the machine, e.g., EBCDIC.
+ *
+ * The TO_ASCII() function is the inverse of TO_LOCAL().
+ */
+static int
+date_lex(void)
+{
+    int		c;
+    char	*p;
+    char	buff[20];
+    int		sign;
+    int		i;
+    int		nesting;
+
+    for(;;) {
+	/* Get first character after the whitespace. */
+	for(;;) {
+	    while (CTYPE(isspace, *yyInput))
+		yyInput++;
+	    c = *yyInput;
+
+	    /* Ignore RFC 822 comments, typically time zone names. */
+	    if (c != LPAREN)
+		break;
+	    for (nesting = 1;
+		 (c = *++yyInput) != RPAREN || --nesting;
+		 ) {
+		if (c == LPAREN) {
+		    nesting++;
+		} else if (!IS7BIT(c) || c == '\0' || c == '\r'
+		        || (c == '\\'
+			 && ((c = *++yyInput) == '\0'
+			  || !IS7BIT(c)))) {
+		    /* Lexical error: bad comment. */
+		    return '?';
+		}
+	    }
+	    yyInput++;
+	}
+
+	/* A number? */
+	if (CTYPE(isdigit, c) || c == '-' || c == '+') {
+	    if (c == '-' || c == '+') {
+		sign = c == '-' ? -1 : 1;
+		yyInput++;
+		if (!CTYPE(isdigit, *yyInput)) {
+		    /* Return the isolated plus or minus sign. */
+		    --yyInput;
+		    return *yyInput++;
+		}
+	    } else {
+		sign = 0;
+	    }
+	    for (p = buff;
+		 (c = *yyInput++) != '\0' && CTYPE(isdigit, c);
+		 ) {
+		if (p < &buff[sizeof buff - 1])
+		    *p++ = (char) c;
+	    }
+	    *p = '\0';
+	    i = atoi(buff);
+
+	    yyInput--;
+	    yylval.Number = sign < 0 ? -i : i;
+	    return sign ? tSNUMBER : tUNUMBER;
+	}
+
+	/* A word? */
+	if (CTYPE(isalpha, c)) {
+	    for (p = buff;
+		 (c = *yyInput++) == '.' || CTYPE(isalpha, c);
+		 ) {
+		if (p < &buff[sizeof buff - 1])
+		    *p++ = (char) (CTYPE(isupper, c) ? tolower(c) : c);
+	    }
+	    *p = '\0';
+	    yyInput--;
+	    return LookupWord(buff, p - buff);
+	}
+
+	return *yyInput++;
+    }
+}
+
+
+static int
+GetTimeInfo(
+    TIMEINFO		*Now)
+{
+    static time_t	LastTime;
+    static long		LastTzone;
+    struct tm		*tm;
+#if	defined(HAVE_GETTIMEOFDAY)
+    struct timeval	tv;
+#endif	/* defined(HAVE_GETTIMEOFDAY) */
+#if	defined(DONT_HAVE_TM_GMTOFF)
+    struct tm		local;
+    struct tm		gmt;
+#endif	/* !defined(DONT_HAVE_TM_GMTOFF) */
+
+    /* Get the basic time. */
+#if defined(HAVE_GETTIMEOFDAY)
+    if (gettimeofday(&tv, (struct timezone *)NULL) == -1)
+	return -1;
+    Now->time = tv.tv_sec;
+    Now->usec = tv.tv_usec;
+#else
+    /* Can't check for -1 since that might be a time, I guess. */
+    (void)time(&Now->time);
+    Now->usec = 0;
+#endif /* defined(HAVE_GETTIMEOFDAY) */
+
+    /* Now get the timezone if it's been an hour since the last time. */
+    if (Now->time - LastTime > 60 * 60) {
+	LastTime = Now->time;
+	if ((tm = localtime(&Now->time)) == NULL)
+	    return -1;
+#if	defined(DONT_HAVE_TM_GMTOFF)
+	/* To get the timezone, compare localtime with GMT. */
+	local = *tm;
+	if ((tm = gmtime(&Now->time)) == NULL)
+	    return -1;
+	gmt = *tm;
+
+	/* Assume we are never more than 24 hours away. */
+	LastTzone = gmt.tm_yday - local.tm_yday;
+	if (LastTzone > 1)
+	    LastTzone = -24;
+	else if (LastTzone < -1)
+	    LastTzone = 24;
+	else
+	    LastTzone *= 24;
+
+	/* Scale in the hours and minutes; ignore seconds. */
+	LastTzone += gmt.tm_hour - local.tm_hour;
+	LastTzone *= 60;
+	LastTzone += gmt.tm_min - local.tm_min;
+#else
+	LastTzone =  (0 - tm->tm_gmtoff) / 60;
+#endif	/* defined(DONT_HAVE_TM_GMTOFF) */
+    }
+    Now->tzone = LastTzone;
+    return 0;
+}
+
+
+time_t
+parsedate(
+    char		*p,
+    TIMEINFO		*now)
+{
+    struct tm		*tm;
+    TIMEINFO		ti;
+    time_t		Start;
+
+    yyInput = p;
+    if (now == NULL) {
+	now = &ti;
+	(void)GetTimeInfo(&ti);
+    }
+
+    tm = localtime(&now->time);
+    yyYear = tm->tm_year + 1900;
+    yyMonth = tm->tm_mon + 1;
+    yyDay = tm->tm_mday;
+    yyTimezone = now->tzone;
+    if (tm->tm_isdst)                   /* Correct timezone offset for DST */
+      yyTimezone += DST_OFFSET * 60;
+    yyDSTmode = DSTmaybe;
+    yyHour = 0;
+    yyMinutes = 0;
+    yySeconds = 0;
+    yyMeridian = MER24;
+    yyRelSeconds = 0;
+    yyRelMonth = 0;
+    yyHaveDate = 0;
+    yyHaveRel = 0;
+    yyHaveTime = 0;
+
+    if (date_parse() || yyHaveTime > 1 || yyHaveDate > 1)
+	return -1;
+
+    if (yyHaveDate || yyHaveTime) {
+	Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
+		    yyMeridian, yyDSTmode);
+	if (Start < 0)
+	    return -1;
+    } else {
+	Start = now->time;
+	if (!yyHaveRel)
+	    Start -= (tm->tm_hour * 60L + tm->tm_min) * 60L + tm->tm_sec;
+    }
+
+    Start += yyRelSeconds;
+    if (yyRelMonth)
+	Start += RelativeMonth(Start, yyRelMonth);
+
+    /* Have to do *something* with a legitimate -1 so it's distinguishable
+     * from the error return value.  (Alternately could set errno on error.) */
+    return Start == -1 ? 0 : Start;
+}
diff --git a/src/socketshr_tcp.opt b/src/socketshr_tcp.opt
new file mode 100644
index 00000000..f6e31316
--- /dev/null
+++ b/src/socketshr_tcp.opt
@@ -0,0 +1 @@
+socketshr/share
diff --git a/src/strstr.c b/src/strstr.c
new file mode 100644
index 00000000..7dcd7d56
--- /dev/null
+++ b/src/strstr.c
@@ -0,0 +1,60 @@
+/*
+ * strstr.c -- return the offset of one string within another.
+ *
+ * Copyright (C) 1997 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* Written by Philippe De Muyter <phdm@macqel.be>.  */
+
+/*
+ * NAME
+ *
+ * strstr -- locate first occurrence of a substring
+ *
+ * SYNOPSIS
+ *
+ * char *strstr (char *s1, char *s2)
+ *
+ * DESCRIPTION
+ *
+ * Locates the first occurrence in the string pointed to by S1 of the string
+ * pointed to by S2.  Returns a pointer to the substring found, or a NULL
+ * pointer if not found.  If S2 points to a string with zero length, the
+ * function returns S1.
+ *
+ * BUGS
+ *
+ */
+
+char *strstr(char *buf, char *sub)
+{
+    register char *bp;
+    register char *sp;
+
+    if (!*sub)
+	return buf;
+    while (*buf) {
+	bp = buf;
+	sp = sub;
+	do {
+	    if (!*sp)
+		return buf;
+	} while (*bp++ == *sp++);
+	buf += 1;
+    }
+    return 0;
+}
diff --git a/src/structdump.h b/src/structdump.h
new file mode 100644
index 00000000..f9be55ae
--- /dev/null
+++ b/src/structdump.h
@@ -0,0 +1,157 @@
+/*
+ *
+ * Some macros to dump out formatted struct's via the trace file.  -KED
+ *
+ */
+#ifndef STRUCTDUMP_H
+#define STRUCTDUMP_H
+
+/* usage: DUMPSTRUCT_LINK(link_ptr, "message"); */
+#define   DUMPSTRUCT_LINK(L,X) \
+if ((L)) { \
+CTRACE((tfp, "\n" \
+            "KED:     link_ptr=%p  sizeof=%d  ["X"]\n" \
+            "link       struct {\n"      \
+            "           *lname=%p\n"     \
+            "            lname=|%s|\n"   \
+            "          *target=%p\n"     \
+            "           target=|%s|\n"   \
+            "        *hightext=%p\n"     \
+            "         hightext=|%s|\n"   \
+            "       *hightext2=%p\n"     \
+            "        hightext2=|%s|\n"   \
+            " hightext2_offset=%d\n"     \
+            "      inUnderline=%1x\n"    \
+            "               lx=%d\n"     \
+            "               ly=%d\n"     \
+            "             type=%d\n"     \
+            "    anchor_number=%d\n"     \
+            "  anchor_line_num=%d\n"     \
+            "            *form=%p\n"     \
+            "}\n", \
+            (L), sizeof(*((L))), \
+            (L)->lname, (L)->lname, (L)->target, (L)->target, \
+            (L)->l_hightext, (L)->l_hightext, \
+	    (L)->l_hightext2, (L)->l_hightext2, \
+            (L)->l_hightext2_offset, \
+	    (L)->inUnderline, (L)->lx, (L)->ly, \
+            (L)->type, (L)->anchor_number, (L)->anchor_line_num, (L)->form)); \
+}else{ \
+CTRACE((tfp, "\n" \
+            "KED:     link_ptr=0x00000000  (NULL)     ["X"]\n")); \
+} \
+CTRACE_FLUSH(tfp);
+
+/* usage: DUMPSTRUCT_ANCHOR(anchor_ptr, "message"); */
+#define   DUMPSTRUCT_ANCHOR(A,X) \
+if ((A)) { \
+CTRACE((tfp, "\n" \
+            "KED:   anchor_ptr=%p  sizeof=%d  ["X"]\n" \
+            "TextAnchor struct {\n"      \
+            "            *next=%p\n"     \
+            "           number=%d\n"     \
+            "         line_pos=%d\n"     \
+            "           extent=%d\n"     \
+            "         line_num=%d\n"     \
+            "        *hightext=%p\n"     \
+            "         hightext=|%s|\n"   \
+            "       *hightext2=%p\n"     \
+            "        hightext2=|%s|\n"   \
+            "  hightext2offset=%d\n"     \
+            "        link_type=%d\n"     \
+            "     *input_field=%p\n"     \
+            "      input_field=|%s|\n"   \
+            "      show_anchor=%1x\n"    \
+            "      inUnderline=%1x\n"    \
+            "   expansion_anch=%1x\n"    \
+            "          *anchor=%p\n"     \
+            "}\n", \
+            (A), sizeof(*((A))), \
+            (A)->next, (A)->number, (A)->line_pos, \
+            (A)->extent, (A)->line_num, \
+            (A)->hightext, (A)->hightext, (A)->hightext2, (A)->hightext2, \
+            (A)->hightext2offset, (A)->link_type, \
+            (A)->input_field, (A)->input_field->name, (A)->show_anchor, \
+            (A)->inUnderline, (A)->expansion_anch, (A)->anchor)); \
+}else{ \
+CTRACE((tfp, "\n" \
+            "KED:   anchor_ptr=0x00000000  (NULL)     ["X"]\n")); \
+} \
+CTRACE_FLUSH(tfp);
+
+/* usage: DUMPSTRUCT_FORM(forminfo_ptr, "message"); */
+#define   DUMPSTRUCT_FORMINFO(F,X) \
+if ((F)) { \
+CTRACE((tfp, "\n" \
+            "KED: forminfo_ptr=%p  sizeof=%d  ["X"]\n" \
+            "FormInfo   struct {\n"      \
+            "            *name=%p\n"     \
+            "             name=|%s|\n"   \
+            "           number=%d\n"     \
+            "             type=%d\n"     \
+            "           *value=%p\n"     \
+            "            value=|%s|\n"   \
+            "      *orig_value=%p\n"     \
+            "       orig_value=|%s|\n"   \
+            "             size=%d\n"     \
+            "        maxlength=%d\n"     \
+            "            group=%d\n"     \
+            "        num_value=%d\n"     \
+            "           hrange=%d\n"     \
+            "           lrange=%d\n"     \
+            "     *select_list=%p\n"     \
+            "    submit_action=|%s|\n"   \
+            "    submit_method=%d\n"     \
+            "   submit_enctype=|%s|\n"   \
+            "     submit_title=|%s|\n"   \
+            "         no_cache=%1x\n"    \
+            "  cp_submit_value=|%s|\n"   \
+            "orig_submit_value=|%s|\n"   \
+            "           size_l=%d\n"     \
+            "         disabled=%d\n"     \
+            "          name_cs=%d\n"     \
+            "         value_cs=%d\n"     \
+            "        accept_cs=|%s|\n"   \
+            "}\n", \
+            (F), sizeof(*((F))), \
+            (F)->name, (F)->name, (F)->number, (F)->type, \
+            (F)->value, (F)->value, (F)->orig_value, (F)->orig_value, \
+            (F)->size, (F)->maxlength, (F)->group, (F)->num_value, \
+            (F)->hrange, (F)->lrange, (F)->select_list, (F)->submit_action, \
+            (F)->submit_method, (F)->submit_enctype, (F)->submit_title, \
+            (F)->no_cache, (F)->cp_submit_value, (F)->orig_submit_value, \
+            (F)->size_l, (F)->disabled, (F)->name_cs, (F)->value_cs, \
+            (F)->accept_cs)); \
+} else { \
+CTRACE((tfp, "\n" \
+            "KED: forminfo_ptr=0x00000000  (NULL)     ["X"]\n")); \
+} \
+CTRACE_FLUSH(tfp);
+
+/* usage: DUMPSTRUCT_LINE(htline_ptr, "message"); */
+#define   DUMPSTRUCT_LINE(L,X) \
+if ((L)) { \
+CTRACE((tfp, "\n" \
+            "KED: htline_ptr=%p  sizeof=%d  ["X"]\n" \
+            "HTLine  struct {\n"      \
+            "         *next=%p\n"     \
+            "         *prev=%p\n"     \
+            "        offset=%d\n"     \
+            "          size=%d\n"     \
+            "   split_after=%1x\n"    \
+            "        bullet=%1x\n"    \
+            "expansion_line=%1x\n"    \
+            "w/o U_C_S def\n"         \
+            "        data[]=%p\n"     \
+            "          data=|%s|\n"   \
+            "}\n", \
+            (L), sizeof(*((L))), \
+            (L)->next, (L)->prev, (L)->offset, (L)->size, (L)->split_after, \
+            (L)->bullet, (L)->expansion_line, (L)->data, (L)->data)); \
+}else{ \
+CTRACE((tfp, "\n" \
+            "KED: htline_ptr=0x00000000  (NULL)     ["X"]\n")); \
+} \
+CTRACE_FLUSH(tfp);
+
+#endif /* STRUCTDUMP_H */
diff --git a/src/tcpipolb.opt b/src/tcpipolb.opt
new file mode 100644
index 00000000..9c87cc8d
--- /dev/null
+++ b/src/tcpipolb.opt
@@ -0,0 +1 @@
+tcpip$library:tcpip$lib/library
diff --git a/src/tcpipshr.opt b/src/tcpipshr.opt
new file mode 100644
index 00000000..1b55c34c
--- /dev/null
+++ b/src/tcpipshr.opt
@@ -0,0 +1 @@
+sys$library:tcpip$ipc_shr/share
diff --git a/src/tcpwareolb.opt b/src/tcpwareolb.opt
new file mode 100644
index 00000000..045d770c
--- /dev/null
+++ b/src/tcpwareolb.opt
@@ -0,0 +1 @@
+tcpware:ucx$ipc/library
diff --git a/src/tcpwareshr.opt b/src/tcpwareshr.opt
new file mode 100644
index 00000000..e34f8469
--- /dev/null
+++ b/src/tcpwareshr.opt
@@ -0,0 +1 @@
+tcpware:ucx$ipc_shr.exe/share
diff --git a/src/tidy_tls.c b/src/tidy_tls.c
new file mode 100644
index 00000000..473020cd
--- /dev/null
+++ b/src/tidy_tls.c
@@ -0,0 +1,601 @@
+/*
+ * $LynxId: tidy_tls.c,v 1.4 2010/04/29 20:49:46 tom Exp $
+ * Copyright 2008, Thomas E. Dickey
+ * with fix Copyright 2008 by Thomas Viehmann
+ *
+ * Required libraries:
+ *	libgnutls
+ *	libcrypt
+ */
+#include <tidy_tls.h>
+
+#include <gnutls/x509.h>
+#include <gcrypt.h>
+#include <libtasn1.h>		/* ASN1_SUCCESS,etc */
+#include <string.h>
+
+#define typeCalloc(type) (type *) calloc(1, sizeof(type))
+
+static int last_error = 0;
+
+/* ugly, but hey, we could just use a more sane api, too */
+#define GetDnByOID(target, oid, thewhat) \
+		len = sizeof(target); \
+                if (! thewhat) \
+		  gnutls_x509_crt_get_dn_by_oid(xcert, oid, 0, 0, target, &len); \
+                else \
+                  gnutls_x509_crt_get_issuer_dn_by_oid(xcert, oid, 0, 0, target, &len)
+
+/* thewhat: which DN to get 0 = subject, 1 = issuer */
+static int ExtractCertificate(const gnutls_datum_t * cert, X509_NAME * result, int thewhat)
+{
+    gnutls_x509_crt_t xcert;
+    int rc;
+    size_t len;
+
+    if ((rc = gnutls_x509_crt_init(&xcert)) >= 0) {
+	if ((rc = gnutls_x509_crt_import(xcert, cert, GNUTLS_X509_FMT_DER)) >= 0) {
+	    GetDnByOID(result->country,
+		       GNUTLS_OID_X520_COUNTRY_NAME, thewhat);
+	    GetDnByOID(result->organization,
+		       GNUTLS_OID_X520_ORGANIZATION_NAME, thewhat);
+	    GetDnByOID(result->organizational_unit_name,
+		       GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, thewhat);
+	    GetDnByOID(result->common_name,
+		       GNUTLS_OID_X520_COMMON_NAME, thewhat);
+	    GetDnByOID(result->locality_name,
+		       GNUTLS_OID_X520_LOCALITY_NAME, thewhat);
+	    GetDnByOID(result->state_or_province_name,
+		       GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, thewhat);
+	    GetDnByOID(result->email,
+		       GNUTLS_OID_PKCS9_EMAIL, thewhat);
+	    rc = 0;
+	}
+	gnutls_x509_crt_deinit(xcert);
+    }
+    return rc;
+}
+
+/*
+ * (stub)
+ * ERR_error_string() generates a human-readable string representing the
+ * error code 'e', and places it at 'buffer', which must be at least 120 bytes
+ * long. If 'buffer' is NULL, the error string is placed in a static buffer.
+ */
+const char *ERR_error_string(unsigned long e, char *buffer)
+{
+    (void) buffer;
+    return gnutls_strerror(-e);
+}
+
+/*
+ * (stub)
+ * Return the earliest error code from the thread's error queue and remove the
+ * entry.
+ */
+unsigned long ERR_get_error(void)
+{
+    unsigned long rc;
+
+    rc = -last_error;
+    last_error = 0;
+
+    return rc;
+}
+
+/*
+ * Put 'num' cryptographically strong pseudo-random bytes into 'buffer'.
+ */
+int RAND_bytes(unsigned char *buffer, int num)
+{
+    gcry_randomize(buffer, num, GCRY_VERY_STRONG_RANDOM);
+    return 1;
+}
+
+/*
+ * (stub)
+ * Generate a default path for the random seed file.  'buffer' points to a
+ * buffer of size 'len' in which to store the filename.
+ */
+const char *RAND_file_name(char *buffer, size_t len)
+{
+    (void) buffer;
+    (void) len;
+    return "";
+}
+
+/*
+ * (stub)
+ * Read a number of bytes from file 'name' and adds them to the PRNG.  If
+ * 'maxbytes' is non-negative, up to to 'maxbytes' are read; if 'maxbytes' is
+ * -1, the complete file is read.
+ */
+int RAND_load_file(const char *name, long maxbytes)
+{
+    (void) name;
+    return maxbytes;
+}
+
+/*
+ * (stub)
+ * Mix the 'num' bytes at 'buffer' into the PRNG state.
+ */
+void RAND_seed(const void *buffer, int num)
+{
+    (void) buffer;
+    (void) num;
+}
+
+/*
+ * (stub)
+ * Return 1 if the PRNG has been seeded with enough data, 0 otherwise.
+ */
+int RAND_status(void)
+{
+    return 1;
+}
+
+/*
+ * (stub)
+ * Write a number of random bytes (currently 1024) to file 'name' which can be
+ * used to initialize the PRNG by calling RAND_load_file() in a later session.
+ */
+int RAND_write_file(const char *name)
+{
+    (void) name;
+    return 0;
+}
+
+/*
+ * Return the number of secret bits used for cipher.  If 'bits' is not NULL, it
+ * contains the number of bits processed by the chosen algorithm.  If cipher is
+ * NULL, 0 is returned.
+ */
+int SSL_CIPHER_get_bits(SSL_CIPHER * cipher, int *bits)
+{
+    int result = 0;
+
+    if (cipher) {
+	result = (8 * gnutls_cipher_get_key_size(cipher->encrypts));
+    }
+
+    if (bits)
+	*bits = result;
+
+    return result;
+}
+
+/*
+ * Return a pointer to the name of 'cipher'.  If 'cipher' is NULL the constant
+ * value "NONE" is returned.
+ */
+const char *SSL_CIPHER_get_name(SSL_CIPHER * cipher)
+{
+    const char *result = "NONE";
+
+    if (cipher) {
+	result = gnutls_cipher_suite_get_name(cipher->key_xchg,
+					      cipher->encrypts,
+					      cipher->msg_code);
+    }
+    return result;
+}
+
+/*
+ * Return the protocol version for cipher, currently "SSLv2", "SSLv3", or
+ * "TLSv1".  If cipher is NULL, "(NONE)" is returned.
+ */
+const char *SSL_CIPHER_get_version(SSL_CIPHER * cipher)
+{
+    const char *result = "NONE";
+
+    if (cipher) {
+	if ((result = gnutls_protocol_get_name(cipher->protocol)) == 0)
+	    result = "unknown";
+    }
+    return result;
+}
+
+/*
+ * Free an allocated SSL_CTX object.
+ */
+void SSL_CTX_free(SSL_CTX * ctx)
+{
+    free(ctx->method);
+    free(ctx);
+}
+
+/*
+ * Create a new SSL_CTX object as framework for TLS/SSL enabled functions.
+ */
+SSL_CTX *SSL_CTX_new(SSL_METHOD * method)
+{
+    SSL_CTX *ctx;
+
+    ctx = typeCalloc(SSL_CTX);
+    ctx->method = method;
+
+    return ctx;
+}
+
+/*
+ * See SSL_CTX_load_verify_locations() - this sets the paths for that and
+ * SSL_CTX_set_verify() to their default values.  GNU TLS does not have a
+ * comparable feature (stub).
+ */
+int SSL_CTX_set_default_verify_paths(SSL_CTX * ctx)
+{
+    (void) ctx;
+    return 0;
+}
+
+/*
+ * SSL_CTX_set_options() adds the options set via bitmask in options to
+ * ctx.  Options already set before are not cleared.
+ */
+unsigned long SSL_CTX_set_options(SSL_CTX * ctx, unsigned long options)
+{
+    ctx->options |= options;
+    return ctx->options;
+}
+
+/*
+ * Set peer certificate verification parameters.
+ */
+void SSL_CTX_set_verify(SSL_CTX * ctx, int verify_mode,
+			int (*verify_callback) (int, X509_STORE_CTX *))
+{
+    ctx->verify_mode = verify_mode;
+    ctx->verify_callback = verify_callback;
+}
+
+static void RemoveProtocol(SSL * ssl, int protocol)
+{
+    int j, k;
+    int changed = 0;
+    int *protocols = ssl->ctx->method->priority.protocol;
+
+    for (j = k = 0; j < GNUTLS_MAX_ALGORITHM_NUM;) {
+	if (protocols[k] == protocol) {
+	    if (++k >= GNUTLS_MAX_ALGORITHM_NUM)
+		break;
+	    changed = 1;
+	} else {
+	    protocols[j++] = protocols[k++];
+	}
+    }
+
+    if (changed) {
+	gnutls_protocol_set_priority(ssl->gnutls_state, protocols);
+    }
+}
+
+/*
+ * Initiate the TLS/SSL handshake with an TLS/SSL server.
+ */
+int SSL_connect(SSL * ssl)
+{
+    X509_STORE_CTX *store;
+    int rc;
+
+    if (ssl->options & SSL_OP_NO_TLSv1)
+	RemoveProtocol(ssl, GNUTLS_TLS1);
+    if (ssl->options & SSL_OP_NO_SSLv3)
+	RemoveProtocol(ssl, GNUTLS_SSL3);
+
+    rc = gnutls_handshake(ssl->gnutls_state);
+    ssl->last_error = rc;
+
+    if (rc < 0) {
+	last_error = rc;
+	return 0;
+    }
+
+    store = typeCalloc(X509_STORE_CTX);
+    store->ssl = ssl;
+    store->cert_list = SSL_get_peer_certificate(ssl);
+
+    if (ssl->verify_callback) {
+	ssl->verify_callback(1, store);
+    }
+    ssl->state = SSL_ST_OK;
+
+    free(store);
+
+    /* FIXME: deal with error from callback */
+
+    return 1;
+}
+
+/*
+ * Free an allocated SSL structure.
+ */
+void SSL_free(SSL * ssl)
+{
+    gnutls_certificate_free_credentials(ssl->gnutls_cred);
+    gnutls_deinit(ssl->gnutls_state);
+    free(ssl);
+}
+
+/*
+ * Get SSL_CIPHER data of a connection.
+ */
+SSL_CIPHER *SSL_get_current_cipher(SSL * ssl)
+{
+    SSL_CIPHER *result = 0;
+
+    if (ssl) {
+	result = &(ssl->ciphersuite);
+
+	result->protocol = gnutls_protocol_get_version(ssl->gnutls_state);
+	result->encrypts = gnutls_cipher_get(ssl->gnutls_state);
+	result->key_xchg = gnutls_kx_get(ssl->gnutls_state);
+	result->msg_code = gnutls_mac_get(ssl->gnutls_state);
+	result->compress = gnutls_compression_get(ssl->gnutls_state);
+	result->cert = gnutls_certificate_type_get(ssl->gnutls_state);
+    }
+
+    return result;
+}
+
+/*
+ * Get the X509 certificate of the peer.
+ */
+X509 *SSL_get_peer_certificate(SSL * ssl)
+{
+    gnutls_datum_t *result;
+    unsigned list_size = 0;
+
+    result = (gnutls_datum_t *) gnutls_certificate_get_peers(ssl->gnutls_state,
+							     &list_size);
+
+    return (X509 *) result;
+}
+
+/*
+ * Initialize SSL library by registering algorithms.
+ */
+int SSL_library_init(void)
+{
+    gnutls_global_init();
+    return 1;
+}
+
+/*
+ * (stub)
+ * OpenSSL uses this to prepare for ERR_get_error() calls.
+ */
+void SSL_load_error_strings(void)
+{
+}
+
+/*
+ * Create a new SSL structure for a connection.
+ */
+SSL *SSL_new(SSL_CTX * ctx)
+{
+    SSL *ssl;
+    int rc;
+
+    if ((ssl = typeCalloc(SSL)) != 0) {
+
+	rc = gnutls_certificate_allocate_credentials(&ssl->gnutls_cred);
+	if (rc < 0) {
+	    last_error = rc;
+	    free(ssl);
+	    ssl = 0;
+	} else {
+
+	    gnutls_init(&ssl->gnutls_state, ctx->method->connend);
+
+	    gnutls_protocol_set_priority(ssl->gnutls_state,
+					 ctx->method->priority.protocol);
+	    gnutls_cipher_set_priority(ssl->gnutls_state,
+				       ctx->method->priority.encrypts);
+	    gnutls_compression_set_priority(ssl->gnutls_state,
+					    ctx->method->priority.compress);
+	    gnutls_kx_set_priority(ssl->gnutls_state,
+				   ctx->method->priority.key_xchg);
+	    gnutls_mac_set_priority(ssl->gnutls_state,
+				    ctx->method->priority.msg_code);
+
+	    gnutls_credentials_set(ssl->gnutls_state, GNUTLS_CRD_CERTIFICATE,
+				   ssl->gnutls_cred);
+	    if (ctx->certfile)
+		gnutls_certificate_set_x509_trust_file(ssl->gnutls_cred,
+						       ctx->certfile,
+						       ctx->certfile_type);
+	    if (ctx->keyfile)
+		gnutls_certificate_set_x509_key_file(ssl->gnutls_cred,
+						     ctx->certfile,
+						     ctx->keyfile,
+						     ctx->keyfile_type);
+	    ssl->ctx = ctx;
+	    ssl->verify_mode = ctx->verify_mode;
+	    ssl->verify_callback = ctx->verify_callback;
+
+	    ssl->options = ctx->options;
+
+	    ssl->rfd = (gnutls_transport_ptr_t) (-1);
+	    ssl->wfd = (gnutls_transport_ptr_t) (-1);
+	}
+    }
+    return ssl;
+}
+
+/*
+ * Read 'length' bytes into 'buffer' from the given SSL connection.
+ * Returns the number of bytes read, or zero on error.
+ */
+int SSL_read(SSL * ssl, void *buffer, int length)
+{
+    int rc;
+
+    rc = gnutls_record_recv(ssl->gnutls_state, buffer, length);
+    ssl->last_error = rc;
+
+    if (rc < 0) {
+	last_error = rc;
+	rc = 0;
+    }
+
+    return rc;
+}
+
+/*
+ * Connect the SSL object with a file descriptor.
+ * This always returns 1 (success) since GNU TLS does not check for errors.
+ */
+int SSL_set_fd(SSL * ssl, int fd)
+{
+    gnutls_transport_set_ptr(ssl->gnutls_state,
+			     (gnutls_transport_ptr_t) (fd));
+    return 1;
+}
+
+/*
+ * Write 'length' bytes from 'buffer' to the given SSL connection.
+ */
+int SSL_write(SSL * ssl, const void *buffer, int length)
+{
+    int rc;
+
+    rc = gnutls_record_send(ssl->gnutls_state, buffer, length);
+    ssl->last_error = rc;
+
+    if (rc < 0) {
+	last_error = rc;
+	rc = 0;
+    }
+
+    return rc;
+}
+
+/*
+ * Return method-data for SSL verion 3, with the option of rollback to SSL
+ * version 2.
+ */
+SSL_METHOD *SSLv23_client_method(void)
+{
+    SSL_METHOD *m;
+
+    if ((m = typeCalloc(SSL_METHOD)) != 0) {
+
+	/*
+	 * List the protocols in decreasing order of priority.
+	 */
+	m->priority.protocol[0] = GNUTLS_TLS1;
+	m->priority.protocol[1] = GNUTLS_SSL3;
+	m->priority.protocol[2] = 0;
+
+	/*
+	 * List the cipher algorithms in decreasing order of priority.
+	 */
+	m->priority.encrypts[0] = GNUTLS_CIPHER_AES_128_CBC;
+	m->priority.encrypts[1] = GNUTLS_CIPHER_3DES_CBC;
+	m->priority.encrypts[2] = GNUTLS_CIPHER_AES_256_CBC;
+	m->priority.encrypts[3] = GNUTLS_CIPHER_ARCFOUR_128;
+	m->priority.encrypts[4] = 0;
+
+	/*
+	 * List the compression algorithms in decreasing order of priority.
+	 */
+	m->priority.compress[0] = GNUTLS_COMP_ZLIB;
+	m->priority.compress[1] = GNUTLS_COMP_NULL;
+	m->priority.compress[2] = 0;
+
+	/*
+	 * List the key exchange algorithms in decreasing order of priority.
+	 */
+	m->priority.key_xchg[0] = GNUTLS_KX_DHE_RSA;
+	m->priority.key_xchg[1] = GNUTLS_KX_RSA;
+	m->priority.key_xchg[2] = GNUTLS_KX_DHE_DSS;
+	m->priority.key_xchg[3] = 0;
+
+	/*
+	 * List message authentication code (MAC) algorithms in decreasing
+	 * order of priority to specify via gnutls_mac_set_priority().
+	 */
+	m->priority.msg_code[0] = GNUTLS_MAC_SHA1;
+	m->priority.msg_code[1] = GNUTLS_MAC_MD5;
+	m->priority.msg_code[2] = 0;
+
+	/*
+	 * For gnutls_init, says we're a client.
+	 */
+	m->connend = GNUTLS_CLIENT;
+    }
+
+    return m;
+}
+
+static int add_name(char *target, int len, const char *tag, const char *data)
+{
+    int need = strlen(tag);
+
+    target += strlen(target);
+    if (need < len) {
+	strcat(target, tag);
+	len -= need;
+	target += need;
+    }
+    need = strlen(data);
+    if (need >= len - 1)
+	need = len - 1;
+    strncat(target, data, need)[need] = '\0';
+    return len;
+}
+#define ADD_NAME(tag, data) len = add_name(target, len, tag, data);
+
+/*
+ * Convert the X509 name 'source' to a string in the given buffer 'target',
+ * whose length is 'len'.  Return a pointer to the buffer.
+ */
+char *X509_NAME_oneline(X509_NAME * source, char *target, int len)
+{
+    if (target && (len > 0)) {
+	*target = '\0';
+	if (source) {
+	    ADD_NAME("C=", source->country);
+	    ADD_NAME(", ST=", source->state_or_province_name);
+	    ADD_NAME(", L=", source->locality_name);
+	    ADD_NAME(", O=", source->organization);
+	    ADD_NAME(", OU=", source->organizational_unit_name);
+	    ADD_NAME(", CN=", source->common_name);
+	    ADD_NAME("/Email=", source->email);
+	}
+    }
+    return target;
+}
+
+/*
+ * Extract the certificate's issuer-name data.
+ */
+X509_NAME *X509_get_issuer_name(const X509 * cert)
+{
+    X509_NAME *result;
+
+    if ((result = typeCalloc(X509_NAME)) != 0) {
+	if (ExtractCertificate(cert, result, 1) < 0) {
+	    free(result);
+	    result = 0;
+	}
+    }
+    return result;
+}
+
+/*
+ * Extract the certificate's subject-name data.
+ */
+X509_NAME *X509_get_subject_name(const X509 * cert)
+{
+    X509_NAME *result;
+
+    if ((result = typeCalloc(X509_NAME)) != 0) {
+	if (ExtractCertificate(cert, result, 0) < 0) {
+	    free(result);
+	    result = 0;
+	}
+    }
+    return result;
+}
diff --git a/src/ucxolb.opt b/src/ucxolb.opt
new file mode 100644
index 00000000..2c7cb546
--- /dev/null
+++ b/src/ucxolb.opt
@@ -0,0 +1 @@
+sys$library:ucx$ipc/library
diff --git a/src/ucxshr.opt b/src/ucxshr.opt
new file mode 100644
index 00000000..ba84be0d
--- /dev/null
+++ b/src/ucxshr.opt
@@ -0,0 +1 @@
+sys$share:ucx$ipc_shr/share
diff --git a/src/vaxc.opt b/src/vaxc.opt
new file mode 100644
index 00000000..fbb523b0
--- /dev/null
+++ b/src/vaxc.opt
@@ -0,0 +1,2 @@
+sys$share:vaxcrtl/share
+sys$library:vaxccurse/library
diff --git a/src/win_tcp.opt b/src/win_tcp.opt
new file mode 100644
index 00000000..7fcb9fdf
--- /dev/null
+++ b/src/win_tcp.opt
@@ -0,0 +1 @@
+twg$tcp:[netdist.lib]twglib/library