diff options
Diffstat (limited to 'src')
196 files changed, 137302 insertions, 0 deletions
diff --git a/src/AttrList.h b/src/AttrList.h new file mode 100644 index 0000000..cf38261 --- /dev/null +++ b/src/AttrList.h @@ -0,0 +1,62 @@ +/* + * $LynxId: AttrList.h,v 1.17 2013/05/03 20:54:09 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 + 1]; + +/* 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 0000000..3dc17f7 --- /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 0000000..806a053 --- /dev/null +++ b/src/GridText.c @@ -0,0 +1,15054 @@ +/* + * $LynxId: GridText.c,v 1.347 2024/01/15 19:11:55 Gisle.Vanem 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 <LYSearch.h> +#include <TRSTable.h> +#include <LYHistory.h> +#ifdef EXP_CHARTRANS_AUTOSWITCH +#include <UCAuto.h> +#endif /* EXP_CHARTRANS_AUTOSWITCH */ + +#include <LYexit.h> +#include <LYLeaks.h> + +#ifdef USE_COLOR_STYLE +#include <AttrList.h> +#include <LYHash.h> +#include <LYStyle.h> +#endif + +#ifdef EXP_WCWIDTH_SUPPORT +# ifdef HAVE_WCWIDTH +# ifdef HAVE_WCHAR_H +# include <wchar.h> +# endif +# else +# include <wcwidth.h> +# define wcwidth(n) mk_wcwidth(n) +# endif +#endif + +#include <LYJustify.h> + +#define is_CJK2(b) (IS_CJK_TTY && is8bits(UCH(b))) + +#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, int final, int stop_before); + +#define IS_UTF_FIRST(ch) (text->T.output_utf8 && \ + (UCH((ch))&0xc0) == 0xc0) + +#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 + +#undef CHAR_WIDTH + +#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 = FALSE; +static BOOLEAN bold_on = FALSE; + +#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 + +#define BITS_DIR 2 +#define BITS_POS 14 + +#define MASK_DIR ((1U << BITS_DIR) - 1) +#define CAST_DIR(n) ((MASK_DIR) & (unsigned)(n)) + +#define MASK_POS ((1U << BITS_POS) - 1) +#define CAST_POS(n) ((MASK_POS) & (unsigned)(n)) + +typedef struct { + unsigned sc_direction:BITS_DIR; /* on or off */ + unsigned sc_horizpos:BITS_POS; /* horizontal position of this change */ + unsigned 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; + unsigned 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, (unsigned) ((n) * sizeof(pool_data))) +#define POOLallocHTLine(ptr, size) ptr = (HTLine*) ALLOC_IN_POOL(&HTMainText->pool, (unsigned) LINE_SIZE(size)) +#define POOLallocstring(ptr, len) ptr = (char*) ALLOC_IN_POOL(&HTMainText->pool, (unsigned) ((len) + 1)) +#define POOLtypecalloc(T, ptr) ptr = (T*) ALLOC_IN_POOL(&HTMainText->pool, (unsigned) sizeof(T)) + +/**************************************************************************/ +/* + * Allocates 'n' items in the pool of type 'HTPool' pointed by 'poolptr'. + * Returns a pointer to the "allocated" memory if successful. + * Updates 'poolptr' if necessary. + */ +static void *ALLOC_IN_POOL(HTPool ** ppoolptr, unsigned request) +{ + HTPool *pool = *ppoolptr; + pool_data *ptr; + unsigned n; + unsigned j; + + if (!pool) { + outofmem(__FILE__, "ALLOC_IN_POOL"); + } else { + n = request; + if (n == 0) + n = 1; + j = (n % ALIGN_SIZE); + if (j != 0) + n += (unsigned) (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((size_t) 1, sizeof(HTPool)); + + if (!newpool) { + outofmem(__FILE__, "ALLOC_IN_POOL"); + } 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((size_t) 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; + + /* Allow for terminator */ +#define LINE_SIZE(size) (sizeof(HTLine) + (size_t)(size)) + +#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 show_number; /* For user interface (unique-urls) */ + 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? */ +#ifdef EXP_JAPANESE_SPACES + char LastChars[7]; /* utf-8 buffer */ +#else + char LastChar; /* For absorbing white space */ +#endif + +/* 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 */ + int next_number; /* next a->number value */ +#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; + int old_dtd; + int keypad_mode; + int disp_lines; /* Screen size */ + int 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 "* " preceding <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)) + +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 EXP_WCWIDTH_SUPPORT +static int utfxtracells_on_this_line = 0; /* num of UTF-8 extra cells in line */ +#endif + +#ifdef WIDEC_CURSES +# ifdef EXP_WCWIDTH_SUPPORT /* TODO: support for !WIDEC_CURSES */ +#define UTFXTRA_ON_THIS_LINE utfxtracells_on_this_line +# else +#define UTFXTRA_ON_THIS_LINE 0 +# endif +#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, int IgnoreSpaces); + +static int HText_TrueLineSize(HTLine *line, HText *text, int 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(int factor, size_t bytes) +{ + void *p; + + if (bytes < MIN_NEEDED_MEM && factor > 0) + bytes = MIN_NEEDED_MEM; + if (factor == 0) + factor = 1; + p = malloc((size_t) 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, (size_t) 700)) { + HTAlert(gettext("Memory exhausted, display interrupted!")); + } + } else { + if ((!HTMainText || HTMainText->halted == 0) && + mem_is_avail(0, (size_t) 700)) { + HTAlert(gettext("Memory exhausted, will interrupt transfer!")); + if (HTMainText) + HTMainText->halted = 1; + } + } + } + return (calloc(nmemb, size)); +} + +#endif /* CHECK_FREE_MEM */ + +#ifdef EXP_WCWIDTH_SUPPORT +static int utfextracells(const char *s) +{ + UCode_t ucs = UCGetUniFromUtf8String(&s); + int result = 0; + + if (ucs > 0) { + int cells = wcwidth((wchar_t) ucs); + + if (cells > 1) + result = (cells - 1); + } + return result; +} + +static void permit_split_after_CJchar(HText *text, const char *s, unsigned short pos) +{ + /* Can split after almost any CJ char (Korean uses space) */ + /* TODO: UAX#14 Unicode Line Breaking Algorithm (use ICU4C?) */ + if (isUTF8CJChar(s)) + text->permissible_split = pos; +} +#endif /* EXP_WCWIDTH_SUPPORT */ + +#if defined(EXP_WCWIDTH_SUPPORT) || defined(EXP_JAPANESE_SPACES) +BOOL isUTF8CJChar(const char *s) +{ + UCode_t u = UCGetUniFromUtf8String(&s); + + if ((u >= 0x4e00 && u <= 0x9fff) || /* CJK Unified Ideographs */ + (u >= 0x3000 && u <= 0x30ff) || /* CJK Symbols and Punctuation, Hiragana, Katakana */ + (u >= 0xff00 && u <= 0xffef) || /* Halfwidth and Fullwidth Forms. Fullwidth ?! are often used */ + /* rare characters */ + (u >= 0x3400 && u <= 0x4dbf) || /* CJK Unified Ideographs Extension A */ + (u >= 0xf900 && u <= 0xfaff) || /* CJK Compatibility Ideographs */ + (u >= 0x20000 && u <= 0x3ffff)) /* {Supplementary,Tertiary} Ideographic Plane */ + return YES; + return NO; +} +#endif /* EXP_WCWIDTH_SUPPORT || EXP_JAPANESE_SPACES */ + +#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 = (int) 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) +{ + a->lites.hl_info = NULL; + 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, + unsigned len) +{ + if (text != NULL) { + POOLallocstring(a->lites.hl_base.hl_text, len + 1); + memcpy(a->lites.hl_base.hl_text, text, (size_t) 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; + size_t need = (unsigned) (a->lites.hl_len - 1); + size_t want; + + a->lites.hl_len = (short) (a->lites.hl_len + 1); + want = (size_t) (a->lites.hl_len) * 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 = (short) 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 != NULL) ? (int) (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; + if (result) + 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, (unsigned) 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->data.submit_action); + FREE(form->data.submit_enctype); + FREE(form->data.submit_title); + 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) + outofmem(__FILE__, "HText_New"); + + 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) { + 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; + self->next_number = 1; +#ifdef USE_SOURCE_CACHE + /* + * Remember the parse settings. + */ + /* *INDENT-EQLS* */ + 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); +#ifdef EXP_JAPANESE_SPACES + memset(self->LastChars, 0, sizeof(self->LastChars)); +#else + self->LastChar = '\0'; +#endif + +#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)); + + /* + * 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, '.', (size_t) (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, '_', (size_t) (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 + (no_title ? 0 : 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) { + LYStrNCpy(&buffer[1], data, utf_extra); + 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); + (void) y; + 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[40]; + 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, gettext(" (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, gettext(" (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'; + } + + /* Update the terminal-emulator title */ + if (update_term_title) { + CTRACE((tfp, "update_term_title:%s\n", title)); + fprintf(stderr, "\033]0;%s%sLynx\007", title, *title ? " - " : ""); + fflush(stderr); + } + + /* + * 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 && user_mode != MINIMAL_MODE && + (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 - (int) strlen(percent) - LYstrCells(title); + if (i <= 0) { /* title is truncated */ + i = limit - LYbarWidth - (int) 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) - (int) (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 + CheckScreenSize(); + 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) { + size_t itmp = 0; + size_t written = 0; + int x_off = offset + (int) (cp - data); + size_t len = strlen(target); + size_t utf_extra = 0; + + 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_off--; + + } else if (&data[itmp] >= cp) { + if (cp == &data[itmp]) { + /* + * First printable character of target. + */ + LYmove((i + title_lines), + line->offset + LYstrExtent2(line->data, + x_off - line->offset)); + } + /* + * Output all the printable target chars. + */ + utf_extra = utf8_length(text->T.output_utf8, data + itmp); + if (utf_extra) { + LYStrNCpy(&tmp[1], &line->data[itmp + 1], utf_extra); + 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(); + data = (char *) &data[itmp]; + offset = (int) (data - line->data + line->offset); + + } /* end while */ + LYmove((i + title_lines + 1), 0); +#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); + { + auto char *cp_AnchorAddress = NULL; + + if (traversal) { + cp_AnchorAddress = stub_HTAnchor_address(link_dest); + } else if (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 { + cp_AnchorAddress = HTAnchor_address(link_dest); + } + } else { + cp_AnchorAddress = HTAnchor_address(link_dest); + } + FREE(links[nlinks].lname); + + if (cp_AnchorAddress != NULL) + links[nlinks].lname = cp_AnchorAddress; + else + StrAllocCopy(links[nlinks].lname, empty_string); + } + + links[nlinks].lx = Anchor_ptr->line_pos; + links[nlinks].ly = ((Anchor_ptr->line_num + 1) - line_number); + 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(FALSE, (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. + * Checks of the line length (as the non-UTF-8-aware display library would see + * it) against LYcols_cu are used to try to prevent lines with UTF-8 chars from + * being 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. + */ + +static int set_style_by_embedded_chars(char *s, + char *e, + unsigned start_c, + unsigned end_c) +{ + int ret = NO; + + while (--e >= s) { + if (UCH(*e) == UCH(end_c)) + break; + if (UCH(*e) == UCH(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 = (short) (a->line_pos + shift); + a->extent = (short) (a->extent - shift); + head_processed = 1; + } + /* Fix the end */ + if (last < ebyte) { + a->extent = (short) (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, (unsigned) (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; +#ifdef EXP_WCWIDTH_SUPPORT + if (text && text->T.output_utf8 && IS_UTF_FIRST(*s)) + ioldc += utfextracells(s); +#endif + } + 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, + (int) (copied - line->data), (int) (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 = CAST_POS(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 = (short) ((*prev_anchor)->extent + shift); + } + *t = '\0'; + mod_line->size = (unsigned short) (t - newdata); + return mod_line; +} + +#if defined(USE_COLOR_STYLE) +#define direction2s(d) ((d) == STACK_OFF \ + ? "OFF" \ + : ((d) == STACK_ON \ + ? "ON" \ + : "*ON")) + +/* + * 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 (%s), split %u\n", + tmp->sc_horizpos, + tmp->sc_style, + direction2s(tmp->sc_direction), + split_pos)); + for (; tmp >= start; tmp--) { + CTRACE_STYLE((tfp, "... %d %d (%s)\n", + tmp->sc_horizpos, + tmp->sc_style, + direction2s(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 = CAST_POS(split_pos); + } + } + return result; +} +#endif /* USE_COLOR_STYLE */ + +#define reset_horizpos(value) value = 0, value ^= MASK_POS + +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, (size_t) 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 */ +#ifdef EXP_WCWIDTH_SUPPORT + utfxtracells_on_this_line = 0; +#endif + HText_setLastChar(text, ' '); + +#ifdef DEBUG_APPCH + CTRACE((tfp, "GridText: split_line(%p,%d) called\n", text, split)); + CTRACE((tfp, " previous=%s\n", previous->data)); + 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 = (unsigned) (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, j; + + /* Split the line. -FM */ + prevdata[previous->size] = '\0'; + previous->size = (unsigned short) 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 = (unsigned) strlen(p); + if (plen) { /* Count funny characters */ + for (i = (int) (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++; +#ifdef EXP_WCWIDTH_SUPPORT + } else if (IS_UTF_FIRST(p[i])) { + utfxtracells_on_this_line += utfextracells(&p[i]); +#endif + } + if (p[i] == LY_SOFT_HYPHEN && + (int) text->permissible_split < i) + text->permissible_split = (unsigned) (i + 1); + } + ctrl_chars_on_this_line += utfxtra_on_this_line; + + /* Add the data to the new line. -FM */ + for (i = 0, j = (int) strlen(linedata); + (linedata[j++] = p[i++]) != '\0'; + ) ; + line->size = (unsigned short) (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. */ + } + /* Strip trailers. */ + TailTrim = (int) (previous->data + previous->size - 1 - p); + previous->size = (unsigned short) (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 = (int) 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 = CAST_POS(to->sc_horizpos + + SpecialAttrChars + - s_post); + } else if ((int) to->sc_horizpos > s_pre && + (to->sc_direction == STACK_ON || + to->sc_direction == ABS_ON)) { + if ((int) to->sc_horizpos < s) + to->sc_horizpos = 0; + else + to->sc_horizpos = CAST_POS(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, + (unsigned) 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 = CAST_POS(s_pre); + CTRACE_STYLE((tfp, + "split_line, %d:style[%d] %d (dir=%d)\n", + s_pre, + (int) (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 = CAST_POS(SpecialAttrChars); + to--; + } else { + CTRACE((tfp, "BUG: style overflow after split_line.\n")); + break; + } + } + if ((int) scan->sc_horizpos > s_pre) { + scan->sc_horizpos = CAST_POS(s_pre); + } + scan--; + } + line->numstyles = (unsigned short) (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) { + reset_horizpos(line->styles[0].sc_horizpos); + } + previous->numstyles = (unsigned short) (at_end - previous->styles + 1); + if (previous->numstyles == 0) { + reset_horizpos(previous->styles[0].sc_horizpos); + } + } +#endif /*USE_COLOR_STYLE */ + + { + HTLine *temp; + + allocHTLine(temp, previous->size); + if (!temp) + outofmem(__FILE__, "split_line_2"); + + 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 = (unsigned short) ((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 = (short) (a->extent + - (TailTrim + HeadTrim) + + SpecialAttrChars); + } else { + CTRACE_SPLITLINE((tfp, ", cut the end.\n")); + a->extent = (short) (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 = (short) 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 = (short) (start - s_post + SpecialAttrChars); + a->extent = (short) 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 = (int) 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 * (size_t) (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_TrueEmptyLine(text->last_line, 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 USE_TH_JP_AUTO_DETECT + && (text->specified_kcode != EUC) + && (text->detected_kcode != DET_EUC) +#endif + && ( +#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)) { + if (conv_jisx0201kana) { + 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; + } + /* 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 = TRUE; + 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 = FALSE; + 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 = TRUE; + 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 = FALSE; + 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 = (int) (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) { + if (line->size < MAX_LINE) { + 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); + } + if (line->size < MAX_LINE) { + line->data[line->size++] = (char) ch; + line->data[line->size] = '\0'; + utfxtra_on_this_line++; + ctrl_chars_on_this_line++; + } +#ifdef EXP_WCWIDTH_SUPPORT + /* update utfxtracells_on_this_line on last byte of UTF-8 sequence */ + { + /* find start position of UTF-8 sequence */ + int utff = line->size - 2; + int utf_xlen; + + while (utff > 0 && IS_UTF_EXTRA(line->data[utff])) + utff--; + utf_xlen = UTF_XLEN(line->data[utff]); + + if (line->size - utff == utf_xlen + 1) { /* have last byte */ + utfxtracells_on_this_line += utfextracells(&(line->data[utff])); + permit_split_after_CJchar(text, &(line->data[utff]), line->size); + } + } +#endif + 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] & 0xc0) + == 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); + } + } + } + + /* + * 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 = (unsigned short) (line->offset + (target - here)); + } else { + for (; here < target; here++) { + /* Put character into line */ + if (line->size >= MAX_LINE) + break; + 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; + BOOL add_blank = (dont_wrap_pre + && line->size + && (line->data[line->size - 1] == ' ')); + + 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); + if (add_blank) + HText_appendCharacter(text, ' '); + 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. + * + * Notes: + * 1) text->permissible_split is nonzero if we found a place to split the + * line. If there is no such place, we still will wrap at the display + * limits (the comparison against LYcols_cu). Furthermore, if the + * curses-pads feature is active, we will ignore the first comparison + * (against WRAP_COLS) to allow wide preformatted text to be displayed + * without wrapping. + * 2) ctrl_chars_on_this_line are nonprintable bytes used for formatting. + */ + actual = ((indent + (int) line->offset + (int) line->size) + + ((line->size > 0) && + (int) (line->data[line->size - 1] == LY_SOFT_HYPHEN ? 1 : 0)) + - ctrl_chars_on_this_line); + + if (( +#if !defined(USE_SLANG) && !defined(PDCURSES) + (text->permissible_split +#ifdef USE_CURSES_PADS + || !LYwideLines +#endif + ) && +#endif + (actual + + (int) style->rightIndent + + ((IS_CJK_TTY && text->kanji_buf) ? 1 : 0) +#ifdef EXP_WCWIDTH_SUPPORT + + utfxtracells_on_this_line +#endif + ) >= WRAP_COLS(text)) + || (text->T.output_utf8 + && ((actual + + UTFXTRA_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 character handling. + */ + { + HTFont font = style->font; + unsigned char hi, lo, tmp[2]; + + line = text->last_line; /* May have changed */ + + if (line->size >= MAX_LINE) { + ; + } else 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)) { + if (conv_jisx0201kana) { + JISx0201TO0208_EUC(hi, lo, &hi, &lo); + } + 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)) { + if (!conv_jisx0201kana && IS_EUC_X0201KANA(hi, lo)) + line->data[line->size++] = (char) lo; + else { + 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; + } else if (!conv_jisx0201kana + && (HTCJK == JAPANESE) + && IS_SJIS_X0201KANA(UCH((ch))) && + (kanji_code == EUC)) { + line->data[line->size++] = (char) UCH(0x8e); + line->data[line->size++] = (char) ch; + } 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 = CAST_POS(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 = + CAST_POS(line->styles[line->numstyles].sc_horizpos + - ctrl_chars_on_this_line); + } + line->styles[line->numstyles].sc_style = (unsigned short) style; + line->styles[line->numstyles].sc_direction = CAST_DIR(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, int ch) +{ + if (!text) + return; + +#ifdef EXP_JAPANESE_SPACES + if (IS_UTF_EXTRA(ch) && IS_UTF_FIRST(text->LastChars[0])) { + int i; + + for (i = 1; + text->LastChars[i] != '\0' && i < sizeof(text->LastChars) - 1; + i++) { + ; + } + text->LastChars[i] = (char) ch; + text->LastChars[i + 1] = '\0'; + return; + } + memset(text->LastChars, 0, sizeof(text->LastChars)); + text->LastChars[0] = (char) ch; +#else + text->LastChar = (char) ch; +#endif +} + +/* Get LastChar element in the text object. + * ---------------------------------------- + */ +char HText_getLastChar(HText *text) +{ + if (!text) + return ('\0'); + +#ifdef EXP_JAPANESE_SPACES + if (IS_UTF_FIRST(text->LastChars[0])) { + int i; + + for (i = 1; + text->LastChars[i] != '\0' && i < sizeof(text->LastChars); + i++) { + ; + } + return ((char) text->LastChars[i - 1]); + } + return ((char) text->LastChars[0]); +#else + return ((char) text->LastChar); +#endif +} + +#ifdef EXP_JAPANESE_SPACES +BOOL HText_checkLastChar_needSpaceOnJoinLines(HText *text) +{ + if (!text) + return YES; + + if (IS_UTF_FIRST(text->LastChars[0]) && isUTF8CJChar(text->LastChars)) + return NO; + if ((HTCJK == CHINESE || HTCJK == JAPANESE) && is8bits(text->LastChars[0])) { + /* TODO: support 2nd byte of some SJIS kanji (!is8bits && IS_SJIS_LO) */ + return NO; + } + if (text->LastChars[0] != ' ') + return YES; + return NO; +} +#endif + +/* 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 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 * (size_t)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; + } + 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 = (unsigned short) (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, int alignment) +{ + if (me) { +#ifdef EXP_NESTED_TABLES + STable_info *current = me->stbl; +#endif + +#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 != NULL && 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, int 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, + int alignment, + int 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, + int alignment, + int 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, int alignment) +{ + if (!me || !me->stbl) + return; + if (Stbl_addRowGroup(me->stbl, alignment) < 0) { + HText_cancelStbl(me); /* give up */ + } +} + +static void compute_show_number(TextAnchor *a) +{ + HTAnchor *cur, *tst; + TextAnchor *b; + int match; + + a->show_number = a->number; + if (unique_urls + && HTMainText != 0 + && HTMainText->first_anchor != 0 + && a->anchor != 0 + && (cur = a->anchor->dest) != 0 + && cur->parent != 0 + && cur->parent->address != 0) { + + match = 0; + for (b = HTMainText->first_anchor; b != a; b = b->next) { + if (b->anchor != 0 + && (tst = b->anchor->dest) != 0 + && tst->parent != 0 + && tst->parent->address != 0 + && !strcmp(cur->parent->address, + tst->parent->address) + && !strcmp(NonNull(a->anchor->tag), NonNull(b->anchor->tag))) { + match = b->show_number; + break; + } + } + if (match) + a->show_number = match; + else + a->show_number = HTMainText->next_number++; + } +} + +/* Anchor handling + * --------------- + */ +static void add_link_number(HText *text, TextAnchor *a, int 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 = HText_getLastChar(text); + int saved_linenum = text->Lines; + HTAnchor *link_dest; + char *link_text; + + compute_show_number(a); + + if (dump_links_inline + && (link_dest = HTAnchor_followLink(a->anchor)) != 0 + && (link_text = HTAnchor_address(link_dest)) != 0) { + HText_appendText(text, "["); + HText_appendText(text, link_text); + HText_appendText(text, "]"); + } else { + sprintf(marker, "[%d]", a->show_number); + HText_appendText(text, marker); + } + if (saved_linenum && text->Lines && saved_lastchar != ' ') + HText_setLastChar(text, ']'); /* if marker not after space caused split */ + if (save_position) { + a->line_num = text->Lines; + a->line_pos = (short) text->last_line->size; + } + } +} + +/* Start an anchor field +*/ +int HText_beginAnchor(HText *text, int underline, + HTChildAnchor *anc) +{ + TextAnchor *a; + + POOLtypecalloc(TextAnchor, a); + + if (a == NULL) + outofmem(__FILE__, "HText_beginAnchor"); + + a->inUnderline = (BOOLEAN) underline; + + a->sgml_offset = SGML_offset(); + a->line_num = text->Lines; + a->line_pos = (short) text->last_line->size; + if (text->last_anchor) { + text->last_anchor->next = a; + } else { + text->first_anchor = a; + } + a->next = 0; + a->anchor = anc; + a->extent = 0; + a->link_type = HYPERTEXT_ANCHOR; + text->last_anchor = a; + + if (track_internal_links + && HTAnchor_followTypedLink(anc, HTInternalLink)) { + a->number = ++(text->last_anchor_number); + a->link_type = INTERNAL_LINK_ANCHOR; + } else if (HTAnchor_followLink(anc)) { + a->number = ++(text->last_anchor_number); + } else { + a->number = 0; + } + a->show_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 = (short) (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 = (short) (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 = (unsigned) j; + else + text->permissible_split -= (unsigned) 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 = (short) (anc->line_pos - NumSize); + } + } + start->size = (unsigned short) 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 = (unsigned short) 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 -= (unsigned) 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 = (short) (anc->line_pos - i); + } + } + start->size = (unsigned short) 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 = (unsigned short) 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 = (short) (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 && + text != NULL && + text->halted != 3) { + 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. + */ + if (LYtrimBlankLines && (line_ptr = FirstHTLine(text)) != 0) { + /* + * Remove blank lines at the end of the document. + */ + while (text->last_line->data[0] == '\0' && text->Lines > 0) { + 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, + int 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 (; line_ptr != NULL && anchor_ptr->line_num > cur_line; + line_ptr = line_ptr->next, cur_line++) { + ; /* null body */ + } + if (line_ptr == NULL) + continue; + + 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 = (short) 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 (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], + (unsigned) 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 = (int) 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, + (size_t) (actual_len - hilite_len)); + actual_len -= (int) 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 = (short) LYstrExtent2(line_ptr->data, anchor_col); + if (line_ptr->data[0] == LY_SOFT_NEWLINE) + anchor_ptr->line_pos = (short) (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 = (short) (anchor_ptr->line_pos - offset); + /*handle LY_SOFT_NEWLINEs -VH */ + anchor_ptr->line_pos = (short) (anchor_ptr->line_pos + have_soft_newline_in_1st_line); + } +#endif /* WIDEC_CURSES */ + + /* + * Set the line number. + */ + anchor_ptr->line_pos = (short) (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; +} + +/* + * For the -unique-urls option, find the anchor-number of the first occurrence + * of a given address. + */ +int HText_findAnchorNumber(void *avoid) +{ + TextAnchor *a = (TextAnchor *) avoid; + + if (a->number > 0 && a->show_number == 0) + compute_show_number(a); + + return a->show_number; +} + +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; + + HTAnchor *link_dest_intl = NULL; + 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 if (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 { + cp_freeme = HTAnchor_address(link_dest); + } + } else { + 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, + int 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, + int 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 = ¤t; + break; + } + } else if (direction > 0 && + same_anchor_as_link(curlink, previous.anc, ta_skip)) { + group_to_go = ¤t; + 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 = ¤t; + break; + } + } else { + group_to_go = ¤t; + break; + } + } + } + } + } + if (!group_to_go && curlink < 0 && direction <= 0) { + group_to_go = ¤t; + } + 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 != ¤t && + 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, + int 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 + (int) 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, + int 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); +} + +/* + * 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); +} + +/* + * 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 + && (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; + int l; + + 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; + } + 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) +{ + bstring *searchstring = NULL; + bstring *temp = NULL; + char *cp; + char *tmpaddress = NULL; + int ch; + RecallType recall; + int QueryTotal; + int QueryNum; + BOOLEAN PreviousSearch = FALSE; + int code; + + /* + * 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; + BStrCopy0(searchstring, ++cp); + for (cp = searchstring->str; *cp; cp++) + if (*cp == '+') + *cp = ' '; + HTUnEscape(searchstring->str); + BStrCopy(temp, searchstring); + /* + * Make sure it's treated as the most recent query. -FM + */ + HTAddSearchQuery(searchstring->str); + } else { + /* + * New search; no default. + */ + BStrCopy0(searchstring, ""); + BStrCopy0(temp, ""); + } + + /* + * Prompt for a query string. + */ + if (isBEmpty(searchstring)) { + 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 = LYgetBString(&searchstring, FALSE, 0, recall)) < 0 || + isBEmpty(searchstring) || + ch == UPARROW_KEY || + ch == DNARROW_KEY) { + + if (recall && ch == UPARROW_KEY) { + 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) { + BStrCopy0(searchstring, cp); + if (!isBEmpty(temp) && + !strcmp(temp->str, searchstring->str)) { + _statusline(EDIT_CURRENT_QUERY); + } else if ((!isBEmpty(temp) && QueryTotal == 2) || + (isBEmpty(temp) && QueryTotal == 1)) { + _statusline(EDIT_THE_PREV_QUERY); + } else { + _statusline(EDIT_A_PREV_QUERY); + } + goto get_query; + } + } else if (recall && ch == DNARROW_KEY) { + 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) { + BStrCopy0(searchstring, cp); + if (!isBEmpty(temp) && + !strcmp(temp->str, searchstring->str)) { + _statusline(EDIT_CURRENT_QUERY); + } else if ((!isBEmpty(temp) && QueryTotal == 2) || + (isBEmpty(temp) && QueryTotal == 1)) { + _statusline(EDIT_THE_PREV_QUERY); + } else { + _statusline(EDIT_A_PREV_QUERY); + } + goto get_query; + } + } + + /* + * Search cancelled. + */ + HTInfoMsg(CANCELLED); + code = NULLFILE; + } else { + + LYTrimLeading(searchstring->str); + LYTrimTrailing(searchstring->str); + if (isBEmpty(searchstring)) { + HTInfoMsg(CANCELLED); + code = NULLFILE; + } else if (!LYforce_no_cache && + !isBEmpty(temp) && + !strcmp(temp->str, searchstring->str)) { + /* + * Don't resubmit the same query unintentionally. + */ + HTUserMsg(USE_C_R_TO_RESUB_CUR_QUERY); + code = NULLFILE; + } else { + + /* + * Add searchstring to the query list, + * or make it the most current. -FM + */ + HTAddSearchQuery(searchstring->str); + + /* + * Show the URL with the new query. + */ + if ((cp = StrChr(doc->address, '?')) != NULL) + *cp = '\0'; + StrAllocCopy(tmpaddress, doc->address); + StrAllocCat(tmpaddress, "?"); + StrAllocCat(tmpaddress, searchstring->str); + 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->str, HTMainAnchor)) { + 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. + */ + code = NORMAL; + } else { + + /* + * 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 + */ + code = NOT_FOUND; + } + } + } + BStrFree(searchstring); + BStrFree(temp); + return code; +} + +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 = (int) strlen(string); + + if (!HTisDocumentSource()) { + int adjust = result; + int 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 = (int) (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 */ + char 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); + if (result == NULL) + outofmem(__FILE__, "allocAnchorIndex"); + + 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"); + + 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 = p->value ? (int) strlen(p->value) : 0; + + 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); + } +} + +/* + * Return the column (counting from zero) at which a field should be overlaid + * on the form. + */ +static int FieldFirst(AnchorIndex * p, int wrap) +{ + return (wrap ? 0 : (p)->offset); +} + +/* + * Return the column (counting from zero) just past the field in a form. + */ +static int FieldLast(AnchorIndex * p, int wrap) +{ + return ((p)->size - wrap) + FieldFirst(p, wrap); +} + +/* + * 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. + */ +void print_wwwfile_to_fd(FILE *fp, + int is_email, + int is_reply) +{ + int line_num, byte_num, byte_count, byte_offset; + 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 != NULL; ++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_offset = line->offset; + byte_count = TrimmedLength(line->data); + for (byte_num = 0; byte_num < byte_count; byte_num += 1) { + int cell_chr, temp_chr; + size_t cell_len, temp_len; + const char *cell_ptr, *temp_ptr, *try_utf8; + + cell_ptr = &line->data[byte_num]; + cell_len = 1; + cell_chr = UCH(*cell_ptr); + + while (cur != 0 && FieldLast(cur, this_wrap) < byte_offset) { + CTRACE2(TRACE_GRIDTEXT, + (tfp, "skip field since last %d < %d\n", + FieldLast(cur, this_wrap), 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, this_wrap), + FieldLast(cur, this_wrap) - 1)); + } + if (cur != 0 + && FieldFirst(cur, this_wrap) <= byte_offset + && FieldLast(cur, this_wrap) > byte_offset) { + int off2 = ((in_field > 0) + ? in_field + : (byte_offset - FieldFirst(cur, this_wrap))); + + /* + * 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, this_wrap)) { + 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)); + } + } + + if (off2 >= 0 && off2 < cur->length) { + temp_ptr = &(cur->value[off2]); + try_utf8 = temp_ptr; + temp_chr = (int) UCGetUniFromUtf8String(&try_utf8); + if (temp_chr > 127) { + temp_len = (size_t) (try_utf8 - temp_ptr) + 1; + } else { + temp_chr = UCH(*temp_ptr); + temp_len = 1; + } + } else { + temp_ptr = &(cur->filler); + temp_len = 1; + temp_chr = UCH(*temp_ptr); + } + + if (cell_chr != temp_chr) { + CTRACE2(TRACE_GRIDTEXT, + (tfp, "line %d %d/%d [%d..%d] map %d %04X->%04X\n", + line_num, + off2, cur->length, + FieldFirst(cur, this_wrap), + FieldLast(cur, this_wrap) - 1, + byte_offset, + (unsigned) cell_chr, + (unsigned) temp_chr)); + cell_chr = temp_chr; + cell_ptr = temp_ptr; + cell_len = temp_len; + } + off2 += (int) temp_len; + byte_offset += (int) temp_len; + if ((off2 >= cur->size) && + (off2 >= cur->length || F_TEXTLIKE(cur->type))) { + in_field = -1; + this_wrap = 0; + next_wrap = 0; + } else { + in_field = off2; + } + } else { + byte_offset++; + } + + if (!IsSpecialAttrChar(cell_chr)) { +#ifndef NO_DUMP_WITH_BACKSPACES + size_t n; + + if (in_b) { + IGNORE_RC(fwrite(cell_ptr, sizeof(char), cell_len, fp)); + + for (n = 0; n < cell_len; ++n) { + fputc('\b', fp); + } + IGNORE_RC(fwrite(cell_ptr, sizeof(char), cell_len, fp)); + } else if (in_u) { + for (n = 0; n < cell_len; ++n) { + fputc('_', fp); + } + for (n = 0; n < cell_len; ++n) { + fputc('\b', fp); + } + IGNORE_RC(fwrite(cell_ptr, sizeof(char), cell_len, fp)); + } else +#endif + IGNORE_RC(fwrite(cell_ptr, sizeof(char), cell_len, fp)); + } else if (cell_chr == LY_SOFT_HYPHEN && + (byte_num + 1) >= byte_count) { + write_hyphen(fp); + } else if (dump_output_immediately && use_underscore) { + switch (cell_chr) { + 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 (cell_chr) { + 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 (!HTMainText || (line == HTMainText->last_line)) { + break; + } + } + fputc('\n', fp); + + /* + * Add the References list if appropriate + */ + if ((no_list == FALSE) && + (dump_links_inline == 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; + } + } + } +} + +/* + * see also link_has_target + */ +static BOOL anchor_has_target(TextAnchor *a, char *target) +{ + char *text = NULL; + const char *last = "?"; + int count; + + /* + * Combine the parts of the link's text using the highlighting information, + * and compare the target against that. + */ + for (count = 0; count < 10; ++count) { + const char *part = LYGetHiTextStr(a, count); + + if (part == NULL || part == last) { + if (text != NULL && LYno_attr_strstr(text, target)) { + return TRUE; + } + break; + } + StrAllocCat(text, part); + last = part; + } + + return field_has_target(a->input_field, target); +} + +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++; + a = HTMainText->first_anchor; + } + 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++; + a = HTMainText->last_anchor; + } + 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")); + } +} + +/* + * This magic FREE(anchor->UCStages) call + * stolen from HTuncache_current_document() above. + */ +static void magicUncache(void) +{ + if (!(HTOutputFormat && HTOutputFormat == WWW_SOURCE)) { + FREE(HTMainAnchor->UCStages); + } + /* avoid null-reference later */ + if (!HTOutputFormat) + HTOutputFormat = WWW_SOURCE; +} + +#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)); + + magicUncache(); + + /* + * 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)); + (void) 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)); + + magicUncache(); + + 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 || + HTMainText->disp_lines != LYlines); +} +#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) +{ + const char *result = NULL; + + if (HTMainText && + HTMainText->node_anchor) { + result = HTMainText->node_anchor->charset; + } + + return result; +} + +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, int 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, int 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, int 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, int 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, int 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++; +#ifdef EXP_WCWIDTH_SUPPORT + if (text && text->T.output_utf8 && + IS_UTF_FIRST(line->data[i])) { + true_size += utfextracells(&(line->data[i])); + } +#endif + } + } + } else { + for (i = 0; i < line->size; i++) { + if (!IsSpecialAttrChar(line->data[i]) && + IS_UTF8_EXTRA(line->data[i])) { + true_size++; +#ifdef EXP_WCWIDTH_SUPPORT + if (text && text->T.output_utf8 && + IS_UTF_FIRST(line->data[i])) { + true_size += utfextracells(&(line->data[i])); + } +#endif + } + } + } + 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, int 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) + + (int) text->last_line->offset + + HText_LastLineSize(text, IgnoreSpaces)); + } + 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); + } + + 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))) { + char *value = HTAnchor_address(dest); + BOOL ignore = FALSE; + + if (unique_urls) { + int cnt; + char *check; + + for (cnt = 0;; ++cnt) { + + check = (char *) HTList_objectAt(text->hidden_links, cnt); + if (check == 0) + break; + if (!strcmp(check, value)) { + ignore = TRUE; + break; + } + } + } + if (ignore) { + FREE(value); + } else { + HTList_appendObject(text->hidden_links, value); + } + } + + 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 BOOLEAN HTFormDisabled = FALSE; +static PerFormInfo *HTCurrentForm; + +static BOOLEAN addFormAction(FormInfo * f) +{ + BOOLEAN result = FALSE; + + if (HTCurrentForm != NULL) { + result = TRUE; + f->submit_action = NULL; + StrAllocCopy(f->submit_action, HTCurrentForm->data.submit_action); + if (HTCurrentForm->data.submit_enctype != NULL) + StrAllocCopy(f->submit_enctype, HTCurrentForm->data.submit_enctype); + if (HTCurrentForm->data.submit_title != NULL) + StrAllocCopy(f->submit_title, HTCurrentForm->data.submit_title); + f->submit_method = HTCurrentForm->data.submit_method; + } + return result; +} + +void HText_beginForm(char *action, + char *method, + char *enctype, + char *title, + const char *accept_cs) +{ + PerFormInfo *newform; + int HTFormMethod = URL_GET_METHOD; + char *HTFormAction = NULL; + char *HTFormEnctype = NULL; + char *HTFormTitle = NULL; + char *HTFormAcceptCharset = NULL; + + 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. + * This will be appended to the forms list kept by the HText object if and + * when we reach a HText_endForm. + */ + newform = typecalloc(PerFormInfo); + if (newform == NULL) + outofmem(__FILE__, "HText_beginForm"); + + PerFormInfo_free(HTCurrentForm); /* shouldn't happen here - kw */ + HTCurrentForm = newform; + + newform->number = HTFormNumber; + newform->data.submit_action = HTFormAction; + newform->data.submit_enctype = HTFormEnctype; + newform->data.submit_method = HTFormMethod; + newform->data.submit_title = HTFormTitle; + newform->accept_cs = HTFormAcceptCharset; + + 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_TEXTAREA_TYPE && + F_TEXTLIKE(a->input_field->type)) { + /* + * Got it. Make it submitting. -FM + */ + if (addFormAction(a->input_field)) { + 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). + */ + if (HTCurrentForm) { + if (HTFormDisabled) + HTCurrentForm->disabled = TRUE; + 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); + HTFormFields = 0; + HTFormDisabled = FALSE; +} + +void HText_beginSelect(char *name, + int name_cs, + int 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 = (int) (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, + int 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 = (int) (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 = typecalloc(OptionType); + if (new_ptr == NULL) + outofmem(__FILE__, "HText_setLastOptionValue"); + + text->last_anchor->input_field->select_list = new_ptr; + 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"); + } + + 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 ((tmp = typecallocn(unsigned char, strlen(cp) * 2 + 1)) != 0) { + 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 { + outofmem(__FILE__, "HText_setLastOptionValue"); + } + } 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 = (int) strlen(new_ptr->name); + int newlen = (HTCurSelectedOptionValue + ? (int) 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 = + (int) 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, + int 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"); + + a->sgml_offset = SGML_offset(); + a->inUnderline = (BOOLEAN) underline; + a->line_num = text->Lines; + a->line_pos = (short) 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->readonly = I->readonly; + f->no_cache = NO; + + HTFormFields++; + + /* + * 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 = (unsigned) 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 = (int) 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 = (int) 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 = (int) 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; + } + addFormAction(f); + } 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)) { + if (HTMainText != 0) { + HText_findAnchorNumber(a); + } else { + a->show_number = a->number; + } + sprintf(marker, "[%d]", a->show_number); + adjust_marker = (int) 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 = (short) 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 -= (int) strlen(marker); + } + + /* + * Save value for submit/reset buttons so they + * will be visible when printing the page. - LE + */ + if (f->type == F_SUBMIT_TYPE) + FREE(I->value); + 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 */ + /* + * Set the no_cache flag if the METHOD is POST. -FM + */ + if (HTCurrentForm->data.submit_method == URL_POST_METHOD) + f->no_cache = TRUE; + /* + * 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 && !HTCurrentForm->accept_cs) { + StrAllocCopy(HTCurrentForm->accept_cs, 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->show_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 parameter 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 bfr[BUFSIZ + 1]; + + 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(bfr, sizeof(char), sizeof(bfr) - 1, fd)) != 0) { + HTSABCat(result, bfr, (int) 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 + : STR_PLAINTEXT; +} +#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) +{ + size_t j; + size_t have = strlen(*Boundary); + size_t last = (size_t) BStrLen(data); + char *text = BStrData(data); + char *want = *Boundary; + + for (j = 0; (long) j <= (long) (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, + size_t len) +{ +#define B64_LINE 76 + + static const char basis_64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + char *dest; + size_t rlen; /* length of result string */ + unsigned char c1, c2, c3; + const char *eol; + char *r; + const char *str; + size_t 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"); + } + 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, STR_PLAINTEXT); + 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, (unsigned) 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)); +} + +PerFormInfo *HText_PerFormInfo(int number) +{ + return (PerFormInfo *) HTList_objectAt(HTMainText->forms, number - 1); +} + +/* + * 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, + const char *link_name, + const 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 = HText_PerFormInfo(form_number); + /* 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, STR_PLAINTEXT, 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. + */ + StrAllocCopy(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 && + non_empty(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, (size_t) anchor_limit); + if (my_data == 0) + outofmem(__FILE__, "HText_SubmitForm"); + } + + if (target_csname == NULL) { + if (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"; + } + } else { + target_csname = "us-ascii"; + target_cs = UCLYhndl_for_unspec; /* always >= 0 */ + } + } else if (target_cs < 0) { + target_cs = UCLYhndl_for_unspec; /* always >= 0 */ + } + + 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); + free(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, + STR_PLAINTEXT); + } 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 = FALSE; + if (HTCJK == JAPANESE) { + if ((0 <= target_cs) && + !strcmp(LYCharSet_UC[target_cs].MIMEname, "euc-jp")) { + TO_EUC((const unsigned char *) val_used, + (unsigned char *) copied_val_used); + success = YES; + } else if ((0 <= target_cs) && + !strcmp(LYCharSet_UC[target_cs].MIMEname, + "shift_jis")) { + TO_SJIS((const unsigned char *) val_used, + (unsigned char *) copied_val_used); + success = YES; + } + } + if (!success) { + 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), + (size_t) + 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); + } + 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); + + /* + * File-URLs (whether via GET or POST) cannot provide search queries. + * The relevant RFCs 1630, 1738 are silent on what to do with + * unexpected query parameters in a file-URL. + * + * Internet Explorer trims the query string here (after all, a "?" is + * not a legal part of a Windows filename), and other browsers copy the + * behavior. We do this for compatibility, in case someone cares. + */ + if (my_query != 0 && + my_query->len > 5 && + !strncmp(my_query->str, "file:", (size_t) 5)) { + strtok(my_query->str, "?"); + } + 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)); + LYFreePostData(doc); + FREE(content_type_out); + HTSABFree(&my_query); + } + 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 != NULL) { + /* + * 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); + 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 explicitly 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 explicitly, 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 USE_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 USE_JAPANESEUTF8_SUPPORT + && (p_in->enc != UCT_ENC_UTF8) +#endif + )) { + HTCJK = NOCJK; + } + } + } + + if (charset_explicit +#ifdef USE_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 = (short) (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 = (int) 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 = (short) (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 = (int) strlen(p); /* count of 1st part chars in this line */ + post_n = (int) 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)) { + const char *r; + + if (*old_val != 0) + (*old_val)++; + val += incr; + sprintf(lx, "%d", val); + new_n = (int) strlen(lx); + if ((r = StrChr(ht->next->data, ']')) == 0) { + r = ""; + } + strcat(lx, r); + + /* + * 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 = (int) 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"); + + 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 ((int) 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"); + + /* 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->readonly = anchor->input_field->readonly; + 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 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 = (unsigned short) (htline->size + 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 char *readEditedFile(char *ed_temp) +{ + struct stat stat_info; + size_t size; + + FILE *fp; + + char *ebuf; + + CTRACE((tfp, "GridText: entered HText_EditTextArea()\n")); + + /* + * Read back the edited temp file into our buffer. + */ + if ((stat(ed_temp, &stat_info) < 0) || + !S_ISREG(stat_info.st_mode) || + ((size = (size_t) stat_info.st_size) == 0)) { + size = 0; + ebuf = typecalloc(char); + + if (!ebuf) + outofmem(__FILE__, "HText_EditTextArea"); + } 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; + } + + if ((fp = fopen(ed_temp, "r")) != 0) { + size = fread(ebuf, (size_t) 1, size, fp); + LYCloseInput(fp); + ebuf[size] = '\0'; /* Terminate! - kw */ + } else { + size = 0; + } + } + + /* + * 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'; + + return ebuf; +} + +static int finish_ExtEditForm(LinkInfo * form_link, TextAnchor *start_anchor, + char *ed_temp, + int orig_cnt) +{ + 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 display_size; + int wanted_fieldlen_wrap = -1; /* not yet asked; 0 means don't. */ + char *skip_at = NULL; + int skip_num = 0, i; + size_t line_used = MAX_LINE; + + CTRACE((tfp, "GridText: entered HText_EditTextArea()\n")); + + if ((ebuf = readEditedFile(ed_temp)) == 0) { + return 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 = typeMallocn(char, line_used)) == 0) + outofmem(__FILE__, "HText_EditTextArea"); + + anchor_ptr = start_anchor; + display_size = anchor_ptr->input_field->size; + if (display_size <= 4 || + display_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 = (int) (skip_at - lp); + LYStrNCpy(line, lp, len0); + 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 = (int) (cp - lp); + else + len = (int) strlen(lp); + + if (wanted_fieldlen_wrap < 0 && + !wrapalert && + len0 + len >= display_size && + (cp = StrChr(lp, ' ')) != NULL && + (cp - lp) < display_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 = display_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; + } + + /* + * Check if the new text will fit in the buffer. HTML does not define + * a "maxlength" attribute for TEXTAREA; its data can grow as needed. + * Lynx will not adjust the display to reflect larger amounts of text; + * it relies on the rows/cols attributes as well as the initial content + * of the text area for the layout. + */ + if ((size_t) (len0 + len) >= line_used) { + line_used = (size_t) (3 * (len0 + len)) / 2; + if ((line = typeRealloc(char, line, line_used)) == 0) + outofmem(__FILE__, "HText_EditTextArea"); + } + + strncat(line, lp, (size_t) 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_EditTextArea(LinkInfo * form_link) +{ + char *ed_temp; + FILE *fp; + + TextAnchor *anchor_ptr; + TextAnchor *start_anchor = NULL; + BOOLEAN firstanchor = TRUE; + + char ed_offset[DigitsOf(int) + 3]; + int start_line = 0; + int entry_line = form_link->anchor_line_num; + int orig_cnt = 0; + int offset = 0; + + FormInfo *form = form_link->l_form; + + CTRACE((tfp, "GridText: entered HText_EditTextArea()\n")); + + if ((ed_temp = typeMallocn(char, LY_MAXPATH)) == 0) { + outofmem(__FILE__, "HText_EditTextArea"); + } else if ((fp = LYOpenTemp(ed_temp, "", "w")) != 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); + + if (start_anchor != 0) { + 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 = finish_ExtEditForm(form_link, start_anchor, ed_temp, orig_cnt); + + CTRACE((tfp, "GridText: exiting HText_EditTextArea()\n")); + } + (void) LYRemoveTemp(ed_temp); + FREE(ed_temp); + } + + /* + * 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; +} + +/* + * Similar to HText_EditTextArea, but assume a single-line text field -TD + */ +void HText_EditTextField(LinkInfo * form_link) +{ + char *ed_temp; + FILE *fp; + + FormInfo *form = form_link->l_form; + + CTRACE((tfp, "GridText: entered HText_EditTextField()\n")); + + ed_temp = typeMallocn(char, LY_MAXPATH); + + if ((fp = LYOpenTemp(ed_temp, "", "w")) == 0) { + FREE(ed_temp); + return; + } + + /* + * Write the anchors' text to the temp edit file. + */ + fputs(form->value, fp); + fputc('\n', fp); + + LYCloseTempFP(fp); + + CTRACE((tfp, "GridText: text field |%s| dumped to tempfile\n", form_link->lname)); + CTRACE((tfp, "GridText: invoking editor (%s) on tempfile\n", editor)); + + edit_temporary_file(ed_temp, "", NULL); + + CTRACE((tfp, "GridText: returned from editor (%s)\n", editor)); + + if (!form->disabled) { + char *ebuf; + char *p; + + if ((ebuf = readEditedFile(ed_temp)) != 0) { + /* + * Only use the first line of the result. + */ + for (p = ebuf; *p != '\0'; ++p) { + if (*p == '\n' || *p == '\r') { + *p = '\0'; + break; + } + } + StrAllocCopy(form->value, ebuf); + FREE(ebuf); + } + } + + (void) LYRemoveTemp(ed_temp); + FREE(ed_temp); + + CTRACE((tfp, "GridText: exiting HText_EditTextField()\n")); +} + +/* + * 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; + } + + if (end_anchor == NULL) + return; + + 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 = 0; + char *line = 0; + 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, (size_t) 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")); + FREE(line); + FREE(fbuf); + 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"); + + /* Init all the fields in the new TextAnchor. */ + /* [anything "special" needed based on ->show_anchor value ?] */ + /* *INDENT-EQLS* */ + a->next = anchor_ptr; + a->number = anchor_ptr->number; + a->show_number = anchor_ptr->show_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->readonly = anchor_ptr->input_field->readonly; + 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 = typeMallocn(char, MAX_LINE)) == 0) + outofmem(__FILE__, "HText_InsertFile"); + + match_tag = anchor_ptr->number; + + lp = fbuf; + + while (*lp) { + + if ((cp = StrChr(lp, '\n')) != 0) + len = (int) (cp - lp); + else + len = (int) 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 */ + } + LYStrNCpy(line, lp, len); + + /* + * 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 available 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 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: + if (text->T.output_utf8 && is8bits(buffer[0])) { + utf_extra = utf8_length(text->T.output_utf8, data - 1); + LastDisplayChar = 'M'; + } + if (utf_extra) { + LYStrNCpy(&buffer[1], data, utf_extra); + 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, + int 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 (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 = (int) 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) { + LYStrNCpy(&buffer[1], data, utf_extra); + 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]) + && (!conv_jisx0201kana && (kanji_code != SJIS))) { + /* + * 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, + int inU, + int utf_flag) +{ +#define pvtTITLE_HEIGHT 1 + HTLine *todr; + int i, n = 0; + int XP_draw_min = 0; + int flags = ((flag == TRUE) ? 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, (unsigned) 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); + if (col >= 0) { + LYmove(row, col); + redraw_part_of_line(todr1, text, (int) strlen(text), HTMainText); + } + todr1 = todr1->next; + row++; + } +#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 + off_t 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 == 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: %" PRI_off_t " ", CAST_off_t (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 0000000..9c68d3d --- /dev/null +++ b/src/GridText.h @@ -0,0 +1,302 @@ +/* + * $LynxId: GridText.h,v 1.70 2022/06/12 16:38:03 KIHARA.Hideto 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 int HText_findAnchorNumber(void *avoid); + 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, int ch); + extern char HText_getLastChar(HText *text); +#ifdef EXP_JAPANESE_SPACES + extern BOOL HText_checkLastChar_needSpaceOnJoinLines(HText *text); +#endif + + 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, + int 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, int is_email, int 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, + int ta_skip); + extern BOOL HText_getFirstTargetInLine(HText *text, + int line_num, + int 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, int IgnoreSpaces); + extern BOOL HText_PreviousLineEmpty(HText *me, int 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, int IgnoreSpaces); + extern int HText_PreviousLineSize(HText *me, int 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, int, int); + extern void HText_startStblRowGroup(HText *, int); + extern void HText_startStblTABLE(HText *, int); + extern void HText_startStblTD(HText *, int, int, int, int); + extern void HText_startStblTR(HText *, int); + +/* 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, + int multiple, + char *len); + extern int HText_getOptionNum(HText *text); + extern char *HText_setLastOptionValue(HText *text, + char *value, + char *submit_value, + int order, + int checked, + int val_cs, + int submit_val_cs); + extern int HText_beginInput(HText *text, + int underline, + InputFieldData * I); + extern void HText_endInput(HText *text); + extern PerFormInfo *HText_PerFormInfo(int number); + extern int HText_SubmitForm(FormInfo * submit_item, DocInfo *doc, + const char *link_name, + const 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_EditTextArea(LinkInfo * form_link); + extern void HText_EditTextField(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, + int inU, + int 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); + +#if defined(EXP_WCWIDTH_SUPPORT) || defined(EXP_JAPANESE_SPACES) + extern BOOL isUTF8CJChar(const char *s); +#endif + +#ifdef __cplusplus +} +#endif +#endif /* LYGRIDTEXT_H */ diff --git a/src/HTAlert.c b/src/HTAlert.c new file mode 100644 index 0000000..81594cf --- /dev/null +++ b/src/HTAlert.c @@ -0,0 +1,1201 @@ +/* + * $LynxId: HTAlert.c,v 1.103 2017/07/02 19:54:30 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((unsigned)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)); + LYSleepDelay(); +} + +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) + || (rate == rateEtaKB2) +#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 (isRateInKB(LYTransferRate)) { + 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) / (double) 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 (36) +static char *sprint_tbuf(char *s, long t) +{ + const char *format = ((LYTransferRate == rateEtaBYTES2 || + LYTransferRate == rateEtaKB2) + ? "% 2ld%c" + : "%ld%c"); + char *base = s; + + if (t < 0) { + strcpy(s, "forever"); + } else { + if (t > (3600 * 24)) { + sprintf(s, format, t / (3600 * 24), 'd'); + s += strlen(s); + t %= (3600 * 24); + } + if (t > 3600) { + sprintf(s, format, t / 3600, 'h'); + s += strlen(s); + t %= 3600; + } + if (t > 60) { + sprintf(s, format, t / 60, 'm'); + s += strlen(s); + t %= 60; + } + if (s == base) { + sprintf(s, "% 2ld sec", t); + } else if (t != 0) { + sprintf(s, format, t, 's'); + } + } + return base; +} +#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 = (double) tv.tv_sec + (double) 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) ((double) (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) ((double) 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 = (float) bytes / (float) total; + int meter = (int) (((float) LYcolLimit * percent) - 5); + + CTRACE((tfp, "rateBAR: bytes: %" PRI_off_t ", total: " + "%" PRI_off_t "\n", + CAST_off_t (bytes), + CAST_off_t (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 + || LYTransferRate == rateEtaBYTES2 + || LYTransferRate == rateEtaKB2) { + 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; + bstring *data = NULL; + + _statusline(Msg); + BStrCopy0(data, deflt ? deflt : ""); + + if (!dump_output_immediately) + (void) LYgetBString(&data, FALSE, 0, NORECALL); + + StrAllocCopy(rep, data->str); + + BStrFree(data); + return rep; +} + +/* + * Prompt for password without echoing the reply. HTPromptPassword() + * ---------------------------------------------- + */ +char *HTPromptPassword(const char *Msg, const char *given) +{ + char *result = NULL; + bstring *data = NULL; + + if (isEmpty(given)) + given = ""; + if (!dump_output_immediately) { + _statusline(Msg ? Msg : PASSWORD_PROMPT); + BStrCopy0(data, given); + (void) LYgetBString(&data, TRUE, 0, NORECALL); + StrAllocCopy(result, data->str); + BStrFree(data); + } else { + printf("\n%s\n", PASSWORD_REQUIRED); + StrAllocCopy(result, given); + } + 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, + int 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 if (isEmpty(*username)) { + /* + * 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 if (isEmpty(*password)) { + /* + * 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)) { + *password = HTPromptPassword(PASSWORD_PROMPT, *password); + } 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 */ + const 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; + } + /* FALLTHRU */ + + 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 LYSleepDelay(void) +{ + if (okToSleep()) + LYSleep(DelaySecs); +} + +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 0000000..03106f5 --- /dev/null +++ b/src/HTAlert.h @@ -0,0 +1,168 @@ +/* + * $LynxId: HTAlert.h,v 1.35 2016/11/24 23:44:49 tom Exp $ + * + * 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, const char *given); + +/* 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, + int 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 LYSleepDelay(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 0000000..06a669a --- /dev/null +++ b/src/HTFWriter.c @@ -0,0 +1,1516 @@ +/* + * $LynxId: HTFWriter.c,v 1.125 2023/11/05 23:46:45 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. + */ + +#define HTSTREAM_INTERNAL 1 + +#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, const 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, int 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, (size_t) 1, (size_t) l, me->fp); + if (result != (size_t) l) { + HTFWriter_error(me, "HTFWriter_write"); + } + } +} + +static void decompress_gzip(HTStream *me) +{ + char *in_name = me->anchor->FileCache; + char copied[LY_MAXPATH]; + FILE *fp = LYOpenTemp(copied, ".tmp.gz", BIN_W); + + if (fp != 0) { +#ifdef USE_ZLIB + char buffer[BUFSIZ]; + gzFile gzfp; + int status; + + CTRACE((tfp, "decompressing '%s'\n", in_name)); + if ((gzfp = gzopen(in_name, BIN_R)) != 0) { + BOOL success = TRUE; + size_t actual = 0; + + CTRACE((tfp, "...opened '%s'\n", copied)); + while ((status = gzread(gzfp, buffer, sizeof(buffer))) > 0) { + size_t want = (size_t) status; + size_t have = fwrite(buffer, sizeof(char), want, fp); + + actual += have; + if (want != have) { + success = FALSE; + break; + } + } + gzclose(gzfp); + LYCloseTempFP(fp); + CTRACE((tfp, "...decompress %" PRI_off_t " to %lu\n", + CAST_off_t (me->anchor->actual_length), + (unsigned long)actual)); + if (success) { + if (LYRenameFile(copied, in_name) == 0) + me->anchor->actual_length = (off_t) actual; + (void) LYRemoveTemp(copied); + } + } +#else +#define FMT "%s %s" + const char *program; + + if (LYCopyFile(in_name, copied) == 0) { + char expanded[LY_MAXPATH]; + char *command = NULL; + + if ((program = HTGetProgramPath(ppUNCOMPRESS)) != NULL) { + HTAddParam(&command, FMT, 1, program); + HTAddParam(&command, FMT, 2, copied); + HTEndParam(&command, FMT, 2); + } + if (LYSystem(command) == 0) { + struct stat stat_buf; + + strcpy(expanded, copied); + *strrchr(expanded, '.') = '\0'; + if (LYRenameFile(expanded, in_name) != 0) { + CTRACE((tfp, "rename failed %s to %s\n", expanded, in_name)); + } else if (stat(in_name, &stat_buf) != 0) { + CTRACE((tfp, "stat failed for %s\n", in_name)); + } else { + me->anchor->actual_length = stat_buf.st_size; + } + } else { + CTRACE((tfp, "command failed: %s\n", command)); + } + free(command); + (void) LYRemoveTemp(copied); + } +#undef FMT +#endif + } +} + +/* 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 + HANDLE cur_handle; + + cur_handle = GetForegroundWindow(); +#endif + + if (me->fp) + fflush(me->fp); + if (me->end_command) { /* Temp file */ + LYCloseTempFP(me->fp); + /* + * Handle a special case where the server used "Content-Type: gzip". + * Normally that feeds into the presentation stages, but if the link + * happens to point to something that will not be presented, but + * instead offered as a download, it comes here. In that case, ungzip + * the content before prompting the user for the place to store it. + */ + if (me->anchor->FileCache != NULL + && me->anchor->no_content_encoding == FALSE + && me->input_format == HTAtom_for("application/x-gzip") + && !strcmp(me->anchor->content_encoding, "gzip")) { + decompress_gzip(me); + } +#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'; + (void) 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'; + (void) remove(path); + } + } else if (len > 3 && !strcasecomp(&path[len - 2], "br")) { +#ifdef USE_BROTLI + if (!skip_loadfile) { + use_zread = YES; + } else +#endif /* USE_BROTLI */ + { + path[len - 3] = '\0'; + (void) remove(path); + } + } else if (len > 2 && !strcasecomp(&path[len - 1], "Z")) { + path[len - 2] = '\0'; + (void) remove(path); + } + if (!use_zread) { + if (!dump_output_immediately) { + /* + * Tell user what's happening. - FM + */ + _HTProgress(me->end_command); + } + /* + * Uncompress it. - FM + */ + if (!isEmpty(me->end_command)) + 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); + (void) 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 = (unsigned) (the_dash - path); + + StrAllocCopy(new_path, path); + new_path[off] = '.'; + if (strlen(new_path + off) > 4) + new_path[off + 4] = '\0'; + if (LYRenameFile(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")); + (void) 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 == WWW_PRESENT) { + FREE(addr); + (void) 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, wait_viewer_termination); +#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")); + (void) 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")); + (void) SetForegroundWindow(cur_handle); + } +#else + start_curses(); +#endif + } + } + FREE(me->end_command); + } + FREE(me->viewer_command); + + if (dump_output_immediately) { + if (me->anchor->FileCache) + (void) 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 + (void) 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"); + + 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"); + + 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 */ + char *cp, *view_fname; + + 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(UCH(*cp)) || IS_SJIS_HI2(UCH(*cp))) { + cp += 2; + continue; + } else if (*cp == '/') { + *cp = '\\'; + } + cp++; + } + if (StrChr(view_fname, ' ')) + view_fname = 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, STR_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 = NULL; + 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"); + + 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 == WWW_DOWNLOAD) + goto Prepend_BASE; + return ret_obj; + } + + LYCancelDownload = FALSE; + if (HTOutputFormat != 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, STR_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 = NULL; + 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, STR_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, STR_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=\"" STR_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->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(STR_BINARY); + 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 cftBrotli: + if ((program = HTGetProgramPath(ppBROTLI)) != NULL) { + StrAllocCopy(uncompress_mask, program); + StrAllocCat(uncompress_mask, " -j -d %s"); + compress_suffix = "br"; + } + 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 == WWW_DOWNLOAD || /* download */ + !strcasecomp(pres->rep_out->name, STR_DOWNLOAD) || /* download */ + (traversal && /* only handle html or plain text for traversals */ + strcasecomp(anchor->content_type, STR_HTML) && + strcasecomp(anchor->content_type, STR_PLAINTEXT))) { + /* + * 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"); + + 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) { + (void) LYRemoveTemp(anchor->FileCache); + FREE(anchor->FileCache); + } + + /* + * Get a new temporary filename and substitute a suitable suffix. - FM + */ + middle = NULL; + if (!strcasecomp(anchor->content_type, STR_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_BROTLI + if (compress_suffix[0] == 'b' /* e.g., ".br" */ + && compress_suffix[1] == 'r' + && !me->viewer_command) { + /* + * We won't call brotli externally, so we don't need to supply a + * command for it. + */ + StrAllocCopy(me->end_command, ""); + } else +#endif +#ifdef USE_BZLIB + if (compress_suffix[0] == 'b' /* must be bzip2 */ + && compress_suffix[1] == 'z' + && !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 = NULL; + 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 = NULL; + 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"); + + 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 0000000..90c2b9e --- /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 0000000..17f7491 --- /dev/null +++ b/src/HTForms.h @@ -0,0 +1,174 @@ +/* + * $LynxId: HTForms.h,v 1.34 2018/05/04 22:50:54 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, + int use_last_tfpos, + int immediate_submit); + + extern int change_form_link_ex(int cur, + DocInfo *newdoc, + BOOLEAN *refresh_screen, + int use_last_tfpos, + int immediate_submit, + int 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; + int readonly; + 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 readonly; /* If YES, can't change values */ + int name_cs; + int value_cs; + char *accept_cs; + } FormInfo; + +#define FormIsReadonly(form) ((form) && ((form)->disabled || (form)->readonly)) + +/* + * 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 */ + int disabled; /* If YES, can't change values */ + FormInfo data; + 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_SUBMITLIKE(type) ((type) == F_SUBMIT_TYPE || \ + (type) == F_IMAGE_SUBMIT_TYPE || \ + (type) == F_TEXT_SUBMIT_TYPE) + +#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 0000000..466bc72 --- /dev/null +++ b/src/HTInit.c @@ -0,0 +1,1503 @@ +/* + * $LynxId: HTInit.c,v 1.98 2022/06/12 21:17:37 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, 0L, media) + +#define SET_EXTERNL(rep_in, rep_out, command, quality) \ + HTSetConversion(rep_in, rep_out, command, quality, 3.0, 0.0, 0L, mediaEXT) + +#define SET_INTERNL(rep_in, rep_out, command, quality) \ + HTSetConversion(rep_in, rep_out, command, quality, 0.0, 0.0, 0L, 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", STR_PRESENT, HTMIMERedirect, 2.0); + SET_INTERNL("message/x-http-redirection", "www/debug", HTMIMERedirect, 1.0); + SET_INTERNL("www/mime", STR_PRESENT, HTMIMEConvert, 1.0); + SET_INTERNL("www/mime", STR_DOWNLOAD, HTMIMEConvert, 1.0); + SET_INTERNL("www/mime", STR_SOURCE, HTMIMEConvert, 1.0); + SET_INTERNL("www/mime", STR_DUMP, HTMIMEConvert, 1.0); + + /* + * Add our compressed file handlers. + */ + SET_INTERNL("www/compressed", STR_DOWNLOAD, HTCompressed, 1.0); + SET_INTERNL("www/compressed", STR_PRESENT, HTCompressed, 1.0); + SET_INTERNL("www/compressed", STR_SOURCE, HTCompressed, 1.0); + SET_INTERNL("www/compressed", STR_DUMP, HTCompressed, 1.0); + + /* + * The following support some content types seen here/there: + */ + SET_INTERNL("application/html", "text/x-c", HTMLToC, 0.5); + SET_INTERNL("application/html", STR_PLAINTEXT, HTMLToPlain, 0.5); + SET_INTERNL("application/html", STR_PRESENT, HTMLPresent, 2.0); + SET_INTERNL("application/html", STR_SOURCE, HTPlainPresent, 1.0); + SET_INTERNL("application/xml", STR_PRESENT, HTMLPresent, 2.0); + SET_INTERNL("application/x-wais-source", STR_SOURCE, HTPlainPresent, 1.0); + SET_INTERNL("application/x-wais-source", STR_PRESENT, HTWSRCConvert, 2.0); + SET_INTERNL("application/x-wais-source", STR_DOWNLOAD, HTWSRCConvert, 1.0); + SET_INTERNL("application/x-wais-source", STR_DUMP, HTWSRCConvert, 1.0); + + /* + * Save all unknown mime types to disk. + */ + SET_EXTERNL(STR_SOURCE, STR_PRESENT, HTSaveToFile, 1.0); + SET_EXTERNL(STR_SOURCE, STR_SOURCE, HTSaveToFile, 1.0); + SET_EXTERNL(STR_SOURCE, STR_DOWNLOAD, HTSaveToFile, 1.0); + SET_EXTERNL(STR_SOURCE, "*", HTSaveToFile, 1.0); + + /* + * Output all www/dump presentations to stdout. + */ + SET_EXTERNL(STR_SOURCE, STR_DUMP, HTDumpToStdout, 1.0); + + /* + * Other internal types, which must precede the "www/present" entries + * below (otherwise, they will be filtered out in HTFilterPresentations()). + */ + SET_INTERNL("text/css", STR_PLAINTEXT, HTMLToPlain, 0.5); + SET_INTERNL(STR_HTML, STR_PLAINTEXT, HTMLToPlain, 0.5); + SET_INTERNL(STR_HTML, "text/x-c", HTMLToC, 0.5); + SET_INTERNL(STR_HTML, STR_SOURCE, HTPlainPresent, 1.0); + SET_INTERNL(STR_PLAINTEXT, STR_SOURCE, HTPlainPresent, 1.0); + SET_INTERNL("text/sgml", STR_SOURCE, HTPlainPresent, 1.0); + SET_INTERNL("text/x-sgml", STR_SOURCE, HTPlainPresent, 1.0); + + /* + * Now add our basic conversions. These include the types which will + * be listed in a "Accept:" line sent to a server. These criteria are + * used in HTFilterPresentations() to select acceptable types: + * + * a) input is not "www/mime" or "www/compressed" + * b) output is "www/present" + * c) quality is in the range 0.0 to 1.0, i.e., excludes the 2.0's. + * + * For reference: + * RFC 1874 - text/sgml + * RFC 2046 - text/plain + * RFC 2318 - text/css + * RFC 3023 - text/xml + * obsolete - text/x-sgml + * + * as well as + * http://www.iana.org/assignments/media-types/media-types.xhtml + * + * and + * http://www.w3.org/TR/xhtml-media-types/ + * + * which describes + * application/xhtml+xml + * text/html + */ + SET_INTERNL("application/xhtml+xml", STR_PRESENT, XHTMLPresent, 1.0); + SET_INTERNL("application/xhtml+xml", STR_SOURCE, HTPlainPresent, 1.0); + SET_INTERNL("text/css", STR_PRESENT, HTPlainPresent, 1.0); + SET_INTERNL(STR_HTML, STR_PRESENT, HTMLPresent, 1.0); + SET_INTERNL(STR_PLAINTEXT, STR_PRESENT, HTPlainPresent, 1.0); + SET_INTERNL("text/sgml", STR_PRESENT, HTMLPresent, 1.0); + SET_INTERNL("text/x-sgml", STR_PRESENT, HTMLPresent, 2.0); + SET_INTERNL("text/xml", STR_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(STR_HTML, STR_SOURCE, HTMLParsedPresent, 1.0); + SET_INTERNL(STR_HTML, STR_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); + + *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 == ESCAPE) { + 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) + squote = FALSE; + } else if (dquote) { + switch (ch) { + case DQUOTE: + dquote = FALSE; + break; + case ESCAPE: + escape = TRUE; + break; + } + } else { + switch (ch) { + case DQUOTE: + dquote = TRUE; + break; + case SQUOTE: + squote = 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); + + *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); + } + if (len > 0 && LineBuf[len - 1] == ESCAPE) { + 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, STR_HTML, 9) || + !strncasecomp(t, STR_PLAINTEXT, 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 == ESCAPE) { + escaped = 1; + } else if (*s == DQUOTE) { + ++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 == DQUOTE) + 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 == DQUOTE) { + t = LYSkipQuoted(s); + StrAllocCopy(result, s + 1); + result[t - s - 2] = '\0'; + for (j = k = 0;; ++j, ++k) { + if (result[j] == ESCAPE) { + ++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 == ESCAPE) { + 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, UCH(*from)); + } else if (*from == ESCAPE) { + escaped = 1; + } else if (prefixed) { + prefixed = 0; + switch (*from) { + case '%': /* not defined */ + HTChunkPutc(cmd, UCH(*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) { + 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 (name) { + 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, UCH(*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); + (void) 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"); + + 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 won't 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("*", STR_PLAINTEXT, "8bit"); + SET_SUFFIX1("*.*", STR_PLAINTEXT, "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", STR_BINARY, "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", STR_BINARY, "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"); + + SET_SUFFIX1(".br", "application/x-brotli", "binary"); + + SET_SUFFIX1(".xz", "application/x-xz", "binary"); + + SET_SUFFIX1(".lz", "application/x-lzip", "binary"); + SET_SUFFIX1(".lzma", "application/x-lzma", "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", STR_BINARY, "binary", "Prog. Object", 0.5); + HTSetSuffix5(".a", STR_BINARY, "binary", "Prog. Library", 0.5); + HTSetSuffix5(".so", STR_BINARY, "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", STR_BINARY, "gzip", "GNU BAK File"); + SET_SUFFIX5(".bkp-gz", STR_BINARY, "gzip", "GNU BAK File"); + SET_SUFFIX5(".bck_gz", STR_BINARY, "gzip", "GNU BAK File"); + SET_SUFFIX5(".bck-gz", STR_BINARY, "gzip", "GNU BAK File"); + + SET_SUFFIX5(".bkp-Z", STR_BINARY, "compress", "Comp. BAK File"); + SET_SUFFIX5(".bkp_Z", STR_BINARY, "compress", "Comp. BAK File"); + SET_SUFFIX5(".bck-Z", STR_BINARY, "compress", "Comp. BAK File"); + SET_SUFFIX5(".bck_Z", STR_BINARY, "compress", "Comp. BAK File"); +#else + HTSetSuffix5(".bak", NULL, "binary", "Backup", 0.5); + SET_SUFFIX5(".bkp", STR_BINARY, "binary", "VMS BAK File"); + SET_SUFFIX5(".bck", STR_BINARY, "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", STR_BINARY, "binary", "VMS Help Libr."); + SET_SUFFIX5(".olb", STR_BINARY, "binary", "VMS Obj. Libr."); + SET_SUFFIX5(".tlb", STR_BINARY, "binary", "VMS Text Libr."); + SET_SUFFIX5(".obj", STR_BINARY, "binary", "Prog. Object"); + SET_SUFFIX5(".decw$book", STR_BINARY, "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", STR_PLAINTEXT, "8bit"); + SET_SUFFIX1(".cc", STR_PLAINTEXT, "8bit"); + SET_SUFFIX1(".c++", STR_PLAINTEXT, "8bit"); + SET_SUFFIX1(".css", STR_PLAINTEXT, "8bit"); + SET_SUFFIX1(".h", STR_PLAINTEXT, "8bit"); + SET_SUFFIX1(".pl", STR_PLAINTEXT, "8bit"); + SET_SUFFIX1(".text", STR_PLAINTEXT, "8bit"); + SET_SUFFIX1(".txt", STR_PLAINTEXT, "8bit"); + + SET_SUFFIX1(".php", STR_HTML, "8bit"); + SET_SUFFIX1(".php3", STR_HTML, "8bit"); + SET_SUFFIX1(".html3", STR_HTML, "8bit"); + SET_SUFFIX1(".ht3", STR_HTML, "8bit"); + SET_SUFFIX1(".phtml", STR_HTML, "8bit"); + SET_SUFFIX1(".shtml", STR_HTML, "8bit"); + SET_SUFFIX1(".sht", STR_HTML, "8bit"); + SET_SUFFIX1(".htmlx", STR_HTML, "8bit"); + SET_SUFFIX1(".htm", STR_HTML, "8bit"); + SET_SUFFIX1(".html", STR_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", STR_HTML, "8bit"); + SET_SUFFIX1(".html", STR_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, int stop, int stop2) +{ + int x = 0, y; + + for (x = 0; (line[x] + && UCH(line[x]) != UCH(stop) + && UCH(line[x]) != UCH(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, (int) 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 0000000..5c57a07 --- /dev/null +++ b/src/HTML.c @@ -0,0 +1,8198 @@ +/* + * $LynxId: HTML.c,v 1.200 2022/07/22 20:22:13 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 Overridden + * + */ + +#define HTSTREAM_INTERNAL 1 + +#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) {} +#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, + STRING2PTR 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(STRING2PTR value, int src_type); +static char *MakeNewImageValue(STRING2PTR value); +static char *MakeNewMapValue(STRING2PTR 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) +{ + if (dump_output_immediately && dump_to_stderr) + fprintf(stderr, "%s", message); + + switch ((enumBadHtml) cfg_bad_html) { + case BAD_HTML_IGNORE: + break; + case BAD_HTML_TRACE: + case BAD_HTML_MESSAGE: + case BAD_HTML_WARN: + CTRACE((tfp, "%s", message)); + break; + } + + switch ((enumBadHtml) cfg_bad_html) { + case BAD_HTML_IGNORE: + case BAD_HTML_TRACE: + case BAD_HTML_WARN: + break; + case BAD_HTML_MESSAGE: + LYstore_message(message); + break; + } +} + +/*_________________________________________________________________________ + * + * A C T I O N R O U T I N E S + */ + +/* Character handling + * ------------------ + */ +void HTML_put_character(HTStructured * me, int c) +{ + unsigned uc = UCH(c); + + /* + * Ignore all non-MAP content when just scanning a document for MAPs. - FM + */ + if (LYMapsOnly && me->sp[0].tag_number != HTML_OBJECT) + return; + + c = (int) uc; + + /* + * 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'; + uc = UCH(c); + } + + /* + * 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, uc); +#ifdef EXP_JAPANESE_SPACES + } else if (c == '\t') { + HTChunkPutc(&me->title, ' '); + /* don't replace '\n' with ' ' if Chinese or Japanese - HN */ + } else if (me->title.size > 0 && + is8bits(me->title.data[me->title.size - 1])) { + if (HTCJK == CHINESE || HTCJK == JAPANESE) { + /* TODO: support 2nd byte of SJIS (!is8bits && IS_SJIS_LO) */ + return; + } else if (IS_UTF8_TTY) { + /* find start position of UTF-8 sequence */ + int i = me->title.size - 1; + + while (i > 0 && (me->title.data[i] & 0xc0) == 0x80) /* UTF_EXTRA */ + i--; + if (isUTF8CJChar(&(me->title.data[i]))) + return; + } + HTChunkPutc(&me->title, ' '); +#endif + } else { + HTChunkPutc(&me->title, ' '); + } + return; + + case HTML_STYLE: + HTChunkPutc(&me->style_block, uc); + return; + + case HTML_SCRIPT: + HTChunkPutc(&me->script, uc); + return; + + case HTML_OBJECT: + HTChunkPutc(&me->object, uc); + return; + + case HTML_TEXTAREA: + HTChunkPutc(&me->textarea, uc); + return; + + case HTML_SELECT: + case HTML_OPTION: + HTChunkPutc(&me->option, uc); + return; + + case HTML_MATH: + HTChunkPutc(&me->math, uc); + 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, uc); + 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 (me->in_word) { +#ifdef EXP_JAPANESE_SPACES + if (HText_checkLastChar_needSpaceOnJoinLines(me->text)) { +#else + if (HText_getLastChar(me->text) != ' ') { +#endif + 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 */ + } 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) +{ + HTChunk *target = NULL; + +#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: + target = &me->title; + break; + + case HTML_STYLE: + target = &me->style_block; + break; + + case HTML_SCRIPT: + target = &me->script; + 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: + target = &me->object; + break; + + case HTML_TEXTAREA: + target = &me->textarea; + break; + + case HTML_SELECT: + case HTML_OPTION: + target = &me->option; + break; + + case HTML_MATH: + target = &me->math; + 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 (me->in_word) { +#ifdef EXP_JAPANESE_SPACES + if (HText_checkLastChar_needSpaceOnJoinLines(me->text)) +#else + if (HText_getLastChar(me->text) != ' ') +#endif + 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 */ + + if (target != NULL) { + if (target->data == s) { + CTRACE((tfp, "BUG: appending chunk to itself: `%.*s'\n", + target->size, target->data)); + } else { + HTChunkPuts(target, s); + } + } +#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 track_internal_links is true, 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 track_internal_links is false, 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_CHK(flag) (HTLinkType *)((flag) ? HTInternalLink : NULL) +#define INTERN_LT INTERN_CHK(intern_flag) + +#ifdef USE_COLOR_STYLE +static char *Style_className = 0; +static char *Style_className_end = 0; +static size_t 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, + size_t length) +{ + size_t offset = strlen(prefix); + size_t have = (unsigned) (Style_className_end - Style_className); + size_t 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 + +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, + STRING2PTR value, + int isobject, + int imagemap, + const char *id, + const char *src, + int convert, + int 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, /* Address */ + INTERN_CHK(*intern_flag)); /* 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, + STRING2PTR 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 + const char *class_name; + const char *prefix_string; + BOOL class_used = FALSE; +#endif + + 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, + (size_t) 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 = (non_empty(class_name) + ? -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)); + + prefix_string = ""; + if (current_tag_style == -1) { /* Append class_name */ + hcode = color_style_1(HTML_dtd.tags[element_number].name); + if (non_empty(class_name)) { + int ohcode = hcode; + + prefix_string = HTML_dtd.tags[element_number].name; + hcode = color_style_3(prefix_string, ".", class_name); + if (!hashStyles[hcode].used) { /* None such -> classless version */ + hcode = ohcode; + prefix_string = ""; + 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", + prefix_string, class_name, hcode)); + class_used = TRUE; + } + } + + class_string[0] = '\0'; + + } else { /* (current_tag_style!=-1) */ + if (non_empty(class_name)) { + addClassName(".", class_name, strlen(class_name)); + class_string[0] = '\0'; + } + hcode = current_tag_style; + if (hcode >= 0 && hashStyles[hcode].used) { + prefix_string = hashStyles[hcode].name; + } + CTRACE2(TRACE_STYLE, + (tfp, "STYLE.start_element: <%s>, hcode=%d.\n", + HTML_dtd.tags[element_number].name, hcode)); + current_tag_style = -1; + } + + 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 = color_style_3(prefix_string, ".type.", type); + if (!hashStyles[hcode].used) { /* 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)); + } + } + + 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 (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, /* Address */ + (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, /* Address */ + (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; + int hcode2; + + HTSprintf0(&tmp, "link.%s.%s", value[HTML_LINK_CLASS], title); + hcode2 = color_style_1(tmp); + CTRACE2(TRACE_STYLE, + (tfp, "STYLE.link: using style <%s>\n", tmp)); + + HText_characterStyle(me->text, hcode2, 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, hcode2, 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_SECTION: + case HTML_ARTICLE: + case HTML_MAIN: + case HTML_ASIDE: + case HTML_HEADER: + case HTML_FOOTER: + case HTML_NAV: + CHECK_ID(HTML_GEN5_ID); + if (HText_hasToolbar(me->text)) + HText_appendParagraph(me->text); + break; + + case HTML_FIGURE: + CHECK_ID(HTML_GEN5_ID); + 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, /* Address */ + (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, /* Address */ + (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, /* Address */ + (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 conditions are true: + * * We are not collapsing BR's (and either we are not trimming + * blank lines, or the preceding line is non-empty), or + * * The current line has text on it. + * Otherwise, don't do anything. -DH 19980814, TD 19980827/20170704 + */ + if ((LYCollapseBRs == FALSE && + (!LYtrimBlankLines || + !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_2: + 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_2: + 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: + if (me->List_Nesting_Level < 0) { + change_paragraph_style(me, styles[ElementNumber]); + UPDATE_STYLE; + if (me->sp->tag_number == (int) ElementNumber) + LYEnsureDoubleSpace(me); + } else { + LYHandlePlike(me, present, value, include, -1, TRUE); + } + 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, /* Address */ + 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, /* Address */ + 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, /* Address */ + 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, /* Address */ + 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, /* Address */ + 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, /* Address */ + 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, /* Address */ + (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, /* Address */ + 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, /* Address */ + (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, /* Address */ + (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, /* Address */ + (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, /* Address */ + (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, /* Address */ + (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; + BOOL faked_button = FALSE; + + /* 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]; + CTRACE((tfp, "found button for a script\n")); + } + } 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; + /* + * 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"); + faked_button = TRUE; + } + } else if (I.value == 0) { + StrAllocCopy(I.value, "BUTTON"); + } + + if (present && present[HTML_BUTTON_READONLY]) + I.readonly = YES; + + 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 highlighted 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; + } + if (faked_button) + 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, /* Address */ + (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), + (const void *) 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.readonly = 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 highlighted 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); + if (strcasecomp(NonNull(I.type), "submit")) + 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_readonly = NO; + if (present && present[HTML_TEXTAREA_READONLY]) + me->textarea_readonly = YES; + + me->textarea_disabled = NO; + 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, /* Address */ + (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, (STRING2PTR) 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, /* Address */ + 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; + 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 = (int) (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, STACK_OFF); + + FastTrimColorClass(HTML_dtd.tags[element_number].name, + HTML_dtd.tags[element_number].name_len, + Style_className, + &Style_className_end, &hcode); + } +#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 + */ +static int HTML_end_element(HTStructured * me, int element_number, + char **include) +{ + static char empty[1]; + + 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); + + 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)); + } + + /* + * 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> "); + 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, (STRING2PTR) 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_2: + 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_2: + 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, (size_t) 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) { + /* + * Minimum 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.readonly = me->textarea_readonly; + 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 = NULL; /* HTML_put_characters may overwrite this */ + StrAllocCopy(data, cp + 1); + } else { + if (*data != '\0') { + StrAllocCopy(temp, data); + } else { + FREE(temp); + } + data = empty; + } + /* + * 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 = empty; + } + } 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 = empty; + } else { + FREE(temp); + } + } + if (data != empty) { + FREE(data); + } + 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 */ + FastTrimColorClass(HTML_dtd.tags[element_number].name, + HTML_dtd.tags[element_number].name_len, + Style_className, + &Style_className_end, &hcode); + + 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, 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(parent %s, format %s)\n", + ((anchor) + ? NONNULL(anchor->address) + : "<NULL>"), + HTAtom_name(format_out))); + + 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_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->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("", "", (size_t) 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)); + (void) 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; + } + (void) 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)); + (void) 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, int 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) { + if (fwrite(str, (size_t) 1, (size_t) l, me->fp) < (size_t) l + || 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"); + + 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), FALSE)); +} + +/* 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), FALSE)); +} + +/* 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, FALSE)); +} + +/* 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), FALSE)); +} + +HTStream *XHTMLPresent(HTPresentation *pres GCC_UNUSED, + HTParentAnchor *anchor, + HTStream *sink GCC_UNUSED) +{ + CTRACE((tfp, "XHTMLPresent calling CacheThru_new\n")); + return CacheThru_new(anchor, + SGML_new(&HTML_dtd, anchor, + HTML_new(anchor, WWW_PRESENT, NULL), TRUE)); +} +#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(STRING2PTR 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(STRING2PTR 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(STRING2PTR 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 0000000..6e5ebc3 --- /dev/null +++ b/src/HTML.h @@ -0,0 +1,283 @@ +/* + * $LynxId: HTML.h,v 1.36 2022/07/22 20:22:13 tom Exp $ + * + * 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; + int textarea_readonly; + 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; + + const char *comment_start; /* for literate programming */ + const 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 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, int 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 HTStream *XHTMLPresent(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 0000000..5a5f103 --- /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 0000000..35ce390 --- /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 0000000..ee88cf9 --- /dev/null +++ b/src/LYBookmark.c @@ -0,0 +1,1169 @@ +/* + * $LynxId: LYBookmark.c,v 1.88 2023/01/07 16:09:53 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 _WINDOWS +#include <io.h> /* for _chsize() */ +#endif + +#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); + +unsigned LYindex2MBM(int n) +{ + static char MBMcodes[MBM_V_MAXFILES + 2] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + + return n >= 0 && n <= MBM_V_MAXFILES ? UCH(MBMcodes[n]) : UCH('?'); +} + +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 (int) (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; + + (void) 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></li>\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); + +#if defined(_WINDOWS) && !defined(ftruncate) +#define ftruncate(fd, len) _chsize(fd, len) +#endif + +/* + * 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 *Address = NULL; + char *Title = NULL; + int i, c; + bstring *string_data = NULL; + bstring *tmp_data = NULL; + 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 (isEmpty(BookmarkPage)) { + 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: + BStrAlloc(tmp_data, MAX_LINE + 2 * (int) strlen(title)); + TO_EUC((const unsigned char *) title, (unsigned char *) tmp_data->str); + break; + case SJIS: + BStrAlloc(tmp_data, MAX_LINE + (int) strlen(title)); + TO_SJIS((const unsigned char *) title, (unsigned char *) tmp_data->str); + break; + default: + break; + } + BStrCopy0(string_data, tmp_data ? tmp_data->str : title); + } else { + BStrCopy0(string_data, title); + } + LYReduceBlanks(string_data->str); + LYMBM_statusline(TITLE_PROMPT); + LYgetBString(&string_data, FALSE, 0, NORECALL); + if (isBEmpty(string_data)) { + LYMBM_statusline(CANCELLED); + LYSleepMsg(); + FREE(bookmark_URL); + BStrFree(tmp_data); + return; + } + } while (!havevisible(string_data->str)); + + /* + * Create the Title with any left-angle-brackets converted to < entities + * and any ampersands converted to & 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_data->str); + LYEntify(&Title, TRUE); + if (UCSaveBookmarksInUnicode && + have8bit(Title) && (!LYHaveCJKCharacterSet)) { + char *p = title_convert8bit(Title); + + if (p != 0) { + 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); + BStrFree(tmp_data); + return; + } + + /* + * Convert all ampersands in the address to & entities. - FM + */ + StrAllocCopy(Address, address); + LYEntify(&Address, FALSE); + + if (!first_time) { + BOOLEAN empty_file = TRUE; + FILE *bp = tmpfile(); + char *buffer = NULL; + + rewind(fp); + while (LYSafeGets(&buffer, fp)) { + empty_file = FALSE; + if (LYstrstr(buffer, "</ol>")) + break; + fprintf(bp, "%s", buffer); + } + + fflush(bp); + rewind(bp); + + rewind(fp); + ftruncate(fileno(fp), 0); + + while (LYSafeGets(&buffer, bp)) { + fprintf(fp, "%s", buffer); + } + fclose(bp); + + if (empty_file) + first_time = TRUE; + } + + /* + * If we created a new bookmark file, write the headers. - FM + * Once and forever... + */ + if (first_time) { + fprintf(fp, "%s\n", LYNX_DOCTYPE); + fprintf(fp, "<html>\n"); + 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=\"" STR_HTML ";charset=iso-2022-jp\""); +#else + LYAddMETAcharsetToFD(fp, -1); +#endif /* !_WINDOWS */ + fprintf(fp, "<title>%s</title>\n</head>\n", BOOKMARK_TITLE); + fprintf(fp, "<body>\n"); +#ifdef _WINDOWS + fprintf(fp, "<p>%s", + gettext(" You can delete links by the 'R' key<br>\n<ol>\n")); +#else + fprintf(fp, "<p>%s<br>\n%s\n\n<!--\n%s\n--></p>\n\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></li>\n", Address, Title); + fprintf(fp, "</ol></body></html>\n"); + } + 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. + */ + BStrFree(string_data); + BStrFree(tmp_data); + 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; + 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); + (void) chmod(newfile, HIDE_CHMOD); + 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) { + (void) 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 { + (void) 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(UCH(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 (non_empty(MBM_A_subbookmark[d])) + 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 if (charset_out >= 0) { + long unicode; + char replace_buf[32]; + + if (UCTransCharStr(replace_buf, (int) 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 == NULL + || 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 0000000..a9eb494 --- /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 unsigned 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 0000000..72493b2 --- /dev/null +++ b/src/LYCgi.c @@ -0,0 +1,757 @@ +/* + * $LynxId: LYCgi.c,v 1.72 2018/03/18 18:56:05 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> + +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"); + } + } + + env[envc++] = DeConst(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) +{ + (void) arg; + (void) anAnchor; + (void) format_out; + (void) 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 (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 + + fd1[0] = -1; + fd1[1] = -1; + fd2[0] = -1; + fd2[1] = -1; + + 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 == 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, */ + ssize_t chars; + off_t total_chars; + + close(fd2[1]); + + if (anAnchor->post_data) { + ssize_t written; + int 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, + (size_t) 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", + (int) written)); + total_written += (int) written; + remaining -= (int) written; + if (remaining == 0) + break; + } + if (remaining != 0) { + CTRACE((tfp, "LYNXCGI: %d bytes remain unwritten!\n", + remaining)); + } + close(fd1[1]); + } + + HTReadProgress(total_chars = 0, (off_t) 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; + } + total_chars += (int) chars; + HTReadProgress(total_chars, (off_t) 0); + CTRACE((tfp, "LYNXCGI: Rx: %.*s\n", (int) chars, buf)); + (*target->isa->put_block) (target, buf, (int) 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; + } + +#ifndef 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"); + } + + 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 (non_empty(LYCgiDocumentRoot)) { + /* 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 (non_empty(LYCgiDocumentRoot) && 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: " STR_PLAINTEXT "\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 0000000..6b90f2d --- /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 0000000..94b7a04 --- /dev/null +++ b/src/LYCharSets.c @@ -0,0 +1,1157 @@ +/* + * $LynxId: LYCharSets.c,v 1.71 2021/06/29 22:01:12 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 initialized later in LYMain.c */ +int linedrawing_char_set = -1; +STRING2PTR 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) (Æ) - AElig */ + "\301", /* capital A, acute accent (Á) - Aacute */ + "\302", /* capital A, circumflex accent (Â) - Acirc */ + "\300", /* capital A, grave accent (À) - Agrave */ + "\305", /* capital A, ring - Aring (Å) */ + "\303", /* capital A, tilde - Atilde (Ã) */ + "\304", /* capital A, dieresis or umlaut mark (Ä) - Auml */ + "\307", /* capital C, cedilla - Ccedil (Ç) */ + "\320", /* capital Eth or D with stroke (Ð) - Dstrok */ + "\320", /* capital Eth, Icelandic (Ð) - ETH */ + "\311", /* capital E, acute accent (É) - Eacute */ + "\312", /* capital E, circumflex accent (Ê) - Ecirc */ + "\310", /* capital E, grave accent (È) - Egrave */ + "\313", /* capital E, dieresis or umlaut mark (Ë) - Euml */ + "\315", /* capital I, acute accent (Í) - Iacute */ + "\316", /* capital I, circumflex accent (Î) - Icirc */ + "\314", /* capital I, grave accent (Ì) - Igrave */ + "\317", /* capital I, dieresis or umlaut mark (Ï) - Iuml */ + "\321", /* capital N, tilde (Ñ) - Ntilde */ + "\323", /* capital O, acute accent (Ó) - Oacute */ + "\324", /* capital O, circumflex accent (Ô) - Ocirc */ + "\322", /* capital O, grave accent (Ò) - Ograve */ + "\330", /* capital O, slash (Ø) - Oslash */ + "\325", /* capital O, tilde (Õ) - Otilde */ + "\326", /* capital O, dieresis or umlaut mark (Ö) - Ouml */ + "\336", /* capital THORN, Icelandic (Þ) - THORN */ + "\332", /* capital U, acute accent (Ú) - Uacute */ + "\333", /* capital U, circumflex accent (Û) - Ucirc */ + "\331", /* capital U, grave accent (Ù) - Ugrave */ + "\334", /* capital U, dieresis or umlaut mark (Ü) - Uuml */ + "\335", /* capital Y, acute accent (Ý) - Yacute */ + "\341", /* small a, acute accent (á) - aacute */ + "\342", /* small a, circumflex accent (â) - acirc */ + "\264", /* spacing acute (´) - acute */ + "\346", /* small ae diphthong (ligature) (æ) - aelig */ + "\340", /* small a, grave accent (à) - agrave */ + "\046", /* ampersand (&) - amp */ + "\345", /* small a, ring (å) - aring */ + "\343", /* small a, tilde (ã) - atilde */ + "\344", /* small a, dieresis or umlaut mark (ä) - auml */ + "\246", /* broken vertical bar (¦) - brkbar */ + "\246", /* broken vertical bar (¦) - brvbar */ + "\347", /* small c, cedilla (ç) - ccedil */ + "\270", /* spacing cedilla (¸) - cedil */ + "\242", /* cent sign (¢) - cent */ + "\251", /* copyright sign (©) - copy */ + "\244", /* currency sign (¤) - curren */ + "\260", /* degree sign (°) - deg */ + "\250", /* spacing dieresis (¨) - die */ + "\367", /* division sign (÷) - divide */ + "\351", /* small e, acute accent (é) - eacute */ + "\352", /* small e, circumflex accent (ê) - ecirc */ + "\350", /* small e, grave accent (è) - 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 (ð) - eth */ + "\353", /* small e, dieresis or umlaut mark (ë) - euml */ + "\275", /* fraction 1/2 (½) - frac12 */ + "\274", /* fraction 1/4 (¼) - frac14 */ + "\276", /* fraction 3/4 (¾) - frac34 */ + "\076", /* greater than (>) - gt */ + "\257", /* spacing macron (¯) - hibar */ + "\355", /* small i, acute accent (í) - iacute */ + "\356", /* small i, circumflex accent (î) - icirc */ + "\241", /* inverted exclamation mark (¡) - iexcl */ + "\354", /* small i, grave accent (ì) - igrave */ + "\277", /* inverted question mark (¿) - iquest */ + "\357", /* small i, dieresis or umlaut mark (ï) - iuml */ + "\253", /* angle quotation mark, left («) - laquo */ + "\074", /* less than (<) - lt */ + "\257", /* spacing macron (¯) - macr */ + "-", /* dash the width of emsp - mdash */ + "\265", /* micro sign (µ) - micro */ + "\267", /* middle dot (·) - middot */ + "\001", /* nbsp non-breaking space NEVER CHANGE THIS - nbsp */ + "-", /* dash the width of ensp - ndash */ + "\254", /* negation sign (¬) - not */ + "\361", /* small n, tilde (ñ) - ntilde */ + "\363", /* small o, acute accent (ó) - oacute */ + "\364", /* small o, circumflex accent (ô) - ocirc */ + "\362", /* small o, grave accent (ò) - ograve */ + "\252", /* feminine ordinal indicator (ª) - ordf */ + "\272", /* masculine ordinal indicator (º) - ordm */ + "\370", /* small o, slash (ø) - oslash */ + "\365", /* small o, tilde (õ) - otilde */ + "\366", /* small o, dieresis or umlaut mark (ö) - ouml */ + "\266", /* paragraph sign (¶) - para */ + "\261", /* plus-or-minus sign (±) - plusmn */ + "\243", /* pound sign (£) - pound */ + "\042", /* quote '"' (") - quot */ + "\273", /* angle quotation mark, right (») - raquo */ + "\256", /* circled R registered sign (®) - reg */ + "\247", /* section sign (§) - sect */ + "\007", /* soft hyphen (­) NEVER CHANGE THIS - shy */ + "\271", /* superscript 1 (¹) - sup1 */ + "\262", /* superscript 2 (²) - sup2 */ + "\263", /* superscript 3 (³) - sup3 */ + "\337", /* small sharp s, German (sz ligature) (ß) - szlig */ + "\002", /* thin space - not collapsed NEVER CHANGE THIS - thinsp */ + "\376", /* small thorn, Icelandic (þ) - thorn */ + "\327", /* multiplication sign (×) - times */ + "(TM)", /* circled TM trade mark sign (™) - trade */ + "\372", /* small u, acute accent (ú) - uacute */ + "\373", /* small u, circumflex accent (û) - ucirc */ + "\371", /* small u, grave accent (ù) - ugrave */ + "\250", /* spacing dieresis (¨) - uml */ + "\374", /* small u, dieresis or umlaut mark (ü) - uuml */ + "\375", /* small y, acute accent (ý) - yacute */ + "\245", /* yen sign (¥) - yen */ + "\377", /* small y, dieresis or umlaut mark (ÿ) - 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) (Æ) - AElig */ + "A", /* capital A, acute accent (Á) - Aacute */ + "A", /* capital A, circumflex accent (Â) - Acirc */ + "A", /* capital A, grave accent (À) - Agrave */ + "A", /* capital A, ring - Aring (Å) */ + "A", /* capital A, tilde - Atilde (Ã) */ +#ifdef LY_UMLAUT + "Ae", /* capital A, dieresis or umlaut mark (Ä) - Auml */ +#else + "A", /* capital A, dieresis or umlaut mark (Ä) - Auml */ +#endif /* LY_UMLAUT */ + "C", /* capital C, cedilla (Ç) - Ccedil */ + "Dj", /* capital D with stroke (Ð) - Dstrok */ + "DH", /* capital Eth, Icelandic (Ð) - ETH */ + "E", /* capital E, acute accent (É) - Eacute */ + "E", /* capital E, circumflex accent (Ê) - Ecirc */ + "E", /* capital E, grave accent (È) - Egrave */ + "E", /* capital E, dieresis or umlaut mark (Ë) - Euml */ + "I", /* capital I, acute accent (Í) - Iacute */ + "I", /* capital I, circumflex accent (Î) - Icirc */ + "I", /* capital I, grave accent (Ì) - Igrave */ + "I", /* capital I, dieresis or umlaut mark (Ï) - Iuml */ + "N", /* capital N, tilde - Ntilde (Ñ) */ + "O", /* capital O, acute accent (Ó) - Oacute */ + "O", /* capital O, circumflex accent (Ô) - Ocirc */ + "O", /* capital O, grave accent (Ò) - Ograve */ + "O", /* capital O, slash (Ø) - Oslash */ + "O", /* capital O, tilde (Õ) - Otilde */ +#ifdef LY_UMLAUT + "Oe", /* capital O, dieresis or umlaut mark (Ö) - Ouml */ +#else + "O", /* capital O, dieresis or umlaut mark (Ö) - Ouml */ +#endif /* LY_UMLAUT */ + "P", /* capital THORN, Icelandic (Þ) - THORN */ + "U", /* capital U, acute accent (Ú) - Uacute */ + "U", /* capital U, circumflex accent (Û) - Ucirc */ + "U", /* capital U, grave accent (Ù) - Ugrave */ +#ifdef LY_UMLAUT + "Ue", /* capital U, dieresis or umlaut mark (Ü) - Uuml */ +#else + "U", /* capital U, dieresis or umlaut mark (Ü) - Uuml */ +#endif /* LY_UMLAUT */ + "Y", /* capital Y, acute accent (Ý) - Yacute */ + "a", /* small a, acute accent (á) - aacute */ + "a", /* small a, circumflex accent (â) - acirc */ + "'", /* spacing acute (´) - acute */ + "ae", /* small ae diphthong (ligature) (æ) - aelig */ + "`a", /* small a, grave accent (è) - agrave */ + "&", /* ampersand (&) - amp */ + "a", /* small a, ring (å) - aring */ + "a", /* small a, tilde (ã) - atilde */ +#ifdef LY_UMLAUT + "ae", /* small a, dieresis or umlaut mark (ä) - auml */ +#else + "a", /* small a, dieresis or umlaut mark (ä) - auml */ +#endif /* LY_UMLAUT */ + "|", /* broken vertical bar (¦) - brkbar */ + "|", /* broken vertical bar (¦) - brvbar */ + "c", /* small c, cedilla (ç) - ccedil */ + ",", /* spacing cedilla (¸) - cedil */ + "-c-", /* cent sign (¢) - cent */ + "(c)", /* copyright sign (©) - copy */ + "CUR", /* currency sign (¤) - curren */ + "DEG", /* degree sign (°) - deg */ + "\042", /* spacing dieresis (¨) - die */ + "/", /* division sign (÷) - divide */ + "e", /* small e, acute accent (é) - eacute */ + "e", /* small e, circumflex accent (ê) - ecirc */ + "e", /* small e, grave accent (è) - 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 (ð) */ + "e", /* small e, dieresis or umlaut mark (ë) - euml */ + " 1/2", /* fraction 1/2 (½) - frac12 */ + " 1/4", /* fraction 1/4 (¼) - frac14 */ + " 3/4", /* fraction 3/4 (¾) - frac34 */ + ">", /* greater than (>) - gt */ + "-", /* spacing macron (¯) - hibar */ + "i", /* small i, acute accent (í) - iacute */ + "i", /* small i, circumflex accent (î) - icirc */ + "!", /* inverted exclamation mark (¡) - iexcl */ + "`i", /* small i, grave accent (ì) - igrave */ + "?", /* inverted question mark (¿) - iquest */ + "i", /* small i, dieresis or umlaut mark (ï) - iuml */ + "<<", /* angle quotation mark, left («) - laquo */ + "<", /* less than - lt (<) */ + "-", /* spacing macron (¯) - macr */ + "-", /* dash the width of emsp - mdash */ + "u", /* micro sign (µ) - micro */ + ".", /* middle dot (·) - middot */ + "\001", /* nbsp non-breaking space NEVER CHANGE THIS - nbsp */ + "-", /* dash the width of ensp - ndash */ + "NOT", /* negation sign (¬) - not */ + "n", /* small n, tilde (ñ) - ntilde */ + "o", /* small o, acute accent (ó) - oacute */ + "o", /* small o, circumflex accent (ô) - ocirc */ + "o", /* small o, grave accent (ò) - ograve */ + "-a", /* feminine ordinal indicator (ª) - ordf */ + "-o", /* masculine ordinal indicator (º) - ordm */ + "o", /* small o, slash (ø) - oslash */ + "o", /* small o, tilde (õ) - otilde */ +#ifdef LY_UMLAUT + "oe", /* small o, dieresis or umlaut mark (ö) - ouml */ +#else + "o", /* small o, dieresis or umlaut mark (ö) - ouml */ +#endif /* LY_UMLAUT */ + "P:", /* paragraph sign (¶) - para */ + "+-", /* plus-or-minus sign (±) - plusmn */ + "-L-", /* pound sign (£) - pound */ + "\"", /* quote '"' (") - quot */ + ">>", /* angle quotation mark, right (») - raquo */ + "(R)", /* circled R registered sign (®) - reg */ + "S:", /* section sign (§) - sect */ + "\007", /* soft hyphen (­) NEVER CHANGE THIS - shy */ + "^1", /* superscript 1 (¹) - sup1 */ + "^2", /* superscript 2 (²) - sup2 */ + "^3", /* superscript 3 (³) - sup3 */ + "ss", /* small sharp s, German (sz ligature) (ß) - szlig */ + "\002", /* thin space - not collapsed NEVER CHANGE THIS - thinsp */ + "p", /* small thorn, Icelandic (þ) - thorn */ + "*", /* multiplication sign (×) - times */ + "(TM)", /* circled TM trade mark sign (™) - trade */ + "u", /* small u, acute accent (ú) - uacute */ + "u", /* small u, circumflex accent (û) - ucirc */ + "u", /* small u, grave accent (ù) - ugrave */ + "\042", /* spacing dieresis (¨) - uml */ +#ifdef LY_UMLAUT + "ue", /* small u, dieresis or umlaut mark (ü) - uuml */ +#else + "u", /* small u, dieresis or umlaut mark (ü) - uuml */ +#endif /* LY_UMLAUT */ + "y", /* small y, acute accent (ý) - yacute */ + "YEN", /* yen sign (¥) - yen */ + "y", /* small y, dieresis or umlaut mark (ÿ) - 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 + */ +STRING2PTR 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(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 USE_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, + int modeflag) +{ + if (LYCharSet_UC[i].enc != UCT_ENC_CJK) { + + int chndl = safeUCGetLYhndl_byMIME(UCAssume_MIMEcharset); + + if (i == chndl) + LYUseDefaultRawMode = (BOOLEAN) modeflag; + else + LYUseDefaultRawMode = (BOOL) (!modeflag); + } else /* CJK encoding: */ + LYUseDefaultRawMode = (BOOLEAN) 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; + + if (value == NULL) + return -1; + + LYTrimTrailing(value); + 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 ™ 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 = UCS_REPL; + 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 0000000..c0d1553 --- /dev/null +++ b/src/LYCharSets.h @@ -0,0 +1,154 @@ +/* + * $LynxId: LYCharSets.h,v 1.34 2012/02/10 18:43:40 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 STRING2PTR LYCharSets[]; + extern const char *SevenBitApproximations[]; + extern STRING2PTR 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, int 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 0000000..0013989 --- /dev/null +++ b/src/LYCharUtils.c @@ -0,0 +1,3419 @@ +/* + * $LynxId: LYCharUtils.c,v 1.137 2021/10/24 00:47:08 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 */ + +static size_t count_char(const char *value, int ch) +{ + const char *found; + size_t result = 0; + + while ((*value != '\0') && (found = StrChr(value, ch)) != NULL) { + ++result; + value = (found + 1); + } + return result; +} + +/* + * This function converts any ampersands in a pre-allocated string to "&". + * If brackets is TRUE, it also converts any angle-brackets to "<" or ">". + */ +void LYEntify(char **in_out, + int brackets) +{ + char *source = *in_out; + char *target; + char *result = NULL; + size_t count_AMPs = 0; + size_t count_LTs = 0; + size_t count_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 (non_empty(source)) { + count_AMPs = count_char(*in_out, '&'); + if (brackets) { + count_LTs = count_char(*in_out, '<'); + count_GTs = count_char(*in_out, '>'); + } + + if (count_AMPs != 0 || count_LTs != 0 || count_GTs != 0) { + + target = typecallocn(char, + (strlen(*in_out) + + (4 * count_AMPs) + + (3 * count_LTs) + + (3 * count_GTs) + 1)); + + if ((result = target) == NULL) + outofmem(__FILE__, "LYEntify"); + + for (source = *in_out; *source; source++) { +#ifdef CJK_EX + if (IS_CJK_TTY) { + switch (state) { + case S_text: + if (*source == '\033') { + state = S_esc; + *target++ = *source; + continue; + } + break; + + case S_esc: + if (*source == '$') { + state = S_dollar; + } else if (*source == '(') { + state = S_paren; + } else { + state = S_text; + } + *target++ = *source; + continue; + + case S_dollar: + if (*source == '@' || *source == 'B' || *source == 'A') { + state = S_nonascii_text; + } else if (*source == '(') { + state = S_dollar_paren; + } else { + state = S_text; + } + *target++ = *source; + continue; + + case S_dollar_paren: + if (*source == 'C') { + state = S_nonascii_text; + } else { + state = S_text; + } + *target++ = *source; + continue; + + case S_paren: + if (*source == 'B' || *source == 'J' || *source == 'T') { + state = S_text; + } else if (*source == 'I') { + state = S_nonascii_text; + } else if (*source == '\033') { + state = S_esc; + } + *target++ = *source; + continue; + + case S_nonascii_text: + if (*source == '\033') + state = S_esc; + *target++ = *source; + continue; + + default: + break; + } + if (*(source + 1) != '\0' && + (IS_EUC(UCH(*source), UCH(*(source + 1))) || + IS_SJIS(UCH(*source), UCH(*(source + 1)), in_sjis) || + IS_BIG5(UCH(*source), UCH(*(source + 1))))) { + *target++ = *source++; + *target++ = *source; + continue; + } + } +#endif + switch (*source) { + case '&': + *target++ = '&'; + *target++ = 'a'; + *target++ = 'm'; + *target++ = 'p'; + *target++ = ';'; + break; + case '<': + if (brackets) { + *target++ = '&'; + *target++ = 'l'; + *target++ = 't'; + *target++ = ';'; + break; + } + /* FALLTHRU */ + case '>': + if (brackets) { + *target++ = '&'; + *target++ = 'g'; + *target++ = 't'; + *target++ = ';'; + break; + } + /* FALLTHRU */ + default: + *target++ = *source; + break; + } + } + *target = '\0'; + FREE(*in_out); + *in_out = result; + } + } +} + +/* + * 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; +} + +void LYAddMETAcharsetToStream(HTStream *target, int disp_chndl) +{ + char *buf = 0; + + if (disp_chndl == -1) + /* + * -1 means use current_char_set. + */ + disp_chndl = current_char_set; + + if (target != 0 && disp_chndl >= 0) { + HTSprintf0(&buf, "<META %s content=\"" STR_HTML ";charset=%s\">\n", + "http-equiv=\"content-type\"", + LYCharSet_UC[disp_chndl].MIMEname); + (*target->isa->put_string) (target, buf); + FREE(buf); + } +} + +/* + * 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=\"" STR_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; + } + + 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; + } + + 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; + } + + 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; + } + + 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, + int do_ent, + int use_lynx_specials, + int plain_space, + int hidden, + int 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; + 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 = 0; + 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'; + + unsigned char sjis_str[3]; +#endif + + /* + * Make sure we have a non-empty string. - FM + */ + if (isEmpty(*str)) + return str; + + if (cs_from < 0 || cs_to < 0) { + CTRACE((tfp, "BUG: LYUCFullyTranslateString from=%d, to=%d\n", + cs_from, cs_to)); + 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 USE_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, (int) (q - qs)); \ + HTChunkPuts(CHUNK, s); \ + qs = q = *str + +#define REPLACE_CHAR(c) if (q > p) { \ + HTChunkPutb(CHUNK, qs, (int) (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 { + if (conv_jisx0201kana && 0xA1 <= code && code <= 0xDF) { + sjis_str[2] = '\0'; + JISx0201TO0208_SJIS(UCH(code), + sjis_str, sjis_str + 1); + REPLACE_STRING(sjis_str); + p++; + continue; + } + } + } +#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 USE_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) { + const char *pq = p; + + puni = p; + code = UCGetUniFromUtf8String(&pq); + if (code <= 0) { + code = UCH(*p); + } else { + what = P_utf8; + puni += (pq - (const char *) p); + } + } + } 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 (UCScanCode(&code, cp, (BOOL) (what == P_hex))) { + code = LYcp1252ToUnicode(code); + state = S_check_uni; + } else { + state = S_recover; + break; + } + 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 '%" + PRI_UCode_t "'.\n", CAST_UCode_t (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 + } + /* FALLTHRU */ + + 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%.2" PRI_UCode_t "", CAST_UCode_t (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, (int) (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, + int use_lynx_specials, + int plain_space, + int 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, + int 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, (size_t) (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, + STRING2PTR value, + char **include GCC_UNUSED) +{ + char *http_equiv = NULL, *name = NULL, *content = NULL, *charset = 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); + } + } + if (present[HTML_META_CHARSET] && + non_empty(value[HTML_META_CHARSET])) { + StrAllocCopy(charset, value[HTML_META_CHARSET]); + convert_to_spaces(charset, TRUE); + LYUCTranslateHTMLString(&charset, me->tag_charset, me->tag_charset, + NO, NO, YES, st_other); + if (*charset == '\0') { + FREE(charset); + } + } + CTRACE((tfp, + "LYHandleMETA: HTTP-EQUIV=\"%s\" NAME=\"%s\" CONTENT=\"%s\" CHARSET=\"%s\"\n", + NONNULL(http_equiv), + NONNULL(name), + NONNULL(content), + NONNULL(charset))); + + /* + * 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 + */ + if (isEmpty(me->node_anchor->charset) && + (charset || + (!strcasecomp(NonNull(http_equiv), "Content-Type") && content))) { + LYUCcharset *p_in = NULL; + LYUCcharset *p_out = NULL; + + if (charset) { + LYLowerCase(charset); + } else { + LYUCTranslateHTMLString(&content, me->tag_charset, me->tag_charset, + NO, NO, YES, st_other); + LYLowerCase(content); + } + + if ((cp1 = charset) != NULL || + (cp1 = strstr(content, "charset")) != NULL) { + BOOL chartrans_ok = NO; + char *cp3 = NULL, *cp4; + int chndl; + + if (!charset) + 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 priority! */ + HTAnchor_setUCInfoStage(me->node_anchor, + current_char_set, + UCT_STAGE_STRUCTURED, + UCT_SETBY_MIME); /* highest priority! */ + 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 USE_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); + } + + /* + * 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 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; + /* id_string seems to be used wrong below if given. + not that it matters much. avoid setting it here. - kw */ + if (track_internal_links && + (StrNCmp(href, "http", 4) == 0) && + (cp = StrChr(href, '#')) != NULL) { + StrAllocCopy(id_string, cp); + *cp = '\0'; + } + 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, /* Address */ + (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); + FREE(charset); +} + +/* + * 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, + STRING2PTR value, + char **include GCC_UNUSED, + int align_idx, + int start) +{ + /* + * FIG content should be a true block, which like P inherits the current + * style. APPLET is like character elements or an ALT attribute, unless + * its 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 && align_idx >= 0) { + 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 its + * 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, + STRING2PTR value, + char **include GCC_UNUSED, + int 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, + int force_slash, + int 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) { + int n; + + for (n = 0; (p[n] = pound[n]) != '\0'; ++n) ; + } + } + } + } + } + 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, + STRING2PTR 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, /* Address */ + (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, /* Address */ + (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 < entities + * and any ampersands converted to & 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"); + + 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 0000000..2b9ca88 --- /dev/null +++ b/src/LYCharUtils.h @@ -0,0 +1,109 @@ +/* + * $LynxId: LYCharUtils.h,v 1.28 2012/02/10 18:36:39 tom Exp $ + */ +#ifndef LYCHARUTILS_H +#define LYCHARUTILS_H + +#ifndef HTUTILS_H +#include <HTUtils.h> +#endif /* HTUTILS_H */ + +#ifndef HTSTREAM_H +#include <HTStream.h> +#endif /* HTSTREAM_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, + int do_ent, + int use_lynx_specials, + int plain_space, + int hidden, + int Back, + CharUtil_st stype); + extern BOOL LYUCTranslateHTMLString(char **str, + int cs_from, + int cs_to, + int use_lynx_specials, + int plain_space, + int hidden, + CharUtil_st stype); + extern BOOL LYUCTranslateBackFormData(char **str, + int cs_from, + int cs_to, + int plain_space); + extern void LYEntify(char **str, + int 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 LYAddMETAcharsetToStream(HTStream *target, + 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, + STRING2PTR value, + char **include); + extern void LYHandlePlike(HTStructured * me, const BOOL *present, + STRING2PTR value, + char **include, + int align_idx, + int start); + extern void LYHandleSELECT(HTStructured * me, const BOOL *present, + STRING2PTR value, + char **include, + int start); + extern int LYLegitimizeHREF(HTStructured * me, char **href, + int force_slash, + int strip_dots); + extern void LYCheckForContentBase(HTStructured * me); + extern void LYCheckForID(HTStructured * me, const BOOL *present, + STRING2PTR 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 0000000..43fac15 --- /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 0000000..3679d87 --- /dev/null +++ b/src/LYClean.c @@ -0,0 +1,203 @@ +/* $LynxId: LYClean.c,v 1.40 2013/10/10 23:47:25 tom 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) { + + /* + * Reassert the AST. + */ + (void) signal(SIGINT, cleanup_sig); + if (LYCursesON) { + lynx_force_repaint(); /* wipe away the "cancel" message */ + LYrefresh(); + + /* + * Ask if exit is intended. + */ + if (LYQuitDefaultYes == TRUE) { + int Dft = ((LYQuitDefaultYes == TRUE) ? YES : NO); + int c = HTConfirmDefault(REALLY_EXIT, Dft); + + HadVMSInterrupt = TRUE; + if (c != Dft) { + return; + } + } + } else { + return; + } + } +#endif /* VMS */ + + /* + * Ignore signals from terminal. + */ +#ifndef NOSIGHUP + (void) signal(SIGHUP, SIG_IGN); +#endif /* NOSIGHUP */ + +#ifdef VMS + /* + * Use ttclose() from cleanup() for VMS if not dumping. + */ + if (dump_output_immediately) + (void) signal(SIGTERM, SIG_IGN); +#else /* Unix: */ + (void) signal(SIGINT, SIG_IGN); + (void) signal(SIGTERM, SIG_IGN); +#endif /* VMS */ + + 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 + /* + * It is useful to save the session if a user closed lynx in a + * nonstandard way, such as closing xterm window or in even a 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, this erases the temporary files + * that lynx created. + */ +void cleanup_files(void) +{ + LYCleanupTemp(); + FREE(lynx_temp_space); +} + +void cleanup(void) +{ + /* + * Ignore signals from terminal. + */ +#ifndef NOSIGHUP + (void) signal(SIGHUP, SIG_IGN); +#endif /* NOSIGHUP */ +#ifndef VMS /* use ttclose() from cleanup() for VMS */ + (void) signal(SIGINT, SIG_IGN); +#endif /* !VMS */ + (void) signal(SIGTERM, SIG_IGN); + + if (LYCursesON) { + LYParkCursor(); + 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 + 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 0000000..d7f54be --- /dev/null +++ b/src/LYClean.h @@ -0,0 +1,25 @@ +#ifndef LYCLEAN_H +#define LYCLEAN_H + +#ifndef HTUTILS_H +#include <HTUtils.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif +#ifdef VMS + extern BOOLEAN DidCleanup; + 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 0000000..6755829 --- /dev/null +++ b/src/LYCookie.c @@ -0,0 +1,2898 @@ +/* + * $LynxId: LYCookie.c,v 1.147 2019/01/26 00:50:13 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 + * + * Modified to follow RFC-6265 regarding leading dot of Domain, and + * matching of hostname vs domain (2011/06/10 -TD) + * + * Modified to address differences between RFC-6262 versus RFC-2109 and + * RFC-2965 by making the older behavior optional (2019/01/25 -TD) + * + * FM's 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 the number of cookies from a domain, limiting the + total number of cookies, and limiting the number of bytes from a + processed cookie, 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) + +#define LeadingDot(s) ((s)[0] == '.') +#define SkipLeadingDot(s) (LeadingDot(s) ? ((s) + 1) : (s)) + +#define AssumeCookieVersion(p) \ + if (USE_RFC_2965 && (p)->version < 1) { \ + (p)->version = 1; \ + } + +/* + * 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 */ + char *ddomain; /* Domain without leading "." */ + 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 */ + +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"); + + 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->ddomain); + FREE(co->path); + FREE(co->PortList); + FREE(co); + } +} + +static void freeCookies(domain_entry * de) +{ + FREE(de->domain); + FREE(de->ddomain); + HTList_delete(de->cookie_list); + de->cookie_list = NULL; +} + +#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", NonNull(de->ddomain))); + cl = de->cookie_list; + while (cl) { + next = cl->next; + co = cl->object; + if (co) { + HTList_removeObject(de->cookie_list, co); + freeCookie(co); + } + cl = next; + } + freeCookies(de); + FREE(dl->object); + } + dl = dl->next; + } + cookie_list = NULL; + HTList_delete(domain_list); + domain_list = NULL; +} +#endif /* LY_FIND_LEAKS */ + +/* + * RFC 2109 - + * 4.2.2 Set-Cookie Syntax + * An explicitly specified domain must always start with a dot. + * 4.3.2 Rejecting Cookies + * ...rejects a cookie (shall not store its information) if any of the + * following is true: + * ... + * The value for the Domain attribute contains no embedded dots or does not + * start with a dot. + * + * RFC 2965 - + * 3.2.2 Set-Cookie2 Syntax + * Domain=value + * OPTIONAL. The value of the Domain attribute specifies the domain + * for which the cookie is valid. If an explicitly specified value + * does not start with a dot, the user agent supplies a leading dot. + */ +static BOOLEAN has_embedded_dot(const char *value) +{ + BOOLEAN leading = NO; + BOOLEAN embedded = NO; + const char *p; + + for (p = value; *p != '\0'; ++p) { + if (*p == '.') { + if (p == value) { + leading = YES; + } else if (p[1] != '\0') { + embedded = YES; + } else { + embedded = NO; + } + } + } + return (leading || USE_RFC_2965) && embedded; +} + +/* + * RFC 6265 - + * 4.1.2.3. The Domain Attribute + * (Note that a leading %x2E ("."), if present, is ignored even though that + * character is not permitted, but a trailing %x2E ("."), if present, will + * cause the user agent to ignore the attribute.) + */ +static BOOLEAN has_trailing_dot(const char *value) +{ + BOOLEAN result = NO; + const char *p; + + for (p = value; *p != '\0'; ++p) { + if (*p == '.') { + if (p[1] == '\0') { + result = YES; + break; + } + } + } + return result; +} + +/* + * Compare a string against a domain as specified in RFC-6265 Section 5.1.3 + */ +static BOOLEAN domain_matches(const char *value, + const char *domain) +{ + BOOLEAN result = NO; + + if (isEmpty(value)) { + CTrace((tfp, "BUG: comparing empty value in domain_matches\n")); + } else if (isEmpty(domain)) { + CTrace((tfp, "BUG: comparing empty domain in domain_matches\n")); + } else { + if (!strcasecomp(value, domain)) { + result = YES; + } else { + int value_len = (int) strlen(value); + int suffix_len = (int) strlen(domain); + int offset = value_len - suffix_len; + + if (offset > 1 + && value[offset - 1] == '.' + && !strcasecomp(value + offset, domain)) { + result = YES; + } + } + } + return result; +} + +/* + * 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; + const char *find; + + if (name != 0 + && *(find = SkipLeadingDot(name)) != '\0') { + for (hl = domain_list; hl != NULL; hl = hl->next) { + de = (domain_entry *) hl->object; + if (de != NULL && de->domain != NULL && de->ddomain != NULL) { + CTrace((tfp, + "...test_domain_entry(%s) ->(%s) bv:%u, invcheck_bv:%u\n", + find, + NonNull(de->ddomain), + de->bv, + de->invcheck_bv)); + if (!strcasecomp(find, de->ddomain)) { + 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; +} + +static void SetCookieDomain(cookie * co, const char *domain) +{ + StrAllocCopy(co->ddomain, SkipLeadingDot(domain)); + CTrace((tfp, "SetCookieDomain(%s)\n", co->ddomain)); +} + +/* + * 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. + * + * RFC 2109 - + * 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. + * + * RFC 6265 - + * Section 4.1.2.4 describes Path, but omits any mention of the user agent + * rejecting a cookie because of Path. Instead, it deals only with the + * cases where a cookie returned by the user agent would be valid, based on + * Path. In section 8.6, RFC 6265 presents an example which would not have + * been valid with RFC 2109 to claim that the Path attribute is unreliable + * from the standpoint of the user agent. + * + * RFC 6265 does not go into any detail regarding its differences from the + * older RFCs 2109 / 2965. The relevant text covering all of these changes + * is just this (no case studies are cited): + * User agents MUST implement the more liberal processing rules defined in + * Section 5, in order to maximize interoperability with existing servers + * that do not conform to the well-behaved profile defined in Section 4. + */ + if (!USE_RFC_6265 && !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: + /* will prompt later if we get that far */ + invprompt_reasons |= FAILS_COND1; + break; + + 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; + } + + /* + * RFC 2109 - + * 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 + * + * RFC 6265 - + * If the first character of the attribute-value string is %x2E ("."): + * + * Let cookie-domain be the attribute-value without the leading %x2E + * (".") character. + * + * Otherwise: + * + * Let cookie-domain be the entire attribute-value. + * + * Convert the cookie-domain to lower case. + */ + SetCookieDomain(co, co->domain); + if (isEmpty(co->ddomain)) { + CTrace((tfp, "store_cookie: Rejecting domain '%s'.\n", co->ddomain)); + freeCookie(co); + return; + } + if (!USE_RFC_6265) { + if (!has_embedded_dot(co->ddomain)) { + CTrace((tfp, "store_cookie: Rejecting domain '%s'.\n", co->ddomain)); + freeCookie(co); + return; + } + } else { + if (has_trailing_dot(co->ddomain)) { + CTrace((tfp, "store_cookie: Rejecting domain '%s'.\n", co->ddomain)); + freeCookie(co); + return; + } + } + + /* + * RFC 2109 - + * Section 4.3.2, condition 3: The value for the request-host does not + * domain-match the Domain attribute. + * + * RFC 6265 - + * Section 4.1.2.3, + * The user agent will reject cookies unless the Domain attribute + * specifies a scope for the cookie that would include the origin + * server. + */ + if (!domain_matches(hostname, co->ddomain)) { + CTrace((tfp, + "store_cookie: Rejecting domain '%s' for host '%s'.\n", + co->ddomain, hostname)); + freeCookie(co); + return; + } + + /* + * RFC 2109 - + * 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. + * + * RFC 6265 - + * There is nothing comparable in RFC 6265, since this check appears to + * have reflected assumptions about how domain names were constructed + * when RFC 2109 was written. Section 5.1.3. (Domain Matching) is + * loosely related to these assumptions. + */ + if (!USE_RFC_6265) { + 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->ddomain, + hostname); + if (!HTForcedPrompt(cookie_noprompt, msg, NO)) { + CTrace((tfp, + "store_cookie: Rejecting domain '%s' for host '%s'.\n", + co->ddomain, + 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"); + + 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); + StrAllocCopy(de->ddomain, co->ddomain); + 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->ddomain, c2->ddomain) && + !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->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, + int secure) +{ + HTList *hl, *next; + cookie *co; + time_t now = time(NULL); + char crlftab[8]; + + sprintf(crlftab, "%c%c%c", CR, LF, '\t'); + for (hl = sublist; hl != NULL; hl = next) { + next = hl->next; + co = (cookie *) hl->object; + + if (co == NULL) { + continue; + } + + /* speed-up host_matches() and limit trace output */ + if (LYstrstr(hostname, co->ddomain) != 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->ddomain ? co->ddomain : "(no domain)"), + domain_matches(hostname, co->ddomain), + 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->flags & COOKIE_FLAG_EXPIRES_SET) && + co->expires <= now) { + next = hl->next; + HTList_removeObject(sublist, co); + freeCookie(co); + total_cookies--; + if (next) + continue; + break; + } + + /* + * Check if we have a unexpired match, and handle if we do. + */ + if (co->domain != 0 && + co->name != 0 && + domain_matches(hostname, co->ddomain) && + (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) { + continue; + } + + /* + * Skip if we have a port list and the current port is not listed. + * - FM + */ + if (USE_RFC_2965 + && co->PortList + && !port_matches(port, co->PortList)) { + 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); + } + } 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); */ + /* } */ + + } + /* + * Include the cookie name=value pair. + */ + StrAllocCat(header, co->name); + StrAllocCat(header, "="); + if (co->quoted) { + StrAllocCat(header, "\""); + } + StrAllocCat(header, co->value); + if (co->quoted) { + StrAllocCat(header, "\""); + } + /* + * 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)) { + HTSprintf(&header, "; $Path=\"%s\"", co->path); + } + if (co->PortList && isdigit(UCH(*co->PortList))) { + HTSprintf(&header, "; $Port=\"%s\"", co->PortList); + } + if (co->domain && (co->flags & COOKIE_FLAG_DOMAIN_SET)) { + HTSprintf(&header, "; $Domain=\"%s\"", co->domain); + } + } + } + } + + 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 = (int) (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) + +/* + * Attribute-names are matched ignoring case. + * + * Attribute RFC-2109 (1997) RFC-2965 (2000) RFC-6265 (2011) + * --------------------------------------------------------------- + * comment yes yes - + * commentURL - yes - + * discard - yes - + * domain yes yes yes + * expires yes yes yes + * httponly - - yes + * max-age yes yes yes + * path yes yes yes + * port - yes - + * secure yes yes yes + * version yes yes - + * --------------------------------------------------------------- + */ +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; + + CTrace((tfp, "parse_attribute %.*s\n", attr_len, attr_start)); + + flags &= (unsigned) (~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 (USE_RFC_6265 && is_attr("httponly", 8)) { + if (value == NULL) { + known_attr = YES; /* known, but irrelevant to lynx */ + } else { + known_attr = NO; + } + } else if (USE_RFC_2965 && 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 ((USE_RFC_2109 || USE_RFC_2965) && 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 (USE_RFC_2965 && 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)); + HTSprintf0(&(cur_cookie->domain), ".%s", 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; + SetCookieDomain(cur_cookie, cur_cookie->domain); + } + } 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; + CTrace((tfp, " ->%.*s\n", cur_cookie->pathlen, cur_cookie->path)); + } + } else if (USE_RFC_2965 && 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); + CTrace((tfp, " ->%s\n", 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 ((USE_RFC_2109 || USE_RFC_2965) && 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 = (int) strtol(value, NULL, 10); + + if (errno != -ERANGE) { + cur_cookie->version = temp; + } + } + } else if (is_attr("max-age", 7)) { + known_attr = YES; + /* + * Don't process a repeat max-age. - FM + */ + if (cur_cookie != NULL && value && + !(flags & FLAGS_MAXAGE_ATTR)) { + long 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. + */ + 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, + (int) (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)) { + AssumeCookieVersion(cur_cookie); + 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(hostname); + 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; + SetCookieDomain(cur_cookie, hostname); + } + 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)) { + AssumeCookieVersion(cur_cookie); + 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, + (int) (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) { + AssumeCookieVersion(cur_cookie); + 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(hostname); + 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; + SetCookieDomain(cur_cookie, hostname); + } + 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) { + AssumeCookieVersion(cur_cookie); + 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) { + if (ptr == path) { + ++ptr; + } + CTrace((tfp, "discarding \"%s\" from request URI\n", ptr)); + *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, + int 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 + */ + freeCookies(de); + 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); + SetCookieDomain(moo, 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 (LeadingDot(domain)) + 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 " %s %" PRI_time_t " ", + CAST_time_t (now), + (now < co->expires) ? "<" : ">", + 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->ddomain, + (co->flags & COOKIE_FLAG_DOMAIN_SET) ? "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 %s\n", de->ddomain)); + } + } + if (cookie_handle != NULL) { + LYCloseOutput(cookie_handle); + HTSYS_purge(cookie_file); + } +} +#endif + +/* + * Check if the given string is completely US-ASCII. If so (and if the + * original were hex-encoded), it is likely to be more useful in a decoded + * form. + */ +static BOOLEAN valueNonAscii(const char *value) +{ + BOOLEAN result = FALSE; + + while (*value != '\0') { + int ch = UCH(*value++); + + if (ch < 32 || ch > 126) { + result = TRUE; + break; + } + } + + return result; +} + +/* 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 + */ + freeCookies(de); + 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->ddomain); + 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 + */ + freeCookies(de); + 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)) { + freeCookies(de); + 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->ddomain); + return (HT_NO_DATA); + + case 'V': + /* + * Set to reject all cookies from this domain. - FM + */ + de->bv = REJECT_ALWAYS; + HTUserMsg2(NEVER_ALLOWING_COOKIES, de->ddomain); + 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 == 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)) + + WriteStreamTitle(target, COOKIE_JAR_TITLE); + 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, "<div><em>Note:</em> %s\n", ACTIVATE_TO_GOBBLE); + PUTS(buf); + HTSprintf0(&buf, "%s</div>\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/\"><em>Domain:</em> %s</a>\n", + de->ddomain, STR_LYNXCOOKIE, de->ddomain, de->ddomain); + 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); + HTUnEscape(value); + if (valueNonAscii(value)) + strcpy(value, co->value); + LYEntify(&value, TRUE); + } else { + StrAllocCopy(value, NO_VALUE); + } + HTSprintf0(&buf, "<dd><a href=\"%s//%s/%s\"><em>%s</em>=%s</a>\n", + STR_LYNXCOOKIE, de->ddomain, 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><em>Path:</em> %s\n<dd><em>Port:</em> %d <em>Secure:</em> %s <em>Discard:</em> %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><em>PortList:</em> \"%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><em>CommentURL:</em> <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><em>Comment:</em> %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, "\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"); + + 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); + StrAllocCopy(de->ddomain, SkipLeadingDot(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; + + CTrace((tfp, "LYConfigCookies\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 0000000..ecbf930 --- /dev/null +++ b/src/LYCookie.h @@ -0,0 +1,59 @@ +/* $LynxId: LYCookie.h,v 1.20 2011/06/07 08:29:39 tom Exp $ */ +#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 */ + char *ddomain; /* Domain without leading "." */ + 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, + int 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 0000000..d1ad337 --- /dev/null +++ b/src/LYCurses.c @@ -0,0 +1,3307 @@ +/* $LynxId: LYCurses.c,v 1.207 2024/01/09 00:30:44 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 + +#ifdef PDCURSES +#undef HAVE_NEWTERM /* not needed, since /dev/tty is unused */ +#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; + +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((SLsmg_Color_Type) (Current_Attr & ~Masked_Attr)); +} + +void LYsubAttr(int a) +{ + Current_Attr &= ~a; + SLsmg_set_color((SLsmg_Color_Type) (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, (SLtt_Char_Type) (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); + LYGetScreenSize(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; + + if (code >= 0) { + unsigned i; + int pair = PAIR_NUMBER((unsigned) code); + int bold = (pair != 0 && ((unsigned) code & A_BOLD) != 0); + + StrAllocCopy(result, ""); + + if (bold) + code &= (int) ~A_BOLD; + + for (i = 0; i < TABLESIZE(Mono_Attrs); i++) { + if (Mono_Attrs[i].code & code) { + if (non_empty(result)) + StrAllocCat(result, "+"); + StrAllocCat(result, Mono_Attrs[i].name); + } + } + if (pair != 0) { + short f, b; + + if (pair_content((short) pair, &f, &b) != ERR) { + if (non_empty(result)) + StrAllocCat(result, "+"); + StrAllocCat(result, lookup_color(bold ? f + COLORS : f)); + StrAllocCat(result, "/"); + StrAllocCat(result, lookup_color(b)); + } + } + } else { + FREE(result); + } + return result; +} +#endif /* USE_COLOR_STYLE */ +#endif /* FANCY_CURSES */ +#endif /* USE_SLANG */ + +/* + * This function boxes windows for (n)curses. + */ +void LYbox(WINDOW * win, int formfield GCC_UNUSED) +{ +#ifdef USE_SLANG + SLsmg_draw_box(win->top_y, + win->left_x, + (unsigned) win->height, + (unsigned) 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, (unsigned) color, (unsigned) mono)); + + ds->used = TRUE; + ds->color = color; + ds->cattr = cattr; + ds->mono = mono; +} + +/* + * set the curses attributes to be color or mono - RP + */ +static void LYAttrset(WINDOW * win, int color, + int mono) +{ + char *shown = NULL; + + if (lynx_has_color + && LYShowColor >= SHOW_COLOR_ON + && color >= 0) { + CTRACE2(TRACE_STYLE, (tfp, "CSS:LYAttrset color %#x -> (%s)\n", + (unsigned) color, + shown = attr_to_string(color))); + (void) wattrset(win, color); + } else if (mono >= 0) { + CTRACE2(TRACE_STYLE, (tfp, "CSS:LYAttrset mono %#x -> (%s)\n", + (unsigned) mono, + shown = attr_to_string(mono))); + (void) wattrset(win, mono); + } else { + CTRACE2(TRACE_STYLE, (tfp, "CSS:LYAttrset (A_NORMAL)\n")); + (void) wattrset(win, A_NORMAL); + } + if (shown != NULL) + (void) attr_to_string(-1); +} + +void curses_w_style(WINDOW * win, int style, + int dir) +{ + int YP, XP; + bucket *ds; + BOOL free_ds = TRUE; + + switch (style) { + case NOSTYLE: + ds = nostyle_bucket(); + break; + default: + ds = &hashStyles[style]; + free_ds = FALSE; + break; + } + + if (!ds->used) { + CTRACE2(TRACE_STYLE, (tfp, "CSS.CS:Style %d not configured\n", style)); + if (free_ds) + free(ds); + return; + } + + CTRACE2(TRACE_STYLE, (tfp, "CSS.CS:<%s%s> style %d color %#x\n", + (dir ? "" : "/"), + ds->name, + style, + (unsigned) ds->color)); + + getyx(win, YP, XP); + + if (style == s_normal && dir) { + LYAttrset(win, ds->color, ds->mono); + if (win == LYwin) + SetCachedStyle(YP, XP, (unsigned) s_normal); + if (free_ds) + free(ds); + 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", + (unsigned) last_styles[last_colorattr_ptr], + "in LynxChangeStyle(curses_w_style)")); + last_colorattr_ptr = MAX_LAST_STYLES - 1; + } + last_styles[last_colorattr_ptr++] = (int) LYgetattrs(win); + /* don't cache style changes for active links */ + /* 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, (unsigned) style); + } + LYAttrset(win, ds->color, ds->mono); + break; + } + + if (free_ds) + free(ds); + + return; +} + +/* + * 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 = color_style_1(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", (void *) pclass)); + if (pclass) + *pclass = '\0'; + else + try_again = 0; + } else { + CTRACE2(TRACE_STYLE, (tfp, "ok (%d)\n", tmpHash)); + curses_w_style(win, tmpHash, 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 (int) 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 ((unsigned) color_attr & A_BOLD) + code |= 1; + if ((unsigned) color_attr & (A_REVERSE | A_DIM)) + code |= 2; + if ((unsigned) 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((unsigned) second); + int mono = (int) ((unsigned) 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", + (unsigned) mask, + (unsigned) second, + (unsigned) 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) + && n < (int) TABLESIZE(lynx_color_cfg)) { + 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) + ? get_color_pair(a + 1) + : (int) 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 |= (int) SLTT_BOLD_MASK; + if (a & 2) + Masked_Attr |= (int) SLTT_REV_MASK; + if (a & 4) + Masked_Attr |= (int) SLTT_ULINE_MASK; + lynx_setup_attrs(); +#else +#ifdef USE_COLOR_TABLE + Masked_Attr = decode_mono_code(a); +#endif +#endif +} + +#define NEWTERM_NAME "newterm" + +#ifndef USE_SLANG +static WINDOW *my_subwindow; + +#define delete_subwindow() if (my_subwindow) { delwin(my_subwindow); my_subwindow = NULL; } +#endif + +#ifdef WIDEC_CURSES +static WINDOW *fake_win; +static int fake_max; + +#define delete_fake_win() if (fake_win) { delwin(fake_win); fake_win = NULL; fake_max = 0; } +#else +#define delete_fake_win() /* nothing */ +#endif + +#if !defined(VMS) && !defined(USE_SLANG) +#if defined(NCURSES) && defined(HAVE_RESIZETERM) + +static SCREEN *LYscreen = NULL; + +static void delete_screen(SCREEN * screen) +{ + delete_fake_win(); + delete_subwindow(); + delscreen(screen); +} + +#define LYDELSCR() /* ncurses does not need this */ + +#elif defined(HAVE_NEWTERM) && defined(HAVE_DELSCREEN) + +static SCREEN *LYscreen = NULL; + +#if defined(USE_DEFAULT_COLORS) +static void delete_screen(SCREEN * screen) +{ + delete_fake_win(); + delete_subwindow(); + delscreen(screen); +} +#endif + +#define LYDELSCR() { \ +CheckScreenSize(); \ +if (recent_sizechange) { \ + CTRACE((tfp, "Screen size: delscreen()\n")); \ + delete_screen(LYscreen); \ + LYscreen = NULL; } } + +#else /* HAVE_NEWTERM */ + + /* + * If newterm is not defined, assume a curses subset which + * supports only initscr. --gil + */ +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; + +int saved_scrsize_x2 = 0; +int saved_scrsize_y2 = 0; +int saved_winpos_x2 = 0; +int saved_winpos_y2 = 0; + +static int LYresize_term(int nlines, int ncols) +{ +#ifdef _WINDOWS + HANDLE hConsole; + CONSOLE_SCREEN_BUFFER_INFO csbi; + SMALL_RECT srWindowRect; + + hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + GetConsoleScreenBufferInfo(hConsole, &csbi); + srWindowRect.Right = min(ncols, csbi.dwSize.X) - 1; + srWindowRect.Bottom = min(nlines, csbi.dwSize.Y) - 1; + srWindowRect.Left = srWindowRect.Top = (SHORT) 0; + SetConsoleWindowInfo(hConsole, TRUE, &srWindowRect); +#endif + return resize_term(nlines, ncols); +} +#endif + +#ifdef USE_MAXSCREEN_TOGGLE +static HWND currentWindowHandle = NULL; +static char dummyWindowTitle[256]; + +static int CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) +{ + char this_title[256]; + + (void) lParam; + if (GetWindowText(hwnd, this_title, sizeof(this_title) - 1) && + (strncmp(dummyWindowTitle, this_title, 256) == 0)) { + currentWindowHandle = hwnd; + return FALSE; + } + return TRUE; +} + +static void setCurrentWindowHandle(void) +{ + char org_title[256]; + DWORD pid; + int i; + + if (currentWindowHandle != NULL) { + return; + } + pid = GetCurrentProcessId(); + sprintf(dummyWindowTitle, "Lynx for Win32 (pid=%ld)", pid); + GetConsoleTitle(org_title, sizeof(org_title) - 1); + SetConsoleTitle(dummyWindowTitle); + for (i = 0; i < 10; i++) { + EnumWindows(EnumWindowsProc, (LPARAM) 0); + if (currentWindowHandle != NULL) { + break; + } + CTRACE((tfp, + "Failed to get current window handle. Try again...(%d)\n", i)); + Sleep(100); + } + SetConsoleTitle(org_title); +} + +static void moveWindowHXY(HWND hwnd, int x, int y) +{ + int win_height, win_width; + RECT winrect; + + GetWindowRect(hwnd, &winrect); + win_width = winrect.right - winrect.left; + win_height = winrect.bottom - winrect.top; + + if ((x != winrect.left) || (y != winrect.top)) { + MoveWindow(hwnd, x, y, win_width, win_height, TRUE); + CTRACE((tfp, "move window from (%d,%d) to (%d,%d).\n", + (int) winrect.left, + (int) winrect.top, x, y)); + } +} + +static void adjustWindowPos(void) +{ + int disp_height, disp_width, win_height, win_width; + int newwin_left, newwin_top; + RECT winrect; + DWORD pid; + + setCurrentWindowHandle(); + if (currentWindowHandle == NULL) { + return; + } + GetWindowThreadProcessId(currentWindowHandle, &pid); + disp_width = GetSystemMetrics(SM_CXFULLSCREEN); + disp_height = GetSystemMetrics(SM_CYFULLSCREEN); + Sleep(100); /* If not, GetWindowRect sometimes return wrong value. */ + GetWindowRect(currentWindowHandle, &winrect); + win_width = winrect.right - winrect.left; + win_height = winrect.bottom - winrect.top; + CTRACE((tfp, "Display Size: (%4d,%3d)\n", disp_width, disp_height)); + CTRACE((tfp, "Orig WinRect: (%4d,%4d,%3d,%3d), ", + (int) winrect.left, (int) winrect.right, + (int) winrect.top, (int) winrect.bottom)); + CTRACE((tfp, "Size: (%4d,%3d)\n", win_width, win_height)); + + newwin_left = winrect.left; + newwin_top = winrect.top; + if (disp_width < winrect.right) { + if (win_width <= disp_width) { + newwin_left = disp_width - win_width; + } else { + newwin_left = 0; + } + } + if (disp_height < winrect.bottom) { + if (win_height <= disp_height) { + newwin_top = disp_height - win_height; + } else { + newwin_top = 0; + } + } + + moveWindowHXY(currentWindowHandle, newwin_left, newwin_top); +} + +void maxmizeWindowSize(void) +{ + RECT winrect; + HANDLE hConsole; + COORD coordScreen; + + setCurrentWindowHandle(); + if (currentWindowHandle == NULL) { + return; + } + GetWindowRect(currentWindowHandle, &winrect); + saved_winpos_x2 = winrect.left; + saved_winpos_y2 = winrect.top; + + hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + coordScreen = GetLargestConsoleWindowSize(hConsole); + + LYcols = scrsize_x = coordScreen.X - 1; + LYlines = scrsize_y = coordScreen.Y - 1; + LYlines--; + CTRACE((tfp, "Request maximum screen size: %dx%d\n", + scrsize_x, scrsize_y)); + LYresize_term(scrsize_y, scrsize_x); + Sleep(100); + moveWindowHXY(currentWindowHandle, 0, 0); + LYlines = LYscreenHeight(); + LYcols = LYscreenWidth(); + CTRACE((tfp, "...actual maximum screen size: %dx%d\n", + LYcols, LYlines)); + LYStatusLine = -1; + LYGetScreenSize(0); + recent_sizechange = TRUE; +} + +void recoverWindowSize(void) +{ + if ((0 < saved_scrsize_x2) && (0 < saved_scrsize_y2)) { + LYcols = scrsize_x = saved_scrsize_x2; + LYlines = scrsize_y = saved_scrsize_y2; + LYlines--; + LYStatusLine = -1; + wclear(curscr); + doupdate(); + LYresize_term(scrsize_y, scrsize_x); + + setCurrentWindowHandle(); + if (currentWindowHandle != NULL) { + Sleep(100); + moveWindowHXY(currentWindowHandle, saved_winpos_x2, saved_winpos_y2); + } + recent_sizechange = TRUE; + } else { + CTRACE((tfp, "scrsize_{xy} is not saved yet.\n")); + } +} +#endif + +#if defined(USE_DEFAULT_COLORS) +void restart_curses(void) +{ + SCREEN *oldscreen = LYscreen; + + if (!(LYscreen = newterm(NULL, stdout, stdin))) { /* start curses */ + fprintf(tfp, "%s\n", + gettext("Terminal reinitialisation failed - unknown terminal type?")); + exit_immediately(EXIT_FAILURE); + } + + /* force xterm mouse-mode off in the physical terminal */ + lynx_enable_mouse(0); + keypad(LYwin, FALSE); + wrefresh(LYwin); + + LYwin = stdscr; + /* re-enable xterm mouse-mode in the new screen */ + keypad(LYwin, TRUE); + lynx_enable_mouse(1); + +#if defined(USE_KEYMAPS) + if (-1 == lynx_initialize_keymaps()) { + endwin(); + exit_immediately(EXIT_FAILURE); + } +#endif + if (has_colors()) { + start_color(); + } + + delete_screen(oldscreen); +} +#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 & (int) 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() every time 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); + LYGetScreenSize(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(NCURSES_VERSION) +#if defined(SIGWINCH) + size_change(0); + LYGetScreenSize(0); + recent_sizechange = FALSE; /* prevent mainloop drawing 1st doc twice */ +#endif /* SIGWINCH */ +#endif /* NCURSES_VERSION */ + CTRACE((tfp, "Screen size is now %d x %d\n", LYcols, LYlines)); + +#ifdef USE_CURSES_PADS + if (LYuseCursesPads) { + CTRACE((tfp, "using curses-pads\n")); + keypad(stdscr, TRUE); + LYwin = newpad(LYlines, MAX_COLS); + LYshiftWin = 0; + LYwideLines = FALSE; + } else { + LYwin = stdscr; + } +#endif + +#if defined(USE_KEYMAPS) && defined(NCURSES_VERSION) +# ifdef HAVE_KEYPAD + /* Need to switch keypad on before initializing keymaps, otherwise + when the keypad is switched on, some keybindings may be overridden. */ + keypad(LYwin, TRUE); + keypad_on = 1; +# endif /* HAVE_KEYPAD */ + + if (-1 == lynx_initialize_keymaps()) { + endwin(); + exit_immediately(EXIT_FAILURE); + } +#if defined(CAN_CUT_AND_PASTE) && defined(IXON) && defined(IXOFF) + /* + * Since the ifdef is enabled, we know that at least the copy/paste + * commands are enabled. Binding the copy to ^S is inconvenient, so + * ask curses to permit that (saving an external tweak with stty). + * + * Disable the feature in lynx.cfg with + * KEYMAP:^S:UNMAPPED + */ + if (keymap[KTL('S')] != LYK_UNKNOWN) { + struct termios alter_tty; + + CTRACE((tfp, "Allowing ^S as key-binding\n")); + if (tcgetattr(fileno(stdin), &alter_tty) == 0) { + alter_tty.c_iflag &= (unsigned) ~(IXON | IXOFF); + tcsetattr(fileno(stdout), TCSAFLUSH, &alter_tty); + def_prog_mode(); + } + } +#endif +#endif /* ncurses-keymaps */ + + /* + * 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 + update_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 */ + +#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 + 1; + } + 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)); + LYresize_term(scrsize_y, scrsize_x); + LYlines = LYscreenHeight(); + LYcols = LYscreenWidth(); + LYStatusLine = -1; + LYclear(); +#ifdef _WINDOWS + adjustWindowPos(); +#endif + } + if (saved_scrsize_x2 == 0) { + if (saved_scrsize_x == 0) { + saved_scrsize_x2 = COLS; + saved_scrsize_y2 = LINES + 1; + } else { + saved_scrsize_x2 = scrsize_x; + saved_scrsize_y2 = scrsize_y; + } + } +#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 */ + +/***********************************************************************/ +#else + (void) state; +#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 struct termios 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 { + tcgetattr(fileno(stdout), &saved_tty); + did_save = TRUE; + if ((saved_tty.c_oflag & ONLCR)) + can_fix = FALSE; +#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) { + struct termios alter_tty = saved_tty; + + alter_tty.c_oflag |= ONLCR; + tcsetattr(fileno(stdout), TCSAFLUSH, &alter_tty); + def_prog_mode(); + waiting = TRUE; + nonl(); + } + } else { + if (waiting) { + tcsetattr(fileno(stdout), TCSAFLUSH, &saved_tty); + def_prog_mode(); + 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' && + (non_empty(form_get_data) || + non_empty(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); + (void) y; + 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. + */ +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, + (unsigned) *height, + (unsigned) *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; + + (void) y; + (void) y0; +#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)) + && (y0 >= 0) + && (x0 >= 0) + && (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, + int retCellNum GCC_UNUSED) +{ + int used, result; + + if (isEmpty(string)) { + used = ((len > 0) ? len : 0); + } else { + used = ((len < 0) ? (int) strlen(string) : len); + } + result = used; +#ifdef WIDEC_CURSES + if (non_empty(string) && used > 0 && lynx_called_initscr) { + 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 + * initialization 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 (y < 0) + y = 0; + if (x < 0) + x = 0; + 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 == TRUE ? 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 earlier 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 0000000..f152ce2 --- /dev/null +++ b/src/LYCurses.h @@ -0,0 +1,865 @@ +/* $LynxId: LYCurses.h,v 1.98 2023/10/23 23:35:36 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 +#define ENABLE_SLFUTURE_CONST 1 +#include <slang.h> +#ifndef SLFUTURE_CONST +#define SLFUTURE_CONST /* nothing */ +#endif +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 KEY_EVENT +#undef KEY_EVENT /* wincon.h or Cygwin's copy of it */ +#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 + +#if defined(_WINDOWS) && defined(PDCURSES) && defined(PDC_BUILD) && PDC_BUILD >= 2401 +#define USE_MAXSCREEN_TOGGLE 1 +extern void maxmizeWindowSize(void); +extern void recoverWindowSize(void); +#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, int 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 */ + +/* + * Check if the SIGWINCH handler has caught a signal, before testing the + * recent_sizechange variable. + */ +#ifdef SIGWINCH +#if defined(KEY_RESIZE) && defined(HAVE_RESIZETERM) && defined(HAVE_WRESIZE) +#define USE_CURSES_RESIZE 1 +#endif + +#if defined(USE_CURSES_RESIZE) || defined(USE_SLANG) +#define CheckScreenSize() \ + do { \ + if (size_is_changed) { \ + size_is_changed = FALSE; \ + LYGetScreenSize(SIGWINCH); \ + } \ + } while (0) +#endif +#endif + +#ifndef CheckScreenSize +#define CheckScreenSize() /* nothing */ +#endif + +/* + * 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 add_to_lss_list(const char *source, const char *resolved); + extern void clear_lss_list(void); + extern void curses_css(char *name, int dir); + extern void curses_style(int style, int dir); + extern void curses_w_style(WINDOW * win, int style, int dir); + extern void init_color_styles(char **from_cmdline, const char *default_styles); + extern void reinit_color_styles(void); + 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 update_color_style(void); + extern void wcurses_css(WINDOW * win, char *name, 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) +#define COLS SLtt_Screen_Cols +#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) + +#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 /* 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); + + extern void restart_curses(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 + +#define LYParkCursor() LYmove((LYlines - 1), 0); LYclrtoeol() + + 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 0000000..afd6638 --- /dev/null +++ b/src/LYDownload.c @@ -0,0 +1,591 @@ +/* $LynxId: LYDownload.c,v 1.72 2021/07/29 20:30:00 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; + bstring *buffer = NULL; + bstring *command = NULL; + 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 + */ + if (StrAllocCopy(Line, line) == 0) + goto failed; + + /* + * Parse out the File, sug_file, and the Method. + */ + if ((file = LYstrstr(Line, "/File=")) == NULL) + goto failed; + *file = '\0'; + /* + * Go past "File=". + */ + file += 6; + + if ((sug_file = LYstrstr(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 = LYstrstr(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) { + BStrCopy0(buffer, sug_file); + } else { + BStrCopy0(buffer, ""); + } + + check_recall: + if ((ch = LYgetBString(&buffer, FALSE, 0, recall)) < 0 || + isBEmpty(buffer) || + ch == UPARROW_KEY || + ch == DNARROW_KEY) { + + if (recall && ch == UPARROW_KEY) { + 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) { + BStrCopy0(buffer, cp); + if (FnameTotal == 1) { + _statusline(EDIT_THE_PREV_FILENAME); + } else { + _statusline(EDIT_A_PREV_FILENAME); + } + goto check_recall; + } + } else if (recall && ch == DNARROW_KEY) { + 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) { + BStrCopy0(buffer, cp); + if (FnameTotal == 1) { + _statusline(EDIT_THE_PREV_FILENAME); + } else { + _statusline(EDIT_A_PREV_FILENAME); + } + goto check_recall; + } + } + + /* + * Save cancelled. + */ + goto cancelled; + } + + BStrCopy(command, buffer); + if (!LYValidateFilename(&buffer, &command)) + goto cancelled; +#ifdef HAVE_POPEN + else if (LYIsPipeCommand(buffer->str)) { + /* 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->str)) { + case 'Y': + break; + case 'N': + _statusline(NEW_FILENAME_PROMPT); + FirstRecall = TRUE; + FnameNum = FnameTotal; + goto retry; + default: + goto cleanup; + } + + /* + * See if we can write to it. + */ + CTRACE((tfp, "LYDownload: filename is %s\n", buffer->str)); + + SecondS = TRUE; + + HTInfoMsg(SAVING); +#ifdef VMS + /* + * Try rename() first. - FM + */ + CTRACE((tfp, "command: rename(%s, %s)\n", file, buffer->str)); + if (rename(file, buffer->str)) { + /* + * Failed. Use spawned COPY_COMMAND. - FM + */ + CTRACE((tfp, " FAILED!\n")); + LYCopyFile(file, buffer->str); + } 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->str, HIDE_CHMOD); +#else /* Unix: */ + + LYCopyFile(file, buffer->str); + LYRelaxFilePermissions(buffer->str); +#endif /* VMS */ + + } else { + /* + * Use configured download commands. + */ + BStrCopy0(buffer, ""); + 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) { + BStrCopy0(buffer, sug_file); + } else { + BStrCopy0(buffer, ""); + } + + check_again: + if ((ch = LYgetBString(&buffer, FALSE, 0, recall)) < 0 || + isBEmpty(buffer) || + ch == UPARROW_KEY || + ch == DNARROW_KEY) { + + if (recall && ch == UPARROW_KEY) { + 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) { + BStrCopy0(buffer, cp); + if (FnameTotal == 1) { + _statusline(EDIT_THE_PREV_FILENAME); + } else { + _statusline(EDIT_A_PREV_FILENAME); + } + goto check_again; + } + } else if (recall && ch == DNARROW_KEY) { + 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) { + BStrCopy0(buffer, cp); + 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->str) == '.') { + 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->str)) { + 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->str); + 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->str, "sys$disk:", 9)) { + if (0 == StrNCmp((buffer->str + 9), "[]", 2)) { + HTAddSugFilename(buffer->str + 11); + } else { + HTAddSugFilename(buffer->str + 9); + } + } else { + HTAddSugFilename(buffer->str); + } +#else + HTAddSugFilename(buffer->str); +#endif /* VMS */ + } + goto cleanup; + + failed: + HTAlert(CANNOT_DOWNLOAD_FILE); + goto cleanup; + + cancelled: + HTInfoMsg(CANCELLING); + + cleanup: + FREE(Line); + BStrFree(buffer); + BStrFree(command); + 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 0000000..5926df8 --- /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 0000000..304bb4e --- /dev/null +++ b/src/LYEdit.c @@ -0,0 +1,298 @@ +/* $LynxId: LYEdit.c,v 1.43 2021/06/09 21:39:57 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, (unsigned) 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 0000000..3c07062 --- /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 0000000..dec2ac9 --- /dev/null +++ b/src/LYEditmap.c @@ -0,0 +1,1931 @@ +/* + * $LynxId: LYEditmap.c,v 1.76 2018/12/27 10:33:52 tom Exp $ + * + * LYEditMap.c + * Keybindings for line and form editing. + */ + +#include <HTUtils.h> +#include <HTAlert.h> +#include <HTFile.h> +#include <LYGlobalDefs.h> +#include <LYCharUtils.h> +#include <LYStrings.h> +#include <LYKeymap.h> /* KEYMAP_SIZE, LKC_*, LYK_* - kw */ +#include <LYLeaks.h> + +#define PUTS(buf) (*target->isa->put_string)(target, buf) + +/* * * * * LynxEditactionCodes * * * * */ +#ifdef USE_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 /* USE_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* */ +struct emap { + const char *name; + const int code; + const char *descr; +}; + +#define SEPARATOR {"", -1, ""} + +static struct emap ekmap[] = { + {"NOP", LYE_NOP, "Do Nothing"}, + {"CHAR", LYE_CHAR, "Insert printable char"}, + SEPARATOR, + {"ENTER", LYE_ENTER, "Input complete, return char"}, + {"TAB", LYE_TAB, "Input complete, return TAB"}, + {"STOP", LYE_STOP, "Input deactivated"}, + {"ABORT", LYE_ABORT, "Input cancelled"}, + SEPARATOR, + {"PASS", LYE_FORM_PASS, "Fields only: input complete"}, + SEPARATOR, + {"DELBL", LYE_DELBL, "Delete back to BOL"}, + {"DELEL", LYE_DELEL, "Delete thru EOL"}, + {"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"}, + SEPARATOR, + + {"ERASE", LYE_ERASE, "Erase the line"}, + SEPARATOR, + {"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"}, + SEPARATOR, + {"LOWER", LYE_LOWER, "Lower case the line"}, + {"UPPER", LYE_UPPER, "Upper case the line"}, + SEPARATOR, + {"LKCMD", LYE_LKCMD, "Invoke command prompt"}, + {"SWMAP", LYE_SWMAP, "Switch input keymap"}, + SEPARATOR, + {"C1CHAR", LYE_C1CHAR, "Insert C1 char if printable"}, + {"SETM1", LYE_SETM1, "Set modifier 1 flag"}, + {"SETM2", LYE_SETM2, "Set modifier 2 flag"}, + {"UNMOD", LYE_UNMOD, "Fall back to no-modifier command"}, + SEPARATOR, + {"TPOS", LYE_TPOS, "Transpose characters"}, + {"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 + SEPARATOR, + {"PASTE", LYE_PASTE, "ClipBoard to Lynx"}, +#endif + SEPARATOR, + {"AIX", LYE_AIX, "Hex 97"}, + {0, -1, 0}, +}; +#undef SEPARATOR +/* *INDENT-ON* */ + +static LYEditCode DefaultEditBinding[KEYMAP_SIZE]; + +#ifdef USE_ALT_BINDINGS +static LYEditCode BetterEditBinding[KEYMAP_SIZE]; +static LYEditCode BashlikeEditBinding[KEYMAP_SIZE]; + +/* + * If a modifier bit is set in a lynxkeycode, it is first looked up here. + * + * 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 LYEditCode Mod1Binding[KEYMAP_SIZE]; + +/* 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 LYEditCode *Mod2Binding = Mod1Binding; +static LYEditCode *Mod3Binding = Mod1Binding; + +static const LYEditInit initMod1Binding[] = +{ + {CTL('A'), LYE_BOL}, + {CTL('B'), LYE_BACKW}, + {CTL('C'), LYE_UNMOD}, + {CTL('D'), LYK_NEXT_LINK | LYE_FORM_LAC}, + {CTL('E'), LYK_EDITTEXTAREA | LYE_FORM_LAC}, + {CTL('F'), LYE_FORWW}, + {CTL('G'), LYE_ABORT}, + {CTL('H'), LYE_DELPW}, + {CTL('I'), LYE_UNMOD}, + {CTL('J'), LYE_ENTER}, + {CTL('K'), LYK_LPOS_NEXT_LINK | LYE_FORM_LAC}, + {CTL('L'), LYE_FORM_PASS}, + {CTL('M'), LYE_ENTER}, + {CTL('N'), LYE_FORWW}, + {CTL('O'), LYE_UNMOD}, + {CTL('P'), LYE_BACKW}, + {CTL('R'), LYE_BACKW}, + {CTL('U'), LYE_FORM_PASS}, + {CTL('W'), LYE_KILLREG}, + {CTL('X'), LYE_XPMARK}, + {CTL('Y'), LYE_UNMOD}, + {CTL('Z'), LYE_FORM_PASS}, + {CTL('\\'), LYE_FORM_PASS}, + {CTL(']'), LYE_FORM_PASS}, + {CTL('^'), 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_FORM_PASS}, + {'0', LYE_UNMOD}, + {'1', LYE_UNMOD}, + {'2', LYE_UNMOD}, + {'3', LYE_UNMOD}, + {'4', LYE_UNMOD}, + {'5', LYE_UNMOD}, + {'6', LYE_UNMOD}, + {'7', LYE_UNMOD}, + {'8', LYE_UNMOD}, + {'9', LYE_UNMOD}, + {':', LYE_UNMOD}, + {';', LYE_UNMOD}, + {'<', LYK_HOME | LYE_FORM_LAC}, + {'=', LYE_UNMOD}, + {'>', LYK_END | LYE_FORM_LAC}, + {'?', LYE_UNMOD}, + {'@', LYE_C1CHAR}, + {'A', LYE_C1CHAR}, + {'B', LYE_C1CHAR}, + {'C', LYE_C1CHAR}, + {'D', LYE_C1CHAR}, + {'E', LYE_C1CHAR}, + {'F', LYE_C1CHAR}, + {'G', LYE_C1CHAR}, + {'H', LYE_C1CHAR}, + {'I', LYE_C1CHAR}, + {'J', LYE_C1CHAR}, + {'K', LYE_C1CHAR}, + {'L', LYE_C1CHAR}, + {'M', LYE_C1CHAR}, + {'N', LYE_C1CHAR}, + {'O', LYE_C1CHAR}, + {'P', LYE_C1CHAR}, + {'Q', LYE_C1CHAR}, + {'R', LYE_C1CHAR}, + {'S', LYE_C1CHAR}, + {'T', LYE_C1CHAR}, + {'U', LYE_C1CHAR}, + {'V', LYE_C1CHAR}, + {'W', LYE_C1CHAR}, + {'X', LYE_C1CHAR}, + {'Y', LYE_C1CHAR}, + {'Z', LYE_C1CHAR}, + {'[', LYE_C1CHAR}, + {'\\', LYE_C1CHAR}, + {']', LYE_C1CHAR}, + {'^', LYE_C1CHAR}, + {'_', LYE_C1CHAR}, + {'`', LYE_UNMOD}, + {'a', LYE_BOL}, + {'b', LYE_BACKW}, + {'c', LYE_UNMOD}, + {'d', LYE_DELNW}, + {'e', LYK_EDITTEXTAREA | LYE_FORM_LAC}, + {'f', LYE_FORWW}, + {'g', LYK_GROWTEXTAREA | LYE_FORM_LAC}, + {'h', LYE_CHAR}, + {'i', LYK_INSERTFILE | LYE_FORM_LAC}, + {'j', LYE_CHAR}, + {'k', LYE_ERASE}, + {'l', LYE_LOWER}, + {'m', LYE_CHAR}, + {'n', LYE_FORM_PASS}, + {'o', LYE_UNMOD}, + {'p', LYE_CHAR}, + {'u', LYE_UPPER}, + {'z', LYE_UNMOD}, + {'{', LYE_UNMOD}, + {'|', LYE_UNMOD}, + {'}', LYE_UNMOD}, + {'~', LYE_UNMOD}, + {DEL_KEY, LYE_DELPW}, + {160, LYE_UNMOD}, + {161, LYE_UNMOD}, + {162, LYE_UNMOD}, + {163, LYE_UNMOD}, + {164, LYE_UNMOD}, + {165, LYE_UNMOD}, + {166, LYE_UNMOD}, + {167, LYE_UNMOD}, + {168, LYE_UNMOD}, + {169, LYE_UNMOD}, + {170, LYE_UNMOD}, + {171, LYE_UNMOD}, + {172, LYE_UNMOD}, + {173, LYE_UNMOD}, + {174, LYE_UNMOD}, + {175, LYE_UNMOD}, + {176, LYE_UNMOD}, + {177, LYE_UNMOD}, + {178, LYE_UNMOD}, + {179, LYE_UNMOD}, + {180, LYE_UNMOD}, + {181, LYE_UNMOD}, + {182, LYE_UNMOD}, + {183, LYE_UNMOD}, + {184, LYE_UNMOD}, + {185, LYE_UNMOD}, + {186, LYE_UNMOD}, + {187, LYE_UNMOD}, + {188, LYE_UNMOD}, + {189, LYE_UNMOD}, + {190, LYE_UNMOD}, + {191, LYE_UNMOD}, + {192, LYE_UNMOD}, + {193, LYE_UNMOD}, + {194, LYE_UNMOD}, + {195, LYE_UNMOD}, + {196, LYE_UNMOD}, + {197, LYE_UNMOD}, + {198, LYE_UNMOD}, + {199, LYE_UNMOD}, + {200, LYE_UNMOD}, + {201, LYE_UNMOD}, + {202, LYE_UNMOD}, + {203, LYE_UNMOD}, + {204, LYE_UNMOD}, + {205, LYE_UNMOD}, + {206, LYE_UNMOD}, + {207, LYE_UNMOD}, + {208, LYE_UNMOD}, + {209, LYE_UNMOD}, + {210, LYE_UNMOD}, + {211, LYE_UNMOD}, + {212, LYE_UNMOD}, + {213, LYE_UNMOD}, + {214, LYE_UNMOD}, + {215, LYE_UNMOD}, + {216, LYE_UNMOD}, + {217, LYE_UNMOD}, + {218, LYE_UNMOD}, + {219, LYE_UNMOD}, + {220, LYE_UNMOD}, + {221, LYE_UNMOD}, + {222, LYE_UNMOD}, + {223, LYE_UNMOD}, + {224, LYE_UNMOD}, + {225, LYE_UNMOD}, + {226, LYE_UNMOD}, + {227, LYE_UNMOD}, + {228, LYE_UNMOD}, + {229, LYE_UNMOD}, + {230, LYE_UNMOD}, + {231, LYE_UNMOD}, + {232, LYE_UNMOD}, + {233, LYE_UNMOD}, + {234, LYE_UNMOD}, + {235, LYE_UNMOD}, + {236, LYE_UNMOD}, + {237, LYE_UNMOD}, + {238, LYE_UNMOD}, + {239, LYE_UNMOD}, + {240, LYE_UNMOD}, + {241, LYE_UNMOD}, + {242, LYE_UNMOD}, + {243, LYE_UNMOD}, + {244, LYE_UNMOD}, + {245, LYE_UNMOD}, + {246, LYE_UNMOD}, + {247, LYE_UNMOD}, + {248, LYE_UNMOD}, + {249, LYE_UNMOD}, + {250, LYE_UNMOD}, + {251, LYE_UNMOD}, + {252, LYE_UNMOD}, + {253, LYE_UNMOD}, + {254, LYE_UNMOD}, + {255, LYE_UNMOD}, + {UPARROW_KEY, LYE_UNMOD}, + {DNARROW_KEY, LYE_UNMOD}, + {RTARROW_KEY, LYE_UNMOD}, + {LTARROW_KEY, LYE_UNMOD}, + {PGDOWN_KEY, LYE_UNMOD}, + {PGUP_KEY, LYE_UNMOD}, + {HOME_KEY, LYE_FORM_PASS}, + {END_KEY, LYE_FORM_PASS}, + {F1_KEY, LYK_DWIMHELP | LYE_FORM_LAC}, + {DO_KEY, LYE_UNMOD}, +#if (defined(_WINDOWS) || defined(__DJGPP__)) + {FIND_KEY, LYE_UNMOD}, + {SELECT_KEY, LYE_UNMOD}, +#else + {FIND_KEY, LYK_WHEREIS | LYE_FORM_LAC}, + {SELECT_KEY, LYK_NEXT | LYE_FORM_LAC}, +#endif + {INSERT_KEY, LYE_UNMOD}, + {DO_NOTHING, LYE_UNMOD}, + {BACKTAB_KEY, LYE_UNMOD}, +#if (defined(_WINDOWS) || defined(__DJGPP__)) && defined(USE_SLANG) && !defined(DJGPP_KEYHANDLER) + {272, LYE_DELPW}, +#else + {272, LYE_UNMOD}, +#endif + {273, LYE_UNMOD}, + {-1, LYE_UNKNOWN} +}; + +LYEditConfig LYModifierBindings[] = +{ + {"Modifier Binding", initMod1Binding, Mod1Binding}, +}; + +#endif /* USE_ALT_BINDINGS */ + +static const LYEditInit initDefaultEditor[] = +{ + {CTL('A'), LYE_BOL}, + {CTL('B'), LYE_DELPW}, + {CTL('C'), LYE_ABORT}, + {CTL('D'), LYE_DELN}, + {CTL('E'), LYE_EOL}, + {CTL('F'), LYE_DELNW}, + {CTL('G'), LYE_ABORT}, + {CTL('H'), LYE_DELP}, + {CTL('I'), LYE_TAB}, + {CTL('J'), LYE_ENTER}, + {CTL('K'), LYE_LOWER}, + {CTL('M'), LYE_ENTER}, + {CTL('N'), LYE_FORWW}, + {CTL('O'), LYE_ABORT}, + {CTL('P'), LYE_BACKW}, + {CTL('R'), LYE_DELN}, + {CTL('T'), LYE_UPPER}, + {CTL('U'), LYE_ERASE}, + {CTL('V'), LYE_LKCMD}, +#ifdef CAN_CUT_AND_PASTE + {CTL('W'), LYE_PASTE}, +#endif + {CTL('X'), LYE_SETM1}, + {CTL('^'), LYE_SWMAP}, + {CTL('_'), LYE_DELEL}, + {' ', LYE_CHAR}, + {'!', LYE_CHAR}, + {'"', LYE_CHAR}, + {'#', LYE_CHAR}, + {'$', LYE_CHAR}, + {'%', LYE_CHAR}, + {'&', LYE_CHAR}, + {'\'', LYE_CHAR}, + {'(', LYE_CHAR}, + {')', LYE_CHAR}, + {'*', LYE_CHAR}, + {'+', LYE_CHAR}, + {',', LYE_CHAR}, + {'-', LYE_CHAR}, + {'.', LYE_CHAR}, + {'/', LYE_CHAR}, + {'0', LYE_CHAR}, + {'1', LYE_CHAR}, + {'2', LYE_CHAR}, + {'3', LYE_CHAR}, + {'4', LYE_CHAR}, + {'5', LYE_CHAR}, + {'6', LYE_CHAR}, + {'7', LYE_CHAR}, + {'8', LYE_CHAR}, + {'9', LYE_CHAR}, + {':', LYE_CHAR}, + {';', LYE_CHAR}, + {'<', LYE_CHAR}, + {'=', LYE_CHAR}, + {'>', LYE_CHAR}, + {'?', LYE_CHAR}, + {'@', LYE_CHAR}, + {'A', LYE_CHAR}, + {'B', LYE_CHAR}, + {'C', LYE_CHAR}, + {'D', LYE_CHAR}, + {'E', LYE_CHAR}, + {'F', LYE_CHAR}, + {'G', LYE_CHAR}, + {'H', LYE_CHAR}, + {'I', LYE_CHAR}, + {'J', LYE_CHAR}, + {'K', LYE_CHAR}, + {'L', LYE_CHAR}, + {'M', LYE_CHAR}, + {'N', LYE_CHAR}, + {'O', LYE_CHAR}, + {'P', LYE_CHAR}, + {'Q', LYE_CHAR}, + {'R', LYE_CHAR}, + {'S', LYE_CHAR}, + {'T', LYE_CHAR}, + {'U', LYE_CHAR}, + {'V', LYE_CHAR}, + {'W', LYE_CHAR}, + {'X', LYE_CHAR}, + {'Y', LYE_CHAR}, + {'Z', LYE_CHAR}, + {'[', LYE_CHAR}, + {'\\', LYE_CHAR}, + {']', LYE_CHAR}, + {'^', LYE_CHAR}, + {'_', LYE_CHAR}, + {'`', LYE_CHAR}, + {'a', LYE_CHAR}, + {'b', LYE_CHAR}, + {'c', LYE_CHAR}, + {'d', LYE_CHAR}, + {'e', LYE_CHAR}, + {'f', LYE_CHAR}, + {'g', LYE_CHAR}, + {'h', LYE_CHAR}, + {'i', LYE_CHAR}, + {'j', LYE_CHAR}, + {'k', LYE_CHAR}, + {'l', LYE_CHAR}, + {'m', LYE_CHAR}, + {'n', LYE_CHAR}, + {'o', LYE_CHAR}, + {'p', LYE_CHAR}, + {'q', LYE_CHAR}, + {'r', LYE_CHAR}, + {'s', LYE_CHAR}, + {'t', LYE_CHAR}, + {'u', LYE_CHAR}, + {'v', LYE_CHAR}, + {'w', LYE_CHAR}, + {'x', LYE_CHAR}, + {'y', LYE_CHAR}, + {'z', LYE_CHAR}, + {'{', LYE_CHAR}, + {'|', LYE_CHAR}, + {'}', LYE_CHAR}, + {'~', LYE_CHAR}, + {DEL_KEY, LYE_DELP}, + {128, LYE_CHAR}, + {129, LYE_CHAR}, + {130, LYE_CHAR}, + {131, LYE_CHAR}, + {132, LYE_CHAR}, + {133, LYE_CHAR}, + {134, LYE_CHAR}, + {135, LYE_CHAR}, + {136, LYE_CHAR}, + {137, LYE_CHAR}, + {138, LYE_CHAR}, + {139, LYE_CHAR}, + {140, LYE_CHAR}, + {141, LYE_CHAR}, +#ifdef CJK_EX /* 1997/11/03 (Mon) 20:30:54 */ + {142, LYE_CHAR}, +#else + {142, LYE_AIX}, +#endif + {143, LYE_CHAR}, + {144, LYE_CHAR}, + {145, LYE_CHAR}, + {146, LYE_CHAR}, + {147, LYE_CHAR}, + {148, LYE_CHAR}, + {149, LYE_CHAR}, + {150, LYE_CHAR}, + {151, LYE_CHAR}, + {152, LYE_CHAR}, + {153, LYE_CHAR}, + {154, LYE_CHAR}, + {155, LYE_CHAR}, + {156, LYE_CHAR}, + {157, LYE_CHAR}, + {158, LYE_CHAR}, + {159, LYE_CHAR}, + {160, LYE_CHAR}, + {161, LYE_CHAR}, + {162, LYE_CHAR}, + {163, LYE_CHAR}, + {164, LYE_CHAR}, + {165, LYE_CHAR}, + {166, LYE_CHAR}, + {167, LYE_CHAR}, + {168, LYE_CHAR}, + {169, LYE_CHAR}, + {170, LYE_CHAR}, + {171, LYE_CHAR}, + {172, LYE_CHAR}, + {173, LYE_CHAR}, + {174, LYE_CHAR}, + {175, LYE_CHAR}, + {176, LYE_CHAR}, + {177, LYE_CHAR}, + {178, LYE_CHAR}, + {179, LYE_CHAR}, + {180, LYE_CHAR}, + {181, LYE_CHAR}, + {182, LYE_CHAR}, + {183, LYE_CHAR}, + {184, LYE_CHAR}, + {185, LYE_CHAR}, + {186, LYE_CHAR}, + {187, LYE_CHAR}, + {188, LYE_CHAR}, + {189, LYE_CHAR}, + {190, LYE_CHAR}, + {191, LYE_CHAR}, + {192, LYE_CHAR}, + {193, LYE_CHAR}, + {194, LYE_CHAR}, + {195, LYE_CHAR}, + {196, LYE_CHAR}, + {197, LYE_CHAR}, + {198, LYE_CHAR}, + {199, LYE_CHAR}, + {200, LYE_CHAR}, + {201, LYE_CHAR}, + {202, LYE_CHAR}, + {203, LYE_CHAR}, + {204, LYE_CHAR}, + {205, LYE_CHAR}, + {206, LYE_CHAR}, + {207, LYE_CHAR}, + {208, LYE_CHAR}, + {209, LYE_CHAR}, + {210, LYE_CHAR}, + {211, LYE_CHAR}, + {212, LYE_CHAR}, + {213, LYE_CHAR}, + {214, LYE_CHAR}, + {215, LYE_CHAR}, + {216, LYE_CHAR}, + {217, LYE_CHAR}, + {218, LYE_CHAR}, + {219, LYE_CHAR}, + {220, LYE_CHAR}, + {221, LYE_CHAR}, + {222, LYE_CHAR}, + {223, LYE_CHAR}, + {224, LYE_CHAR}, + {225, LYE_CHAR}, + {226, LYE_CHAR}, + {227, LYE_CHAR}, + {228, LYE_CHAR}, + {229, LYE_CHAR}, + {230, LYE_CHAR}, + {231, LYE_CHAR}, + {232, LYE_CHAR}, + {233, LYE_CHAR}, + {234, LYE_CHAR}, + {235, LYE_CHAR}, + {236, LYE_CHAR}, + {237, LYE_CHAR}, + {238, LYE_CHAR}, + {239, LYE_CHAR}, + {240, LYE_CHAR}, + {241, LYE_CHAR}, + {242, LYE_CHAR}, + {243, LYE_CHAR}, + {244, LYE_CHAR}, + {245, LYE_CHAR}, + {246, LYE_CHAR}, + {247, LYE_CHAR}, + {248, LYE_CHAR}, + {249, LYE_CHAR}, + {250, LYE_CHAR}, + {251, LYE_CHAR}, + {252, LYE_CHAR}, + {253, LYE_CHAR}, + {254, LYE_CHAR}, + {255, LYE_CHAR}, + {UPARROW_KEY, LYE_FORM_PASS}, + {DNARROW_KEY, LYE_FORM_PASS}, + {RTARROW_KEY, LYE_FORW}, + {LTARROW_KEY, LYE_BACK}, + {PGDOWN_KEY, LYE_FORM_PASS}, + {PGUP_KEY, LYE_FORM_PASS}, + {HOME_KEY, LYE_BOL}, + {END_KEY, LYE_EOL}, + {F1_KEY, LYE_FORM_PASS}, +#if !(defined(_WINDOWS) || defined(__DJGPP__)) + {DO_KEY, LYE_TAB}, + {FIND_KEY, LYE_BOL}, + {SELECT_KEY, LYE_EOL}, +#endif + {REMOVE_KEY, LYE_DELP}, + {BACKTAB_KEY, LYE_FORM_PASS}, +#if (defined(_WINDOWS) || defined(__DJGPP__)) && defined(USE_SLANG) && !defined(DJGPP_KEYHANDLER) + {272, LYE_DELP}, + {273, LYE_ENTER}, +#endif + {-1, LYE_UNKNOWN} +}; + +#ifdef USE_ALT_BINDINGS +static const LYEditInit initBetterEditor[] = +{ + {CTL('A'), LYE_BOL}, + {CTL('B'), LYE_BACK}, + {CTL('C'), LYE_ABORT}, + {CTL('D'), LYE_DELN}, + {CTL('E'), LYE_EOL}, + {CTL('F'), LYE_FORW}, + {CTL('G'), LYE_ABORT}, + {CTL('H'), LYE_DELP}, + {CTL('I'), LYE_ENTER}, + {CTL('J'), LYE_ENTER}, + {CTL('K'), LYE_DELEL}, + {CTL('M'), LYE_ENTER}, + {CTL('N'), LYE_FORWW}, + {CTL('O'), LYE_ABORT}, + {CTL('P'), LYE_BACKW}, + {CTL('R'), LYE_DELPW}, + {CTL('T'), LYE_DELNW}, + {CTL('U'), LYE_ERASE}, + {CTL('V'), LYE_LKCMD}, +#ifdef CAN_CUT_AND_PASTE + {CTL('W'), LYE_PASTE}, +#endif + {CTL('X'), LYE_SETM1}, + {CTL('^'), LYE_UPPER}, + {CTL('_'), LYE_LOWER}, + {' ', LYE_CHAR}, + {'!', LYE_CHAR}, + {'"', LYE_CHAR}, + {'#', LYE_CHAR}, + {'$', LYE_CHAR}, + {'%', LYE_CHAR}, + {'&', LYE_CHAR}, + {'\'', LYE_CHAR}, + {'(', LYE_CHAR}, + {')', LYE_CHAR}, + {'*', LYE_CHAR}, + {'+', LYE_CHAR}, + {',', LYE_CHAR}, + {'-', LYE_CHAR}, + {'.', LYE_CHAR}, + {'/', LYE_CHAR}, + {'0', LYE_CHAR}, + {'1', LYE_CHAR}, + {'2', LYE_CHAR}, + {'3', LYE_CHAR}, + {'4', LYE_CHAR}, + {'5', LYE_CHAR}, + {'6', LYE_CHAR}, + {'7', LYE_CHAR}, + {'8', LYE_CHAR}, + {'9', LYE_CHAR}, + {':', LYE_CHAR}, + {';', LYE_CHAR}, + {'<', LYE_CHAR}, + {'=', LYE_CHAR}, + {'>', LYE_CHAR}, + {'?', LYE_CHAR}, + {'@', LYE_CHAR}, + {'A', LYE_CHAR}, + {'B', LYE_CHAR}, + {'C', LYE_CHAR}, + {'D', LYE_CHAR}, + {'E', LYE_CHAR}, + {'F', LYE_CHAR}, + {'G', LYE_CHAR}, + {'H', LYE_CHAR}, + {'I', LYE_CHAR}, + {'J', LYE_CHAR}, + {'K', LYE_CHAR}, + {'L', LYE_CHAR}, + {'M', LYE_CHAR}, + {'N', LYE_CHAR}, + {'O', LYE_CHAR}, + {'P', LYE_CHAR}, + {'Q', LYE_CHAR}, + {'R', LYE_CHAR}, + {'S', LYE_CHAR}, + {'T', LYE_CHAR}, + {'U', LYE_CHAR}, + {'V', LYE_CHAR}, + {'W', LYE_CHAR}, + {'X', LYE_CHAR}, + {'Y', LYE_CHAR}, + {'Z', LYE_CHAR}, + {'[', LYE_CHAR}, + {'\\', LYE_CHAR}, + {']', LYE_CHAR}, + {'^', LYE_CHAR}, + {'_', LYE_CHAR}, + {'`', LYE_CHAR}, + {'a', LYE_CHAR}, + {'b', LYE_CHAR}, + {'c', LYE_CHAR}, + {'d', LYE_CHAR}, + {'e', LYE_CHAR}, + {'f', LYE_CHAR}, + {'g', LYE_CHAR}, + {'h', LYE_CHAR}, + {'i', LYE_CHAR}, + {'j', LYE_CHAR}, + {'k', LYE_CHAR}, + {'l', LYE_CHAR}, + {'m', LYE_CHAR}, + {'n', LYE_CHAR}, + {'o', LYE_CHAR}, + {'p', LYE_CHAR}, + {'q', LYE_CHAR}, + {'r', LYE_CHAR}, + {'s', LYE_CHAR}, + {'t', LYE_CHAR}, + {'u', LYE_CHAR}, + {'v', LYE_CHAR}, + {'w', LYE_CHAR}, + {'x', LYE_CHAR}, + {'y', LYE_CHAR}, + {'z', LYE_CHAR}, + {'{', LYE_CHAR}, + {'|', LYE_CHAR}, + {'}', LYE_CHAR}, + {'~', LYE_CHAR}, + {DEL_KEY, LYE_DELP}, + {128, LYE_CHAR}, + {129, LYE_CHAR}, + {130, LYE_CHAR}, + {131, LYE_CHAR}, + {132, LYE_CHAR}, + {133, LYE_CHAR}, + {134, LYE_CHAR}, + {135, LYE_CHAR}, + {136, LYE_CHAR}, + {137, LYE_CHAR}, + {138, LYE_CHAR}, + {139, LYE_CHAR}, + {140, LYE_CHAR}, + {141, LYE_CHAR}, +#ifdef CJK_EX /* 1997/11/03 (Mon) 20:30:54 */ + {142, LYE_CHAR}, +#else + {142, LYE_AIX}, +#endif + {143, LYE_CHAR}, + {144, LYE_CHAR}, + {145, LYE_CHAR}, + {146, LYE_CHAR}, + {147, LYE_CHAR}, + {148, LYE_CHAR}, + {149, LYE_CHAR}, + {150, LYE_CHAR}, + {151, LYE_CHAR}, + {152, LYE_CHAR}, + {153, LYE_CHAR}, + {154, LYE_CHAR}, + {155, LYE_CHAR}, + {156, LYE_CHAR}, + {157, LYE_CHAR}, + {158, LYE_CHAR}, + {159, LYE_CHAR}, + {160, LYE_CHAR}, + {161, LYE_CHAR}, + {162, LYE_CHAR}, + {163, LYE_CHAR}, + {164, LYE_CHAR}, + {165, LYE_CHAR}, + {166, LYE_CHAR}, + {167, LYE_CHAR}, + {168, LYE_CHAR}, + {169, LYE_CHAR}, + {170, LYE_CHAR}, + {171, LYE_CHAR}, + {172, LYE_CHAR}, + {173, LYE_CHAR}, + {174, LYE_CHAR}, + {175, LYE_CHAR}, + {176, LYE_CHAR}, + {177, LYE_CHAR}, + {178, LYE_CHAR}, + {179, LYE_CHAR}, + {180, LYE_CHAR}, + {181, LYE_CHAR}, + {182, LYE_CHAR}, + {183, LYE_CHAR}, + {184, LYE_CHAR}, + {185, LYE_CHAR}, + {186, LYE_CHAR}, + {187, LYE_CHAR}, + {188, LYE_CHAR}, + {189, LYE_CHAR}, + {190, LYE_CHAR}, + {191, LYE_CHAR}, + {192, LYE_CHAR}, + {193, LYE_CHAR}, + {194, LYE_CHAR}, + {195, LYE_CHAR}, + {196, LYE_CHAR}, + {197, LYE_CHAR}, + {198, LYE_CHAR}, + {199, LYE_CHAR}, + {200, LYE_CHAR}, + {201, LYE_CHAR}, + {202, LYE_CHAR}, + {203, LYE_CHAR}, + {204, LYE_CHAR}, + {205, LYE_CHAR}, + {206, LYE_CHAR}, + {207, LYE_CHAR}, + {208, LYE_CHAR}, + {209, LYE_CHAR}, + {210, LYE_CHAR}, + {211, LYE_CHAR}, + {212, LYE_CHAR}, + {213, LYE_CHAR}, + {214, LYE_CHAR}, + {215, LYE_CHAR}, + {216, LYE_CHAR}, + {217, LYE_CHAR}, + {218, LYE_CHAR}, + {219, LYE_CHAR}, + {220, LYE_CHAR}, + {221, LYE_CHAR}, + {222, LYE_CHAR}, + {223, LYE_CHAR}, + {224, LYE_CHAR}, + {225, LYE_CHAR}, + {226, LYE_CHAR}, + {227, LYE_CHAR}, + {228, LYE_CHAR}, + {229, LYE_CHAR}, + {230, LYE_CHAR}, + {231, LYE_CHAR}, + {232, LYE_CHAR}, + {233, LYE_CHAR}, + {234, LYE_CHAR}, + {235, LYE_CHAR}, + {236, LYE_CHAR}, + {237, LYE_CHAR}, + {238, LYE_CHAR}, + {239, LYE_CHAR}, + {240, LYE_CHAR}, + {241, LYE_CHAR}, + {242, LYE_CHAR}, + {243, LYE_CHAR}, + {244, LYE_CHAR}, + {245, LYE_CHAR}, + {246, LYE_CHAR}, + {247, LYE_CHAR}, + {248, LYE_CHAR}, + {249, LYE_CHAR}, + {250, LYE_CHAR}, + {251, LYE_CHAR}, + {252, LYE_CHAR}, + {253, LYE_CHAR}, + {254, LYE_CHAR}, + {255, LYE_CHAR}, + {UPARROW_KEY, LYE_FORM_PASS}, + {DNARROW_KEY, LYE_FORM_PASS}, + {RTARROW_KEY, LYE_FORW}, + {LTARROW_KEY, LYE_BACK}, + {PGDOWN_KEY, LYE_FORM_PASS}, + {PGUP_KEY, LYE_FORM_PASS}, + {HOME_KEY, LYE_BOL}, + {END_KEY, LYE_EOL}, + {F1_KEY, LYE_FORM_PASS}, +#if !(defined(_WINDOWS) || defined(__DJGPP__)) + {DO_KEY, LYE_TAB}, + {FIND_KEY, LYE_BOL}, + {SELECT_KEY, LYE_EOL}, +#endif + {REMOVE_KEY, LYE_DELP}, + {BACKTAB_KEY, LYE_FORM_PASS}, +#if (defined(_WINDOWS) || defined(__DJGPP__)) && defined(USE_SLANG) && !defined(DJGPP_KEYHANDLER) + {272, LYE_DELP}, + {273, LYE_ENTER}, +#endif + {-1, LYE_UNKNOWN} +}; + +static const LYEditInit initBashlikeEditor[] = +{ + {CTL('@'), LYE_SETMARK}, + {CTL('A'), LYE_BOL}, + {CTL('B'), LYE_BACK}, + {CTL('C'), LYE_ABORT}, + {CTL('D'), LYE_DELN}, + {CTL('E'), LYE_EOL | LYE_DF}, + {CTL('F'), LYE_FORW}, + {CTL('G'), LYE_ABORT}, + {CTL('H'), LYE_DELP}, + {CTL('I'), LYE_TAB}, + {CTL('J'), LYE_ENTER}, + {CTL('K'), LYE_DELEL | LYE_DF}, + {CTL('L'), LYE_FORM_PASS}, + {CTL('M'), LYE_ENTER}, + {CTL('N'), LYE_FORM_PASS}, + {CTL('O'), LYE_FORM_PASS}, + {CTL('P'), LYE_FORM_PASS}, + {CTL('R'), LYE_BACKW}, + {CTL('S'), LYE_FORWW}, + {CTL('T'), LYE_TPOS}, + {CTL('U'), LYE_DELBL}, + {CTL('V'), LYE_LKCMD}, + {CTL('W'), LYE_DELPW}, + {CTL('X'), LYE_SETM1}, + {CTL('Y'), LYE_YANK}, + {CTL('Z'), LYE_FORM_PASS}, + {CTL('['), LYE_SETM2}, + {CTL('\\'), LYE_FORM_PASS}, + {CTL(']'), LYE_FORM_PASS}, + {CTL('^'), LYE_SWMAP}, + {CTL('_'), LYE_ABORT}, + {' ', LYE_CHAR}, + {'!', LYE_CHAR}, + {'"', LYE_CHAR}, + {'#', LYE_CHAR}, + {'$', LYE_CHAR}, + {'%', LYE_CHAR}, + {'&', LYE_CHAR}, + {'\'', LYE_CHAR}, + {'(', LYE_CHAR}, + {')', LYE_CHAR}, + {'*', LYE_CHAR}, + {'+', LYE_CHAR}, + {',', LYE_CHAR}, + {'-', LYE_CHAR}, + {'.', LYE_CHAR}, + {'/', LYE_CHAR}, + {'0', LYE_CHAR}, + {'1', LYE_CHAR}, + {'2', LYE_CHAR}, + {'3', LYE_CHAR}, + {'4', LYE_CHAR}, + {'5', LYE_CHAR}, + {'6', LYE_CHAR}, + {'7', LYE_CHAR}, + {'8', LYE_CHAR}, + {'9', LYE_CHAR}, + {':', LYE_CHAR}, + {';', LYE_CHAR}, + {'<', LYE_CHAR}, + {'=', LYE_CHAR}, + {'>', LYE_CHAR}, + {'?', LYE_CHAR}, + {'@', LYE_CHAR}, + {'A', LYE_CHAR}, + {'B', LYE_CHAR}, + {'C', LYE_CHAR}, + {'D', LYE_CHAR}, + {'E', LYE_CHAR}, + {'F', LYE_CHAR}, + {'G', LYE_CHAR}, + {'H', LYE_CHAR}, + {'I', LYE_CHAR}, + {'J', LYE_CHAR}, + {'K', LYE_CHAR}, + {'L', LYE_CHAR}, + {'M', LYE_CHAR}, + {'N', LYE_CHAR}, + {'O', LYE_CHAR}, + {'P', LYE_CHAR}, + {'Q', LYE_CHAR}, + {'R', LYE_CHAR}, + {'S', LYE_CHAR}, + {'T', LYE_CHAR}, + {'U', LYE_CHAR}, + {'V', LYE_CHAR}, + {'W', LYE_CHAR}, + {'X', LYE_CHAR}, + {'Y', LYE_CHAR}, + {'Z', LYE_CHAR}, + {'[', LYE_CHAR}, + {'\\', LYE_CHAR}, + {']', LYE_CHAR}, + {'^', LYE_CHAR}, + {'_', LYE_CHAR}, + {'`', LYE_CHAR}, + {'a', LYE_CHAR}, + {'b', LYE_CHAR}, + {'c', LYE_CHAR}, + {'d', LYE_CHAR}, + {'e', LYE_CHAR}, + {'f', LYE_CHAR}, + {'g', LYE_CHAR}, + {'h', LYE_CHAR}, + {'i', LYE_CHAR}, + {'j', LYE_CHAR}, + {'k', LYE_CHAR}, + {'l', LYE_CHAR}, + {'m', LYE_CHAR}, + {'n', LYE_CHAR}, + {'o', LYE_CHAR}, + {'p', LYE_CHAR}, + {'q', LYE_CHAR}, + {'r', LYE_CHAR}, + {'s', LYE_CHAR}, + {'t', LYE_CHAR}, + {'u', LYE_CHAR}, + {'v', LYE_CHAR}, + {'w', LYE_CHAR}, + {'x', LYE_CHAR}, + {'y', LYE_CHAR}, + {'z', LYE_CHAR}, + {'{', LYE_CHAR}, + {'|', LYE_CHAR}, + {'}', LYE_CHAR}, + {'~', LYE_CHAR}, + {DEL_KEY, LYE_DELP}, + {128, LYE_CHAR}, + {129, LYE_CHAR}, + {130, LYE_CHAR}, + {131, LYE_CHAR}, + {132, LYE_CHAR}, + {133, LYE_CHAR}, + {134, LYE_CHAR}, + {135, LYE_CHAR}, + {136, LYE_CHAR}, + {137, LYE_CHAR}, + {138, LYE_CHAR}, + {139, LYE_CHAR}, + {140, LYE_CHAR}, + {141, LYE_CHAR}, + {142, LYE_CHAR}, + {143, LYE_CHAR}, + {144, LYE_CHAR}, + {145, LYE_CHAR}, + {146, LYE_CHAR}, + {147, LYE_CHAR}, + {148, LYE_CHAR}, + {149, LYE_CHAR}, + {150, LYE_CHAR}, + {151, LYE_AIX}, + {152, LYE_CHAR}, + {153, LYE_CHAR}, + {154, LYE_CHAR}, + {155, LYE_CHAR}, + {156, LYE_CHAR}, + {157, LYE_CHAR}, + {158, LYE_CHAR}, + {159, LYE_CHAR}, + {160, LYE_CHAR}, + {161, LYE_CHAR}, + {162, LYE_CHAR}, + {163, LYE_CHAR}, + {164, LYE_CHAR}, + {165, LYE_CHAR}, + {166, LYE_CHAR}, + {167, LYE_CHAR}, + {168, LYE_CHAR}, + {169, LYE_CHAR}, + {170, LYE_CHAR}, + {171, LYE_CHAR}, + {172, LYE_CHAR}, + {173, LYE_CHAR}, + {174, LYE_CHAR}, + {175, LYE_CHAR}, + {176, LYE_CHAR}, + {177, LYE_CHAR}, + {178, LYE_CHAR}, + {179, LYE_CHAR}, + {180, LYE_CHAR}, + {181, LYE_CHAR}, + {182, LYE_CHAR}, + {183, LYE_CHAR}, + {184, LYE_CHAR}, + {185, LYE_CHAR}, + {186, LYE_CHAR}, + {187, LYE_CHAR}, + {188, LYE_CHAR}, + {189, LYE_CHAR}, + {190, LYE_CHAR}, + {191, LYE_CHAR}, + {192, LYE_CHAR}, + {193, LYE_CHAR}, + {194, LYE_CHAR}, + {195, LYE_CHAR}, + {196, LYE_CHAR}, + {197, LYE_CHAR}, + {198, LYE_CHAR}, + {199, LYE_CHAR}, + {200, LYE_CHAR}, + {201, LYE_CHAR}, + {202, LYE_CHAR}, + {203, LYE_CHAR}, + {204, LYE_CHAR}, + {205, LYE_CHAR}, + {206, LYE_CHAR}, + {207, LYE_CHAR}, + {208, LYE_CHAR}, + {209, LYE_CHAR}, + {210, LYE_CHAR}, + {211, LYE_CHAR}, + {212, LYE_CHAR}, + {213, LYE_CHAR}, + {214, LYE_CHAR}, + {215, LYE_CHAR}, + {216, LYE_CHAR}, + {217, LYE_CHAR}, + {218, LYE_CHAR}, + {219, LYE_CHAR}, + {220, LYE_CHAR}, + {221, LYE_CHAR}, + {222, LYE_CHAR}, + {223, LYE_CHAR}, + {224, LYE_CHAR}, + {225, LYE_CHAR}, + {226, LYE_CHAR}, + {227, LYE_CHAR}, + {228, LYE_CHAR}, + {229, LYE_CHAR}, + {230, LYE_CHAR}, + {231, LYE_CHAR}, + {232, LYE_CHAR}, + {233, LYE_CHAR}, + {234, LYE_CHAR}, + {235, LYE_CHAR}, + {236, LYE_CHAR}, + {237, LYE_CHAR}, + {238, LYE_CHAR}, + {239, LYE_CHAR}, + {240, LYE_CHAR}, + {241, LYE_CHAR}, + {242, LYE_CHAR}, + {243, LYE_CHAR}, + {244, LYE_CHAR}, + {245, LYE_CHAR}, + {246, LYE_CHAR}, + {247, LYE_CHAR}, + {248, LYE_CHAR}, + {249, LYE_CHAR}, + {250, LYE_CHAR}, + {251, LYE_CHAR}, + {252, LYE_CHAR}, + {253, LYE_CHAR}, + {254, LYE_CHAR}, + {255, LYE_CHAR}, + {UPARROW_KEY, LYE_FORM_PASS}, + {DNARROW_KEY, LYE_FORM_PASS}, + {RTARROW_KEY, LYE_FORW}, + {LTARROW_KEY, LYE_BACK}, + {PGDOWN_KEY, LYE_FORM_PASS}, + {PGUP_KEY, LYE_FORM_PASS}, + {HOME_KEY, LYE_BOL}, + {END_KEY, LYE_EOL}, + {F1_KEY, LYE_FORM_PASS}, +#if !(defined(_WINDOWS) || defined(__DJGPP__)) + {DO_KEY, LYE_TAB}, + {FIND_KEY, LYE_BOL}, + {SELECT_KEY, LYE_EOL}, +#endif + {REMOVE_KEY, LYE_DELN}, + {BACKTAB_KEY, LYE_FORM_PASS}, +#if (defined(_WINDOWS) || defined(__DJGPP__)) && defined(USE_SLANG) && !defined(DJGPP_KEYHANDLER) + {272, LYE_DELP}, + {273, LYE_ENTER}, +#endif + {-1, LYE_UNKNOWN} +}; +#endif /* USE_ALT_BINDINGS */ + +LYEditConfig LYLineEditors[] = +{ + {"Default Binding", initDefaultEditor, DefaultEditBinding}, +#ifdef USE_ALT_BINDINGS + {"Alternate Bindings", initBetterEditor, BetterEditBinding}, + {"Bash-like Bindings", initBashlikeEditor, BashlikeEditBinding}, +#endif +}; + +const char *LYEditorNames[TABLESIZE(LYLineEditors) + 1]; + +/* + * 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 USE_ALT_BINDINGS + ALT_EDIT_HELP, + BASHLIKE_EDIT_HELP, +#endif + (char *) 0 +}; + +static struct emap *name2emap(const char *name) +{ + struct emap *mp; + struct emap *result = 0; + + if (non_empty(name)) { + for (mp = ekmap; mp->name != NULL; mp++) { + if (strcasecomp(mp->name, name) == 0) { + result = mp; + break; + } + } + } + return result; +} + +static struct emap *code2emap(int code) +{ + struct emap *mp; + struct emap *result = 0; + + for (mp = ekmap; mp->name != NULL; mp++) { + if (mp->code == code) { + result = mp; + break; + } + } + return result; +} + +/* + * 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) +{ + struct emap *mp; + int result = -1; + + if ((mp = name2emap(func)) != 0) { + result = mp->code; + } + return result; +} + +const char *lec_to_lecname(int code) +{ + struct emap *mp; + const char *result = 0; + + if ((mp = code2emap(code)) != 0) { + result = mp->name; + } + return result; +} + +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 USE_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)) { + LYEditCode code = (LYEditCode) lec; + +#ifdef USE_ALT_BINDINGS + if (xlkc & LKC_MOD1) { + if (c <= LAST_MOD1_LKC) { + Mod1Binding[c] = code; + success = TRUE; + } + } else if (xlkc & LKC_MOD2) { + if (c <= LAST_MOD2_LKC) { + Mod2Binding[c] = code; + success = TRUE; + } + } else if (xlkc & LKC_MOD3) { + if (c <= LAST_MOD3_LKC) { + Mod3Binding[c] = code; + success = TRUE; + } + } else +#endif /* USE_ALT_BINDINGS */ + { +#ifndef UCHAR_MAX +#define UCHAR_MAX 255 +#endif + if ((unsigned int) lec <= UCHAR_MAX) { + if (select_edi > 0) { + if ((unsigned int) select_edi < TABLESIZE(LYLineEditors)) { + LYLineEditors[select_edi - 1].used[c] = code; + success = TRUE; + } + } else { + for (j = 0; j < (int) TABLESIZE(LYLineEditors); j++) { + success = TRUE; + if ((select_edi < 0) && ((j + 1 + select_edi) == 0)) + continue; + LYLineEditors[j].used[c] = code; + } + } + } + } + } + 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 - 1)) { + 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 - 1)) { + 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 USE_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 /* USE_ALT_BINDINGS */ + if (pmodkey) + *pmodkey = -1; + return (-1); +} + +#if 0 +/* + * This function was useful in converting the hand-crafted key-bindings to + * their reusable form in 2.8.8 -TD + */ +static void checkEditMap(LYEditConfig * table) +{ + unsigned j, k; + char comment[80]; + int first = TRUE; + + for (j = 0; table->init[j].code >= 0; ++j) { + int code = table->init[j].code; + + if (table->init[j].edit != table->used[code]) { + if (first) { + printf("TABLE %s\n", table->name); + first = FALSE; + } + printf("%u: init %d vs used %d\n", + j, + table->init[j].edit, + table->used[code]); + } + } + for (j = 0; j < KEYMAP_SIZE; ++j) { + int code = (int) j; + BOOL found = FALSE; + + for (k = 0; table->init[k].code >= 0; ++k) { + if (code == table->init[k].code) { + found = TRUE; + break; + } + } + if (!found) { + if (table->used[j] != 0) { + int edit = table->used[j]; + int has_DF = (edit & LYE_DF); + int has_LAC = (edit & LYE_FORM_LAC); + const char *prefix = "LYE_"; + const char *name = 0; + + edit &= 0x7f; + if (has_LAC) { + Kcmd *cmd = LYKeycodeToKcmd(edit); + + if (cmd != 0) { + prefix = "LYK_"; + name = cmd->name; + } + } else { + name = lec_to_lecname(edit); + } + + if (j < 32) { + char temp[80]; + const char *what = 0; + + switch (j) { + case 0: + what = "nul"; + break; + case 17: + what = "XON"; + break; + case 19: + what = "XOFF"; + break; + default: + sprintf(temp, "^%c", j + 'A'); + what = temp; + break; + } + sprintf(comment, "\t/* %s */", what); + } else if (j < 127) { + sprintf(comment, "\t/* %c */", j); + } else { + const char *what = LYextraKeysToName(j); + + if (Non_Empty(what)) { + sprintf(comment, "\t/* %s%s */", what, + ((StrChr(what, '_') != 0) + ? "" + : "_KEY")); + } else { + strcpy(comment, ""); + } + } + if (name == 0) { + name = "XXX"; + } else if (!strcasecomp(name, "PASS")) { + name = "FORM_PASS"; + } + if (first) { + printf("TABLE %s\n", table->name); + first = FALSE; + } + printf("\t{ %d, %s%s%s%s },%s\n", code, prefix, name, + has_DF ? "|LYE_DF" : "", + has_LAC ? "|LYE_FORM_LAC" : "", + comment); + } + } + } +} + +#else +#define checkEditMap(table) /* nothing */ +#endif + +static void initLineEditor(LYEditConfig * table) +{ + unsigned k; + LYEditCode *used = table->used; + const LYEditInit *init = table->init; + + memset(used, 0, sizeof(LYEditCode) * KEYMAP_SIZE); + + for (k = 0; init[k].code >= 0; ++k) { + int code = init[k].code; + + used[code] = init[k].edit; + } + checkEditMap(table); +} + +/* + * Reset the editor bindings to their default values. + */ +void LYinitEditmap(void) +{ + unsigned j; + + for (j = 0; j < TABLESIZE(LYLineEditors); ++j) { + LYEditorNames[j] = LYLineEditors[j].name; + initLineEditor(&LYLineEditors[j]); + } +#ifdef USE_ALT_BINDINGS + for (j = 0; j < TABLESIZE(LYModifierBindings); ++j) { + initLineEditor(&LYModifierBindings[j]); + } +#endif +} + +static char *showRanges(int *state) +{ + char *result = 0; + int range[2]; + int i; + + range[0] = range[1] = -1; + for (i = 0; i < KEYMAP_SIZE; ++i) { + if (!state[i]) { + int code = CurrentLineEditor()[i]; + + if (code == LYE_CHAR) { + if (range[0] < 0) + range[0] = i; + range[1] = i; + state[i] = 3; + } else if (range[0] >= 0) { + if (non_empty(result)) + StrAllocCat(result, ", "); + HTSprintf(&result, "%d-%d", range[0], range[1]); + range[0] = range[1] = -1; + } + } + } + return result; +} + +static int LYLoadEditmap(const char *arg GCC_UNUSED, + HTParentAnchor *anAnchor, + HTFormat format_out, + HTStream *sink) +{ +#define FORMAT " %-*s %-*s - %s\n" + HTFormat format_in = WWW_HTML; + HTStream *target; + int state[KEYMAP_SIZE]; + int width[2]; + char *buf = 0; + char *ranges = 0; + struct emap *mp; + int i; + int hanging; + int wrapped; + int had_output = FALSE; + int result; + + if ((target = HTStreamStack(format_in, format_out, sink, anAnchor)) != 0) { + anAnchor->no_cache = TRUE; + + HTSprintf0(&buf, + "<html>\n<head>\n<title>%s</title>\n</head>\n<body>\n", + CURRENT_EDITMAP_TITLE); + PUTS(buf); + HTSprintf0(&buf, "<pre>\n"); + PUTS(buf); + + /* determine the column-widths we will use for showing bindings */ + width[0] = 0; + width[1] = 0; + for (i = 0; i < KEYMAP_SIZE; ++i) { + int code = CurrentLineEditor()[i]; + + if (code == LYE_NOP) { + state[i] = 1; + } else { + int need; + + if ((mp = code2emap(code)) != 0) { + state[i] = 0; + if ((need = (int) strlen(mp->name)) > width[0]) + width[0] = need; + if ((need = (int) strlen(mp->descr)) > width[1]) + width[1] = need; + } else { + state[i] = 2; + } + } + } + hanging = 2 + width[0] + 2 + width[1] + 5; + wrapped = hanging; + + /* + * Tell which set of bindings we are showing, and link to the + * handcrafted page, which adds explanations. + */ + PUTS(gettext("These are the current edit-bindings:")); + HTSprintf0(&buf, + " <a href=\"%s\">%s</a>\n\n", + LYLineeditHelpURL(), + LYEditorNames[current_lineedit]); + PUTS(buf); + + /* Show by groups to match the arrangement in the handmade files. */ + for (mp = ekmap; mp->name != 0; ++mp) { + if (isEmpty(mp->name)) { + if (had_output) { + PUTS("\n"); + had_output = FALSE; + } + } else if (mp->code == LYE_CHAR) { + ranges = showRanges(state); + HTSprintf0(&buf, FORMAT, + width[0], mp->name, + width[1], mp->descr, + ranges); + FREE(ranges); + PUTS(buf); + had_output = TRUE; + } else { + for (i = 0; i < KEYMAP_SIZE; ++i) { + int code = CurrentLineEditor()[i]; + + if ((code == mp->code) && !state[i]) { + char *value = LYKeycodeToString(i, (i >= 160 && + i <= 255)); + int before = wrapped + (ranges ? ((int) + strlen(ranges)) : 0); + int after = before; + + if (non_empty(ranges)) { + StrAllocCat(ranges, ", "); + after += 2; + } + after += (int) strlen(value) + 2; + if ((before / LYcols) != (after / LYcols)) { + wrapped += (LYcols - (before % LYcols)); + HTSprintf(&ranges, "\n%-*s", hanging, " "); + } + StrAllocCat(ranges, value); + } + } + if (non_empty(ranges)) { + LYEntify(&ranges, TRUE); + HTSprintf0(&buf, FORMAT, + width[0], mp->name, + width[1], mp->descr, + ranges); + PUTS(buf); + FREE(ranges); + had_output = TRUE; + } + } + } + + HTSprintf0(&buf, "</pre>\n</body>\n</html>\n"); + PUTS(buf); + + (*target->isa->_free) (target); + result = HT_LOADED; + } else { + HTSprintf0(&buf, CANNOT_CONVERT_I_TO_O, + HTAtom_name(format_in), HTAtom_name(format_out)); + HTAlert(buf); + result = HT_NOT_LOADED; + } + FREE(ranges); + FREE(buf); + return result; +#undef FORMAT +} + +#ifdef GLOBALDEF_IS_MACRO +#define _LYEDITMAP_C_GLOBALDEF_1_INIT { "LYNXEDITMAP", LYLoadEditmap, 0} +GLOBALDEF(HTProtocol, LYLynxEditmap, _LYEDITMAP_C_GLOBALDEF_1_INIT); +#else +GLOBALDEF HTProtocol LYLynxEditmap = +{ + "LYNXEDITMAP", LYLoadEditmap, 0 +}; +#endif /* GLOBALDEF_IS_MACRO */ diff --git a/src/LYExtern.c b/src/LYExtern.c new file mode 100644 index 0000000..d36ba0b --- /dev/null +++ b/src/LYExtern.c @@ -0,0 +1,443 @@ +/* + * $LynxId: LYExtern.c,v 1.55 2018/02/15 01:53:07 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) ((UCH(x) >= '0' && UCH(x) <= '9') ? \ + (UCH(x) - '0') : (toupper(UCH(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(UCH(*(s + 1))) && isxdigit(UCH(*(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 (strncasecomp("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 (strncasecomp("telnet://", param, 9) == 0) { + char host[sizeof(pram_string)]; + int last_pos; + + LYStrNCpy(host, param + 9, sizeof(host)); + last_pos = (int) strlen(host) - 1; + if (last_pos > 1 && host[last_pos] == '/') + host[last_pos] = '\0'; + + format(&cmdbuf, command, host); + } else if (strncasecomp("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, + int only_overriders) +{ + int pass, num_disabled, num_matched, num_choices, cur_choice; + size_t length = 0; + char *cmdbuf = NULL; + char **actions = 0; + 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) { + actions[num_choices] = cmdbuf; + choices[num_choices] = + format_command(ptr->menu_name, param); + } + } + num_choices++; + } + } + } + if (length > 1) { + if (pass == 0) { + actions = typecallocn(char *, length + 1); + choices = typecallocn(char *, length + 1); + + if (actions == 0 || choices == 0) + outofmem(__FILE__, "lookup_external"); + } else { + actions[num_choices] = 0; + 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, + (STRING2PTR) 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 = actions[pass]; + } else { + FREE(actions[pass]); + } + FREE(choices[pass]); + } + } + + if (actions) { + for (pass = 0; actions[pass] != 0; ++pass) { + if (actions[pass] != cmdbuf) + FREE(actions[pass]); + } + FREE(actions); + } + + if (choices) { + for (pass = 0; choices[pass] != 0; ++pass) { + FREE(choices[pass]); + } + FREE(choices); + } + + return cmdbuf; +} + +BOOL run_external(char *param, + int 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 = (int) 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 (strncasecomp(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 0000000..89c84c9 --- /dev/null +++ b/src/LYExtern.h @@ -0,0 +1,16 @@ +/* $LynxId: LYExtern.h,v 1.14 2010/09/24 09:39:20 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, int only_overriders); +#ifdef __cplusplus +} +#endif +#endif /* EXTERNALS_H */ diff --git a/src/LYForms.c b/src/LYForms.c new file mode 100644 index 0000000..3f9c111 --- /dev/null +++ b/src/LYForms.c @@ -0,0 +1,1086 @@ +/* $LynxId: LYForms.c,v 1.119 2022/04/02 00:13:32 Paul.G.Fox 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 RepaintKey() 12 /* CTRL-L for repaint */ +#else +#define RepaintKey() ((!enable_scrollback) ? 23 : 12) /* CTRL-W or CTRL-L */ +#endif /* VMS && !USE_SLANG */ + +static int form_getstr(int cur, + int use_last_tfpos, + int 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); + + if (result == 0) + outofmem(__FILE__, "options_list"); + } else { + result[len] = 0; + } + } + + return result; +} + +int change_form_link_ex(int cur, + DocInfo *newdoc, + BOOLEAN *refresh_screen, + int use_last_tfpos, + int immediate_submit, + int redraw_only) +{ + FormInfo *form = links[cur].l_form; + char *link_name; + char *link_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); + } + link_name = form->name; + link_value = form->value; + 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 (FormIsReadonly(form)) + 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 (FormIsReadonly(form)) { + (void) LYhandlePopupList(form->num_value, + links[cur].ly, + links[cur].lx, + (STRING2PTR) my_data, + form->size, + form->size_l, + FormIsReadonly(form), + FALSE); + c = RepaintKey(); + break; + } + form->num_value = LYhandlePopupList(form->num_value, + links[cur].ly, + links[cur].lx, + (STRING2PTR) my_data, + form->size, + form->size_l, + FormIsReadonly(form), + 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; + } + c = RepaintKey(); + break; + + case F_RADIO_TYPE: + if (FormIsReadonly(form)) + 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 (FormIsReadonly(form)) + 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 (FormIsReadonly(form) && + (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_MOUSE_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_MOUSE_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 (FormIsReadonly(form)) + 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, + int use_last_tfpos, + int 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, + int use_last_tfpos, + int redraw_only) +{ + FormInfo *form = links[cur].l_form; + char *link_value = form->value; + int ch; + int far_col; + int startcol, startline; + int action, repeat; + int last_xlkc = -1; + +#ifdef SUPPORT_MULTIBYTE_EDIT + BOOL refresh_mb = TRUE; +#endif + + FieldEditor MyEdit, *edit = &MyEdit; + BOOLEAN Edited = FALSE; /* Value might be updated? */ + + /* + * Get the initial position of the cursor. + */ + LYGetYX(startline, startcol); + if (startline < 0) + startline = 0; + if (startcol < 0) + startcol = 0; + 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 + */ + if (form->maxlength != 0 && + strlen(form->value) > form->maxlength) { + /* + * We can't fit the entire value into the editing buffer, so enter as + * much of the tail as fits. - FM + */ + link_value += (strlen(form->value) - form->maxlength); + if (!FormIsReadonly(form) && + !(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(edit, link_value, form->maxlength, (far_col - startcol)); + edit->efPadChar = '_'; + edit->efIsMasked = (BOOL) (form->type == F_PASSWORD_TYPE); + if (use_last_tfpos && + LastTFPos >= 0 && + LastTFPos < (int) edit->efBufInUse) { +#if defined(TEXTFIELDS_MAY_NEED_ACTIVATION) && defined(INACTIVE_INPUT_STYLE_VH) + if (redraw_only) { + if (!((int) edit->efBufInUse >= edit->efWidth && + LastTFPos >= edit->efWidth - edit->efPanMargin)) { + edit->efEditAt = LastTFPos; + if ((int) edit->efBufInUse >= edit->efWidth) + textinput_redrawn = FALSE; + } + } else +#endif /* TEXTFIELDS_MAY_NEED_ACTIVATION && INACTIVE_INPUT_STYLE_VH */ + edit->efEditAt = LastTFPos; +#ifdef ENHANCED_LINEEDIT + if (edit->efEditAt == 0) + /* Do not show the region. */ + edit->efEditMark = -(int) (1 + edit->efBufInUse); +#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) { + edit->efEditAt = 0; + } + } + textinput_redrawn = FALSE; + } +#else + if (peek_mouse_levent()) { + if (!use_last_tfpos) + edit->efEditAt = 0; + } +#endif /* TEXTFIELDS_MAY_NEED_ACTIVATION && INACTIVE_INPUT_STYLE_VH */ + LYRefreshEdit(edit); + if (redraw_only) { + LYFinishEdit(edit); + return 0; /*return value won't be analysed */ + } +#ifdef FEPCTRL + fep_on(); +#endif + /* + * 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 + */ + edit->efIsDirty = TRUE; + } + } else +# endif /* NCURSES || PDCURSES */ +#endif /* USE_MOUSE */ + + { + if (!(ch & LKC_ISLECLAC)) + ch |= edit->efInputMods; + edit->efInputMods = 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. + */ + edit->efInputMods |= LKC_MOD1; + continue; + } + if (action == LYE_SETM2) { + /* + * Set flag for modifier 2. + */ + edit->efInputMods |= 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(edit, s, (int) (e1 - s), -1, TRUE); + s = e1; + if (*e1 == '\t') { /* Replace by space */ + LYEditInsert(edit, (unsigned const char *) " ", 1, + -1, TRUE); + s = ++e1; + } else + break; + } else + ++e1; + } + if (e1 > s) + LYEditInsert(edit, s, (int) (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(); + _statusline(ENTER_TEXT_ARROWS_OR_TAB); + if (strcmp(link_value, edit->efBuffer) != 0) { + Edited = TRUE; + } + if (buf) { + put_clip(buf); + FREE(buf); + ch = '\n'; /* Sometimes moves to the next line */ + break; + } + LYRefreshEdit(edit); + } else { + HTInfoMsg(gettext("Clipboard empty or Not text data.")); +#ifdef FEPCTRL + fep_off(); +#endif + 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) { +#ifdef FEPCTRL + fep_off(); +#endif + LYFinishEdit(edit); + return (DO_NOTHING); + } + if (action == LYE_STOP) { +#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION + textfields_need_activation = TRUE; + break; +#else +#ifdef ENHANCED_LINEEDIT + if (edit->efEditMark >= 0) + /* Disable. */ + edit->efEditMark = -(int) (1 + edit->efBufInUse); +#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 && edit->efEditAt == 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(edit->efBuffer, link_value)) { + c = HTConfirmDefault(PREV_DOC_QUERY, NO); + } + if (c == YES) { +#ifdef FEPCTRL + fep_off(); +#endif + LYFinishEdit(edit); + return (ch); + } else { + if (FormIsReadonly(form)) + _statusline(ARROWS_OR_TAB_TO_MOVE); + else + _statusline(ENTER_TEXT_ARROWS_OR_TAB); + } + } + if (FormIsReadonly(form)) { + /* + * 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 = LYDoEdit(edit, 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. */ + switch (action) { + case LYE_BACK_LL: + if (cur > 0 + && links[cur - 1].ly == links[cur].ly) { + goto breakfor; + } + break; + case LYE_FORW_RL: + if (cur >= 0 + && cur < nlinks - 1 + && links[cur + 1].ly == links[cur].ly) { + goto breakfor; + } + break; + default: + 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) { + LYDoEdit(edit, 0, LYE_DELP, TRUE); + } + } +#endif /* SUPPORT_MULTIBYTE_EDIT */ + } + _statusline(ENTER_TEXT_ARROWS_OR_TAB); + if (strcmp(link_value, edit->efBuffer)) { + Edited = TRUE; + } +#ifdef SUPPORT_MULTIBYTE_EDIT + if (refresh_mb) +#endif + LYRefreshEdit(edit); + LYSetLastTFPos(edit->efEditAt); + } + } + breakfor: + if (Edited) { + + /* + * Load the new value. + */ + if (link_value == form->value) { + /* + * The previous value did fit in the line buffer, so replace it + * with the new value. - FM + */ + StrAllocCopy(form->value, edit->efBuffer); + } else { + int old_len = (int) strlen(form->value); + int new_len = (int) strlen(link_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, edit->efBuffer); + 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; + } +#ifdef FEPCTRL + fep_off(); +#endif + LYFinishEdit(edit); + return (ch); +} + +#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION +#define TMA_PANEL(fp,np) ((for_what == FOR_PANEL) ? fp : np) +#else +#define TMA_PANEL(fp,np) np +#endif + +/* + * 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 (FormIsReadonly(form)) + statusline(FORM_LINK_PASSWORD_UNM_MSG); + else + statusline(TMA_PANEL(FORM_LINK_PASSWORD_MESSAGE_INA, + FORM_LINK_PASSWORD_MESSAGE)); + break; + case F_OPTION_LIST_TYPE: + if (FormIsReadonly(form)) { + statusline(FORM_LINK_OPTION_LIST_UNM_MSG); + } else if (fields_are_named()) { + char *submit_str = NULL; + + HTSprintf0(&submit_str, FORM_LINK_OPTION_LIST_ADV_MSG, form->name); + statusline(submit_str); + FREE(submit_str); + } else { + statusline(FORM_LINK_OPTION_LIST_MESSAGE); + } + break; + case F_CHECKBOX_TYPE: + if (FormIsReadonly(form)) { + statusline(FORM_LINK_CHECKBOX_UNM_MSG); + } else if (fields_are_named()) { + char *submit_str = NULL; + + HTSprintf0(&submit_str, FORM_LINK_CHECKBOX_ADV_MSG, form->name); + statusline(submit_str); + FREE(submit_str); + } else { + statusline(FORM_LINK_CHECKBOX_MESSAGE); + } + break; + case F_RADIO_TYPE: + if (FormIsReadonly(form)) { + statusline(FORM_LINK_RADIO_UNM_MSG); + } else if (fields_are_named()) { + char *submit_str = NULL; + + HTSprintf0(&submit_str, FORM_LINK_RADIO_ADV_MSG, form->name); + statusline(submit_str); + FREE(submit_str); + } else { + statusline(FORM_LINK_RADIO_MESSAGE); + } + break; + case F_TEXT_SUBMIT_TYPE: + if (FormIsReadonly(form)) { + 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 + statusline(TMA_PANEL(FORM_TEXT_SUBMIT_MAILTO_MSG_INA, + FORM_LINK_TEXT_SUBMIT_MAILTO_MSG)); + } else if (form->no_cache) { + statusline(TMA_PANEL(FORM_TEXT_RESUBMIT_MESSAGE_INA, + 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)) { + HTSprintf0(&submit_str, + TMA_PANEL(FORM_TEXT_SUBMIT_MESSAGE_INA_X, + FORM_LINK_TEXT_SUBMIT_MESSAGE_X), + xkey_info); + statusline(submit_str); + FREE(submit_str); + } else { + statusline(TMA_PANEL(FORM_LINK_TEXT_SUBMIT_MESSAGE_INA, + FORM_LINK_TEXT_SUBMIT_MESSAGE)); + } + FREE(xkey_info); + } + break; + case F_SUBMIT_TYPE: + case F_IMAGE_SUBMIT_TYPE: + if (FormIsReadonly(form)) { + 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 == MINIMAL_MODE) { + statusline(""); + } 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 == MINIMAL_MODE) { + statusline(""); + } else 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 == MINIMAL_MODE) { + statusline(""); + } 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 (FormIsReadonly(form)) + statusline(FORM_LINK_RESET_DIS_MSG); + else + statusline(FORM_LINK_RESET_MESSAGE); + break; + case F_BUTTON_TYPE: + if (FormIsReadonly(form)) { + statusline(FORM_LINK_BUTTON_DIS_MSG); + } else if (fields_are_named()) { + char *submit_str = NULL; + + HTSprintf0(&submit_str, FORM_LINK_BUTTON_ADV_MSG, form->name); + statusline(submit_str); + FREE(submit_str); + } else { + statusline(FORM_LINK_BUTTON_MESSAGE); + } + break; + case F_FILE_TYPE: + if (FormIsReadonly(form)) + statusline(FORM_LINK_FILE_UNM_MSG); + else + statusline(FORM_LINK_FILE_MESSAGE); + break; + case F_TEXT_TYPE: + if (FormIsReadonly(form)) { + statusline(FORM_LINK_TEXT_UNM_MSG); + } else if (fields_are_named()) { + char *submit_str = NULL; + + HTSprintf0(&submit_str, + TMA_PANEL(FORM_LINK_TEXT_ADV_MSG_INA, + FORM_LINK_TEXT_ADV_MSG), + form->name); + statusline(submit_str); + FREE(submit_str); + } else { + statusline(TMA_PANEL(FORM_LINK_TEXT_MESSAGE_INA, + FORM_LINK_TEXT_MESSAGE)); + } + break; + case F_TEXTAREA_TYPE: + if (FormIsReadonly(form)) { + 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_EDITTEXTAREA, for_what); +#ifdef TEXTAREA_AUTOEXTEDIT + if (!xkey_info) + xkey_info = key_for_func_ext(LYK_DWIMEDIT, for_what); +#endif + } + if (non_empty(xkey_info)) { + if (fields_are_named()) { + HTSprintf0(&submit_str, + TMA_PANEL(FORM_LINK_TEXTAREA_ADV_MSG_INA_E, + FORM_LINK_TEXTAREA_ADV_MSG_E), + form->name, + xkey_info); + } else { + HTSprintf0(&submit_str, + TMA_PANEL(FORM_LINK_TEXTAREA_MESSAGE_INA_E, + FORM_LINK_TEXTAREA_MESSAGE_E), + xkey_info); + } + statusline(submit_str); + FREE(submit_str); + } else { + if (fields_are_named()) { + HTSprintf0(&submit_str, + TMA_PANEL(FORM_LINK_TEXTAREA_ADV_MSG_INA, + FORM_LINK_TEXTAREA_ADV_MSG), + form->name); + statusline(submit_str); + FREE(submit_str); + } else { + statusline(TMA_PANEL(FORM_LINK_TEXTAREA_MESSAGE_INA, + FORM_LINK_TEXTAREA_MESSAGE)); + } + } + FREE(xkey_info); + } + break; + } +} diff --git a/src/LYGCurses.h b/src/LYGCurses.h new file mode 100644 index 0000000..ce5303c --- /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 0000000..57e7f30 --- /dev/null +++ b/src/LYGetFile.c @@ -0,0 +1,1581 @@ +/* $LynxId: LYGetFile.c,v 1.97 2024/01/14 20:02:21 Viatrix 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 == LYNXEDITMAP_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) || + (non_empty(helpfilepath) && + 0 == STRNADDRCOMP(WWWDoc.address, helpfilepath, + strlen(helpfilepath))) || + (non_empty(lynxlistfile) && + 0 == STRNADDRCOMP(WWWDoc.address, lynxlistfile, + strlen(lynxlistfile))) || + (non_empty(lynxlinksfile) && + 0 == STRNADDRCOMP(WWWDoc.address, lynxlinksfile, + strlen(lynxlinksfile))) || + (non_empty(lynxjumpfile) && + 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 == LYNXEDITMAP_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; + if (track_internal_links && doc->internal_link && !reloading) { + LYinternal_flag = TRUE; + } +#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 if (!dump_output_immediately) { + 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 != LYNXEDITMAP_URL_TYPE && + 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; + } else if ((cp = StrChr(doc->address + 9, '/')) != NULL && + 0 == StrNCmp(++cp, "hURL", 4)) { + StrAllocCopy(tmp, cp + 4); + HTUnEscape(tmp); + if (*tmp == ':') { + CTRACE((tfp, "getfile: URL '%s'\n", doc->address)); + StrAllocCopy(doc->address, tmp + 1); + FREE(tmp); + CTRACE((tfp, " changed to '%s'\n", doc->address)); + return getfile(doc, target); + } else + FREE(tmp); + } + } + + 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) +{ + bstring *temp = NULL; + char *p; + int rel = 0; + int new_top, new_link; + BOOL want_go; + int curline = *num; /* passed in from mainloop() */ + int code; + + CTRACE((tfp, "follow_link_number(%d,%d,...)\n", c, cur)); + BStrCopy0(temp, "?"); + temp->str[0] = (char) c; + *num = -1; + _statusline(FOLLOW_LINK_NUMBER); + + /* + * Get the number, possibly with a letter suffix, from the user. + */ + if (LYgetBString(&temp, FALSE, 120, NORECALL) < 0 || + isBEmpty(temp)) { + HTInfoMsg(CANCELLED); + return (DO_NOTHING); + } + + p = temp->str; + *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->str, *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)); + code = 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))); + code = DO_GOTOPAGE_STUFF; + } else { + + /* + * 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; + code = DO_LINK_STUFF; + } else if (info == LINK_LINE_FOUND) { + doc->line = new_top + 1; + doc->link = new_link; + code = DO_GOTOLINK_STUFF; + } else if (info) { + links[cur].type = WWW_LINK_TYPE; + code = DO_LINK_STUFF; + } else { + code = PRINT_ERROR; + } + } else { + code = PRINT_ERROR; + } + } + BStrFree(temp); + return code; +} + +#if defined(EXEC_LINKS) || defined(LYNXCGI_LINKS) + +struct trust { + char *src; + char *path; + int type; + struct trust *next; +}; + +static struct trust *trusted_exec = 0; +static struct trust *always_trusted_exec; +static struct trust *trusted_cgi = 0; + +static struct trust *new_trust(const char *src, const char *path, int type) +{ + struct trust *tp; + + tp = typecalloc(struct trust); + + if (tp == NULL) + outofmem(__FILE__, "new_trust"); + + tp->type = type; + StrAllocCopy(tp->src, src); + StrAllocCopy(tp->path, path); + + return tp; +} + +static struct trust *get_trust(struct trust **table, const char *src, int type) +{ + if (*table == 0) { + *table = new_trust(src, "", type); + } + return *table; +} + +#ifdef LY_FIND_LEAKS +static void free_data(struct trust **data) +{ + struct trust *cur = (*data); + struct trust *next; + + while (cur) { + FREE(cur->src); + FREE(cur->path); + next = cur->next; + FREE(cur); + cur = next; + } + *data = NULL; +} + +static void LYTrusted_free(void) +{ + free_data(&trusted_exec); + free_data(&always_trusted_exec); + free_data(&trusted_cgi); + + return; +} +#endif /* LY_FIND_LEAKS */ + +void add_trusted(char *str, + int type) +{ + struct trust *tp; + char *path; + char *src = str; + const char *after_tab; + 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'; + after_tab = path; + } else { + after_tab = ""; + } + + tp = new_trust(src, after_tab, Type); + + if (Type == EXEC_PATH) { + tp->next = trusted_exec; + trusted_exec = tp; + } else if (Type == ALWAYS_EXEC_PATH) { + tp->next = always_trusted_exec; + always_trusted_exec = tp; + } else if (Type == CGI_PATH) { + 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 = get_trust(&trusted_exec, "file://localhost/", EXEC_PATH); + } else if (Type == ALWAYS_EXEC_PATH) { + tp = get_trust(&always_trusted_exec, "none", ALWAYS_EXEC_PATH); + } else if (Type == CGI_PATH) { + tp = get_trust(&trusted_cgi, "none", CGI_PATH); + } 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->next != 0) { + 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)); + if (second == 0) + second = first + strlen(first); + else + *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 0000000..f204d07 --- /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 0000000..37e30ba --- /dev/null +++ b/src/LYGlobalDefs.h @@ -0,0 +1,740 @@ +/* + * $LynxId: LYGlobalDefs.h,v 1.155 2023/10/23 08:04:43 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 fields_are_named() \ + (user_mode == ADVANCED_MODE) + +#define HIDDENLINKS_MERGE 0 +#define HIDDENLINKS_SEPARATE 1 +#define HIDDENLINKS_IGNORE 2 + +#define NOVICE_MODE 0 +#define INTERMEDIATE_MODE 1 +#define ADVANCED_MODE 2 +#define MINIMAL_MODE 3 + 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 + ,rateEtaBYTES2 + ,rateEtaKB2 +#endif +#ifdef USE_PROGRESSBAR + ,rateBAR +#endif + } TransferRate; + +#ifdef USE_READPROGRESS +# define isRateInKB(n) ((n) == rateKB || (n) == rateEtaKB || (n) == rateEtaKB2) +#else +# define isRateInKB(n) ((n) == 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 */ + extern BOOLEAN LYAutoSession; /* Auto restore/save session? */ + 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 LYtrimBlankLines; + extern BOOLEAN LYtrimInputFields; + extern BOOLEAN LYxhtml_parsing; + extern BOOLEAN bold_H1; + extern BOOLEAN bold_headers; + extern BOOLEAN bold_name_anchors; + extern BOOLEAN LYcase_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_decoded; + extern BOOLEAN dump_links_inline; + 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; /* valid size_is_changed */ + extern BOOLEAN rlogin_ok; + extern BOOLEAN size_is_changed; /* SIGWINCH was caught */ + 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 LYContentType; + extern const char *ContentTypes[]; + 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; + extern int max_uri_size; +#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 *personal_mail_name; + 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 LYGuessScheme; + extern BOOLEAN unique_urls; + extern BOOLEAN use_underscore; + extern BOOLEAN no_list; + extern BOOLEAN no_margins; + extern BOOLEAN no_pause; + extern BOOLEAN no_title; + extern BOOLEAN update_term_title; + extern BOOLEAN historical_comments; + extern BOOLEAN html5_charsets; + 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 track_internal_links; + 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 *socks5_proxy; + 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 DelaySecs; + 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 char *SSL_client_cert_file; /* Default client CERT file */ + extern char *SSL_client_key_file; /* Default client key file */ + + typedef enum { + HTTP_1_0 + ,HTTP_1_1 + } HTTP_LEVEL; + + extern int HTprotocolLevel; + + 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; + + extern int cookie_version; + + typedef enum { + COOKIES_RFC_2109 + ,COOKIES_RFC_2965 + ,COOKIES_RFC_6265 + } COOKIES_VERSION; + +#define USE_RFC_2109 (cookie_version == (COOKIES_RFC_2109)) +#define USE_RFC_2965 (cookie_version == (COOKIES_RFC_2965)) +#define USE_RFC_6265 (cookie_version == (COOKIES_RFC_6265)) + +#ifdef USE_SSL + extern int ssl_noprompt; +#endif + + extern int LYNoZapKey; /* 0: off (do 'z' checking), 1: full, 2: initially */ + +#ifdef USE_JUSTIFY_ELTS + extern BOOLEAN ok_justify; + extern int justify_max_void_percent; +#endif + +#ifdef USE_LOCALE_CHARSET + extern BOOLEAN LYLocaleCharset; +#endif + extern BOOLEAN assumed_charset; + +#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 + + extern BOOLEAN conv_jisx0201kana; + extern BOOLEAN wait_viewer_termination; + +#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 int LYuse_color_style; /* color-style vs oldlynx */ + 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 USE_PROGRAM_DIR + extern char *program_dir; +#endif + +#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; + extern CRITICAL_SECTION critSec_READ; +#endif /* _WINDOWS */ + + extern BOOLEAN show_cfg; + extern BOOLEAN no_table_center; + +#if USE_BLAT_MAILER + extern BOOLEAN mail_is_altblat; + extern BOOLEAN mail_is_blat; +#endif + +#if defined(__CYGWIN__) +#include <io.h> +#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 __CYGWIN__ +#define ConvertToWin32Path(p, q) \ + cygwin_conv_to_full_win32_path(p, q); +#else +#define ConvertToWin32Path(p, q) \ + q = p +#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 0000000..f419c7e --- /dev/null +++ b/src/LYHash.c @@ -0,0 +1,144 @@ +/* + * $LynxId: LYHash.c,v 1.39 2018/03/29 00:38:59 tom Exp $ + * + * A hash table for the (fake) CSS support in Lynx-rp + * (c) 1996 Rob Partington + * rewritten 1997 by Klaus Weide. + * rewritten 2018 -TD + */ +#include <LYHash.h> +#include <LYUtils.h> +#include <LYLeaks.h> +#include <LYStrings.h> +#include <LYGlobalDefs.h> + +#ifdef USE_COLOR_STYLE + +#undef HASH_TYPE + +#define HASH_SIZE CSHASHSIZE +#define HASH_TYPE int +#define HASH_OF(h, v) ((HASH_TYPE)((h) * 3 + UCH(v)) % HASH_SIZE) + +static int count_bump; +static size_t limit; +static char *buffer; + +static char *get_buffer(size_t need) +{ + if (++need > limit) { + char *test = realloc(buffer, (limit = (1 + need) * 2)); + + if (test == 0) + outofmem(__FILE__, "LYHash"); + buffer = test; + } + return buffer; +} + +/* + * This is the same algorithm as the private anchor_hash() in HTAnchor.c, but + * with a different value for HASH_SIZE. + */ +static HASH_TYPE cs_hash(const char *string) +{ + HASH_TYPE hash = 0; + HASH_TYPE best, n; + bucket *data; + const char *p; + + for (p = string; *p; p++) + hash = HASH_OF(hash, *p); + + /* + * The computed hash-code is only a starting point. Check for collision. + */ + best = hash; + for (n = 0; n < HASH_SIZE; n++) { + int nn = (n + hash) % HASH_SIZE; + + data = &hashStyles[nn]; + if (data->name == 0 || !strcmp(string, data->name)) { + best = nn; + hash = nn; + break; + } + ++count_bump; + } + data = &hashStyles[best]; + if (data->name != 0) { + if (strcmp(string, data->name)) { + CTRACE_STYLE((tfp, "cs_hash(%s) overwriting %d\n", string, data->name)); + FREE(data->name); + StrAllocCopy(data->name, string); + } + } else { + StrAllocCopy(data->name, string); + } + + CTRACE_STYLE((tfp, "cs_hash(%s) = %d\n", string, hash)); + return hash; +} + +int color_style_1(const char *string) +{ + int hash; + + if (dump_output_immediately) { + hash = 0; + } else { + get_buffer(strlen(string)); + strcpy(buffer, string); + LYLowerCase(buffer); + hash = cs_hash(buffer); + } + return hash; +} + +int color_style_3(const char *p, const char *q, const char *r) +{ + int hash; + + if (dump_output_immediately) { + hash = 0; + } else { + get_buffer(strlen(p) + strlen(q) + strlen(r)); + strcpy(buffer, p); + strcat(buffer, q); + strcat(buffer, r); + LYLowerCase(buffer); + hash = cs_hash(buffer); + } + return hash; +} + +void report_hashStyles(void) +{ + int i; + int count_name = 0; + int count_used = 0; + + for (i = 0; i < CSHASHSIZE; i++) { + count_name += (hashStyles[i].name != 0); + count_used += (hashStyles[i].used != 0); + } + CTRACE((tfp, "Style hash:\n")); + CTRACE((tfp, "%5d names allocated\n", count_name)); + CTRACE((tfp, "%5d buckets used\n", count_used)); + CTRACE((tfp, "%5d hash collisions\n", count_bump)); +} + +void free_hashStyles(void) +{ + int i; + + for (i = 0; i < CSHASHSIZE; i++) { + FREE(hashStyles[i].name); + hashStyles[i].used = FALSE; + } + FREE(buffer); + limit = 0; + count_bump = 0; +} + +#endif /* USE_COLOR_STYLE */ diff --git a/src/LYHash.h b/src/LYHash.h new file mode 100644 index 0000000..e0e369b --- /dev/null +++ b/src/LYHash.h @@ -0,0 +1,67 @@ +/* $LynxId: LYHash.h,v 1.40 2018/03/10 01:47:33 tom Exp $ */ +#ifndef _LYHASH_H_ +#define _LYHASH_H_ 1 + +#ifndef HTUTILS_H +#include <HTUtils.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { + char *name; /* name of this item */ + BOOL used; /* color/attributes have been assigned */ + int color; /* color highlighting to be done */ + int mono; /* mono highlighting to be done */ + int cattr; /* attributes to go with the color */ + } bucket; + +#define CSHASHSIZE 9973 /* Arbitrary prime. Memory/speed tradeoff */ + +#define NOSTYLE -1 + + extern bucket hashStyles[CSHASHSIZE]; + extern bucket *nostyle_bucket(void); + + extern int color_style_1(const char *string); + extern int color_style_3(const char *p, const char *q, const char *r); + extern void free_hashStyles(void); + extern void report_hashStyles(void); + + 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 + +#ifdef __cplusplus +} +#endif +#endif /* _LYHASH_H_ */ diff --git a/src/LYHistory.c b/src/LYHistory.c new file mode 100644 index 0000000..4fd5567 --- /dev/null +++ b/src/LYHistory.c @@ -0,0 +1,1163 @@ +/* + * $LynxId: LYHistory.c,v 1.94 2021/06/09 22:55:43 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 auxiliary */ +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/%u (%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) || + isLYNXEDITMAP(doc->address) || + isLYNXKEYMAP(doc->address) || + LYIsUIPage(doc->address, UIP_LIST_PAGE) || +#ifdef USE_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"); + + 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(unsigned entries) +{ + CTRACE((tfp, "LYAllocHistory %u vs %u\n", entries, size_history)); + if (entries + 1 >= size_history) { + size_t want; + unsigned save = size_history; + + size_history += (entries + 2) * 2; + want = ((size_t) size_history) * sizeof(*history); + + if (history == 0) { + history = typecallocn(HistInfo, want); + } else { + history = typeRealloc(HistInfo, history, want); + memset(&history[save], 0, size_history - save); + } + if (history == 0) + outofmem(__FILE__, "LYAllocHistory"); + } + CTRACE((tfp, "...LYAllocHistory %u vs %u\n", entries, size_history)); +} + +/* + * Push the current filename, link and line number onto the history list. + */ +int LYpush(DocInfo *doc, int 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((unsigned) 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((unsigned) 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 || (unsigned) 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((unsigned) 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)) { + if (track_internal_links) { + LYforce_no_cache = FALSE; + LYinternal_flag = TRUE; + newdoc->internal_link = TRUE; + treat_as_intern = TRUE; + } + } 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 *, (size_t) 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()]. + */ + +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 == 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=\"" STR_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 0000000..a8f2b1c --- /dev/null +++ b/src/LYHistory.h @@ -0,0 +1,39 @@ +/* + * $LynxId: LYHistory.h,v 1.22 2021/06/09 22:33:06 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, int force_push); + extern int showhistory(char **newfile); + extern void LYAddVisitedLink(DocInfo *doc); + extern void LYAllocHistory(unsigned 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 0000000..001af56 --- /dev/null +++ b/src/LYIcon.rc @@ -0,0 +1,34 @@ +// $LynxId: LYIcon.rc,v 1.57 2024/01/07 11:13:00 tom Exp $ + +#include <windows.h> + +100 ICON "../samples/lynx.ico" + +VS_VERSION_INFO VERSIONINFO +FILEVERSION 2,9,0,1013 +PRODUCTVERSION 2,9,0,1013 +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEFLAGS 0 +FILEOS VOS_NT_WINDOWS32 +FILETYPE VFT_APP +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "https://invisible-island.net/lynx" + VALUE "FileDescription", "Lynx - web browser" + VALUE "FileVersion", "2.9.0.1013" + VALUE "InternalName", "Lynx" + VALUE "LegalCopyright", "©1997-2024 Thomas E. Dickey" + VALUE "OriginalFilename", "lynx.exe" + VALUE "ProductName", "Lynx - web browser" + VALUE "ProductVersion", "2.9.0.1013" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/src/LYJump.c b/src/LYJump.c new file mode 100644 index 0000000..877d532 --- /dev/null +++ b/src/LYJump.c @@ -0,0 +1,504 @@ +/* + * $LynxId: LYJump.c,v 1.52 2018/03/18 19:17:00 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"); + } + + /* + * 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 (isEmpty(jumpfile)) + StrAllocCopy(jumpfile, JThead->file); + jtp = typecalloc(struct JumpTable); + + if (jtp == NULL) { + outofmem(__FILE__, "LYJumpInit"); + } + + 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) +{ + static bstring *buf = NULL; + + JumpDatum seeking; + JumpDatum *found; + 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)) { + BStrCopy0(buf, ""); + } else if (non_empty(jtp->shortcut)) { + size_t len = (size_t) BStrLen(buf); + + if (strlen(jtp->shortcut) > len) { + jtp->shortcut[len] = '\0'; + BStrCopy0(buf, jtp->shortcut); + } + } + + ShortcutTotal = (jtp->history ? HTList_count(jtp->history) : 0); + if (jump_buffer && !isBEmpty(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 = LYgetBString(&buf, FALSE, 0, recall)) < 0) { + /* + * User cancelled the Jump via ^G. - FM + */ + HTInfoMsg(CANCELLED); + return NULL; + } + + check_recall: + bp = buf->str; + if (TOUPPER(key) == 'G' && StrNCmp(buf->str, "o ", 2) == 0) + bp++; + bp = LYSkipBlanks(bp); + if (*bp == '\0' && + !(recall && (ch == UPARROW_KEY || ch == DNARROW_KEY))) { + /* + * User cancelled the Jump via a zero-length string. - FM + */ + BStrCopy0(buf, ""); + StrAllocCopy(jtp->shortcut, buf->str); + HTInfoMsg(CANCELLED); + return NULL; + } +#ifdef PERMIT_GOTO_FROM_JUMP + if (StrChr(bp, ':') || StrChr(bp, '/')) { + char *temp = NULL; + + LYJumpFileURL = FALSE; + if (no_goto) { + BStrCopy0(buf, ""); + StrAllocCopy(jtp->shortcut, buf->str); + HTUserMsg(RANDOM_URL_DISALLOWED); + return NULL; + } + HTSprintf0(&temp, "Go %s", bp); + BStrCopy0(buf, temp); + FREE(temp); + return (bp = buf->str); + } +#endif /* PERMIT_GOTO_FROM_JUMP */ + + if (recall && ch == UPARROW_KEY) { + 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) { + BStrCopy0(buf, cp); + if (jump_buffer && jtp->shortcut && + !strcmp(buf->str, 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 = LYgetBString(&buf, FALSE, 0, recall)) < 0) { + /* + * User cancelled the jump via ^G. + */ + HTInfoMsg(CANCELLED); + return NULL; + } + goto check_recall; + } + } else if (recall && ch == DNARROW_KEY) { + 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) { + BStrCopy0(buf, cp); + if (jump_buffer && jtp->shortcut && + !strcmp(buf->str, 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 = LYgetBString(&buf, FALSE, 0, 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, + (size_t) jtp->nel, sizeof(JumpDatum), LYCompare); + if (!found) { + user_message("Unknown target '%s'", buf->str); + 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); + close(fd); + 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); + close(fd); + return 0; + } else { + while (fgets(mp + strlen(mp), blocksize, fp) != NULL) { + ; + } + } + LYCloseInput(fp); + close(fd); + } +#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 + 1) * 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++; + } + + 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 0000000..159bdd6 --- /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 0000000..997cd05 --- /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 0000000..164fa23 --- /dev/null +++ b/src/LYKeymap.c @@ -0,0 +1,1545 @@ +/* $LynxId: LYKeymap.c,v 1.125 2024/01/10 08:53:58 tom 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 */ + +/* * * 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 + * specify key `functions', see LYKeymap.h. + */ + +/* the character gets 1 added to it before lookup, + * so that EOF maps to 0 + */ +LYKeymap_t keymap[KEYMAP_SIZE]; + +static const LYEditInit initKeymapData[] = +{ + {KTL('@'), LYK_DO_NOTHING}, + {KTL('A'), LYK_HOME}, + {KTL('B'), LYK_PREV_PAGE}, + {KTL('D'), LYK_ABORT}, + {KTL('E'), LYK_END}, + {KTL('F'), LYK_NEXT_PAGE}, + {KTL('H'), LYK_HISTORY}, + {KTL('I'), LYK_FASTFORW_LINK}, + {KTL('J'), LYK_ACTIVATE}, + {KTL('K'), LYK_COOKIE_JAR}, + {KTL('L'), LYK_REFRESH}, + {KTL('M'), LYK_ACTIVATE}, + {KTL('N'), LYK_DOWN_TWO}, + {KTL('P'), LYK_UP_TWO}, + {KTL('Q'), LYK_CHANGE_CENTER}, + {KTL('R'), LYK_RELOAD}, +#ifdef CAN_CUT_AND_PASTE + {KTL('S'), LYK_TO_CLIPBOARD}, +#endif + {KTL('T'), LYK_TRACE_TOGGLE}, + {KTL('U'), LYK_NEXT_DOC}, + {KTL('V'), LYK_SWITCH_DTD}, + {KTL('W'), LYK_REFRESH}, + {KTL('X'), LYK_CACHE_JAR}, + {KTL('Z'), LYK_MAXSCREEN_TOGGLE}, + {KHR(' '), LYK_NEXT_PAGE}, + {KHR('!'), LYK_SHELL}, + {KHR('"'), LYK_SOFT_DQUOTES}, + {KHR('#'), LYK_TOOLBAR}, + {KHR('$'), LYK_LAST_LINK}, + {KHR('\''), LYK_HISTORICAL}, + {KHR('('), LYK_UP_HALF}, + {KHR(')'), LYK_DOWN_HALF}, + {KHR('*'), LYK_IMAGE_TOGGLE}, + {KHR('+'), LYK_NEXT_PAGE}, + {KHR(','), LYK_EXTERN_PAGE}, + {KHR('-'), LYK_PREV_PAGE}, + {KHR('.'), LYK_EXTERN_LINK}, + {KHR('/'), LYK_WHEREIS}, + {KHR('0'), LYK_F_LINK_NUM}, + {KHR('1'), LYK_1}, + {KHR('2'), LYK_2}, + {KHR('3'), LYK_3}, + {KHR('4'), LYK_4}, + {KHR('5'), LYK_5}, + {KHR('6'), LYK_6}, + {KHR('7'), LYK_7}, + {KHR('8'), LYK_8}, + {KHR('9'), LYK_9}, + {KHR(':'), LYK_COMMAND}, + {KHR(';'), LYK_TRACE_LOG}, + {KHR('<'), LYK_UP_LINK}, + {KHR('='), LYK_INFO}, + {KHR('>'), LYK_DOWN_LINK}, + {KHR('?'), LYK_HELP}, + {KHR('@'), LYK_RAW_TOGGLE}, + {KHR('A'), LYK_ADDRLIST}, + {KHR('B'), LYK_PREV_PAGE}, +#ifdef SUPPORT_CHDIR + {KHR('C'), LYK_CHDIR}, +#else + {KHR('C'), LYK_COMMENT}, +#endif + {KHR('D'), LYK_DOWNLOAD}, + {KHR('E'), LYK_ELGOTO}, + {KHR('F'), LYK_DIRED_MENU}, + {KHR('G'), LYK_ECGOTO}, + {KHR('H'), LYK_HELP}, + {KHR('I'), LYK_INDEX}, +#ifdef KANJI_CODE_OVERRIDE + {KHR('J'), LYK_CHANGE_KCODE}, +#else + {KHR('J'), LYK_JUMP}, +#endif + {KHR('K'), LYK_KEYMAP}, + {KHR('L'), LYK_LIST}, + {KHR('M'), LYK_MAIN_MENU}, + {KHR('N'), LYK_PREV}, + {KHR('O'), LYK_OPTIONS}, + {KHR('P'), LYK_PRINT}, + {KHR('Q'), LYK_ABORT}, + {KHR('R'), LYK_DEL_BOOKMARK}, + {KHR('S'), LYK_INDEX_SEARCH}, + {KHR('T'), LYK_TAG_LINK}, + {KHR('U'), LYK_PREV_DOC}, + {KHR('V'), LYK_VLINKS}, + {KHR('X'), LYK_NOCACHE}, + {KHR('Z'), LYK_INTERRUPT}, + {KHR('['), LYK_INLINE_TOGGLE}, + {KHR('\\'), LYK_SOURCE}, + {KHR(']'), LYK_HEAD}, + {KHR('^'), LYK_FIRST_LINK}, + {KHR('_'), LYK_CLEAR_AUTH}, + {KHR('`'), LYK_MINIMAL}, + {KHR('a'), LYK_ADD_BOOKMARK}, + {KHR('b'), LYK_PREV_PAGE}, + {KHR('c'), LYK_COMMENT}, + {KHR('d'), LYK_DOWNLOAD}, + {KHR('e'), LYK_EDIT}, + {KHR('f'), LYK_DIRED_MENU}, + {KHR('g'), LYK_GOTO}, + {KHR('h'), LYK_HELP}, + {KHR('i'), LYK_INDEX}, + {KHR('j'), LYK_JUMP}, + {KHR('k'), LYK_KEYMAP}, + {KHR('l'), LYK_LIST}, + {KHR('m'), LYK_MAIN_MENU}, + {KHR('n'), LYK_NEXT}, + {KHR('o'), LYK_OPTIONS}, + {KHR('p'), LYK_PRINT}, + {KHR('q'), LYK_QUIT}, + {KHR('r'), LYK_DEL_BOOKMARK}, + {KHR('s'), LYK_INDEX_SEARCH}, + {KHR('t'), LYK_TAG_LINK}, + {KHR('u'), LYK_PREV_DOC}, + {KHR('v'), LYK_VIEW_BOOKMARK}, + {KHR('x'), LYK_NOCACHE}, + {KHR('z'), LYK_INTERRUPT}, + {KHR('{'), LYK_SHIFT_LEFT}, + {KHR('|'), LYK_LINEWRAP_TOGGLE}, + {KHR('}'), LYK_SHIFT_RIGHT}, + {KHR('~'), LYK_NESTED_TABLES}, + {KHR(DEL_KEY), LYK_HISTORY}, + {KHR(UPARROW_KEY), LYK_PREV_LINK}, + {KHR(DNARROW_KEY), LYK_NEXT_LINK}, + {KHR(RTARROW_KEY), LYK_ACTIVATE}, + {KHR(LTARROW_KEY), LYK_PREV_DOC}, + {KHR(PGDOWN_KEY), LYK_NEXT_PAGE}, + {KHR(PGUP_KEY), LYK_PREV_PAGE}, + {KHR(HOME_KEY), LYK_HOME}, + {KHR(END_KEY), LYK_END}, + {KHR(F1_KEY), LYK_DWIMHELP}, +#if !(defined(_WINDOWS) || defined(__DJGPP__)) + {KHR(DO_KEY), LYK_ACTIVATE}, + {KHR(FIND_KEY), LYK_HOME}, + {KHR(SELECT_KEY), LYK_END}, +#endif + {KHR(INSERT_KEY), LYK_UP_TWO}, + {KHR(REMOVE_KEY), LYK_DOWN_TWO}, + {KHR(DO_NOTHING), LYK_DO_NOTHING}, + {KHR(BACKTAB_KEY), LYK_FASTBACKW_LINK}, + {KHR(F11_KEY), LYK_DO_NOTHING}, +#ifdef DJGPP_KEYHANDLER + {302, LYK_ABORT}, +#endif /* DJGPP_KEYHANDLER */ +#if (defined(_WINDOWS) || defined(__DJGPP__) || defined(__CYGWIN__)) && !defined(USE_SLANG) /* PDCurses */ + {441, LYK_ABORT}, /* ALT_X */ + {459, LYK_WHEREIS}, /* KP_SLASH */ + {464, LYK_IMAGE_TOGGLE}, /* KP_* */ + {465, LYK_PREV_PAGE}, /* KP_- */ + {466, LYK_NEXT_PAGE}, /* KP_+ */ +#endif + {657, LYK_CHANGE_LINK}, + {-1, LYE_UNKNOWN} +}; + +static LYEditConfig myKeymapData = +{ + "Key Map", initKeymapData, keymap +}; + +#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]; + +#define EDIT_INIT(c,l) {KHR(c), (l)} +static const LYEditInit initOverrideData[] = +{ + EDIT_INIT(CTL('V'), LYK_NEXT_DOC), + EDIT_INIT('.', LYK_TAG_LINK), +#ifndef SUPPORT_CHDIR + EDIT_INIT('C', LYK_CREATE), +#else + EDIT_INIT('C', LYK_CHDIR), +#endif + EDIT_INIT('F', LYK_DIRED_MENU), + EDIT_INIT('M', LYK_MODIFY), + EDIT_INIT('R', LYK_REMOVE), + EDIT_INIT('T', LYK_TAG_LINK), + EDIT_INIT('U', LYK_UPLOAD), + EDIT_INIT('c', LYK_CREATE), + EDIT_INIT('f', LYK_DIRED_MENU), + EDIT_INIT('m', LYK_MODIFY), + EDIT_INIT('r', LYK_REMOVE), + EDIT_INIT('t', LYK_TAG_LINK), + EDIT_INIT('u', LYK_UPLOAD), + EDIT_INIT(DO_NOTHING, LYK_DO_NOTHING), + {-1, LYE_UNKNOWN} +}; + +static LYEditConfig myOverrideData = +{ + "Key Override", initOverrideData, key_override +}; +#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_MOUSE_SUBMIT, "MOUSE_SUBMIT", + "DO NOT MAP: follow current link, submit"), + DATA( + LYK_SUBMIT, "SUBMIT", + "prompt and submit form"), + DATA( + LYK_RESET, "RESET", + "reset fields on current form"), + 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 occurrence"), + DATA( + LYK_NEXT, "NEXT", + "search for the next occurrence"), + 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_EDITMAP, "EDITMAP", + "display the current edit-key map"), + 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_EDITTEXTAREA, "EDITTEXTAREA", + "use an external editor to edit a form's textarea"), + DATA( + LYK_GROWTEXTAREA, "GROWTEXTAREA", + "add 5 new blank lines to the bottom of a textarea"), + DATA( + LYK_INSERTFILE, "INSERTFILE", + "insert file into a textarea (just above cursorline)"), +#ifdef USE_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_CHANGE_CENTER, "CHANGE_CENTER", + "toggle center alignment in HTML TABLE"), +#ifdef KANJI_CODE_OVERRIDE + DATA( + LYK_CHANGE_KCODE, "CHANGE_KCODE", + "Change Kanji code"), +#endif +#endif /* VMS */ +#ifdef SUPPORT_CHDIR + DATA( + LYK_CHDIR, "CHDIR", + "change current directory"), + DATA( + LYK_PWD, "PWD", + "print 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 +#ifdef USE_MAXSCREEN_TOGGLE + DATA( + LYK_MAXSCREEN_TOGGLE, "MAXSCREEN_TOGGLE", + "toggle max screen and normal"), +#endif + DATA( + LYK_UNKNOWN, NULL, + "") +}; + + +#undef DATA +/* *INDENT-OFF* */ +static const struct { + int key; + const char *name; +} named_keys[] = { + { '\t', "<tab>" }, + { '\r', "<return>" }, + { CH_ESC, "ESC" }, + { ' ', "<space>" }, + { '<', "<" }, + { '>', ">" }, + /* LYExtraKeys */ + { CH_DEL, "<delete>" }, + { UPARROW_KEY, "Up Arrow" }, + { DNARROW_KEY, "Down Arrow" }, + { RTARROW_KEY, "Right Arrow" }, + { LTARROW_KEY, "Left Arrow" }, + { PGDOWN_KEY, "Page Down" }, + { PGUP_KEY, "Page Up" }, + { HOME_KEY, "Home" }, + { END_KEY, "End" }, + { F1_KEY, "F1" }, + { F2_KEY, "F2" }, + { F3_KEY, "F3" }, + { F4_KEY, "F4" }, + { F5_KEY, "F5" }, + { F6_KEY, "F6" }, + { F7_KEY, "F7" }, + { F8_KEY, "F8" }, + { F9_KEY, "F9" }, + { F10_KEY, "F10" }, + { F11_KEY, "F11" }, + { F12_KEY, "F12" }, + { 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" }, +}; +/* *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) +{ + size_t need = strlen(name); + size_t 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, + int 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; + LYStrNCpy(buf, named_keys[n].name, sizeof(buf) - 1); + 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", (unsigned) c); + else + sprintf(buf, "0x%x", (unsigned) 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 = (int) strtol(src, &dst, 0); + if (non_empty(dst)) + key = -1; + } else if (len > 6 && !strncasecomp(src, "key-", 4)) { + char *dst = 0; + + key = (int) 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[] = { + { '<', "<" }, + { '>', ">" }, + { '"', """ }, + { '&', "&" } + }; + /* *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; + LYStrNCpy(dst, + table[n].name, + sizeof(buf) - (size_t) ((dst - buf) - 1)); + adj += (int) strlen(dst) - 1; + dst += (int) strlen(dst); + break; + } + } + if (!found) { + *dst++ = (char) c; + } + } + adj -= (int) (dst - buf) - PRETTY_LEN; + while (adj-- > 0) + *dst++ = ' '; + *dst = 0; + return buf; + } + + return 0; +} + +static char *format_binding(LYKeymap_t *table, int i) +{ + LYKeymap_t the_key = table[i]; + char *buf = 0; + char *formatted; + Kcmd *rmap = LYKeycodeToKcmd((LYKeymapCode) 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, int both) +{ + char *buf; + LYKeymap_t lac1 = LYK_UNKNOWN; /* 0 */ + +#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE) + if (prev_lynx_edit_mode && !no_dired_support && + (lac1 = 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 = 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 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))) { + char *next = 0; + + c = (int) strtol(src, &next, 0); + if (next != 0 && *next != '\0') + c = (-1); +#ifdef USE_KEYMAPS + } else { + map_string_to_keysym(src, &c, TRUE); +#ifndef USE_SLANG + if (c >= 0) { + /* make curses-keys mapped from Keysym_Strings[] available here */ + if ((c & LKC_MASK) > 255) + c &= ~LKC_ISLKC; + } +#endif +#endif + } + + if (c == CH_ESC) { + escape_bound = 1; + } else if (c < -1) { + c = (-1); + } + + 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 == 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 = KHR('a'); i <= KHR('z'); 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, + int for_dired) +{ + Kcmd *mp; + int c; + int result = 0; + +#if !defined(DIRED_SUPPORT) || !defined(OK_OVERRIDE) + if (for_dired) { + return result; + } +#endif + if (func != NULL) { + c = lkcstring_to_lkc(key); + if ((c >= 0) && (c < KEYMAP_SIZE)) { + /* 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))) { + c &= LKC_MASK; + if ((mp = LYStringToKcmd(func)) != 0) { +#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE) + if (for_dired) + key_override[KHR(c)] = mp->code; + else +#endif + keymap[KHR(c)] = (LYKeymap_t) mp->code; + /* don't return 0, successful */ + result = (c ? c : (int) LAC_TO_LKC0(mp->code)); + } + } + } + } + return result; +} + +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, size_t size) +{ + size_t j; + + for (j = 0; j < size; ++j) { + int c = KHR(table[j].code); + + table[j].save = keymap[c]; + keymap[c] = table[j].map; + } +} + +/* + * Restore the given keys from the table. + */ +static void reset_any_keys(ANY_KEYS * table, size_t size) +{ + size_t j; + + for (j = 0; j < size; ++j) { + int c = KHR(table[j].code); + + keymap[c] = 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 - 1)) { +#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) +{ + BOOLEAN result = FALSE; + + if (ch >= 0 && KHR(ch) < KEYMAP_SIZE) { + if ((ch <= 0 + || StrChr("0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz", ch) == NULL) + && (keymap[KHR(ch)] == KeyName)) { + result = TRUE; + } + } + return result; +} + +/* + * 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; + int result = -1; + + for (i = 1; i < KEYMAP_SIZE; i++) { + if (keymap[i] == KeyName) { + result = (i - 1); + break; + } + } + + return result; +} + +#ifdef EXP_KEYBOARD_LAYOUT +BOOLEAN LYSetKbLayout(char *layout_id) +{ + int i; + BOOLEAN result = FALSE; + + for (i = 0; i < (int) TABLESIZE(LYKbLayoutNames) - 1; i++) { + if (!strcasecomp(LYKbLayoutNames[i], layout_id)) { + current_layout = i; + result = TRUE; + break; + } + } + + return result; +} +#endif + +#if 0 +/* + * This function was useful in converting the hand-crafted key-bindings to + * their reusable form in 2.8.8 -TD + */ +static void checkKeyMap(LYEditConfig * table) +{ + unsigned j, k; + char comment[80]; + int first = TRUE; + + for (j = 0; table->init[j].code >= 0; ++j) { + int code = table->init[j].code; + + if (table->init[j].edit != table->used[code]) { + if (first) { + printf("TABLE %s\n", table->name); + first = FALSE; + } + printf("%u: init %d vs used %d\n", + j, + table->init[j].edit, + table->used[code]); + } + } + for (j = 0; j < KEYMAP_SIZE; ++j) { + int code = (int) j; + BOOL found = FALSE; + + for (k = 0; table->init[k].code >= 0; ++k) { + if (code == table->init[k].code) { + found = TRUE; + break; + } + } + if (!found) { + if (table->used[j] != 0) { + unsigned used = (j - 1); + int edit = table->used[j]; + const char *prefix = "LYK_"; + const char *name = 0; + Kcmd *cmd = LYKeycodeToKcmd(edit + 0); + + if (cmd != 0) { + name = cmd->name; + } + + if (used < 32) { + char temp[80]; + const char *what = 0; + + switch (used) { + case 0: + what = "nul"; + break; + case 17: + what = "XON"; + break; + case 19: + what = "XOFF"; + break; + default: + sprintf(temp, "^%c", used + 'A'); + what = temp; + break; + } + sprintf(comment, "\t/* %s */", what); + } else if (used < 127) { + sprintf(comment, "\t/* %c */", used); + } else if (used == 127) { + strcpy(comment, "\t/* DEL */"); + } else { + const char *what = LYextraKeysToName(used); + + if (non_empty(what)) { + sprintf(comment, "\t/* %s%s */", what, + ((StrChr(what, '_') != 0) + ? "" + : "_KEY")); + } else { + strcpy(comment, ""); + } + } + if (name == 0) { + name = "XXX"; + } + if (first) { + printf("TABLE %s\n", table->name); + first = FALSE; + } + printf("\t{ %d, %s%s },%s\n", code, prefix, name, comment); + } + } + } +} + +#else +#define checkKeyMap(table) /* nothing */ +#endif + +static void initKeyMap(LYEditConfig * table) +{ + unsigned k; + LYEditCode *used = table->used; + const LYEditInit *init = table->init; + + memset(used, 0, sizeof(LYEditCode) * KEYMAP_SIZE); + + for (k = 0; init[k].code >= 0; ++k) { + int code = init[k].code; + + used[code] = init[k].edit; + } + checkKeyMap(table); +} + +/* + * Reset the key bindings to their default values. + */ +void LYinitKeymap(void) +{ + CTRACE((tfp, "LYinitKeymap\n")); + initKeyMap(&myKeymapData); +#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE) + initKeyMap(&myOverrideData); +#endif +} diff --git a/src/LYKeymap.h b/src/LYKeymap.h new file mode 100644 index 0000000..a06db04 --- /dev/null +++ b/src/LYKeymap.h @@ -0,0 +1,307 @@ +/* $LynxId: LYKeymap.h,v 1.55 2015/10/07 23:34:55 tom Exp $ */ +#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 const char *lec_to_lecname(int code); + extern char *LYKeycodeToString(int c, int 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, int 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 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 BOOLEAN LYSetKbLayout(char *layout_id); +#endif + +#if defined(DIRED_SUPPORT) && defined(OK_OVERRIDE) + extern LYKeymap_t key_override[]; +#endif + +/* readable mapping for characters in edit- and key-maps */ +#define CTL(c) ((c) & 0x1f) +#define KHR(c) ((c) + 1) +#define KTL(c) (CTL(c) + 1) + +/* * * 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_MOUSE_SUBMIT /* mostly like LYK_ACTIVATE, for mouse use, don't map */ + ,LYK_SUBMIT + ,LYK_RESET + ,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_EDITMAP + ,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_EDITTEXTAREA + ,LYK_GROWTEXTAREA + ,LYK_INSERTFILE + +#ifdef USE_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_CHANGE_CENTER + +#ifdef KANJI_CODE_OVERRIDE + ,LYK_CHANGE_KCODE +#endif + +#ifdef SUPPORT_CHDIR + ,LYK_CHDIR + ,LYK_PWD +#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 + +#ifdef USE_MAXSCREEN_TOGGLE + ,LYK_MAXSCREEN_TOGGLE +#else +#define LYK_MAXSCREEN_TOGGLE 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 0000000..d082a77 --- /dev/null +++ b/src/LYLeaks.c @@ -0,0 +1,1169 @@ +/* + * $LynxId: LYLeaks.c,v 1.43 2018/12/27 23:48:37 Kamil.Dudka 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 + * 07-23-07 free leaks of THIS module too -TD + * 02-09-12 add bstring functions -TD + */ + +/* + * 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 size_t now_allocated = 0; +static size_t peak_alloced = 0; + +static size_t total_alloced = 0; +static size_t total_freed = 0; + +static long count_mallocs = 0; +static long count_frees = 0; + +static void CountMallocs(size_t size) +{ + ++count_mallocs; + total_alloced += size; + now_allocated += size; + if (peak_alloced < now_allocated) + peak_alloced = now_allocated; +} + +static void CountFrees(size_t size) +{ + ++count_frees; + total_freed += size; + now_allocated -= size; +} + +#else +#define CountMallocs(size) ++count_mallocs +#define CountFrees(size) /* 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(LYLeaksPath); + 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%lu\n", gettext("Peak allocation"), (unsigned long) peak_alloced); + fprintf(Fp_leakagesink, + "%s\t%lu\n", gettext("Bytes allocated"), (unsigned long) 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); + CountMallocs(st_bytes); + } + } + } + } + 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 * st_number); + + /* + * 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); + CountFrees(ALp_renew->st_Bytes); + CountMallocs(st_newBytes); + + 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); + } + } +} + +/* + * Check for leaked strdup() results -TD + */ +char *LYLeakStrdup(const char *source, + const char *cp_File, + const short ssi_Line) +{ + size_t length = strlen(source) + 1; + char *target = (char *) LYLeakMalloc(length, cp_File, ssi_Line); + + if (target != 0) { + memcpy(target, source, length); + } + return target; +} + +/* + * Allocates a new copy of a string, and returns it. + * Tracks allocations by using other LYLeakFoo functions. + * Equivalent to HTSACopy in HTString.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) { + size_t 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; +} + +/******************************************************************************/ + +/* + * Equivalents for bstring functions in HTString.c -TD + */ +/* same as HTSABAlloc */ +void LYLeakSABAlloc(bstring **dest, + int len, + const char *cp_File, + const short ssi_Line) +{ + if (*dest == 0) { + *dest = LYLeakCalloc(1, sizeof(bstring), cp_File, ssi_Line); + } + + if ((*dest)->len != len) { + (*dest)->str = (char *) LYLeakRealloc((*dest)->str, + (size_t) len, + cp_File, + ssi_Line); + if ((*dest)->str == NULL) + outofmem(__FILE__, "LYLeakSABalloc"); + + (*dest)->len = len; + } +} + +/* same as HTSABCopy */ +void LYLeakSABCopy(bstring **dest, + const char *src, + int len, + const char *cp_File, + const short ssi_Line) +{ + bstring *t; + unsigned need = (unsigned) (len + 1); + + CTRACE2(TRACE_BSTRING, + (tfp, "HTSABCopy(%p, %p, %d)\n", + (void *) dest, (const void *) src, len)); + LYLeakSABFree(dest, cp_File, ssi_Line); + if (src) { + if (TRACE_BSTRING) { + CTRACE((tfp, "=== %4d:", len)); + trace_bstring2(src, len); + CTRACE((tfp, "\n")); + } + if ((t = (bstring *) LYLeakMalloc(sizeof(bstring), cp_File, ssi_Line)) + == NULL) + outofmem(__FILE__, "HTSABCopy"); + + if ((t->str = (char *) LYLeakMalloc(need, cp_File, ssi_Line)) == NULL) + outofmem(__FILE__, "HTSABCopy"); + + MemCpy(t->str, src, len); + t->len = len; + t->str[t->len] = '\0'; + *dest = t; + } + if (TRACE_BSTRING) { + CTRACE((tfp, "=> %4d:", BStrLen(*dest))); + trace_bstring(*dest); + CTRACE((tfp, "\n")); + } +} + +/* same as HTSABCopy0 */ +void LYLeakSABCopy0(bstring **dest, + const char *src, + const char *cp_File, + const short ssi_Line) +{ + LYLeakSABCopy(dest, src, (int) strlen(src), cp_File, ssi_Line); +} + +/* same as HTSABCat */ +void LYLeakSABCat(bstring **dest, + const char *src, + int len, + const char *cp_File, + const short ssi_Line) +{ + bstring *t = *dest; + + CTRACE2(TRACE_BSTRING, + (tfp, "HTSABCat(%p, %p, %d)\n", + (void *) dest, (const void *) src, len)); + if (src) { + unsigned need = (unsigned) (len + 1); + + if (TRACE_BSTRING) { + CTRACE((tfp, "=== %4d:", len)); + trace_bstring2(src, len); + CTRACE((tfp, "\n")); + } + if (t) { + unsigned length = (unsigned) t->len + need; + + t->str = (char *) LYLeakRealloc(t->str, length, cp_File, ssi_Line); + } else { + if ((t = (bstring *) LYLeakCalloc(1, sizeof(bstring), cp_File, + ssi_Line)) == NULL) + outofmem(__FILE__, "HTSACat"); + + t->str = (char *) LYLeakMalloc(need, cp_File, ssi_Line); + } + if (t->str == NULL) + outofmem(__FILE__, "HTSACat"); + + MemCpy(t->str + t->len, src, len); + t->len += len; + t->str[t->len] = '\0'; + *dest = t; + } + if (TRACE_BSTRING) { + CTRACE((tfp, "=> %4d:", BStrLen(*dest))); + trace_bstring(*dest); + CTRACE((tfp, "\n")); + } +} + +/* same as HTSABCat0 */ +void LYLeakSABCat0(bstring **dest, + const char *src, + const char *cp_File, + const short ssi_Line) +{ + LYLeakSABCat(dest, src, (int) strlen(src), cp_File, ssi_Line); +} + +/* same as HTSABFree */ +void LYLeakSABFree(bstring **ptr, + const char *cp_File, + const short ssi_Line) +{ + if (*ptr != NULL) { + if ((*ptr)->str) + LYLeakFree((*ptr)->str, cp_File, ssi_Line); + LYLeakFree(*ptr, cp_File, ssi_Line); + *ptr = NULL; + } +} + +/******************************************************************************/ + +#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 the 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); + } + 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 0000000..989b6a3 --- /dev/null +++ b/src/LYList.c @@ -0,0 +1,374 @@ +/* + * $LynxId: LYList.c,v 1.55 2020/02/23 21:20:05 tom Exp $ + * + * 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 <HTParse.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, int titles) +{ + int cnt; + int refs, hidden_links; + int result; + 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 = (BOOLEAN) titles; + LYforce_HTML_mode = TRUE; /* force this file to be HTML */ + LYforce_no_cache = TRUE; /* force this file to be new */ + +#ifdef USE_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 */ + result = 1; + for (cnt = 1; cnt <= refs; cnt++) { + HTChildAnchor *child = HText_childNextNumber(cnt, &helper); + int value = HText_findAnchorNumber(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); + } + } else if (value >= result) { + if (track_internal_links) + dest_intl = HTAnchor_followTypedLink(child, HTInternalLink); + 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); + } + result = value + 1; + } + 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 int print_refs(FILE *fp, int titles, int refs) +{ + int result = 0; + int cnt; + int value; + 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; + int counter = result + 1; + + 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", counter, desc); + } + } else { + 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) { + parent = HTAnchor_parent(dest); + title = titles ? HTAnchor_title(parent) : NULL; + if (links_are_numbered()) { + value = HText_findAnchorNumber(helper); + if (value <= result) + continue; + fprintf(fp, "%4d. ", value); + } + if (((HTAnchor *) parent != dest) && title) { + fprintf(fp, "in "); + } + if (title) { + fprintf(fp, "%s\n", title); + } else { + address = HTAnchor_short_address(dest); + if (dump_links_decoded + && LYCharSet_UC[current_char_set].enc == UCT_ENC_UTF8) { + (void) HTUnEscape(address); + } + fprintf(fp, "%s\n", address); + FREE(address); + } + } + } + if (counter > result) + result = counter; +#ifdef VMS + if (HadVMSInterrupt) + break; +#endif /* VMS */ + } + return result; +} + +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, int 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:")); + } + refs = print_refs(fp, titles, refs) + 1; + + 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 0000000..f6afbc5 --- /dev/null +++ b/src/LYList.h @@ -0,0 +1,16 @@ +/* $LynxId: LYList.h,v 1.12 2010/09/25 11:35:35 tom Exp $ */ +#ifndef LYLIST_H +#define LYLIST_H + +#include <LYStructs.h> + +#ifdef __cplusplus +extern "C" { +#endif + extern int showlist(DocInfo *newdoc, int titles); + extern void printlist(FILE *fp, int titles); + +#ifdef __cplusplus +} +#endif +#endif /* LYLIST_H */ diff --git a/src/LYLocal.c b/src/LYLocal.c new file mode 100644 index 0000000..754a7d4 --- /dev/null +++ b/src/LYLocal.c @@ -0,0 +1,2685 @@ +/* + * $LynxId: LYLocal.c,v 1.135 2023/01/02 23:52:10 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 int get_filename(const char *prompt, + bstring **buf); + +#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, + size_t bufsize, + int 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 */ + +#define MAX_ARGC 10 + +static char **make_argv(const char *command, ...) +{ + static char *result[MAX_ARGC]; + int argc = 0; + char *value; + va_list ap; + + va_start(ap, command); + result[0] = 0; + StrAllocCopy(result[argc++], command); + do { + result[argc] = 0; + value = (char *) va_arg(ap, char *); + + if (value != 0) + StrAllocCopy(result[argc], value); + } while (result[argc++] != 0); + va_end(ap); + + return result; +} + +static void free_argv(char **argv) +{ + int argc; + + for (argc = 0; argv[argc] != 0; ++argc) { + free(argv[argc]); + } +} + +/* + * 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) + (void) msg; + 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 */ +#ifndef 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; + char *msg = 0; + + HTSprintf0(&msg, "make directory %s", path); + args = make_argv("mkdir", + path, + NULL); + code = (LYExecv(program, args, msg) <= 0) ? -1 : 1; + FREE(msg); + free_argv(args); + } 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; + char *tmpbuf = NULL; + + args = make_argv("rm", + "-f", + path, + NULL); + HTSprintf0(&tmpbuf, gettext("remove %s"), path); + code = LYExecv(program, args, tmpbuf); + FREE(tmpbuf); + free_argv(args); + } 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; + char *tmpbuf = NULL; + + args = make_argv("rmdir", + path, + NULL); + HTSprintf0(&tmpbuf, gettext("remove %s"), path); + code = LYExecv(program, args, tmpbuf); + FREE(tmpbuf); + free_argv(args); + } 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; + char *msg = NULL; + + HTSprintf0(&msg, gettext("touch %s"), path); + args = make_argv("touch", + path, + NULL); + code = (LYExecv(program, args, msg) <= 0) ? -1 : 1; + FREE(msg); + free_argv(args); + } 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; + + HTSprintf0(&msg, gettext("move %s to %s"), source, target); + args = make_argv("mv", + source, + target, + NULL); + code = (LYExecv(program, args, msg) <= 0) ? -1 : 1; + FREE(msg); + free_argv(args); + } 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; + bstring *given_target = NULL; + 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: ")); + + BStrCopy0(given_target, ""); + (void) LYgetBString(&given_target, FALSE, 0, NORECALL); + if (!isBEmpty(given_target)) { + /* + * Replace ~/ references to the home directory. + */ + if (LYIsTilde(given_target->str[0]) && LYIsPathSep(given_target->str[1])) { + char *cp1 = NULL; + + StrAllocCopy(cp1, Home_Dir()); + StrAllocCat(cp1, (given_target->str + 1)); + BStrCopy0(given_target, cp1); + FREE(cp1); + } + + /* + * If path is relative, prefix it with current location. + */ + if (!LYIsPathSep(given_target->str[0])) { + dst_path = HTLocalName(testpath); + LYAddPathSep(&dst_path); + StrAllocCat(dst_path, given_target->str); + } else { + dst_path = HTLocalName(given_target->str); + } + + if (!ok_stat(dst_path, &dst_info)) { + FREE(dst_path); + BStrFree(given_target); + 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(&dst_info, &src_info) + || !dir_has_same_owner(&dst_info, &src_info)) { + FREE(src_path); + BStrFree(given_target); + 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); + } + BStrFree(given_target); + return count; +} + +/* + * Modify the name of the specified item. + */ +static int modify_name(char *testpath) +{ + const char *cp; + bstring *tmpbuf = NULL; + 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); + } + + BStrCopy0(tmpbuf, LYPathLeaf(testpath)); + if (get_filename(cp, &tmpbuf)) { + + /* + * Do not allow the user to also change the location at this time. + */ + if (LYLastPathSep(tmpbuf->str) != 0) { + HTAlert(gettext("Illegal character (path-separator) found! Request ignored.")); + } else if (strlen(tmpbuf->str)) { + if ((cp = LYLastPathSep(testpath)) != NULL) { + HTSprintf0(&newpath, "%.*s%s", + (int) (cp - testpath + 1), + testpath, tmpbuf->str); + } else { + StrAllocCopy(newpath, tmpbuf->str); + } + + /* + * Make sure the destination does not already exist. + */ + if (not_already_exists(newpath)) { + code = move_file(testpath, newpath); + } + FREE(newpath); + } + } + } + BStrFree(tmpbuf); + return code; +} + +/* + * Change the location of a file or directory. + */ +static int modify_location(char *testpath) +{ + const char *cp; + char *sp; + bstring *tmpbuf = NULL; + 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); + } + + BStrCopy0(tmpbuf, testpath); + *LYPathLeaf(tmpbuf->str) = '\0'; + if (get_filename(cp, &tmpbuf)) { + if (strlen(tmpbuf->str)) { + StrAllocCopy(savepath, testpath); + StrAllocCopy(newpath, testpath); + + /* + * Allow ~/ references to the home directory. + */ + if (LYIsTilde(tmpbuf->str[0]) + && (tmpbuf->str[1] == '\0' || LYIsPathSep(tmpbuf->str[1]))) { + StrAllocCopy(newpath, Home_Dir()); + StrAllocCat(newpath, (tmpbuf->str + 1)); + BStrCopy0(tmpbuf, newpath); + } + if (LYisAbsPath(tmpbuf->str)) { + StrAllocCopy(newpath, tmpbuf->str); + } else if ((sp = LYLastPathSep(newpath)) != NULL) { + *++sp = '\0'; + StrAllocCat(newpath, tmpbuf->str); + } else { + HTAlert(gettext("Unexpected failure - unable to find trailing path separator")); + FREE(newpath); + FREE(savepath); + BStrFree(tmpbuf); + 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); + } + } + BStrFree(tmpbuf); + return code; +} + +/* + * Modify name or location of a file or directory on localhost. + */ +int local_modify(DocInfo *doc, char **newpath) +{ + int ans; + char *cp; + bstring *testpath = NULL; + int count; + int code = 0; + + 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; + } + BStrCopy0(testpath, cp); + FREE(cp); + + if (ans == 'N') { + code = modify_name(testpath->str); + } else if (ans == 'L') { + if (modify_location(testpath->str)) { + if (doc->link == (nlinks - 1)) + --doc->link; + code = 1; + } +#ifdef OK_PERMIT + } else if (ans == 'P') { + code = permit_location(NULL, testpath->str, newpath); +#endif /* OK_PERMIT */ + } else { + /* + * Code for changing ownership needed here. + */ + HTAlert(gettext("This feature not yet implemented!")); + } + } + BStrFree(testpath); + return code; +} + +#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; + bstring *tmpbuf = NULL; + char *testpath = NULL; + + BStrCopy0(tmpbuf, ""); + if (get_filename(gettext("Enter name of file to create: "), &tmpbuf)) { + + if (strstr(tmpbuf->str, "//") != NULL) { + HTAlert(gettext("Illegal redirection \"//\" found! Request ignored.")); + } else if (strlen(tmpbuf->str) && + StrChr(BadChars(), tmpbuf->str[0]) == NULL) { + StrAllocCopy(testpath, current_location); + LYAddPathSep(&testpath); + + /* + * Append the target filename to the current location. + */ + StrAllocCat(testpath, tmpbuf->str); + + /* + * Make sure the target does not already exist + */ + if (not_already_exists(testpath)) { + code = touch_file(testpath); + } + FREE(testpath); + } + } + BStrFree(tmpbuf); + return code; +} + +/* + * Create a new directory in the current directory. + */ +static int create_directory(char *current_location) +{ + int code = FALSE; + bstring *tmpbuf = NULL; + char *testpath = NULL; + + BStrCopy0(tmpbuf, ""); + if (get_filename(gettext("Enter name for new directory: "), &tmpbuf)) { + + if (strstr(tmpbuf->str, "//") != NULL) { + HTAlert(gettext("Illegal redirection \"//\" found! Request ignored.")); + } else if (strlen(tmpbuf->str) && + StrChr(BadChars(), tmpbuf->str[0]) == NULL) { + StrAllocCopy(testpath, current_location); + LYAddPathSep(&testpath); + + StrAllocCat(testpath, tmpbuf->str); + + /* + * Make sure the target does not already exist. + */ + if (not_already_exists(testpath)) { + code = make_directory(testpath); + } + FREE(testpath); + } + } + BStrFree(tmpbuf); + 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 bstring *LYValidPermitFile = NULL; + +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); + + (void) 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); + BStrCopy0(LYValidPermitFile, srcpath); + + 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 (isBEmpty(LYValidPermitFile)) { + 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->str)) { + 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 |= (mode_t) 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; + char amode[10]; + char *tmpbuf = NULL; + + HTSprintf0(&tmpbuf, "chmod %.4o %s", (unsigned) new_mode, destpath); + sprintf(amode, "%.4o", (unsigned) new_mode); + args = make_argv("chmod", + amode, + destpath, + NULL); + if (LYExecv(program, args, tmpbuf) <= 0) { + code = -1; + } + FREE(tmpbuf); + free_argv(args); + } else { + if (chmod(destpath, new_mode) < 0) { + code = -1; + } + CTRACE((tfp, "builtin chmod %.4o ->%d\n\t%s\n", + (unsigned) 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 == TRUE) { + 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(TRUE, 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) +{ + size_t len = strlen(prefix); + + if (!StrNCmp("LYNXDIRED://", data, 12) + && !strncasecomp(prefix, data + 12, (int) 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 allocated */ + 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 = NULL; + 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, ""); + memset(&dir_info, 0, sizeof(dir_info)); + } + + 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 %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, return true on success + */ +static int get_filename(const char *prompt, + bstring **bufp) +{ + char *cp; + + _statusline(prompt); + + (void) LYgetBString(bufp, FALSE, 0, NORECALL); + if (strstr((*bufp)->str, "../") != NULL) { + HTAlert(gettext("Illegal filename; request ignored.")); + return FALSE; + } else if (no_dotfiles || !show_dotfiles) { + cp = LYLastPathSep((*bufp)->str); /* find last slash */ + if (cp) + cp += 1; + else + cp = (*bufp)->str; + if (*cp == '.') { + HTAlert(gettext("Illegal filename; request ignored.")); + return FALSE; + } + } + return !isBEmpty((*bufp)); +} + +#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; + BOOL used = FALSE; + + /* + * 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"); + + /* + * Conditional on tagged != NULL ? + */ + if ((cp = StrChr(str, ':')) != 0) { + *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; + if ((cp = StrChr(str, ':')) != 0) { + *cp++ = '\0'; + StrAllocCopy(tmp->sfx, str); + + str = cp; + if ((cp = StrChr(str, ':')) != 0) { + *cp++ = '\0'; + StrAllocCopy(tmp->link, str); + + str = cp; + if ((cp = StrChr(str, ':')) != 0) { + *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; + used = TRUE; + } + } else { + menu_head = tmp; + used = TRUE; + } + } + } + } + } + if (!used) + FREE(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, + size_t bufsize, + int 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 0000000..9a7fe24 --- /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 0000000..1443f24 --- /dev/null +++ b/src/LYMail.c @@ -0,0 +1,1774 @@ +/* + * $LynxId: LYMail.c,v 1.100 2020/01/21 21:33:27 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, *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) { + LYStrNCpy(dst, cp, MAX_SUBJECT); + 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, FALSE, 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 + +/* + * blat's options-file parser (see makeargv.cpp) treats backslash and double + * quote characters specially. lynx doesn't. Do a conversion as we write the + * option. + * + * Other quirks (reading blat 3.06): + * + Whitespace not in quotes terminates a line. + * + Blat allows a comment-character to terminate a line. By default, that + * is a semicolon. + * + * Given that, the simplest thing to do is to always quote the string, using + * escaping to handle special cases. + */ +static void blat_option(FILE *fp, const char *option, const char *value) +{ + if (non_empty(value)) { + char *result = malloc(strlen(option) + 4 + 4 * strlen(value)); + char *working = result; + + CTRACE((tfp, "blat_option(opt=\"%s\", value=\"%s\")\n", option, value)); + sprintf(working, "%s \"", option); + working += strlen(working); + + while (*value != '\0') { + unsigned ch = UCH(*value); + + switch (ch) { + case '\\': + *working++ = '\\'; + *working++ = '\\'; + break; + case '"': + *working++ = '\\'; + *working++ = '"'; + break; + default: + if (ch < ' ' || ch > '~') { + sprintf(working, "\\%03o", ch); + } else { + *working++ = ch; + } + break; + } + ++value; + } + *working++ = '"'; + *working++ = '\n'; + *working = 0; + + CTRACE((tfp, "->%s", result)); + fputs(result, fp); + FREE(result); + } +} + +/* +syntax for blat 2.6.2: +Blat <filename> -t <recipient> [optional switches (see below)] + +-bodyF <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) +-help : displays the help message. +-mime : MIME Quoted-Printable Content-Transfer-Encoding. +-q : suppresses *all* output. +-server <addr> : overrides the default SMTP server to be used. + +*/ + +static char *blat_cmd(char *filename, + char *address, + char *subject, + char *ccaddr, + char *mail_addr) +{ + char *b_cmd = NULL; + + if (mail_is_altblat) { + const char *format = "%s %s -t %s -s %s %s%s%s"; + + HTAddParam(&b_cmd, format, 1, ALTBLAT_MAIL); + HTAddParam(&b_cmd, format, 2, filename); + HTAddParam(&b_cmd, format, 3, address); + HTAddParam(&b_cmd, format, 4, subject); + HTAddToCmd(&b_cmd, format, 5, ALTBLAT_MAIL_FLAGS); + if (non_empty(ccaddr)) { + HTAddToCmd(&b_cmd, format, 6, " -c "); + HTAddParam(&b_cmd, format, 7, NonNull(ccaddr)); + } + HTEndParam(&b_cmd, format, 8); + + } else { + + const char *format = "%s -of %s"; + char bl_cmd_file[LY_MAXPATH]; + FILE *fp; + +#ifdef __CYGWIN__ + char dosname[LY_MAXPATH]; + +#else + char *dosname; +#endif + + bl_cmd_file[0] = '\0'; + if ((fp = LYOpenTemp(bl_cmd_file, ".blt", "w")) == NULL) { + HTAlert(FORM_MAILTO_FAILED); + return NULL; + } + + HTAddParam(&b_cmd, format, 1, BLAT_MAIL); + + ConvertToWin32Path(filename, dosname); + blat_option(fp, "-bodyF", dosname); + blat_option(fp, "-t", address); + blat_option(fp, "-s", subject); + blat_option(fp, "-f", mail_addr); + blat_option(fp, "-c", ccaddr); + LYCloseOutput(fp); + + ConvertToWin32Path(bl_cmd_file, dosname); + + HTAddParam(&b_cmd, format, 2, dosname); + HTEndParam(&b_cmd, format, 3); + + } + + 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; + int code; + + if (!LYSystemMail()) + return 0; + +#if USE_BLAT_MAILER + if (mail_is_blat) { + cmd = blat_cmd(the_filename, + the_address, + the_subject, + the_ccaddr, + personal_mail_address); + } else +#endif +#ifdef __DJGPP__ + if (LYGetEnv("SHELL")) { + extern char *shell; + const char *c_option; + const char *format = "%s %s %s -t %s -F %s"; + + if (dj_is_bash) { + c_option = "-c"; + } else { + c_option = "/c"; + } + HTAddParam(&cmd, format, 1, shell); + HTAddParam(&cmd, format, 2, c_option); + HTAddParam(&cmd, format, 3, system_mail); + HTAddParam(&cmd, format, 4, the_address); + HTAddParam(&cmd, format, 5, the_filename); + HTEndParam(&cmd, format, 6); + } else +#endif /* __DJGPP__ */ + { + const char *format = "%s -t %s -F %s"; + + HTAddParam(&cmd, format, 1, system_mail); + HTAddParam(&cmd, format, 2, the_address); + HTAddParam(&cmd, format, 3, the_filename); + HTEndParam(&cmd, format, 4); + } + + 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 = 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, FALSE, 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, FALSE, 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) { + LYStrNCpy(buf, &mailto_content[i], 78); + 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) { + LYStrNCpy(buf, &mailto_content[i], 78); + 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(); + (void) LYRemoveTemp(my_tmpfile); + if (isPMDF) + (void) LYRemoveTemp(hdrfile); +#else /* DOS */ + LYSendMailFile(address, + my_tmpfile, + subject, + ccaddr, + SENDING_FORM_CONTENT); + (void) 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 = 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="); + } + } + + (void) 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); + (void) LYRemoveTemp(my_tmpfile); + if (isPMDF) { + (void) LYRemoveTemp(hdrfile); + } +#else /* DOS */ + LYSendMailFile(address, + my_tmpfile, + subject, + ccaddr, + ""); + (void) 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) +{ + 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; + size_t nbytes; +#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)) { + LYStrNCpy(default_subject, title, MAX_SUBJECT); + } + + /* + * 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: " STR_PLAINTEXT "; 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_mail_name, LINESIZE)) { + goto cancelled; + } + if (*personal_mail_name) { +#if USE_VMS_MAILER + fprintf((isPMDF ? hfd : fd), "%s: %s\n", label, personal_mail_name); +#else + HTSprintf(&header, "%s: %s\n", label, personal_mail_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 (non_empty(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 (non_empty(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(CTRL_G_TO_CANCEL_SEND); + LYaddstr("\n\n"); + LYrefresh(); + *user_input = '\0'; + if (LYGetStr(user_input, FALSE, 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, FALSE, + 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 && non_empty(LynxSigFile)) && + (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 ((nbytes = fread(buf, (size_t) 1, sizeof(buf), fd)) != 0) { + if (fwrite(buf, (size_t) 1, (size_t) nbytes, fp) < nbytes) + break; + } +#if CAN_PIPE_TO_MAILER + pclose(fp); +#else + LYCloseTempFP(fp); /* Close the tmpfile. */ + LYSendMailFile(to_address, + tmpfile2, + the_subject, + ccaddr, + SENDING_COMMENT); + (void) 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) { + (void) LYRemoveTemp(hdrfile); /* Delete the hdrfile. */ + } +#else + FREE(header); + (void) 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 (isEmpty(system_mail) || !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 0000000..f75f686 --- /dev/null +++ b/src/LYMail.h @@ -0,0 +1,88 @@ +/* + * $LynxId: LYMail.h,v 1.17 2011/06/02 10:37:23 tom Exp $ + */ +#ifndef LYMAIL_H +#define LYMAIL_H + +#ifndef LYSTRUCTS_H +#include <LYStructs.h> +#endif /* LYSTRUCTS_H */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef SH_EX +#undef USE_BLAT_MAILER +#define USE_BLAT_MAILER 1 +#endif + +#ifndef USE_ALT_BLAT_MAILER +#define USE_ALT_BLAT_MAILER 0 +#endif + +#ifndef USE_BLAT_MAILER +#define USE_BLAT_MAILER 0 +#endif + +#ifndef ALTBLAT_MAIL_FLAGS +#define ALTBLAT_MAIL_FLAGS "" +#endif + +#ifndef BLAT_MAIL_FLAGS +#define BLAT_MAIL_FLAGS "" +#endif + +#ifdef VMS +#define USE_VMS_MAILER 1 +#else +#define USE_VMS_MAILER 0 +#endif + +#ifndef SYSTEM_MAIL +#define SYSTEM_MAIL "sendmail" +#endif + +#ifndef SYSTEM_MAIL_FLAGS +#define SYSTEM_MAIL_FLAGS "" +#endif + +/* + * Ifdef's in case we have a working popen/pclose, useful for piping to the + * mail program. + */ +#ifndef CAN_PIPE_TO_MAILER +#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 +#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 0000000..ab48cae --- /dev/null +++ b/src/LYMain.c @@ -0,0 +1,4567 @@ +/* + * $LynxId: LYMain.c,v 1.300 2024/01/07 15:31:25 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 <LYMail.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> + +#if defined(HAVE_PWD_H) && defined(HAVE_GETPWUID) && !defined(DONT_HAVE_PW_GECOS) +#define USE_GETPWUID 1 +#include <pwd.h> +#else +#define USE_GETPWUID 0 +#endif + +/* 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 TRACK_INTERNAL_LINKS +BOOLEAN track_internal_links = TRUE; + +#else +BOOLEAN track_internal_links = FALSE; +#endif + +BOOLEAN enable_scrollback = FALSE; + +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 +#ifdef USE_IDN2 +int LYidnaMode = LYidnaTR46; +#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 LYGuessScheme = FALSE; +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 LYtrimBlankLines = TRUE; +BOOLEAN LYtrimInputFields = FALSE; +BOOLEAN LYxhtml_parsing = FALSE; +BOOLEAN bold_H1 = FALSE; +BOOLEAN bold_headers = FALSE; +BOOLEAN bold_name_anchors = FALSE; +BOOLEAN LYcase_sensitive = CASE_SENSITIVE_ALWAYS_ON; +BOOLEAN check_mail = CHECKMAIL; +BOOLEAN child_lynx = FALSE; +BOOLEAN dump_links_decoded = TRUE; +BOOLEAN dump_links_inline = 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 html5_charsets = 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 size_is_changed = FALSE; /* SIGWINCH is caught recently? */ +BOOLEAN soft_dquotes = FALSE; +BOOLEAN unique_urls = 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_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; +BOOLEAN mail_is_altblat = USE_ALT_BLAT_MAILER; + +#if USE_ALT_BLAT_MAILER +#define THIS_BLAT_MAIL ALTBLAT_MAIL +#define THAT_BLAT_MAIL BLAT_MAIL +#else +#define THIS_BLAT_MAIL BLAT_MAIL +#define THAT_BLAT_MAIL ALTBLAT_MAIL +#endif +#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 update_term_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 user's mail address */ +char *personal_mail_name = NULL; /* the user's personal name mail */ +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 */ +unsigned 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 DelaySecs; /* 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 max_uri_size = 8192; +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; +int LYContentType = contentTEXT; +const char *ContentTypes[] = +{ + STR_BINARY, + STR_PLAINTEXT, + STR_HTML +}; +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 */ +char *SSL_client_cert_file = NULL; +char *SSL_client_key_file = NULL; + +int HTprotocolLevel = HTTP_1_0; + +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? */ + +#ifdef LY_FIND_LEAKS +char LYLeaksPath[LY_MAXPATH]; +#endif + +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; +int cookie_version = COOKIES_RFC_6265; + +#ifdef USE_SSL +int ssl_noprompt = FORCE_PROMPT_DFT; +#endif +BOOLEAN conv_jisx0201kana = TRUE; +BOOLEAN wait_viewer_termination = FALSE; + +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 +BOOLEAN assumed_charset = FALSE; + +#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 + +char *socks5_proxy = NULL; + +BOOLEAN LYNonRestartingSIGWINCH = FALSE; +BOOLEAN LYReuseTempfiles = FALSE; +BOOLEAN LYUseBuiltinSuffixes = TRUE; + +int LYNoZapKey = 0; /* 0: off (do z checking), 1: full, 2: initially */ + +#ifndef DISABLE_NEWS +#include <HTNews.h> +#endif + +BOOLEAN FileInitAlreadyDone = FALSE; + +#ifdef USE_PROGRAM_DIR +char *program_dir = NULL; +#endif + +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 GCC_NORETURN void print_help_and_exit(int exit_status); +static void print_help_strings(const char *name, + const char *help, + const char *value, + int 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) +int LYuse_color_style = TRUE; +char *lynx_lss_file = NULL; /* from config-file, etc. */ +static char *lynx_lss_file2 = NULL; /* from command-line options */ +const char *default_color_styles = "\ +lynx.lss;\ +blue-background.lss;\ +bright-blue.lss;\ +midnight.lss;\ +mild-colors.lss;\ +opaque.lss\ +"; +#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, ®s); + 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; + +#if defined(USE_COLOR_STYLE) + clear_lss_list(); +#endif + 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", ""); +#else + (void) putenv("LYNX_VERSION=" 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(personal_mail_name); + FREE(anonftp_password); + FREE(URLDomainPrefixes); + FREE(URLDomainSuffixes); + FREE(XLoadImageCommand); + FREE(lynx_temp_space); + FREE(LYTransferName); + FREE(LYTraceLogPath); + FREE(lynx_cfg_file); + FREE(SSL_cert_file); + FREE(SSL_client_cert_file); + FREE(SSL_client_key_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(FALSE); +#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, + int 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) +#elif defined(OPENSSL_VERSION_TEXT) +#define LYNX_SSL_VERSION OPENSSL_VERSION_TEXT +#elif defined(GNUTLS_VERSION) +#define LYNX_SSL_VERSION "GNUTLS " GNUTLS_VERSION " " +#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 */ + +/* Set the text message domain. */ +void LYSetTextDomain(void) +{ +#if defined(HAVE_LIBINTL_H) || defined(HAVE_LIBGETTEXT_H) + const char *cp; + + if ((cp = LYGetEnv("LYNX_LOCALEDIR")) == 0) { +#ifdef USE_PROGRAM_DIR + char *localedir = NULL; + + HTSprintf0(&localedir, "%s\\locale", program_dir); + cp = localedir; +#else + cp = LOCALEDIR; +#endif + } + bindtextdomain(NLS_TEXTDOMAIN, cp); + textdomain(NLS_TEXTDOMAIN); +#endif +} + +static void SetLocale(void) +{ +#ifdef LOCALE + /* + * LOCALE support for international characters. + */ + setlocale(LC_ALL, ""); +#endif /* LOCALE */ + LYSetTextDomain(); +} + +/* + * 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; + const char *ccp; + char *cp; + FILE *fp; + struct stat dir_info; + char filename[LY_MAXPATH]; + BOOL LYGetStdinArgs = FALSE; + +#if USE_GETPWUID + struct passwd *my_pwd; +#endif + +#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 LY_FIND_LEAKS + /* + * Register the final function to be executed when being exited. Will + * display memory leaks if the -find-leaks option is used. This should + * be the first call to atexit() for leak-checking, which ensures that + * all of the other functions will be invoked before LYLeaks(). + */ + atexit(LYLeaks); + /* + * Register the function which will free our allocated globals. + */ + atexit(free_lynx_globals); + + LYAddPathToHome(LYLeaksPath, (size_t) LY_MAXPATH, LEAKAGE_SINK); +#endif /* LY_FIND_LEAKS */ + +#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 + + LYinitEditmap(); + LYinitKeymap(); +#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_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 (((ccp = LYGetEnv("SHELL")) != NULL) + && (strstr(LYPathLeaf(ccp), "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; +#ifdef USE_PROGRAM_DIR + StrAllocCopy(program_dir, pgm); + if ((cp = strrchr(program_dir, '\\')) != NULL) { + *cp = '\0'; + } else { + FREE(program_dir); + StrAllocCopy(program_dir, "."); + } +#endif + 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 ((ccp = LYGetEnv("LYNX_TRACE_FILE")) == 0) + ccp = FNAME_LYNX_TRACE; + LYTraceLogPath = typeMallocn(char, LY_MAXPATH); + + LYAddPathToHome(LYTraceLogPath, (size_t) LY_MAXPATH, ccp); + + /* + * Act on -version, -trace and -trace-mask NOW. + */ + for (i = 1; i < argc; i++) { + parse_arg(&argv[i], 1, &i); + } + LYOpenTraceLog(); + + 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); + StrAllocCopy(ftp_format, FTP_FORMAT); +#endif /* !VMS */ + + AlertSecs = SECS2Secs(ALERTSECS); + DelaySecs = 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); + StrAllocCopy(system_mail_flags, SYSTEM_MAIL_FLAGS); + + 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 ((ccp = LYGetEnv("LYNX_TEMP_SPACE")) != NULL) + StrAllocCopy(lynx_temp_space, ccp); +#if defined (UNIX) || defined (__DJGPP__) + else if ((ccp = LYGetEnv("TMPDIR")) != NULL) + StrAllocCopy(lynx_temp_space, ccp); +#endif +#if defined (DOSPATH) || defined (__EMX__) + else if ((ccp = LYGetEnv("TEMP")) != NULL) + StrAllocCopy(lynx_temp_space, ccp); + else if ((ccp = LYGetEnv("TMP")) != NULL) + StrAllocCopy(lynx_temp_space, ccp); +#endif + else { +#if defined(USE_PROGRAM_DIR) + StrAllocCopy(lynx_temp_space, program_dir); +#elif defined(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) +#elif USE_GETPWUID + ((my_pwd = getpwuid(getuid())) == NULL + || STREQ(my_pwd->pw_gecos, ANONYMOUS_USER)) +#elif defined(HAVE_CUSERID) + STREQ((char *) cuserid(NULL), ANONYMOUS_USER) +#else + STREQ(((char *) getlogin() == NULL ? " " : getlogin()), ANONYMOUS_USER) +#endif /* checks for user-id */ + ) { + parse_restrictions("default"); + LYRestricted = TRUE; + LYUseTraceLog = FALSE; + } +#ifdef USE_CMD_LOGGING + /* + * Open command-script, if specified + */ + if (non_empty(lynx_cmd_script)) { + LYTildeExpand(&lynx_cmd_script, TRUE); + LYOpenCmdScript(); + } + /* + * Open command-logging, if specified + */ + if (non_empty(lynx_cmd_logfile)) { + 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 (isEmpty(lynx_cfg_file)) { + if (((cp = LYGetEnv("LYNX_CFG")) != NULL) || + (cp = LYGetEnv("lynx_cfg")) != NULL) + StrAllocCopy(lynx_cfg_file, cp); + } +#ifdef USE_PROGRAM_DIR + if (isEmpty(lynx_cfg_file)) { + HTSprintf0(&lynx_cfg_file, "%s\\lynx.cfg", program_dir); + if (!LYCanReadFile(lynx_cfg_file)) { + FREE(lynx_cfg_file); + lynx_cfg_file = NULL; + } + } +#endif + + /* + * If we still don't have a configuration file, use the userdefs.h + * definition. + */ + if (isEmpty(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); + +#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); + + { + static char *client_keyfile = NULL; + static char *client_certfile = NULL; + + if ((client_keyfile = LYGetEnv("SSL_CLIENT_KEY_FILE")) != NULL) { + CTRACE((tfp, + "HTGetSSLHandle: client keyfile is set to %s by SSL_CLIENT_KEY_FILE\n", + client_keyfile)); + StrAllocCopy(SSL_client_key_file, client_keyfile); + } + + if ((client_certfile = LYGetEnv("SSL_CLIENT_CERT_FILE")) != NULL) { + CTRACE((tfp, + "HTGetSSLHandle: client certfile is set to %s by SSL_CLIENT_CERT_FILE\n", + client_certfile)); + StrAllocCopy(SSL_client_cert_file, client_certfile); + } + } + +#if defined(USE_COLOR_STYLE) + if (!dump_output_immediately) { + init_color_styles(&lynx_lss_file2, default_color_styles); + } +#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, (size_t) LY_MAXPATH, FNAME_LYNX_COOKIES); + } else { + LYTildeExpand(&LYCookieFile, FALSE); + } + LYLoadCookies(LYCookieFile); + } + + /* tilde-expand LYCookieSaveFile */ + if (non_empty(LYCookieSaveFile)) { + LYTildeExpand(&LYCookieSaveFile, FALSE); + } +#ifdef USE_PROGRAM_DIR + if (is_url(helpfile) == 0) { + char *tmp = NULL; + + HTSprintf0(&tmp, "%s\\%s", program_dir, helpfile); + FREE(helpfile); + LYLocalFileToURL(&helpfile, tmp); + FREE(tmp); + } +#endif + + /* + * 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. Overriding + * 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 (isEmpty(lynx_save_space)) { + FREE(lynx_save_space); + } + if (non_empty(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 == 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 (non_empty(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 && + !dump_links_inline && + !crawl) /* For -crawl it has already been done! */ + printlist(stdout, FALSE); + if (i != 0) + printf("\n"); + } +#else + status = mainloop(); + if (!no_list && + !dump_links_inline && + !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, LYLynxEditmap); +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 LYLynxEditmap; +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(&LYLynxEditmap); + 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); + (void) LYRemoveTemp(tempfile); + FREE(tempfile); + return; /* can not write the very own file :( */ + } +#ifdef USE_PERSISTENT_COOKIES + if (LYCookieFile != NULL && LYCookieSaveFile != NULL) { + /* set few safe flags: */ + 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); + } +#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); + (void) 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. + */ + /* 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 && LYCookieFile_flag != NULL) { + 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 /* USE_PERSISTENT_COOKIES */ +} +#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 = NULL; + + if (next_arg != NULL) { + StrAllocCopy(auth_info, next_arg); + memset(next_arg, ' ', strlen(next_arg)); /* Let's not show too much */ + } + + if (auth_info != NULL) { + char *cp; + + if ((cp = StrChr(auth_info, ':')) != NULL) { /* 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) +{ + assumed_charset = TRUE; + UCLYhndl_for_unspec = safeUCGetLYhndl_byMIME(next_arg); + StrAllocCopy(UCAssume_MIMEcharset, + LYCharSet_UC[UCLYhndl_for_unspec].MIMEname); + CTRACE((tfp, "assume_charset_fun %s ->%d ->%s\n", + NonNull(next_arg), + UCLYhndl_for_unspec, + 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 == WWW_DUMP) + HTOutputFormat = 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 + +/* -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; +} + +/* -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 */ +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 ? + WWW_DOWNLOAD : 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 &= ~(unsigned) 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; +} + +/* -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; +} + +/* -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 < TABLESIZE(table); 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 += (unsigned) (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 ? + WWW_DOWNLOAD : 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); + +/* + * Define NO_BUILDSTAMP if you really want an executable with no timestamp in + * the -version message. + */ +#ifdef NO_BUILDSTAMP +#define BUILDSTAMP "" +#else +#define BUILDSTAMP " (" __DATE__ " " __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.\n"), SYSTEM_NAME, BUILDSTAMP); +#elif defined(__CYGWIN__) + printf("Compiled by CYGWIN%s.\n", BUILDSTAMP); +#elif defined(__BORLANDC__) + printf("Compiled by Borland C++%s.\n", BUILDSTAMP); +#elif defined(_MSC_VER) + printf("Compiled by Microsoft Visual C++%s.\n", BUILDSTAMP); +#elif defined(__DJGPP__) + printf("Compiled by DJGPP%s.\n", BUILDSTAMP); +#elif !defined(NO_BUILDSTAMP) + printf("Compiled at %s %s.\n", __DATE__, __TIME__); +#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 https://lynx.invisible-island.net/ 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 < 40)) { + scrsize_x = 40; + } + if ((scrsize_y > 0) && (scrsize_y < 6)) { + scrsize_y = 6; + } + 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" + ), +#if USE_BLAT_MAILER + PARSE_SET( + "altblat", 4|TOGGLE_ARG, mail_is_altblat, + "select mail tool (`"THIS_BLAT_MAIL"' ==> `"THAT_BLAT_MAIL"')" + ), +#endif + 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 " STR_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, LYcase_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 + PARSE_SET( + "collapse_br_tags", 4|TOGGLE_ARG, LYCollapseBRs, + "toggles collapsing of BR tags" + ), +#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" + ), + PARSE_FUN( + "convert_to", 4|FUNCTION_ARG, convert_to_fun, + "=FORMAT\nconvert input, FORMAT is in MIME type notation\n(experimental)" + ), +#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 +#ifdef USE_DEFAULT_COLORS + PARSE_SET( + "default_colors", 4|TOGGLE_ARG, LYuse_default_colors, + "use terminal default foreground/background colors" + ), +#endif + PARSE_INT( + "delay", 4|NEED_TIME_ARG, DelaySecs, + "=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", 1|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( + "html5_charsets", 4|TOGGLE_ARG, html5_charsets, + "toggles use of HTML5 charset replacements" + ), + 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( + "list_decoded", 4|TOGGLE_ARG, dump_links_decoded, + "with -dump, forces it to decode URL-encoded links" + ), + PARSE_SET( + "list_inline", 4|TOGGLE_ARG, dump_links_inline, + "with -dump, forces it to show links inline with text" + ), + 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 (`"THIS_BLAT_MAIL"' ==> `"SYSTEM_MAIL"')" + ), +#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" + ), + PARSE_FUN( + "nozap", 4|FUNCTION_ARG, nozap_fun, + "=DURATION (\"initially\" or \"full\") disable checks for 'z' key" + ), + 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 " STR_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_STR( + "socks5_proxy", 2|NEED_LYSTRING_ARG, socks5_proxy, + "=URL\n(via which) SOCKS5 proxy to connect (unrelated to -nosocks!)" + ), + 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_blank_lines", 2|TOGGLE_ARG, LYtrimBlankLines, + "\ntoggle trimming of leading/trailing/collapsed-br blank lines" + ), + 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" + ), + PARSE_SET( + "unique_urls", 4|TOGGLE_ARG, unique_urls, + "toggles use of unique-urls setting for -dump and -listonly options" + ), + PARSE_SET( + "update_term_title", 4|SET_ARG, update_term_title, + "enables updating the title of terminal emulators" + ), +#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, + int 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) + && (countp != 0) + && (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 (((unsigned) (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) = (int) 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 0000000..55be205 --- /dev/null +++ b/src/LYMainLoop.c @@ -0,0 +1,8208 @@ +/* + * $LynxId: LYMainLoop.c,v 1.254 2024/01/15 19:10:04 Gisle.Vanem 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 + +#ifdef PREVENT_KEYBOARD_WRAPAROUND +#define HandleForwardWraparound() \ + *old_c = real_c; \ + HTInfoMsg(ALREADY_AT_END) +#define HandleReverseWraparound() \ + *old_c = real_c; \ + HTInfoMsg(ALREADY_AT_BEGIN) +#else +#define HandleForwardWraparound() \ + LYSetNewline(1) +#define HandleReverseWraparound() \ + int i; \ + i = HText_getNumOfLines() - display_lines + 2; \ + if (i >= 1 && Newline != i) { \ + LYSetNewline(i); \ + *arrowup = TRUE; \ + } +#endif + +#define LinkIsTextarea(linkNumber) \ + (links[linkNumber].type == WWW_FORM_LINK_TYPE && \ + links[linkNumber].l_form->type == F_TEXTAREA_TYPE) + +#define LinkIsTextLike(linkNumber) \ + (links[linkNumber].type == WWW_FORM_LINK_TYPE && \ + F_TEXTLIKE(links[linkNumber].l_form->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((unsigned char *) from, (unsigned char *) 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; + +#define NONINTERNAL_OR_PHYS_DIFFERENT(p,n) \ + ((track_internal_links && \ + (!curdoc.internal_link || are_phys_different(p,n))) || \ + are_different(p,n)) + +#define NO_INTERNAL_OR_DIFFERENT(c,n) \ + (track_internal_links || are_different(c,n)) + +static void exit_immediately_with_error_message(int state, int first_file); +static void status_link(const char *curlink_name, int show_more, int 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); + +static int are_phys_different(DocInfo *doc1, DocInfo *doc2); + +#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 bstring *prev_target = NULL; + +#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->str); + 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(FALSE, curdoc.link, prev_target->str); + 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(TRUE, nextlink, prev_target->str); + 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(bstring **user_input, + 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)->str[0] == '#') { + if ((*user_input)->str[1] && + HTFindPoundSelector((*user_input)->str + 1)) { + /* HTFindPoundSelector will initialize www_search_result, + so we do nothing else. */ + HTAddGotoURL((*user_input)->str); + trimPoundSelector(curdoc.address); + StrAllocCat(curdoc.address, (*user_input)->str); + } + } else { + /* + * If it's not a URL then make it one. + */ + StrAllocCopy(*old_user_input, (*user_input)->str); + LYEnsureAbsoluteURL(old_user_input, "", TRUE); + BStrCopy0((*user_input), *old_user_input); + FREE(*old_user_input); + + for (n = 0; n < TABLESIZE(table); n++) { + if (*(table[n].flag) + && !StrNCmp((*user_input)->str, + 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)->str) && + !isHTTPS_URL((*user_input)->str)) { + HTUserMsg(GOTO_NON_HTTP_DISALLOWED); + + } else { + set_address(&newdoc, (*user_input)->str); + newdoc.isHEAD = FALSE; + /* + * 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)) { + /* + * 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, + bstring **user_input, + 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 = (int) strlen((*user_input)->str); + + if (len >= 3) { + if (len < MAX_LINE - 1 + && LYIsHtmlSep((*user_input)->str[len - 3]) + && LYIsDosDrive((*user_input)->str + len - 2)) + LYAddPathSep0((*user_input)->str); + + } else if (len == 2 && (*user_input)->str[1] == ':') { + if (LYIsDosDrive((*user_input)->str)) { + LYAddPathSep0((*user_input)->str); + } else { + HTUserMsg2(WWW_ILLEGAL_URL_MESSAGE, (*user_input)->str); + BStrCopy0((*user_input), *old_user_input); + FREE(*old_user_input); + ret = FALSE; + break; + } + } +#endif + /* + * Get rid of leading spaces (and any other spaces). + */ + LYTrimAllStartfile((*user_input)->str); + if (isBEmpty(*user_input) && + !(recall && (ch == UPARROW_KEY || ch == DNARROW_KEY))) { + BStrCopy0((*user_input), *old_user_input); + FREE(*old_user_input); + HTInfoMsg(CANCELLED); + ret = FALSE; + break; + } + if (recall && ch == UPARROW_KEY) { + 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) { + BStrCopy0((*user_input), cp); + if (goto_buffer + && **old_user_input + && !strcmp(*old_user_input, (*user_input)->str)) { + _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 = LYgetBString(user_input, FALSE, 0, recall)) < 0) { + /* + * User cancelled the Goto via ^G. Restore + * user_input and break. - FM + */ + BStrCopy0((*user_input), *old_user_input); + FREE(*old_user_input); + HTInfoMsg(CANCELLED); + ret = FALSE; + break; + } + continue; + } + } else if (recall && ch == DNARROW_KEY) { + 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) { + BStrCopy0((*user_input), cp); + if (goto_buffer && **old_user_input && + !strcmp(*old_user_input, (*user_input)->str)) { + _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 = LYgetBString(user_input, FALSE, 0, recall)) < 0) { + /* + * User cancelled the Goto via ^G. Restore + * user_input and break. - FM + */ + BStrCopy0((*user_input), *old_user_input); + 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_KEY; + } 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_KEY; + } + } + } else { + StrAllocCopy(traversal_link_to_add, + links[curdoc.link].lname); + if (!isLYNXIMGMAP(traversal_link_to_add)) + *crawl_ok = TRUE; + c = RTARROW_KEY; + } + } 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_KEY; + } 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_KEY; + } + } + } + CTRACE((tfp, "DoTraversal(%d:%d) -> %s\n", + nlinks > 0 ? curdoc.link : 0, + nlinks, + LYKeycodeToString(c, FALSE))); + return c; +} + +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; +} + +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 (isEmpty(links[curdoc.link].l_form->submit_action)) { + 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) || + isLYNXCACHE(links[curdoc.link].l_form->submit_action) || +#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) || + isLYNXEDITMAP(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_MOUSE_SUBMIT && + !textinput_activated)) ? + FOR_PANEL : FOR_INPUT); + if (user_mode == NOVICE_MODE && + textinput_activated && + (real_cmd == LYK_ACTIVATE || + real_cmd == LYK_MOUSE_SUBMIT)) { + form_noviceline(FormIsReadonly(links[curdoc.link].l_form)); + } + } + + *c = change_form_link(curdoc.link, + &newdoc, refresh_screen, + FALSE, + (real_cmd == LYK_MOUSE_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 == 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_MOUSE_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) && + !((isLYNXEDITMAP(curdoc.address) || + isLYNXKEYMAP(curdoc.address) || + isLYNXCOOKIE(curdoc.address) || + isLYNXCACHE(curdoc.address)) && + !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)); + /* + * 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 (track_internal_links) { + /* + * 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); + } + } + /* + * 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 && + curdoc.address && + !strcmp(lynxjumpfile, curdoc.address))) { + LYUserSpecifiedURL = TRUE; + } else if (no_filereferer == TRUE && + curdoc.address != NULL && + 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) + || isLYNXCACHE(curdoc.address)) { + HTuncache_current_document(); + } + } + } + return 0; +} +/* + * If the given form link does not point to the requested type, search for + * the first link belonging to the form which does. If there are none, + * return null. + */ +#define SameFormAction(form,submit) \ + ((submit) \ + ? (F_SUBMITLIKE((form)->type)) \ + : ((form)->type == F_RESET_TYPE)) + +static FormInfo *FindFormAction(FormInfo * given, int submit) +{ + FormInfo *result = NULL; + FormInfo *fi; + int i; + + if (given == NULL) { + HTAlert(LINK_NOT_IN_FORM); + } else if (SameFormAction(given, submit)) { + result = given; + } else { + for (i = 0; i < nlinks; i++) { + if ((fi = links[i].l_form) != 0 && + fi->number == given->number && + (SameFormAction(fi, submit))) { + result = fi; + break; + } + } + } + return result; +} + +static FormInfo *MakeFormAction(FormInfo * given, int submit) +{ + FormInfo *result = 0; + + if (given != 0) { + result = typecalloc(FormInfo); + + if (result == NULL) + outofmem(__FILE__, "MakeFormAction"); + + *result = *given; + if (submit) { + if (result->submit_action == 0) { + PerFormInfo *pfi = HText_PerFormInfo(result->number); + + *result = pfi->data; + } + result->type = F_SUBMIT_TYPE; + } else { + result->type = F_RESET_TYPE; + } + result->number = given->number; + } + return result; +} + +static void handle_LYK_SUBMIT(int cur, DocInfo *doc, BOOLEAN *refresh_screen) +{ + FormInfo *form = FindFormAction(links[cur].l_form, 1); + FormInfo *make = NULL; + char *save_submit_action = NULL; + + if (form == 0) { + make = MakeFormAction(links[cur].l_form, 1); + form = make; + } + + if (form != 0) { + StrAllocCopy(save_submit_action, form->submit_action); + form->submit_action = HTPrompt(EDIT_SUBMIT_URL, form->submit_action); + + if (isEmpty(form->submit_action) || + (!isLYNXCGI(form->submit_action) && + StrNCmp(form->submit_action, "http", 4))) { + HTUserMsg(FORM_ACTION_NOT_HTTP_URL); + } else { + HTInfoMsg(SUBMITTING_FORM); + HText_SubmitForm(form, doc, form->name, form->value); + *refresh_screen = TRUE; + } + + StrAllocCopy(form->submit_action, save_submit_action); + FREE(make); + } +} + +static void handle_LYK_RESET(int cur, BOOLEAN *refresh_screen) +{ + FormInfo *form = FindFormAction(links[cur].l_form, 0); + FormInfo *make = NULL; + + if (form == 0) { + make = MakeFormAction(links[cur].l_form, 0); + form = make; + } + + if (form != 0) { + HTInfoMsg(RESETTING_FORM); + HText_ResetForm(form); + *refresh_screen = TRUE; + FREE(make); + } +} + +#ifdef USE_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 /* USE_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) && + !isLYNXCACHE(curdoc.address) && + !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) && + !isLYNXCACHE(links[curdoc.link].lname) && + !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(bstring **user_input) +{ + LYKeymapCode ch; + Kcmd *mp; + char *src, *tmp; + + BStrCopy0((*user_input), ""); + _statusline(": "); + if (LYgetBString(user_input, FALSE, 0, RECALL_CMD) >= 0) { + src = LYSkipBlanks((*user_input)->str); + 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'; + } else if ((cp = strrchr(VMSdir, ':')) != NULL) { + *(cp + 1) = '\0'; + } + } + 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 (isEmpty(links[curdoc.link].l_form->submit_action) || + isLYNXOPTIONS(links[curdoc.link].l_form->submit_action)) { + if (*old_c != real_c) { + *old_c = real_c; + HTUserMsg(NO_DOWNLOAD_SPECIAL); + } + return 0; + } + HTOutputFormat = 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 && + !LYstrstr(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 = 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) || + isLYNXCACHE(links[curdoc.link].lname) || + 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)); + /* + * Might be an internal link in the same doc from a POST form. If + * so, don't free the content. - kw + */ + if (track_internal_links) { + if (links[curdoc.link].type != WWW_INTERN_LINK_TYPE) { + LYFreePostData(&newdoc); + FREE(newdoc.bookmark); + newdoc.isHEAD = FALSE; + newdoc.safe = FALSE; + } + } 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)) { + LYFreePostData(&newdoc); + FREE(newdoc.bookmark); + newdoc.isHEAD = FALSE; + newdoc.safe = FALSE; + } + } + newdoc.internal_link = FALSE; + newdoc.link = (user_mode == NOVICE_MODE) ? 1 : 0; + HTOutputFormat = 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) { + HandleForwardWraparound(); + } +} + +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) { + HandleForwardWraparound(); + } +} + +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_EDITTEXTAREA; + 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, + bstring **user_input, + 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 string, and load the current + * document's address. + */ + StrAllocCopy(*old_user_input, (*user_input)->str); + BStrCopy0((*user_input), curdoc.address); + + /* + * 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 = LYgetBString(user_input, FALSE, 0, RECALL_URL)) >= 0) && + !isBEmpty(*user_input) && + strcmp((*user_input)->str, curdoc.address)) { + LYTrimAllStartfile((*user_input)->str); + if (!isBEmpty(*user_input)) { + return 2; + } + } + /* + * User cancelled via ^G, a full deletion, or not modifying the URL. - FM + */ + HTInfoMsg(CANCELLED); + BStrCopy0((*user_input), *old_user_input); + 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 && + !FormIsReadonly(links[curdoc.link].l_form) && + LinkIsTextLike(curdoc.link)) { + *cshelpfile = STR_LYNXEDITMAP; + } +} + +static void handle_LYK_EDITMAP(int *old_c, + int real_c) +{ + if (*old_c != real_c) { + *old_c = real_c; + set_address(&newdoc, STR_LYNXEDITMAP); + StrAllocCopy(newdoc.title, CURRENT_EDITMAP_TITLE); + LYFreePostData(&newdoc); + FREE(newdoc.bookmark); + newdoc.isHEAD = FALSE; + newdoc.safe = FALSE; + newdoc.internal_link = FALSE; +#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_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); + } + } else if (LinkIsTextarea(curdoc.link)) { + /* + * if the current link is in a form TEXTAREA, it requires handling + * for the possible multiple lines. + */ + + /* stop screen */ + stop_curses(); + + (void) HText_EditTextArea(&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 if (LinkIsTextLike(curdoc.link)) { + /* + * other text fields are single-line + */ + stop_curses(); + HText_EditTextField(&links[curdoc.link]); + start_curses(); + *refresh_screen = TRUE; + } else { + + HTInfoMsg(NOT_IN_TEXTAREA_NOEDIT); + } +} + +static int handle_LYK_ELGOTO(int *ch, + bstring **user_input, + 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) && + (isEmpty(links[curdoc.link].l_form->submit_action))) { + /* + * 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 string, and load the current link's + * address. - FM + */ + StrAllocCopy(*old_user_input, (*user_input)->str); + BStrCopy0((*user_input), + ((links[curdoc.link].type == WWW_FORM_LINK_TYPE) + ? links[curdoc.link].l_form->submit_action + : links[curdoc.link].lname)); + /* + * Offer the current link's URL for editing. - FM + */ + _statusline(EDIT_CURLINK_URL); + if (((*ch = LYgetBString(user_input, FALSE, 0, RECALL_URL)) >= 0) && + !isBEmpty(*user_input) && + strcmp((*user_input)->str, + ((links[curdoc.link].type == WWW_FORM_LINK_TYPE) + ? links[curdoc.link].l_form->submit_action + : links[curdoc.link].lname))) { + LYTrimAllStartfile((*user_input)->str); + if (!isBEmpty(*user_input)) { + return 2; + } + } + /* + * User cancelled via ^G, a full deletion, or not modifying the URL. - FM + */ + HTInfoMsg(CANCELLED); + BStrCopy0((*user_input), *old_user_input); + 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, + bstring **user_input, + 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)->str); + if (!goto_buffer) + BStrCopy0((*user_input), ""); + + *URLTotal = (Goto_URLs ? HTList_count(Goto_URLs) : 0); + if (goto_buffer && !isBEmpty(*user_input)) { + *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 = LYgetBString(user_input, FALSE, 0, *recall)) < 0) { + /* + * User cancelled the Goto via ^G. Restore user_input and + * break. - FM + */ + BStrCopy0((*user_input), *old_user_input); + 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 && + FormIsReadonly(links[curdoc.link].l_form)) { + 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(int 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 +#ifdef USE_PROGRAM_DIR + if (is_url(indexfile) == 0) { + char *tmp = NULL; + + HTSprintf0(&tmp, "%s\\%s", program_dir, indexfile); + FREE(indexfile); + LYLocalFileToURL(&indexfile, tmp); + FREE(tmp); + } +#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, + int 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 const char *hexy = "0123456789ABCDEF"; + +#define HEX(n) hexy[(n) & 0xf] +/* + * URL-encode a parameter which can then be appended to a URI. + * RFC-3986 lists reserved characters, which should be encoded. + */ +static char *urlencode(char *str) +{ + char *result = NULL; + char *ptr; + int ch; + + if (str != NULL) { + result = malloc(strlen(str) * 3 + 1); + ptr = result; + + if (result == NULL) + outofmem(__FILE__, "urlencode"); + + while ((ch = UCH(*str++)) != 0) { + if (ch == ' ') { + *ptr = '+'; + ptr++; + } else if (ch > 127 || + StrChr(":/?#[]@!$&'()*+,;=", ch) != 0) { + *ptr++ = '%'; + *ptr++ = HEX(ch >> 4); + *ptr++ = HEX(ch); + } else { + *ptr++ = (char) ch; + } + } + *ptr = '\0'; + } + + return result; +} + +/* + * Fill in "%s" marker(s) in the url_template by prompting the user for the + * values. + */ +static BOOLEAN check_JUMP_param(char **url_template) +{ + int param = 1; + char *subs; + char *result = *url_template; + char *encoded = NULL; + int code = TRUE; + bstring *input = NULL; + + CTRACE((tfp, "check_JUMP_param: %s\n", NONNULL(result))); + + while (result != NULL && (subs = strstr(result, "%s")) != 0) { + char prompt[MAX_LINE]; + RecallType recall = NORECALL; + + CTRACE((tfp, "Prompt for query param%d: %s\n", param, result)); + + sprintf(prompt, gettext("Query parameter %d: "), param++); + statusline(prompt); + BStrCopy0(input, ""); + + if (encoded) + FREE(encoded); + + if (LYgetBString(&input, FALSE, 0, recall) < 0) { + /* + * cancelled via ^G + */ + HTInfoMsg(CANCELLED); + code = FALSE; + break; + } else if ((encoded = urlencode(input->str)) != NULL && *encoded != '\0') { + int subs_at = (int) (subs - result); + int fill_in = (int) strlen(encoded) - 2; + size_t have = strlen(result); + size_t want = strlen(encoded) + have - 1; + int n; + char *update = realloc(result, want + 1); + + if (update == 0) { + HTInfoMsg(NOT_ENOUGH_MEMORY); + code = FALSE; + break; + } + + CTRACE((tfp, " reply: %s\n", input->str)); + CTRACE((tfp, " coded: %s\n", encoded)); + + result = update; + result[want] = '\0'; + for (n = (int) want; (n - fill_in) >= subs_at; --n) { + result[n] = result[n - fill_in]; + } + for (n = subs_at; encoded[n - subs_at] != '\0'; ++n) { + result[n] = encoded[n - subs_at]; + } + CTRACE((tfp, " subst: %s\n", result)); + } else { + HTInfoMsg(CANCELLED); + code = FALSE; + break; + } + } + BStrFree(input); + FREE(encoded); + *url_template = result; + return (BOOLEAN) code; +} + +static void fill_JUMP_Params(char **addressp) +{ + if (LYJumpFileURL) { + check_JUMP_param(addressp); + } +} + +static BOOLEAN handle_LYK_JUMP(int c, + bstring **user_input, + 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)->str); + *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_KEY; + return TRUE; + } + FREE(*old_user_input); + HTUserMsg(NO_RANDOM_URLS_YET); + return FALSE; + } + ret = HTParse((ret + 3), startfile, PARSE_ALL); + BStrCopy0((*user_input), ret); + FREE(ret); + return TRUE; + } +#endif /* PERMIT_GOTO_FROM_JUMP */ + ret = HTParse(ret, startfile, PARSE_ALL); + if (!LYTrimStartfile(ret)) { + LYRemoveBlanks((*user_input)->str); + } + if (check_JUMP_param(&ret)) { + set_address(&newdoc, ret); + StrAllocCopy(lynxjumpfile, ret); + LYFreePostData(&newdoc); + FREE(newdoc.bookmark); + newdoc.isHEAD = FALSE; + newdoc.safe = FALSE; + newdoc.internal_link = FALSE; + LYUserSpecifiedURL = TRUE; + } + FREE(ret); + } 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(FALSE, curdoc.link, prev_target->str); +#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)))) { + /* + * 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 */ +#else + (void) refresh_screen; +#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; + } +#else + (void) cmd; +#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(FALSE, curdoc.link, prev_target->str); +#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) { + HandleForwardWraparound(); + } +} + +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) { + HandleReverseWraparound(); + } +} + +#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 = REAL_RELOAD; + + 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(FALSE, curdoc.link); + break; + } + } + if (!found) { + if (tagged == NULL) + tagged = HTList_new(); + tagname = NULL; + StrAllocCopy(tagname, links[curdoc.link].lname); + HTList_addObject(tagged, tagname); + tagflag(TRUE, 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) { + HandleReverseWraparound(); + } +} + +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) { + HandleReverseWraparound(); + } +} + +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) (!isBEmpty(prev_target) && + 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->str); + 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. + */ + BStrCopy0(prev_target, ""); + } + found = textsearch(&curdoc, &prev_target, + (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 ((LYcase_sensitive && 0 != strcmp(prev_target->str, + remember_old_target)) || + (!LYcase_sensitive && 0 != strcasecomp8(prev_target->str, + 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, + 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)); + /* + * 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 (track_internal_links) { + 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->str); + 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); + } + } + /* + * 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 + */ + HTSprintf0(&temp, LINK_ALREADY_CURRENT, number); + HTUserMsg(temp); + 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 { + HTSprintf0(&temp, ALREADY_AT_PAGE, number); + HTUserMsg(temp); + 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 bstring *buf = NULL; + char *p = NULL; + + if (no_chdir) { + HTUserMsg(CHDIR_DISABLED); + return; + } + + _statusline(gettext("cd to:")); + if (LYgetBString(&buf, FALSE, 0, NORECALL) < 0 || isBEmpty(buf)) { + HTInfoMsg(CANCELLED); + return; + } + + if (LYIsTilde(buf->str[0]) && + (LYIsPathSep(buf->str[1]) || buf->str[1] == '\0')) { + HTSprintf0(&p, "%s%s", Home_Dir(), buf->str + 1); + } else { + StrAllocCopy(p, buf->str); + } + + 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); +} + +static void handle_LYK_PWD(void) +{ + char buffer[LY_MAXPATH]; + int save_secs = InfoSecs; + BOOLEAN save_wait = no_pause; + + if (Secs2SECS(save_secs) < 1) + InfoSecs = SECS2Secs(1); + no_pause = FALSE; + + HTInfoMsg(Current_Dir(buffer)); + + InfoSecs = save_secs; + no_pause = save_wait; +} +#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; + int code = FALSE; + + CTRACE((tfp, "Entering handle_LYK_LINEWRAP_TOGGLE\n")); + if (LYwin != stdscr) { + /* 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, (int) 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) { + CTRACE((tfp, + "...setting LYwideLines %d, LYtableCols %d (have %d and %d)\n", + c, wrap[c], + LYwideLines, + LYtableCols)); + + LYwideLines = c; + LYtableCols = wrap[c]; + + if (LYwideLines == 0) + LYshiftWin = 0; + *flag = TRUE; + HTUserMsg(LYwideLines ? LINEWRAP_OFF : LINEWRAP_ON); + code = reparse_or_reload(cmd); + } + } + return (BOOLEAN) code; +} +#endif + +#ifdef USE_MAXSCREEN_TOGGLE +static BOOLEAN handle_LYK_MAXSCREEN_TOGGLE(int *cmd) +{ + static int flag = 0; + + CTRACE((tfp, "Entering handle_LYK_MAXSCREEN_TOGGLE\n")); + if (flag) { + CTRACE((tfp, "Calling recoverWindowSize()\n")); + recoverWindowSize(); + flag = 0; + } else { + CTRACE((tfp, "Calling maxmizeWindowSize()\n")); + maxmizeWindowSize(); + flag = 1; + } + return reparse_or_reload(cmd); +} +#endif + +#ifdef LY_FIND_LEAKS +#define CleanupMainLoop() \ + BStrFree(prev_target); \ + BStrFree(user_input_buffer) +#else +#define CleanupMainLoop() /* nothing */ +#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; + bstring *user_input_buffer = NULL; + 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(STR_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 + BStrCopy0(user_input_buffer, ""); + BStrCopy0(prev_target, ""); +#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; + + /* + * 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 (track_internal_links && + curdoc.internal_link && + !are_phys_different(&curdoc, &newdoc)) { + LYinternal_flag = TRUE; + LYoverride_no_cache = TRUE; + LYforce_no_cache = FALSE; + try_internal = TRUE; + } else { + /* + * 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). + */ + BStrCopy0(prev_target, ""); /* 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; + + if (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; + fill_JUMP_Params(&newdoc.address); + getresult = getfile(&newdoc, &tmpNewline); + if (!reloading && !popped_doc && (tmpNewline >= 0)) { + LYSetNewline(tmpNewline); + } else { + newdoc.link = tmpDocInfo.link; + } + } + } else { + tmpDocInfo = newdoc; + tmpNewline = -1; + fill_JUMP_Params(&newdoc.address); + getresult = getfile(&newdoc, &tmpNewline); + if (!reloading && !popped_doc && (tmpNewline >= 0)) { + LYSetNewline(tmpNewline); + } else { + newdoc.link = tmpDocInfo.link; + } + } + +#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)) && + !isLYNXCACHE(NonNull(newdoc.address)) && + !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. + */ + CleanupMainLoop(); + 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 { + CleanupMainLoop(); + 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 = (unsigned) 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); + CleanupMainLoop(); + 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); + } + CleanupMainLoop(); + return ((dump_server_status >= 400) ? EXIT_FAILURE : EXIT_SUCCESS); + } + + /* + * If the recent_sizechange variable is set to TRUE then the window + * size changed recently. + */ + CheckScreenSize(); + 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(USE_CURSES_RESIZE) + resizeterm(LYlines, LYcols); + wresize(LYwin, LYlines, LYcols); + wgetch(LYtopwindow()); /* eat the KEY_RESIZE */ +#else + stop_curses(); + start_curses(); + LYclear(); +#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, STR_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->str); + +#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->str); + +#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 && + LinkIsTextLike(curdoc.link)); + + 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 || user_mode == MINIMAL_MODE) { + p = curdoc.title; + } else { + p = links[curdoc.link].lname; + } + + if (strlen(p) < ((sizeof(sjis_buff) / 2) - 1)) { + 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 || user_mode == MINIMAL_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 && curdoc.link >= 0) { + 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(TRUE, curdoc.link, prev_target->str); +#ifndef INACTIVE_INPUT_STYLE_VH + } else if (!textinput_activated) { + LYhighlight(TRUE, curdoc.link, prev_target->str); +#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 { +#ifdef UNIX + FILE *fp = (dump_output_immediately + ? stderr + : stdout); + +#else + FILE *fp = stdout; +#endif + if (!dump_output_immediately) + cleanup(); + fprintf(fp, + gettext("Fatal error - could not open output file %s\n"), + cfile); + CleanupMainLoop(); + 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(FormIsReadonly(links[curdoc.link].l_form)); + } + 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') && + LinkIsTextLike(curdoc.link) && + !textfields_need_activation) { + c = DO_NOTHING; + } +#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION + } else if (LinkIsTextarea(curdoc.link) + && textfields_need_activation + && !FormIsReadonly(links[curdoc.link].l_form) + && 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 + CheckScreenSize(); + 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) { + CleanupMainLoop(); + 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; + + CTRACE((tfp, "Handling key %d as %s\n", cmd, + ((LYKeycodeToKcmd((LYKeymapCode) cmd) != 0) + ? LYKeycodeToKcmd((LYKeymapCode) cmd)->name + : "unknown"))); + switch (cmd) { + case -1: + HTUserMsg(COMMAND_UNKNOWN); + break; + case 0: /* unmapped character */ + default: + if (curdoc.link >= 0 && curdoc.link < nlinks && + LinkIsTextLike(curdoc.link)) { + +#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, &old_c, real_c, &try_internal); + break; + + case LYK_SOURCE: /* toggle view source mode */ + handle_LYK_SOURCE(&ownerS_address); + break; + + case LYK_CHANGE_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()) { + CleanupMainLoop(); + return (EXIT_SUCCESS); + } + break; + + case LYK_ABORT: /* don't ask the user about quitting */ + CleanupMainLoop(); + 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 = typeMallocn(char, len); + + LYStrNCpy(buf, (const char *) s, (e - s)); + 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 != '\n') + *t = *s1; + t++, s1++; + } + } + } else + *t++ = *s++; + } + *t = '\0'; + get_clip_release(); + BStrCopy0(user_input_buffer, buf); + do_check_goto_URL(&user_input_buffer, &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: + CleanupMainLoop(); + 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_MOUSE_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_SUBMIT: + handle_LYK_SUBMIT(curdoc.link, &newdoc, &refresh_screen); + break; + + case LYK_RESET: + handle_LYK_RESET(curdoc.link, &refresh_screen); + 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_EDITTEXTAREA: /* use external editor on a TEXTAREA - KED */ + handle_LYK_EDIT_TEXTAREA(&refresh_screen, &old_c, real_c); + break; + + case LYK_GROWTEXTAREA: /* add new lines to bottom of TEXTAREA - KED */ + handle_LYK_GROW_TEXTAREA(&refresh_screen); + break; + + case LYK_INSERTFILE: /* 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 USE_ADDRLIST_PAGE + case LYK_ADDRLIST: /* always list URL's (only) */ + if (handle_LYK_ADDRLIST(&cmd)) + goto new_cmd; + break; +#endif /* USE_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_EDITMAP: + handle_LYK_EDITMAP(&old_c, real_c); + 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; + case LYK_PWD: + handle_LYK_PWD(); + 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 + +#ifdef USE_MAXSCREEN_TOGGLE + case LYK_MAXSCREEN_TOGGLE: + if (handle_LYK_MAXSCREEN_TOGGLE(&cmd)) + 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 + */ +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); +} + +/* + * 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 *mycopy = NULL; + char *old; + HTList *cur; + + if (isEmpty(url)) + return; + + CTRACE((tfp, "HTAddGotoURL %s\n", url)); + StrAllocCopy(mycopy, url); + + if (!Goto_URLs) { + Goto_URLs = HTList_new(); +#ifdef LY_FIND_LEAKS + atexit(HTGotoURLs_free); +#endif + HTList_addObject(Goto_URLs, mycopy); + return; + } + + cur = Goto_URLs; + while (NULL != (old = (char *) HTList_nextObject(cur))) { + if (!strcmp(old, mycopy)) { + HTList_removeObject(Goto_URLs, old); + FREE(old); + break; + } + } + HTList_addObject(Goto_URLs, mycopy); + + 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 || user_mode == MINIMAL_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 ((user_mode == MINIMAL_MODE) && nlinks > 0) { + /* + * no URL + */ + status_link("", 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 if (user_mode != MINIMAL_MODE) { + _statusline(HELP); + } else { + _statusline(""); + } + + /* 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); + } + LYParkCursor(); + + 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, int 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(); + + if (buf != 0) { +#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(const char *curlink_name, + int show_more, + int 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 - 10) { + _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 0000000..bd2926a --- /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 0000000..29b60f1 --- /dev/null +++ b/src/LYMap.c @@ -0,0 +1,646 @@ +/* + * $LynxId: LYMap.c,v 1.50 2018/03/05 22:32:14 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; + BOOLEAN intern_flag; +} LYMapElement; + +typedef struct _LYImageMap { + char *address; + char *title; + HTList *elements; +} LYImageMap; + +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 track_internal_links is false, 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 + */ + if (track_internal_links && 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 { + 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, + int 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 + */ + if (track_internal_links && 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 { + 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); + if (track_internal_links) + tmp->intern_flag = (BOOLEAN) intern_flag; + 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 track_internal_links is false, 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) +{ + HTList *result; + + if (anchor->post_data) { + fill_DocAddress(wwwdoc, address, anchor, punderlying); + if (non_empty(punderlying)) { + result = (*punderlying)->imaps; + } else { + result = anchor->imaps; + } + } else { + fill_DocAddress(wwwdoc, address, NULL, punderlying); + result = LynxMaps; + } + return result; +} + +/* 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); + } + } + if (track_internal_links) + anAnchor->no_cache = TRUE; + + target = HTStreamStack(format_in, + format_out, + sink, anAnchor); + + if (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=\"" STR_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> %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("\""); + if (track_internal_links && tmp->intern_flag) { + PUTS(" TYPE=\"internal link\""); + } + 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(); + size_t 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); + if (track_internal_links && elt->intern_flag) + fprintf(fp, " TYPE=\"internal link\""); + 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 0000000..7c0b9ff --- /dev/null +++ b/src/LYMap.h @@ -0,0 +1,29 @@ +/* $LynxId: LYMap.h,v 1.12 2010/09/25 11:35:42 tom Exp $ */ +#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, + int 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 0000000..bb49289 --- /dev/null +++ b/src/LYNews.c @@ -0,0 +1,509 @@ +/* + * $LynxId: LYNews.c,v 1.62 2018/03/18 18:51:02 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, + int 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); + (void) 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, FALSE, + 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, FALSE, + 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, (int) 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, FALSE, + 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, FALSE, + 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, FALSE, + 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 ((non_empty(LynxSigFile)) && (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); + (void) 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) + (void) LYRemoveTemp(my_tempfile); + (void) 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 0000000..9a6a3f6 --- /dev/null +++ b/src/LYNews.h @@ -0,0 +1,19 @@ +/* $LynxId: LYNews.h,v 1.10 2010/09/25 11:35:12 tom Exp $ */ +#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, int followup); + +#ifdef __cplusplus +} +#endif +#endif /* LYNEWSPOST_H */ diff --git a/src/LYOptions.c b/src/LYOptions.c new file mode 100644 index 0000000..828aacc --- /dev/null +++ b/src/LYOptions.c @@ -0,0 +1,4365 @@ +/* $LynxId: LYOptions.c,v 1.186 2023/01/05 09:17:16 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> + +#ifdef USE_COLOR_STYLE +#include <LYStyle.h> +#endif + +#include <LYLeaks.h> + +BOOLEAN term_options; + +#define TOP_LINK "/" +#define MBM_LINK "//MBM_MENU" + +#define MARGIN_STR (no_margins ? "" : " ") +#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); + } + } +} + +#if defined(USE_SLANG) || defined(COLOR_CURSES) +static void SetupChosenShowColor(void) +{ + 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, + STRING2PTR choices); + +#define LYChooseBoolean(status, line, column, choices) \ + (BOOLEAN) boolean_choice(status, line, column, (const char *const*)choices) + +#define LYChooseEnum(status, line, column, choices) \ + boolean_choice(status, line, column, (const char *const*)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 *const 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", + "Minimal", + 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. + */ + bstring *my_data = NULL; + char *choices[MAXCHOICES]; + int CurrentCharSet = current_char_set; + int CurrentAssumeCharSet = UCLYhndl_for_unspec; + int CurrentShowColor = LYShowColor; + BOOLEAN CurrentRawMode = LYRawMode; + BOOLEAN AddValueAccepted = FALSE; + 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(LYcase_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(LYEditorNames[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" : + ((user_mode == ADVANCED_MODE) ? "Advanced " : + "Minimal "))); + + 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)) { + BStrCopy0(my_data, editor); + } else { /* clear the NONE */ + LYmove(L_EDITOR, COL_OPTION_VALUES); + LYaddstr(" "); + BStrCopy0(my_data, ""); + } + _statusline(ACCEPT_DATA); + LYmove(L_EDITOR, COL_OPTION_VALUES); + lynx_start_bold(); + ch = LYgetBString(&my_data, FALSE, 0, NORECALL); + lynx_stop_bold(); + LYmove(L_EDITOR, COL_OPTION_VALUES); + if (term_options || ch == -1) { + LYaddstr(non_empty(editor) ? + editor : "NONE"); + } else if (isBEmpty(my_data)) { + FREE(editor); + LYaddstr("NONE"); + } else { + StrAllocCopy(editor, my_data->str); + LYaddstr(editor); + } + LYclrtoeol(); + if (ch == -1) { + HTInfoMsg(CANCELLED); + HTInfoMsg(""); + } else { + _statusline(VALUE_ACCEPTED); + } + } + response = ' '; + break; + + case 'D': /* Change the display. */ + if (non_empty(x_display)) { + BStrCopy0(my_data, x_display); + } else { /* clear the NONE */ + LYmove(L_DISPLAY, COL_OPTION_VALUES); + LYaddstr(" "); + BStrCopy0(my_data, ""); + } + _statusline(ACCEPT_DATA); + LYmove(L_DISPLAY, COL_OPTION_VALUES); + lynx_start_bold(); + ch = LYgetBString(&my_data, FALSE, 0, 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, my_data->str))) { + /* + * 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 (isBEmpty(my_data)) { + 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(my_data->str); + validate_x_display(); + LYaddstr(x_display ? x_display : "NONE"); + LYclrtoeol(); + summarize_x_display(my_data->str); + 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)) { + BStrCopy0(my_data, bookmark_page); + } else { /* clear the NONE */ + LYmove(L_HOME, C_DEFAULT); + LYclrtoeol(); + BStrCopy0(my_data, ""); + } + _statusline(ACCEPT_DATA); + LYmove(L_HOME, C_DEFAULT); + lynx_start_bold(); + ch = LYgetBString(&my_data, FALSE, 0, NORECALL); + lynx_stop_bold(); + LYmove(L_HOME, C_DEFAULT); + BStrAlloc(my_data, my_data->len + LY_MAXPATH); /* lengthen */ + if (term_options || + ch == -1 || isBEmpty(my_data)) { + LYaddstr(non_empty(bookmark_page) ? + bookmark_page : "NONE"); + } else if (!LYPathOffHomeOK(my_data->str, (size_t) my_data->len)) { + LYaddstr(non_empty(bookmark_page) ? + bookmark_page : "NONE"); + LYclrtoeol(); + _statusline(USE_PATH_OFF_HOME); + response = ' '; + break; + } else { + StrAllocCopy(bookmark_page, my_data->str); + 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)) { + BStrCopy0(my_data, personal_mail_address); + } else { /* clear the NONE */ + LYmove(L_MAIL_ADDRESS, COL_OPTION_VALUES); + LYaddstr(" "); + BStrCopy0(my_data, ""); + } + _statusline(ACCEPT_DATA); + LYmove(L_MAIL_ADDRESS, COL_OPTION_VALUES); + lynx_start_bold(); + ch = LYgetBString(&my_data, FALSE, 0, 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 (isBEmpty(my_data)) { + FREE(personal_mail_address); + LYaddstr("NONE"); + } else { + StrAllocCopy(personal_mail_address, my_data->str); + LYaddstr(personal_mail_address); + } + LYclrtoeol(); + if (ch == -1) { + HTInfoMsg(CANCELLED); + HTInfoMsg(""); + } else { + _statusline(VALUE_ACCEPTED); + } + response = ' '; + break; + + case 'S': /* Change case sensitivity for searches. */ + LYcase_sensitive = LYChooseBoolean(LYcase_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 + 1, 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)) { + BStrCopy0(my_data, language); + } else { /* clear the NONE */ + LYmove(L_LANGUAGE, COL_OPTION_VALUES); + LYaddstr(" "); + BStrCopy0(my_data, ""); + } + _statusline(ACCEPT_DATA); + LYmove(L_LANGUAGE, COL_OPTION_VALUES); + lynx_start_bold(); + ch = LYgetBString(&my_data, FALSE, 0, NORECALL); + lynx_stop_bold(); + LYmove(L_LANGUAGE, COL_OPTION_VALUES); + if (term_options || ch == -1) { + LYaddstr(non_empty(language) ? + language : "NONE"); + } else if (isBEmpty(my_data)) { + FREE(language); + LYaddstr("NONE"); + } else { + StrAllocCopy(language, my_data->str); + LYaddstr(language); + } + LYclrtoeol(); + if (ch == -1) { + HTInfoMsg(CANCELLED); + HTInfoMsg(""); + } else { + _statusline(VALUE_ACCEPTED); + } + response = ' '; + break; + + case 'H': /* Change charset preference. */ + if (non_empty(pref_charset)) { + BStrCopy0(my_data, pref_charset); + } else { /* clear the NONE */ + LYmove(L_PREF_CHARSET, COL_OPTION_VALUES); + LYaddstr(" "); + BStrCopy0(my_data, ""); + } + _statusline(ACCEPT_DATA); + LYmove(L_PREF_CHARSET, COL_OPTION_VALUES); + lynx_start_bold(); + ch = LYgetBString(&my_data, FALSE, 0, 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 (isBEmpty(my_data)) { + FREE(pref_charset); + LYaddstr("NONE"); + } else { + StrAllocCopy(pref_charset, my_data->str); + LYaddstr(pref_charset); + } + 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, + LYEditorNames); + } else { + current_lineedit = LYChoosePopup(current_lineedit, + L_Lineed, -1, + LYEditorNames, + 0, FALSE, FALSE); +#if defined(VMS) || defined(USE_SLANG) + LYmove(L_Lineed, COL_OPTION_VALUES); + LYclrtoeol(); + LYaddstr(LYEditorNames[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 == ADVANCED_MODE); + } else { + user_mode = LYChoosePopup(user_mode, + L_User_Mode, -1, + userMode_choices, + 3, FALSE, FALSE); + use_assume_charset = (BOOLEAN) (user_mode == ADVANCED_MODE); +#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)) { + BStrCopy0(my_data, LYUserAgent); + } else { /* clear the NONE */ + LYmove(L_HOME, COL_OPTION_VALUES); + LYaddstr(" "); + BStrCopy0(my_data, ""); + } + _statusline(ACCEPT_DATA_OR_DEFAULT); + LYmove(L_User_Agent, COL_OPTION_VALUES); + lynx_start_bold(); + ch = LYgetBString(&my_data, FALSE, 0, NORECALL); + lynx_stop_bold(); + LYmove(L_User_Agent, COL_OPTION_VALUES); + if (term_options || ch == -1) { + LYaddstr((LYUserAgent && + *LYUserAgent) ? + LYUserAgent : "NONE"); + } else if (isBEmpty(my_data)) { + StrAllocCopy(LYUserAgent, LYUserAgentDefault); + LYaddstr((LYUserAgent && + *LYUserAgent) ? + LYUserAgent : "NONE"); + } else { + StrAllocCopy(LYUserAgent, my_data->str); + LYaddstr(LYUserAgent); + } + 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); + BStrFree(my_data); + return; +} + +static int widest_choice(STRING2PTR 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 = 0; + + if (choice != 0) { + 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, + STRING2PTR 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 */ + bstring *my_data = NULL; + + /* + * 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); + BStrCopy0(my_data, + (!MBM_A_subdescript[a] ? + "" : MBM_A_subdescript[a])); + (void) LYgetBString(&my_data, FALSE, 0, NORECALL); + lynx_stop_bold(); + + if (isBEmpty(my_data)) { + FREE(MBM_A_subdescript[a]); + } else { + StrAllocCopy(MBM_A_subdescript[a], my_data->str); + } + 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(); + BStrCopy0(my_data, NonNull(MBM_A_subbookmark[a])); + (void) LYgetBString(&my_data, FALSE, 0, NORECALL); + lynx_stop_bold(); + + if (isBEmpty(my_data)) { + if (a == 0) + StrAllocCopy(MBM_A_subbookmark[a], bookmark_page); + else + FREE(MBM_A_subbookmark[a]); + } else { + BStrAlloc(my_data, my_data->len + LY_MAXPATH); + if (!LYPathOffHomeOK(my_data->str, (size_t) my_data->len)) { + LYMBM_statusline(USE_PATH_OFF_HOME); + LYSleepAlert(); + } else { + StrAllocCopy(MBM_A_subbookmark[a], my_data->str); + 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(); + LYParkCursor(); + break; + } + } /* end for */ + } /* end while */ + + BStrFree(my_data); + 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, + STRING2PTR choices, + int i_length, + int disabled, + int for_mouse) +{ + if (column < 0) + column = (COL_OPTION_VALUES - 1); + + term_options = FALSE; + cur_choice = LYhandlePopupList(cur_choice, + line, + column, + (STRING2PTR) 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; + +#define END_OPTIONS {0, 0, 0} + +#define HasOptValues(table) (((table) != NULL) && ((table)->LongName != NULL)) + +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"}, + END_OPTIONS +}; + +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 + END_OPTIONS +}; +#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"}, + END_OPTIONS +}; +static const char *lineedit_mode_string = RC_LINEEDIT_MODE; +static const char *mail_address_string = RC_PERSONAL_MAIL_ADDRESS; +static const char *personal_name_string = RC_PERSONAL_MAIL_NAME; +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"}, + END_OPTIONS +}; + +#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}, + END_OPTIONS +}; +#endif + +#ifdef USE_COLOR_STYLE +static const char *color_style_string = RC_COLOR_STYLE; +static OptValues *color_style_values; +static HTList *color_style_list; +#endif + +#ifdef USE_DEFAULT_COLORS +static const char *default_colors_string = RC_DEFAULT_COLORS; +#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}, + END_OPTIONS +}; +static const char *cookie_prompt_string = RC_FORCE_COOKIE_PROMPT; + +static const char RFC_2109_string[] = N_("RFC 2109"); +static const char RFC_2965_string[] = N_("RFC 2965"); +static const char RFC_6265_string[] = N_("RFC 6265"); +static OptValues cookies_values[] = +{ + {COOKIES_RFC_2109, RFC_2109_string, RFC_2109_string}, + {COOKIES_RFC_2965, RFC_2965_string, RFC_2965_string}, + {COOKIES_RFC_6265, RFC_6265_string, RFC_6265_string}, + END_OPTIONS +}; +static const char *cookie_version_string = RC_COOKIE_VERSION; + +#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"}, + {MINIMAL_MODE, N_("Minimal"), "Minimal"}, + END_OPTIONS +}; + +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"}, + END_OPTIONS +}; + +/* + * 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"}, + END_OPTIONS +}; + +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"}, + END_OPTIONS +}; + +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"}, + END_OPTIONS +}; + +static const char *collapse_br_tags_string = RC_COLLAPSE_BR_TAGS; +static OptValues collapse_br_tags_values[] = +{ + /* LYCollapseBRs variable */ + {FALSE, N_("OFF"), "OFF"}, + {TRUE, N_("collapse"), "ON"}, + END_OPTIONS +}; + +static const char *trim_blank_lines_string = RC_TRIM_BLANK_LINES; +static OptValues trim_blank_lines_values[] = +{ + /* LYtrimBlankLines variable */ + {FALSE, N_("OFF"), "OFF"}, + {TRUE, N_("trim-lines"), "ON"}, + END_OPTIONS +}; + +/* + * 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"}, + END_OPTIONS +}; + +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_IDN2 +static const char *idna_mode_string = RC_IDNA_MODE; +static OptValues idna_values[] = +{ + {LYidna2003, N_("IDNA 2003"), "idna2003"}, + {LYidna2008, N_("IDNA 2008"), "idna2008"}, + {LYidnaTR46, N_("IDNA TR46"), "idnaTR46"}, + {LYidnaCompat, N_("IDNA Compatible"), "idnaCompat"}, + END_OPTIONS +}; +#endif + +#ifdef USE_LOCALE_CHARSET +static const char *locale_charset_string = RC_LOCALE_CHARSET; +#endif + +static const char *html5_charsets_string = RC_HTML5_CHARSETS; + +/* + * 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"}, + END_OPTIONS +}; + +#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 + END_OPTIONS +}; +#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"}, + END_OPTIONS +}; +#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"}, + {rateEtaBYTES2, N_("Show %s/sec (2-digits), ETA"), "rate_eta_bytes2"}, + {rateEtaKB2, N_("Show %s/sec (2-digits), ETA"), "rate_eta_kb2"}, +#endif +#ifdef USE_PROGRESSBAR + {rateBAR, N_("Show progressbar"), "rate_bar"}, +#endif + END_OPTIONS +}; +#endif /* USE_READPROGRESS */ + +static const char *preferred_content_string = RC_PREFERRED_CONTENT_TYPE; +static OptValues content_values[] = +{ + {contentBINARY, STR_BINARY, STR_BINARY}, + {contentTEXT, STR_PLAINTEXT, STR_PLAINTEXT}, + {contentHTML, STR_HTML, STR_HTML}, + END_OPTIONS +}; + +/* + * 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"}, + END_OPTIONS +}; + +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 +#if defined(USE_BROTLI) || defined(BROTLI_PATH) + {encodingBROTLI, N_("brotli"), "encoding_brotli"}, +#endif + {encodingALL, N_("All"), "encoding_all"}, + END_OPTIONS +}; + +/* + * Headers transferred to remote server + */ +static const char *http_protocol_string = RC_HTTP_PROTOCOL; +static OptValues http_protocol_values[] = +{ + {HTTP_1_0, N_("HTTP 1.0"), "HTTP_1_0"}, + {HTTP_1_1, N_("HTTP 1.1"), "HTTP_1_1"}, + END_OPTIONS +}; + +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; + +static const char *ssl_client_certificate_file = RC_SSL_CLIENT_CERT_FILE; +static const char *ssl_client_key_file = RC_SSL_CLIENT_KEY_FILE; + +#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; +} + +#ifdef USE_COLOR_STYLE +#ifdef LY_FIND_LEAKS +void free_colorstyle_leaks(void) +{ + FREE(color_style_values); +} +#endif + +void build_lss_enum(HTList *list) +{ + int count = HTList_count(list); + +#ifdef LY_FIND_LEAKS + atexit(free_colorstyle_leaks); +#endif + + FREE(color_style_values); + if (count != 0) { + LSS_NAMES *obj; + int position = 0; + + color_style_values = typecallocn(OptValues, count + 2); + + if (color_style_values == NULL) + outofmem(__FILE__, "build_lss_enum"); + + color_style_values[position++] = bool_values[0]; + while ((obj = HTList_objectAt(list, position - 1)) != 0) { + color_style_values[position].value = position; + color_style_values[position].LongName = obj->given; + color_style_values[position].HtmlName = obj->given; + position++; + } + } + color_style_list = list; +} + +/* + * Find the current lss-file in the list, to get the default value for the + * form. + */ +static int get_color_style_value(void) +{ + int result = 0; + + if (LYuse_color_style && non_empty(lynx_lss_file)) { + LSS_NAMES *obj; + int position = 1; + + while ((obj = HTList_objectAt(color_style_list, position - 1)) != 0) { + if (obj->actual != 0 && !strcmp(obj->actual, lynx_lss_file)) { + result = position; + break; + } else if (!strcmp(obj->given, lynx_lss_file)) { + result = position; + break; + } + ++position; + } + } + return result; +} + +/* + * Return the pathname found in the given list-item. + */ +static char *get_color_style_config(int code) +{ + char *result = 0; + + if (LYuse_color_style) { + LSS_NAMES *obj; + + if ((obj = HTList_objectAt(color_style_list, code - 1)) != 0) { + result = obj->actual; + } + } + return result; +} +#endif + +/* + * 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)"); + + 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)"); + + 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 = (unsigned) 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 +#ifdef USE_DEFAULT_COLORS + BOOLEAN current_default_colors = LYuse_default_colors; +#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; LYEditorNames[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); + } +#ifndef NO_ANONYMOUS_EMAIL + /* Personal Name: INPUT */ + if (!strcmp(data[i].tag, personal_name_string)) { + FREE(personal_mail_name); + StrAllocCopy(personal_mail_name, data[i].value); + } +#endif + + /* 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)) { + LYcase_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(); + } +#ifdef USE_SLANG + SLtt_Use_Ansi_Colors = (LYShowColor > SHOW_COLOR_OFF ? TRUE : FALSE); +#endif + } +#endif /* USE_SLANG || COLOR_CURSES */ + +#ifdef USE_COLOR_STYLE + /* Color Style: ON/OFF */ + if (!strcmp(data[i].tag, color_style_string) + && GetOptValues(color_style_values, data[i].value, &code)) { + if (code) { + LYuse_color_style = TRUE; + StrAllocCopy(lynx_lss_file, get_color_style_config(code)); + reinit_color_styles(); + } else { + LYuse_color_style = FALSE; + } + update_color_style(); + lynx_force_repaint(); + } +#endif + +#ifdef USE_DEFAULT_COLORS + /* Default Colors: ON/OFF */ + if (!strcmp(data[i].tag, default_colors_string) + && GetOptValues(bool_values, data[i].value, &code)) { + LYuse_default_colors = (BOOLEAN) code; + if (current_default_colors != LYuse_default_colors) { + CTRACE((tfp, "default_colors changed, updating colors...\n")); + if (has_colors()) { + if (LYuse_default_colors) { + use_default_colors(); + } else { + restart_curses(); + } + update_default_colors(); + lynx_force_repaint(); + } + } + } +#endif + + /* 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 Version: SELECT */ + if (!strcmp(data[i].tag, cookie_version_string)) + GetOptValues(cookies_values, data[i].value, &cookie_version); + + /* 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; + } + } + + /* Collapse BR Tags: ON/OFF */ + if (!strcmp(data[i].tag, collapse_br_tags_string) + && GetOptValues(collapse_br_tags_values, data[i].value, &code)) { + if (LYCollapseBRs != code) { + LYCollapseBRs = (BOOLEAN) code; + need_reload = TRUE; + } + } + + /* Trim Blank Lines: ON/OFF */ + if (!strcmp(data[i].tag, trim_blank_lines_string) + && GetOptValues(trim_blank_lines_values, data[i].value, &code)) { + if (LYtrimBlankLines != code) { + LYtrimBlankLines = (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 + /* Use HTML5 charset replacements: ON/OFF */ + if (!strcmp(data[i].tag, html5_charsets_string) + && GetOptValues(bool_values, data[i].value, &code)) { + html5_charsets = (BOOLEAN) code; + assume_char_set_changed = TRUE; + } + + /* 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; + } + } +#ifdef USE_IDN2 + /* Internationalized Domain Names: SELECT */ + if (!strcmp(data[i].tag, idna_mode_string) + && GetOptValues(idna_values, data[i].value, &code)) { + LYidnaMode = code; + } +#endif + + /* 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 Content Type: SELECT */ + if (!strcmp(data[i].tag, preferred_content_string)) { + GetOptValues(content_values, data[i].value, &LYContentType); + } + + /* 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; + } + } + + /* + * HTTP protocol: SELECT + */ + if (!strcmp(data[i].tag, http_protocol_string)) { + GetOptValues(http_protocol_values, data[i].value, &HTprotocolLevel); + } + + /* Send User Agent: INPUT */ + if (!strcmp(data[i].tag, send_user_agent_string)) { + LYSendUserAgent = (BOOLEAN) !strcasecomp(data[i].value, "ON"); + } + + if (!strcmp(data[i].tag, ssl_client_certificate_file)) { + FREE(SSL_client_cert_file); + StrAllocCopy(SSL_client_cert_file, data[i].value); + } + + if (!strcmp(data[i].tag, ssl_client_key_file)) { + FREE(SSL_client_key_file); + StrAllocCopy(SSL_client_key_file, data[i].value); + } + + /* 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 */ + + 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)))) { + /* + * 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 = (long) 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 saveable 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, NonNull(name))); + + if (will_save_rc(value) && !no_option_save) { + while (need++ < want) + fprintf(fp, " "); + } else { + want -= 3; + if (need < want) { + fprintf(fp, " "); + ++need; + } + fprintf(fp, "(!)"); + while (need++ < want) { + fprintf(fp, " "); + } + } + 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(STRING2PTR 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, LYcase_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 Version: SELECT */ + PutLabel(fp0, gettext("Cookie RFC-version"), cookie_version_string); + BeginSelect(fp0, cookie_version_string); + PutOptValues(fp0, cookie_version, cookies_values); + 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); + + PutLabel(fp0, gettext("SSL client certificate file"), ssl_client_certificate_file); + PutTextInput(fp0, ssl_client_certificate_file, + NonNull(SSL_client_cert_file), text_len, ""); + + PutLabel(fp0, gettext("SSL client key file"), ssl_client_key_file); + PutTextInput(fp0, ssl_client_key_file, + NonNull(SSL_client_key_file), text_len, ""); + +#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 (LYEditorNames[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; LYEditorNames[i]; i++) { + char temp[DigitsOf(i) + 3]; + + sprintf(temp, "%d", i); + PutOption(fp0, i == current_lineedit, temp, LYEditorNames[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[DigitsOf(i) + 3]; + + 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 + PutLabel(fp0, gettext("Use HTML5 charset replacements"), html5_charsets_string); + BeginSelect(fp0, html5_charsets_string); + PutOptValues(fp0, html5_charsets, bool_values); + EndSelect(fp0); + + /* 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[DigitsOf(i) + 3]; + 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); + } + +#ifdef USE_IDN2 + /* Internationalized Domain Names: SELECT */ + { + PutLabel(fp0, gettext("Internationalized domain names"), idna_mode_string); + BeginSelect(fp0, idna_mode_string); + for (i = 0; idna_values[i].value != 0; i++) { + PutOption(fp0, idna_values[i].value == LYidnaMode, + idna_values[i].HtmlName, + idna_values[i].LongName); + } + EndSelect(fp0); + } +#endif + + /* 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 */ + +#ifdef USE_COLOR_STYLE + /* Color style: ON/OFF */ + if (HasOptValues(color_style_values)) { + PutLabel(fp0, gettext("Color style"), color_style_string); + BeginSelect(fp0, color_style_string); + PutOptValues(fp0, get_color_style_value(), color_style_values); + EndSelect(fp0); + } +#endif + +#ifdef USE_DEFAULT_COLORS + /* Default colors: ON/OFF */ + if (has_colors()) { + PutLabel(fp0, gettext("Default colors"), default_colors_string); + BeginSelect(fp0, default_colors_string); + PutOptValues(fp0, LYuse_default_colors, bool_values); + EndSelect(fp0); + } +#endif + + /* 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); + + /* Collapse BR Tags: ON/OFF */ + PutLabel(fp0, gettext("Collapse BR tags"), collapse_br_tags_string); + BeginSelect(fp0, collapse_br_tags_string); + PutOptValues(fp0, LYCollapseBRs, collapse_br_tags_values); + EndSelect(fp0); + + /* Trim blank lines: ON/OFF */ + PutLabel(fp0, gettext("Trim blank lines"), trim_blank_lines_string); + BeginSelect(fp0, trim_blank_lines_string); + PutOptValues(fp0, LYtrimBlankLines, trim_blank_lines_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, ""); + +#ifndef NO_ANONYMOUS_EMAIL + PutLabel(fp0, gettext("Personal name for mail"), personal_name_string); + PutTextInput(fp0, personal_name_string, + NonNull(personal_mail_name), text_len, ""); +#endif + + /* Anonymous FTP Address: INPUT */ +#ifndef DISABLE_FTP + PutLabel(fp0, gettext("Password for anonymous ftp"), anonftp_password_string); + PutTextInput(fp0, anonftp_password_string, + NonNull(anonftp_password), text_len, ""); +#endif + + /* Preferred content type: SELECT */ + PutLabel(fp0, gettext("Preferred content type"), preferred_content_string); + BeginSelect(fp0, preferred_content_string); + PutOptValues(fp0, LYContentType, content_values); + EndSelect(fp0); + + /* 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, ""); + + /* HTTP protocol SELECT */ + PutLabel(fp0, gettext("HTTP protocol"), http_protocol_string); + BeginSelect(fp0, http_protocol_string); + PutOptValues(fp0, HTprotocolLevel, http_protocol_values); + EndSelect(fp0); + + /* 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 0000000..2811f2a --- /dev/null +++ b/src/LYOptions.h @@ -0,0 +1,46 @@ +/* $LynxId: LYOptions.h,v 1.32 2017/01/01 01:51:23 tom Exp $ */ +#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, + STRING2PTR choices, + int length, + int disabled, + int mouse); + +#define LYChoosePopup(cur, line, column, choices, length, disabled, mouse) \ + popup_choice(cur, line, column, (STRING2PTR) 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 USE_COLOR_STYLE + extern void build_lss_enum(HTList *); +#endif + +#if defined(USE_COLOR_STYLE) && defined(LY_FIND_LEAKS) + extern void free_colorstyle_leaks(void); +#endif + +#ifdef __cplusplus +} +#endif +#endif /* LYOPTIONS_H */ diff --git a/src/LYPrettySrc.c b/src/LYPrettySrc.c new file mode 100644 index 0000000..c2ff39e --- /dev/null +++ b/src/LYPrettySrc.c @@ -0,0 +1,427 @@ +/* + * $LynxId: LYPrettySrc.c,v 1.36 2018/03/06 10:27:28 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(const char *tagname) +{ + HTTag *tag = SGMLFindTag(&HTML_dtd, tagname); + + return (tag && tag != &HTTag_unrecognized) ? (int) (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(const 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); + if (subj == 0) + outofmem(__FILE__, "append_close_tag"); + + subj->element = (HTMLElement) idx; + + subj->present = typecallocn(BOOL, (unsigned) nattr); + + if (subj->present == 0) + outofmem(__FILE__, "append_close_tag"); + + subj->value = typecallocn(char *, (unsigned) nattr); + + if (subj->value == 0) + outofmem(__FILE__, "append_close_tag"); + + 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(const char *tagname, + const 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 + if (non_empty(classname)) { + hcode = color_style_3(tagname, ".", classname); + StrAllocCopy(subj->class_name, classname); + } else { + hcode = color_style_1(tagname); + 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, + int checkonly, + int 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(int dont_exit) +{ + int i; + char *p; + char buf[1000]; + static char empty[] = ""; + + 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 : empty, + (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 0000000..aa59063 --- /dev/null +++ b/src/LYPrettySrc.h @@ -0,0 +1,92 @@ +/* + * $LynxId: LYPrettySrc.h,v 1.13 2020/01/21 21:38:33 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 perform 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, + int checkonly, int isstart); + extern void HTMLSRC_init_caches(int 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 applied 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 0000000..58b81a6 --- /dev/null +++ b/src/LYPrint.c @@ -0,0 +1,1461 @@ +/* + * $LynxId: LYPrint.c,v 1.109 2021/07/29 20:38:35 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(bstring **filename, + const char *sug_filename) +{ + HTFormat format; + HTAtom *encoding; + char *cp; + + BStrCopy0(*filename, sug_filename); /* add suggestion info */ + /* + * FIXME: the history-recall still uses fixed-size buffers + */ + if ((*filename)->len >= LY_MAXPATH) { + (*filename)->str[LY_MAXPATH - 1] = '\0'; + } else { + BStrAlloc(*filename, LY_MAXPATH); + } + change_sug_filename((*filename)->str); + if (!(HTisDocumentSource()) + && (cp = strrchr((*filename)->str, '.')) != NULL) { + format = HTFileFormat((*filename)->str, &encoding, NULL); + CTRACE((tfp, "... format %s\n", format->name)); + if (!strcasecomp(format->name, STR_HTML) || + !IsUnityEnc(encoding)) { + (*filename)->len = (int) (cp - (*filename)->str); + BStrCat0(*filename, TEXT_SUFFIX); + } + } + CTRACE((tfp, "... result %s\n", (*filename)->str)); +} + +#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(bstring **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 = LYgetBString(filename, FALSE, 0, recall)) < 0 || + isBEmpty(*filename) || ch == UPARROW_KEY || ch == DNARROW_KEY) { + if (recall && ch == UPARROW_KEY) { + 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) { + BStrCopy0(*filename, cp); + if (*total == 1) { + _statusline(EDIT_THE_PREV_FILENAME); + } else { + _statusline(EDIT_A_PREV_FILENAME); + } + return FN_READ; + } + } else if (recall && ch == DNARROW_KEY) { + 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) { + BStrCopy0(*filename, cp); + 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; + bstring *buffer = NULL; + bstring *filename = NULL; + int FnameNum = -1; + int FnameTotal; + int c = 0; + + _statusline(FILENAME_PROMPT); + + retry: + SetupFilename(&filename, sug_filename); + if (non_empty(lynx_save_space)) { + BStrCopy0(buffer, lynx_save_space); + BStrCat(buffer, filename); + BStrCopy(filename, buffer); + } else { + BStrCopy0(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 (c = LYValidateOutput(buffer->str)) { + 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->str, c)); + +#ifdef HAVE_POPEN + if (buffer->str[0] == '|') { + if (no_shell) { + HTUserMsg(SPAWNING_DISABLED); + FirstRecall = TRUE; + FnameNum = FnameTotal; + goto retry; + } else if ((outfile_fp = popen(buffer->str + 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->str) + : LYNewTxtFile(buffer->str))) == 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=\"" STR_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->str)) + pclose(outfile_fp); + else +#endif + LYCloseOutput(outfile_fp); + +#ifdef VMS + if (0 == strncasecomp(buffer->str, "sys$disk:", 9)) { + if (0 == StrNCmp((buffer->str + 9), "[]", 2)) { + HTAddSugFilename(buffer->str + 11); + } else { + HTAddSugFilename(buffer->str + 9); + } + } else { + HTAddSugFilename(buffer->str); + } +#else + HTAddSugFilename(buffer->str); +#endif /* VMS */ + + done: + BStrFree(buffer); + BStrFree(filename); + 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; + bstring *user_response = NULL; + + 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); + BStrCopy0(user_response, NonNull(personal_mail_address)); + if (LYgetBString(&user_response, FALSE, 0, RECALL_MAIL) < 0 || + isBEmpty(user_response)) { + 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 response: this is rather technical + * information, show URL. + */ + FREE(subject); + StrAllocCopy(subject, "HEAD "); + StrAllocCat(subject, newdoc->address); + } +#if USE_VMS_MAILER + if (StrChr(user_response->str, '@') && + !StrChr(user_response->str, ':') && + !StrChr(user_response->str, '%') && + !StrChr(user_response->str, '"')) { + char *temp = 0; + + HTSprintf0(&temp, mail_adrs, user_response->str); + BStrCopy0(user_response, temp); + 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: " STR_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: " STR_PLAINTEXT "; 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->str); + } 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->str); + } + + stop_curses(); + SetOutputMode(O_TEXT); + printf(MAILING_FILE); + LYSystem(buffer); + LYSleepAlert(); + start_curses(); + SetOutputMode(O_BINARY); + + if (isPMDF) + (void) LYRemoveTemp(hdrfile); + (void) 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: " STR_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: " STR_PLAINTEXT "; 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->str, 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->str, + my_temp, + subject, + "", + ""); + (void) LYRemoveTemp(my_temp); /* Delete the tmpfile. */ +#endif /* CAN_PIPE_TO_MAILER */ +#endif /* USE_VMS_MAILER */ + + done: /* send_file_to_mail() */ + BStrFree(user_response); + 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; + bstring *my_file = NULL; + 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. + */ + BStrCopy0(my_file, ""); + 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->str) == '.') { + 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->str)) { + CancelPrint(PRINT_REQUEST_CANCELLED); + } + HTAddSugFilename(my_file->str); + } +#ifdef SH_EX /* 1999/01/04 (Mon) 09:37:03 */ + HTAddParam(&the_command, cur_printer->command, 1, my_temp); + if (!isBEmpty(my_file)) { + HTAddParam(&the_command, cur_printer->command, 2, my_file->str); + 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->str); + 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); + (void) 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() */ + BStrFree(my_file); + return; +} + +static void send_file_to_screen(DocInfo *newdoc, + char *content_base, + int Lpansi) +{ + FILE *outfile_fp; + bstring *prompt = NULL; + + if (Lpansi) { + _statusline(CHECK_PRINTER); + } else { + _statusline(PRESS_RETURN_TO_BEGIN); + } + + BStrCopy0(prompt, ""); + if (LYgetBString(&prompt, FALSE, 0, NORECALL) < 0) { + CancelPrint(PRINT_REQUEST_CANCELLED); + } else { + 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() */ + BStrFree(prompt); + 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 = LYstrstr(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 (LYstrstr(link_info, "LOCAL_FILE")) { + type = TO_FILE; + } else if (LYstrstr(link_info, "TO_SCREEN")) { + type = TO_SCREEN; + } else if (LYstrstr(link_info, "LPANSI")) { + Lpansi = TRUE; + type = TO_SCREEN; + } else if (LYstrstr(link_info, "MAIL_FILE")) { + type = MAIL; + } else if (LYstrstr(link_info, "PRINTER")) { + type = PRINTER; + + if ((cp = LYstrstr(link_info, "number=")) != NULL) { + /* number of characters in "number=" */ + cp += 7; + printer_number = atoi(cp); + } + if ((cp = LYstrstr(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; + + bstring *fbuf = NULL; + bstring *tbuf = NULL; + char *result = NULL; + + 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 cleanup; + 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->str, "sys$disk:", 9)) { + if (0 == StrNCmp((fbuf->str + 9), "[]", 2)) { + HTAddSugFilename(fbuf->str + 11); + } else { + HTAddSugFilename(fbuf->str + 9); + } + } else { + HTAddSugFilename(fbuf->str); + } +#else + HTAddSugFilename(fbuf->str); +#endif /* VMS */ + + /* + * Expand tilde's, make filename absolute, etc. + */ + BStrCopy0(tbuf, ""); + if (!LYValidateFilename(&tbuf, &fbuf)) + goto cleanup; + + /* + * Check for file existence; readability. + */ + if ((stat(tbuf->str, &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->str)) { + 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. + */ + StrAllocCopy(result, tbuf->str); + + cleanup: + BStrFree(fbuf); + BStrFree(tbuf); + return (result); +} diff --git a/src/LYPrint.h b/src/LYPrint.h new file mode 100644 index 0000000..645b874 --- /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 0000000..e4ab796 --- /dev/null +++ b/src/LYReadCFG.c @@ -0,0 +1,2682 @@ +/* + * $LynxId: LYReadCFG.c,v 1.200 2021/07/29 22:52:55 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(const char *string) +{ + if (!strcasecomp(string, "TRUE") || !strcasecomp(string, "ON")) + return (TRUE); + else + return (FALSE); +} + +/* + * Find an unescaped colon in a string buffer. + */ +static const char *find_colon(const char *buffer) +{ + char ch; + const char *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_item(lynx_list_item_type **list, + lynx_list_item_type *ptr) +{ + lynx_list_item_type *prev; + lynx_list_item_type *cur; + + for (cur = *list, prev = 0; cur != 0; prev = cur, cur = cur->next) { + if (cur == ptr) { + + if (prev != 0) + prev->next = cur->next; + else + *list = cur->next; + + FREE(cur->name); + FREE(cur->menu_name); + FREE(cur->command); + FREE(cur); + break; + } + } +} + +static void free_item_list(lynx_list_item_type **ptr) +{ + while (*ptr != 0) { + free_item_list_item(ptr, *ptr); + } +} + +/* + * 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; +} + +static const char *parse_list_bool(BOOL *target, const char *source) +{ + const char *result; + + source = LYSkipCBlanks(source); + result = find_colon(source); + + if (*source != '\0') { + char temp[20]; + size_t len = ((result != 0) + ? (size_t) (result - source) + : strlen(source)); + + if (len > sizeof(temp)) + len = (sizeof(temp) - 1); + LYStrNCpy(temp, source, len); + *target = is_true(temp); + CTRACE2(TRACE_CFG, (tfp, "parse_list_bool(%s) '%d'\n", source, *target)); + } + return result; +} + +static const char *parse_list_int(int *target, const char *source) +{ + const char *result; + + source = LYSkipCBlanks(source); + result = find_colon(source); + + if (*source != '\0') { + *target = atoi(source); + CTRACE2(TRACE_CFG, (tfp, "parse_list_int(%s) '%d'\n", source, *target)); + } + return result; +} + +static const char *parse_list_string(char **target, const char *source) +{ + const char *result; + + source = LYSkipCBlanks(source); + result = find_colon(source); + + if (*source != '\0') { + const char *next = ((result == 0) + ? (source + strlen(source)) + : result); + + *target = typecallocn(char, (size_t) (next - source + 1)); + + if (*target == NULL) + outofmem(__FILE__, "read_cfg"); + LYStrNCpy(*target, source, (next - source)); + remove_backslashes(*target); + + CTRACE2(TRACE_CFG, (tfp, "parse_list_string(%s) '%s'\n", source, *target)); + } + return result; +} + +/* + * 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, + int menu_name) +{ + const char *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 ((colon = find_colon(buffer)) == 0) { + return; + } + for (last_colon = colon; + (colon = find_colon(last_colon + 1)) != 0; + last_colon = colon) { + ; + } + + /* + * If colon equals XWINDOWS then only continue + * if there is a $DISPLAY variable + */ + if (!strcasecomp(last_colon + 1, "XWINDOWS")) { + if (LYgetXDisplay() == NULL) + return; + } + /* + * If colon equals NON_XWINDOWS then only continue + * if there is no $DISPLAY variable + */ + else if (!strcasecomp(last_colon + 1, "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"); + + *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; + } + /* fill-in nonzero default values */ + cur_item->pagelen = 66; + + /* + * Find first unescaped colon and process fields + */ + if (find_colon(buffer) != NULL) { + colon = parse_list_string(&(cur_item->name), buffer); + + if (colon && menu_name) { + colon = parse_list_string(&(cur_item->menu_name), colon + 1); + } + if (colon) { + colon = parse_list_string(&(cur_item->command), colon + 1); + } + if (colon) { + colon = parse_list_bool(&(cur_item->always_enabled), colon + 1); + } + if (colon) { + if (special) { + (void) parse_list_int(&(cur_item->pagelen), colon + 1); + } else { + (void) parse_list_bool(&(cur_item->override_action), colon + 1); + } + } + } + + /* ignore empty data */ + if (cur_item->name == NULL + || cur_item->command == NULL) { + CTRACE2(TRACE_CFG, (tfp, "ignoring incomplete list_item '%s'\n", buffer)); + free_item_list_item(list_ptr, cur_item); + } else if (cur_item->menu_name == NULL) { + StrAllocCopy(cur_item->menu_name, cur_item->command); + } +} + +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, + const char *name, + int only_overriders) +{ + return + (ptr->command != 0 + && !strncasecomp(ptr->name, name, (int) strlen(ptr->name)) + && (only_overriders ? ptr->override_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; + const char *fg, *bg; + char *temp_fg = 0; + + /* + * We are expecting a line of the form: + * INTEGER:FOREGROUND:BACKGROUND + */ + color = atoi(buffer); + if (NULL == (fg = find_colon(buffer))) + exit_with_color_syntax(buffer); + + if (NULL == (bg = find_colon(++fg))) + exit_with_color_syntax(buffer); + + StrAllocCopy(temp_fg, fg); + temp_fg[bg++ - fg] = '\0'; + +#if defined(USE_SLANG) + if ((check_color(temp_fg, default_fg) == ERR_COLOR) || + (check_color(bg, default_bg) == ERR_COLOR)) + exit_with_color_syntax(buffer); + + SLtt_set_color(color, NULL, temp_fg, bg); +#else + if (lynx_chg_color(color, + check_color(temp_fg, default_fg), + check_color(bg, default_bg)) < 0) + exit_with_color_syntax(buffer); +#endif + FREE(temp_fg); +} +#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) +{ + assumed_charset = TRUE; + UCLYhndl_for_unspec = safeUCGetLYhndl_byMIME(value); + StrAllocCopy(UCAssume_MIMEcharset, + LYCharSet_UC[UCLYhndl_for_unspec].MIMEname); + CTRACE((tfp, "assume_charset_fun %s ->%d ->%s\n", + NonNull(value), + UCLYhndl_for_unspec, + 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 + && (!strncasecomp(value, "AutoDetect ", 11) + || !strncasecomp(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) +{ + const char *fg = buffer, *bg; + char *temp_fg = 0; + + if (LYuse_default_colors) { + + /* + * We are expecting a line of the form: + * FOREGROUND:BACKGROUND + */ + if (NULL == (bg = find_colon(fg))) + exit_with_color_syntax(buffer); + + StrAllocCopy(temp_fg, fg); + temp_fg[bg++ - fg] = '\0'; + + default_fg = check_color(temp_fg, default_fg); + default_bg = check_color(bg, default_bg); + + if (default_fg == ERR_COLOR + || default_bg == ERR_COLOR) + exit_with_color_syntax(buffer); + FREE(temp_fg); + } 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_COLOR_STYLE +static int lynx_lss_file_fun(char *value) +{ + CTRACE((tfp, "lynx_lss_file_fun '%s'\n", NonNull(value))); + if (isEmpty(value)) { + clear_lss_list(); + } else { + add_to_lss_list(value, NULL); + } + return 0; +} +#endif + +#ifdef USE_DEFAULT_COLORS +void update_default_colors(void) +{ + int old_fg = default_fg; + int old_bg = default_bg; + + default_color_reset = !LYuse_default_colors; + if (LYuse_default_colors) { + default_color_reset = FALSE; + default_fg = DEFAULT_COLOR; + default_bg = DEFAULT_COLOR; + } else { + default_color_reset = TRUE; + default_fg = COLOR_WHITE; + default_bg = COLOR_BLACK; + } + if (old_fg != default_fg || old_bg != default_bg) { + lynx_setup_colors(); +#ifdef USE_COLOR_STYLE + update_color_style(); +#endif + } +} + +static int default_colors_fun(char *value) +{ + LYuse_default_colors = is_true(value); + update_default_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 (!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 (!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 = (int) 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, + (unsigned) lkc, + (unsigned) 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, + (unsigned) lkc, + (unsigned) lec, + efunc); + } else { + fprintf(stderr, + gettext("setting of line-editor binding for key %s (0x%x) for %s failed\n"), + key, + (unsigned) 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 startfile_fun(char *value) +{ + StrAllocCopy(startfile, value); + +#ifdef USE_PROGRAM_DIR + if (is_url(startfile) == 0) { + char *tmp = NULL; + + HTSprintf0(&tmp, "%s\\%s", program_dir, startfile); + FREE(startfile); + LYLocalFileToURL(&startfile, tmp); + FREE(tmp); + } +#endif + 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 recommended. - 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, 0L, 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(int 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, + int 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_EXTERNALS +/* + * EXTERNAL and EXTERNAL_MENU share the same list. EXTERNAL_MENU allows + * setting a different name than the command string. + */ +static int external_fun(char *str) +{ + add_item_to_list(str, &externals, FALSE, TRUE); + return 0; +} +#endif + +#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(); + + *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 + +#if defined(HAVE_LIBINTL_H) || defined(HAVE_LIBGETTEXT_H) +static int message_language_fun(char *value) +{ + char *tmp = NULL; + + HTSprintf0(&tmp, "LANG=%s", value); + putenv(tmp); + + LYSetTextDomain(); + + 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), +#if USE_BLAT_MAILER + PARSE_SET(RC_ALT_BLAT_MAIL, mail_is_altblat), +#endif + 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 +#if USE_BLAT_MAILER + PARSE_SET(RC_BLAT_MAIL, mail_is_blat), +#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_BROTLI_PATH, ppBROTLI), + PARSE_PRG(RC_BZIP2_PATH, ppBZIP2), + PARSE_SET(RC_CASE_SENSITIVE_ALWAYS_ON, LYcase_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_FUN(RC_COLOR_STYLE, lynx_lss_file_fun), +#endif + PARSE_PRG(RC_COMPRESS_PATH, ppCOMPRESS), + PARSE_PRG(RC_COPY_PATH, ppCOPY), + PARSE_INT(RC_CONNECT_TIMEOUT, connect_timeout), + PARSE_SET(RC_CONV_JISX0201KANA, conv_jisx0201kana), + 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_ENU(RC_COOKIE_VERSION, cookie_version, tbl_cookie_version), + PARSE_Env(RC_CSO_PROXY, 0), +#ifdef VMS + PARSE_PRG(RC_CSWING_PATH, ppCSWING), +#endif + PARSE_TIM(RC_DELAYSECS, DelaySecs), + 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_SET(RC_DONT_WRAP_PRE, dont_wrap_pre), + 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), + PARSE_FUN(RC_EXTERNAL_MENU, external_fun), +#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_HTML, LYforce_HTML_mode), + 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_SET(RC_GUESS_SCHEME, LYGuessScheme), + PARSE_STR(RC_HELPFILE, helpfile), + PARSE_FUN(RC_HIDDENLINKS, hiddenlinks_fun), +#ifdef MARK_HIDDEN_LINKS + PARSE_STR(RC_HIDDEN_LINK_MARKER, hidden_link_marker), +#endif + PARSE_SET(RC_HISTORICAL_COMMENTS, historical_comments), + PARSE_SET(RC_HTML5_CHARSETS, html5_charsets), +#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_FUN(RC_HTTP_PROTOCOL, get_http_protocol), + 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), + PARSE_SET(RC_LISTONLY, dump_links_only), + PARSE_SET(RC_LIST_DECODED, dump_links_decoded), +#ifndef VMS + PARSE_STR(RC_LIST_FORMAT, list_format), +#endif + PARSE_SET(RC_LIST_INLINE, dump_links_inline), +#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_SET(RC_LOCALHOST, local_host_only), + 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_INT(RC_MAX_URI_SIZE, max_uri_size), + PARSE_TIM(RC_MESSAGESECS, MessageSecs), +#if defined(HAVE_LIBINTL_H) || defined(HAVE_LIBGETTEXT_H) + PARSE_FUN(RC_MESSAGE_LANGUAGE, message_language_fun), +#endif + 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_SET(RC_UPDATE_TERM_TITLE, update_term_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_CONTENT_TYPE, LYContentType, tbl_preferred_content), + 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_INT(RC_REDIRECTION_LIMIT, redirection_limit), + 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_RMDIR_PATH, ppRMDIR), + 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_SHORT_URL, long_url_ok), + 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_SSL_CLIENT_CERT_FILE, SSL_client_cert_file), + PARSE_STR(RC_SSL_CLIENT_KEY_FILE, SSL_client_key_file), + PARSE_FUN(RC_STARTFILE, startfile_fun), + 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_TRACK_INTERNAL_LINKS, track_internal_links), + PARSE_SET(RC_TRIM_BLANK_LINES, LYtrimBlankLines), + 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_SET(RC_UNIQUE_URLS, unique_urls), + 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_SET(RC_WAIT_VIEWER_TERMINATION, wait_viewer_termination), + PARSE_SET(RC_WITH_BACKSPACES, with_backspaces), + 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, + LYFindConfigFile(cfg_filename, + dft_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. + */ +BOOL LYSetConfigValue(const char *name, + const char *param) +{ + BOOL changed = TRUE; + char *value = NULL; + Config_Type *tbl = lookup_config(name); + ParseUnionPtr q = ParseUnionOf(tbl); + char *temp_name = 0; + char *temp_value = 0; + + if (param == NULL) + param = ""; + StrAllocCopy(value, param); + 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 *); + + if (q->str_value == 0) + outofmem(__FILE__, "LYSetConfigValue"); + } + + 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), + FALSE); + break; + + case CONF_ADD_STRING: + if (*(q->lst_value) == NULL) { + *(q->lst_value) = HTList_new(); + } + temp_value = NULL; + StrAllocCopy(temp_value, value); + HTList_appendObject(*(q->lst_value), temp_value); + temp_value = NULL; + break; + +#if defined(EXEC_LINKS) || defined(LYNXCGI_LINKS) + case CONF_ADD_TRUSTED: + add_trusted(value, (int) q->def_value); + break; +#endif + + case CONF_PRG: + if (isEmpty(value)) { + HTSetProgramPath((ProgramPaths) (q->def_value), NULL); + } else if (StrAllocCopy(temp_value, value)) { + HTSetProgramPath((ProgramPaths) (q->def_value), temp_value); + } + break; + + default: + changed = FALSE; + break; + } + FREE(value); + + return changed; +} + +/* + * 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, " #<begin %s>\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, " #<end of %s>\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(); + + /* + * Do not allow infinite redirection loops. + */ + if (redirection_limit < 5) + redirection_limit = 5; + if (redirection_limit > 25) + redirection_limit = 25; +} + +/* 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(TRUE); + 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 0000000..7347f86 --- /dev/null +++ b/src/LYReadCFG.h @@ -0,0 +1,75 @@ +/* + * $LynxId: LYReadCFG.h,v 1.29 2014/02/12 23:58:37 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); + extern void update_default_colors(void); +#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 hiddenlinks_fun(char *next_arg); + 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, const char *name, int 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 BOOL LYSetConfigValue(const char *name, const char *value); + extern void LYSetTextDomain(void); + +#ifdef __cplusplus +} +#endif +#endif /* LYREADCFG_H */ diff --git a/src/LYSearch.c b/src/LYSearch.c new file mode 100644 index 0000000..a56de21 --- /dev/null +++ b/src/LYSearch.c @@ -0,0 +1,379 @@ +/* + * $LynxId: LYSearch.c,v 1.40 2013/10/13 20:23:07 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> + +#define MATCH(a,b) (BOOL)(LYno_attr_strstr(a, b) != 0) + +/* + * Handle special field-related comparisons for anchor_has_target() and + * link_has_target(). + */ +BOOL field_has_target(FormInfo * field, const char *target) +{ + BOOL result = FALSE; + OptionType *option; + char *stars = NULL; + const char *cp; + + if ((field != NULL && field->value != NULL) && + field->type != F_HIDDEN_TYPE) { + if (field->type == F_PASSWORD_TYPE) { + /* + * Check the actual (hidden password), and then the displayed + * string - FM + */ + if (MATCH(field->value, target)) { + result = TRUE; + } else { + StrAllocCopy(stars, field->value); + memset(stars, '*', strlen(stars)); + result = MATCH(stars, target); + FREE(stars); + } + } else if (field->type == F_OPTION_LIST_TYPE) { + /* + * Search the option strings that are displayed when the popup is + * invoked - FM + */ + for (option = field->select_list; option != NULL; option = option->next) { + if (MATCH(option->name, target)) { + result = TRUE; + break; + } + } + } else if (field->type == F_RADIO_TYPE) { + /* + * Search for checked or unchecked parens - FM + */ + cp = ((field->num_value) + ? checked_radio + : unchecked_radio); + result = MATCH(cp, target); + } else if (field->type == F_CHECKBOX_TYPE) { + /* + * Search for checked or unchecked square brackets - FM + */ + cp = ((field->num_value) + ? checked_box + : unchecked_box); + result = MATCH(cp, target); + } else { + result = MATCH(field->value, target); + } + } + return result; +} + +/* + * see also anchor_has_target + */ +static BOOL link_has_target(int cur, + char *target) +{ + LinkInfo *a = &links[cur]; + char *text = NULL; + const char *last = "?"; + int count; + + /* + * Combine the parts of the link's text using the highlighting information, + * and compare the target against that. + */ + for (count = 0; count < 10; ++count) { + const char *part = LYGetHiliteStr(cur, count); + + if (part == NULL || part == last) { + if (MATCH(text, target)) { + return TRUE; + } + break; + } + StrAllocCat(text, part); + last = part; + } + + return field_has_target(a->l_form, target); +} + +/* + * 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 'LYcase_sensitive' global variable + */ +BOOL textsearch(DocInfo *cur_doc, + bstring **prev_target, + int direction) +{ + int offset; + int oldcur = cur_doc->link; + static bstring *my_prev_target = NULL; + 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) { + BStrCopy0(my_prev_target, ""); + 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. + */ + BStrCopy(*prev_target, my_prev_target); + } else if (*prev_target == 0) { + BStrCopy0(*prev_target, ""); + } + + if (strlen((*prev_target)->str) == 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 = LYgetBString(prev_target, FALSE, 0, recall); + if (ch < 0) { + /* + * User cancelled the search via ^G. Restore prev_target and + * return. - FM + */ + BStrCopy(*prev_target, my_prev_target); + HTInfoMsg(CANCELLED); + return (FALSE); + } + } + + check_recall: + if (strlen((*prev_target)->str) == 0 && + !(recall && (ch == UPARROW_KEY || ch == DNARROW_KEY))) { + /* + * 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_KEY) { + if (FirstRecall) { + /* + * Use the current string or last query in the list. - FM + */ + FirstRecall = FALSE; + if (!isBEmpty(my_prev_target)) { + for (QueryNum = (QueryTotal - 1); QueryNum > 0; QueryNum--) { + if ((cp = (char *) HTList_objectAt(search_queries, + QueryNum)) != NULL && + !strcmp(my_prev_target->str, 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) { + BStrCopy0(*prev_target, cp); + if (!isBEmpty(my_prev_target) && + !strcmp(my_prev_target->str, (*prev_target)->str)) { + _statusline(EDIT_CURRENT_QUERY); + } else if ((!isBEmpty(my_prev_target) && QueryTotal == 2) || + (isBEmpty(my_prev_target) && QueryTotal == 1)) { + _statusline(EDIT_THE_PREV_QUERY); + } else { + _statusline(EDIT_A_PREV_QUERY); + } + ch = LYgetBString(prev_target, FALSE, 0, recall); + if (ch < 0) { + /* + * User canceled the search via ^G. Restore prev_target and + * return. - FM + */ + BStrCopy(*prev_target, my_prev_target); + HTInfoMsg(CANCELLED); + return (FALSE); + } + goto check_recall; + } + } else if (recall && ch == DNARROW_KEY) { + if (FirstRecall) { + /* + * Use the current string or first query in the list. - FM + */ + FirstRecall = FALSE; + if (!isBEmpty(my_prev_target)) { + for (QueryNum = 0; QueryNum < (QueryTotal - 1); QueryNum++) { + if ((cp = (char *) HTList_objectAt(search_queries, + QueryNum)) != NULL && + !strcmp(my_prev_target->str, 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) { + BStrCopy0(*prev_target, cp); + if (!isBEmpty(my_prev_target) && + !strcmp(my_prev_target->str, (*prev_target)->str)) { + _statusline(EDIT_CURRENT_QUERY); + } else if ((!isBEmpty(my_prev_target) && QueryTotal == 2) || + (isBEmpty(my_prev_target) && QueryTotal == 1)) { + _statusline(EDIT_THE_PREV_QUERY); + } else { + _statusline(EDIT_A_PREV_QUERY); + } + ch = LYgetBString(prev_target, FALSE, 0, recall); + if (ch < 0) { + /* + * User cancelled the search via ^G. Restore prev_target and + * return. - FM + */ + BStrCopy(*prev_target, my_prev_target); + HTInfoMsg(CANCELLED); + return (FALSE); + } + goto check_recall; + } + } + /* + * Replace the search string buffer with the new target. - FM + */ + BStrCopy(my_prev_target, *prev_target); + HTAddSearchQuery(my_prev_target->str); + + if (direction < 0) { + offset = 0; + if (check_prev_target_in_links(&cur_doc->link, (*prev_target)->str)) { + /* + * Found in link, changed cur, we're done. + */ + LYhighlight(FALSE, oldcur, (*prev_target)->str); + 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)->str)) { + /* + * Found in link, changed cur, we're done. + */ + LYhighlight(FALSE, oldcur, (*prev_target)->str); + 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)->str, direction); + if (cur_doc->link != oldcur) { + LYhighlight(FALSE, oldcur, (*prev_target)->str); + return (TRUE); + } + return (BOOL) (www_search_result > 0); +} diff --git a/src/LYSearch.h b/src/LYSearch.h new file mode 100644 index 0000000..17fd5dd --- /dev/null +++ b/src/LYSearch.h @@ -0,0 +1,33 @@ +/* + * $LynxId: LYSearch.h,v 1.12 2013/10/03 11:24:06 tom Exp $ + */ +#ifndef LYSEARCH_H +#define LYSEARCH_H + +#ifndef HTFORMS_H +#include <HTForms.h> +#endif + +#ifndef LYSTRUCTS_H +#include <LYStructs.h> +#endif /* LYSTRUCT_H */ + +#ifdef __cplusplus +extern "C" { +#endif + extern BOOL field_has_target(FormInfo * field, const char *target); + extern BOOL textsearch(DocInfo *cur_doc, + bstring **prev_target, + 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 0000000..a4438bd --- /dev/null +++ b/src/LYSession.c @@ -0,0 +1,267 @@ +/* $LynxId: LYSession.c,v 1.12 2018/07/08 15:22:44 tom Exp $ */ + +#include <LYSession.h> + +#include <LYLeaks.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 https://lynx.invisible-island.net + * 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; + + memset(&doc, 0, sizeof(doc)); + + /* + * 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 0000000..90d74fc --- /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 0000000..129810c --- /dev/null +++ b/src/LYShowInfo.c @@ -0,0 +1,485 @@ +/* $LynxId: LYShowInfo.c,v 1.86 2024/01/15 18:16:29 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 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, 0) +#define ADD_WW(label,value) dt_String(fp0, label, value, 1) +#define ADD_NN(label,value,units) dt_Number(fp0, label, (long) value, units) + +static int label_columns; + +/* + * LYNX_VERSION and LYNX_DATE are generated by the configure script. + */ +BOOL LYVersionIsRelease(void) +{ + return TRUE; +} + +const char *LYVersionStatus(void) +{ + return REL_VERSION; +} + +const char *LYVersionDate(void) +{ + return LYNX_DATE; +} + +static void dt_String(FILE *fp, + const char *label, + const char *value, + int allow_wide) +{ + int have; + int need; + char *the_label = NULL; + char *the_value = NULL; + + 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, " "); + if (LYwideLines && allow_wide) + fprintf(fp, "<em>%s</em> <pre>%s</pre>\n", the_label, the_value); + else + 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); +} + +static void dt_URL(FILE *fp0, const char *address) +{ + if (address == NULL) + address = ""; + ADD_WW(gettext("URL:"), address); + + /* + * If the display handles UTF-8, and if the address uses %xy formatted + * characters, show the decoded URL on the next line. + */ + if (LYCharSet_UC[current_char_set].enc == UCT_ENC_UTF8) { + char *working = NULL; + + StrAllocCopy(working, address); + if (strcmp(HTUnEscape(working), address)) { + fprintf(fp0, "\n<br>%s\n", working); + } + free(working); + } +} + +/* + * 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 = NULL; + char *buffer = NULL; + + BOOLEAN LYInfoAdvanced = (BOOL) (user_mode == ADVANCED_MODE); + +#ifdef DIRED_SUPPORT + struct stat dir_info; + const char *name; +#endif /* DIRED_SUPPORT */ + + if (LYReuseTempfiles) { + fp0 = LYOpenTempRewrite(tempfile, HTML_SUFFIX, "w"); + } else { + (void) 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); + + dt_URL(fp0, 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; + size_t limit = sizeof(buf) - 1; + + if ((buf_size = (int) readlink(temp, buf, limit)) != -1) { + if (buf_size > (int) limit) + buf_size = (int) limit; + buf[buf_size] = '\0'; + } else { + sprintf(buf, "%.*s", (int) limit, + 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); + + dt_URL(fp0, 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); + + 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); + } + } + + 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")); + + 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")); + } + + 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> %s\n", + LYEntifyTitle(&buffer, gettext("(Form field)"))); + } + } else { + dt_URL(fp0, NonNull(links[doc->link].lname)); + } + END_DL(); + + } else { + fprintf(fp0, "<h2>%s</h2>", + LYEntifyTitle(&buffer, + gettext("No Links on the current page"))); + } + + if ((cp = HText_getHttpHeaders()) != 0) { + fprintf(fp0, "<h2>%s</h2>", + LYEntifyTitle(&buffer, gettext("Server Headers:"))); + fprintf(fp0, "<pre>%s</pre>", + LYEntifyTitle(&buffer, cp)); + } +#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 0000000..443bff3 --- /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 0000000..627edb3 --- /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 0000000..2bbd56d --- /dev/null +++ b/src/LYStrings.c @@ -0,0 +1,6217 @@ +/* $LynxId: LYStrings.c,v 1.282 2023/10/23 23:41:44 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> +#include <LYrcFile.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 + +#ifdef SUPPORT_MULTIBYTE_EDIT +#define IsWordChar(c) (isalnum(UCH(c)) || is8bits(c)) +#else +#define IsWordChar(c) isalnum(UCH(c)) +#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; + size_t 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_KEY : 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_KEY + : ( +#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_KEY : 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_MOUSE_SUBMIT); + mouse_link = i; + } else { + c = LAC_TO_LKC0(LYK_MOUSE_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() ensures that the copied strings end with a nul byte. + * The nul is written to the n+1 position of the target. + */ +char *LYstrncpy(char *target, + const char *source, + int n) +{ + char *val = target; + int len; + + if (source == 0) + source = ""; + len = (int) strlen(source); + + if (n > 0) { + if (n > len) + n = len; + (void) memcpy(target, source, (size_t) n); + } else { + n = 0; + } + target[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 source 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 *target, + const char *source, + int n_bytes, + int n_glyphs, + int utf_flag) +{ + char *val = target; + int i_bytes = 0, i_glyphs = 0; + + if (n_bytes < 0) + n_bytes = 0; + if (n_glyphs < 0) + n_glyphs = 0; + + for (; *source != '\0' && i_bytes < n_bytes; i_bytes++) { + if (IS_NEW_GLYPH(*source)) { + if (i_glyphs++ >= n_glyphs) { + *target = '\0'; + return val; + } + } + *(target++) = *(source++); + } + *target = '\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, + int 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, + int utf_flag) +{ + const char *result; + int actual; + int target = n_cells; + + do { + result = LYmbcs_skip_glyphs(data, target--, utf_flag); + actual = LYstrExtent2(data, (int) (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, + int utf_flag, + int 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 +#undef USE_CURSES_RESIZE + +#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() +#endif +#endif + +#ifdef USE_CURSES_RESIZE +static int myGetCharResize(void) +{ + int c; + WINDOW *win = LYtopwindow(); + + do { + wtimeout(win, 20); + c = wgetch(win); + wtimeout(win, -1); + CheckScreenSize(); + } while (c <= 0); + return c; +} +#define GetChar() myGetCharResize() +#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; + CheckScreenSize(); + + 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(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. Three more characters are + * expected: BUTTON X Y + */ + *button = (int) 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 = (int) SLang_getkey(); + if (*x == CH_ESC) /* Undo 7-bit replace for large x - kw */ + *x = (int) SLang_getkey() + 64 - 33; + else + *x -= 33; + *y = (int) SLang_getkey(); + if (*y == CH_ESC) /* Undo 7-bit replace for large y - kw */ + *y = (int) 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(int flag) +{ + csi_is_csi = (BOOLEAN) flag; +} + +#if defined(USE_KEYMAPS) + +#ifdef USE_SLANG +#define define_key(string, code) \ + SLkm_define_keysym ((SLFUTURE_CONST char*)(string), \ + (unsigned) code, \ + Keymap_List) +#if SLANG_VERSION < 20000 +#define expand_substring(target, first, last, final) \ + (SLexpand_escaped_string(target, \ + DeConst(first), \ + DeConst(last), 1) +static int SLang_get_error(void) +{ + return SLang_Error; +} +#else +int LY_Slang_UTF8_Mode = 0; + +#define expand_substring(target, first, last, final) \ + (SLexpand_escaped_string(target, \ + DeConst(first), \ + DeConst(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 + +/* + * 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,lynx} +#else +# define INTERN_KEY(string,lynx,curses) {string,curses,lynx} +# define EXTERN_KEY(string,string1,lynx,curses) {string,curses,lynx} +#endif + +typedef struct { + const char *string; + int value; + LYExtraKeys internal; +} Keysym_String_List; +/* *INDENT-OFF* */ +static Keysym_String_List Keysym_Strings [] = +{ + INTERN_KEY( "UPARROW", UPARROW_KEY, KEY_UP ), + INTERN_KEY( "DNARROW", DNARROW_KEY, KEY_DOWN ), + INTERN_KEY( "RTARROW", RTARROW_KEY, KEY_RIGHT ), + INTERN_KEY( "LTARROW", LTARROW_KEY, KEY_LEFT ), + INTERN_KEY( "PGDOWN", PGDOWN_KEY, KEY_NPAGE ), + INTERN_KEY( "PGUP", PGUP_KEY, KEY_PPAGE ), + INTERN_KEY( "HOME", HOME_KEY, KEY_HOME ), + INTERN_KEY( "END", END_KEY, KEY_END ), + INTERN_KEY( "F1", F1_KEY, KEY_F(1) ), + INTERN_KEY( "F2", F2_KEY, KEY_F(2) ), + INTERN_KEY( "F3", F3_KEY, KEY_F(3) ), + INTERN_KEY( "F4", F4_KEY, KEY_F(4) ), + INTERN_KEY( "F5", F5_KEY, KEY_F(5) ), + INTERN_KEY( "F6", F6_KEY, KEY_F(7) ), + INTERN_KEY( "F7", F7_KEY, KEY_F(7) ), + INTERN_KEY( "F8", F8_KEY, KEY_F(8) ), + INTERN_KEY( "F9", F9_KEY, KEY_F(9) ), + INTERN_KEY( "F10", F10_KEY, KEY_F(10) ), + INTERN_KEY( "F11", F11_KEY, KEY_F(11) ), + INTERN_KEY( "F12", F12_KEY, KEY_F(12) ), + 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( "BACKTAB_KEY", BACKTAB_KEY, BACKTAB_KEY ), + INTERN_KEY( NULL, UNKNOWN_KEY, 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; + TERMTYPE *tp = (TERMTYPE *) (cur_term); + + LYStrNCpy(name, first, len); + **result = '\0'; + if ((code = lookup_tiname(name, strnames)) >= 0 + || (code = lookup_tiname(name, strfnames)) >= 0) { + if (tp->Strings[code] != 0) { + LYStrNCpy(*result, tp->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 = (int) 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 *target, + const char *first, + const char *last, + char *final) +{ + int ch; + + while (first < last) { + switch (ch = *first++) { + case ESCAPE: + first = expand_tichar(first, &target, final); + break; + case '^': + ch = *first++; + if (ch == LPAREN) { + const char *s = StrChr(first, RPAREN); + char *was = target; + + if (s == 0) + s = first + strlen(first); + first = expand_tiname(first, (size_t) (s - first), &target, final); + if (target == was) + return FALSE; + if (*first) + first++; + } else if (ch == '?') { /* ASCII delete? */ + *target++ = 127; + } else if ((ch & 0x3f) < 0x20) { /* ASCII control char? */ + *target++ = (char) (ch & 0x1f); + } else { + *target++ = '^'; + first--; /* not legal... */ + } + break; + case 0: /* convert nulls for terminfo */ + ch = 0200; + /* FALLTHRU */ + default: + *target++ = (char) ch; + break; + } + } + *target = '\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 *source, char *target, char *final) +{ + BOOLEAN ok = FALSE; + + if (*source == SQUOTE) { + int keysym = -1; + + unescaped_char(source, &keysym); + if (keysym >= 0) { + target[0] = (char) keysym; + target[1] = '\0'; + ok = TRUE; + } + } else if (*source == DQUOTE) { + if (expand_substring(target, source + 1, source + strlen(source) - 1, final)) + ok = TRUE; + (void) final; + } + return ok; +} + +static Keysym_String_List *lookupKeysymByName(const char *name) +{ + Keysym_String_List *k; + Keysym_String_List *result = 0; + + k = Keysym_Strings; + while (k->string != NULL) { + if (0 == strcasecomp(k->string, name)) { + result = k; + break; + } + k++; + } + return result; +} + +int map_string_to_keysym(const char *str, int *keysym, int internal) +{ + 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) { + LYStrNCpy(buf, str + 4, (other - str - 4)); + *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); + } + } else 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); + } + } + } else 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 = (int) value; +#ifndef USE_SLANG + if (*keysym > 255) + *keysym |= LKC_ISLKC; /* caller should remove this flag - kw */ +#endif + } + } else { + Keysym_String_List *k = lookupKeysymByName(str); + + if (k != 0) { + *keysym = (internal + ? k->internal + : k->value); + } + } + + if (*keysym >= 0) + *keysym |= modifier; + return (*keysym); +} + +LYExtraKeys LYnameToExtraKeys(const char *name) +{ + Keysym_String_List *k = lookupKeysymByName(name); + LYExtraKeys result = UNKNOWN_KEY; + + if (k != 0) + result = k->internal; + return result; +} + +const char *LYextraKeysToName(LYExtraKeys code) +{ + Keysym_String_List *k; + const char *result = 0; + + k = Keysym_Strings; + while (k->string != NULL) { + if (k->internal == code) { + result = k->string; + break; + } + k++; + } + return result; +} + +/* + * 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, FALSE) >= 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", + (unsigned) keysym)); + } else { + MY_TRACE((tfp, "KEYMAP(DEF) keysym=%#x, seq='%s'\n", + (unsigned) 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, FALSE) >= 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; + + CTRACE((tfp, "read_keymap_file %s\n", file)); + 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, KEY_UP ), + INTERN_KEY( "\033OA", UPARROW_KEY, KEY_UP ), + INTERN_KEY( "\033[B", DNARROW_KEY, KEY_DOWN ), + INTERN_KEY( "\033OB", DNARROW_KEY, KEY_DOWN ), + INTERN_KEY( "\033[C", RTARROW_KEY, KEY_RIGHT ), + INTERN_KEY( "\033OC", RTARROW_KEY, KEY_RIGHT ), + INTERN_KEY( "\033[D", LTARROW_KEY, KEY_LEFT ), + INTERN_KEY( "\033OD", LTARROW_KEY, 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, KEY_PPAGE ), + INTERN_KEY( "\033[6~", PGDOWN_KEY, KEY_NPAGE ), + INTERN_KEY( "\033[7~", HOME_KEY, KEY_HOME), + INTERN_KEY( "\033[8~", END_KEY, KEY_END ), + INTERN_KEY( "\033[11~", F1_KEY, KEY_F(1) ), + INTERN_KEY( "\033[28~", F1_KEY, KEY_F(1) ), + INTERN_KEY( "\033OP", F1_KEY, KEY_F(1) ), + INTERN_KEY( "\033[OP", F1_KEY, KEY_F(1) ), + INTERN_KEY( "\033[29~", DO_KEY, KEY_F(16) ), +#if defined(USE_SLANG) +#if defined(__WIN32__) || defined(__MINGW32__) + INTERN_KEY( "\xE0H", UPARROW_KEY, KEY_UP ), + INTERN_KEY( "\xE0P", DNARROW_KEY, KEY_DOWN ), + INTERN_KEY( "\xE0M", RTARROW_KEY, KEY_RIGHT ), + INTERN_KEY( "\xE0K", LTARROW_KEY, KEY_LEFT ), + INTERN_KEY( "\xE0R", INSERT_KEY, KEY_IC ), + INTERN_KEY( "\xE0S", REMOVE_KEY, KEY_DC ), + INTERN_KEY( "\xE0I", PGUP_KEY, KEY_PPAGE ), + INTERN_KEY( "\xE0Q", PGDOWN_KEY, KEY_NPAGE ), + INTERN_KEY( "\xE0G", HOME_KEY, KEY_HOME), + INTERN_KEY( "\xE0O", END_KEY, KEY_END ), +#endif +#if !defined(VMS) + INTERN_KEY( "^(ku)", UPARROW_KEY, KEY_UP ), + INTERN_KEY( "^(kd)", DNARROW_KEY, KEY_DOWN ), + INTERN_KEY( "^(kr)", RTARROW_KEY, KEY_RIGHT ), + INTERN_KEY( "^(kl)", LTARROW_KEY, 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, KEY_PPAGE ), + INTERN_KEY( "^(kN)", PGDOWN_KEY, KEY_NPAGE ), + INTERN_KEY( "^(@7)", END_KEY, KEY_END ), + INTERN_KEY( "^(kh)", HOME_KEY, KEY_HOME), + INTERN_KEY( "^(k1)", F1_KEY, KEY_F(1) ), + INTERN_KEY( "^(k2)", F2_KEY, KEY_F(2) ), + INTERN_KEY( "^(k3)", F3_KEY, KEY_F(3) ), + INTERN_KEY( "^(k4)", F4_KEY, KEY_F(4) ), + INTERN_KEY( "^(k5)", F5_KEY, KEY_F(5) ), + INTERN_KEY( "^(k6)", F6_KEY, KEY_F(6) ), + INTERN_KEY( "^(k7)", F7_KEY, KEY_F(7) ), + INTERN_KEY( "^(k8)", F8_KEY, KEY_F(8) ), + INTERN_KEY( "^(k9)", F9_KEY, KEY_F(9) ), + INTERN_KEY( "^(k;)", F10_KEY, KEY_F(10) ), + INTERN_KEY( "^(F1)", F11_KEY, KEY_F(11) ), + INTERN_KEY( "^(F2)", F12_KEY, KEY_F(12) ), + INTERN_KEY( "^(F6)", DO_KEY, KEY_F(16) ), +#endif /* !VMS */ +#endif /* SLANG */ + }; + /* *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_MOUSE_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; + unsigned filter_out = (unsigned) (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_KEY; + break; + case 'P': + keysym = DNARROW_KEY; + break; + case 'M': + keysym = RTARROW_KEY; + break; + case 'K': + keysym = LTARROW_KEY; + break; + case 'R': + keysym = INSERT_KEY; + break; + case 'S': + keysym = REMOVE_KEY; + break; + case 'I': + keysym = PGUP_KEY; + break; + case 'Q': + keysym = PGDOWN_KEY; + break; + case 'G': + keysym = HOME_KEY; + break; + case 'O': + keysym = END_KEY; + break; + case ';': + keysym = F1_KEY; + break; + } + return (keysym); + } +#endif + return (current_sl_modifier ? 0 : DO_NOTHING); + } else { + keysym = (int) key->f.keysym; + +#if defined (USE_MOUSE) + if (keysym == MOUSE_KEYSYM) + return sl_read_mouse_event(code); +#endif + + if (keysym < 0) { + return 0; + + } else if (keysym & (LKC_ISLECLAC | LKC_ISLAC)) { + return (keysym); + } else { + 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; + } else { + 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%d: Got %#x.\n", code, (unsigned) c)); + 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; + } + } +#ifdef USE_GETCHAR + if (c == EOF && errno == EINTR) /* Ctrl-Z causes EINTR in getchar() */ + goto re_read; +#else + if (c == EOF && errno == EINTR) { + + CTRACE((tfp, "Got EOF with EINTR, recent_sizechange is %d\n", + recent_sizechange)); +#if defined(HAVE_SIZECHANGE) || defined(USE_SLANG) + CheckScreenSize(); + if (!recent_sizechange) { /* not yet detected by ourselves */ + size_change(0); + CTRACE((tfp, "Now recent_sizechange is %d\n", recent_sizechange)); + } +#elif !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) { + CheckScreenSize(); + 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_KEY; + break; + case 'B': + c = DNARROW_KEY; + break; + case 'C': + c = RTARROW_KEY; + break; + case 'D': + c = LTARROW_KEY; + break; + case 'q': /* vt100 application keypad 1 */ + c = END_KEY; + break; + case 'r': /* vt100 application keypad 2 */ + c = DNARROW_KEY; + break; + case 's': /* vt100 application keypad 3 */ + c = PGDOWN_KEY; + break; + case 't': /* vt100 application keypad 4 */ + c = LTARROW_KEY; + break; + case 'v': /* vt100 application keypad 6 */ + c = RTARROW_KEY; + break; + case 'w': /* vt100 application keypad 7 */ + c = HOME_KEY; + break; + case 'x': /* vt100 application keypad 8 */ + c = UPARROW_KEY; + break; + case 'y': /* vt100 application keypad 9 */ + c = PGUP_KEY; + 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_KEY; + break; + case 'u': +#ifdef VMS + if (b != 'O') +#endif /* VMS */ + c = F1_KEY; /* 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_KEY; + 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_KEY; + 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_KEY; + 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_KEY; + 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_KEY; + break; + case KEY_UP: + c = UPARROW_KEY; + break; + case KEY_LEFT: + c = LTARROW_KEY; + break; + case KEY_RIGHT: /* ... */ + c = RTARROW_KEY; + break; +#if defined(PDCURSES) /* for NEC PC-9800 1998/08/30 (Sun) 21:50:35 */ + case KEY_C2: + c = DNARROW_KEY; + break; + case KEY_A2: + c = UPARROW_KEY; + break; + case KEY_B1: + c = LTARROW_KEY; + break; + case KEY_B3: + c = RTARROW_KEY; + 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_KEY; + break; + case KEY_CLEAR: /* Clear screen */ + c = 18; /* CTRL-R */ + break; + case KEY_NPAGE: /* Next page */ + c = PGDOWN_KEY; + break; + case KEY_PPAGE: /* Previous page */ + c = PGUP_KEY; + 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_KEY; + break; + case KEY_A3: /* upper right of keypad */ + c = PGUP_KEY; + 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_KEY; + 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_KEY; + 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_KEY; /* 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: + 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_MOUSE_SUBMIT) && + code == FOR_INPUT) + lac = LYK_MOUSE_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_MOUSE_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 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_MOUSE_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_KEY; + p = "<-"; + } else if (MOUSE_X_POS < HIST_CMD_2) { + c = RTARROW_KEY; + p = "->"; + } else if (MOUSE_X_POS > right) { + c = 'z'; + p = "Cancel"; + } else { + c = PGDOWN_KEY; + p = "PGDOWN"; + } + } else if (MOUSE_Y_POS < V_CMD_AREA) { + /* Screen TOP */ + if (MOUSE_X_POS < left) { + c = LTARROW_KEY; + p = "<-"; + } else if (MOUSE_X_POS < HIST_CMD_2) { + c = RTARROW_KEY; + p = "->"; + } else if (MOUSE_X_POS > right) { + c = 'z'; + p = "Cancel"; + } else { + c = PGUP_KEY; + 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_KEY; + break; + case K_Up: + case K_EUp: + c = UPARROW_KEY; + break; + case K_Left: + case K_ELeft: + c = LTARROW_KEY; + break; + case K_Right: /* ... */ + case K_ERight: + c = RTARROW_KEY; + break; + case K_Home: /* Home key (upward+left arrow) */ + case K_EHome: + c = HOME_KEY; + break; + case K_PageDown: /* Next page */ + case K_EPageDown: + c = PGDOWN_KEY; + break; + case K_PageUp: /* Previous page */ + case K_EPageUp: + c = PGUP_KEY; + break; + case K_End: /* home down or bottom (lower left) */ + case K_EEnd: + c = END_KEY; + break; + case K_F1: /* F1 key */ + c = F1_KEY; + 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_KEY; + break; + case SL_KEY_UP: + c = UPARROW_KEY; + break; + case SL_KEY_LEFT: + c = LTARROW_KEY; + break; + case SL_KEY_RIGHT: /* ... */ + c = RTARROW_KEY; + break; + case SL_KEY_HOME: /* Home key (upward+left arrow) */ + case SL_KEY_A1: /* upper left of keypad */ + c = HOME_KEY; + break; + case SL_KEY_NPAGE: /* Next page */ + case SL_KEY_C3: /* lower right of keypad */ + c = PGDOWN_KEY; + break; + case SL_KEY_PPAGE: /* Previous page */ + case SL_KEY_A3: /* upper right of keypad */ + c = PGUP_KEY; + 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_KEY; + 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); + } else 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) +{ + BOOLEAN result = FALSE; + + 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'; + result = TRUE; + } + } + return result; +} + +/* + * 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) +{ + char *result = NULL; + + 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'; + } + result = buf; + } + return result; +} + +/* + * Skip whitespace + */ +char *LYSkipBlanks(char *buffer) +{ + if (buffer != NULL) { + while (isspace(UCH((*buffer)))) + buffer++; + } + return buffer; +} + +/* + * Skip non-whitespace + */ +char *LYSkipNonBlanks(char *buffer) +{ + if (buffer != NULL) { + 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) +{ + BOOLEAN result = FALSE; + + 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); + result = TRUE; + } + return result; +} + +/* + * 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. + */ + +/* + * Shorthand to get rid of the "edit->suchandsos". + */ +#define IsDirty edit->efIsDirty +#define IsHidden edit->efIsMasked +#define StartX edit->efStartX +#define StartY edit->efStartY +#define Buffer edit->efBuffer +#define EditAt edit->efEditAt /* current editing position (bytes) */ +#define BufInUse edit->efBufInUse /* length (bytes) */ +#define BufAlloc edit->efBufAlloc +#define BufLimit edit->efBufLimit +#define DpyWidth edit->efWidth +#define DpyStart edit->efDpyStart /* display-start (columns) */ +#define PanMargin edit->efPanMargin +#define IsPanned edit->efIsPanned +#define PadChar edit->efPadChar +#ifdef ENHANCED_LINEEDIT +#define EditMark edit->efEditMark +#endif +#define InputMods edit->efInputMods +#define Offs2Col edit->efOffs2Col + +#define enableEditMark() \ + if (EditMark < 0) \ + EditMark = -(1 + EditMark) + +#define disableEditMark() \ + if (EditMark >= 0) \ + EditMark = -(1 + EditMark) + +#ifdef ENHANCED_LINEEDIT +static bstring *killbuffer; +#endif + +static void updateMargin(FieldEditor * edit) +{ + if ((int) BufAlloc > DpyWidth) { /* Need panning? */ + if (DpyWidth > 4) + IsPanned = 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. + */ + PanMargin = DpyWidth / 4; + if (PanMargin > 10) + PanMargin = 10; + } +} + +/* + * Before using an array position, make sure that the array is long enough. + * Reallocate if needed. + */ +static void ExtendEditor(FieldEditor * edit, int position) +{ + size_t need = (size_t) (++position); + + if (need >= BufAlloc && (BufLimit == 0 || need < BufLimit)) { + CTRACE((tfp, "ExtendEditor from %lu to %lu\n", + (unsigned long) BufAlloc, + (unsigned long) need)); + Buffer = typeRealloc(char, Buffer, need); + Offs2Col = typeRealloc(int, Offs2Col, need + 1); + + BufAlloc = need; + updateMargin(edit); + } +} + +void LYFinishEdit(FieldEditor * edit) +{ + CTRACE((tfp, "LYFinishEdit:%s\n", NonNull(Buffer))); + + FREE(Buffer); + FREE(Offs2Col); +} + +void LYSetupEdit(FieldEditor * edit, char *old_value, unsigned buffer_limit, int display_limit) +{ + CTRACE((tfp, "LYSetupEdit buffer %lu, display %d:%s\n", + (unsigned long) buffer_limit, + display_limit, + old_value)); + + BufLimit = buffer_limit; + if (buffer_limit == 0) + buffer_limit = (unsigned) strlen(old_value) + 1; + + /* + * Initialize edit record + */ + LYGetYX(StartY, StartX); + PadChar = ' '; + IsDirty = TRUE; + IsPanned = FALSE; + InputMods = 0; + + BufAlloc = buffer_limit; + DpyWidth = display_limit; + PanMargin = 0; + EditAt = (int) strlen(old_value); +#ifdef ENHANCED_LINEEDIT + EditMark = -1; /* pos=0, but do not show it yet */ +#endif + DpyStart = 0; + + updateMargin(edit); + + BufInUse = strlen(old_value); + Buffer = typecallocn(char, BufAlloc + 1); + + if (Buffer == 0) + outofmem(__FILE__, "LYSetupEdit"); + + LYStrNCpy(Buffer, old_value, buffer_limit); + Offs2Col = typecallocn(int, BufAlloc + 1); + + if (Offs2Col == 0) + outofmem(__FILE__, "LYSetupEdit"); +} + +#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; +} + +/* + * Check if there are no continuation bytes in the multibyte (sub)string of + * length len. + */ +static int mbcs_valid(char *s, int len, int limit) +{ + int i; + int result = FALSE; + + if (IS_UTF8_TTY) { + for (i = 0; s[i] && i < limit; i++) { + if (!IS_UTF8_EXTRA(s[i])) { + if ((i + 1) == len) { + result = TRUE; + break; + } + } + } + } else if (IS_CJK_TTY) { + for (i = 0; s[i] && i < limit; i++) { + if (!is8bits(s[i])) { + if ((i + 1) == len) { + result = TRUE; + break; + } + } + } + } else { + result = TRUE; + } + return result; +} + +/* + * 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)); + if (len != 0) { + int best = -1; + + for (pos = 0; pos <= len; ++pos) { + have = LYstrExtent2(s, pos); + CTRACE_EDIT((tfp, " %2d:%2d:%.*s\n", pos, have, pos, s)); + if (have >= cells) { + if (cells <= 0) + break; + /* the best solution is the one with the most bytes */ + best = pos; + if (mbcs_valid(s, pos, len)) + break; + } + } + if (best >= 0) + pos = best; + if (pos > len) + pos = len; + } else { + pos = 0; + } + 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(FieldEditor * edit, unsigned const char *s, + int len, + int map GCC_UNUSED, + int maxMessage) +{ + int length = (int) strlen(Buffer); + int remains = (int) BufAlloc - (length + len); + int edited = 0, overflow = 0; + + /* + * ch is (presumably) printable character. + */ + if (remains < 0) { + overflow = 1; + len = 0; + if ((int) BufAlloc > length) /* Insert as much as we can */ + len = (int) BufAlloc - length; + else + goto finish; + } + ExtendEditor(edit, length + len); + Buffer[length + len] = '\0'; + for (; length >= EditAt; length--) /* Make room */ + Buffer[length + len] = Buffer[length]; +#ifdef EXP_KEYBOARD_LAYOUT + if (map < 0) + map = map_active; + if (map && IS_UTF8_TTY) { + int off = EditAt; + 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(Buffer + off, tail); + FREE(tail); + len = off; + overflow = 1; + goto finish; + } + if (l > 1 && !tail) + StrAllocCopy(tail, Buffer + EditAt + len); + } else + utfbuf[0] = '?'; + } else + utfbuf[0] = (char) ucode; + } + if ((size_t) (off + l) <= BufAlloc) { + memcpy(Buffer + off, utfbuf, (size_t) l); + edited = 1; + off += l; + } + s++; + } + if (tail) + strcpy(Buffer + off, tail); + len = off - EditAt; + FREE(tail); + } else if (map) { + unsigned const char *e = s + len; + unsigned char *t = (unsigned char *) Buffer + EditAt; + + while (s < e) { + int ch; + + if (*s < 128 && LYKbLayouts[current_layout][*s]) { + ch = UCTransUniChar((UCode_t) 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(Buffer + EditAt, (const char *) s, len); + edited = 1; + } + + finish: + EditAt += len; + BufInUse += (size_t) len; + if (edited) + IsDirty = TRUE; + if (overflow && maxMessage) + _statusline(MAXLEN_REACHED_DEL_OR_MOV); +#ifdef ENHANCED_LINEEDIT + if (EditMark > EditAt) + EditMark += len; + else if (EditMark < -(1 + EditAt)) + EditMark -= len; + disableEditMark(); +#endif + return edited; +} + +/* + * Do one edit-operation, given the input 'ch' and working buffer 'edit'. + * + * If the input is processed, returns zero. + * If the action should be performed outside of line-editing mode, return -ch. + * Otherwise, e.g., returns 'ch'. + */ +int LYDoEdit(FieldEditor * edit, int ch, + int action, + int maxMessage) +{ + int i; + int length; + unsigned char uch; + int offset; + + if ((int) BufAlloc <= 0) + return (0); /* Be defensive */ + + BufInUse = strlen(&Buffer[0]); + length = (int) BufInUse; + + 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: + /* + * Handle CJK characters, or as 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); +#endif + /* FALLTHRU */ + 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 (EditAt <= ((int) BufAlloc) && BufInUse < BufAlloc) { +#ifdef ENHANCED_LINEEDIT + if (EditMark > EditAt) + EditMark++; + else if (EditMark < -(1 + EditAt)) + EditMark--; + disableEditMark(); +#endif + ExtendEditor(edit, length + 1); + for (i = length; i >= EditAt; i--) /* Make room */ + Buffer[i + 1] = Buffer[i]; + Buffer[length + 1] = '\0'; + Buffer[EditAt] = (char) ch; + EditAt++; + } else { + if (maxMessage) { + _statusline(MAXLEN_REACHED_DEL_OR_MOV); + } + return (ch); + } + break; + + case LYE_BACKW: /* go backward one word */ + while (EditAt && !IsWordChar(Buffer[EditAt - 1])) + EditAt--; + while (EditAt && IsWordChar(UCH(Buffer[EditAt - 1]))) + EditAt--; + break; + + case LYE_FORWW: /* go forward one word */ + while (IsWordChar(UCH(Buffer[EditAt]))) + EditAt++; + while (!IsWordChar(Buffer[EditAt]) && Buffer[EditAt]) + EditAt++; + break; + + case LYE_ERASE: /* erase the line */ + Buffer[0] = '\0'; +#ifdef ENHANCED_LINEEDIT + EditMark = -1; /* Do not show the mark */ +#endif + /* FALLTHRU */ + + case LYE_BOL: /* go to beginning of line */ + EditAt = 0; + break; + + case LYE_EOL: /* go to end of line */ + EditAt = length; + break; + + case LYE_DELNW: /* delete next word */ + offset = EditAt; + LYDoEdit(edit, 0, LYE_FORWW, FALSE); + offset = EditAt - offset; + EditAt -= offset; + + goto shrink; /* right below */ + + case LYE_DELPW: /* delete previous word */ + offset = EditAt; + LYDoEdit(edit, 0, LYE_BACKW, FALSE); + offset -= EditAt; + + shrink: + for (i = EditAt; i < length - offset + 1; i++) + Buffer[i] = Buffer[i + offset]; +#ifdef ENHANCED_LINEEDIT + disableEditMark(); + if (EditMark <= -(1 + EditAt + offset)) + EditMark += offset; /* Shift it */ + if (-(1 + EditAt + offset) < EditMark && EditMark < -(1 + EditAt)) + EditMark = -(1 + EditAt); /* Set to the current position */ +#endif + + break; + + case LYE_DELBL: /* delete from cursor to beginning of line */ + for (i = EditAt; i < length + 1; i++) + Buffer[i - EditAt] = Buffer[i]; + +#ifdef ENHANCED_LINEEDIT + disableEditMark(); + if (EditMark <= -(1 + EditAt)) + EditMark += EditAt; /* Shift it */ + else + EditMark = -1; /* Reset it */ +#endif + EditAt = 0; + break; + + case LYE_DELEL: /* delete from cursor to end of line */ + Buffer[EditAt] = '\0'; +#ifdef ENHANCED_LINEEDIT + disableEditMark(); + if (EditMark <= -(1 + EditAt)) + EditMark = -1; /* Reset it */ +#endif + break; + + case LYE_DELN: /* delete next character */ + if (EditAt >= length) + break; +#ifndef SUPPORT_MULTIBYTE_EDIT + EditAt++; +#else + EditAt += mbcs_skip(Buffer + EditAt, 1); +#endif + /* FALLTHRU */ + + case LYE_DELP: /* delete previous character */ + if (length == 0 || EditAt == 0) + break; + +#ifndef SUPPORT_MULTIBYTE_EDIT +#ifdef ENHANCED_LINEEDIT + disableEditMark(); + if (EditMark <= -(1 + EditAt)) + EditMark++; +#endif + EditAt--; + for (i = EditAt; i < length; i++) + Buffer[i] = Buffer[i + 1]; +#else /* SUPPORT_MULTIBYTE_EDIT */ + offset = EditAt - mbcs_skip(Buffer, mbcs_glyphs(Buffer, EditAt) - 1); + EditAt -= offset; + for (i = EditAt; i < length - offset + 1; i++) + Buffer[i] = Buffer[i + offset]; + +#ifdef ENHANCED_LINEEDIT + disableEditMark(); + if (EditMark <= -(1 + EditAt)) + EditMark += offset; /* Shift it */ +#endif + +#endif /* SUPPORT_MULTIBYTE_EDIT */ + break; + + case LYE_FORW_RL: + case LYE_FORW: /* move cursor forward */ +#ifndef SUPPORT_MULTIBYTE_EDIT + if (EditAt < length) + EditAt++; +#else + if (EditAt < length) + EditAt += mbcs_skip(Buffer + EditAt, 1); +#endif + else if (action == LYE_FORW_RL) + return -ch; + break; + + case LYE_BACK_LL: + case LYE_BACK: /* move cursor backward */ +#ifndef SUPPORT_MULTIBYTE_EDIT + if (EditAt > 0) + EditAt--; +#else + if (EditAt > 0) + EditAt = mbcs_skip(Buffer, mbcs_glyphs(Buffer, EditAt) - 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 || EditAt == 0) + return (ch); + if (EditAt == length) + EditAt--; + enableEditMark(); + if (EditMark == EditAt || EditMark == EditAt + 1) + EditMark = EditAt - 1; + disableEditMark(); + if (Buffer[EditAt - 1] == Buffer[EditAt]) { + EditAt++; + break; + } + i = Buffer[EditAt - 1]; + Buffer[EditAt - 1] = Buffer[EditAt]; + Buffer[EditAt++] = (char) i; + break; + + case LYE_SETMARK: /* Emacs-like set-mark-command */ + EditMark = EditAt; + return (0); + + case LYE_XPMARK: /* Emacs-like exchange-point-and-mark */ + enableEditMark(); + if (EditMark == EditAt) + return (0); + i = EditAt; + EditAt = EditMark; + EditMark = i; + break; + + case LYE_KILLREG: /* Emacs-like kill-region */ + enableEditMark(); + if (EditMark == EditAt) { + BStrFree(killbuffer); + return (0); + } + if (EditMark > EditAt) + LYDoEdit(edit, 0, LYE_XPMARK, FALSE); + { + int reglen = EditAt - EditMark; + + BStrCopy1(killbuffer, Buffer + EditMark, reglen); + for (i = EditMark; Buffer[i + reglen]; i++) + Buffer[i] = Buffer[i + reglen]; + Buffer[i] = Buffer[i + reglen]; /* terminate */ + EditAt = EditMark; + } + disableEditMark(); + break; + + case LYE_YANK: /* Emacs-like yank */ + if (!killbuffer) { + EditMark = -(1 + EditAt); + return (0); + } else { + int yanklen = killbuffer->len; + + if ((EditAt + yanklen) <= (int) BufAlloc && + BufInUse + (size_t) yanklen <= BufAlloc) { + + ExtendEditor(edit, EditAt + yanklen); + + EditMark = -(1 + EditAt); + + for (i = length; i >= EditAt; i--) /* Make room */ + Buffer[i + yanklen] = Buffer[i]; + for (i = 0; i < yanklen; i++) + Buffer[EditAt++] = killbuffer->str[i]; + + } else if (maxMessage) { + _statusline(MAXLEN_REACHED_DEL_OR_MOV); + } + } + break; + +#endif /* ENHANCED_LINEEDIT */ + + case LYE_UPPER: + LYUpperCase(Buffer); + break; + + case LYE_LOWER: + LYLowerCase(Buffer); + break; + + default: + return (ch); + } + IsDirty = TRUE; + BufInUse = strlen(&Buffer[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) +{ + bstring *temp = NULL; + int result = 0; + + /* + * Load the c argument into the prompt buffer. + */ + BStrCopy0(temp, "?"); + temp->str[0] = (char) *c; + + _statusline(msg); + + /* + * Get the number, possibly with a suffix, from the user. + */ + if (LYgetBString(&temp, FALSE, 0, NORECALL) < 0 || isBEmpty(temp)) { + HTInfoMsg(CANCELLED); + *c = '\0'; + *rel = '\0'; + } else { + char *p = temp->str; + + *rel = '\0'; + result = 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; + } + BStrFree(temp); + return result; +} + +#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(FieldEditor * 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 + Offs2Col[offset] = x0; + + (void) y0; + (void) 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. + * FieldEditor fields retain their values as byte offsets. + * All external logic still works fine with byte values. + */ +void LYRefreshEdit(FieldEditor * edit) +{ + /* bytes and characters are not the same thing */ +#if defined(DEBUG_EDIT) + int all_bytes; +#endif + int pos_bytes = EditAt; + 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 Buffer */ + 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 EditAt */ + +#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 || (DpyWidth == 0)) + return; + + CTRACE((tfp, "LYRefreshEdit:%s\n", Buffer)); + + IsDirty = FALSE; + + BufInUse = strlen(&Buffer[0]); + + all_cells = LYstrCells(Buffer); + pos_cells = LYstrExtent2(Buffer, EditAt); + +#if defined(SUPPORT_MULTIBYTE_EDIT) && defined(DEBUG_EDIT) + all_bytes = (int) BufInUse; + lft_chars = mbcs_glyphs(Buffer, DpyStart); + pos_chars = mbcs_glyphs(Buffer, EditAt); + all_chars = mbcs_glyphs(Buffer, all_bytes); +#endif + + /* + * Now we have: + * .--DpyWidth--. + * +---------+=============+-----------+ + * | |M M| | (M=PanMargin) + * +---------+=============+-----------+ + * 0 DpyStart BufInUse + * + * Insertion point can be anywhere between 0 and stringlength. Calculate + * a new display starting point. + * + * First, make Lynx scroll several columns at a time as needed when + * extending the string. Doing this helps with lowspeed connections. + */ + + lft_bytes = DpyStart; + lft_cells = LYstrExtent2(Buffer, DpyStart); + + if ((lft_cells + DpyWidth) <= all_cells) { + if (pos_cells >= (lft_cells + DpyWidth) - PanMargin) { + lft_cells = (pos_cells - DpyWidth) + PanMargin; +#ifdef SUPPORT_MULTIBYTE_EDIT + lft_chars = cell2char(Buffer, lft_cells); + lft_bytes = mbcs_skip(Buffer, lft_chars); +#else + lft_bytes = lft_cells; +#endif /* SUPPORT_MULTIBYTE_EDIT */ + } + } + + if (pos_cells < lft_cells + PanMargin) { + lft_cells = pos_cells - PanMargin; + if (lft_cells < 0) + lft_cells = 0; +#ifdef SUPPORT_MULTIBYTE_EDIT + lft_chars = cell2char(Buffer, lft_cells); + lft_bytes = mbcs_skip(Buffer, 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 (IsPanned && 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 = &Buffer[lft_bytes]; + DpyStart = lft_bytes; + + dpy_cells = all_cells - lft_cells; + CTRACE_EDIT((tfp, "Comparing dpy_cells %d > (%d - %d)\n", + dpy_cells, DpyWidth, lft_shift)); + if (dpy_cells > (DpyWidth - lft_shift)) { + rgt_shift = 1; + dpy_cells = (DpyWidth - 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 && *str) { + 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 = (int) (next - str); + + while (i < j) { + Offs2Col[i++] = cell + StartX; + } + cell += LYstrExtent2(last, (int) (next - last)); + } while (i < dpy_bytes); + Offs2Col[i] = cell + StartX; + } else { +#if defined(ENHANCED_LINEEDIT) && defined(USE_COLOR_STYLE) + if (EditMark >= 0 && DpyStart > EditMark) + 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 (EditMark >= 0 && ((DpyStart + i == EditMark && EditAt > EditMark) + || (DpyStart + i == EditAt && EditAt < EditMark))) + TmpStyleOn(prompting ? s_prompt_sel : s_aedit_sel); + if (EditMark >= 0 && ((DpyStart + i == EditMark && EditAt < EditMark) + || (DpyStart + i == EditAt && EditAt > EditMark))) + 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 = Offs2Col[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 (EditMark >= 0 && + ((DpyStart + dpy_bytes <= EditMark && DpyStart + dpy_bytes > EditAt) + || (DpyStart + dpy_bytes > EditMark + && DpyStart + dpy_bytes <= EditAt))) { + TmpStyleOff(prompting ? s_prompt_sel : s_aedit_sel); + } +#endif + } + + /* + * Erase rest of input area. + */ + padsize = DpyWidth - (Offs2Col[dpy_bytes] - StartX); + fill_edited_line(prompting, padsize, PadChar); + + /* + * Scrolling indicators. + */ + if (IsPanned && 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, Offs2Col[EditAt - DpyStart]); + +#ifdef USE_COLOR_STYLE + if (estyle != NOSTYLE) + curses_style(estyle, STACK_OFF); +#endif + LYrefresh(); +} + +static void reinsertEdit(FieldEditor * edit, char *result) +{ + if (result != 0) { + LYDoEdit(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(*(STRING2PTR) a, *(STRING2PTR) b); +} + +static int normalCmpList(const void *a, + const void *b) +{ + return strcmp(*(STRING2PTR) a, *(STRING2PTR) b); +} + +static char **sortedList(HTList *list, int ignorecase) +{ + size_t count = (unsigned) HTList_count(list); + size_t j = 0; + size_t k, jk; + char **result = typecallocn(char *, count + 1); + + if (result == 0) + outofmem(__FILE__, "sortedList"); + + 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(STRING2PTR list) +{ + int result = 0; + + while (*list++ != 0) + result++; + return result; +} + +int LYarrayWidth(STRING2PTR list) +{ + int result = 0; + int check; + + while (*list != 0) { + check = (int) strlen(*list++); + if (check > result) + result = check; + } + return result; +} + +static void FormatChoiceNum(char *target, + int num_choices, + int choice, + const char *value) +{ + if (num_choices >= 0) { + int digits = (num_choices > 9) ? 2 : 1; + + sprintf(target, "%*d: %.*s", + digits, (choice + 1), + MAX_LINE - 9 - digits, value); + } else { + LYStrNCpy(target, value, MAX_LINE - 1); + } +} + +static unsigned options_width(STRING2PTR list) +{ + unsigned width = 0; + int count = 0; + + while (list[count] != 0) { + unsigned ncells = (unsigned) LYstrCells(list[count]); + + if (ncells > width) { + width = ncells; + } + count++; + } + return width; +} + +static void draw_option(WINDOW * win, int entry, + int width, + int 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((SLFUTURE_CONST char *) value, (unsigned) 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 */ +} + +static void show_popup_status(int cur_choice, + STRING2PTR choices, + int disabled, + int for_mouse) +{ + if (disabled) { + _statusline(CHOICE_LIST_UNM_MSG); + } else if (!for_mouse) { + if (fields_are_named()) { + char *status_msg = NULL; + + HTSprintf0(&status_msg, CHOICE_LIST_ADV_MSG, choices[cur_choice]); + _statusline(status_msg); + FREE(status_msg); + } else { + _statusline(CHOICE_LIST_MESSAGE); + } +#if defined(USE_MOUSE) && (defined(NCURSES) || defined(PDCURSES)) + } else { + _statusline(MOUSE_CHOICE_MESSAGE); +#endif + } +} + +#define SHOW_POPUP_STATUS() show_popup_status(cur_choice, choices, disabled, for_mouse) + +/* + * 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, + STRING2PTR choices, + int width, + int i_length, + int disabled, + int 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 bstring *prev_target = NULL; /* Search string buffer */ + static bstring *next_target = NULL; /* 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]; + STRING2PTR 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) { + BStrCopy0(next_target, ""); + first = FALSE; + } + BStrCopy0(prev_target, ""); + 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; + + SHOW_POPUP_STATUS(); + + /* + * 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, !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, !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); + SHOW_POPUP_STATUS(); + break; + } + window_offset = 0; + cur_choice = 0; + SHOW_POPUP_STATUS(); + 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); + SHOW_POPUP_STATUS(); + 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; + SHOW_POPUP_STATUS(); + 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); + SHOW_POPUP_STATUS(); + break; + } + cur_choice = window_offset = ((number - 1) * length); + SHOW_POPUP_STATUS(); + 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); + SHOW_POPUP_STATUS(); + 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; + } + SHOW_POPUP_STATUS(); + goto redraw; + } + + /* + * Not in range. - FM + */ + HTUserMsg(BAD_OPTION_NUM_ENTERED); + } + } + + /* + * Restore the popup statusline. - FM + */ + SHOW_POPUP_STATUS(); + 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 && isBEmpty(next_target)) { + /* + * 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) { + BStrCopy0(next_target, cp); + QueryNum = 0; + FirstRecall = FALSE; + } + } + BStrCopy(prev_target, next_target); + /* FALLTHRU */ + case LYK_WHEREIS: + if (isBEmpty(prev_target)) { + _statusline(ENTER_WHEREIS_QUERY); + if ((ch = LYgetBString(&prev_target, FALSE, 0, recall)) < 0) { + /* + * User cancelled the search via ^G. - FM + */ + HTInfoMsg(CANCELLED); + goto restore_popup_statusline; + } + } + + check_recall: + if (isBEmpty(prev_target) && + !(recall && (ch == UPARROW_KEY || ch == DNARROW_KEY))) { + /* + * No entry. Simply break. - FM + */ + HTInfoMsg(CANCELLED); + goto restore_popup_statusline; + } + + if (recall && ch == UPARROW_KEY) { + if (FirstRecall) { + /* + * Use the current string or last query in the list. - FM + */ + FirstRecall = FALSE; + if (!isBEmpty(next_target)) { + for (QueryNum = (QueryTotal - 1); + QueryNum > 0; QueryNum--) { + if ((cp = (char *) HTList_objectAt(search_queries, + QueryNum)) + != NULL && + !strcmp(next_target->str, 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) { + BStrCopy0(prev_target, cp); + if (!isBEmpty(next_target) && + !strcmp(next_target->str, prev_target->str)) { + _statusline(EDIT_CURRENT_QUERY); + } else if ((!isBEmpty(next_target) && QueryTotal == 2) || + (isBEmpty(next_target) && QueryTotal == 1)) { + _statusline(EDIT_THE_PREV_QUERY); + } else { + _statusline(EDIT_A_PREV_QUERY); + } + if ((ch = LYgetBString(&prev_target, + FALSE, 0, recall)) < 0) { + /* + * User cancelled the search via ^G. - FM + */ + HTInfoMsg(CANCELLED); + goto restore_popup_statusline; + } + goto check_recall; + } + } else if (recall && ch == DNARROW_KEY) { + if (FirstRecall) { + /* + * Use the current string or first query in the list. - FM + */ + FirstRecall = FALSE; + if (!isBEmpty(next_target)) { + for (QueryNum = 0; + QueryNum < (QueryTotal - 1); QueryNum++) { + if ((cp = (char *) HTList_objectAt(search_queries, + QueryNum)) + != NULL && + !strcmp(next_target->str, 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) { + BStrCopy0(prev_target, cp); + if (isBEmpty(next_target) && + !strcmp(next_target->str, prev_target->str)) { + _statusline(EDIT_CURRENT_QUERY); + } else if ((!isBEmpty(next_target) && QueryTotal == 2) || + (isBEmpty(next_target) && QueryTotal == 1)) { + _statusline(EDIT_THE_PREV_QUERY); + } else { + _statusline(EDIT_A_PREV_QUERY); + } + if ((ch = LYgetBString(&prev_target, + FALSE, 0, 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 + */ + BStrCopy(next_target, prev_target); + HTAddSearchQuery(next_target->str); + + /* + * 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 (LYcase_sensitive) { + if (strstr(buffer, next_target->str) != NULL) + break; + } else { + if (LYstrstr(buffer, next_target->str) != 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, next_target->str); + 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 (LYcase_sensitive) { + if (strstr(buffer, next_target->str) != NULL) + break; + } else { + if (LYstrstr(buffer, next_target->str) != 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, next_target->str); + + restore_popup_statusline: + /* + * Restore the popup statusline and reset the search variables. - + * FM + */ + SHOW_POPUP_STATUS(); + BStrCopy0(prev_target, ""); + 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); +} + +/* + * Allow the user to edit a string. + */ +int LYgetBString(bstring **inputline, + int hidden, + unsigned max_cols, + RecallType recall) +{ + int x, y; + int ch; + int xlec = -2; + int last_xlec = -1; + int last_xlkc = -1; + FieldEditor MyEdit, *edit = &MyEdit; + +#ifdef SUPPORT_MULTIBYTE_EDIT + BOOL refresh_mb = TRUE; +#endif /* SUPPORT_MULTIBYTE_EDIT */ + BOOL done = FALSE; + int result = -1; + + CTRACE((tfp, "called LYgetBString hidden %d, recall %d\n", hidden, (int) recall)); + + LYGetYX(y, x); /* Use screen from cursor position to eol */ + + (void) y; + (void) x; + + if (*inputline == NULL) /* caller may not have initialized this */ + BStrCopy0(*inputline, ""); + + LYSetupEdit(edit, (*inputline)->str, max_cols, LYcolLimit - x); + IsHidden = (BOOL) hidden; +#ifdef FEPCTRL + fep_on(); +#endif + + while (!done) { + beginning: +#ifndef SUPPORT_MULTIBYTE_EDIT + LYRefreshEdit(edit); +#else /* SUPPORT_MULTIBYTE_EDIT */ + if (refresh_mb) + LYRefreshEdit(edit); +#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 beginning; +#else + if (!refresh_mb + && (EditBinding(ch) != LYE_CHAR) + && (EditBinding(ch) != LYE_AIX)) + goto beginning; +#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_KEY || ch == DNARROW_KEY)) { + BStrCopy0(*inputline, Buffer); + LYAddToCloset(recall, Buffer); + CTRACE((tfp, "LYgetstr(%s) recall\n", (*inputline)->str)); +#ifdef FEPCTRL + fep_off(); +#endif + LYFinishEdit(edit); + result = ch; + done = TRUE; + continue; + } + ch |= InputMods; + InputMods = 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 beginning; +#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: + InputMods |= LKC_MOD1; + break; + case LYE_SETM2: + InputMods |= 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((STRING2PTR) data); + + while (cur_choice < num_options + && strcasecomp(data[cur_choice], Buffer) < 0) + cur_choice++; + + LYGetYX(old_y, old_x); + cur_choice = LYhandlePopupList(cur_choice, + 0, + old_x, + (STRING2PTR) data, + -1, + -1, + FALSE, + FALSE); + if (cur_choice >= 0) { + if (recall == RECALL_CMD) + _statusline(": "); + reinsertEdit(edit, data[cur_choice]); + } + LYmove(old_y, old_x); + FREE(data); + } + } else { + reinsertEdit(edit, LYFindInCloset(recall, Buffer)); + } + break; + +#ifndef CJK_EX + case LYE_AIX: + /* + * Handle CJK characters, or as 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(edit, ch, FALSE); + break; + } +#endif + /* FALLTHRU */ + case LYE_ENTER: + BStrCopy0(*inputline, Buffer); + if (!hidden) + LYAddToCloset(recall, Buffer); + CTRACE((tfp, "LYgetstr(%s) LYE_ENTER\n", (*inputline)->str)); +#ifdef FEPCTRL + fep_off(); +#endif + LYFinishEdit(edit); + result = ch; + done = TRUE; + continue; + +#ifdef CAN_CUT_AND_PASTE + case LYE_PASTE: + { + unsigned char *s = (unsigned char *) get_clip_grab(), *e; + size_t len; + + if (!s) + break; + len = 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(edit, s, (int) (e1 - s), + map_active, TRUE); + s = e1; + if (*e1 == '\t') { /* Replace by space */ + LYEditInsert(edit, + (unsigned const char *) " ", + 1, + map_active, + TRUE); + s = ++e1; + } else { + break; + } + } else { + ++e1; + } + } + if (e1 > s) { + LYEditInsert(edit, s, (int) (e1 - s), map_active, TRUE); + } + } + get_clip_release(); + break; + } +#endif + + case LYE_ABORT: + CTRACE((tfp, "LYgetstr LYE_ABORT\n")); +#ifdef FEPCTRL + fep_off(); +#endif + LYFinishEdit(edit); + BStrCopy0(*inputline, ""); + done = TRUE; + continue; + + case LYE_STOP: + CTRACE((tfp, "LYgetstr LYE_STOP\n")); +#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION + textfields_need_activation = TRUE; + LYFinishEdit(edit); + BStrCopy0(*inputline, ""); + done = TRUE; + continue; +#else +#ifdef ENHANCED_LINEEDIT + disableEditMark(); +#endif + break; +#endif + + 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(edit, ch, FALSE); +#else /* SUPPORT_MULTIBYTE_EDIT */ + if (LYLineEdit(edit, ch, FALSE) == 0) { + if (refresh_mb && IS_CJK_TTY && (0x81 <= ch) && (ch <= 0xfe)) + refresh_mb = FALSE; + else + refresh_mb = TRUE; + } else { + if (!refresh_mb) { + LYDoEdit(edit, 0, LYE_DELP, FALSE); + } + } +#endif /* SUPPORT_MULTIBYTE_EDIT */ + } + } + return result; +} + +/* + * Use this for fixed-buffer edits which have not been converted to use + * LYgetBString(). + */ +int LYgetstr(char *inputline, /* fixed-size buffer for input/output */ + int hidden, /* true to suppress from command-history */ + unsigned bufsize, /* sizeof(inputline) */ + RecallType recall) /* type of command-history */ +{ + int ch; + bstring *my_bstring = NULL; + + BStrCopy0(my_bstring, inputline); + if (my_bstring != 0) { + ch = LYgetBString(&my_bstring, hidden, bufsize, recall); + if (ch >= 0 && my_bstring != 0) + LYStrNCpy(inputline, my_bstring->str, bufsize); + BStrFree(my_bstring); + } else { + ch = -1; + } + return ch; +} + +const char *LYLineeditHelpURL(void) +{ + static int lasthelp_lineedit = -1; + static char helpbuf[LY_MAXPATH] = "\0"; + static char *phelp = &helpbuf[0]; + const char *result = NULL; + + if (lasthelp_lineedit == current_lineedit) { + result = helpbuf; + } else { + const char *source = LYLineeditHelpURLs[current_lineedit]; + size_t available; + + if (lasthelp_lineedit == -1) { + LYStrNCpy(helpbuf, helpfilepath, sizeof(helpbuf) - 1); + phelp += strlen(helpbuf); + } + available = (sizeof(helpbuf) - (size_t) (phelp - helpbuf)); + if (non_empty(source) && + (strlen(source) <= available)) { + LYStrNCpy(phelp, source, available); + lasthelp_lineedit = current_lineedit; + result = helpbuf; + } + } + return result; +} + +/* + * 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); + *result = 0.0; + if (StrChr(src, '.') != 0) { + long frc_part = 0; + float scale = 1.0; + + if (*src != '.') { + temp = NULL; + frc_part = strtol(src, &temp, 10); + *result = (float) frc_part; + src = temp; + } + if (src != 0 && *src == '.') { + ++src; + if (isdigit(UCH(*src))) { + temp = NULL; + frc_part = strtol(src, &temp, 10); + if (temp != 0) { + int digits = (int) (temp - src); + + while (digits-- > 0) + scale *= (float) 10.0; + *result += ((float) 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; + } + FREE(extra); + 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 *marker; + char *result = 0; + + if (non_empty(stringp)) { + result = *stringp; /* will return the old value */ + marker = strpbrk(*stringp, delim); + if (marker) { + *marker = '\0'; /* terminate the substring */ + *stringp = ++marker; /* point to the next substring */ + } else { + *stringp = 0; /* this was the last */ + } + } + return result; +} + +/* + * LYstrstr finds the first occurrence of the string pointed to by needle + * in the string pointed to by haystack. + * + * It returns NULL if the string is not found. + * + * It is a case insensitive search. + */ +char *LYstrstr(char *haystack, + const char *needle) +{ + int len = (int) strlen(needle); + char *result = NULL; + + for (; *haystack != '\0'; haystack++) { + if (0 == UPPER8(*haystack, *needle)) { + if (0 == strncasecomp8(haystack + 1, needle + 1, len - 1)) { + result = haystack; + break; + } + } + } + + return (result); +} + +#define SkipSpecialChars(p) \ + while (IsSpecialAttrChar(*p) && *p != '\0') \ + p++ + +/* + * LYno_attr_char_case_strstr finds the first occurrence of the + * string pointed to by needle in the string pointed to by haystack. + * + * It ignores special characters, e.g., LY_UNDERLINE_START_CHAR in haystack. + * + * It is a case insensitive search. + */ +const char *LYno_attr_char_case_strstr(const char *haystack, + const char *needle) +{ + const char *refptr, *tstptr; + const char *result = NULL; + + if (haystack != NULL && needle != NULL) { + + SkipSpecialChars(haystack); + + for (; *haystack != '\0' && (result == NULL); haystack++) { + if (0 == UPPER8(*haystack, *needle)) { + refptr = haystack + 1; + tstptr = needle + 1; + + if (*tstptr == '\0') { + result = haystack; + break; + } + + while (1) { + if (!IsSpecialAttrChar(*refptr)) { + if (0 != UPPER8(*refptr, *tstptr)) + break; + refptr++; + tstptr++; + } else { + refptr++; + } + if (*tstptr == '\0') { + result = haystack; + break; + } + if (*refptr == '\0') + break; + } + } + } + } + + return (result); +} + +/* + * LYno_attr_char_strstr finds the first occurrence of the + * string pointed to by needle in the string pointed to by haystack. + * It ignores special characters, e.g., LY_UNDERLINE_START_CHAR in haystack. + * + * It is a case sensitive search. + */ +const char *LYno_attr_char_strstr(const char *haystack, + const char *needle) +{ + const char *refptr, *tstptr; + const char *result = NULL; + + if (haystack != NULL && needle != NULL) { + + SkipSpecialChars(haystack); + + for (; *haystack != '\0' && (result == NULL); haystack++) { + if ((*haystack) == (*needle)) { + refptr = haystack + 1; + tstptr = needle + 1; + + if (*tstptr == '\0') { + result = haystack; + break; + } + + while (1) { + if (!IsSpecialAttrChar(*refptr)) { + if ((*refptr) != (*tstptr)) + break; + refptr++; + tstptr++; + } else { + refptr++; + } + if (*tstptr == '\0') { + result = haystack; + break; + } else if (*refptr == '\0') { + break; + } + } + } + } + } + + return (result); +} + +/* + * LYno_attr_mbcs_case_strstr finds the first occurrence of the string pointed + * to by needle in the string pointed to by haystack. 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 special characters, e.g., LY_UNDERLINE_START_CHAR in haystack. + * + * It assumes UTF8 if utf_flag is set. + * + * It is a case insensitive search. + */ +const char *LYno_attr_mbcs_case_strstr(const char *haystack, + const char *needle, + int utf_flag, + int count_gcells, + int *nstartp, + int *nendp) +{ + const char *refptr; + const char *tstptr; + int len = 0; + int offset; + const char *result = NULL; + + if (haystack != NULL && needle != NULL) { + + SkipSpecialChars(haystack); + + for (; *haystack != '\0' && (result == NULL); haystack++) { + if ((!utf_flag && IS_CJK_TTY && is8bits(*haystack) && + *haystack == *needle && + IsNormalChar(*(haystack + 1))) || + (0 == UPPER8(*haystack, *needle))) { + int tarlen = 0; + + offset = len; + len++; + + refptr = (haystack + 1); + tstptr = (needle + 1); + + if (*tstptr == '\0') { + if (nstartp) + *nstartp = offset; + if (nendp) + *nendp = len; + result = haystack; + break; + } + if (!utf_flag && IS_CJK_TTY && is8bits(*haystack) && + *haystack == *needle && + IsNormalChar(*refptr)) { + /* handle a CJK multibyte string */ + if (*refptr == *tstptr) { + refptr++; + tstptr++; + if (count_gcells) + tarlen++; + if (*tstptr == '\0') { + if (nstartp) + *nstartp = offset; + if (nendp) + *nendp = len + tarlen; + result = haystack; + break; + } + } else { + /* not a match */ + haystack++; + if (count_gcells) + len++; + continue; + } + } + /* compare the remainder of the string */ + while (1) { + if (!IsSpecialAttrChar(*refptr)) { + if (!utf_flag && IS_CJK_TTY && is8bits(*refptr)) { + if (*refptr == *tstptr && + *(refptr + 1) == *(tstptr + 1) && + !IsSpecialAttrChar(*(refptr + 1))) { + refptr++; + tstptr++; + if (count_gcells) + tarlen++; + } else { + break; + } + } else if (0 != UPPER8(*refptr, *tstptr)) { + break; + } + + if (!IS_UTF_EXTRA(*tstptr)) { + tarlen++; + } + refptr++; + tstptr++; + + } else { + refptr++; + } + + if (*tstptr == '\0') { + if (nstartp) + *nstartp = offset; + if (nendp) + *nendp = len + tarlen; + result = haystack; + break; + } + if (*refptr == '\0') + break; + } + } else if (!(IS_UTF_EXTRA(*haystack) || + IsSpecialAttrChar(*haystack))) { + if (!utf_flag && IS_CJK_TTY && is8bits(*haystack) && + IsNormalChar(*(haystack + 1))) { + haystack++; + if (count_gcells) + len++; + } + len++; + } + } + } + + return (result); +} + +/* + * LYno_attr_mbcs_strstr finds the first occurrence of the string pointed + * to by needle in the string pointed to by haystack. + * + * 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 special characters, e.g., LY_UNDERLINE_START_CHAR in haystack. + * + * It assumes UTF8 if utf_flag is set. + * + * It is a case sensitive search. + */ +const char *LYno_attr_mbcs_strstr(const char *haystack, + const char *needle, + int utf_flag, + int count_gcells, + int *nstartp, + int *nendp) +{ + const char *refptr; + const char *tstptr; + int len = 0; + int offset; + const char *result = NULL; + + if (haystack != NULL && needle != NULL) { + + SkipSpecialChars(haystack); + + for (; *haystack != '\0' && (result == NULL); haystack++) { + if ((*haystack) == (*needle)) { + int tarlen = 0; + + offset = len; + len++; + + refptr = (haystack + 1); + tstptr = (needle + 1); + + if (*tstptr == '\0') { + if (nstartp) + *nstartp = offset; + if (nendp) + *nendp = len; + result = haystack; + break; + } else if (!utf_flag && + IS_CJK_TTY && + is8bits(*haystack) && + IsNormalChar(*refptr)) { + /* handle a CJK multibyte string */ + if (*refptr == *tstptr) { + /* found match */ + refptr++; + tstptr++; + if (count_gcells) + tarlen++; + if (*tstptr == '\0') { + if (nstartp) + *nstartp = offset; + if (nendp) + *nendp = len + tarlen; + result = haystack; + break; + } + } else { + /* not a match - restart comparison */ + haystack++; + if (count_gcells) + len++; + continue; + } + } + /* compare the remainder of the string */ + while (1) { + if (!IsSpecialAttrChar(*refptr)) { + if (!utf_flag && IS_CJK_TTY && is8bits(*refptr)) { + if (*refptr == *tstptr && + *(refptr + 1) == *(tstptr + 1) && + !IsSpecialAttrChar(*(refptr + 1))) { + refptr++; + tstptr++; + if (count_gcells) + tarlen++; + } else { + break; + } + } else if ((*refptr) != (*tstptr)) { + break; + } + + if (!IS_UTF_EXTRA(*tstptr)) { + tarlen++; + } + refptr++; + tstptr++; + } else { + refptr++; + } + + if (*tstptr == '\0') { + if (nstartp) + *nstartp = offset; + if (nendp) + *nendp = len + tarlen; + result = haystack; + break; + } + if (*refptr == '\0') + break; + } + } else if (!(IS_UTF_EXTRA(*haystack) || + IsSpecialAttrChar(*haystack))) { + if (!utf_flag && IS_CJK_TTY && is8bits(*haystack) && + IsNormalChar(*(haystack + 1))) { + haystack++; + if (count_gcells) + len++; + } + len++; + } + } + } + return (result); +} + +/* + * Allocate and return a copy of a string. + * see StrAllocCopy + */ +char *SNACopy(char **target, + const char *source, + size_t n) +{ + FREE(*target); + if (source) { + *target = typeMallocn(char, n + 1); + + if (*target == NULL) { + CTRACE((tfp, "Tried to malloc %lu bytes\n", (unsigned long) n)); + outofmem(__FILE__, "SNACopy"); + } + LYStrNCpy(*target, source, n); + } + return *target; +} + +/* + * Combine string allocation and concatenation. + * see StrAllocCat + */ +char *SNACat(char **target, + const char *source, + size_t n) +{ + if (non_empty(source)) { + if (*target) { + size_t length = strlen(*target); + + *target = typeRealloc(char, *target, length + n + 1); + + if (*target == NULL) + outofmem(__FILE__, "SNACat"); + LYStrNCpy(*target + length, source, n); + } else { + *target = typeMallocn(char, n + 1); + + if (*target == NULL) + outofmem(__FILE__, "SNACat"); + MemCpy(*target, source, n); + (*target)[n] = '\0'; /* terminate */ + } + } + return *target; +} + +#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; + long result = upper; + + if (upper > 0) { + /* + * 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; + } else if (diff > 0) { + high = i; + } else if (diff == 0) { + result = unicode_to_lower_case[i].lower; + break; + } + } + } + + return result; +} + +/* + * 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) +{ + int result = 0; + + if (ch1 == ch2) { + result = 0; + } else if (!ch2) { + result = UCH(ch1); + } else if (!ch1) { + result = -UCH(ch2); + } else if (UCH(TOASCII(ch1)) < 128 && UCH(TOASCII(ch2)) < 128) { + /* case-insensitive match for us-ascii */ + result = (TOUPPER(ch1) - TOUPPER(ch2)); + } else if (UCH(TOASCII(ch1)) > 127 && + UCH(TOASCII(ch2)) > 127) { + /* case-insensitive match for upper half */ + if (DisplayCharsetMatchLocale) { + result = (TOUPPER(ch1) - TOUPPER(ch2)); /* old-style */ + } else { + long uni_ch2 = UCTransToUni((char) ch2, current_char_set); + long uni_ch1; + + if (uni_ch2 < 0) { + result = UCH(ch1); + } else { + uni_ch1 = UCTransToUni((char) ch1, current_char_set); + result = (int) (UniToLowerCase(uni_ch1) - UniToLowerCase(uni_ch2)); + } + } + } else { + result = -10; /* mismatch */ + } + + return result; +} + +/* + * 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 **target, + FILE *fp) +{ + char buffer[BUFSIZ]; + char *result = 0; + + if (target != 0) + result = *target; + if (result != 0) + *result = 0; + + while (fgets(buffer, (int) 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 (target != 0) + *target = result; + return result; +} + +#ifdef USE_CMD_LOGGING +static FILE *cmd_logfile; +static FILE *cmd_script; + +void LYOpenCmdLogfile(int argc, + char **argv) +{ + int n; + + if (non_empty(lynx_cmd_logfile)) { + 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 (non_empty(lynx_cmd_script)) { + 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(EXIT_SUCCESS); + 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); + } + if (LYSetConfigValue(src, tmp)) { + CTRACE((tfp, "LYSetConfigValue(%s, %s)\n", src, tmp)); + } else if (LYsetRcValue(src, tmp)) { + CTRACE((tfp, "LYsetRcValue(%s, %s)\n", src, tmp)); + } else { + CTRACE((tfp, "?? set ignored %s\n", src)); + } + } + 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), (unsigned) 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 0000000..a401379 --- /dev/null +++ b/src/LYStrings.h @@ -0,0 +1,402 @@ +/* + * $LynxId: LYStrings.h,v 1.117 2018/05/04 22:47:10 tom Exp $ + */ +#ifndef LYSTRINGS_H +#define LYSTRINGS_H + +#include <LYCurses.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define SQUOTE '\'' +#define DQUOTE '"' +#define ESCAPE '\\' +#define LPAREN '(' +#define RPAREN ')' + + typedef const char *const Const2CharPtr; + 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); +#define LYStrNCpy(dst,src,n) LYstrncpy(dst,src,(int)(n)) + extern void ena_csi(int flag); + extern int get_popup_number(const char *msg, + int *c, + int *rel); + extern int LYarrayLength(STRING2PTR list); + extern int LYarrayWidth(STRING2PTR 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 masked, + unsigned bufsize, + RecallType recall); +#define LYGetStr(input,masked,bufsize,recall) \ + LYgetstr(input,masked,(unsigned)(bufsize),recall) + extern int LYgetBString(bstring **inputline, + int masked, + unsigned max_cols, + 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, + int utf_flag); + extern const char *LYmbcs_skip_cells(const char *data, + int n_cells, + int utf_flag); + extern const char *LYmbcs_skip_glyphs(const char *data, + int n_glyphs, + int utf_flag); + extern int LYmbcsstrlen(const char *str, + int utf_flag, + int count_gcells); + + extern const char *LYno_attr_mbcs_strstr(const char *chptr, + const char *tarptr, + int utf_flag, + int count_gcells, + int *nstartp, + int *nendp); + extern const char *LYno_attr_mbcs_case_strstr(const char *chptr, + const char *tarptr, + int utf_flag, + int count_gcells, + int *nstartp, + int *nendp); + +#define LYno_attr_mb_strstr(chptr, tarptr, utf_flag, count_gcells, nstartp, nendp) \ + (LYcase_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) \ + (LYcase_sensitive \ + ? LYno_attr_char_strstr(chptr, tarptr) \ + : LYno_attr_char_case_strstr(chptr, tarptr)) + + extern char *SNACopy(char **dest, + const char *src, + size_t n); + extern char *SNACat(char **dest, + const char *src, + size_t 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. + * These are "extra" keys which do not fit into a single byte. + */ + typedef enum { + UNKNOWN_KEY = -1 + ,DEL_KEY = 127 + ,UPARROW_KEY = 256 + ,DNARROW_KEY + ,RTARROW_KEY + ,LTARROW_KEY + ,PGDOWN_KEY + ,PGUP_KEY + ,HOME_KEY + ,END_KEY + ,F1_KEY + ,DO_KEY + ,FIND_KEY + ,SELECT_KEY + ,INSERT_KEY + ,REMOVE_KEY + ,DO_NOTHING + ,BACKTAB_KEY + /* these should be referenced by name in keymap, e.g., "f2" */ + ,F2_KEY + ,F3_KEY + ,F4_KEY + ,F5_KEY + ,F6_KEY + ,F7_KEY + ,F8_KEY + ,F9_KEY + ,F10_KEY + ,F11_KEY + ,F12_KEY + /* this has known value */ + ,MOUSE_KEY = 285 /* 0x11D */ + } LYExtraKeys; + +/* ***** 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 */ + +#ifdef USE_ALT_BINDINGS +/* Enable code implementing additional, mostly emacs-like, line-editing + functions. - kw */ +#define ENHANCED_LINEEDIT +#endif + +/* FieldEditor preserves state between calls to LYDoEdit + */ + typedef struct { + + int efStartX; /* Origin of edit-field */ + int efStartY; + int efWidth; /* Screen real estate for editing */ + + char *efBuffer; /* the buffer which is being edited */ + size_t efBufInUse; /* current size of string. */ + size_t efBufAlloc; /* current buffer-size, excluding nul at end */ + size_t efBufLimit; /* buffer size limit, zero if indefinite */ + + char efPadChar; /* Right padding typically ' ' or '_' */ + BOOL efIsMasked; /* Masked password entry flag */ + + BOOL efIsDirty; /* accumulate refresh requests */ + BOOL efIsPanned; /* Need horizontal scroll indicator */ + int efDpyStart; /* Horizontal scroll offset */ + int efEditAt; /* Insertion point in string */ + int efPanMargin; /* Number of columns look-ahead/look-back */ + int efInputMods; /* Modifiers for next input lynxkeycode */ +#ifdef ENHANCED_LINEEDIT + int efEditMark; /* position of emacs-like mark, or -1-pos to denote + unactive mark. */ +#endif + + int *efOffs2Col; /* fixups for multibyte characters */ + + } FieldEditor; + +/* line-edit action encoding */ + + typedef enum { + LYE_UNKNOWN = -1 /* no binding */ + ,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, int internal); +#endif + + extern BOOL LYRemapEditBinding(int xlkc, int lec, int select_edi); /* in LYEditmap.c */ + extern BOOLEAN LYRemoveNewlines(char *buffer); + extern BOOLEAN LYTrimStartfile(char *buffer); + extern LYExtraKeys LYnameToExtraKeys(const char *name); + extern char *LYElideString(char *str, int cut_pos); + extern char *LYReduceBlanks(char *buffer); + extern char *LYRemoveBlanks(char *buffer); + extern char *LYSkipBlanks(char *buffer); + extern char *LYSkipNonBlanks(char *buffer); + extern char *LYTrimNewline(char *buffer); + extern const char *LYSkipCBlanks(const char *buffer); + extern const char *LYSkipCNonBlanks(const char *buffer); + extern const char *LYextraKeysToName(LYExtraKeys code); + extern int EditBinding(int ch); /* in LYEditmap.c */ + extern int LYDoEdit(FieldEditor * edit, int ch, int action, int maxMessage); + extern int LYEditKeyForAction(int lac, int *pmodkey); /* LYEditmap.c */ + extern int LYKeyForEditAction(int lec); /* in LYEditmap.c */ + extern int LYhandlePopupList(int cur_choice, int ly, int lx, + STRING2PTR choices, + int width, + int i_length, + int disabled, + int for_mouse); + extern void LYCloseCloset(RecallType recall); + extern void LYEscapeStartfile(char **buffer); + extern void LYFinishEdit(FieldEditor * edit); + extern void LYLowerCase(char *buffer); + extern void LYRefreshEdit(FieldEditor * edit); + extern void LYSetupEdit(FieldEditor * edit, char *old, + unsigned buffer_limit, + int display_limit); + extern void LYTrimAllStartfile(char *buffer); + extern void LYTrimLeading(char *buffer); + extern void LYTrimTrailing(char *buffer); + extern void LYUpperCase(char *buffer); + + typedef short LYEditCode; + + typedef struct { + int code; + LYEditCode edit; + } LYEditInit; + + typedef struct { + const char *name; + const LYEditInit *init; + LYEditCode *used; + } LYEditConfig; + + extern int current_lineedit; + extern const char *LYEditorNames[]; + extern LYEditConfig LYLineEditors[]; + extern const char *LYLineeditHelpURLs[]; + +#define CurrentLineEditor() LYLineEditors[current_lineedit].used + + extern void LYinitEditmap(void); + extern void LYinitKeymap(void); + extern const char *LYLineeditHelpURL(void); + + extern int escape_bound; + +#define LYLineEdit(e,c,m) LYDoEdit(e, c, EditBinding(c) & ~LYE_DF, m) + + extern int LYEditInsert(FieldEditor * edit, unsigned const char *s, + int len, int map_active, + int maxMessage); + +#ifdef __cplusplus +} +#endif +#endif /* LYSTRINGS_H */ diff --git a/src/LYStructs.h b/src/LYStructs.h new file mode 100644 index 0000000..179914a --- /dev/null +++ b/src/LYStructs.h @@ -0,0 +1,190 @@ +/* + * $LynxId: LYStructs.h,v 1.32 2021/06/09 20:56:05 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; + 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 unsigned 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 *menu_name; /* menu-name for EXTERNAL / EXTERNAL_MENU */ + char *command; /* the command to execute */ + BOOL always_enabled; /* a constant to tell whether or + * not to disable the printer + * when the no_print option is on + */ + /* HTML lists: */ + BOOL override_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 0000000..77be188 --- /dev/null +++ b/src/LYStyle.c @@ -0,0 +1,970 @@ +/* + * $LynxId: LYStyle.c,v 1.111 2021/06/09 22:00:35 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 <LYOptions.h> +#include <LYPrettySrc.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 HTList *list_of_lss_files; + +/* 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; +static BOOL empty_lss_list = FALSE; /* true if list explicitly emptied */ + +/* stack of attributes during page rendering */ +int last_styles[MAX_LAST_STYLES + 1] = +{0}; +int last_colorattr_ptr = 0; + +bucket hashStyles[CSHASHSIZE]; + +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 void style_initialiseHashTable(void); + +static bucket *new_bucket(const char *name) +{ + bucket *result = typecalloc(bucket); + + if (!result) + outofmem(__FILE__, "new_bucket"); + StrAllocCopy(result->name, name); + return result; +} + +bucket *nostyle_bucket(void) +{ + return new_bucket("<NOSTYLE>"); +} + +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 = color_style_1(element); + int colored_attr; + + 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 = !!((unsigned) cA & A_BOLD); + int iBlink = !!((unsigned) cA & M_BLINK); + + CTRACE2(TRACE_STYLE, (tfp, "parse_attributes %d/%d %d/%d %#x\n", + fA, default_fg, bA, default_bg, (unsigned) 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)); + colored_attr = ((int) COLOR_PAIR(curPair)) | ((int) cA); + if (style < DSTYLE_ELEMENTS) + setStyle(style, colored_attr, cA, mA); + setHashStyle(newstyle, colored_attr, 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, color_style_1(element), + (hashStyles[color_style_1(element)].used) + ? "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) = color_style_1(table[n].name); + found = TRUE; + break; + } + } + + if (found) { + if (!strcasecomp(element, "normal")) { + /* added - kw */ + parse_attributes(mono, fg, bg, DSTYLE_NORMAL, "html"); + s_normal = color_style_1("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 = (int) (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_lss_list(void) +{ + LSS_NAMES *obj; + + while ((obj = HTList_objectAt(list_of_lss_files, 0)) != 0) { + FREE(obj->given); + FREE(obj->actual); + FREE(obj); + if (!HTList_removeObject(list_of_lss_files, obj)) { + break; + } + } + HTList_delete(list_of_lss_files); +} + +static void free_colorstylestuff(void) +{ + if (TRACE_STYLE) { + report_hashStyles(); + } + style_initialiseHashTable(); + free_hashStyles(); + style_deleteStyleList(); + memset(our_pairs, 0, sizeof(our_pairs)); + FreeCachedStyles(); +} + +/* Set all the buckets in the hash table to be empty */ +static void style_initialiseHashTable(void) +{ + int i; + static BOOL firsttime = TRUE; + + for (i = 0; i < CSHASHSIZE; i++) { + hashStyles[i].used = FALSE; + } + if (firsttime) { + firsttime = FALSE; +#ifdef LY_FIND_LEAKS + atexit(free_colorstylestuff); + atexit(free_colorstyle_leaks); +#endif + } + s_alink = color_style_1("alink"); + s_a = color_style_1("a"); + s_status = color_style_1("status"); + s_alert = color_style_1("alert"); + s_title = color_style_1("title"); +#ifdef USE_SCROLLBAR + s_sb_bar = color_style_1("scroll.bar"); + s_sb_bg = color_style_1("scroll.back"); + s_sb_aa = color_style_1("scroll.arrow"); + s_sb_naa = color_style_1("scroll.noarrow"); +#endif +} + +/* + * 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" }, + { 5, "input" }, + { 6, "alink" }, + { 7, "whereis" }, +#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); +} + +void parse_userstyles(void) +{ + char *name; + HTList *cur = LYuse_color_style ? lss_styles : 0; + + 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); + FREE(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 = color_style_1(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 = color_style_1(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) +{ + int i; + + for (i = 0; i < HTML_ELEMENTS; ++i) { + cached_tag_styles[i] = color_style_1(HTML_dtd.tags[i].name); + } +} + +#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; + } +} + +/* + * Recompute the pairs associated with the color style. + */ +void update_color_style(void) +{ + CTRACE((tfp, "update_color_style %p\n", (void *) lss_styles)); + memset(our_pairs, 0, sizeof(our_pairs)); + parse_userstyles(); +} + +static char *find_lss_file(const char *nominal) +{ + return LYFindConfigFile(nominal, LYNX_LSS_FILE); +} + +void clear_lss_list(void) +{ + CTRACE((tfp, "clear_lss_list()\n")); + free_lss_list(); + empty_lss_list = TRUE; +} + +/* + * Add an entry to the lss-list, and cache the resolved filename if known. + */ +void add_to_lss_list(const char *source, const char *resolved) +{ + LSS_NAMES *obj; + LSS_NAMES *chk; + BOOLEAN found = FALSE; + int position = 0; + +#ifdef LY_FIND_LEAKS + atexit(free_colorstyle_leaks); +#endif + + CTRACE((tfp, "add_to_lss_list(\"%s\", \"%s\")\n", + NonNull(source), + NonNull(resolved))); + + if (list_of_lss_files == 0) { + list_of_lss_files = HTList_new(); + } + + while ((chk = HTList_objectAt(list_of_lss_files, position++)) != 0) { + if (!strcmp(source, chk->given)) { + found = TRUE; + if (resolved && !chk->actual) { + StrAllocCopy(chk->actual, resolved); + } + break; + } + } + + if (!found) { + obj = typecalloc(LSS_NAMES); + if (obj == NULL) + outofmem(__FILE__, "add_to_lss_list"); + + StrAllocCopy(obj->given, source); + StrAllocCopy(obj->actual, resolved); + HTList_appendObject(list_of_lss_files, obj); + empty_lss_list = FALSE; + } +} + +/* + * This is called after reading lynx.cfg, to set the initial value for the + * lss-file, and read its data. + */ +void init_color_styles(char **from_cmdline, const char *default_styles) +{ + char *user_lss_file = *from_cmdline; + char *cp; + + /* + * If a command-line "-lss" option was given, or if an environment variable + * is found, use that in preference to data from lynx.cfg + */ + if (user_lss_file == 0) + user_lss_file = LYGetEnv("LYNX_LSS"); + if (user_lss_file == 0) + user_lss_file = LYGetEnv("lynx_lss"); + if (user_lss_file != 0) + empty_lss_list = (*user_lss_file == '\0'); + + /* + * If the color-style is explicitly emptied, go no further. + */ + if (empty_lss_list) { + CTRACE((tfp, "init_color_styles: overridden/empty\n")); + return; + } else if (list_of_lss_files == 0) { + char *source = 0; + char *config; + + StrAllocCopy(source, default_styles); + config = source; + while ((cp = LYstrsep(&config, ";")) != 0) { + char *target; + + target = find_lss_file(LYPathLeaf(cp)); + if (target != 0) { + add_to_lss_list(cp, target); + FREE(target); + } + } + FREE(source); + } + + if (user_lss_file != 0) { + FREE(lynx_lss_file); + lynx_lss_file = find_lss_file(cp = user_lss_file); + *from_cmdline = 0; + } else { + lynx_lss_file = find_lss_file(cp = DeConst(LYNX_LSS_FILE)); + } + CTRACE1((tfp, "init_color_styles(%s)\n", NonNull(lynx_lss_file))); + + if (isEmpty(lynx_lss_file)) + return; + /* + * If the lynx-style file is not available, inform the user and exit. + */ + if (!LYCanReadFile(lynx_lss_file)) { + fprintf(stderr, gettext("\nLynx file \"%s\" is not available.\n\n"), + NonNull(cp)); + exit_immediately(EXIT_FAILURE); + } + + /* + * Otherwise, load the initial lss-file and add it to the list for the + * options menu. + */ + style_readFromFile(lynx_lss_file); + add_to_lss_list(LYPathLeaf(lynx_lss_file), lynx_lss_file); +#ifndef NO_OPTION_FORMS + build_lss_enum(list_of_lss_files); +#endif +} + +void reinit_color_styles(void) +{ +#ifdef USE_PRETTYSRC + int cs; + + for (cs = 0; cs < HTL_num_lexemes; ++cs) { + html_src_clean_item((HTlexeme) cs); + } +#endif + free_colorstylestuff(); + style_readFromFile(lynx_lss_file); +} + +#endif /* USE_COLOR_STYLE */ diff --git a/src/LYStyle.h b/src/LYStyle.h new file mode 100644 index 0000000..a7e5e81 --- /dev/null +++ b/src/LYStyle.h @@ -0,0 +1,88 @@ +/* $LynxId: LYStyle.h,v 1.20 2020/01/21 21:35:25 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 + + typedef struct { + char *given; + char *actual; + } LSS_NAMES; + + /* 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. A value of -1 means that the 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 element + * of 'cached_tag_styles' -HV + */ + extern void cache_tag_styles(void); + + /* use this for reading the end of string found during last invocation 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 0000000..2aa95fa --- /dev/null +++ b/src/LYTraversal.c @@ -0,0 +1,182 @@ +/* + * $LynxId: LYTraversal.c,v 1.30 2010/09/24 22:57:01 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, int 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; + size_t 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 0000000..8e712e5 --- /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 0000000..a83a103 --- /dev/null +++ b/src/LYUpload.c @@ -0,0 +1,221 @@ +/* + * $LynxId: LYUpload.c,v 1.41 2021/07/29 20:32:26 tom Exp $ + * + * 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 = LYstrstr(line, "TO=")) == NULL) + goto failed; + *(directory - 1) = '\0'; + /* go past "TO=" */ + directory += 3; + + if ((method = LYstrstr(line, "UPLOAD=")) == NULL) + goto failed; + /* + * Go past "UPLOAD=". + */ + 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, FALSE, 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)); + + 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; + char *curloc = NULL; + + if ((fp0 = InternalPageFP(tempfile, TRUE)) == 0) + return (-1); + +#ifdef VMS + StrAllocCopy(curloc, "/sys$login"); +#else + StrAllocCopy(curloc, HTfullURL_toFile(directory)); + LYTrimPathSep(curloc); +#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, " <NONE>\n"); + } + + fprintf(fp0, "</pre>\n"); + EndInternalPage(fp0); + LYCloseTempFP(fp0); + + LYforce_no_cache = TRUE; + FREE(curloc); + + return (0); +} diff --git a/src/LYUpload.h b/src/LYUpload.h new file mode 100644 index 0000000..84a71a0 --- /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 0000000..e45e641 --- /dev/null +++ b/src/LYUtils.c @@ -0,0 +1,8038 @@ +/* + * $LynxId: LYUtils.c,v 1.309 2024/01/15 17:10:52 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 +#elif defined(HAVE_GETTEXT) +#undef gettext +#define gettext conio_gettext +#else +#undef gettext +#endif + +#include <conio.h> + +#ifdef DONT_USE_GETTEXT +#define gettext(s) s +#elif defined(HAVE_GETTEXT) +#undef gettext +#ifdef _INTL_REDIRECT_MACROS +#define gettext libintl_gettext /* restore definition from libintl.h */ +#endif +#else +#undef gettext +#define gettext(s) s +#endif + +#if !defined(kbhit) && defined(_WCONIO_DEFINED) +#define kbhit() _kbhit() /* reasonably recent conio.h */ +#endif +#elif defined(__minix) +#include <termios.h> /* for struct winsize */ + +#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 + */ +static 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; +} + +static char *w32_get_shell_folder(const char *name) +{ + static HKEY rootkey = HKEY_CURRENT_USER; + + char *result = 0; + HKEY hkey; + char buffer[LY_MAXPATH]; + + if (RegOpenKeyEx(rootkey, + W32_STRING("Software" + "\\Microsoft" + "\\Windows" + "\\CurrentVersion" + "\\Explorer" + "\\Shell Folders"), + 0, + KEY_READ, + &hkey) == ERROR_SUCCESS) { + if (w32_get_reg_sz(hkey, name, buffer, sizeof(buffer)) == ERROR_SUCCESS) { + + result = strdup(buffer); + (void) RegCloseKey(hkey); + } + + (void) RegCloseKey(hkey); + } + return non_empty(result) ? result : 0; +} +#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 < (int) 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(int 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 = DeConst(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; + size_t need = (unsigned) (list->hl_len - 1); + size_t want; + + list->hl_len = (short) (list->hl_len + 1); + want = (size_t) list->hl_len; + + 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, + int TargetEmphasisON, + int utf_flag) +{ + const char *mydata = 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)), + (int) (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 + */ + mydata = theData; + while ((Offset < hoffset) && + ((Offset + tLen) <= hoffset)) { + data = (mydata + tlen); + offset = (Offset + tLen); + if (((cp = LYno_attr_mb_strstr(data, + target, + utf_flag, YES, + &HitOffset, + &LenNeeded)) != NULL) + && (offset + LenNeeded) < LYcols) { + mydata = 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 >= 0 && + (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], utf_extra); + itmp += (int) utf_extra; + /* + * Start emphasis immediately if we are making the link + * non-current. -FM + */ + if (flag != TRUE) { + 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 != TRUE) { + 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 != TRUE) { + 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], 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 == TRUE && data[(itmp + 1)] == '\0') { + LYstopTargetEmphasis(); + TargetEmphasisON = FALSE; + LYGetYX(y, offset); + (void) y; + 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 == TRUE && 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 == TRUE && 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 == TRUE ? (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, + mydata, + 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 == TRUE ? (hLen - 1) : hLen))) { + /* + * Set up the data and offset for the hit, and let the code + * for within hightext hits handle it. -FM + */ + mydata = 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 == TRUE ? (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], 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 != TRUE || + (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 != TRUE || + (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 != TRUE || + (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], 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 == TRUE && 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 == TRUE && 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 == TRUE && 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 == TRUE ? (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, + mydata, + 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 == TRUE ? (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, mydata, 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], 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 == TRUE && 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 == TRUE && 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 == TRUE && 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 { + mydata = cp; + Offset = (offset + HitOffset); + data = buffer; + offset = hoffset; + goto highlight_hit_within_hightext; + } + } + } + } + } + highlight_search_done: + FREE(theData); + return (BOOLEAN) 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 != TRUE) { + 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; + + 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 == FALSE) + textinput_redrawn = FALSE; +#endif + + if (nlinks > 0) { +#ifdef USE_COLOR_STYLE + if (flag == TRUE || 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 == TRUE, 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 = (int) (LYmbcs_skip_cells(text, avail_space, utf_flag) - text); + LYwaddnstr(LYwin, text, (size_t) len); + while (len++ < avail_space) + LYaddch('_'); + +#ifdef USE_COLOR_STYLE + } else if (flag == FALSE) { + 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)), + (int) (sizeof(buffer) - 1), + (LYcolLimit - LXP), + utf_flag); + LYaddstr(buffer); + } + + /* + * Display a second line as well. + */ + 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); + if (hi_offset < 0) + continue; + lynx_stop_link_color(flag == TRUE, 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 == TRUE ? s_alink : s_a)); + LynxChangeStyle(flag == TRUE ? s_alink : s_a, ABS_ON); +#else + lynx_start_link_color(flag == TRUE, 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 == TRUE, 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, + int 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"); + + 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 = (int) strlen(text_buff); + if (len >= (int) (sizeof(buffer) - 1)) + len = (int) (sizeof(buffer) - 1); + LYStrNCpy(buffer, text_buff, len); + /* 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].used) + ? 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); + (void) 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); + + LYParkCursor(); +#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 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) { + const 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; +} + +/* + * 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(int 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; +} + +static int fake_zap = 0; + +void LYFakeZap(int 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 time_t last; + time_t 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); + + if (LYNoZapKey) + return (TRUE); + /* + * 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_sec * 10); + 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 CompareType(tst,cmp,len) compare_type((tst),(cmp),(size_t)(len)) + +#define DoubleHtmlSep(s) (LYIsHtmlSep((s)[0]) && LYIsHtmlSep((s)[1])) +#define compare_two(tst,cmp,len,limit) \ + ((len + 2) <= limit \ + && DoubleHtmlSep(tst + len) \ + && CompareType(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 address. + */ + 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 (CompareType(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 (CompareType(cp, STR_LYNXPROG, LEN_LYNXPROG)) { + /* + * Special External Lynx type to handle execution of commands, + * scripts or programs which do not require a pause to read + * screen upon completion. + */ + result = LYNXPROG_URL_TYPE; + + } else if (CompareType(cp, STR_LYNXCGI, LEN_LYNXCGI)) { + result = LYNXCGI_URL_TYPE; + + } else if (CompareType(cp, STR_LYNXPRINT, LEN_LYNXPRINT)) { + result = LYNXPRINT_URL_TYPE; + + } else if (CompareType(cp, STR_LYNXOPTIONS, LEN_LYNXOPTIONS)) { + result = LYNXOPTIONS_URL_TYPE; + + } else if (CompareType(cp, STR_LYNXCFG, LEN_LYNXCFG)) { + result = LYNXCFG_URL_TYPE; + + } else if (CompareType(cp, STR_LYNXMESSAGES, LEN_LYNXMESSAGES)) { + result = LYNXMESSAGES_URL_TYPE; + + } else if (CompareType(cp, STR_LYNXCFLAGS, LEN_LYNXCFLAGS)) { + result = LYNXCOMPILE_OPTS_URL_TYPE; + + } else if (CompareType(cp, STR_LYNXDOWNLOAD, LEN_LYNXDOWNLOAD)) { + result = LYNXDOWNLOAD_URL_TYPE; + + } else if (CompareType(cp, STR_LYNXDIRED, LEN_LYNXDIRED)) { + result = LYNXDIRED_URL_TYPE; + + } else if (CompareType(cp, STR_LYNXEDITMAP, LEN_LYNXEDITMAP)) { + result = LYNXEDITMAP_URL_TYPE; + + } else if (CompareType(cp, STR_LYNXHIST, LEN_LYNXHIST)) { + result = LYNXHIST_URL_TYPE; + +#ifdef USE_CACHEJAR + } else if (CompareType(cp, STR_LYNXCACHE, LEN_LYNXCACHE)) { + result = LYNXCACHE_URL_TYPE; +#endif + } else if (CompareType(cp, STR_LYNXKEYMAP, LEN_LYNXKEYMAP)) { + result = LYNXKEYMAP_URL_TYPE; + + } else if (CompareType(cp, STR_LYNXIMGMAP, LEN_LYNXIMGMAP)) { + /* force lower/uppercase of next part */ + (void) is_url(&cp[LEN_LYNXIMGMAP]); + result = LYNXIMGMAP_URL_TYPE; + + } else if (CompareType(cp, STR_LYNXCOOKIE, LEN_LYNXCOOKIE)) { + result = LYNXCOOKIE_URL_TYPE; + + } + break; +#ifndef DISABLE_NEWS + /* + * NEWSfoo: schemes - + */ + case 'N': + case 'n': + if (CompareType(cp, STR_NEWS_URL, LEN_NEWS_URL)) { + result = NEWS_URL_TYPE; + + } else if (CompareType(cp, STR_NNTP_URL, LEN_NNTP_URL)) { + result = NNTP_URL_TYPE; + + } else if (CompareType(cp, "newspost:", 9)) { + /* + * Special Lynx type to handle news posts. + */ + result = NEWSPOST_URL_TYPE; + + } else if (CompareType(cp, "newsreply:", 10)) { + /* + * Special Lynx type to handle news replies (followups). + */ + result = NEWSREPLY_URL_TYPE; + } + break; + + /* + * SNEWSfoo: schemes - + */ + case 'S': + case 's': + if (CompareType(cp, STR_SNEWS_URL, LEN_SNEWS_URL)) { + result = SNEWS_URL_TYPE; + + } else if (CompareType(cp, "snewspost:", 10)) { + /* + * Special Lynx type to handle snews posts. + */ + result = NEWSPOST_URL_TYPE; + + } else if (CompareType(cp, "snewsreply:", 11)) { + /* + * Special Lynx type to handle snews replies (followups). + */ + result = NEWSREPLY_URL_TYPE; + } + break; +#endif + case 'M': + case 'm': + if (CompareType(cp, STR_MAILTO_URL, LEN_MAILTO_URL)) { + result = MAILTO_URL_TYPE; + } + break; + + case 'F': + case 'f': + if (CompareType(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 (CompareType(cp, STR_BIBP_URL, LEN_BIBP_URL)) { + result = BIBP_URL_TYPE; + } +#endif + break; + + case 'D': + case 'd': + if (CompareType(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 (CompareType(cp, STR_HTTP_URL, LEN_HTTP_URL)) { + result = HTTP_URL_TYPE; + + } else if (CompareType(cp, STR_HTTPS_URL, LEN_HTTPS_URL)) { + result = HTTPS_URL_TYPE; + } + break; + +#ifndef DISABLE_GOPHER + case 'G': + case 'g': + if (CompareType(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 (CompareType(cp, STR_WAIS_URL, LEN_WAIS_URL)) { + result = WAIS_URL_TYPE; + } + break; + + case 'T': + case 't': + if (CompareType(cp, STR_TELNET_URL, LEN_TELNET_URL)) { + result = TELNET_URL_TYPE; + + } else if (CompareType(cp, STR_TN3270_URL, LEN_TN3270_URL)) { + result = TN3270_URL_TYPE; + } + break; + + case 'R': + case 'r': + if (CompareType(cp, STR_RLOGIN_URL, LEN_RLOGIN_URL)) { + result = RLOGIN_URL_TYPE; + } + break; + + case 'C': + case 'c': + if (CompareType(cp, STR_CSO_URL, LEN_CSO_URL)) { + result = CSO_URL_TYPE; + } + break; + + case 'A': + case 'a': + if (CompareType(cp, "afs:", 4)) { + result = AFS_URL_TYPE; + } + break; + + case 'P': + case 'p': + if (CompareType(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) +{ + int result = FALSE; + + if (fp != 0) { + int err = ferror(fp); + LY_TEMP *p = FindTempfileByFP(fp); + + fclose(fp); + if (p != 0) { + p->file = 0; + } + if (!err) { + result = TRUE; + } + } + return (BOOLEAN) result; +} + +/* + * Close an output file, reporting any problems with writing to it. + */ +BOOLEAN LYCloseOutput(FILE *fp) +{ + int result = FALSE; + + if (fp != 0) { + int err = ferror(fp); + LY_TEMP *p = FindTempfileByFP(fp); + + fclose(fp); + if (p != 0) { + p->file = 0; + } + if (!err) { + result = TRUE; + } + } + if (!result) { + HTAlert(CANNOT_WRITE_TO_FILE); + } + return (BOOLEAN) result; +} + +/* + * Test if we'll be able to write a file. If not, warn the user. + */ +BOOLEAN LYCanWriteFile(const char *filename) +{ + BOOLEAN result = FALSE; + + if (LYCloseOutput(fopen(filename, "w"))) { + if (remove(filename) == 0) { + result = TRUE; + } + } else { + _statusline(NEW_FILENAME_PROMPT); + } + return result; +} + +/* + * Test if we'll be able to read a file. + */ +BOOLEAN LYCanReadFile(const char *filename) +{ + FILE *fp; + BOOLEAN result = FALSE; + + if (non_empty(filename)) { + if ((fp = fopen(filename, "r")) != 0) { + result = LYCloseInput(fp); + } + } + return result; +} + +char *LYFindConfigFile(const char *nominal, const char *dftfile) +{ + char *result = 0; + char *path = 0; + char *head = 0; + char *leaf; + char *item; + + if (non_empty(nominal)) { + StrAllocCopy(result, nominal); + + /* + * Look for it in as-is - first expanding any tilde. + */ + LYTildeExpand(&result, TRUE); + if (!LYCanReadFile(result)) { + const char *cfg_path; + char *list = 0; + BOOLEAN found = FALSE; + + /* + * Now try in the config-path. + */ + if ((cfg_path = LYGetEnv("LYNX_CFG_PATH")) == NULL) + cfg_path = LYNX_CFG_PATH; + + StrAllocCopy(list, cfg_path); + path = list; + while ((item = LYstrsep(&path, PATH_SEPARATOR)) != 0) { + if (isEmpty(item)) + continue; + FREE(result); + HTSprintf0(&result, "%s%s%s", item, FILE_SEPARATOR, nominal); + LYTildeExpand(&result, TRUE); + if (LYCanReadFile(result)) { + found = TRUE; + break; + } + } + FREE(list); + + if (!found) { + /* + * If not found, try finding it in the same directory as the + * compiled-in location of the default file. + */ + StrAllocCopy(head, dftfile); + if (strcmp(nominal, dftfile) && + (leaf = LYPathLeaf(head)) != head) { + + head[leaf - head] = '\0'; + StrAllocCopy(result, head); + StrAllocCat(result, nominal); + + if (!LYCanReadFile(result)) { + FREE(result); + } + } +#ifdef USE_PROGRAM_DIR + else { + /* + * Finally, try in the same directory as the executable. + */ + StrAllocCopy(result, program_dir); + LYAddPathSep(&result); + StrAllocCat(result, nominal); + LYTildeExpand(&result, TRUE); + if (!LYCanReadFile(result)) { + FREE(result); + } + } +#endif + } + } + + } + FREE(head); + return result; +} + +/* + * 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 = cp; + if (!strncmp(mytty, "/dev/", 5)) { + mytty += 5; /* pty's can be like "pts/0" in utmp */ + } else { + if ((mytty = LYLastPathSep(cp)) != 0) + ++mytty; + } + } + + result = FALSE; + if (mytty && (fp = fopen(UTMP_FILE, "r")) != NULL) { + size_t ulen = strlen(mytty); + + if (ulen > sizeof(me.ut_line)) + ulen = sizeof(me.ut_line); + do { + n = (int) fread((char *) &me, sizeof(struct utmp), (size_t) 1, fp); + + if (n <= 0) + break; + } while (memcmp(me.ut_line, mytty, ulen)); + (void) LYCloseInput(fp); + + if (n > 0) { + for (ulen = 0; ulen < sizeof(me.ut_host); ++ulen) { + if (me.ut_host[ulen] == '\0') + break; + } + if (ulen > strlen(LYLocalDomain) && + !memcmp(LYLocalDomain, + me.ut_host + ulen - strlen(LYLocalDomain), + strlen(LYLocalDomain))) { + result = TRUE; + } +#ifdef LINUX + /* Linux fix to check for local user. J.Cullen 11Jul94 */ + else if (ulen == 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; + 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 LYGetScreenSize(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; + if (sig == 0) + /* If called from start_curses(), no need to record size-changed */ + return; +#else /* Curses: */ +#ifdef HAVE_SIZECHANGE +#if defined(TIOCGSIZE) + struct ttysize win; + + if (ioctl(0, TIOCGSIZE, &win) == 0) { + if (win.ts_lines != 0) { + LYlines = win.ts_lines; + } + if (win.ts_cols != 0) { + LYcols = win.ts_cols; + } + } +#elif defined(TIOCGWINSZ) + struct winsize win; + + if (ioctl(0, (long) TIOCGWINSZ, &win) == 0) { + if (win.ws_row != 0) { + LYlines = win.ws_row; + } + if (win.ws_col != 0) { + LYcols = win.ws_col; + } + } +#elif !defined(PDCURSES) +#error inconsistent settings for TIOCGSIZE/TIOCGWINSZ +#endif /* TIOCGSIZE/TIOCGWINSZ */ +#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 + } +} + +/* + * curses/slang functions call this to start catching SIGWINCH when sig==0, + * and continue catching when sig!=0. + */ +void size_change(int sig GCC_UNUSED) +{ +#ifdef SIGWINCH + if (sig) + size_is_changed = TRUE; + LYExtSignal(SIGWINCH, size_change); +#endif /* SIGWINCH */ +} + +/* + * 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))) { + if ((cp = strrchr(fname, '.')) != 0) { + 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 *) NULL) + (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) { + long get_rand = (long) lynx_rand(); + long max_rand = (long) LYNX_RAND_MAX; + + counter = (unsigned) (((float) MAX_TEMPNAME * (float) get_rand) / + (float) max_rand + 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_KEY; + break; + case '3': + number = PGDOWN_KEY; + break; + case '4': + number = LTARROW_KEY; + break; + case '5': + number = DO_NOTHING; + break; + case '6': + number = RTARROW_KEY; + break; + case '7': + number = HOME_KEY; + break; + case '8': + number = UPARROW_KEY; + break; + case '9': + number = PGUP_KEY; + 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", (int) (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", (int) (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, (int) (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); +} + +static const char *default_scheme = "http://"; + +static const char *guess_scheme(const char *url) +{ + const char *scheme = NULL; + + if (LYGuessScheme && non_empty(url)) { + if (0 == strncasecomp(url, "www.", 4)) { + scheme = "http://"; + } else if (0 == strncasecomp(url, "ftp.", 4)) { + scheme = "ftp://"; + } else if (0 == strncasecomp(url, "gopher.", 7)) { + scheme = "gopher://"; + } else if (0 == strncasecomp(url, "wais.", 5)) { + scheme = "wais://"; + } else if (0 == strncasecomp(url, "cso.", 4) || + 0 == strncasecomp(url, "ns.", 3) || + 0 == strncasecomp(url, "ph.", 3)) { + scheme = "cso://"; + } else if (0 == strncasecomp(url, "finger.", 7)) { + scheme = "finger://"; + } else if (0 == strncasecomp(url, "news.", 5)) { + scheme = "news://"; + } else if (0 == strncasecomp(url, "nntp.", 5)) { + scheme = "nntp://"; + } + } + CTRACE((tfp, "guess_scheme(%s) -> '%s'\n", NonNull(url), NonNull(scheme))); + return scheme; +} + +/* + * 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. + */ +static void LYAddSchemeForURL(char **AllocatedString) +{ + if (non_empty(*AllocatedString)) { + char *Str = NULL; + const char *scheme = guess_scheme(*AllocatedString); + + if (scheme == NULL) + scheme = default_scheme; + + StrAllocCopy(Str, scheme); + StrAllocCat(Str, *AllocatedString); + StrAllocCopy(*AllocatedString, Str); + + FREE(Str); + } +} + +/* + * 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). + */ +static 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); + + /* + * If it's a NULL or zero-length string, or if it begins with a slash or + * hash, don't continue pointlessly. - FM + */ + if (isEmpty(*AllocatedString) || + *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); + strip_userid(host, FALSE); + 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 + if (HTCheckAddrInfo(host, 80)) +#else + if (LYCheckHostByName(host)) +#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 (guess_scheme(*AllocatedString) != NULL) { + 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 = LYCheckHostByName(host); + 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; +} +/* + * 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 { + LYAddSchemeForURL(&old_string); + 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 { + LYAddSchemeForURL(&old_string); + 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)) { + char *his_home = NULL; + + StrAllocCopy(his_home, old_string); + LYTildeExpand(&his_home, FALSE); + StrAllocCat(*AllocatedString, his_home); + FREE(his_home); + + 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)) { + LYAddSchemeForURL(&old_string); + StrAllocCopy(*AllocatedString, old_string); + } else if (fixit) { + 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; +} + +#if (!defined(__MINGW32__) && !defined(sleep)) || (defined(__MINGW32__) && !defined(HAVE_SLEEP)) +void sleep(unsigned sec) +{ + unsigned int i, j; + + for (j = 0; j < sec; j++) { + for (i = 0; i < 10; i++) { + Sleep(100); + if (kbhit()) { + (void) LYgetch(); + return; + } + } + } +} +#endif /* !__MINGW32__ */ +#endif /* _WINDOWS */ +/* + * 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, TRUE); + 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, <able); + return; + } + + lvalue.dsc$w_length = strlen(LogicalValue); + lvalue.dsc$a_pointer = LogicalValue; + lib$set_logical(&lname, &lvalue, <able, 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, (size_t) 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; + } + CTRACE((tfp, "CheckDir(%s) %s\n", NonNull(path), path ? "OK" : "ERR")); + 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) && defined(_WIN32) + if (result == 0) { + char *head; + char *leaf; + static char *temp = NULL; + + result = w32_get_shell_folder("Personal"); + if (result == 0) { + /* Windows Vista/7 */ + if ((head = LYGetEnv("USERPROFILE")) != 0) { + HTSprintf0(&temp, "%s%sDocuments", head, FILE_SEPARATOR); + result = CheckDir(temp); + if (result == 0) { + /* Windows 2000 */ + HTSprintf0(&temp, "%s%sMy Documents", head, FILE_SEPARATOR); + result = CheckDir(temp); + } + } + } + /* NT4 */ + if (result == 0) { + if ((head = LYGetEnv("HOMEDRIVE")) != 0) { + if ((leaf = LYGetEnv("HOMEPATH")) != 0) { + HTSprintf0(&temp, "%s%s%s", head, FILE_SEPARATOR, leaf); + result = CheckDir(temp); + } + } + } + /* General M$ */ +#ifdef USE_PROGRAM_DIR + if (result == 0) + result = CheckDir(program_dir); +#endif + 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, FILE_SEPARATOR); + result = CheckDir(temp); + } + } + if (result == 0) + result = CheckDir("C:" FILE_SEPARATOR); + } +#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 = (int) 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, FALSE); + + /* + * 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, int 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 (*fname && !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, + int 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())); +#ifndef NOUSERS + } else { + char *save; + char saved = '\0'; + struct passwd *pw; + + for (save = temp; *save != '\0'; ++save) { + if (LYIsPathSep(*save)) { + saved = *save; + *save = '\0'; + break; + } + } + pw = getpwnam(temp + 1); + *save = saved; + if (pw != 0 && non_empty(pw->pw_dir)) { + temp = NULL; + StrAllocCopy(temp, save); + StrAllocCopy(*pathname, pw->pw_dir); + StrAllocCat(*pathname, temp); + FREE(temp); + } +#endif + } + 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, (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 (non_empty(lynx_save_space)) { + 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; + size_t size = LY_MAXPATH; + size_t used; + char *buffer = typeMallocn(char, (unsigned) size); + char *check; + + if (buffer != 0) { + while ((used = (size_t) readlink(name, buffer, (size - 1))) == size - 1) { + check = typeRealloc(char, buffer, (unsigned) (size *= 2)); + + if (check == 0) + break; + buffer = check; + } + if (buffer != 0) { + if ((int) 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, FILE_SEPARATOR, 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 which is not accessed by a symbolic 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_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. + * + * Likewise, the t-bit would be nice to rely upon. However + * it is marked as an extension in POSIX, rather than + * required. + */ + 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)) { + if (remove(name) == 0) { + /* 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); + (void) 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"); + + (void) 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"); + (void) 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"); + (void) 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_t) (((mode & 0700) | 0066) & ~save); + (void) umask(save); + (void) chmod(name, mode); + } +} +#endif + +/* + * Check if the given anchor has an associated file-cache. + */ +BOOLEAN LYCachedTemp(char *target, + char **cached) +{ + BOOLEAN result = FALSE; + + if (*cached) { + LYStrNCpy(target, *cached, LY_MAXPATH); + FREE(*cached); + if (LYCanReadFile(target)) { + if (remove(target) != 0) { + CTRACE((tfp, "cannot remove %s\n", target)); + } + } + result = TRUE; + } + return result; +} + +#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, + (unsigned) (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); + } + (void) 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, (off_t) 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) { + (void) 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 USE_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; + BOOL result = NO; + + if (url) { + for (i = 0; i < TABLESIZE(ly_uip); i++) { + if (ly_uip[i].type == type) { + if (!ly_uip[i].url) { + break; + } 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)) { + result = 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)) { + result = YES; + break; + } + } + } + break; + } + } + } + return result; +} + +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. + */ +BOOLEAN LYValidateFilename(bstring **result, + bstring **given) +{ + BOOLEAN code = TRUE; + char *cp = NULL; + + /* + * Cancel if the user entered "/dev/null" on Unix, or an "nl:" path on VMS. + * - FM + */ + if (LYIsNullDevice((*given)->str)) { + /* just ignore it */ + code = FALSE; +#ifdef HAVE_POPEN + } else if (LYIsPipeCommand((*given)->str)) { + if (no_shell) { + HTUserMsg(SPAWNING_DISABLED); + code = FALSE; + } else { + BStrCopy(*result, (*given)); + } +#endif + } else { + if (FindLeadingTilde((*given)->str, TRUE) != 0) { + char *cp1 = NULL; + + StrAllocCopy(cp1, (*given)->str); + LYTildeExpand(&cp1, TRUE); + BStrCopy0(*result, cp1); + BStrCopy0(*given, cp1); + FREE(cp1); + } +#ifdef VMS + if (StrChr((*given)->str, '/') != NULL) { + BStrCopy0(*result, HTVMS_name("", (*given)->str)); + BStrCopy(*given, *result); + } + if ((*given)->str[0] != '/' + && StrChr((*given)->str, ':') == NULL) { + BStrCopy0(*result, "sys$disk:"); + if (StrChr((*given)->str, ']') == NULL) + BStrCat0(*result, "[]"); + BStrCat(*result, (*given)); + } else { + BStrCopy(*result, (*given)); + } +#else + +#ifndef __EMX__ + if (!LYisAbsPath((*given)->str)) { +#if defined(__DJGPP__) || defined(_WINDOWS) + if (StrChr((*result)->str, ':') != 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; + + if (cp) { + LYTrimPathSep(cp); + BStrCopy0(*result, cp); + BStrCat0(*result, "/"); + } else { + BStrCopy0(*result, ""); + } + if (code) { + cp = HTSYS_name((*given)->str); + BStrCat0(*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; + + CTRACE((tfp, "LYValidateOutput '%s'\n", filename)); + + /* + * 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'; + } + } else if (!LYCanWriteFile(filename)) { + 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 (leaf && !LYIsHtmlSep(*leaf)) + LYAddHtmlSep(target); + StrAllocCat(*target, leaf); +} + +#define PUT_STRING(buf) (*(target)->isa->put_string)(target, buf) + +/* + * Like WriteInternalTitle, used for writing title on pages constructed via + * streams. + */ +void WriteStreamTitle(HTStream *target, const char *Title) +{ + char *buf = 0; + + PUT_STRING(LYNX_DOCTYPE); + PUT_STRING("<html>\n<head>\n"); + LYAddMETAcharsetToStream(target, -1); + HTSprintf0(&buf, "<title>%s</title>\n</head>\n<body>\n", Title); + PUT_STRING(buf); + FREE(buf); +} + +/* + * 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 { + (void) 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, LYNX_DOCTYPE); + + 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, FILE_SEPARATOR); + } +} + +/* + * 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, FILE_SEPARATOR); + } +} + +/* + * 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, "/"); + } +} + +/* + * Rename a file + */ +int LYRenameFile(char *src, + char *dst) +{ +#ifdef _WINDOWS + /* + * If dest_file exists prior to calling rename(), rename() will fail on Windows platforms. + * https://www.securecoding.cert.org/confluence/display/c/FIO10-C.+Take+care+when+using+the+rename%28%29+function + */ + struct stat st; + + if (stat(dst, &st) == 0) { + unlink(dst); + } +#endif + return rename(src, dst); +} + +/* + * 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]; + size_t len; + + code = EOF; + if ((fin = fopen(src, BIN_R)) != 0) { + if ((fout = fopen(dst, BIN_W)) != 0) { + code = 0; + while ((len = fread(buff, (size_t) 1, sizeof(buff), fin)) != 0) { + if (fwrite(buff, (size_t) 1, len, fout) < len + || 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, (long) 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; + + display_putenv_command = NULL; /* yes, this is a leak - cannot fix */ + 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(); +} + +#elif 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 = (int) 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; +} + +#elif 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 = typeMallocn(char, PASTE_BUFFER); + + while (1) { + len = (int) fread(paste_buf + off, + (size_t) 1, + (size_t) 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; + size_t l = strlen(s), res; + + if (!cmd) + return -1; + + fh = popen(cmd, TXT_W); + if (!fh) + return -1; + res = fwrite(s, (size_t) 1, l, fh); + if (pclose(fh) != 0 || res != l) + return -1; + return 0; +} + +#endif /* __EMX__ ... HAVE_POPEN */ +#endif /* CAN_CUT_AND_PASTE */ + +/* + * 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) + +#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 */ +#if defined(__CYGWIN__) || defined(__MINGW32__) + 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((char *) 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 */ + +#if defined(WIN_EX) || defined(__CYGWIN__) /* 2000/03/07 (Tue) 17:17:46 */ + +#define IS_SEP(p) ((p == '\\') || (p == '/') || (p == ':')) + +static char *black_list[] = +{ + "con", + "prn", + "clock$", + "config$", + NULL +}; + +static int is_device(char *fname) +{ + HANDLE fileHandle; + DWORD val; + int i; + + i = 0; + while (black_list[i] != NULL) { + if (strcasecomp(fname, black_list[i]) == 0) { + return 1; /* device file */ + } + i++; + } + + fileHandle = CreateFile(fname, 0, 0, 0, OPEN_EXISTING, 0, 0); + + if (fileHandle == INVALID_HANDLE_VALUE) { + return 0; /* normal file */ + } else { + val = GetFileType(fileHandle); + switch (val) { + case 1: + val = 0; + break; + case 2: + val = 1; /* device file */ + break; + default: + val = 0; + break; + } + + CloseHandle(fileHandle); + } + return val; +} + +static char *device_list[] = +{ + "con", + "nul", + "aux", + "prn", + NULL +}; + +int unsafe_filename(const char *fname) +{ + int i, len, sum; + char *cp; + char *save; + + i = 0; + while (device_list[i] != NULL) { + if (strcasecomp(fname, device_list[i]) == 0) { + return 0; /* device file (open OK) */ + } + i++; + } + + save = cp = strdup(fname); + + while (*cp) { + if (IS_SJIS_HI1(UCH(*cp)) || IS_SJIS_HI2(UCH(*cp))) + cp += 2; /* KANJI skip */ + if (IS_SEP(*cp)) { + *cp = '\0'; + } + cp++; + } + + sum = 0; + cp = save; + len = (int) strlen(fname); + while (cp < (save + len)) { + if (*cp == '\0') { + cp++; + } else { + char *q; + + q = StrChr(cp, '.'); + if (q) + *q = '\0'; + if (is_device(cp)) { + sum++; + break; + } + if (q) + cp = q + 1; + while (*cp) + cp++; + } + } + free(save); + + return (sum != 0); +} + +FILE *safe_fopen(const char *fname, const char *mode) +{ + if (unsafe_filename(fname)) { + return (FILE *) NULL; + } else { + return fopen(fname, mode); + } +} + +#endif /* defined(WIN_EX) || defined(__CYGWIN__) */ diff --git a/src/LYUtils.h b/src/LYUtils.h new file mode 100644 index 0000000..7ad76db --- /dev/null +++ b/src/LYUtils.h @@ -0,0 +1,578 @@ +/* $LynxId: LYUtils.h,v 1.102 2023/10/23 08:04:01 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 */ + +#ifndef HTSTREAM_H +#include <HTStream.h> +#endif /* HTSTREAM_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 PATH_SEPARATOR ";" +#define FILE_SEPARATOR "\\" +#define LYIsPathSep(ch) ((ch) == '/' || (ch) == '\\') +#define LYIsDosDrive(s) (isalpha(UCH((s)[0])) && (s)[1] == ':') +#else +#define PATH_SEPARATOR ":" +#define FILE_SEPARATOR "/" +#define LYIsPathSep(ch) ((ch) == '/') +#define LYIsDosDrive(s) FALSE /* really nothing */ +#endif + +#ifdef USE_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 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 LYFixCursesOnForAccess(const char *addr, const char *physical); + extern BOOLEAN LYPathOffHomeOK(char *fbuffer, size_t fbuffer_size); + extern BOOLEAN LYValidateFilename(bstring **result, bstring **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 *LYFindConfigFile(const char *nominal, const char *dftfile); + extern char *LYGetEnv(const char *name); + extern char *LYLastPathSep(const char *path); + extern char *LYPathLeaf(char *pathname); + extern char *LYTildeExpand(char **pathname, int 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(int need_selectable); + extern int LYRenameFile(char *src, char *dst); + 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(int utf_flag, const char *data); + extern time_t LYmktime(char *string, int 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(int set); + extern void LYFixCursesOn(const char *reason); + extern void LYFreeHilites(int first, int last); + extern void LYFreeStringList(HTList *list); + extern void LYGetScreenSize(int sig); + 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 WriteStreamTitle(HTStream *target, const char *Title); + extern void change_sug_filename(char *fname); + extern void convert_to_spaces(char *string, int 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 + +#if defined(WIN_EX) || defined(__CYGWIN__) /* 2000/03/07 (Tue) 17:17:46 */ + extern int unsafe_filename(const char *fname); + extern FILE *safe_fopen(const char *fname, const char *mode); +#endif + +#ifdef VMS + extern void Define_VMSLogical(char *LogicalName, char *LogicalValue); +#endif /* VMS */ + +#if !defined(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); + +#if (defined(__MINGW32__) && !defined(HAVE_SLEEP)) +#undef sleep + void sleep(unsigned sec); +#endif +#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, + LYNXEDITMAP_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 */ +#ifdef USE_CACHEJAR +#define STR_LYNXCACHE "LYNXCACHE:" +#define LEN_LYNXCACHE 10 +#define isLYNXCACHE(addr) !strncasecomp(addr, STR_LYNXCACHE, LEN_LYNXCACHE) +#else +#define isLYNXCACHE(addr) FALSE +#endif + +#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_LYNXEDITMAP "LYNXEDITMAP:" +#define LEN_LYNXEDITMAP 11 +#define isLYNXEDITMAP(addr) !strncasecomp(addr, STR_LYNXEDITMAP, LEN_LYNXEDITMAP) + +#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 */ + +#undef STREQ /* conflict with wais.h */ + +/* + * Miscellaneous. + */ +#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 0000000..6fde3c3 --- /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 0000000..30c9822 --- /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 0000000..8b94048 --- /dev/null +++ b/src/LYexit.c @@ -0,0 +1,185 @@ +/* + * $LynxId: LYexit.c,v 1.38 2020/02/25 10:14:48 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; + +/* + * Capture "atexit()" calls for our own accounting. + */ +int LYatexit(void (*function) (void)) +{ + int result = 0; + + if (topOfStack >= ATEXITSIZE) { + CTRACE((tfp, "(LY)atexit: Too many functions, ignoring one!\n")); + result = -1; + } else { + int n; + BOOLEAN found = FALSE; + + for (n = 0; n < topOfStack; ++n) { + if (callstack[n] == function) { + found = TRUE; + break; + } + } + if (!found) { + callstack[topOfStack++] = function; + } + } + return result; +} + +/* + * 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_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_CURSES_EXIT) + curses_exit(status); +#elif 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 0000000..5cc1dc4 --- /dev/null +++ b/src/LYmktime.c @@ -0,0 +1,364 @@ +/* $LynxId: LYmktime.c,v 1.20 2019/08/28 22:54:45 tom Exp $ */ + +#include <LYStrings.h> +#include <LYUtils.h> + +#include <parsdate.h> + +#ifdef TEST_DRIVER + +int ascii_toupper(int i) +{ + if (123 > i && i > 96) + return (i - 32); + else + return i; +} + +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" is 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, + int absolute) +{ +#if USE_PARSDATE + time_t result = 0; + + if (non_empty(string)) { + CTRACE((tfp, "LYmktime: Parsing '%s'\n", string)); + if ((result = parsedate(string, 0)) == ((time_t) -1)) + result = 0; + + if (!absolute) { + time_t now = time((time_t *) NULL); + + if (result < now) + 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 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, (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) { + /* + * 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, (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, (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, (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"); + test_mktime("Sun Jul 06 07:00:00 2018 GMT"); + test_mktime("Sun Jul 06 07:00:00 2028 GMT"); + test_mktime("Tue Jan 01 07:00:00 2036 GMT"); + test_mktime("Thu Jan 01 07:00:00 2037 GMT"); + /* problems with 32-bits */ + test_mktime("Fri Jan 01 07:00:00 2038 GMT"); + test_mktime("Sun Jul 06 07:00:00 2038 GMT"); + test_mktime("Mon, 22-Aug-2039 15:13:56 GMT"); + test_mktime("Sat, 28 Aug 2066 18:41:53 -0400"); + test_mktime("Fri, 28 Aug 2099 18:41:53 -0400"); + test_mktime("Sat, 28 Aug 2100 18:41:53 -0400"); + test_mktime("Sun Jul 06 07:00:00 2138 GMT"); + test_mktime("Sat, 28 Aug 2150 18:41:53 -0400"); + test_mktime("Sat, 28 Aug 2200 18:41:53 -0400"); + printf("DONE!\n"); + return 0; +} +#endif diff --git a/src/LYrcFile.c b/src/LYrcFile.c new file mode 100644 index 0000000..b8cef37 --- /dev/null +++ b/src/LYrcFile.c @@ -0,0 +1,1140 @@ +/* $LynxId: LYrcFile.c,v 1.107 2022/04/02 00:12:18 Paul.G.Fox 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_HTTP_protocol[] = { + { "1.0", HTTP_1_0 }, + { "1.1", HTTP_1_1 }, + { 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 }, +}; + +#ifdef USE_IDN2 +static Config_Enum tbl_idna_mode[] = { + { "IDNA2003", LYidna2003 }, + { "IDNA2008", LYidna2008 }, + { "TR46", LYidnaTR46 }, + { "Compatible", LYidnaCompat }, + { NULL, -1 }, +}; +#endif + +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 } +}; + +Config_Enum tbl_preferred_content[] = { + { STR_BINARY, contentBINARY }, + { STR_PLAINTEXT, contentTEXT }, + { STR_HTML, contentHTML }, + { 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 +#if defined(USE_BROTLI) || defined(BROTLI_PATH) + { "br", encodingBROTLI }, +#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 }, + { "KB2,ETA", rateEtaKB2 }, + { "BYTES2,ETA", rateEtaBYTES2 }, +#endif +#ifdef USE_PROGRESSBAR + { "METER", rateBAR }, + { "FALSE", rateBAR }, +#endif + { NULL, -1 }, +}; + +Config_Enum tbl_user_mode[] = { + { "MINIMAL", MINIMAL_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_cookie_version[] = { + { "RFC-2109", COOKIES_RFC_2109 }, + { "RFC-2965", COOKIES_RFC_2965 }, + { "RFC-6265", COOKIES_RFC_6265 }, + { NULL, -1 } +}; + +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 = (unsigned) 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; + } + } + CTRACE((tfp, "LYgetEnum: no match found for \"%s\"\n", name)); + 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_http_protocol(char *value) +{ + int found = HTprotocolLevel; + + if (LYgetEnum(tbl_HTTP_protocol, value, &found) + && HTprotocolLevel != found) { + HTprotocolLevel = found; + } + return 0; +} + +static void put_http_protocol(FILE *fp, struct config_type *tbl) +{ + fprintf(fp, "%s=%s\n\n", tbl->name, LYputEnum(tbl_HTTP_protocol, HTprotocolLevel)); +} + +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, LYcase_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\ +")), + MAYBE_SET(RC_COLLAPSE_BR_TAGS, LYCollapseBRs, MSG_ENABLE_LYNXRC), + 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), + MAYBE_ENU(RC_COOKIE_VERSION, cookie_version, tbl_cookie_version, + 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 + MAYBE_SET(RC_HTML5_CHARSETS, html5_charsets, MSG_ENABLE_LYNXRC), + MAYBE_FUN(RC_HTTP_PROTOCOL, get_http_protocol, put_http_protocol, + MSG_ENABLE_LYNXRC), +#ifdef USE_IDN2 + MAYBE_ENU(RC_IDNA_MODE, LYidnaMode, tbl_idna_mode, + 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, LYEditorNames, 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_PERSONAL_MAIL_NAME, personal_mail_name, N_("\ +personal_mail_name specifies your personal name, for mail. The\n\ +name is sent for mailed comments. Lynx will prompt for this,\n\ +showing the configured value as a default when sending mail.\n\ +This is not necessarily the same as a name provided as part of the\n\ +personal_mail_address.\n\ +Lynx does not save your changes to that default value as a side-effect\n\ +of sending email. To update the default value, you must use the options\n\ +menu, or modify this file directly.\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_CONTENT_TYPE, LYContentType, tbl_preferred_content, + MSG_ENABLE_LYNXRC), + 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_TRIM_BLANK_LINES, LYtrimBlankLines, 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; +} + +BOOL LYsetRcValue(const char *name, const char *param) +{ + char MBM_line[256]; + char *notes; + int n; + Config_Type *tbl; + ParseUnionPtr q; + BOOL changed = TRUE; + char *value = NULL; + char *orig_value = NULL; + + if (param == NULL) + param = ""; + StrAllocCopy(value, param); + orig_value = value; + 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. + * This includes known keywords where there is no ENABLE_LYNXRC. + */ + if (tbl->name == 0) { + CTRACE((tfp, "LYrcFile: ignored %s=%s\n", name, value)); + FREE(orig_value); + return FALSE; + } + } + + 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", UCH(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; + + default: + changed = FALSE; + break; + } + FREE(orig_value); + + return changed; +} + +/* 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]; + + 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; + + /* 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); + LYsetRcValue(name, value); + } + + 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=", UCH(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 0000000..1285a21 --- /dev/null +++ b/src/LYrcFile.h @@ -0,0 +1,318 @@ +/* + * $LynxId: LYrcFile.h,v 1.59 2021/07/29 22:53:37 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_ALT_BLAT_MAIL "alt_blat_mail" +#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_BLAT_MAIL "blat_mail" +#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_BROTLI_PATH "brotli_path" +#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_CONV_JISX0201KANA "conv_jisx0201kana" +#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_COOKIE_VERSION "cookie_version" +#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_DONT_WRAP_PRE "dont_wrap_pre" +#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_EXTERNAL_MENU "external_menu" +#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_HTML "force_html" +#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_GUESS_SCHEME "guess_scheme" +#define RC_GZIP_PATH "gzip_path" +#define RC_HELPFILE "helpfile" +#define RC_HIDDENLINKS "hiddenlinks" +#define RC_HIDDEN_LINK_MARKER "hidden_link_marker" +#define RC_HISTORICAL_COMMENTS "historical_comments" +#define RC_HTML5_CHARSETS "html5_charsets" +#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_PROTOCOL "http_protocol" +#define RC_HTTP_PROXY "http_proxy" +#define RC_IDNA_MODE "idna_mode" +#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_LISTONLY "listonly" +#define RC_LIST_DECODED "list_decoded" +#define RC_LIST_FORMAT "list_format" +#define RC_LIST_INLINE "list_inline" +#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 "localhost" +#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_MAX_URI_SIZE "max_uri_size" +#define RC_MESSAGESECS "messagesecs" +#define RC_MESSAGE_LANGUAGE "message_language" +#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_PERSONAL_MAIL_NAME "personal_mail_name" +#define RC_POSITIONABLE_EDITOR "positionable_editor" +#define RC_PREFERRED_CHARSET "preferred_charset" +#define RC_PREFERRED_CONTENT_TYPE "preferred_content_type" +#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_REDIRECTION_LIMIT "redirection_limit" +#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_RMDIR_PATH "rmdir_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_SHORT_URL "short_url" +#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_SSL_CLIENT_CERT_FILE "ssl_client_cert_file" +#define RC_SSL_CLIENT_KEY_FILE "ssl_client_key_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_TRACK_INTERNAL_LINKS "track_internal_links" +#define RC_TRIM_BLANK_LINES "trim_blank_lines" +#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_UNIQUE_URLS "unique_urls" +#define RC_UNZIP_PATH "unzip_path" +#define RC_UPDATE_TERM_TITLE "update_term_title" +#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_WAIT_VIEWER_TERMINATION "wait_viewer_termination" +#define RC_WITH_BACKSPACES "with_backspaces" +#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_cookie_version[]; +extern Config_Enum tbl_force_prompt[]; +extern Config_Enum tbl_keypad_mode[]; +extern Config_Enum tbl_multi_bookmarks[]; +extern Config_Enum tbl_preferred_content[]; +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 LYsetRcValue(const char *name, const char *param); +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_http_protocol(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 0000000..4123596 --- /dev/null +++ b/src/TRSTable.c @@ -0,0 +1,2012 @@ +/* + * $LynxId: TRSTable.c,v 1.43 2023/11/10 01:01:54 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 could 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_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(int alignment) +{ + STable_info *me = typecalloc(STable_info); + + CTRACE2(TRACE_TRST, + (tfp, "TRST:Stbl_startTABLE(align=%d)\n", alignment)); + if (me) { + me->alignment = (short) 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) growby=%d\n", + icell, colspan, growby)); + 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, 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 stateLine=%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 (empty) { + ret = (lastcell->len <= 0 ? 0 : lastcell->len); + } else { + ret = (lastcell->len <= 0 ? 0 : -1); + } + 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 (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; + } + newstate = CS__cb; + } 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; + } + 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 (empty) { + ret = (lastcell->len <= 0 ? 0 : lastcell->len); + } else { + ret = (lastcell->len <= 0 ? 0 : -1); + } + 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 = CS__ef; + s->state = newstate; + 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; + } + newstate = CS__cf; + } else { + goto trace_and_return; + } + 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 = 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 = 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 + + 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, line=%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)); + colspan = HTMAX(colspan, 0); + } 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 = (int) (lastrow - me->rows); + + CTRACE2(TRACE_TRST, + (tfp, + "TRST:Stbl_fakeFinishCellInTable(line=%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 */ + 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 = (int) (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(line=%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(line=%d, pos=%d, isheader=%d, cs=%d, rs=%d, al=%d)\n", + lineno, pos, 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; + + 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 - 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(line=%d, pos=%d, off=%d, end_td=%d)\n", + lineno, pos, offset, 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; + + 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, + int alignment, + int isgroup) +{ + STable_cellinfo *sumcols, *sumcol; + int i, icolinfo; + + CTRACE2(TRACE_TRST, + (tfp, "TRST:Stbl_addColInfo(cs=%d, al=%d, isgroup=%d)\n", + colspan, alignment, 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 = (short) 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, int alignment) +{ + CTRACE2(TRACE_TRST, (tfp, "TRST:Stbl_addRowGroup()\n")); + Stbl_cancelRowSpans(me); + me->rowgroup_align = (short) 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 0000000..ffebf4a --- /dev/null +++ b/src/TRSTable.h @@ -0,0 +1,50 @@ +/* $LynxId: TRSTable.h,v 1.16 2010/09/25 11:40:05 tom Exp $ */ +#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(int); + 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, int, int); + extern int Stbl_finishColGroup(STable_info *); + extern int Stbl_addRowGroup(STable_info *, int); + +#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 0000000..2b17591 --- /dev/null +++ b/src/UCAuto.c @@ -0,0 +1,816 @@ +/* + * $LynxId: UCAuto.c,v 1.56 2021/06/09 22:29:43 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_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, (unsigned) 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 ((non_empty(x_display)) || + 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, + (unsigned) status)); + } + FREE(tmpbuf1); + } + } + if (newcs < 0 && p == 0) { + if (old_font) { + (void) LYRemoveTemp(old_font); + FREE(old_font); + } + if (old_umap) { + (void) 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"); + + 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, (unsigned) 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) + (void) LYRemoveTemp(old_font); + 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_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, + gettext("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 && !strncasecomp(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; + } + LYStrNCpy(buf, r, s - r); + n = UCGetLYhndl_byMIME(buf); + if (n < 0) { + sprintf(buf, + gettext("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, gettext("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, + gettext("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, gettext("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, gettext("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, gettext("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 0000000..98d2243 --- /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 0000000..a10f624 --- /dev/null +++ b/src/UCAux.c @@ -0,0 +1,798 @@ +/* + * $LynxId: UCAux.c,v 1.59 2024/01/15 11:24:17 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 USE_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; + + 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 +#ifdef EXP_CHINESEUTF8_SUPPORT + || !strcmp(p_in->MIMEname, "euc-cn") + || !strcmp(p_in->MIMEname, "big5") + || !strcmp(p_in->MIMEname, "euc-kr") +#endif + ) + ); + /* + * 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; +#ifdef EXP_CHINESEUTF8_SUPPORT + if (!strcmp(p_in->MIMEname, "euc-cn") || + !strcmp(p_in->MIMEname, "big5") || + !strcmp(p_in->MIMEname, "euc-kr")) { + pT->trans_to_uni = (BOOL) UCCanUniTranslateFrom(cs_in); + } +#endif + 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)); + } + } + CTRACE((tfp, "UCSetTransParams (done):\n")); + CTRACE((tfp, " transp: %d\n", pT->transp)); + CTRACE((tfp, " do_cjk: %d\n", pT->do_cjk)); + CTRACE((tfp, " decode_utf8: %d\n", pT->decode_utf8)); + CTRACE((tfp, " output_utf8: %d\n", pT->output_utf8)); + CTRACE((tfp, " do_8bitraw: %d\n", pT->do_8bitraw)); + CTRACE((tfp, " use_raw_char_in: %d\n", pT->use_raw_char_in)); + CTRACE((tfp, " strip_raw_char_in: %d\n", pT->strip_raw_char_in)); + CTRACE((tfp, " pass_160_173_raw: %d\n", pT->pass_160_173_raw)); + CTRACE((tfp, " trans_to_uni: %d\n", pT->trans_to_uni)); + CTRACE((tfp, " trans_C0_to_uni: %d\n", pT->trans_C0_to_uni)); + CTRACE((tfp, " repl_translated_C0: %d\n", pT->repl_translated_C0)); + CTRACE((tfp, " trans_from_uni: %d\n", pT->trans_from_uni)); +} + +/* + * 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; + UCode_t 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) { + static char acsc_name[] = "acsc"; + char *map = tigetstr(acsc_name); + + 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] %#" PRI_UCode_t " -> %#x\n", + table[n].mapping, + CAST_UCode_t (table[n].internal), + (unsigned)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, + (unsigned) test, + (unsigned) 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, UCode_t 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(const char **ppuni) +{ + UCode_t uc_out = 0; + const 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; +} + +/* + * Combine UTF-8 into Unicode. Incomplete characters are either ignored, or + * returned as the UCS replacement character. + */ +dUTF8 HTDecodeUTF8(UTFDecodeState * me, int *c_in_out, UCode_t *result) +{ + dUTF8 rc = dUTF8_ok; + int c = *c_in_out; + unsigned uc = UCH(c); + + if (TOASCII(uc) > 127) { + /* + * continue a multibyte character... + */ + if (me->utf_count > 0 && (TOASCII(c) & 0xc0) == 0x80) { + if (me->utf_count == 1) { + int limit = (int) (me->utf_buf_p - me->utf_buf) + 1; + int maybe = 0; + + /* + * Check for overlong sequences (from comment in xterm): + * 1100000x 10xxxxxx + * 11100000 100xxxxx 10xxxxxx + * 11110000 1000xxxx 10xxxxxx 10xxxxxx + * 11111000 10000xxx 10xxxxxx 10xxxxxx 10xxxxxx + * 11111100 100000xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + switch (limit) { + case 2: + maybe = (UCH(me->utf_buf[0]) & 0xfe) == 0xc0; + break; + case 3: + maybe = ((UCH(me->utf_buf[0]) == 0xe0) && + (UCH(me->utf_buf[1]) & 0xf0) == 0x80); + break; + case 4: + maybe = ((UCH(me->utf_buf[0]) == 0xf0) && + (UCH(me->utf_buf[1]) & 0xf8) == 0x80); + break; + case 5: + maybe = ((UCH(me->utf_buf[0]) == 0xf8) && + (UCH(me->utf_buf[1]) & 0xfd) == 0x80); + break; + } + if (maybe) { + while (limit-- > 2) { + if ((UCH(me->utf_buf[limit]) & 0xc0) != 0x80) { + maybe = 0; + break; + } + } + if (maybe) { + me->utf_char = UCS_REPL; + } + } + } + if (me->utf_char == UCS_REPL) { + rc = dUTF8_err; + } else if (me->utf_char || ((uc & 0x7f) >> (7 - me->utf_count))) { + me->utf_char = (me->utf_char << 6) | (TOASCII(c) & 0x3f); + if ((me->utf_char >= 0xd800 && + me->utf_char <= 0xdfff) || + (me->utf_char == 0xfffe) || + (me->utf_char == UCS_HIDE)) { + me->utf_char = UCS_REPL; + rc = dUTF8_err; + } + } else { + me->utf_char = UCS_REPL; + rc = dUTF8_err; + } + me->utf_count--; + *(me->utf_buf_p) = (char) c; + (me->utf_buf_p)++; + + if (me->utf_count == 0) { + *(me->utf_buf_p) = '\0'; + *result = me->utf_char; + if (*result < 256) { + *c_in_out = UCH(*result & 0xff); + } + switch (*result) { + case 0x200e: /* left-to-right mark */ + case 0x200f: /* right-to-left mark */ + /* lynx does not use these */ + *result = '\0'; + break; + } + } else { + rc = dUTF8_more; + } + } else { + /* + * begin a multibyte character + */ + rc = dUTF8_more; + me->utf_buf_p = me->utf_buf; + *(me->utf_buf_p) = (char) c; + (me->utf_buf_p)++; + if ((uc & 0xe0) == 0xc0) { + me->utf_count = 1; + me->utf_char = (uc & 0x1f); + } else if ((uc & 0xf0) == 0xe0) { + me->utf_count = 2; + me->utf_char = (uc & 0x0f); + } else if ((uc & 0xf8) == 0xf0) { + me->utf_count = 3; + me->utf_char = (uc & 0x07); + } else if ((uc & 0xfc) == 0xf8) { + me->utf_count = 4; + me->utf_char = (uc & 0x03); + } else if ((uc & 0xfe) == 0xfc) { + me->utf_count = 5; + me->utf_char = (uc & 0x01); + } else { + me->utf_count = 0; + me->utf_buf_p = me->utf_buf; + *(me->utf_buf_p) = '\0'; + rc = dUTF8_err; + } + } + } else { + me->utf_count = 0; + me->utf_buf_p = me->utf_buf; + *(me->utf_buf_p) = '\0'; + } + +#if 0 + if (rc != dUTF8_ok) { + CTRACE((tfp, "UTF8 %#x ->%#x %s\n", + uc, UCH(*c_in_out), + (rc == dUTF8_err) ? "err" : "more")); + } else { + if (*result > 127) { + CTRACE((tfp, "UTF8 %#x == %#x\n", uc, (int) *result)); + } else if (c != UCS_REPL && !isspace(c)) { + CTRACE((tfp, "CHAR %#x == %c (%#x)\n", uc, uc, (int) *result)); + } + } +#endif + return rc; +} diff --git a/src/UCdomap.c b/src/UCdomap.c new file mode 100644 index 0000000..0ec239a --- /dev/null +++ b/src/UCdomap.c @@ -0,0 +1,2524 @@ +/* + * $LynxId: UCdomap.c,v 1.111 2023/01/05 09:17:16 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 General Public License 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 + +#if defined(USE_JAPANESEUTF8_SUPPORT) || defined(EXP_CHINESEUTF8_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 <iso16_uni.h> /* ISO 8859-16 (Latin 10) */ +#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(unsigned unicode, unsigned fontpos, int fordefault); +static int con_insert_unipair_str(unsigned 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(unsigned 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, UCode_t 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 STRING2PTR 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 : + typeMallocn(unsigned char, 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((long) 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] = UCS_REPL; + } + } + 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(unsigned unicode, unsigned fontpos, int fordefault) +{ + int i; + unsigned n; + u16 **p1, *p2; + + if (fordefault) + p1 = unidefault_pagedir[n = unicode >> 11]; + else + p1 = uni_pagedir[n = unicode >> 11]; + if (!p1) { + if ((p1 = typecallocn(u16 *, 32)) == NULL) + return ucError; + if (fordefault) + unidefault_pagedir[n] = p1; + else + uni_pagedir[n] = p1; + } + + 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] = UCS_HIDE; /* No glyph for this character (yet) */ + } + } + + p2[unicode & 0x3f] = (u16) fontpos; + + return 0; +} + +static int con_insert_unipair_str(unsigned unicode, const char *replace_str, + int fordefault) +{ + unsigned 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) { + if ((p1 = typecallocn(char **, 32)) == NULL) + return ucError; + + if (fordefault) + unidefault_pagedir_str[n] = p1; + else + uni_pagedir_str[n] = p1; + } + + n = ((unicode >> 6) & 0x1f); + if (!p1[n]) { + if ((p1[n] = typecallocn(char *, 64)) == NULL) + return ucError; + } + 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(unsigned 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 = UCS_REPL; + } 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, + UCode_t ucs, + int usedefault) +{ + char *h; + char ***p1, **p2; + + /* + * Only 16-bit codes supported at this time. + */ + if (ucs > 0xffff) { + /* + * U+FFFD: REPLACEMENT CHARACTER. + */ + ucs = UCS_REPL; + /* + * 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, (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(UCode_t 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(UCS_REPL, 0); + } + if ((isdefault || trydefault) && (rc == ucNotFound)) { + rc = conv_uni_to_pc(UCS_REPL, 1); + } + return rc; +} + +/* + * Returns string length, or negative value for error. + */ +int UCTransUniCharStr(char *outbuf, + int buflen, + UCode_t 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 USE_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(%" PRI_UCode_t + "). Not supported.\n", CAST_UCode_t (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, UCS_REPL, 0); + if ((rc == ucNotFound) && (isdefault || trydefault)) + rc = conv_uni_to_str(outbuf, buflen, UCS_REPL, 1); + if (rc >= 0) + return (int) strlen(outbuf); + } + if (chk_single_flag && src == ucNotFound) { + if (!isdefault) + rc = conv_uni_to_pc(UCS_REPL, 0); + if ((rc == ucNotFound) && (isdefault || trydefault)) + rc = conv_uni_to_pc(UCS_REPL, 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(int ch_in, + int charset_in, + int charset_out) +{ + UCode_t unicode; + int 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(UCS_REPL, 0); + } + if ((rc == ucNotFound) && (isdefault || trydefault)) { + rc = conv_uni_to_pc(UCS_REPL, 1); + } + return rc; +} + +#if defined(USE_JAPANESEUTF8_SUPPORT) || defined(EXP_CHINESEUTF8_SUPPORT) +UCode_t 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. + */ +UCode_t UCTransToUni(int ch_in, + int charset_in) +{ + static char buffer[10]; + static unsigned inx = 0; + + UCode_t unicode; + int 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_iu)) { + unsigned need; + const char *ptr; + + buffer[inx++] = (char) ch_iu; + buffer[inx] = '\0'; + need = (unsigned) 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 USE_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] = (char) ch_in; + inx = 1; + return ucNeedMore; + } else if (IS_SJIS_X0201KANA(ch_iu)) { + buffer[0] = (char) ch_in; + buffer[1] = 0; + cd = iconv_open("UTF-16BE", "Shift_JIS"); + ilen = 1; + (void) iconv(cd, (ICONV_CONST char **) &pin, &ilen, &pout, &olen); + iconv_close(cd); + if ((ilen == 0) && (olen == 0)) { + return (UCH(obuffer[0]) << 8) + UCH(obuffer[1]); + } + } + } else { + if (IS_SJIS_LO(ch_iu)) { + buffer[1] = (char) 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) || ch_iu == 0x8E) { + buffer[0] = (char) ch_in; + inx = 1; + return ucNeedMore; + } + } else { + if (IS_EUC_LOX(ch_iu)) { + buffer[1] = (char) 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(int 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, + int ch_in, + int charset_in, + int charset_out, + int chk_single_flag) +{ + UCode_t unicode; + int 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] = (char) 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, UCS_REPL, 0); + if ((rc == ucNotFound) && (isdefault || trydefault)) + rc = conv_uni_to_str(outbuf, buflen, UCS_REPL, 1); + if (rc >= 0) + return (int) strlen(outbuf); + } + if (chk_single_flag && src == ucNotFound) { + if (!isdefault) + rc = conv_uni_to_pc(UCS_REPL, 0); + if ((rc == ucNotFound) && (isdefault || trydefault)) + rc = conv_uni_to_pc(UCS_REPL, 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 (isEmpty(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 (!strcasecomp(value, "iso-8859-8-i") || + !strcasecomp(value, "iso-8859-8-e")) { + return UCGetLYhndl_byMIME("iso-8859-8"); + } +#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")) || + (!strcasecomp(value, "ks_c_5601-1987"))) { + 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 STRING2PTR 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 STRING2PTR UC_setup_LYCharSets_repl(int UC_charset_in_hndl, + unsigned lowest8) +{ + STRING2PTR 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: + */ + if ((tp = typecallocn(const char *, 96)) == NULL) + return NULL; + + if ((ti = typecallocn(u8, 96)) == NULL) { + FREE(tp); + return NULL; + } + + 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) && + !(UCH(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 (STRING2PTR) 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; + STRING2PTR 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 UCode_t 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_iso_8859_16; /* ISO 8859-16 (Latin 10) */ + + 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 UCode_t lst[3]; + unsigned UCode_t 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) +{ + char *name; + + CTRACE((tfp, "LYFindLocaleCharset(%d)\n", LYLocaleCharset)); + name = nl_langinfo(CODESET); + + if (name != 0) { + int value = UCGetLYhndl_byMIME(name); + + if (value >= 0) { + linedrawing_char_set = value; + CTRACE((tfp, "Found name \"%s\" -> %d\n", name, value)); + /* + * If no locale was set, we will get the POSIX character set, which + * in Lynx is treated as US-ASCII. However, Lynx's longstanding + * behavior has been to default to ISO-8859-1. So we treat that + * encoding specially. Otherwise, if LOCALE_CHARSET is set, then + * we will use the locale encoding -- unless overridden by the + * ASSUME_CHARSET value and/or command-line option. + */ + if (LYLocaleCharset) { + CTRACE((tfp, "...prior LocaleCharset '%s'\n", NonNull(UCAssume_MIMEcharset))); + if (value == US_ASCII) { + CTRACE((tfp, "...prefer existing charset to ASCII\n")); + } else if (assumed_charset) { + CTRACE((tfp, "...already assumed-charset\n")); + } else { + current_char_set = linedrawing_char_set; + UCLYhndl_for_unspec = current_char_set; + StrAllocCopy(UCAssume_MIMEcharset, name); + CTRACE((tfp, "...using LocaleCharset '%s'\n", NonNull(UCAssume_MIMEcharset))); + } + } + } else { + CTRACE((tfp, "Cannot find a handle for MIME name \"%s\"\n", name)); + } + } else { + CTRACE((tfp, "Cannot find a MIME name for locale\n")); + } +} +#endif /* USE_LOCALE_CHARSET */ + +BOOL UCScanCode(UCode_t *target, const char *source, BOOL isHex) +{ + BOOL status = FALSE; + long lcode; + char *endptr; + + errno = 0; + *target = 0; + lcode = strtol(source, &endptr, isHex ? 16 : 10); + if (lcode >= 0 + && (endptr > source) +#if defined(ERANGE) && defined(LONG_MAX) && defined(LONG_MIN) + && (errno != ERANGE || (lcode != LONG_MAX && lcode != LONG_MIN)) +#else + && (endptr - source) < (isHex ? 8 : 10) +#endif + && (endptr != 0) + && (*endptr == '\0')) { + *target = (UCode_t) lcode; + status = TRUE; + } + return status; +} diff --git a/src/UCdomap.h b/src/UCdomap.h new file mode 100644 index 0000000..1a2f00e --- /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 0000000..cc6cdd8 --- /dev/null +++ b/src/Xsystem.c @@ -0,0 +1,589 @@ +/* $LynxId: Xsystem.c,v 1.29 2018/02/17 14:58:15 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 +#define NEAR +#endif + +#define SAVE_FD 10 + +typedef struct _proc { + struct _proc *next; + char *line; + char *cmd; + char *arg; + char *inf; + int infmod; + char *outf; + int outfmod; + int ored[SAVE_FD]; + int sred[SAVE_FD]; +} 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 = (int) 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 = (int) strlen(cmdtab[i]); + if (lc < l && strncasecomp(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(®, ®); + result = reg.h.dl; +#endif + return result; +} + +#define spawnl_rc(flag, rc) \ + ((flag == P_WAIT) \ + ? (int)(rc) \ + : ((rc) == 0 ? -2 : 0)) + +static int NEAR csystem(PRO * p, int flag) +{ + char *cmp; + char SW[3]; + intptr_t 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 spawnl_rc(flag, 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; + intptr_t rc2; + + sprintf(cmdb, "%.*s.com", (int) sizeof(cmdb) - 5, cnm); + if ((rc = open(cmdb, O_RDONLY)) >= 0) { + close(rc); + rc2 = spawnl(flag, cmdb, cmdb, p->arg, (char *) 0); + return spawnl_rc(flag, rc2); + } + sprintf(cmdb, "%.*s.exe", (int) sizeof(cmdb) - 5, cnm); + if ((rc = open(cmdb, O_RDONLY)) >= 0) { + close(rc); + rc2 = spawnl(flag, cmdb, cmdb, p->arg, (char *) 0); + return spawnl_rc(flag, rc2); + } + sprintf(cmdb, "%.*s.bat", (int) 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; + intptr_t rc2; + + cmd_len = (int) 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); + rc2 = spawnl(flag, cmdb, cmdb, p->arg, (char *) 0); + rc = spawnl_rc(flag, rc2); + } + } 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); + rc2 = spawnl(flag, p->cmd, p->cmd, p->arg, (char *) 0); + return spawnl_rc(flag, rc2); + } + 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 = (int) 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) +{ + int rc; + + 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); + + 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 0000000..7437b50 --- /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 0000000..14431da --- /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 0000000..f2de902 --- /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 0000000..edbba85 --- /dev/null +++ b/src/chrtrans/build-chrtrans.com @@ -0,0 +1,142 @@ +$ 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 iso16_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 0000000..ff8a6f5 --- /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 0000000..5894a3f --- /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 0000000..8a19d55 --- /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 0000000..0d928f9 --- /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 0000000..50ce967 --- /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 0000000..49523d4 --- /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 0000000..3f0af9e --- /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 0000000..1ab99c0 --- /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 0000000..6cd0081 --- /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 0000000..4f45ce7 --- /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 0000000..e57e261 --- /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 0000000..26a3ff7 --- /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 0000000..9d05af2 --- /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 0000000..978cca4 --- /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 0000000..bd30996 --- /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 0000000..ebf1222 --- /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 0000000..6568577 --- /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 0000000..029e025 --- /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 0000000..8d2dee0 --- /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 0000000..d662b83 --- /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 0000000..14b7ac4 --- /dev/null +++ b/src/chrtrans/def7_uni.tbl @@ -0,0 +1,2951 @@ +# $LynxId: def7_uni.tbl,v 1.33 2020/01/21 22:21:19 tom 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" # Ä, 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" # Ö, not the best choice for some languages. +U+00d7: * +U+00d8:O/ +0x55 U+00d9-U+00db +U+00dc "Ue" # Ü, 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" # ä, 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" # ö, not the best choice for some languages. +U+00f7:-: +U+00f8:o/ +0x75 U+00f9-U+00fb +U+00fc "ue" # ü, 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+010a U+010c # C +U+0108 "Ch" +# The following line is an example for mapping several accented versions +# of small letter 'c' to 'c': +0x63 U+0107 U+010b U+010d # c +U+0109 "ch" +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+011e U+0120 U+0122 # G +0x67 U+011f U+0121 U+0123 # g +U+011C "Gh" +U+011D "gh" +U+0124 "Hh" +U+0125" "hh" +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 +U+0134 "Jh" +U+0135 "jh" +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+015e U+0160 U+0218 # S +0x73 U+015b U+015f U+0161 U+0219 # s +U+015C "Sh" +U+015d "sh" +0x54 U+0162 U+0164 U+021a # T +0x74 U+0163 U+0165 U+021b # 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 +0x27 U+02bc +0x27 U+02bd +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+200A 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" + +# Don't want to see these: +# POP DIRECTIONAL FORMATTING 202C +U+202c: +# LEFT-TO-RIGHT OVERRIDE 202D +U+202d: + +U+202b "" +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 ™ +# 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 0000000..ab9edb2 --- /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 (¡) - iexcl +0xA2 U+00A2 # cent sign (¢) - cent +0xA3 U+00A3 # pound sign (£) - pound +0xA5 U+00A5 # yen sign (¥) - yen +# broken vertical bar (¦) - brvbar, brkbar +U+00A6:| +0xA7 U+00A7 # section sign (§) - sect +0xA8 U+00A4 # currency sign (¤) - curren +# spacing diaeresis (¨) - uml, die +U+00A8:" +0xA9 U+00A9 # copyright sign (©) - copy +0xAA U+00AA # feminine ordinal indicator (ª) - ordf +0xAB U+00AB # angle quotation mark, left («) - laquo +# negation sign (¬); - not +U+00AC:NOT +# soft hyphen (­) - shy +#U+00AD +# circled R registered sign (®) - reg +U+00AE:(R) +# spacing macron (¯) - hibar, macr +U+00AF:- +0xB0 U+00B0 # degree sign (°) - deg +0xB1 U+00B1 # plus-or-minus sign (±) - plusmn +0xB2 U+00B2 # superscript 2 (²) - sup2 +0xB3 U+00B3 # superscript 3 (³) - sup3 +#spacing acute (´) - acute +U+00B4:' +0xB5 U+00B5 # micro sign (µ) - micro +0xB6 U+00B6 # paragraph sign (¶) - para +0xB7 U+00B7 # middle dot (·) - middot +# spacing cedilla (¸) - cedil +U+00B8:, +0xB9 U+00B9 # superscript 1 (¹) - sup1 +0xBA U+00BA # masculine ordinal indicator (º) - ordm +0xBB U+00BB # angle quotation mark, right (») - raquo +0xBC U+00BC # fraction 1/4 (¼) - frac14 +0xBD U+00BD # fraction 1/2 (½) - frac12 +# fraction 3/4 (¾) - frac34 +U+00BE: 3/4 +0xBF U+00BF # inverted question mark (¿) - iquest +0xC0 U+00C0 # capital A, grave accent (À) - Agrave +0xC1 U+00C1 # capital A, acute accent (Á) - Aacute +0xC2 U+00C2 # capital A, circumflex accent (Â) - Acirc +0xC3 U+00C3 # capital A, tilde (Ã) - Atilde +0xC4 U+00C4 # capital A, dieresis or umlaut mark (Ä) - Auml +0xC5 U+00C5 # capital A, ring (Å) - Aring +0xC6 U+00C6 # capital AE diphthong (ligature) (Æ) - AElig +0xC7 U+00C7 # capital C, cedilla (Ç) - Ccedil +0xC8 U+00C8 # capital E, grave accent (È) - Egrave +0xC9 U+00C9 # capital E, acute accent (É) - Eacute +0xCA U+00CA # capital E, circumflex accent (Ê) - Ecirc +0xCB U+00CB # capital E, dieresis or umlaut mark (Ë) - Euml +0xCC U+00CC # capital I, grave accent (Ì) - Igrave +0xCD U+00CD # capital I, acute accent (Í) - Iacute +0xCE U+00CE # capital I, circumflex accent (Î) - Icirc +0xCF U+00CF # capital I, dieresis or umlaut mark (Ï) - Iuml +# capital Eth, Icelandic (Ð) - ETH */ +U+00D0:DH +# Dj # capital D with stroke - Dstrok +0xD1 U+00D1 # capital N, tilde (Ñ) - Ntilde +0xD2 U+00D2 # capital O, grave accent (Ò) - Ograve +0xD3 U+00D3 # capital O, acute accent (Ó) - Oacute +0xD4 U+00D4 # capital O, circumflex accent (Ô) - Ocirc +0xD5 U+00D5 # capital O, tilde (Õ) - Otilde +0xD6 U+00D6 # capital O, dieresis or umlaut mark (Ö) - Ouml +0xD7 U+0152 # capital OE ligature (Œ) - OElig +# multiplication sign (×) - times +U+00D7:* +0xD8 U+00D8 # capital O, slash (Ø) - Oslash +0xD9 U+00D9 # capital U, grave accent (Ù) - Ugrave +0xDA U+00DA # capital U, acute accent (Ú) - Uacute +0xDB U+00DB # capital U, circumflex accent (Û) - Ucirc +0xDC U+00DC # capital U, dieresis or umlaut mark (Ü) - Uuml +0xDD U+0178 # capital Y, dieresis or umlaut mark (Ÿ) - Yuml +# capital Y, acute accent (Ý) - Yacute +U+00DD:Y' +# capital THORN, Icelandic (Þ) - THORN */ +U+00DE:TH +0xDF U+00DF # small sharp s, German (sz ligature) (ß) - szlig +0xE0 U+00E0 # small a, grave accent (à) - agrave +0xE1 U+00E1 # small a, acute accent (á) - aacute +0xE2 U+00E2 # small a, circumflex accent (â) - acirc +0xE3 U+00E3 # small a, tilde (ã) - atilde +0xE4 U+00E4 # small a, dieresis or umlaut mark (ä) - auml +0xE5 U+00E5 # small a, ring (å) - aring +0xE6 U+00E6 # small ae diphthong (ligature) (æ) - aelig +0xE7 U+00E7 # small c, cedilla (ç) - ccedil +0xE8 U+00E8 # small e, grave accent (è) - egrave +0xE9 U+00E9 # small e, acute accent (é) - eacute +0xEA U+00EA # small e, circumflex accent (ê) - ecirc +0xEB U+00EB # small e, dieresis or umlaut mark (ë) - euml +0xEC U+00EC # small i, grave accent (ì) - igrave +0xED U+00ED # small i, acute accent (í) - iacute +0xEE U+00EE # small i, circumflex accent (î) - icirc +0xEF U+00EF # small i, dieresis or umlaut mark (ï) - iuml +# small eth, Icelandic (ð) - eth +U+00F0:dh +0xF1 U+00F1 # small n, tilde (ñ) - ntilde +0xF2 U+00F2 # small o, grave accent (ò) - ograve +0xF3 U+00F3 # small o, acute accent (ó) - oacute +0xF4 U+00F4 # small o, circumflex accent (ô) - ocirc +0xF5 U+00F5 # small o, tilde (õ) - otilde +0xF6 U+00F6 # small o, dieresis or umlaut mark (ö) - ouml +0xF7 U+0153 # small oe ligature (œ) - oelig +# division sign (÷) - divide +U+00F7:/ +0xF8 U+00F8 # small o, slash (ø) - oslash +0xF9 U+00F9 # small u, grave accent (ù) - ugrave +0xFA U+00FA # small u, acute accent (ú) - uacute +0xFB U+00FB # small u, circumflex accent (û) - ucirc +0xFC U+00FC # small u, dieresis or umlaut mark (ü) - uuml +0xFD U+00FF # small y, dieresis or umlaut mark (ÿ) - yuml +# small y, acute accent (ý) - yacute +U+00FD:y' +# small thorn, Icelandic (þ) - 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 0000000..66f66ab --- /dev/null +++ b/src/chrtrans/entities.h @@ -0,0 +1,1414 @@ +/* + * $LynxId: entities.h,v 1.6 2020/01/21 21:31:36 tom Exp $ + * + * Entity Names to Unicode table + * ----------------------------- + * + * This is a one-way mapping to Unicode so chartrans implementation + * now process character entities like   the similar way it handles + * the numeric entities like {. + * The only call to this structure is via HTMLGetEntityUCValue(). + * + +Unlike the numeric entities ê 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 everything +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 <, >, &. + * 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 ◊ 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 ◊ 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 0000000..5802551 --- /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 0000000..95e705e --- /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 0000000..fe3fc71 --- /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 0000000..aafce8d --- /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 0000000..a1c3ed0 --- /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 0000000..afbb213 --- /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 0000000..e4ef995 --- /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 0000000..87372a9 --- /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 0000000..d162396 --- /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 0000000..87afe48 --- /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 0000000..edd59e2 --- /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 0000000..33e3b8f --- /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 (German) +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 0000000..630a946 --- /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 0000000..398affd --- /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+017E # 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/iso16_uni.tbl b/src/chrtrans/iso16_uni.tbl new file mode 100644 index 0000000..e846b24 --- /dev/null +++ b/src/chrtrans/iso16_uni.tbl @@ -0,0 +1,120 @@ +#The MIME name of this charset. +Miso-8859-16 + +#Name as a Display Charset (used on Options screen) +OLatin 10 (ISO-8859-16) + +# This is not the default font! +D0 + +#Codepage number +#C28606 + +#This table contains the data the Unicode Consortium has on how +#ISO/IEC 8859-16:2001 characters map into Unicode. +# +#Format: Three tab-separated columns +# Column #1 is the ISO/IEC 8859-16 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-16 order. + +0x20-0x7E idem # ASCII + +0xA0 U+00A0 # NO-BREAK SPACE +0xA1 U+0104 # LATIN CAPITAL LETTER A WITH OGONEK +0xA2 U+0105 # LATIN SMALL LETTER A WITH OGONEK +0xA3 U+0141 # LATIN CAPITAL LETTER L WITH STROKE +0xA4 U+20AC # EURO SIGN +0xA5 U+201E # DOUBLE LOW-9 QUOTATION MARK +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+0218 # LATIN CAPITAL LETTER S WITH COMMA BELOW +0xAB U+00AB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK +0xAC U+0179 # LATIN CAPITAL LETTER Z WITH ACUTE +0xAD U+00AD # SOFT HYPHEN +0xAE U+017A # LATIN SMALL LETTER Z WITH ACUTE +0xAF U+017B # LATIN CAPITAL LETTER Z WITH DOT ABOVE +0xB0 U+00B0 # DEGREE SIGN +0xB1 U+00B1 # PLUS-MINUS SIGN +0xB2 U+010C # LATIN CAPITAL LETTER C WITH CARON +0xB3 U+0142 # LATIN SMALL LETTER L WITH STROKE +0xB4 U+017D # LATIN CAPITAL LETTER Z WITH CARON +0xB5 U+201D # RIGHT DOUBLE QUOTATION MARK +0xB6 U+00B6 # PILCROW SIGN +0xB7 U+00B7 # MIDDLE DOT +0xB8 U+017E # LATIN SMALL LETTER Z WITH CARON +0xB9 U+010D # LATIN SMALL LETTER C WITH CARON +0xBA U+0219 # LATIN SMALL LETTER S WITH COMMA BELOW +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+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 +0xC3 U+0102 # LATIN CAPITAL LETTER A WITH BREVE +0xC4 U+00C4 # LATIN CAPITAL LETTER A WITH DIAERESIS +0xC5 U+0106 # LATIN CAPITAL LETTER C WITH ACUTE +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+0110 # LATIN CAPITAL LETTER D WITH STROKE +0xD1 U+0143 # LATIN CAPITAL LETTER N WITH ACUTE +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+0150 # LATIN CAPITAL LETTER O WITH DOUBLE ACUTE +0xD6 U+00D6 # LATIN CAPITAL LETTER O WITH DIAERESIS +0xD7 U+015A # LATIN CAPITAL LETTER S WITH ACUTE +0xD8 U+0170 # LATIN CAPITAL LETTER U WITH DOUBLE ACUTE +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+0118 # LATIN CAPITAL LETTER E WITH OGONEK +0xDE U+021A # LATIN CAPITAL LETTER T WITH COMMA BELOW +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+0103 # LATIN SMALL LETTER A WITH BREVE +0xE4 U+00E4 # LATIN SMALL LETTER A WITH DIAERESIS +0xE5 U+0107 # LATIN SMALL LETTER C WITH ACUTE +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+0111 # LATIN SMALL LETTER D WITH STROKE +0xF1 U+0144 # LATIN SMALL LETTER N WITH ACUTE +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+0151 # LATIN SMALL LETTER O WITH DOUBLE ACUTE +0xF6 U+00F6 # LATIN SMALL LETTER O WITH DIAERESIS +0xF7 U+015B # LATIN SMALL LETTER S WITH ACUTE +0xF8 U+0171 # LATIN SMALL LETTER U WITH DOUBLE ACUTE +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+0119 # LATIN SMALL LETTER E WITH OGONEK +0xFE U+021B # LATIN SMALL LETTER T WITH COMMA BELOW +0xFF U+00FF # LATIN SMALL LETTER Y WITH DIAERESIS diff --git a/src/chrtrans/jcuken_kb.h b/src/chrtrans/jcuken_kb.h new file mode 100644 index 0000000..5f42d26 --- /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 0000000..8bf4001 --- /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 0000000..2c13845 --- /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 0000000..2564701 --- /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 (¦) - brvbar, brkbar +U+00A6:| +# superscript 3 (³) - sup3 +U+00B3:^3 +# superscript 2 (²) - sup2 +U+00B2:^2 +# superscript 1 (¹) - sup1 +U+00B9:^1 +# fraction 1/4 (¼) - frac14 +U+00BC: 1/4 +# fraction 1/2 (½) - frac12 +U+00BD: 1/2 +# fraction 3/4 (¾) - frac34 +U+00BE: 3/4 +# capital Eth, Icelandic (Ð) - ETH +U+00D0:DH +# Dj # capital D with stroke - Dstrok +# capital Y, acute accent (Ý) - Yacute +U+00DD:Y' +# capital THORN, Icelandic (Þ) - THORN +U+00DE:P +# multiplication sign (×) - times +U+00D7:* +# small eth, Icelandic (ð) - eth +U+00F0:dh +# small y, acute accent (ý) - yacute +U+00FD:y' +# small thorn, Icelandic (þ) - thorn +U+00FE:p +# diff --git a/src/chrtrans/make-msc.bat b/src/chrtrans/make-msc.bat new file mode 100644 index 0000000..81d615e --- /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 0000000..134a395 --- /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 0000000..9878637 --- /dev/null +++ b/src/chrtrans/makefile.dos @@ -0,0 +1,137 @@ +# +# 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 \ + iso16_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 +iso16_uni.h: iso16_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 0000000..8eb4e58 --- /dev/null +++ b/src/chrtrans/makefile.in @@ -0,0 +1,201 @@ +# $LynxId: makefile.in,v 1.45 2021/03/14 17:14:26 emil 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@ +datarootdir = @datarootdir@ +top_srcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = $(srcdir) + +top_builddir = ../.. + +CC = @CC@ +CPP = @CPP@ +CFLAGS = @CFLAGS@ @EXTRA_CPPFLAGS@ +CPPFLAGS = @CPPFLAGS@ + +WWWINC = WWW/Library/Implementation + +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@ +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)/ \ + $(SITE_DEFS) $(BUILD_CPPFLAGS) +CC_OPTS = $(CPP_OPTS) $(BUILD_CFLAGS) + +LINT = @LINT@ +LINTOPTS = @LINT_OPTS@ + +CTAGS = @CTAGS@ + +# +# 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 \ + iso16_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) + +all tables: $(TABLES) + +OBJS = makeuctb$o +C_SRC = $(OBJS:$o=.c) + +$(MAKEUCTB) : $(OBJS) + $(BUILD_CC) $(CC_OPTS) $(BUILD_LDFLAGS) -o $@ $(OBJS) $(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) $(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) +iso16_uni.h: $(srcdir)/iso16_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 + +tags: + $(CTAGS) *.[ch] + +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 0000000..8dd8ace --- /dev/null +++ b/src/chrtrans/makefile.msc @@ -0,0 +1,139 @@ +# +# 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 "WIN32_LEAN_AND_MEAN" /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 /EHsc /O2 /c + +MACHINE = i386 +LDFLAGS = /nologo /subsystem:console /incremental:no /machine:$(MACHINE) +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 \ + iso16_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 +iso16_uni.h: iso16_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 0000000..a349897 --- /dev/null +++ b/src/chrtrans/makehdrs.bat @@ -0,0 +1,51 @@ +@rem $LynxId: makehdrs.bat,v 1.2 2021/03/14 17:14:26 emil 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 iso16_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 0000000..29d43ee --- /dev/null +++ b/src/chrtrans/makeuctb.c @@ -0,0 +1,914 @@ +/* + * $LynxId: makeuctb.c,v 1.52 2021/03/22 22:52:58 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> + +#ifdef LY_FIND_LEAKS /* CF_ARG_ENABLE(find-leaks) */ +#define FreeLeak(p) FREE(p) +#else +#define FreeLeak(p) /* nothing */ +#endif + +#define L_CURL '{' +#define R_CURL '}' + +/* + * Don't try to use LYexit() since this is a standalone file. + */ +#ifdef exit +#undef exit +#endif /* exit */ + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#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 GCC_NORETURN void done(int code); + +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 +static 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 (int) 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 *) calloc(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) { + FreeLeak(themap_str.entries[i].replace_str); + 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); + } + FreeLeak(themap_str.entries[themap_str.entry_ct].replace_str); + themap_str.entries[themap_str.entry_ct].unicode = (u16) 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]] = (u16) 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 = NULL, 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, (int) 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 = (int) 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 = (int) 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 = (int) 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 = (int) 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 = (char) TOLOWER(*p); + } + if (argc > 4) { + StrNCpy(this_LYNXcharset, argv[4], UC_MAXLEN_LYNXCSNAME); + } else if (this_LYNXcharset[0] == '\0') { + memcpy(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] = (char) (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\ +%c\n\t", argv[0], argv[1], id_append, fontlen, L_CURL); + + 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%c;\n", R_CURL); + } 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%c\n\t", + id_append, nuni, L_CURL); + } 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%c;\n", R_CURL); + } 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\ +%c\n\t", id_append, themap_str.entry_ct, L_CURL); + } 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, "%c0x%x,\"%s\"%c", + L_CURL, + themap_str.entries[i].unicode, + themap_str.entries[i].replace_str, + R_CURL); + if (i == (themap_str.entry_ct - 1)) { + fprintf(chdr, "\n%c;\n", R_CURL); + } 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 = %c%d,repl_map%s,", + id_append, L_CURL, themap_str.entry_ct, id_append); + } else { + fprintf(chdr, "\n\ +static const struct unimapdesc_str dfont_replacedesc%s = %c0,NULL,", id_append, L_CURL); + } + fprintf(chdr, "%d,%d%c;\n", + this_isDefaultMap ? 1 : 0, + (useDefaultMap && !this_isDefaultMap) ? 1 : 0, + R_CURL); + + 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); +#ifdef LY_FIND_LEAKS /* CF_ARG_ENABLE(find-leaks) */ + for (i = 0; i < themap_str.entry_ct; i++) { + FreeLeak(themap_str.entries[i].replace_str); + } +#endif + return 0; +} diff --git a/src/chrtrans/makew32.bat b/src/chrtrans/makew32.bat new file mode 100644 index 0000000..da47601 --- /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 0000000..3fc122c --- /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 0000000..02bd8ea --- /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 0000000..a76ae34 --- /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 acquisition +# 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 0000000..0bacb52 --- /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 0000000..65fa17e --- /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 0000000..1df5f32 --- /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 0000000..88ad492 --- /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 0000000..617f1e1 --- /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 0000000..8301c81 --- /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 0000000..24e43fe --- /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 0000000..670a21b --- /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 0000000..6b22b78 --- /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 0000000..0fe5159 --- /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 0000000..5409676 --- /dev/null +++ b/src/makefile.dos @@ -0,0 +1,115 @@ +# $LynxId: makefile.dos,v 1.33 2011/05/28 13:07:55 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 \ + -DUSE_ADDRLIST_PAGE \ + -DUSE_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 0000000..64024f7 --- /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 0000000..5246226 --- /dev/null +++ b/src/makefile.in @@ -0,0 +1,242 @@ +# $LynxId: makefile.in,v 1.77 2021/02/28 15:56:53 tom Exp $ +# template-makefile for Lynx src directory + +SHELL = @CONFIG_SHELL@ +CDPATH = . + +@SET_MAKE@ +prefix = @prefix@ +datarootdir = @datarootdir@ +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@ @EXTRA_CPPFLAGS@ +DEFS = @DEFS@ +CHARSET_DEFS = @CHARSET_DEFS@ +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@ +BUILD_LDFLAGS = @BUILD_LDFLAGS@ +BUILD_LIBS = @BUILD_LIBS@ + +MAKE_RECUR = $(MAKE) @cf_cv_makeflags@ DESTDIR="$(DESTDIR)" CC="$(CC)" + +YACC = @YACC@ +WINDRES = @WINDRES@ + +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) $(CPPFLAGS) +CC_OPTS = $(CFLAGS) $(CPP_OPTS) + +LINT = @LINT@ +LINTOPTS = + +CTAGS = @CTAGS@ + +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) $(CPP_OPTS) $< >$@ + +lynx$x: message $(top_builddir)/LYHelp.h $(OBJS) $(WWWLIB) + @echo "Linking and creating Lynx executable" + $(CC) $(CC_OPTS) $(LDFLAGS) -o $@ $(OBJS) $(WWWLIB) $(LIBS) $(INTLLIB) + @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_RECUR) \ + 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_RECUR) clean ) + +tags: + $(CTAGS) *.[ch] + +distclean: clean + +CMN=$(top_srcdir)/WWW/Library/Implementation/ + +GridText$o : $(top_srcdir)/userdefs.h +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: LYIcon.rc + $(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)cp857_uni.h \ + $(CHRTR)cp862_uni.h \ + $(CHRTR)cp864_uni.h \ + $(CHRTR)cp866_uni.h \ + $(CHRTR)cp866u_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)iso13_uni.h \ + $(CHRTR)iso14_uni.h \ + $(CHRTR)iso15_uni.h \ + $(CHRTR)koi8r_uni.h \ + $(CHRTR)koi8u_uni.h \ + $(CHRTR)mac_uni.h \ + $(CHRTR)mnem2_suni.h \ + $(CHRTR)mnem_suni.h \ + $(CHRTR)next_uni.h \ + $(CHRTR)pt154_uni.h \ + $(CHRTR)rfc_suni.h \ + $(CHRTR)utf8_uni.h \ + $(CHRTR)viscii_uni.h + +$(TABLES): + -( cd chrtrans && $(MAKE_RECUR) 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_RECUR) 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 0000000..5890ec3 --- /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 0000000..961f6b4 --- /dev/null +++ b/src/mktime.c @@ -0,0 +1,71 @@ +/* + * 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 0000000..97b420f --- /dev/null +++ b/src/multinet.opt @@ -0,0 +1 @@ +multinet:multinet_socket_library/share diff --git a/src/multinet_ucx.opt b/src/multinet_ucx.opt new file mode 100644 index 0000000..51bac45 --- /dev/null +++ b/src/multinet_ucx.opt @@ -0,0 +1 @@ +multinet_root:[multinet.library]ucx$ipc/LIBRARY diff --git a/src/parsdate.c b/src/parsdate.c new file mode 100644 index 0000000..07bbc9d --- /dev/null +++ b/src/parsdate.c @@ -0,0 +1,2425 @@ +/* original parser id follows */ +/* yysccsid[] = "@(#)yaccpar 1.9 (Berkeley) 02/21/93" */ +/* (use YYMAJOR/YYMINOR for ifdefs dependent on parser version) */ + +#define YYBYACC 1 +#define YYMAJOR 2 +#define YYMINOR 0 +#define YYPATCH 20240109 + +#define YYEMPTY (-1) +#define yyclearin (yychar = YYEMPTY) +#define yyerrok (yyerrflag = 0) +#define YYRECOVERING() (yyerrflag != 0) +#define YYENOMEM (-2) +#define YYEOF 0 +#undef YYBTYACC +#define YYBTYACC 0 +#define YYDEBUGSTR YYPREFIX "debug" +#define YYPREFIX "yy" + +#define YYPURE 0 + +#line 2 "./parsdate.y" + +#include <LYLeaks.h> + +/* + * $LynxId: parsdate.y,v 1.32 2024/01/15 11:09:08 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 : 2019-08-28 (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 + +#define BAD_TIME ((time_t)-1) +#define isBadTime(n) ((n) != 0 && (((n) == BAD_TIME) || !((n) > 0))) + + /* See the LeapYears table in Convert. */ +#define EPOCH 1970 +#define END_OF_TIME 2200 + + /* 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 */ +} + +#ifdef YYSTYPE +#undef YYSTYPE_IS_DECLARED +#define YYSTYPE_IS_DECLARED 1 +#endif +#ifndef YYSTYPE_IS_DECLARED +#define YYSTYPE_IS_DECLARED 1 +#line 139 "./parsdate.y" +typedef union YYSTYPE { + time_t Number; + enum _MERIDIAN Meridian; +} YYSTYPE; +#endif /* !YYSTYPE_IS_DECLARED */ +#line 172 "y.tab.c" + +/* 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 + +/* Parameters sent to lex. */ +#ifdef YYLEX_PARAM +# define YYLEX_DECL() yylex(void *YYLEX_PARAM) +# define YYLEX yylex(YYLEX_PARAM) +#else +# define YYLEX_DECL() yylex(void) +# define YYLEX yylex() +#endif + +#if !(defined(yylex) || defined(YYSTATE)) +int YYLEX_DECL(); +#endif + +/* Parameters sent to yyerror. */ +#ifndef YYERROR_DECL +#define YYERROR_DECL() yyerror(const char *s) +#endif +#ifndef YYERROR_CALL +#define YYERROR_CALL(msg) yyerror(msg) +#endif + +extern int YYPARSE_DECL(); + +#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 +typedef int YYINT; +static const YYINT 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 YYINT 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 YYINT 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, +}; +#if defined(YYDESTRUCT_CALL) || defined(YYSTYPE_TOSTRING) +static const YYINT yystos[] = { 0, + 268, 257, 260, 263, 264, 272, 273, 274, 275, 276, + 260, 44, 264, 261, 262, 259, 260, 261, 262, 263, + 58, 47, 271, 258, 263, 265, 269, 270, 270, 264, + 264, 44, 264, 263, 264, 264, 266, 269, 264, 260, + 45, 264, 58, 269, 271, 47, 58, 264, 260, 264, + 264, 264, 263, 269, 271, 58, 264, 264, +}; +#endif /* YYDESTRUCT_CALL || YYSTYPE_TOSTRING */ +static const YYINT yydgoto[] = { 1, + 27, 28, 23, 6, 7, 8, 9, 10, +}; +static const YYINT 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 YYINT 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, +}; +#if YYBTYACC +static const YYINT yycindex[] = { 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 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 +static const YYINT yygindex[] = { 0, + -17, 44, -31, 0, 0, 0, 0, 0, +}; +#define YYTABLESIZE 300 +static const YYINT 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 YYINT 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, +}; +#if YYBTYACC +static const YYINT yyctable[] = { -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, -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, -1, -1, -1, -1, -1, -1, +}; +#endif +#define YYFINAL 1 +#ifndef YYDEBUG +#define YYDEBUG 0 +#endif +#define YYMAXTOKEN 266 +#define YYUNDFTOKEN 277 +#define YYTRANSLATE(a) ((a) > YYMAXTOKEN ? YYUNDFTOKEN : (a)) +#if YYDEBUG +static const char *const yyname[] = { + +"$end",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,"','","'-'",0,"'/'",0,0,0,0,0,0,0,0,0,0,"':'",0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"error","tDAY","tDAYZONE", +"tMERIDIAN","tMONTH","tMONTH_UNIT","tSEC_UNIT","tSNUMBER","tUNUMBER","tZONE", +"tDST","$accept","spec","numzone","zone","o_merid","item","time","date","both", +"rel","illegal-symbol", +}; +static const char *const 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 +int yydebug; +#endif + +int yyerrflag; +int yychar; +YYSTYPE yyval; +YYSTYPE yylval; +int yynerrs; + +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) +YYLTYPE yyloc; /* position returned by actions */ +YYLTYPE yylloc; /* position from the lexer */ +#endif + +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) +#ifndef YYLLOC_DEFAULT +#define YYLLOC_DEFAULT(loc, rhs, n) \ +do \ +{ \ + if (n == 0) \ + { \ + (loc).first_line = YYRHSLOC(rhs, 0).last_line; \ + (loc).first_column = YYRHSLOC(rhs, 0).last_column; \ + (loc).last_line = YYRHSLOC(rhs, 0).last_line; \ + (loc).last_column = YYRHSLOC(rhs, 0).last_column; \ + } \ + else \ + { \ + (loc).first_line = YYRHSLOC(rhs, 1).first_line; \ + (loc).first_column = YYRHSLOC(rhs, 1).first_column; \ + (loc).last_line = YYRHSLOC(rhs, n).last_line; \ + (loc).last_column = YYRHSLOC(rhs, n).last_column; \ + } \ +} while (0) +#endif /* YYLLOC_DEFAULT */ +#endif /* defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) */ +#if YYBTYACC + +#ifndef YYLVQUEUEGROWTH +#define YYLVQUEUEGROWTH 32 +#endif +#endif /* YYBTYACC */ + +/* define the initial stack-sizes */ +#ifdef YYSTACKSIZE +#undef YYMAXDEPTH +#define YYMAXDEPTH YYSTACKSIZE +#else +#ifdef YYMAXDEPTH +#define YYSTACKSIZE YYMAXDEPTH +#else +#define YYSTACKSIZE 10000 +#define YYMAXDEPTH 10000 +#endif +#endif + +#ifndef YYINITSTACKSIZE +#define YYINITSTACKSIZE 200 +#endif + +typedef struct { + unsigned stacksize; + YYINT *s_base; + YYINT *s_mark; + YYINT *s_last; + YYSTYPE *l_base; + YYSTYPE *l_mark; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + YYLTYPE *p_base; + YYLTYPE *p_mark; +#endif +} YYSTACKDATA; +#if YYBTYACC + +struct YYParseState_s +{ + struct YYParseState_s *save; /* Previously saved parser state */ + YYSTACKDATA yystack; /* saved parser stack */ + int state; /* saved parser state */ + int errflag; /* saved error recovery status */ + int lexeme; /* saved index of the conflict lexeme in the lexical queue */ + YYINT ctry; /* saved index in yyctable[] for this conflict */ +}; +typedef struct YYParseState_s YYParseState; +#endif /* YYBTYACC */ +/* variables for the parser stack */ +static YYSTACKDATA yystack; +#if YYBTYACC + +/* Current parser state */ +static YYParseState *yyps = 0; + +/* yypath != NULL: do the full parse, starting at *yypath parser state. */ +static YYParseState *yypath = 0; + +/* Base of the lexical value queue */ +static YYSTYPE *yylvals = 0; + +/* Current position at lexical value queue */ +static YYSTYPE *yylvp = 0; + +/* End position of lexical value queue */ +static YYSTYPE *yylve = 0; + +/* The last allocated position at the lexical value queue */ +static YYSTYPE *yylvlim = 0; + +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) +/* Base of the lexical position queue */ +static YYLTYPE *yylpsns = 0; + +/* Current position at lexical position queue */ +static YYLTYPE *yylpp = 0; + +/* End position of lexical position queue */ +static YYLTYPE *yylpe = 0; + +/* The last allocated position at the lexical position queue */ +static YYLTYPE *yylplim = 0; +#endif + +/* Current position at lexical token queue */ +static YYINT *yylexp = 0; + +static YYINT *yylexemes = 0; +#endif /* YYBTYACC */ +#line 361 "./parsdate.y" + + +/* +** An entry in the lexical lookup table. +*/ +/* *INDENT-OFF* */ +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 */ +}; +/* *INDENT-ON* */ + +static time_t ToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian) +{ + if (isBadTime(Minutes) || Minutes > 59 || isBadTime(Seconds) || Seconds > 61) + return BAD_TIME; + if (Meridian == MER24) { + if (isBadTime(Hours) || Hours > 23) + return BAD_TIME; + } else { + if (Hours < 1 || Hours > 12) + return BAD_TIME; + 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 + }; + /* *INDENT-OFF* */ + static const int LeapYears[] = + { + 1972, 1976, 1980, 1984, 1988, 1992, 1996, + 2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036, + 2040, 2044, 2048, 2052, 2056, 2060, 2064, 2068, 2072, 2076, + 2080, 2084, 2088, 2092, 2096, /***/ 2104, 2108, 2112, 2116, + 2120, 2124, 2128, 2132, 2136, 2140, 2144, 2148, 2152, 2156, + 2160, 2164, 2168, 2172, 2176, 2180, 2184, 2188, 2192, 2196, + }; + /* *INDENT-ON* */ + + const int *yp; + const int *mp; + int i; + time_t Julian; + time_t tod; + + if (isBadTime(Year)) + 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 BAD_TIME; + } + + 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; + tod = ToSeconds(Hours, Minutes, Seconds, Meridian); + if (isBadTime(tod)) { + return BAD_TIME; + } + Julian += tod; + tod = Julian; + if (dst == DSTon) { + Julian -= DST_OFFSET * 60 * 60; + } else if (dst == DSTmaybe) { + struct tm *tm = localtime(&tod); + + if (tm != NULL) { + if (tm->tm_isdst) + Julian -= DST_OFFSET * 60 * 60; + } else { + Julian = BAD_TIME; + } + } + return Julian; +} + +static time_t DSTcorrect(time_t Start, time_t Future) +{ + struct tm *tm; + time_t StartDay; + time_t FutureDay; + + if ((tm = localtime(&Start)) == NULL) + return BAD_TIME; + StartDay = (tm->tm_hour + 1) % 24; + + if ((tm = localtime(&Future)) == NULL) + return BAD_TIME; + FutureDay = (tm->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; + + if ((tm = localtime(&Start)) == NULL) + return BAD_TIME; + + 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; + + /* 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, (int) (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. */ + memset(Now, 0, sizeof(TIMEINFO)); +#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; +} + +#if defined(YYBYACC) && defined(YYPURE) && defined(LY_FIND_LEAKS) +#undef YYPURE +#define YYPURE 1 +static void yyfreestack(YYSTACKDATA *); +static void parsedate_leaks(void) +{ + yyfreestack(&yystack); +} +#endif + +time_t parsedate(char *p, + TIMEINFO * now) +{ + struct tm *tm; + TIMEINFO ti; + time_t Start; + +#if defined(YYBYACC) && defined(YYPURE) && defined(LY_FIND_LEAKS) + static int initialized; + + if (!initialized) { + initialized = 1; + atexit(parsedate_leaks); + } +#endif + + yyInput = p; + if (now == NULL) { + now = &ti; + (void) GetTimeInfo(&ti); + } + + if ((tm = localtime(&now->time)) == NULL) + return BAD_TIME; + + /* *INDENT-EQLS* */ + 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 BAD_TIME; + + if (yyHaveDate || yyHaveTime) { + Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, + yyMeridian, yyDSTmode); + if (isBadTime(Start)) + return BAD_TIME; + } 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 == BAD_TIME) ? 0 : Start; +} +#line 1203 "y.tab.c" + +/* For use in generated program */ +#define yydepth (int)(yystack.s_mark - yystack.s_base) +#if YYBTYACC +#define yytrial (yyps->save) +#endif /* YYBTYACC */ + +#if YYDEBUG +#include <stdio.h> /* needed for printf */ +#endif + +#include <stdlib.h> /* needed for malloc, etc */ +#include <string.h> /* needed for memset */ + +/* allocate initial stack or double stack size, up to YYMAXDEPTH */ +static int yygrowstack(YYSTACKDATA *data) +{ + int i; + unsigned newsize; + YYINT *newss; + YYSTYPE *newvs; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + YYLTYPE *newps; +#endif + + if ((newsize = data->stacksize) == 0) + newsize = YYINITSTACKSIZE; + else if (newsize >= YYMAXDEPTH) + return YYENOMEM; + else if ((newsize *= 2) > YYMAXDEPTH) + newsize = YYMAXDEPTH; + + i = (int) (data->s_mark - data->s_base); + newss = (YYINT *)realloc(data->s_base, newsize * sizeof(*newss)); + if (newss == 0) + return YYENOMEM; + + data->s_base = newss; + data->s_mark = newss + i; + + newvs = (YYSTYPE *)realloc(data->l_base, newsize * sizeof(*newvs)); + if (newvs == 0) + return YYENOMEM; + + data->l_base = newvs; + data->l_mark = newvs + i; + +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + newps = (YYLTYPE *)realloc(data->p_base, newsize * sizeof(*newps)); + if (newps == 0) + return YYENOMEM; + + data->p_base = newps; + data->p_mark = newps + i; +#endif + + data->stacksize = newsize; + data->s_last = data->s_base + newsize - 1; + +#if YYDEBUG + if (yydebug) + fprintf(stderr, "%sdebug: stack size increased to %d\n", YYPREFIX, newsize); +#endif + return 0; +} + +#if YYPURE || defined(YY_NO_LEAKS) +static void yyfreestack(YYSTACKDATA *data) +{ + free(data->s_base); + free(data->l_base); +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + free(data->p_base); +#endif + memset(data, 0, sizeof(*data)); +} +#else +#define yyfreestack(data) /* nothing */ +#endif /* YYPURE || defined(YY_NO_LEAKS) */ +#if YYBTYACC + +static YYParseState * +yyNewState(unsigned size) +{ + YYParseState *p = (YYParseState *) malloc(sizeof(YYParseState)); + if (p == NULL) return NULL; + + p->yystack.stacksize = size; + if (size == 0) + { + p->yystack.s_base = NULL; + p->yystack.l_base = NULL; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + p->yystack.p_base = NULL; +#endif + return p; + } + p->yystack.s_base = (YYINT *) malloc(size * sizeof(YYINT)); + if (p->yystack.s_base == NULL) return NULL; + p->yystack.l_base = (YYSTYPE *) malloc(size * sizeof(YYSTYPE)); + if (p->yystack.l_base == NULL) return NULL; + memset(p->yystack.l_base, 0, size * sizeof(YYSTYPE)); +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + p->yystack.p_base = (YYLTYPE *) malloc(size * sizeof(YYLTYPE)); + if (p->yystack.p_base == NULL) return NULL; + memset(p->yystack.p_base, 0, size * sizeof(YYLTYPE)); +#endif + + return p; +} + +static void +yyFreeState(YYParseState *p) +{ + yyfreestack(&p->yystack); + free(p); +} +#endif /* YYBTYACC */ + +#define YYABORT goto yyabort +#define YYREJECT goto yyabort +#define YYACCEPT goto yyaccept +#define YYERROR goto yyerrlab +#if YYBTYACC +#define YYVALID do { if (yyps->save) goto yyvalid; } while(0) +#define YYVALID_NESTED do { if (yyps->save && \ + yyps->save->save == 0) goto yyvalid; } while(0) +#endif /* YYBTYACC */ + +int +YYPARSE_DECL() +{ + int yym, yyn, yystate, yyresult; +#if YYBTYACC + int yynewerrflag; + YYParseState *yyerrctx = NULL; +#endif /* YYBTYACC */ +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + YYLTYPE yyerror_loc_range[3]; /* position of error start/end (0 unused) */ +#endif +#if YYDEBUG + const char *yys; + + if ((yys = getenv("YYDEBUG")) != 0) + { + yyn = *yys; + if (yyn >= '0' && yyn <= '9') + yydebug = yyn - '0'; + } + if (yydebug) + fprintf(stderr, "%sdebug[<# of symbols on state stack>]\n", YYPREFIX); +#endif +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + memset(yyerror_loc_range, 0, sizeof(yyerror_loc_range)); +#endif + +#if YYBTYACC + yyps = yyNewState(0); if (yyps == 0) goto yyenomem; + yyps->save = 0; +#endif /* YYBTYACC */ + yym = 0; + /* yyn is set below */ + yynerrs = 0; + yyerrflag = 0; + yychar = YYEMPTY; + yystate = 0; + +#if YYPURE + memset(&yystack, 0, sizeof(yystack)); +#endif + + if (yystack.s_base == NULL && yygrowstack(&yystack) == YYENOMEM) goto yyoverflow; + yystack.s_mark = yystack.s_base; + yystack.l_mark = yystack.l_base; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + yystack.p_mark = yystack.p_base; +#endif + yystate = 0; + *yystack.s_mark = 0; + +yyloop: + if ((yyn = yydefred[yystate]) != 0) goto yyreduce; + if (yychar < 0) + { +#if YYBTYACC + do { + if (yylvp < yylve) + { + /* we're currently re-reading tokens */ + yylval = *yylvp++; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + yylloc = *yylpp++; +#endif + yychar = *yylexp++; + break; + } + if (yyps->save) + { + /* in trial mode; save scanner results for future parse attempts */ + if (yylvp == yylvlim) + { /* Enlarge lexical value queue */ + size_t p = (size_t) (yylvp - yylvals); + size_t s = (size_t) (yylvlim - yylvals); + + s += YYLVQUEUEGROWTH; + if ((yylexemes = (YYINT *)realloc(yylexemes, s * sizeof(YYINT))) == NULL) goto yyenomem; + if ((yylvals = (YYSTYPE *)realloc(yylvals, s * sizeof(YYSTYPE))) == NULL) goto yyenomem; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + if ((yylpsns = (YYLTYPE *)realloc(yylpsns, s * sizeof(YYLTYPE))) == NULL) goto yyenomem; +#endif + yylvp = yylve = yylvals + p; + yylvlim = yylvals + s; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + yylpp = yylpe = yylpsns + p; + yylplim = yylpsns + s; +#endif + yylexp = yylexemes + p; + } + *yylexp = (YYINT) YYLEX; + *yylvp++ = yylval; + yylve++; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + *yylpp++ = yylloc; + yylpe++; +#endif + yychar = *yylexp++; + break; + } + /* normal operation, no conflict encountered */ +#endif /* YYBTYACC */ + yychar = YYLEX; +#if YYBTYACC + } while (0); +#endif /* YYBTYACC */ + if (yychar < 0) yychar = YYEOF; +#if YYDEBUG + if (yydebug) + { + if ((yys = yyname[YYTRANSLATE(yychar)]) == NULL) yys = yyname[YYUNDFTOKEN]; + fprintf(stderr, "%s[%d]: state %d, reading token %d (%s)", + YYDEBUGSTR, yydepth, yystate, yychar, yys); +#ifdef YYSTYPE_TOSTRING +#if YYBTYACC + if (!yytrial) +#endif /* YYBTYACC */ + fprintf(stderr, " <%s>", YYSTYPE_TOSTRING(yychar, yylval)); +#endif + fputc('\n', stderr); + } +#endif + } +#if YYBTYACC + + /* Do we have a conflict? */ + if (((yyn = yycindex[yystate]) != 0) && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == (YYINT) yychar) + { + YYINT ctry; + + if (yypath) + { + YYParseState *save; +#if YYDEBUG + if (yydebug) + fprintf(stderr, "%s[%d]: CONFLICT in state %d: following successful trial parse\n", + YYDEBUGSTR, yydepth, yystate); +#endif + /* Switch to the next conflict context */ + save = yypath; + yypath = save->save; + save->save = NULL; + ctry = save->ctry; + if (save->state != yystate) YYABORT; + yyFreeState(save); + + } + else + { + + /* Unresolved conflict - start/continue trial parse */ + YYParseState *save; +#if YYDEBUG + if (yydebug) + { + fprintf(stderr, "%s[%d]: CONFLICT in state %d. ", YYDEBUGSTR, yydepth, yystate); + if (yyps->save) + fputs("ALREADY in conflict, continuing trial parse.\n", stderr); + else + fputs("Starting trial parse.\n", stderr); + } +#endif + save = yyNewState((unsigned)(yystack.s_mark - yystack.s_base + 1)); + if (save == NULL) goto yyenomem; + save->save = yyps->save; + save->state = yystate; + save->errflag = yyerrflag; + save->yystack.s_mark = save->yystack.s_base + (yystack.s_mark - yystack.s_base); + memcpy (save->yystack.s_base, yystack.s_base, (size_t) (yystack.s_mark - yystack.s_base + 1) * sizeof(YYINT)); + save->yystack.l_mark = save->yystack.l_base + (yystack.l_mark - yystack.l_base); + memcpy (save->yystack.l_base, yystack.l_base, (size_t) (yystack.l_mark - yystack.l_base + 1) * sizeof(YYSTYPE)); +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + save->yystack.p_mark = save->yystack.p_base + (yystack.p_mark - yystack.p_base); + memcpy (save->yystack.p_base, yystack.p_base, (size_t) (yystack.p_mark - yystack.p_base + 1) * sizeof(YYLTYPE)); +#endif + ctry = yytable[yyn]; + if (yyctable[ctry] == -1) + { +#if YYDEBUG + if (yydebug && yychar >= YYEOF) + fprintf(stderr, "%s[%d]: backtracking 1 token\n", YYDEBUGSTR, yydepth); +#endif + ctry++; + } + save->ctry = ctry; + if (yyps->save == NULL) + { + /* If this is a first conflict in the stack, start saving lexemes */ + if (!yylexemes) + { + yylexemes = (YYINT *) malloc((YYLVQUEUEGROWTH) * sizeof(YYINT)); + if (yylexemes == NULL) goto yyenomem; + yylvals = (YYSTYPE *) malloc((YYLVQUEUEGROWTH) * sizeof(YYSTYPE)); + if (yylvals == NULL) goto yyenomem; + yylvlim = yylvals + YYLVQUEUEGROWTH; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + yylpsns = (YYLTYPE *) malloc((YYLVQUEUEGROWTH) * sizeof(YYLTYPE)); + if (yylpsns == NULL) goto yyenomem; + yylplim = yylpsns + YYLVQUEUEGROWTH; +#endif + } + if (yylvp == yylve) + { + yylvp = yylve = yylvals; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + yylpp = yylpe = yylpsns; +#endif + yylexp = yylexemes; + if (yychar >= YYEOF) + { + *yylve++ = yylval; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + *yylpe++ = yylloc; +#endif + *yylexp = (YYINT) yychar; + yychar = YYEMPTY; + } + } + } + if (yychar >= YYEOF) + { + yylvp--; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + yylpp--; +#endif + yylexp--; + yychar = YYEMPTY; + } + save->lexeme = (int) (yylvp - yylvals); + yyps->save = save; + } + if (yytable[yyn] == ctry) + { +#if YYDEBUG + if (yydebug) + fprintf(stderr, "%s[%d]: state %d, shifting to state %d\n", + YYDEBUGSTR, yydepth, yystate, yyctable[ctry]); +#endif + if (yychar < 0) + { + yylvp++; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + yylpp++; +#endif + yylexp++; + } + if (yystack.s_mark >= yystack.s_last && yygrowstack(&yystack) == YYENOMEM) + goto yyoverflow; + yystate = yyctable[ctry]; + *++yystack.s_mark = (YYINT) yystate; + *++yystack.l_mark = yylval; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + *++yystack.p_mark = yylloc; +#endif + yychar = YYEMPTY; + if (yyerrflag > 0) --yyerrflag; + goto yyloop; + } + else + { + yyn = yyctable[ctry]; + goto yyreduce; + } + } /* End of code dealing with conflicts */ +#endif /* YYBTYACC */ + if (((yyn = yysindex[yystate]) != 0) && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == (YYINT) yychar) + { +#if YYDEBUG + if (yydebug) + fprintf(stderr, "%s[%d]: state %d, shifting to state %d\n", + YYDEBUGSTR, yydepth, yystate, yytable[yyn]); +#endif + if (yystack.s_mark >= yystack.s_last && yygrowstack(&yystack) == YYENOMEM) goto yyoverflow; + yystate = yytable[yyn]; + *++yystack.s_mark = yytable[yyn]; + *++yystack.l_mark = yylval; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + *++yystack.p_mark = yylloc; +#endif + yychar = YYEMPTY; + if (yyerrflag > 0) --yyerrflag; + goto yyloop; + } + if (((yyn = yyrindex[yystate]) != 0) && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == (YYINT) yychar) + { + yyn = yytable[yyn]; + goto yyreduce; + } + if (yyerrflag != 0) goto yyinrecovery; +#if YYBTYACC + + yynewerrflag = 1; + goto yyerrhandler; + goto yyerrlab; /* redundant goto avoids 'unused label' warning */ + +yyerrlab: + /* explicit YYERROR from an action -- pop the rhs of the rule reduced + * before looking for error recovery */ + yystack.s_mark -= yym; + yystate = *yystack.s_mark; + yystack.l_mark -= yym; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + yystack.p_mark -= yym; +#endif + + yynewerrflag = 0; +yyerrhandler: + while (yyps->save) + { + int ctry; + YYParseState *save = yyps->save; +#if YYDEBUG + if (yydebug) + fprintf(stderr, "%s[%d]: ERROR in state %d, CONFLICT BACKTRACKING to state %d, %d tokens\n", + YYDEBUGSTR, yydepth, yystate, yyps->save->state, + (int)(yylvp - yylvals - yyps->save->lexeme)); +#endif + /* Memorize most forward-looking error state in case it's really an error. */ + if (yyerrctx == NULL || yyerrctx->lexeme < yylvp - yylvals) + { + /* Free old saved error context state */ + if (yyerrctx) yyFreeState(yyerrctx); + /* Create and fill out new saved error context state */ + yyerrctx = yyNewState((unsigned)(yystack.s_mark - yystack.s_base + 1)); + if (yyerrctx == NULL) goto yyenomem; + yyerrctx->save = yyps->save; + yyerrctx->state = yystate; + yyerrctx->errflag = yyerrflag; + yyerrctx->yystack.s_mark = yyerrctx->yystack.s_base + (yystack.s_mark - yystack.s_base); + memcpy (yyerrctx->yystack.s_base, yystack.s_base, (size_t) (yystack.s_mark - yystack.s_base + 1) * sizeof(YYINT)); + yyerrctx->yystack.l_mark = yyerrctx->yystack.l_base + (yystack.l_mark - yystack.l_base); + memcpy (yyerrctx->yystack.l_base, yystack.l_base, (size_t) (yystack.l_mark - yystack.l_base + 1) * sizeof(YYSTYPE)); +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + yyerrctx->yystack.p_mark = yyerrctx->yystack.p_base + (yystack.p_mark - yystack.p_base); + memcpy (yyerrctx->yystack.p_base, yystack.p_base, (size_t) (yystack.p_mark - yystack.p_base + 1) * sizeof(YYLTYPE)); +#endif + yyerrctx->lexeme = (int) (yylvp - yylvals); + } + yylvp = yylvals + save->lexeme; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + yylpp = yylpsns + save->lexeme; +#endif + yylexp = yylexemes + save->lexeme; + yychar = YYEMPTY; + yystack.s_mark = yystack.s_base + (save->yystack.s_mark - save->yystack.s_base); + memcpy (yystack.s_base, save->yystack.s_base, (size_t) (yystack.s_mark - yystack.s_base + 1) * sizeof(YYINT)); + yystack.l_mark = yystack.l_base + (save->yystack.l_mark - save->yystack.l_base); + memcpy (yystack.l_base, save->yystack.l_base, (size_t) (yystack.l_mark - yystack.l_base + 1) * sizeof(YYSTYPE)); +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + yystack.p_mark = yystack.p_base + (save->yystack.p_mark - save->yystack.p_base); + memcpy (yystack.p_base, save->yystack.p_base, (size_t) (yystack.p_mark - yystack.p_base + 1) * sizeof(YYLTYPE)); +#endif + ctry = ++save->ctry; + yystate = save->state; + /* We tried shift, try reduce now */ + if ((yyn = yyctable[ctry]) >= 0) goto yyreduce; + yyps->save = save->save; + save->save = NULL; + yyFreeState(save); + + /* Nothing left on the stack -- error */ + if (!yyps->save) + { +#if YYDEBUG + if (yydebug) + fprintf(stderr, "%sdebug[%d,trial]: trial parse FAILED, entering ERROR mode\n", + YYPREFIX, yydepth); +#endif + /* Restore state as it was in the most forward-advanced error */ + yylvp = yylvals + yyerrctx->lexeme; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + yylpp = yylpsns + yyerrctx->lexeme; +#endif + yylexp = yylexemes + yyerrctx->lexeme; + yychar = yylexp[-1]; + yylval = yylvp[-1]; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + yylloc = yylpp[-1]; +#endif + yystack.s_mark = yystack.s_base + (yyerrctx->yystack.s_mark - yyerrctx->yystack.s_base); + memcpy (yystack.s_base, yyerrctx->yystack.s_base, (size_t) (yystack.s_mark - yystack.s_base + 1) * sizeof(YYINT)); + yystack.l_mark = yystack.l_base + (yyerrctx->yystack.l_mark - yyerrctx->yystack.l_base); + memcpy (yystack.l_base, yyerrctx->yystack.l_base, (size_t) (yystack.l_mark - yystack.l_base + 1) * sizeof(YYSTYPE)); +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + yystack.p_mark = yystack.p_base + (yyerrctx->yystack.p_mark - yyerrctx->yystack.p_base); + memcpy (yystack.p_base, yyerrctx->yystack.p_base, (size_t) (yystack.p_mark - yystack.p_base + 1) * sizeof(YYLTYPE)); +#endif + yystate = yyerrctx->state; + yyFreeState(yyerrctx); + yyerrctx = NULL; + } + yynewerrflag = 1; + } + if (yynewerrflag == 0) goto yyinrecovery; +#endif /* YYBTYACC */ + + YYERROR_CALL("syntax error"); +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + yyerror_loc_range[1] = yylloc; /* lookahead position is error start position */ +#endif + +#if !YYBTYACC + goto yyerrlab; /* redundant goto avoids 'unused label' warning */ +yyerrlab: +#endif + ++yynerrs; + +yyinrecovery: + if (yyerrflag < 3) + { + yyerrflag = 3; + for (;;) + { + if (((yyn = yysindex[*yystack.s_mark]) != 0) && (yyn += YYERRCODE) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == (YYINT) YYERRCODE) + { +#if YYDEBUG + if (yydebug) + fprintf(stderr, "%s[%d]: state %d, error recovery shifting to state %d\n", + YYDEBUGSTR, yydepth, *yystack.s_mark, yytable[yyn]); +#endif + if (yystack.s_mark >= yystack.s_last && yygrowstack(&yystack) == YYENOMEM) goto yyoverflow; + yystate = yytable[yyn]; + *++yystack.s_mark = yytable[yyn]; + *++yystack.l_mark = yylval; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + /* lookahead position is error end position */ + yyerror_loc_range[2] = yylloc; + YYLLOC_DEFAULT(yyloc, yyerror_loc_range, 2); /* position of error span */ + *++yystack.p_mark = yyloc; +#endif + goto yyloop; + } + else + { +#if YYDEBUG + if (yydebug) + fprintf(stderr, "%s[%d]: error recovery discarding state %d\n", + YYDEBUGSTR, yydepth, *yystack.s_mark); +#endif + if (yystack.s_mark <= yystack.s_base) goto yyabort; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + /* the current TOS position is the error start position */ + yyerror_loc_range[1] = *yystack.p_mark; +#endif +#if defined(YYDESTRUCT_CALL) +#if YYBTYACC + if (!yytrial) +#endif /* YYBTYACC */ +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + YYDESTRUCT_CALL("error: discarding state", + yystos[*yystack.s_mark], yystack.l_mark, yystack.p_mark); +#else + YYDESTRUCT_CALL("error: discarding state", + yystos[*yystack.s_mark], yystack.l_mark); +#endif /* defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) */ +#endif /* defined(YYDESTRUCT_CALL) */ + --yystack.s_mark; + --yystack.l_mark; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + --yystack.p_mark; +#endif + } + } + } + else + { + if (yychar == YYEOF) goto yyabort; +#if YYDEBUG + if (yydebug) + { + if ((yys = yyname[YYTRANSLATE(yychar)]) == NULL) yys = yyname[YYUNDFTOKEN]; + fprintf(stderr, "%s[%d]: state %d, error recovery discarding token %d (%s)\n", + YYDEBUGSTR, yydepth, yystate, yychar, yys); + } +#endif +#if defined(YYDESTRUCT_CALL) +#if YYBTYACC + if (!yytrial) +#endif /* YYBTYACC */ +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + YYDESTRUCT_CALL("error: discarding token", yychar, &yylval, &yylloc); +#else + YYDESTRUCT_CALL("error: discarding token", yychar, &yylval); +#endif /* defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) */ +#endif /* defined(YYDESTRUCT_CALL) */ + yychar = YYEMPTY; + goto yyloop; + } + +yyreduce: + yym = yylen[yyn]; +#if YYDEBUG + if (yydebug) + { + fprintf(stderr, "%s[%d]: state %d, reducing by rule %d (%s)", + YYDEBUGSTR, yydepth, yystate, yyn, yyrule[yyn]); +#ifdef YYSTYPE_TOSTRING +#if YYBTYACC + if (!yytrial) +#endif /* YYBTYACC */ + if (yym > 0) + { + int i; + fputc('<', stderr); + for (i = yym; i > 0; i--) + { + if (i != yym) fputs(", ", stderr); + fputs(YYSTYPE_TOSTRING(yystos[yystack.s_mark[1-i]], + yystack.l_mark[1-i]), stderr); + } + fputc('>', stderr); + } +#endif + fputc('\n', stderr); + } +#endif + if (yym > 0) + yyval = yystack.l_mark[1-yym]; + else + memset(&yyval, 0, sizeof yyval); +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + + /* Perform position reduction */ + memset(&yyloc, 0, sizeof(yyloc)); +#if YYBTYACC + if (!yytrial) +#endif /* YYBTYACC */ + { + YYLLOC_DEFAULT(yyloc, &yystack.p_mark[-yym], yym); + /* just in case YYERROR is invoked within the action, save + the start of the rhs as the error start position */ + yyerror_loc_range[1] = yystack.p_mark[1-yym]; + } +#endif + + switch (yyn) + { +case 3: +#line 157 "./parsdate.y" + { + yyHaveTime++; +#if defined(lint) + /* I am compulsive about lint natterings... */ + if (yyHaveTime == -1) { + YYERROR; + } +#endif /* defined(lint) */ + } +#line 1884 "y.tab.c" +break; +case 4: +#line 166 "./parsdate.y" + { + yyHaveTime++; + yyTimezone = yystack.l_mark[0].Number; + } +#line 1892 "y.tab.c" +break; +case 5: +#line 170 "./parsdate.y" + { + yyHaveDate++; + } +#line 1899 "y.tab.c" +break; +case 6: +#line 173 "./parsdate.y" + { + yyHaveDate++; + yyHaveTime++; + } +#line 1907 "y.tab.c" +break; +case 7: +#line 177 "./parsdate.y" + { + yyHaveDate++; + yyHaveTime++; + yyTimezone = yystack.l_mark[0].Number; + } +#line 1916 "y.tab.c" +break; +case 8: +#line 182 "./parsdate.y" + { + yyHaveRel = 1; + } +#line 1923 "y.tab.c" +break; +case 9: +#line 187 "./parsdate.y" + { + if (yystack.l_mark[-1].Number < 100) { + yyHour = yystack.l_mark[-1].Number; + yyMinutes = 0; + } + else { + yyHour = yystack.l_mark[-1].Number / 100; + yyMinutes = yystack.l_mark[-1].Number % 100; + } + yySeconds = 0; + yyMeridian = yystack.l_mark[0].Meridian; + } +#line 1939 "y.tab.c" +break; +case 10: +#line 199 "./parsdate.y" + { + yyHour = yystack.l_mark[-3].Number; + yyMinutes = yystack.l_mark[-1].Number; + yySeconds = 0; + yyMeridian = yystack.l_mark[0].Meridian; + } +#line 1949 "y.tab.c" +break; +case 11: +#line 205 "./parsdate.y" + { + yyHour = yystack.l_mark[-3].Number; + yyMinutes = yystack.l_mark[-1].Number; + yyTimezone = yystack.l_mark[0].Number; + yyMeridian = MER24; + yyDSTmode = DSToff; + } +#line 1960 "y.tab.c" +break; +case 12: +#line 212 "./parsdate.y" + { + yyHour = yystack.l_mark[-5].Number; + yyMinutes = yystack.l_mark[-3].Number; + yySeconds = yystack.l_mark[-1].Number; + yyMeridian = yystack.l_mark[0].Meridian; + } +#line 1970 "y.tab.c" +break; +case 13: +#line 218 "./parsdate.y" + { + yyHour = yystack.l_mark[-5].Number; + yyMinutes = yystack.l_mark[-3].Number; + yySeconds = yystack.l_mark[-1].Number; + yyTimezone = yystack.l_mark[0].Number; + yyMeridian = MER24; + yyDSTmode = DSToff; + } +#line 1982 "y.tab.c" +break; +case 14: +#line 228 "./parsdate.y" + { + yyval.Number = yystack.l_mark[0].Number; + yyDSTmode = DSToff; + } +#line 1990 "y.tab.c" +break; +case 15: +#line 232 "./parsdate.y" + { + yyval.Number = yystack.l_mark[0].Number; + yyDSTmode = DSTon; + } +#line 1998 "y.tab.c" +break; +case 16: +#line 236 "./parsdate.y" + { + yyTimezone = yystack.l_mark[-1].Number; + yyDSTmode = DSTon; + } +#line 2006 "y.tab.c" +break; +case 17: +#line 240 "./parsdate.y" + { + /* Only allow "GMT+300" and "GMT-0800" */ + if (yystack.l_mark[-1].Number != 0) { + YYABORT; + } + yyval.Number = yystack.l_mark[0].Number; + yyDSTmode = DSToff; + } +#line 2018 "y.tab.c" +break; +case 18: +#line 248 "./parsdate.y" + { + yyval.Number = yystack.l_mark[0].Number; + yyDSTmode = DSToff; + } +#line 2026 "y.tab.c" +break; +case 19: +#line 254 "./parsdate.y" + { + int i; + + /* Unix and GMT and numeric timezones -- a little confusing. */ + if ((long)yystack.l_mark[0].Number < 0) { + /* Don't work with negative modulus. */ + yystack.l_mark[0].Number = -(int)yystack.l_mark[0].Number; + if (yystack.l_mark[0].Number > 9999 || (i = (int) (yystack.l_mark[0].Number % 100)) >= 60) { + YYABORT; + } + yyval.Number = (yystack.l_mark[0].Number / 100) * 60 + i; + } + else { + if (yystack.l_mark[0].Number > 9999 || (i = (int) (yystack.l_mark[0].Number % 100)) >= 60) { + YYABORT; + } + yyval.Number = -((yystack.l_mark[0].Number / 100) * 60 + i); + } + } +#line 2049 "y.tab.c" +break; +case 20: +#line 275 "./parsdate.y" + { + yyMonth = yystack.l_mark[-2].Number; + yyDay = yystack.l_mark[0].Number; + } +#line 2057 "y.tab.c" +break; +case 21: +#line 279 "./parsdate.y" + { + if (yystack.l_mark[-4].Number > 100) { + yyYear = yystack.l_mark[-4].Number; + yyMonth = yystack.l_mark[-2].Number; + yyDay = yystack.l_mark[0].Number; + } + else { + yyMonth = yystack.l_mark[-4].Number; + yyDay = yystack.l_mark[-2].Number; + yyYear = yystack.l_mark[0].Number; + } + } +#line 2073 "y.tab.c" +break; +case 22: +#line 291 "./parsdate.y" + { + yyMonth = yystack.l_mark[-1].Number; + yyDay = yystack.l_mark[0].Number; + } +#line 2081 "y.tab.c" +break; +case 23: +#line 295 "./parsdate.y" + { + yyMonth = yystack.l_mark[-3].Number; + yyDay = yystack.l_mark[-2].Number; + yyYear = yystack.l_mark[0].Number; + } +#line 2090 "y.tab.c" +break; +case 24: +#line 300 "./parsdate.y" + { + yyDay = yystack.l_mark[-1].Number; + yyMonth = yystack.l_mark[0].Number; + } +#line 2098 "y.tab.c" +break; +case 25: +#line 304 "./parsdate.y" + { + yyDay = yystack.l_mark[-2].Number; + yyMonth = yystack.l_mark[-1].Number; + yyYear = yystack.l_mark[0].Number; + } +#line 2107 "y.tab.c" +break; +case 26: +#line 309 "./parsdate.y" + { + yyDay = yystack.l_mark[-2].Number; + yyMonth = yystack.l_mark[-1].Number; + yyYear = yystack.l_mark[0].Number; + } +#line 2116 "y.tab.c" +break; +case 27: +#line 314 "./parsdate.y" + { + yyDay = yystack.l_mark[-3].Number; + yyMonth = yystack.l_mark[-1].Number; + yyYear = -yystack.l_mark[0].Number; + } +#line 2125 "y.tab.c" +break; +case 28: +#line 319 "./parsdate.y" + { + yyDay = yystack.l_mark[-2].Number; + yyMonth = -yystack.l_mark[-1].Number; + yyYear = -yystack.l_mark[0].Number; + yyDSTmode = DSToff; /* assume midnight if no time given */ + yyTimezone = 0; /* Lynx assumes GMT for this format */ + } +#line 2136 "y.tab.c" +break; +case 29: +#line 328 "./parsdate.y" + { + yyMonth = yystack.l_mark[-7].Number; + yyDay = yystack.l_mark[-6].Number; + yyYear = yystack.l_mark[0].Number; + yyHour = yystack.l_mark[-5].Number; + yyMinutes = yystack.l_mark[-3].Number; + yySeconds = yystack.l_mark[-1].Number; + } +#line 2148 "y.tab.c" +break; +case 30: +#line 338 "./parsdate.y" + { + yyRelSeconds += yystack.l_mark[-1].Number * yystack.l_mark[0].Number; + } +#line 2155 "y.tab.c" +break; +case 31: +#line 341 "./parsdate.y" + { + yyRelSeconds += yystack.l_mark[-1].Number * yystack.l_mark[0].Number; + } +#line 2162 "y.tab.c" +break; +case 32: +#line 344 "./parsdate.y" + { + yyRelMonth += yystack.l_mark[-1].Number * yystack.l_mark[0].Number; + } +#line 2169 "y.tab.c" +break; +case 33: +#line 347 "./parsdate.y" + { + yyRelMonth += yystack.l_mark[-1].Number * yystack.l_mark[0].Number; + } +#line 2176 "y.tab.c" +break; +case 34: +#line 352 "./parsdate.y" + { + yyval.Meridian = MER24; + } +#line 2183 "y.tab.c" +break; +case 35: +#line 355 "./parsdate.y" + { + yyval.Meridian = yystack.l_mark[0].Meridian; + } +#line 2190 "y.tab.c" +break; +#line 2192 "y.tab.c" + default: + break; + } + yystack.s_mark -= yym; + yystate = *yystack.s_mark; + yystack.l_mark -= yym; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + yystack.p_mark -= yym; +#endif + yym = yylhs[yyn]; + if (yystate == 0 && yym == 0) + { +#if YYDEBUG + if (yydebug) + { + fprintf(stderr, "%s[%d]: after reduction, ", YYDEBUGSTR, yydepth); +#ifdef YYSTYPE_TOSTRING +#if YYBTYACC + if (!yytrial) +#endif /* YYBTYACC */ + fprintf(stderr, "result is <%s>, ", YYSTYPE_TOSTRING(yystos[YYFINAL], yyval)); +#endif + fprintf(stderr, "shifting from state 0 to final state %d\n", YYFINAL); + } +#endif + yystate = YYFINAL; + *++yystack.s_mark = YYFINAL; + *++yystack.l_mark = yyval; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + *++yystack.p_mark = yyloc; +#endif + if (yychar < 0) + { +#if YYBTYACC + do { + if (yylvp < yylve) + { + /* we're currently re-reading tokens */ + yylval = *yylvp++; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + yylloc = *yylpp++; +#endif + yychar = *yylexp++; + break; + } + if (yyps->save) + { + /* in trial mode; save scanner results for future parse attempts */ + if (yylvp == yylvlim) + { /* Enlarge lexical value queue */ + size_t p = (size_t) (yylvp - yylvals); + size_t s = (size_t) (yylvlim - yylvals); + + s += YYLVQUEUEGROWTH; + if ((yylexemes = (YYINT *)realloc(yylexemes, s * sizeof(YYINT))) == NULL) + goto yyenomem; + if ((yylvals = (YYSTYPE *)realloc(yylvals, s * sizeof(YYSTYPE))) == NULL) + goto yyenomem; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + if ((yylpsns = (YYLTYPE *)realloc(yylpsns, s * sizeof(YYLTYPE))) == NULL) + goto yyenomem; +#endif + yylvp = yylve = yylvals + p; + yylvlim = yylvals + s; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + yylpp = yylpe = yylpsns + p; + yylplim = yylpsns + s; +#endif + yylexp = yylexemes + p; + } + *yylexp = (YYINT) YYLEX; + *yylvp++ = yylval; + yylve++; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + *yylpp++ = yylloc; + yylpe++; +#endif + yychar = *yylexp++; + break; + } + /* normal operation, no conflict encountered */ +#endif /* YYBTYACC */ + yychar = YYLEX; +#if YYBTYACC + } while (0); +#endif /* YYBTYACC */ + if (yychar < 0) yychar = YYEOF; +#if YYDEBUG + if (yydebug) + { + if ((yys = yyname[YYTRANSLATE(yychar)]) == NULL) yys = yyname[YYUNDFTOKEN]; + fprintf(stderr, "%s[%d]: state %d, reading token %d (%s)\n", + YYDEBUGSTR, yydepth, YYFINAL, yychar, yys); + } +#endif + } + if (yychar == YYEOF) goto yyaccept; + goto yyloop; + } + if (((yyn = yygindex[yym]) != 0) && (yyn += yystate) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == (YYINT) yystate) + yystate = yytable[yyn]; + else + yystate = yydgoto[yym]; +#if YYDEBUG + if (yydebug) + { + fprintf(stderr, "%s[%d]: after reduction, ", YYDEBUGSTR, yydepth); +#ifdef YYSTYPE_TOSTRING +#if YYBTYACC + if (!yytrial) +#endif /* YYBTYACC */ + fprintf(stderr, "result is <%s>, ", YYSTYPE_TOSTRING(yystos[yystate], yyval)); +#endif + fprintf(stderr, "shifting from state %d to state %d\n", *yystack.s_mark, yystate); + } +#endif + if (yystack.s_mark >= yystack.s_last && yygrowstack(&yystack) == YYENOMEM) goto yyoverflow; + *++yystack.s_mark = (YYINT) yystate; + *++yystack.l_mark = yyval; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + *++yystack.p_mark = yyloc; +#endif + goto yyloop; +#if YYBTYACC + + /* Reduction declares that this path is valid. Set yypath and do a full parse */ +yyvalid: + if (yypath) YYABORT; + while (yyps->save) + { + YYParseState *save = yyps->save; + yyps->save = save->save; + save->save = yypath; + yypath = save; + } +#if YYDEBUG + if (yydebug) + fprintf(stderr, "%s[%d]: state %d, CONFLICT trial successful, backtracking to state %d, %d tokens\n", + YYDEBUGSTR, yydepth, yystate, yypath->state, (int)(yylvp - yylvals - yypath->lexeme)); +#endif + if (yyerrctx) + { + yyFreeState(yyerrctx); + yyerrctx = NULL; + } + yylvp = yylvals + yypath->lexeme; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + yylpp = yylpsns + yypath->lexeme; +#endif + yylexp = yylexemes + yypath->lexeme; + yychar = YYEMPTY; + yystack.s_mark = yystack.s_base + (yypath->yystack.s_mark - yypath->yystack.s_base); + memcpy (yystack.s_base, yypath->yystack.s_base, (size_t) (yystack.s_mark - yystack.s_base + 1) * sizeof(YYINT)); + yystack.l_mark = yystack.l_base + (yypath->yystack.l_mark - yypath->yystack.l_base); + memcpy (yystack.l_base, yypath->yystack.l_base, (size_t) (yystack.l_mark - yystack.l_base + 1) * sizeof(YYSTYPE)); +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + yystack.p_mark = yystack.p_base + (yypath->yystack.p_mark - yypath->yystack.p_base); + memcpy (yystack.p_base, yypath->yystack.p_base, (size_t) (yystack.p_mark - yystack.p_base + 1) * sizeof(YYLTYPE)); +#endif + yystate = yypath->state; + goto yyloop; +#endif /* YYBTYACC */ + +yyoverflow: + YYERROR_CALL("yacc stack overflow"); +#if YYBTYACC + goto yyabort_nomem; +yyenomem: + YYERROR_CALL("memory exhausted"); +yyabort_nomem: +#endif /* YYBTYACC */ + yyresult = 2; + goto yyreturn; + +yyabort: + yyresult = 1; + goto yyreturn; + +yyaccept: +#if YYBTYACC + if (yyps->save) goto yyvalid; +#endif /* YYBTYACC */ + yyresult = 0; + +yyreturn: +#if defined(YYDESTRUCT_CALL) + if (yychar != YYEOF && yychar != YYEMPTY) +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + YYDESTRUCT_CALL("cleanup: discarding token", yychar, &yylval, &yylloc); +#else + YYDESTRUCT_CALL("cleanup: discarding token", yychar, &yylval); +#endif /* defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) */ + + { + YYSTYPE *pv; +#if defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) + YYLTYPE *pp; + + for (pv = yystack.l_base, pp = yystack.p_base; pv <= yystack.l_mark; ++pv, ++pp) + YYDESTRUCT_CALL("cleanup: discarding state", + yystos[*(yystack.s_base + (pv - yystack.l_base))], pv, pp); +#else + for (pv = yystack.l_base; pv <= yystack.l_mark; ++pv) + YYDESTRUCT_CALL("cleanup: discarding state", + yystos[*(yystack.s_base + (pv - yystack.l_base))], pv); +#endif /* defined(YYLTYPE) || defined(YYLTYPE_IS_DECLARED) */ + } +#endif /* defined(YYDESTRUCT_CALL) */ + +#if YYBTYACC + if (yyerrctx) + { + yyFreeState(yyerrctx); + yyerrctx = NULL; + } + while (yyps) + { + YYParseState *save = yyps; + yyps = save->save; + save->save = NULL; + yyFreeState(save); + } + while (yypath) + { + YYParseState *save = yypath; + yypath = save->save; + save->save = NULL; + yyFreeState(save); + } +#endif /* YYBTYACC */ + yyfreestack(&yystack); + return (yyresult); +} diff --git a/src/parsdate.h b/src/parsdate.h new file mode 100644 index 0000000..9853f50 --- /dev/null +++ b/src/parsdate.h @@ -0,0 +1,21 @@ +/* $LynxId: parsdate.h,v 1.2 2010/10/31 17:56:22 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 0000000..faab5e5 --- /dev/null +++ b/src/parsdate.y @@ -0,0 +1,991 @@ +%{ + +#include <LYLeaks.h> + +/* + * $LynxId: parsdate.y,v 1.32 2024/01/15 11:09:08 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 : 2019-08-28 (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 + +#define BAD_TIME ((time_t)-1) +#define isBadTime(n) ((n) != 0 && (((n) == BAD_TIME) || !((n) > 0))) + + /* See the LeapYears table in Convert. */ +#define EPOCH 1970 +#define END_OF_TIME 2200 + + /* 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 ((long)$1 < 0) { + /* Don't work with negative modulus. */ + $1 = -(int)$1; + if ($1 > 9999 || (i = (int) ($1 % 100)) >= 60) { + YYABORT; + } + $$ = ($1 / 100) * 60 + i; + } + else { + if ($1 > 9999 || (i = (int) ($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. +*/ +/* *INDENT-OFF* */ +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 */ +}; +/* *INDENT-ON* */ + +static time_t ToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian) +{ + if (isBadTime(Minutes) || Minutes > 59 || isBadTime(Seconds) || Seconds > 61) + return BAD_TIME; + if (Meridian == MER24) { + if (isBadTime(Hours) || Hours > 23) + return BAD_TIME; + } else { + if (Hours < 1 || Hours > 12) + return BAD_TIME; + 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 + }; + /* *INDENT-OFF* */ + static const int LeapYears[] = + { + 1972, 1976, 1980, 1984, 1988, 1992, 1996, + 2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028, 2032, 2036, + 2040, 2044, 2048, 2052, 2056, 2060, 2064, 2068, 2072, 2076, + 2080, 2084, 2088, 2092, 2096, /***/ 2104, 2108, 2112, 2116, + 2120, 2124, 2128, 2132, 2136, 2140, 2144, 2148, 2152, 2156, + 2160, 2164, 2168, 2172, 2176, 2180, 2184, 2188, 2192, 2196, + }; + /* *INDENT-ON* */ + + const int *yp; + const int *mp; + int i; + time_t Julian; + time_t tod; + + if (isBadTime(Year)) + 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 BAD_TIME; + } + + 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; + tod = ToSeconds(Hours, Minutes, Seconds, Meridian); + if (isBadTime(tod)) { + return BAD_TIME; + } + Julian += tod; + tod = Julian; + if (dst == DSTon) { + Julian -= DST_OFFSET * 60 * 60; + } else if (dst == DSTmaybe) { + struct tm *tm = localtime(&tod); + + if (tm != NULL) { + if (tm->tm_isdst) + Julian -= DST_OFFSET * 60 * 60; + } else { + Julian = BAD_TIME; + } + } + return Julian; +} + +static time_t DSTcorrect(time_t Start, time_t Future) +{ + struct tm *tm; + time_t StartDay; + time_t FutureDay; + + if ((tm = localtime(&Start)) == NULL) + return BAD_TIME; + StartDay = (tm->tm_hour + 1) % 24; + + if ((tm = localtime(&Future)) == NULL) + return BAD_TIME; + FutureDay = (tm->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; + + if ((tm = localtime(&Start)) == NULL) + return BAD_TIME; + + 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; + + /* 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, (int) (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. */ + memset(Now, 0, sizeof(TIMEINFO)); +#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; +} + +#if defined(YYBYACC) && defined(YYPURE) && defined(LY_FIND_LEAKS) +#undef YYPURE +#define YYPURE 1 +static void yyfreestack(YYSTACKDATA *); +static void parsedate_leaks(void) +{ + yyfreestack(&yystack); +} +#endif + +time_t parsedate(char *p, + TIMEINFO * now) +{ + struct tm *tm; + TIMEINFO ti; + time_t Start; + +#if defined(YYBYACC) && defined(YYPURE) && defined(LY_FIND_LEAKS) + static int initialized; + + if (!initialized) { + initialized = 1; + atexit(parsedate_leaks); + } +#endif + + yyInput = p; + if (now == NULL) { + now = &ti; + (void) GetTimeInfo(&ti); + } + + if ((tm = localtime(&now->time)) == NULL) + return BAD_TIME; + + /* *INDENT-EQLS* */ + 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 BAD_TIME; + + if (yyHaveDate || yyHaveTime) { + Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, + yyMeridian, yyDSTmode); + if (isBadTime(Start)) + return BAD_TIME; + } 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 == BAD_TIME) ? 0 : Start; +} diff --git a/src/socketshr_tcp.opt b/src/socketshr_tcp.opt new file mode 100644 index 0000000..f6e3131 --- /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 0000000..7dcd7d5 --- /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 0000000..78e25db --- /dev/null +++ b/src/structdump.h @@ -0,0 +1,164 @@ +/* + * + * 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=%lu ["X"]\n" \ + "TextAnchor struct {\n" \ + " *next=%p\n" \ + " *prev=%p\n" \ + " number=%d\n" \ + " line_pos=%d\n" \ + " extent=%d\n" \ + " line_num=%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), (unsigned long) sizeof(*((A))), \ + (A)->next, (A)->prev, \ + (A)->number, (A)->line_pos, \ + (A)->extent, (A)->line_num, \ + (A)->link_type, \ + (A)->input_field, \ + (A)->input_field ? NonNull((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=%lu ["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=%lu\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" \ + " readonly=%d\n" \ + " name_cs=%d\n" \ + " value_cs=%d\n" \ + " accept_cs=|%s|\n" \ + "}\n", \ + (F), (unsigned long) sizeof(*((F))), \ + (F)->name, NonNull((F)->name), \ + (F)->number, (F)->type, \ + (F)->value, NonNull((F)->value), \ + (F)->orig_value, NonNull((F)->orig_value), \ + (F)->size, (unsigned long) (F)->maxlength, \ + (F)->group, (F)->num_value, \ + (F)->hrange, (F)->lrange, (F)->select_list, \ + NonNull((F)->submit_action), \ + (F)->submit_method, \ + NonNull((F)->submit_enctype), \ + NonNull((F)->submit_title), \ + (F)->no_cache, \ + NonNull((F)->cp_submit_value), \ + NonNull((F)->orig_submit_value), \ + (F)->size_l, (F)->disabled, (F)->readonly, (F)->name_cs, (F)->value_cs, \ + NonNull((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 0000000..9c87cc8 --- /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 0000000..729be9f --- /dev/null +++ b/src/tcpipshr.opt @@ -0,0 +1 @@ +tcpip$ipc_shr/share diff --git a/src/tcpwareolb.opt b/src/tcpwareolb.opt new file mode 100644 index 0000000..045d770 --- /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 0000000..e34f846 --- /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 0000000..b0288fb --- /dev/null +++ b/src/tidy_tls.c @@ -0,0 +1,707 @@ +/* + * $LynxId: tidy_tls.c,v 1.41 2020/03/03 11:46:07 Gisle.Vanem Exp $ + * Copyright 2008-2019,2020 Thomas E. Dickey + * with fix Copyright 2008 by Thomas Viehmann + * + * Required libraries: + * libgnutls + * libcrypt + */ +#include <HTUtils.h> +#include <tidy_tls.h> + +#include <gnutls/x509.h> +#ifdef HAVE_GNUTLS_RND +#include <gnutls/crypto.h> +#else +#include <gcrypt.h> +#endif +#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((int) -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 = (unsigned long) (-last_error); + last_error = 0; + + return rc; +} + +/* + * Put 'num' cryptographically strong pseudo-random bytes into 'buffer'. + */ +int RAND_bytes(unsigned char *buffer, int num) +{ + int rc; + +#ifdef HAVE_GNUTLS_RND + rc = gnutls_rnd(GNUTLS_RND_KEY, buffer, (size_t) num); +#else + gcry_randomize(buffer, num, GCRY_VERY_STRONG_RANDOM); + rc = 1; +#endif + return rc; +} + +/* + * (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 (int) 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 * (int) 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; + + if ((ctx = typeCalloc(SSL_CTX)) != 0) { + 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; +} + +#ifdef HAVE_GNUTLS_PROTOCOL_SET_PRIORITY +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); + } +} +#endif + +/* + * Initiate the TLS/SSL handshake with an TLS/SSL server. + */ +int SSL_connect(SSL * ssl) +{ + X509_STORE_CTX *store; + int rc; + gnutls_alert_description_t alert; + const char *aname; + + if (ssl->options & SSL_OP_NO_TLSv1) { +#ifdef HAVE_GNUTLS_PROTOCOL_SET_PRIORITY + RemoveProtocol(ssl, GNUTLS_TLS1); +#else + gnutls_priority_set_direct(ssl->gnutls_state, "NORMAL:-VERS-TLS1.0", NULL); +#endif + } + + while ((rc = gnutls_handshake(ssl->gnutls_state)) < 0 && + !gnutls_error_is_fatal(rc)) { + if (rc == GNUTLS_E_WARNING_ALERT_RECEIVED) { + alert = gnutls_alert_get(ssl->gnutls_state); + aname = gnutls_alert_get_name(alert); + CTRACE((tfp, "SSL Alert: %s\n", NonNull(aname))); + switch (gnutls_alert_get(ssl->gnutls_state)) { + case GNUTLS_A_UNRECOGNIZED_NAME: + continue; /* ignore */ + default: + break; + } + break; /* treat all other alerts as fatal */ + } + } + ssl->last_error = rc; + + if (rc < 0) { + last_error = rc; + return 0; + } + + store = typeCalloc(X509_STORE_CTX); + if (store == 0) + outofmem(__FILE__, "SSL_connect"); + + 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->cert = gnutls_certificate_type_get(ssl->gnutls_state); +#if !defined(_GNUTLS_GCC_VERSION) || (_GNUTLS_GCC_VERSION < 30100) + result->compress = gnutls_compression_get(ssl->gnutls_state); +#else + result->compress = GNUTLS_COMP_UNKNOWN; +#endif + } + + return result; +} + +/* + * Get the X509 certificate of the peer. + */ +const X509 *SSL_get_peer_certificate(SSL * ssl) +{ + const gnutls_datum_t *result; + unsigned list_size = 0; + + result = + (const gnutls_datum_t *) gnutls_certificate_get_peers(ssl->gnutls_state, + &list_size); + + return (const 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 { + ssl->ctx = ctx; + + gnutls_init(&ssl->gnutls_state, ctx->method->connend); + gnutls_set_default_priority(ssl->gnutls_state); + + 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->client_keyfile) + gnutls_certificate_set_x509_key_file(ssl->gnutls_cred, + ctx->client_certfile, + ctx->client_keyfile, + ctx->client_keyfile_type); + 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); + ssl->bytes_sent = 0; + ssl->sendbuffer = 0; + } + } + + 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; + + do { + rc = (int) gnutls_record_recv(ssl->gnutls_state, buffer, (size_t) length); + } + while ((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)); + + if (rc < 0 && gnutls_error_is_fatal(rc) == 0) { + if (rc == GNUTLS_E_REHANDSHAKE) { + (void) gnutls_handshake(ssl->gnutls_state); + do { + rc = (int) gnutls_record_send(ssl->gnutls_state, + ssl->sendbuffer, + (size_t) ssl->bytes_sent); + } + while ((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)); + do { + rc = (int) gnutls_record_recv(ssl->gnutls_state, + buffer, + (size_t) length); + } + while ((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)); + } + } + + ssl->last_error = rc; + + if (rc < 0) { + last_error = rc; + rc = 0; + } + + return rc; +} + +#ifdef _WINDOWS +static int Lynx_gtls_push(void *s, const void *buf, size_t len) +{ + return NETWRITE((SOCKET) s, buf, len); +} + +/* This calls 'recv()' in a thread for every GnuTls pull. Maybe too much overhead? + */ +static int Lynx_gtls_pull(void *s, void *buf, size_t len) +{ + return NETREAD((SOCKET) s, buf, len); +} +#endif + +/* + * 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) +{ +#ifdef _WINDOWS + /* register callback functions to send and receive data. */ + gnutls_transport_set_push_function(ssl->gnutls_state, Lynx_gtls_push); + gnutls_transport_set_pull_function(ssl->gnutls_state, Lynx_gtls_pull); +#endif + + gnutls_transport_set_ptr(ssl->gnutls_state, + (gnutls_transport_ptr_t) (intptr_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; + + do { + rc = (int) gnutls_record_send(ssl->gnutls_state, buffer, (size_t) length); + } + while ((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)); + ssl->last_error = rc; + + if (rc < 0) { + last_error = rc; + rc = 0; + } else { + size_t need = (size_t) rc; + + free(ssl->sendbuffer); + ssl->sendbuffer = malloc(need); + ssl->bytes_sent = need; + } + + return rc; +} + +/* + * Return method-data for SSL version 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) { + int n; + + /* + * List the protocols in decreasing order of priority. + */ + n = 0; +#if GNUTLS_VERSION_NUMBER >= 0x030000 + m->priority.protocol[n++] = GNUTLS_SSL3; + m->priority.protocol[n++] = GNUTLS_TLS1_2; +#endif + m->priority.protocol[n++] = GNUTLS_TLS1_1; + m->priority.protocol[n++] = GNUTLS_TLS1_0; + m->priority.protocol[n] = 0; + + /* + * List the cipher algorithms in decreasing order of priority. + */ + n = 0; +#if GNUTLS_VERSION_NUMBER >= 0x030000 + m->priority.encrypts[n++] = GNUTLS_CIPHER_AES_256_GCM; + m->priority.encrypts[n++] = GNUTLS_CIPHER_AES_128_GCM; +#endif + m->priority.encrypts[n++] = GNUTLS_CIPHER_AES_256_CBC; + m->priority.encrypts[n++] = GNUTLS_CIPHER_AES_128_CBC; + m->priority.encrypts[n++] = GNUTLS_CIPHER_CAMELLIA_256_CBC; + m->priority.encrypts[n++] = GNUTLS_CIPHER_CAMELLIA_128_CBC; + m->priority.encrypts[n++] = GNUTLS_CIPHER_3DES_CBC; + m->priority.encrypts[n] = 0; + + /* + * List the compression algorithms in decreasing order of priority. + */ + n = 0; + m->priority.compress[n++] = GNUTLS_COMP_NULL; + m->priority.compress[n] = 0; + + /* + * List the key exchange algorithms in decreasing order of priority. + */ + n = 0; + m->priority.key_xchg[n++] = GNUTLS_KX_DHE_RSA; + m->priority.key_xchg[n++] = GNUTLS_KX_RSA; + m->priority.key_xchg[n++] = GNUTLS_KX_DHE_DSS; + m->priority.key_xchg[n] = 0; + + /* + * List message authentication code (MAC) algorithms in decreasing + * order of priority to specify via gnutls_mac_set_priority(). + */ + n = 0; + m->priority.msg_code[n++] = GNUTLS_MAC_SHA1; + m->priority.msg_code[n++] = GNUTLS_MAC_MD5; + m->priority.msg_code[n] = 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) +{ + if (*data != '\0') { + int need = (int) strlen(tag) + 2; + + target += strlen(target); + if (need < len) { + strcat(target, "/"); + strcat(target, tag); + strcat(target, "="); + len -= need; + target += need; + } + need = (int) strlen(data); + if (need >= len - 1) + need = len - 1; + strncat(target, data, (size_t) 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) +{ + static X509_NAME *result; + + free(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) +{ + static X509_NAME *result; + + free(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 0000000..2c7cb54 --- /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 0000000..ba84be0 --- /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 0000000..fbb523b --- /dev/null +++ b/src/vaxc.opt @@ -0,0 +1,2 @@ +sys$share:vaxcrtl/share +sys$library:vaxccurse/library diff --git a/src/wcwidth.c b/src/wcwidth.c new file mode 100644 index 0000000..ea46336 --- /dev/null +++ b/src/wcwidth.c @@ -0,0 +1,709 @@ +/* $XTermId: wcwidth.c,v 1.42 2018/12/04 10:13:14 tom Exp $ */ + +/* $XFree86: xc/programs/xterm/wcwidth.c,v 1.9 2006/06/19 00:36:52 dickey Exp $ */ + +/* + * Copyright 2002-2017,2018 by Thomas E. Dickey + * + * All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name(s) of the above copyright + * holders shall not be used in advertising or otherwise to promote the + * sale, use or other dealings in this Software without prior written + * authorization. + *----------------------------------------------------------------------------- + * This is an updated version of Markus Kuhn's implementation of wcwidth. + * + * Originally added to xterm in 2000 (patch #141), there were a couple of + * updates from Kuhn until 2005 (patch #202), renaming entrypoints and applying + * data from Unicode.org (e.g., 3.2, 4.0, 4.1.0). The Unicode data is + * transformed into tables in this file by a script "uniset" written by Kuhn. + * + * While Kuhn implemented the original CJK variant, it was unused by xterm + * until Jungshik Shin used it in 2002 to implement the -cjk_width command-line + * option. + * + * Kuhn added a check for the vertical forms block (double-width) in 2007; + * other updates were derived from the Unicode.org data (release 5.0). + * + * Since then, additional updates have been made: + * + data-type fixes + * + new Unicode releases (6.2.0, 9.0.0), + * + additional special symbol blocks have been added to the special cases. + * + soft-hyphen behavior has been made configurable. + * + added table shows when a character is not part of Unicode. + * + * Kuhn's original header follows giving the design information: + *----------------------------------------------------------------------------- + * This is an implementation of wcwidth() and wcswidth() (defined in + * IEEE Std 1002.1-2001) for Unicode. + * + * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html + * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html + * + * In fixed-width output devices, Latin characters all occupy a single + * "cell" position of equal width, whereas ideographic CJK characters + * occupy two such cells. Interoperability between terminal-line + * applications and (teletype-style) character terminals using the + * UTF-8 encoding requires agreement on which character should advance + * the cursor by how many cell positions. No established formal + * standards exist at present on which Unicode character shall occupy + * how many cell positions on character terminals. These routines are + * a first attempt of defining such behavior based on simple rules + * applied to data provided by the Unicode Consortium. + * + * For some graphical characters, the Unicode standard explicitly + * defines a character-cell width via the definition of the East Asian + * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes. + * In all these cases, there is no ambiguity about which width a + * terminal shall use. For characters in the East Asian Ambiguous (A) + * class, the width choice depends purely on a preference of backward + * compatibility with either historic CJK or Western practice. + * Choosing single-width for these characters is easy to justify as + * the appropriate long-term solution, as the CJK practice of + * displaying these characters as double-width comes from historic + * implementation simplicity (8-bit encoded characters were displayed + * single-width and 16-bit ones double-width, even for Greek, + * Cyrillic, etc.) and not any typographic considerations. + * + * Much less clear is the choice of width for the Not East Asian + * (Neutral) class. Existing practice does not dictate a width for any + * of these characters. It would nevertheless make sense + * typographically to allocate two character cells to characters such + * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be + * represented adequately with a single-width glyph. The following + * routines at present merely assign a single-cell width to all + * neutral characters, in the interest of simplicity. This is not + * entirely satisfactory and should be reconsidered before + * establishing a formal standard in this area. At the moment, the + * decision which Not East Asian (Neutral) characters should be + * represented by double-width glyphs cannot yet be answered by + * applying a simple rule from the Unicode database content. Setting + * up a proper standard for the behavior of UTF-8 character terminals + * will require a careful analysis not only of each Unicode character, + * but also of each presentation form, something the author of these + * routines has avoided to do so far. + * + * http://www.unicode.org/unicode/reports/tr11/ + * + * Markus Kuhn -- 2007-05-25 (Unicode 5.0) + * + * 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/wcwidth.c + */ + +#include <wcwidth.h> + +struct interval { + unsigned long first; + unsigned long last; +}; + +static int use_latin1 = 1; + +/* auxiliary function for binary search in interval table */ +static int bisearch(unsigned long ucs, const struct interval *table, int max) { + + if (ucs >= table[0].first && ucs <= table[max].last) { + int min = 0; + + while (max >= min) { + int mid; + + mid = (min + max) / 2; + if (ucs > table[mid].last) + min = mid + 1; + else if (ucs < table[mid].first) + max = mid - 1; + else + return 1; + } + } + + return 0; +} + +/* + * Provide a way to change the behavior of soft-hyphen. + */ +void +mk_wcwidth_init(int mode) +{ + use_latin1 = (mode == 0); +} + +/* The following two functions define the column width of an ISO 10646 + * character as follows: + * + * - The null character (U+0000) has a column width of 0. + * + * - Other C0/C1 control characters and DEL will lead to a return + * value of -1. + * + * - Non-spacing and enclosing combining characters (general + * category code Mn or Me in the Unicode database) have a + * column width of 0. + * + * - A few spacing combining marks have a column width of 0. + * + * - SOFT HYPHEN (U+00AD) has a column width of 1 in Latin-1, 0 in Unicode. + * An initialization function is used to switch between the two. + * + * - Other format characters (general category code Cf in the Unicode + * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. + * + * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) + * have a column width of 0. + * + * - Spacing characters in the East Asian Wide (W) or East Asian + * Full-width (F) category as defined in Unicode Technical + * Report #11 have a column width of 2. In that report, some codes + * were unassigned. Characters in these blocks use a column width of 1: + * 4DC0..4DFF; Yijing Hexagram Symbols + * A960..A97F; Hangul Jamo Extended-A + * + * - All remaining characters (including all printable + * ISO 8859-1 and WGL4 characters, Unicode control characters, + * etc.) have a column width of 1. + * + * - Codes which do not correspond to a Unicode character have a column + * width of -1. + * + * This implementation assumes that wchar_t characters are encoded + * in ISO 10646. + */ + +int mk_wcwidth(wchar_t ucs) +{ + unsigned long cmp = (unsigned long) ucs; + + /* sorted list of non-overlapping intervals of non-spacing characters */ + /* generated by + * uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c + */ + static const struct interval combining[] = { + { 0x0300, 0x036F }, { 0x0483, 0x0489 }, { 0x0591, 0x05BD }, + { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, { 0x05C4, 0x05C5 }, + { 0x05C7, 0x05C7 }, { 0x0600, 0x0605 }, { 0x0610, 0x061A }, + { 0x061C, 0x061C }, { 0x064B, 0x065F }, { 0x0670, 0x0670 }, + { 0x06D6, 0x06DD }, { 0x06DF, 0x06E4 }, { 0x06E7, 0x06E8 }, + { 0x06EA, 0x06ED }, { 0x070F, 0x070F }, { 0x0711, 0x0711 }, + { 0x0730, 0x074A }, { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, + { 0x0816, 0x0819 }, { 0x081B, 0x0823 }, { 0x0825, 0x0827 }, + { 0x0829, 0x082D }, { 0x0859, 0x085B }, { 0x08D4, 0x0902 }, + { 0x093A, 0x093A }, { 0x093C, 0x093C }, { 0x0941, 0x0948 }, + { 0x094D, 0x094D }, { 0x0951, 0x0957 }, { 0x0962, 0x0963 }, + { 0x0981, 0x0981 }, { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, + { 0x09CD, 0x09CD }, { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, + { 0x0A3C, 0x0A3C }, { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, + { 0x0A4B, 0x0A4D }, { 0x0A51, 0x0A51 }, { 0x0A70, 0x0A71 }, + { 0x0A75, 0x0A75 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, + { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, + { 0x0AE2, 0x0AE3 }, { 0x0AFA, 0x0AFF }, { 0x0B01, 0x0B01 }, + { 0x0B3C, 0x0B3C }, { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B44 }, + { 0x0B4D, 0x0B4D }, { 0x0B56, 0x0B56 }, { 0x0B62, 0x0B63 }, + { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, { 0x0BCD, 0x0BCD }, + { 0x0C00, 0x0C00 }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, + { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0C62, 0x0C63 }, + { 0x0C81, 0x0C81 }, { 0x0CBC, 0x0CBC }, { 0x0CBF, 0x0CBF }, + { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, { 0x0CE2, 0x0CE3 }, + { 0x0D00, 0x0D01 }, { 0x0D3B, 0x0D3C }, { 0x0D41, 0x0D44 }, + { 0x0D4D, 0x0D4D }, { 0x0D62, 0x0D63 }, { 0x0DCA, 0x0DCA }, + { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, { 0x0E31, 0x0E31 }, + { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, { 0x0EB1, 0x0EB1 }, + { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, { 0x0EC8, 0x0ECD }, + { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, { 0x0F37, 0x0F37 }, + { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, { 0x0F80, 0x0F84 }, + { 0x0F86, 0x0F87 }, { 0x0F8D, 0x0F97 }, { 0x0F99, 0x0FBC }, + { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, { 0x1032, 0x1037 }, + { 0x1039, 0x103A }, { 0x103D, 0x103E }, { 0x1058, 0x1059 }, + { 0x105E, 0x1060 }, { 0x1071, 0x1074 }, { 0x1082, 0x1082 }, + { 0x1085, 0x1086 }, { 0x108D, 0x108D }, { 0x109D, 0x109D }, + { 0x1160, 0x11FF }, { 0x135D, 0x135F }, { 0x1712, 0x1714 }, + { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, { 0x1772, 0x1773 }, + { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, { 0x17C6, 0x17C6 }, + { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, { 0x180B, 0x180E }, + { 0x1885, 0x1886 }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, + { 0x1927, 0x192B }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, + { 0x1A17, 0x1A18 }, { 0x1A1B, 0x1A1B }, { 0x1A56, 0x1A56 }, + { 0x1A58, 0x1A5E }, { 0x1A60, 0x1A60 }, { 0x1A62, 0x1A62 }, + { 0x1A65, 0x1A6C }, { 0x1A73, 0x1A7C }, { 0x1A7F, 0x1A7F }, + { 0x1AB0, 0x1ABE }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, + { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, + { 0x1B6B, 0x1B73 }, { 0x1B80, 0x1B81 }, { 0x1BA2, 0x1BA5 }, + { 0x1BA8, 0x1BA9 }, { 0x1BAB, 0x1BAD }, { 0x1BE6, 0x1BE6 }, + { 0x1BE8, 0x1BE9 }, { 0x1BED, 0x1BED }, { 0x1BEF, 0x1BF1 }, + { 0x1C2C, 0x1C33 }, { 0x1C36, 0x1C37 }, { 0x1CD0, 0x1CD2 }, + { 0x1CD4, 0x1CE0 }, { 0x1CE2, 0x1CE8 }, { 0x1CED, 0x1CED }, + { 0x1CF4, 0x1CF4 }, { 0x1CF8, 0x1CF9 }, { 0x1DC0, 0x1DF9 }, + { 0x1DFB, 0x1DFF }, { 0x200B, 0x200F }, { 0x202A, 0x202E }, + { 0x2060, 0x2064 }, { 0x2066, 0x206F }, { 0x20D0, 0x20F0 }, + { 0x2CEF, 0x2CF1 }, { 0x2D7F, 0x2D7F }, { 0x2DE0, 0x2DFF }, + { 0x302A, 0x302F }, { 0x3099, 0x309A }, { 0xA66F, 0xA672 }, + { 0xA674, 0xA67D }, { 0xA69E, 0xA69F }, { 0xA6F0, 0xA6F1 }, + { 0xA802, 0xA802 }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, + { 0xA825, 0xA826 }, { 0xA8C4, 0xA8C5 }, { 0xA8E0, 0xA8F1 }, + { 0xA926, 0xA92D }, { 0xA947, 0xA951 }, { 0xA980, 0xA982 }, + { 0xA9B3, 0xA9B3 }, { 0xA9B6, 0xA9B9 }, { 0xA9BC, 0xA9BC }, + { 0xA9E5, 0xA9E5 }, { 0xAA29, 0xAA2E }, { 0xAA31, 0xAA32 }, + { 0xAA35, 0xAA36 }, { 0xAA43, 0xAA43 }, { 0xAA4C, 0xAA4C }, + { 0xAA7C, 0xAA7C }, { 0xAAB0, 0xAAB0 }, { 0xAAB2, 0xAAB4 }, + { 0xAAB7, 0xAAB8 }, { 0xAABE, 0xAABF }, { 0xAAC1, 0xAAC1 }, + { 0xAAEC, 0xAAED }, { 0xAAF6, 0xAAF6 }, { 0xABE5, 0xABE5 }, + { 0xABE8, 0xABE8 }, { 0xABED, 0xABED }, { 0xFB1E, 0xFB1E }, + { 0xFE00, 0xFE0F }, { 0xFE20, 0xFE2F }, { 0xFEFF, 0xFEFF }, + { 0xFFF9, 0xFFFB }, { 0x101FD, 0x101FD }, { 0x102E0, 0x102E0 }, + { 0x10376, 0x1037A }, { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, + { 0x10A0C, 0x10A0F }, { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, + { 0x10AE5, 0x10AE6 }, { 0x11001, 0x11001 }, { 0x11038, 0x11046 }, + { 0x1107F, 0x11081 }, { 0x110B3, 0x110B6 }, { 0x110B9, 0x110BA }, + { 0x110BD, 0x110BD }, { 0x11100, 0x11102 }, { 0x11127, 0x1112B }, + { 0x1112D, 0x11134 }, { 0x11173, 0x11173 }, { 0x11180, 0x11181 }, + { 0x111B6, 0x111BE }, { 0x111CA, 0x111CC }, { 0x1122F, 0x11231 }, + { 0x11234, 0x11234 }, { 0x11236, 0x11237 }, { 0x1123E, 0x1123E }, + { 0x112DF, 0x112DF }, { 0x112E3, 0x112EA }, { 0x11300, 0x11301 }, + { 0x1133C, 0x1133C }, { 0x11340, 0x11340 }, { 0x11366, 0x1136C }, + { 0x11370, 0x11374 }, { 0x11438, 0x1143F }, { 0x11442, 0x11444 }, + { 0x11446, 0x11446 }, { 0x114B3, 0x114B8 }, { 0x114BA, 0x114BA }, + { 0x114BF, 0x114C0 }, { 0x114C2, 0x114C3 }, { 0x115B2, 0x115B5 }, + { 0x115BC, 0x115BD }, { 0x115BF, 0x115C0 }, { 0x115DC, 0x115DD }, + { 0x11633, 0x1163A }, { 0x1163D, 0x1163D }, { 0x1163F, 0x11640 }, + { 0x116AB, 0x116AB }, { 0x116AD, 0x116AD }, { 0x116B0, 0x116B5 }, + { 0x116B7, 0x116B7 }, { 0x1171D, 0x1171F }, { 0x11722, 0x11725 }, + { 0x11727, 0x1172B }, { 0x11A01, 0x11A06 }, { 0x11A09, 0x11A0A }, + { 0x11A33, 0x11A38 }, { 0x11A3B, 0x11A3E }, { 0x11A47, 0x11A47 }, + { 0x11A51, 0x11A56 }, { 0x11A59, 0x11A5B }, { 0x11A8A, 0x11A96 }, + { 0x11A98, 0x11A99 }, { 0x11C30, 0x11C36 }, { 0x11C38, 0x11C3D }, + { 0x11C3F, 0x11C3F }, { 0x11C92, 0x11CA7 }, { 0x11CAA, 0x11CB0 }, + { 0x11CB2, 0x11CB3 }, { 0x11CB5, 0x11CB6 }, { 0x11D31, 0x11D36 }, + { 0x11D3A, 0x11D3A }, { 0x11D3C, 0x11D3D }, { 0x11D3F, 0x11D45 }, + { 0x11D47, 0x11D47 }, { 0x16AF0, 0x16AF4 }, { 0x16B30, 0x16B36 }, + { 0x16F8F, 0x16F92 }, { 0x1BC9D, 0x1BC9E }, { 0x1BCA0, 0x1BCA3 }, + { 0x1D167, 0x1D169 }, { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, + { 0x1D1AA, 0x1D1AD }, { 0x1D242, 0x1D244 }, { 0x1DA00, 0x1DA36 }, + { 0x1DA3B, 0x1DA6C }, { 0x1DA75, 0x1DA75 }, { 0x1DA84, 0x1DA84 }, + { 0x1DA9B, 0x1DA9F }, { 0x1DAA1, 0x1DAAF }, { 0x1E000, 0x1E006 }, + { 0x1E008, 0x1E018 }, { 0x1E01B, 0x1E021 }, { 0x1E023, 0x1E024 }, + { 0x1E026, 0x1E02A }, { 0x1E8D0, 0x1E8D6 }, { 0x1E944, 0x1E94A }, + { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, { 0xE0100, 0xE01EF } + }; + + /* sorted list of non-overlapping intervals of non-characters */ + /* generated by + * uniset +0000..DFFF -4e00..9fd5 +F900..10FFFD unknown +2028..2029 c + */ + static const struct interval unknowns[] = { + { 0x0378, 0x0379 }, { 0x0380, 0x0383 }, { 0x038B, 0x038B }, + { 0x038D, 0x038D }, { 0x03A2, 0x03A2 }, { 0x0530, 0x0530 }, + { 0x0557, 0x0558 }, { 0x0560, 0x0560 }, { 0x0588, 0x0588 }, + { 0x058B, 0x058C }, { 0x0590, 0x0590 }, { 0x05C8, 0x05CF }, + { 0x05EB, 0x05EF }, { 0x05F5, 0x05FF }, { 0x061D, 0x061D }, + { 0x070E, 0x070E }, { 0x074B, 0x074C }, { 0x07B2, 0x07BF }, + { 0x07FB, 0x07FF }, { 0x082E, 0x082F }, { 0x083F, 0x083F }, + { 0x085C, 0x085D }, { 0x085F, 0x085F }, { 0x086B, 0x089F }, + { 0x08B5, 0x08B5 }, { 0x08BE, 0x08D3 }, { 0x0984, 0x0984 }, + { 0x098D, 0x098E }, { 0x0991, 0x0992 }, { 0x09A9, 0x09A9 }, + { 0x09B1, 0x09B1 }, { 0x09B3, 0x09B5 }, { 0x09BA, 0x09BB }, + { 0x09C5, 0x09C6 }, { 0x09C9, 0x09CA }, { 0x09CF, 0x09D6 }, + { 0x09D8, 0x09DB }, { 0x09DE, 0x09DE }, { 0x09E4, 0x09E5 }, + { 0x09FE, 0x0A00 }, { 0x0A04, 0x0A04 }, { 0x0A0B, 0x0A0E }, + { 0x0A11, 0x0A12 }, { 0x0A29, 0x0A29 }, { 0x0A31, 0x0A31 }, + { 0x0A34, 0x0A34 }, { 0x0A37, 0x0A37 }, { 0x0A3A, 0x0A3B }, + { 0x0A3D, 0x0A3D }, { 0x0A43, 0x0A46 }, { 0x0A49, 0x0A4A }, + { 0x0A4E, 0x0A50 }, { 0x0A52, 0x0A58 }, { 0x0A5D, 0x0A5D }, + { 0x0A5F, 0x0A65 }, { 0x0A76, 0x0A80 }, { 0x0A84, 0x0A84 }, + { 0x0A8E, 0x0A8E }, { 0x0A92, 0x0A92 }, { 0x0AA9, 0x0AA9 }, + { 0x0AB1, 0x0AB1 }, { 0x0AB4, 0x0AB4 }, { 0x0ABA, 0x0ABB }, + { 0x0AC6, 0x0AC6 }, { 0x0ACA, 0x0ACA }, { 0x0ACE, 0x0ACF }, + { 0x0AD1, 0x0ADF }, { 0x0AE4, 0x0AE5 }, { 0x0AF2, 0x0AF8 }, + { 0x0B00, 0x0B00 }, { 0x0B04, 0x0B04 }, { 0x0B0D, 0x0B0E }, + { 0x0B11, 0x0B12 }, { 0x0B29, 0x0B29 }, { 0x0B31, 0x0B31 }, + { 0x0B34, 0x0B34 }, { 0x0B3A, 0x0B3B }, { 0x0B45, 0x0B46 }, + { 0x0B49, 0x0B4A }, { 0x0B4E, 0x0B55 }, { 0x0B58, 0x0B5B }, + { 0x0B5E, 0x0B5E }, { 0x0B64, 0x0B65 }, { 0x0B78, 0x0B81 }, + { 0x0B84, 0x0B84 }, { 0x0B8B, 0x0B8D }, { 0x0B91, 0x0B91 }, + { 0x0B96, 0x0B98 }, { 0x0B9B, 0x0B9B }, { 0x0B9D, 0x0B9D }, + { 0x0BA0, 0x0BA2 }, { 0x0BA5, 0x0BA7 }, { 0x0BAB, 0x0BAD }, + { 0x0BBA, 0x0BBD }, { 0x0BC3, 0x0BC5 }, { 0x0BC9, 0x0BC9 }, + { 0x0BCE, 0x0BCF }, { 0x0BD1, 0x0BD6 }, { 0x0BD8, 0x0BE5 }, + { 0x0BFB, 0x0BFF }, { 0x0C04, 0x0C04 }, { 0x0C0D, 0x0C0D }, + { 0x0C11, 0x0C11 }, { 0x0C29, 0x0C29 }, { 0x0C3A, 0x0C3C }, + { 0x0C45, 0x0C45 }, { 0x0C49, 0x0C49 }, { 0x0C4E, 0x0C54 }, + { 0x0C57, 0x0C57 }, { 0x0C5B, 0x0C5F }, { 0x0C64, 0x0C65 }, + { 0x0C70, 0x0C77 }, { 0x0C84, 0x0C84 }, { 0x0C8D, 0x0C8D }, + { 0x0C91, 0x0C91 }, { 0x0CA9, 0x0CA9 }, { 0x0CB4, 0x0CB4 }, + { 0x0CBA, 0x0CBB }, { 0x0CC5, 0x0CC5 }, { 0x0CC9, 0x0CC9 }, + { 0x0CCE, 0x0CD4 }, { 0x0CD7, 0x0CDD }, { 0x0CDF, 0x0CDF }, + { 0x0CE4, 0x0CE5 }, { 0x0CF0, 0x0CF0 }, { 0x0CF3, 0x0CFF }, + { 0x0D04, 0x0D04 }, { 0x0D0D, 0x0D0D }, { 0x0D11, 0x0D11 }, + { 0x0D45, 0x0D45 }, { 0x0D49, 0x0D49 }, { 0x0D50, 0x0D53 }, + { 0x0D64, 0x0D65 }, { 0x0D80, 0x0D81 }, { 0x0D84, 0x0D84 }, + { 0x0D97, 0x0D99 }, { 0x0DB2, 0x0DB2 }, { 0x0DBC, 0x0DBC }, + { 0x0DBE, 0x0DBF }, { 0x0DC7, 0x0DC9 }, { 0x0DCB, 0x0DCE }, + { 0x0DD5, 0x0DD5 }, { 0x0DD7, 0x0DD7 }, { 0x0DE0, 0x0DE5 }, + { 0x0DF0, 0x0DF1 }, { 0x0DF5, 0x0E00 }, { 0x0E3B, 0x0E3E }, + { 0x0E5C, 0x0E80 }, { 0x0E83, 0x0E83 }, { 0x0E85, 0x0E86 }, + { 0x0E89, 0x0E89 }, { 0x0E8B, 0x0E8C }, { 0x0E8E, 0x0E93 }, + { 0x0E98, 0x0E98 }, { 0x0EA0, 0x0EA0 }, { 0x0EA4, 0x0EA4 }, + { 0x0EA6, 0x0EA6 }, { 0x0EA8, 0x0EA9 }, { 0x0EAC, 0x0EAC }, + { 0x0EBA, 0x0EBA }, { 0x0EBE, 0x0EBF }, { 0x0EC5, 0x0EC5 }, + { 0x0EC7, 0x0EC7 }, { 0x0ECE, 0x0ECF }, { 0x0EDA, 0x0EDB }, + { 0x0EE0, 0x0EFF }, { 0x0F48, 0x0F48 }, { 0x0F6D, 0x0F70 }, + { 0x0F98, 0x0F98 }, { 0x0FBD, 0x0FBD }, { 0x0FCD, 0x0FCD }, + { 0x0FDB, 0x0FFF }, { 0x10C6, 0x10C6 }, { 0x10C8, 0x10CC }, + { 0x10CE, 0x10CF }, { 0x1249, 0x1249 }, { 0x124E, 0x124F }, + { 0x1257, 0x1257 }, { 0x1259, 0x1259 }, { 0x125E, 0x125F }, + { 0x1289, 0x1289 }, { 0x128E, 0x128F }, { 0x12B1, 0x12B1 }, + { 0x12B6, 0x12B7 }, { 0x12BF, 0x12BF }, { 0x12C1, 0x12C1 }, + { 0x12C6, 0x12C7 }, { 0x12D7, 0x12D7 }, { 0x1311, 0x1311 }, + { 0x1316, 0x1317 }, { 0x135B, 0x135C }, { 0x137D, 0x137F }, + { 0x139A, 0x139F }, { 0x13F6, 0x13F7 }, { 0x13FE, 0x13FF }, + { 0x169D, 0x169F }, { 0x16F9, 0x16FF }, { 0x170D, 0x170D }, + { 0x1715, 0x171F }, { 0x1737, 0x173F }, { 0x1754, 0x175F }, + { 0x176D, 0x176D }, { 0x1771, 0x1771 }, { 0x1774, 0x177F }, + { 0x17DE, 0x17DF }, { 0x17EA, 0x17EF }, { 0x17FA, 0x17FF }, + { 0x180F, 0x180F }, { 0x181A, 0x181F }, { 0x1878, 0x187F }, + { 0x18AB, 0x18AF }, { 0x18F6, 0x18FF }, { 0x191F, 0x191F }, + { 0x192C, 0x192F }, { 0x193C, 0x193F }, { 0x1941, 0x1943 }, + { 0x196E, 0x196F }, { 0x1975, 0x197F }, { 0x19AC, 0x19AF }, + { 0x19CA, 0x19CF }, { 0x19DB, 0x19DD }, { 0x1A1C, 0x1A1D }, + { 0x1A5F, 0x1A5F }, { 0x1A7D, 0x1A7E }, { 0x1A8A, 0x1A8F }, + { 0x1A9A, 0x1A9F }, { 0x1AAE, 0x1AAF }, { 0x1ABF, 0x1AFF }, + { 0x1B4C, 0x1B4F }, { 0x1B7D, 0x1B7F }, { 0x1BF4, 0x1BFB }, + { 0x1C38, 0x1C3A }, { 0x1C4A, 0x1C4C }, { 0x1C89, 0x1CBF }, + { 0x1CC8, 0x1CCF }, { 0x1CFA, 0x1CFF }, { 0x1DFA, 0x1DFA }, + { 0x1F16, 0x1F17 }, { 0x1F1E, 0x1F1F }, { 0x1F46, 0x1F47 }, + { 0x1F4E, 0x1F4F }, { 0x1F58, 0x1F58 }, { 0x1F5A, 0x1F5A }, + { 0x1F5C, 0x1F5C }, { 0x1F5E, 0x1F5E }, { 0x1F7E, 0x1F7F }, + { 0x1FB5, 0x1FB5 }, { 0x1FC5, 0x1FC5 }, { 0x1FD4, 0x1FD5 }, + { 0x1FDC, 0x1FDC }, { 0x1FF0, 0x1FF1 }, { 0x1FF5, 0x1FF5 }, + { 0x1FFF, 0x1FFF }, { 0x2028, 0x2029 }, { 0x2065, 0x2065 }, + { 0x2072, 0x2073 }, { 0x208F, 0x208F }, { 0x209D, 0x209F }, + { 0x20C0, 0x20CF }, { 0x20F1, 0x20FF }, { 0x218C, 0x218F }, + { 0x2427, 0x243F }, { 0x244B, 0x245F }, { 0x2B74, 0x2B75 }, + { 0x2B96, 0x2B97 }, { 0x2BBA, 0x2BBC }, { 0x2BC9, 0x2BC9 }, + { 0x2BD3, 0x2BEB }, { 0x2BF0, 0x2BFF }, { 0x2C2F, 0x2C2F }, + { 0x2C5F, 0x2C5F }, { 0x2CF4, 0x2CF8 }, { 0x2D26, 0x2D26 }, + { 0x2D28, 0x2D2C }, { 0x2D2E, 0x2D2F }, { 0x2D68, 0x2D6E }, + { 0x2D71, 0x2D7E }, { 0x2D97, 0x2D9F }, { 0x2DA7, 0x2DA7 }, + { 0x2DAF, 0x2DAF }, { 0x2DB7, 0x2DB7 }, { 0x2DBF, 0x2DBF }, + { 0x2DC7, 0x2DC7 }, { 0x2DCF, 0x2DCF }, { 0x2DD7, 0x2DD7 }, + { 0x2DDF, 0x2DDF }, { 0x2E4A, 0x2E7F }, { 0x2E9A, 0x2E9A }, + { 0x2EF4, 0x2EFF }, { 0x2FD6, 0x2FEF }, { 0x2FFC, 0x2FFF }, + { 0x3040, 0x3040 }, { 0x3097, 0x3098 }, { 0x3100, 0x3104 }, + { 0x312F, 0x3130 }, { 0x318F, 0x318F }, { 0x31BB, 0x31BF }, + { 0x31E4, 0x31EF }, { 0x321F, 0x321F }, { 0x32FF, 0x32FF }, + { 0x4DB6, 0x4DBF }, { 0x9FD6, 0x9FFF }, { 0xA48D, 0xA48F }, + { 0xA4C7, 0xA4CF }, { 0xA62C, 0xA63F }, { 0xA6F8, 0xA6FF }, + { 0xA7AF, 0xA7AF }, { 0xA7B8, 0xA7F6 }, { 0xA82C, 0xA82F }, + { 0xA83A, 0xA83F }, { 0xA878, 0xA87F }, { 0xA8C6, 0xA8CD }, + { 0xA8DA, 0xA8DF }, { 0xA8FE, 0xA8FF }, { 0xA954, 0xA95E }, + { 0xA97D, 0xA97F }, { 0xA9CE, 0xA9CE }, { 0xA9DA, 0xA9DD }, + { 0xA9FF, 0xA9FF }, { 0xAA37, 0xAA3F }, { 0xAA4E, 0xAA4F }, + { 0xAA5A, 0xAA5B }, { 0xAAC3, 0xAADA }, { 0xAAF7, 0xAB00 }, + { 0xAB07, 0xAB08 }, { 0xAB0F, 0xAB10 }, { 0xAB17, 0xAB1F }, + { 0xAB27, 0xAB27 }, { 0xAB2F, 0xAB2F }, { 0xAB66, 0xAB6F }, + { 0xABEE, 0xABEF }, { 0xABFA, 0xABFF }, { 0xD7A4, 0xD7AF }, + { 0xD7C7, 0xD7CA }, { 0xD7FC, 0xDFFF }, { 0xFA6E, 0xFA6F }, + { 0xFADA, 0xFAFF }, { 0xFB07, 0xFB12 }, { 0xFB18, 0xFB1C }, + { 0xFB37, 0xFB37 }, { 0xFB3D, 0xFB3D }, { 0xFB3F, 0xFB3F }, + { 0xFB42, 0xFB42 }, { 0xFB45, 0xFB45 }, { 0xFBC2, 0xFBD2 }, + { 0xFD40, 0xFD4F }, { 0xFD90, 0xFD91 }, { 0xFDC8, 0xFDEF }, + { 0xFDFE, 0xFDFF }, { 0xFE1A, 0xFE1F }, { 0xFE53, 0xFE53 }, + { 0xFE67, 0xFE67 }, { 0xFE6C, 0xFE6F }, { 0xFE75, 0xFE75 }, + { 0xFEFD, 0xFEFE }, { 0xFF00, 0xFF00 }, { 0xFFBF, 0xFFC1 }, + { 0xFFC8, 0xFFC9 }, { 0xFFD0, 0xFFD1 }, { 0xFFD8, 0xFFD9 }, + { 0xFFDD, 0xFFDF }, { 0xFFE7, 0xFFE7 }, { 0xFFEF, 0xFFF8 }, + { 0xFFFE, 0xFFFF }, { 0x1000C, 0x1000C }, { 0x10027, 0x10027 }, + { 0x1003B, 0x1003B }, { 0x1003E, 0x1003E }, { 0x1004E, 0x1004F }, + { 0x1005E, 0x1007F }, { 0x100FB, 0x100FF }, { 0x10103, 0x10106 }, + { 0x10134, 0x10136 }, { 0x1018F, 0x1018F }, { 0x1019C, 0x1019F }, + { 0x101A1, 0x101CF }, { 0x101FE, 0x1027F }, { 0x1029D, 0x1029F }, + { 0x102D1, 0x102DF }, { 0x102FC, 0x102FF }, { 0x10324, 0x1032C }, + { 0x1034B, 0x1034F }, { 0x1037B, 0x1037F }, { 0x1039E, 0x1039E }, + { 0x103C4, 0x103C7 }, { 0x103D6, 0x103FF }, { 0x1049E, 0x1049F }, + { 0x104AA, 0x104AF }, { 0x104D4, 0x104D7 }, { 0x104FC, 0x104FF }, + { 0x10528, 0x1052F }, { 0x10564, 0x1056E }, { 0x10570, 0x105FF }, + { 0x10737, 0x1073F }, { 0x10756, 0x1075F }, { 0x10768, 0x107FF }, + { 0x10806, 0x10807 }, { 0x10809, 0x10809 }, { 0x10836, 0x10836 }, + { 0x10839, 0x1083B }, { 0x1083D, 0x1083E }, { 0x10856, 0x10856 }, + { 0x1089F, 0x108A6 }, { 0x108B0, 0x108DF }, { 0x108F3, 0x108F3 }, + { 0x108F6, 0x108FA }, { 0x1091C, 0x1091E }, { 0x1093A, 0x1093E }, + { 0x10940, 0x1097F }, { 0x109B8, 0x109BB }, { 0x109D0, 0x109D1 }, + { 0x10A04, 0x10A04 }, { 0x10A07, 0x10A0B }, { 0x10A14, 0x10A14 }, + { 0x10A18, 0x10A18 }, { 0x10A34, 0x10A37 }, { 0x10A3B, 0x10A3E }, + { 0x10A48, 0x10A4F }, { 0x10A59, 0x10A5F }, { 0x10AA0, 0x10ABF }, + { 0x10AE7, 0x10AEA }, { 0x10AF7, 0x10AFF }, { 0x10B36, 0x10B38 }, + { 0x10B56, 0x10B57 }, { 0x10B73, 0x10B77 }, { 0x10B92, 0x10B98 }, + { 0x10B9D, 0x10BA8 }, { 0x10BB0, 0x10BFF }, { 0x10C49, 0x10C7F }, + { 0x10CB3, 0x10CBF }, { 0x10CF3, 0x10CF9 }, { 0x10D00, 0x10E5F }, + { 0x10E7F, 0x10FFF }, { 0x1104E, 0x11051 }, { 0x11070, 0x1107E }, + { 0x110C2, 0x110CF }, { 0x110E9, 0x110EF }, { 0x110FA, 0x110FF }, + { 0x11135, 0x11135 }, { 0x11144, 0x1114F }, { 0x11177, 0x1117F }, + { 0x111CE, 0x111CF }, { 0x111E0, 0x111E0 }, { 0x111F5, 0x111FF }, + { 0x11212, 0x11212 }, { 0x1123F, 0x1127F }, { 0x11287, 0x11287 }, + { 0x11289, 0x11289 }, { 0x1128E, 0x1128E }, { 0x1129E, 0x1129E }, + { 0x112AA, 0x112AF }, { 0x112EB, 0x112EF }, { 0x112FA, 0x112FF }, + { 0x11304, 0x11304 }, { 0x1130D, 0x1130E }, { 0x11311, 0x11312 }, + { 0x11329, 0x11329 }, { 0x11331, 0x11331 }, { 0x11334, 0x11334 }, + { 0x1133A, 0x1133B }, { 0x11345, 0x11346 }, { 0x11349, 0x1134A }, + { 0x1134E, 0x1134F }, { 0x11351, 0x11356 }, { 0x11358, 0x1135C }, + { 0x11364, 0x11365 }, { 0x1136D, 0x1136F }, { 0x11375, 0x113FF }, + { 0x1145A, 0x1145A }, { 0x1145C, 0x1145C }, { 0x1145E, 0x1147F }, + { 0x114C8, 0x114CF }, { 0x114DA, 0x1157F }, { 0x115B6, 0x115B7 }, + { 0x115DE, 0x115FF }, { 0x11645, 0x1164F }, { 0x1165A, 0x1165F }, + { 0x1166D, 0x1167F }, { 0x116B8, 0x116BF }, { 0x116CA, 0x116FF }, + { 0x1171A, 0x1171C }, { 0x1172C, 0x1172F }, { 0x11740, 0x1189F }, + { 0x118F3, 0x118FE }, { 0x11900, 0x119FF }, { 0x11A48, 0x11A4F }, + { 0x11A84, 0x11A85 }, { 0x11A9D, 0x11A9D }, { 0x11AA3, 0x11ABF }, + { 0x11AF9, 0x11BFF }, { 0x11C09, 0x11C09 }, { 0x11C37, 0x11C37 }, + { 0x11C46, 0x11C4F }, { 0x11C6D, 0x11C6F }, { 0x11C90, 0x11C91 }, + { 0x11CA8, 0x11CA8 }, { 0x11CB7, 0x11CFF }, { 0x11D07, 0x11D07 }, + { 0x11D0A, 0x11D0A }, { 0x11D37, 0x11D39 }, { 0x11D3B, 0x11D3B }, + { 0x11D3E, 0x11D3E }, { 0x11D48, 0x11D4F }, { 0x11D5A, 0x11FFF }, + { 0x1239A, 0x123FF }, { 0x1246F, 0x1246F }, { 0x12475, 0x1247F }, + { 0x12544, 0x12FFF }, { 0x1342F, 0x143FF }, { 0x14647, 0x167FF }, + { 0x16A39, 0x16A3F }, { 0x16A5F, 0x16A5F }, { 0x16A6A, 0x16A6D }, + { 0x16A70, 0x16ACF }, { 0x16AEE, 0x16AEF }, { 0x16AF6, 0x16AFF }, + { 0x16B46, 0x16B4F }, { 0x16B5A, 0x16B5A }, { 0x16B62, 0x16B62 }, + { 0x16B78, 0x16B7C }, { 0x16B90, 0x16EFF }, { 0x16F45, 0x16F4F }, + { 0x16F7F, 0x16F8E }, { 0x16FA0, 0x16FDF }, { 0x16FE2, 0x187FF }, + { 0x18AF3, 0x1AFFF }, { 0x1B11F, 0x1B16F }, { 0x1B2FC, 0x1BBFF }, + { 0x1BC6B, 0x1BC6F }, { 0x1BC7D, 0x1BC7F }, { 0x1BC89, 0x1BC8F }, + { 0x1BC9A, 0x1BC9B }, { 0x1BCA4, 0x1CFFF }, { 0x1D0F6, 0x1D0FF }, + { 0x1D127, 0x1D128 }, { 0x1D1E9, 0x1D1FF }, { 0x1D246, 0x1D2FF }, + { 0x1D357, 0x1D35F }, { 0x1D372, 0x1D3FF }, { 0x1D455, 0x1D455 }, + { 0x1D49D, 0x1D49D }, { 0x1D4A0, 0x1D4A1 }, { 0x1D4A3, 0x1D4A4 }, + { 0x1D4A7, 0x1D4A8 }, { 0x1D4AD, 0x1D4AD }, { 0x1D4BA, 0x1D4BA }, + { 0x1D4BC, 0x1D4BC }, { 0x1D4C4, 0x1D4C4 }, { 0x1D506, 0x1D506 }, + { 0x1D50B, 0x1D50C }, { 0x1D515, 0x1D515 }, { 0x1D51D, 0x1D51D }, + { 0x1D53A, 0x1D53A }, { 0x1D53F, 0x1D53F }, { 0x1D545, 0x1D545 }, + { 0x1D547, 0x1D549 }, { 0x1D551, 0x1D551 }, { 0x1D6A6, 0x1D6A7 }, + { 0x1D7CC, 0x1D7CD }, { 0x1DA8C, 0x1DA9A }, { 0x1DAA0, 0x1DAA0 }, + { 0x1DAB0, 0x1DFFF }, { 0x1E007, 0x1E007 }, { 0x1E019, 0x1E01A }, + { 0x1E022, 0x1E022 }, { 0x1E025, 0x1E025 }, { 0x1E02B, 0x1E7FF }, + { 0x1E8C5, 0x1E8C6 }, { 0x1E8D7, 0x1E8FF }, { 0x1E94B, 0x1E94F }, + { 0x1E95A, 0x1E95D }, { 0x1E960, 0x1EDFF }, { 0x1EE04, 0x1EE04 }, + { 0x1EE20, 0x1EE20 }, { 0x1EE23, 0x1EE23 }, { 0x1EE25, 0x1EE26 }, + { 0x1EE28, 0x1EE28 }, { 0x1EE33, 0x1EE33 }, { 0x1EE38, 0x1EE38 }, + { 0x1EE3A, 0x1EE3A }, { 0x1EE3C, 0x1EE41 }, { 0x1EE43, 0x1EE46 }, + { 0x1EE48, 0x1EE48 }, { 0x1EE4A, 0x1EE4A }, { 0x1EE4C, 0x1EE4C }, + { 0x1EE50, 0x1EE50 }, { 0x1EE53, 0x1EE53 }, { 0x1EE55, 0x1EE56 }, + { 0x1EE58, 0x1EE58 }, { 0x1EE5A, 0x1EE5A }, { 0x1EE5C, 0x1EE5C }, + { 0x1EE5E, 0x1EE5E }, { 0x1EE60, 0x1EE60 }, { 0x1EE63, 0x1EE63 }, + { 0x1EE65, 0x1EE66 }, { 0x1EE6B, 0x1EE6B }, { 0x1EE73, 0x1EE73 }, + { 0x1EE78, 0x1EE78 }, { 0x1EE7D, 0x1EE7D }, { 0x1EE7F, 0x1EE7F }, + { 0x1EE8A, 0x1EE8A }, { 0x1EE9C, 0x1EEA0 }, { 0x1EEA4, 0x1EEA4 }, + { 0x1EEAA, 0x1EEAA }, { 0x1EEBC, 0x1EEEF }, { 0x1EEF2, 0x1EFFF }, + { 0x1F02C, 0x1F02F }, { 0x1F094, 0x1F09F }, { 0x1F0AF, 0x1F0B0 }, + { 0x1F0C0, 0x1F0C0 }, { 0x1F0D0, 0x1F0D0 }, { 0x1F0F6, 0x1F0FF }, + { 0x1F10D, 0x1F10F }, { 0x1F12F, 0x1F12F }, { 0x1F16C, 0x1F16F }, + { 0x1F1AD, 0x1F1E5 }, { 0x1F203, 0x1F20F }, { 0x1F23C, 0x1F23F }, + { 0x1F249, 0x1F24F }, { 0x1F252, 0x1F25F }, { 0x1F266, 0x1F2FF }, + { 0x1F6D5, 0x1F6DF }, { 0x1F6ED, 0x1F6EF }, { 0x1F6F9, 0x1F6FF }, + { 0x1F774, 0x1F77F }, { 0x1F7D5, 0x1F7FF }, { 0x1F80C, 0x1F80F }, + { 0x1F848, 0x1F84F }, { 0x1F85A, 0x1F85F }, { 0x1F888, 0x1F88F }, + { 0x1F8AE, 0x1F8FF }, { 0x1F90C, 0x1F90F }, { 0x1F93F, 0x1F93F }, + { 0x1F94D, 0x1F94F }, { 0x1F96C, 0x1F97F }, { 0x1F998, 0x1F9BF }, + { 0x1F9C1, 0x1F9CF }, { 0x1F9E7, 0x1FFFF }, { 0x2A6D7, 0x2F7FF }, + { 0x2FA1E, 0xE0000 }, { 0xE0002, 0xE001F }, { 0xE0080, 0xE00FF }, + { 0xE01F0, 0x10FFFD } + }; + + int result; + +#define Lookup(cmp, table) \ + bisearch(cmp, table, \ + (int) (sizeof(table) / sizeof(struct interval) - 1)) + + /* test for 8-bit control characters */ + if (cmp == 0) { + result = 0; + } else if (cmp < 32 || (cmp >= 0x7f && cmp < 0xa0)) { + result = -1; + } else if (cmp == 0xad) { + result = use_latin1; + } else if (Lookup(cmp, combining)) { + /* binary search in table of non-spacing characters */ + result = 0; + } else { + /* if we arrive here, cmp is not a combining or C0/C1 control character */ + result = 1; + + if (cmp >= 0x1100 && + (cmp <= 0x115f || /* Hangul Jamo init. consonants */ + cmp == 0x2329 || + cmp == 0x232a || + (cmp >= 0x2e80 && cmp <= 0x4dbf && + cmp != 0x303f) || /* CJK ... Yi */ + (cmp >= 0x4e00 && cmp <= 0xa4cf) || /* CJK Unified Ideographs, Yi */ + (cmp >= 0xa960 && cmp <= 0xa97f) || /* Hangul Jamo Extended-A */ + (cmp >= 0xac00 && cmp <= 0xd7a3) || /* Hangul Syllables */ + (cmp >= 0xf900 && cmp <= 0xfaff) || /* CJK Compatibility Ideographs */ + (cmp >= 0xfe10 && cmp <= 0xfe19) || /* Vertical forms */ + (cmp >= 0xfe30 && cmp <= 0xfe6f) || /* CJK Compatibility Forms */ + (cmp >= 0xff00 && cmp <= 0xff60) || /* Fullwidth Forms */ + (cmp >= 0xffe0 && cmp <= 0xffe6) || + (cmp >= 0x20000 && cmp <= 0x2fffd) || + (cmp >= 0x30000 && cmp <= 0x3fffd))) { + result = 2; + } + if (cmp >= unknowns[0].first && Lookup(cmp, unknowns)) { + result = -1; + } + } + return result; +} + + +int mk_wcswidth(const wchar_t *pwcs, size_t n) +{ + int width = 0; + + for (;*pwcs && n-- > 0; pwcs++) { + int w; + + if ((w = mk_wcwidth(*pwcs)) < 0) + return -1; + else + width += w; + } + + return width; +} + + +/* + * The following functions are the same as mk_wcwidth() and + * mk_wcwidth_cjk(), except that spacing characters in the East Asian + * Ambiguous (A) category as defined in Unicode Technical Report #11 + * have a column width of 2. This variant might be useful for users of + * CJK legacy encodings who want to migrate to UCS without changing + * the traditional terminal character-width behaviour. It is not + * otherwise recommended for general use. + */ +int mk_wcwidth_cjk(wchar_t ucs) +{ + /* sorted list of non-overlapping intervals of East Asian Ambiguous + * characters, generated by + * + * uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf \ + * +E000..F8FF \ + * +F0000..FFFFD \ + * +100000..10FFFD c + * + * "WIDTH-A" is a file extracted from EastAsianWidth.txt by selecting + * only those with width "A", and omitting: + * + * 0xAD + * all lines with "COMBINING" + * + * (uniset does not recognize the range expressions in WIDTH-A). + */ + static const struct interval ambiguous[] = { + { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 }, + { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 }, + { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 }, + { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 }, + { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED }, + { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA }, + { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 }, + { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B }, + { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 }, + { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 }, + { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 }, + { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE }, + { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 }, + { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA }, + { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 }, + { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB }, + { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB }, + { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 }, + { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 }, + { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 }, + { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 }, + { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 }, + { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 }, + { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 }, + { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC }, + { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 }, + { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 }, + { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 }, + { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 }, + { 0x2189, 0x2189 }, { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, + { 0x21D2, 0x21D2 }, { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, + { 0x2200, 0x2200 }, { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, + { 0x220B, 0x220B }, { 0x220F, 0x220F }, { 0x2211, 0x2211 }, + { 0x2215, 0x2215 }, { 0x221A, 0x221A }, { 0x221D, 0x2220 }, + { 0x2223, 0x2223 }, { 0x2225, 0x2225 }, { 0x2227, 0x222C }, + { 0x222E, 0x222E }, { 0x2234, 0x2237 }, { 0x223C, 0x223D }, + { 0x2248, 0x2248 }, { 0x224C, 0x224C }, { 0x2252, 0x2252 }, + { 0x2260, 0x2261 }, { 0x2264, 0x2267 }, { 0x226A, 0x226B }, + { 0x226E, 0x226F }, { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, + { 0x2295, 0x2295 }, { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, + { 0x22BF, 0x22BF }, { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, + { 0x24EB, 0x254B }, { 0x2550, 0x2573 }, { 0x2580, 0x258F }, + { 0x2592, 0x2595 }, { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, + { 0x25B2, 0x25B3 }, { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, + { 0x25C0, 0x25C1 }, { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, + { 0x25CE, 0x25D1 }, { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, + { 0x2605, 0x2606 }, { 0x2609, 0x2609 }, { 0x260E, 0x260F }, + { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 }, + { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 }, + { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F }, + { 0x269E, 0x269F }, { 0x26BF, 0x26BF }, { 0x26C6, 0x26CD }, + { 0x26CF, 0x26D3 }, { 0x26D5, 0x26E1 }, { 0x26E3, 0x26E3 }, + { 0x26E8, 0x26E9 }, { 0x26EB, 0x26F1 }, { 0x26F4, 0x26F4 }, + { 0x26F6, 0x26F9 }, { 0x26FB, 0x26FC }, { 0x26FE, 0x26FF }, + { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0x2B56, 0x2B59 }, + { 0x3248, 0x324F }, { 0xE000, 0xF8FF }, { 0xFFFD, 0xFFFD }, + { 0x1F100, 0x1F10A }, { 0x1F110, 0x1F12D }, { 0x1F130, 0x1F169 }, + { 0x1F170, 0x1F18D }, { 0x1F18F, 0x1F190 }, { 0x1F19B, 0x1F1AC }, + { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD } + }; + + /* binary search in table of non-spacing characters */ + if (Lookup((unsigned long) ucs, ambiguous)) + return 2; + + return mk_wcwidth(ucs); +} + + +int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n) +{ + int width = 0; + + for (;*pwcs && n-- > 0; pwcs++) { + int w; + + if ((w = mk_wcwidth_cjk(*pwcs)) < 0) + return -1; + else + width += w; + } + + return width; +} diff --git a/src/wcwidth.h b/src/wcwidth.h new file mode 100644 index 0000000..dde7c89 --- /dev/null +++ b/src/wcwidth.h @@ -0,0 +1,47 @@ +/* $XTermId: wcwidth.h,v 1.14 2017/06/18 17:56:35 tom Exp $ */ + +/* $XFree86: xc/programs/xterm/wcwidth.h,v 1.5 2005/05/03 00:38:25 dickey Exp $ */ + +/* + * Copyright 2000-2005,2017 by Thomas E. Dickey + * + * All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name(s) of the above copyright + * holders shall not be used in advertising or otherwise to promote the + * sale, use or other dealings in this Software without prior written + * authorization. + */ +#ifndef included_wcwidth_h +#define included_wcwidth_h 1 + +#include <stddef.h> + +extern void mk_wcwidth_init(int mode); + +extern int mk_wcswidth(const wchar_t * pwcs, size_t n); +extern int mk_wcswidth_cjk(const wchar_t * pwcs, size_t n); +extern int mk_wcwidth(wchar_t ucs); +extern int mk_wcwidth_cjk(wchar_t ucs); +extern int wcswidth_cjk(const wchar_t * pwcs, size_t n); + +#endif /* included_wcwidth_h */ diff --git a/src/win_tcp.opt b/src/win_tcp.opt new file mode 100644 index 0000000..7fcb9fd --- /dev/null +++ b/src/win_tcp.opt @@ -0,0 +1 @@ +twg$tcp:[netdist.lib]twglib/library |