home *** CD-ROM | disk | FTP | other *** search
/ Dream 52 / Amiga_Dream_52.iso / Linux / Divers / lynx2.8.1dev.10.tar.gz / lynx2.8.1dev.10.tar / lynx2-8 / src / GridText.c < prev    next >
C/C++ Source or Header  |  1998-05-02  |  212KB  |  8,179 lines

  1. /*        Character grid hypertext object
  2. **        ===============================
  3. */
  4.  
  5. #include <HTUtils.h>
  6. #include <tcp.h>
  7. #include <HTString.h>
  8. #include <HTFont.h>
  9. #include <HTAccess.h>
  10. #include <HTAnchor.h>
  11. #include <HTParse.h>
  12. #include <HTTP.h>
  13. #include <HTAlert.h>
  14. #include <HTCJK.h>
  15. #include <UCDefs.h>
  16. #include <UCAux.h>
  17.  
  18. #include <assert.h>
  19. #include <ctype.h>
  20. #ifndef VMS
  21. #ifdef SYSLOG_REQUESTED_URLS
  22. #include <syslog.h>
  23. #endif /* SYSLOG_REQUESTED_URLS */
  24. #endif /* !VMS */
  25.  
  26. #include <GridText.h>
  27. #include <LYCurses.h>
  28. #include <LYUtils.h>
  29. #include <LYStrings.h>
  30. #include <LYStructs.h>
  31. #include <LYGlobalDefs.h>
  32. #include <LYGetFile.h>
  33. #include <LYSignal.h>
  34. #include <LYMail.h>
  35. #include <LYList.h>
  36. #include <LYCharSets.h>
  37. #include <LYCharUtils.h>    /* LYUCTranslateBack... */
  38. #include <UCMap.h>
  39. #ifdef EXP_CHARTRANS_AUTOSWITCH
  40. #include <UCAuto.h>
  41. #endif /* EXP_CHARTRANS_AUTOSWITCH */
  42.  
  43. #include <LYexit.h>
  44. #include <LYLeaks.h>
  45.  
  46. #ifdef USE_COLOR_STYLE
  47. #include <AttrList.h>
  48. #include <LYHash.h>
  49.  
  50. unsigned int cached_styles[CACHEH][CACHEW];
  51.  
  52. #endif
  53.  
  54. #ifdef USE_COLOR_STYLE_UNUSED
  55. void LynxClearScreenCache NOARGS
  56. {
  57.     int i,j;
  58.  
  59.     CTRACE(tfp, "flushing cached screen styles\n");
  60.     for (i=0;i<CACHEH;i++)
  61.     for (j=0;j<CACHEW;j++)
  62.         cached_styles[i][j]=s_a;
  63. }
  64. #endif /* USE_COLOR_STYLE */
  65.  
  66. struct _HTStream {                      /* only know it as object */
  67.     CONST HTStreamClass *       isa;
  68.     /* ... */
  69. };
  70.  
  71. #define TITLE_LINES  1
  72. #define IS_UTF_EXTRA(ch) (text->T.output_utf8 && \
  73.               ((unsigned char)(ch)&0xc0) == 0x80)
  74.  
  75. #define FREE(x) if (x) {free(x); x = NULL;}
  76.  
  77. extern BOOL HTPassHighCtrlRaw;
  78. extern HTkcode kanji_code;
  79. extern HTCJKlang HTCJK;
  80.  
  81. /*    Exports
  82. */
  83. PUBLIC HText * HTMainText = NULL;        /* Equivalent of main window */
  84. PUBLIC HTParentAnchor * HTMainAnchor = NULL;    /* Anchor for HTMainText */
  85.  
  86. PUBLIC char * HTAppName = "Lynx";        /* Application name */
  87. PUBLIC char * HTAppVersion = LYNX_VERSION;    /* Application version */
  88.  
  89. PUBLIC int HTFormNumber = 0;
  90. PUBLIC int HTFormFields = 0;
  91. PUBLIC char * HTCurSelectGroup = NULL;        /* Form select group name */
  92. PRIVATE int HTCurSelectGroupCharset = -1;    /* ... and name's charset */
  93. PUBLIC int HTCurSelectGroupType = F_RADIO_TYPE;    /* Group type */
  94. PUBLIC char * HTCurSelectGroupSize = NULL;    /* Length of select */
  95. PRIVATE char * HTCurSelectedOptionValue = NULL;    /* Select choice */
  96.  
  97. PUBLIC char * checked_box = "[X]";
  98. PUBLIC char * unchecked_box = "[ ]";
  99. PUBLIC char * checked_radio = "(*)";
  100. PUBLIC char * unchecked_radio = "( )";
  101.  
  102. PUBLIC BOOLEAN underline_on = OFF;
  103. PUBLIC BOOLEAN bold_on      = OFF;
  104.  
  105. #if defined(USE_COLOR_STYLE)
  106. #define MAX_STYLES_ON_LINE 64
  107.  
  108. typedef struct _stylechange {
  109.     int    horizpos;    /* horizontal position of this change */
  110.     int    style;        /* which style to change to */
  111.     int    direction;    /* on or off */
  112.     int    previous;    /* previous style */
  113. } HTStyleChange;
  114. #endif
  115.  
  116. typedef struct _line {
  117.     struct _line    *next;
  118.     struct _line    *prev;
  119.     int unsigned    offset;        /* Implicit initial spaces */
  120.     int unsigned    size;        /* Number of characters */
  121.     BOOL    split_after;        /* Can we split after? */
  122.     BOOL    bullet;            /* Do we bullet? */
  123. #if defined(USE_COLOR_STYLE)
  124.     HTStyleChange    styles[MAX_STYLES_ON_LINE];
  125.     int    numstyles;
  126. #endif
  127.     char    data[1];        /* Space for terminator at least! */
  128. } HTLine;
  129.  
  130. #define LINE_SIZE(l) (sizeof(HTLine)+(l))    /* Allow for terminator */
  131.  
  132. typedef struct _TextAnchor {
  133.     struct _TextAnchor *    next;
  134.     int            number;        /* For user interface */
  135.     int            start;        /* Characters */
  136.     int            line_pos;    /* Position in text */
  137.     int            extent;     /* Characters */
  138.     int            line_num;    /* Place in document */
  139.     char *            hightext;    /* The link text */
  140.     char *            hightext2;    /* A second line*/
  141.     int            hightext2offset;/* offset from left */
  142.     int            link_type;    /* Normal, internal, or form? */
  143.     FormInfo *        input_field;    /* Info for form links */
  144.     BOOL            show_anchor;    /* Show the anchor? */
  145.     BOOL            inUnderline;    /* context is underlined */
  146.     HTChildAnchor *        anchor;
  147. } TextAnchor;
  148.  
  149. typedef struct _HTTabID {
  150.     char *            name;        /* ID value of TAB */
  151.     int            column;        /* Zero-based column value */
  152. } HTTabID;
  153.  
  154.  
  155. /*    Notes on struct _Htext:
  156. **    next_line is valid if state is false.
  157. **    top_of_screen line means the line at the top of the screen
  158. **            or just under the title if there is one.
  159. */
  160. struct _HText {
  161.     HTParentAnchor *    node_anchor;
  162.     HTLine *         last_line;
  163.     int            Lines;        /* Number of them */
  164.     int            chars;        /* Number of them */
  165.     TextAnchor *        first_anchor;    /* Singly linked list */
  166.     TextAnchor *        last_anchor;
  167.     HTList *        forms;        /* also linked internally */
  168.     int            last_anchor_number;    /* user number */
  169.     BOOL            source;        /* Is the text source? */
  170.     BOOL            toolbar;    /* Toolbar set? */
  171.     HTList *        tabs;        /* TAB IDs */
  172.     HTList *        hidden_links;    /* Content-less links ... */
  173.     int            hiddenlinkflag; /*  ... and how to treat them */
  174.     BOOL            no_cache;    /* Always refresh? */
  175.     char            LastChar;    /* For absorbing white space */
  176.     BOOL            IgnoreExcess;    /* Ignore chars at wrap point */
  177.  
  178. /* For Internal use: */
  179.     HTStyle *        style;            /* Current style */
  180.     int            display_on_the_fly;    /* Lines left */
  181.     int            top_of_screen;        /* Line number */
  182.     HTLine *        top_of_screen_line;    /* Top */
  183.     HTLine *        next_line;        /* Bottom + 1 */
  184.     int            permissible_split;    /* in last line */
  185.     BOOL            in_line_1;        /* of paragraph */
  186.     BOOL            stale;            /* Must refresh */
  187.     BOOL            page_has_target; /* has target on screen */
  188.  
  189.     HTkcode         kcode;            /* Kanji code? */
  190.     enum grid_state       { S_text, S_esc, S_dollar, S_paren,
  191.                 S_nonascii_text, S_dollar_paren,
  192.                 S_jisx0201_text }
  193.                 state;            /* Escape sequence? */
  194.     char            kanji_buf;        /* Lead multibyte */
  195.     int            in_sjis;        /* SJIS flag */
  196.     int            halted;         /* emergency halt */
  197.  
  198.     BOOL            have_8bit_chars;   /* Any non-ASCII chars? */
  199.     LYUCcharset *        UCI;           /* node_anchor UCInfo */
  200.     int            UCLYhndl;       /* charset we are fed */
  201.     UCTransParams        T;
  202.  
  203.     HTStream *        target;         /* Output stream */
  204.     HTStreamClass        targetClass;        /* Output routines */
  205. };
  206.  
  207. PRIVATE void HText_AddHiddenLink PARAMS((HText *text, TextAnchor *textanchor));
  208.  
  209. /*
  210.  *  Boring static variable used for moving cursor across
  211.  */
  212. #define UNDERSCORES(n) \
  213.  ((n) >= MAX_LINE ? underscore_string : &underscore_string[(MAX_LINE-1)] - (n))
  214.  
  215. /*
  216.  *    Memory leak fixed.
  217.  *    05-29-94 Lynx 2-3-1 Garrett Arch Blythe
  218.  *    Changed to arrays.
  219.  */
  220. PRIVATE char underscore_string[MAX_LINE + 1];
  221. PUBLIC char star_string[MAX_LINE + 1];
  222.  
  223. PRIVATE int ctrl_chars_on_this_line = 0; /* num of ctrl chars in current line */
  224.  
  225. PRIVATE HTStyle default_style =
  226.     { 0,  "(Unstyled)", "",
  227.     (HTFont)0, 1, HT_BLACK,        0, 0,
  228.     0, 0, 0, HT_LEFT,        1, 0,    0,
  229.     NO, NO, 0, 0,            0 };
  230.  
  231.  
  232.  
  233. PRIVATE HTList * loaded_texts = NULL;     /* A list of all those in memory */
  234. PUBLIC  HTList * search_queries = NULL;  /* isindex and whereis queries   */
  235. PRIVATE void free_all_texts NOARGS;
  236. PRIVATE int HText_TrueLineSize PARAMS((
  237.     HTLine *    line,
  238.     HText *        text,
  239.     BOOL        IgnoreSpaces));
  240.  
  241. #ifndef VMS            /* VMS has a better way - right? - kw */
  242. #define CHECK_FREE_MEM
  243. #endif
  244.  
  245. #ifdef CHECK_FREE_MEM
  246.  
  247. /*
  248.  *  text->halted = 1: have set fake 'Z' and output a message
  249.  *                 2: next time when HText_appendCharacter is called
  250.  *              it will append *** MEMORY EXHAUSTED ***, then set
  251.  *              to 3.
  252.  *           3: normal text output will be suppressed (but not anchors,
  253.  *              form fields etc.)
  254.  */
  255. PRIVATE void HText_halt NOARGS
  256. {
  257.     if (HTFormNumber > 0)
  258.     HText_DisableCurrentForm();
  259.     if (!HTMainText)
  260.     return;
  261.     if (HTMainText->halted < 2)
  262.     HTMainText->halted = 2;
  263. }
  264.  
  265. #define MIN_NEEDED_MEM 5000
  266.  
  267. /*
  268.  *  Check whether factor*min(bytes,MIN_NEEDED_MEM) is available,
  269.  *  or bytes if factor is 0.
  270.  *  MIN_NEEDED_MEM and factor together represent a security margin,
  271.  *  to take account of all the memory allocations where we don't check
  272.  *  and of buffers which may be emptied before HTCheckForInterupt()
  273.  *  is (maybe) called and other things happening, with some chance of
  274.  *  success.
  275.  *  This just tries to malloc() the to-be-checked-for amount of memory,
  276.  *  which might make the situation worse depending how allocation works.
  277.  *  There should be a better way... - kw
  278.  */
  279. PRIVATE BOOL mem_is_avail ARGS2(
  280.     size_t,    factor,
  281.     size_t,    bytes)
  282. {
  283.     void *p;
  284.     if (bytes < MIN_NEEDED_MEM && factor > 0)
  285.     bytes = MIN_NEEDED_MEM;
  286.     if (factor == 0)
  287.     factor = 1;
  288.     p = malloc(factor * bytes);
  289.     if (p) {
  290.     FREE(p);
  291.     return YES;
  292.     } else {
  293.     return NO;
  294.     }
  295. }
  296.  
  297. /*
  298.  *  Replacement for calloc which checks for "enough" free memory
  299.  *  (with some security margins) and tries various recovery actions
  300.  *  if deemed necessary. - kw
  301.  */
  302. PRIVATE void * LY_check_calloc ARGS2(
  303.     size_t,    nmemb,
  304.     size_t,    size)
  305. {
  306.     int i, n;
  307.     if (mem_is_avail(4, nmemb * size)) {
  308.     return (calloc(nmemb, size));
  309.     }
  310.     n = HTList_count(loaded_texts);
  311.     for (i = n - 1; i > 0; i--) {
  312.     HText * t = HTList_objectAt(loaded_texts, i);
  313.     if (t == HTMainText)
  314.         t = NULL;        /* shouldn't happen */
  315.     {
  316.     CTRACE(tfp, "\r *** Emergency freeing document %d/%d for '%s'%s!\n",
  317.             i + 1, n,
  318.             ((t && t->node_anchor &&
  319.               t->node_anchor->address) ?
  320.              t->node_anchor->address : "unknown anchor"),
  321.             ((t && t->node_anchor &&
  322.               t->node_anchor->post_data) ?
  323.              " with POST data" : ""));
  324.     }
  325.     HTList_removeObjectAt(loaded_texts, i);
  326.     HText_free(t);
  327.     if (mem_is_avail(4, nmemb * size)) {
  328.         return (calloc(nmemb, size));
  329.     }
  330.     }
  331.     LYFakeZap(YES);
  332.     if (!HTMainText || HTMainText->halted <= 1) {
  333.     if (!mem_is_avail(2, nmemb * size)) {
  334.         HText_halt();
  335.         if (mem_is_avail(0, 700)) {
  336.         HTAlert("Memory exhausted, display interrupted!");
  337.         }
  338.     } else {
  339.         if ((!HTMainText || HTMainText->halted == 0) &&
  340.         mem_is_avail(0, 700)) {
  341.         HTAlert("Memory exhausted, will interrupt transfer!");
  342.         if (HTMainText)
  343.             HTMainText->halted = 1;
  344.         }
  345.     }
  346.     }
  347.     return (calloc(nmemb, size));
  348. }
  349.  
  350. #define LY_CALLOC LY_check_calloc
  351.  
  352. #else  /* CHECK_FREE_MEM */
  353.  
  354.   /* using the regular calloc */
  355. #define LY_CALLOC calloc
  356.  
  357. #endif /* CHECK_FREE_MEM */
  358.  
  359. PRIVATE void HText_getChartransInfo ARGS1(
  360.     HText *,    me)
  361. {
  362.     me->UCLYhndl = HTAnchor_getUCLYhndl(me->node_anchor, UCT_STAGE_HTEXT);
  363.     if (me->UCLYhndl < 0) {
  364.     int chndl = current_char_set;
  365.     HTAnchor_setUCInfoStage(me->node_anchor, chndl,
  366.                 UCT_STAGE_HTEXT, UCT_SETBY_STRUCTURED);
  367.     me->UCLYhndl = HTAnchor_getUCLYhndl(me->node_anchor,
  368.                         UCT_STAGE_HTEXT);
  369.     }
  370.     me->UCI = HTAnchor_getUCInfoStage(me->node_anchor, UCT_STAGE_HTEXT);
  371. }
  372.  
  373. PRIVATE void PerFormInfo_free ARGS1(
  374.     PerFormInfo *,    form)
  375. {
  376.     if (form) {
  377.     FREE(form->accept_cs);
  378.     FREE(form->thisacceptcs);
  379.     FREE(form);
  380.     }
  381. }
  382.  
  383. PRIVATE void FormList_delete ARGS1(
  384.     HTList *,        forms)
  385. {
  386.     HTList *cur = forms;
  387.     PerFormInfo *form;
  388.     while ((form = (PerFormInfo *)HTList_nextObject(cur)) != NULL)
  389.     PerFormInfo_free(form);
  390.     HTList_delete(forms);
  391. }
  392.  
  393. /*            Creation Method
  394. **            ---------------
  395. */
  396. PUBLIC HText *    HText_new ARGS1(
  397.     HTParentAnchor *,    anchor)
  398. {
  399. #if defined(VMS) && defined(VAXC) && !defined(__DECC)
  400. #include <lib$routines.h>
  401.     int status, VMType=3, VMTotal;
  402. #endif /* VMS && VAXC && !__DECC */
  403.     HTLine * line = NULL;
  404.     HText * self = (HText *) calloc(1, sizeof(*self));
  405.     if (!self)
  406.     return self;
  407.  
  408. #if defined(VMS) && defined (VAXC) && !defined(__DECC)
  409.     status = lib$stat_vm(&VMType, &VMTotal);
  410.     CTRACE(tfp, "GridText: VMTotal = %d\n", VMTotal);
  411. #endif /* VMS && VAXC && !__DECC */
  412.  
  413.     if (!loaded_texts)    {
  414.     loaded_texts = HTList_new();
  415.     atexit(free_all_texts);
  416.     }
  417.  
  418.     /*
  419.      *  Links between anchors & documents are a 1-1 relationship. If
  420.      *  an anchor is already linked to a document we didn't call
  421.      *  HTuncache_current_document(), e.g., for the showinfo, options,
  422.      *  download, print, etc., temporary file URLs, so we'll check now
  423.      *  and free it before reloading. - Dick Wesseling (ftu@fi.ruu.nl)
  424.      */
  425.     if (anchor->document) {
  426.     HTList_removeObject(loaded_texts, anchor->document);
  427.     CTRACE(tfp, "GridText: Auto-uncaching\n") ;
  428.     ((HText *)anchor->document)->node_anchor = NULL;
  429.     HText_free((HText *)anchor->document);
  430.     anchor->document = NULL;
  431.     }
  432.  
  433.     HTList_addObject(loaded_texts, self);
  434. #if defined(VMS) && defined(VAXC) && !defined(__DECC)
  435.     while (HTList_count(loaded_texts) > HTCacheSize &&
  436.        VMTotal > HTVirtualMemorySize) {
  437. #else
  438.     if (HTList_count(loaded_texts) > HTCacheSize) {
  439. #endif /* VMS && VAXC && !__DECC */
  440.     CTRACE(tfp, "GridText: Freeing off cached doc.\n");
  441.     HText_free((HText *)HTList_removeFirstObject(loaded_texts));
  442. #if defined(VMS) && defined (VAXC) && !defined(__DECC)
  443.     status = lib$stat_vm(&VMType, &VMTotal);
  444.     CTRACE(tfp, "GridText: VMTotal reduced to %d\n", VMTotal);
  445. #endif /* VMS && VAXC && !__DECC */
  446.     }
  447.  
  448.     line = self->last_line = (HTLine *)calloc(1, LINE_SIZE(MAX_LINE));
  449.     if (line == NULL)
  450.     outofmem(__FILE__, "HText_New");
  451.     line->next = line->prev = line;
  452.     line->offset = line->size = 0;
  453. #ifdef USE_COLOR_STYLE
  454.     line->numstyles = 0;
  455. #endif
  456.     self->Lines = self->chars = 0;
  457.     self->first_anchor = self->last_anchor = NULL;
  458.     self->style = &default_style;
  459.     self->top_of_screen = 0;
  460.     self->node_anchor = anchor;
  461.     self->last_anchor_number = 0;    /* Numbering of them for references */
  462.     self->stale = YES;
  463.     self->toolbar = NO;
  464.     self->tabs = NULL;
  465.     /*
  466.      *  If we are going to render the List Page, always merge in hidden
  467.      *  links to get the numbering consistent if form fields are numbered
  468.      *  and show up as hidden links in the list of links. - kw
  469.      */
  470.     if (anchor->address && !strcmp(anchor->address, LYlist_temp_url()))
  471.     self->hiddenlinkflag = HIDDENLINKS_MERGE;
  472.     else
  473.     self->hiddenlinkflag = LYHiddenLinks;
  474.     self->hidden_links = NULL;
  475.     self->no_cache = ((anchor->no_cache || anchor->post_data) ?
  476.                               YES : NO);
  477.     self->LastChar = '\0';
  478.     self->IgnoreExcess = FALSE;
  479.  
  480.     if (HTOutputFormat == WWW_SOURCE)
  481.     self->source = YES;
  482.     else
  483.     self->source = NO;
  484.     HTAnchor_setDocument(anchor, (HyperDoc *)self);
  485.     HTFormNumber = 0;  /* no forms started yet */
  486.     HTMainText = self;
  487.     HTMainAnchor = anchor;
  488.     self->display_on_the_fly = 0;
  489.     self->kcode = NOKANJI;
  490.     self->state = S_text;
  491.     self->kanji_buf = '\0';
  492.     self->in_sjis = 0;
  493.     self->have_8bit_chars = NO;
  494.     HText_getChartransInfo(self);
  495.     UCSetTransParams(&self->T,
  496.              self->UCLYhndl, self->UCI,
  497.              current_char_set,
  498.              &LYCharSet_UC[current_char_set]);
  499.  
  500.     /*
  501.      *  Check the kcode setting if the anchor has a charset element. - FM
  502.      */
  503.     if (anchor->charset)
  504.     HText_setKcode(self, anchor->charset,
  505.                HTAnchor_getUCInfoStage(anchor, UCT_STAGE_HTEXT));
  506.  
  507.     /*
  508.      *    Memory leak fixed.
  509.      *  05-29-94 Lynx 2-3-1 Garrett Arch Blythe
  510.      *    Check to see if our underline and star_string need initialization
  511.      *        if the underline is not filled with dots.
  512.      */
  513.     if (underscore_string[0] != '.') {
  514.     /*
  515.      *  Create an array of dots for the UNDERSCORES macro. - FM
  516.      */
  517.     memset(underscore_string, '.', (MAX_LINE-1));
  518.     underscore_string[(MAX_LINE-1)] = '\0';
  519.     underscore_string[MAX_LINE] = '\0';
  520.     /*
  521.      *  Create an array of underscores for the STARS macro. - FM
  522.      */
  523.     memset(star_string, '_', (MAX_LINE-1));
  524.     star_string[(MAX_LINE-1)] = '\0';
  525.     star_string[MAX_LINE] = '\0';
  526.     }
  527.  
  528.     underline_on = FALSE; /* reset */
  529.     bold_on = FALSE;
  530.  
  531.     return self;
  532. }
  533.  
  534. /*                      Creation Method 2
  535. **                      ---------------
  536. **
  537. **      Stream is assumed open and left open.
  538. */
  539. PUBLIC HText *  HText_new2 ARGS2(
  540.     HTParentAnchor *,    anchor,
  541.     HTStream *,        stream)
  542.  
  543. {
  544.     HText * this = HText_new(anchor);
  545.  
  546.     if (stream) {
  547.     this->target = stream;
  548.     this->targetClass = *stream->isa;    /* copy action procedures */
  549.     }
  550.     return this;
  551. }
  552.  
  553. /*    Free Entire Text
  554. **    ----------------
  555. */
  556. PUBLIC void HText_free ARGS1(
  557.     HText *,    self)
  558. {
  559.     if (!self)
  560.     return;
  561.  
  562.     HTAnchor_setDocument(self->node_anchor, (HyperDoc *)0);
  563.  
  564.     while (YES) {    /* Free off line array */
  565.     HTLine * l = self->last_line;
  566.     if (l) {
  567.         l->next->prev = l->prev;
  568.         l->prev->next = l->next;    /* Unlink l */
  569.         self->last_line = l->prev;
  570.         if (l != self->last_line) {
  571.             FREE(l);
  572.         } else {
  573.             free(l);
  574.         }
  575.     }
  576.     if (l == self->last_line) {    /* empty */
  577.         l = NULL;
  578.         break;
  579.     }
  580.     };
  581.  
  582.     while (self->first_anchor) {        /* Free off anchor array */
  583.     TextAnchor * l = self->first_anchor;
  584.     self->first_anchor = l->next;
  585.  
  586.     if (l->link_type == INPUT_ANCHOR && l->input_field) {
  587.         /*
  588.          *  Free form fields.
  589.          */
  590.         if (l->input_field->type == F_OPTION_LIST_TYPE &&
  591.         l->input_field->select_list != NULL) {
  592.         /*
  593.          *  Free off option lists if present.
  594.          *  It should always be present for F_OPTION_LIST_TYPE
  595.          *  unless we had invalid markup which prevented
  596.          *  HText_setLastOptionValue from finishing its job
  597.          *  and left the input field in an insane state. - kw
  598.          */
  599.         OptionType *optptr = l->input_field->select_list;
  600.         OptionType *tmp;
  601.         while (optptr) {
  602.             tmp = optptr;
  603.             optptr = tmp->next;
  604.             FREE(tmp->name);
  605.             FREE(tmp->cp_submit_value);
  606.             FREE(tmp);
  607.         }
  608.         l->input_field->select_list = NULL;
  609.         /*
  610.          *  Don't free the value field on option
  611.          *  lists since it points to a option value
  612.          *  same for orig value.
  613.          */
  614.         l->input_field->value = NULL;
  615.         l->input_field->orig_value = NULL;
  616.         l->input_field->cp_submit_value = NULL;
  617.         l->input_field->orig_submit_value = NULL;
  618.         } else {
  619.         FREE(l->input_field->value);
  620.         FREE(l->input_field->orig_value);
  621.         FREE(l->input_field->cp_submit_value);
  622.         FREE(l->input_field->orig_submit_value);
  623.         }
  624.         FREE(l->input_field->name);
  625.         FREE(l->input_field->submit_action);
  626.         FREE(l->input_field->submit_enctype);
  627.         FREE(l->input_field->submit_title);
  628.  
  629.         FREE(l->input_field->accept_cs);
  630.  
  631.         FREE(l->input_field);
  632.     }
  633.  
  634.     FREE(l->hightext);
  635.     FREE(l->hightext2);
  636.  
  637.     FREE(l);
  638.     }
  639.     FormList_delete(self->forms);
  640.  
  641.     /*
  642.      *  Free the tabs list. - FM
  643.      */
  644.     if (self->tabs) {
  645.     HTTabID * Tab = NULL;
  646.     HTList * cur = self->tabs;
  647.  
  648.     while (NULL != (Tab = (HTTabID *)HTList_nextObject(cur))) {
  649.         FREE(Tab->name);
  650.         FREE(Tab);
  651.     }
  652.     HTList_delete(self->tabs);
  653.     self->tabs = NULL;
  654.     }
  655.  
  656.     /*
  657.      *  Free the hidden links list. - FM
  658.      */
  659.     if (self->hidden_links) {
  660.     char * href = NULL;
  661.     HTList * cur = self->hidden_links;
  662.  
  663.     while (NULL != (href = (char *)HTList_nextObject(cur)))
  664.         FREE(href);
  665.     HTList_delete(self->hidden_links);
  666.     self->hidden_links = NULL;
  667.     }
  668.  
  669.     /*
  670.      *  Invoke HTAnchor_delete() to free the node_anchor
  671.      *  if it is not a destination of other links. - FM
  672.      */
  673.     if (self->node_anchor) {
  674.     HTAnchor_resetUCInfoStage(self->node_anchor, -1, UCT_STAGE_STRUCTURED,
  675.                   UCT_SETBY_NONE);
  676.     HTAnchor_resetUCInfoStage(self->node_anchor, -1, UCT_STAGE_HTEXT,
  677.                   UCT_SETBY_NONE);
  678.     if (HTAnchor_delete(self->node_anchor))
  679.         /*
  680.          *  Make sure HTMainAnchor won't point
  681.          *  to an invalid structure. - KW
  682.          */
  683.         HTMainAnchor = NULL;
  684.     }
  685.  
  686.     FREE(self);
  687. }
  688.  
  689. /*        Display Methods
  690. **        ---------------
  691. */
  692.  
  693.  
  694. /*    Output a line
  695. **    -------------
  696. */
  697. PRIVATE int display_line ARGS2(
  698.     HTLine *,    line,
  699.     HText *,    text)
  700. {
  701.     register int i, j;
  702.     char buffer[7];
  703.     char *data;
  704.     size_t utf_extra = 0;
  705. #ifdef USE_COLOR_STYLE
  706.     int current_style = 0;
  707. #endif
  708.     char LastDisplayChar = ' ';
  709.  
  710.     /*
  711.      *  Set up the multibyte character buffer,
  712.      *  and clear the line to which we will be
  713.      *  writing.
  714.      */
  715.     buffer[0] = buffer[1] = buffer[2] = '\0';
  716.     clrtoeol();
  717.  
  718.     /*
  719.      *  Add offset, making sure that we do not
  720.      *  go over the COLS limit on the display.
  721.      */
  722.     j = (int)line->offset;
  723.     if (j > (int)LYcols - 1)
  724.     j = (int)LYcols - 1;
  725. #ifdef USE_SLANG
  726.     SLsmg_forward (j);
  727.     i = j;
  728. #else
  729. #ifdef USE_COLOR_STYLE
  730.     if (line->size == 0)
  731.     i = j;
  732.     else
  733. #endif
  734.     for (i = 0; i < j; i++)
  735.     addch (' ');
  736. #endif /* USE_SLANG */
  737.  
  738.     /*
  739.      *  Add the data, making sure that we do not
  740.      *  go over the COLS limit on the display.
  741.      */
  742.     data = line->data;
  743.     i++;
  744.     while ((i < LYcols) && ((buffer[0] = *data) != '\0')) {
  745.     data++;
  746.  
  747. #if defined(USE_COLOR_STYLE) || defined(SLSC)
  748. #define CStyle line->styles[current_style]
  749.  
  750.     while (current_style < line->numstyles &&
  751.            i >= (int) (CStyle.horizpos + line->offset + 1))
  752.     {
  753.         LynxChangeStyle (CStyle.style,CStyle.direction,CStyle.previous);
  754.         current_style++;
  755.     }
  756. #endif
  757.     switch (buffer[0]) {
  758.  
  759. #ifndef USE_COLOR_STYLE
  760.         case LY_UNDERLINE_START_CHAR:
  761.             if (dump_output_immediately && use_underscore) {
  762.             addch('_');
  763.             i++;
  764.         } else {
  765.             start_underline();
  766.         }
  767.         break;
  768.  
  769.         case LY_UNDERLINE_END_CHAR:
  770.             if (dump_output_immediately && use_underscore) {
  771.             addch('_');
  772.             i++;
  773.         } else {
  774.             stop_underline();
  775.         }
  776.         break;
  777.  
  778.         case LY_BOLD_START_CHAR:
  779.         start_bold();
  780.         break;
  781.  
  782.         case LY_BOLD_END_CHAR:
  783.         stop_bold ();
  784.         break;
  785. #endif
  786.  
  787.         case LY_SOFT_HYPHEN:
  788.             if (*data != '\0' ||
  789.             isspace((unsigned char)LastDisplayChar) ||
  790.             LastDisplayChar == '-') {
  791.             /*
  792.              *  Ignore the soft hyphen if it is not the last
  793.              *  character in the line.  Also ignore it if it
  794.              *  first character following the margin, or if it
  795.              *  is preceded by a white character (we loaded 'M'
  796.              *  into LastDisplayChar if it was a multibyte
  797.              *  character) or hyphen, though it should have
  798.              *  been excluded by HText_appendCharacter() or by
  799.              *  split_line() in those cases. - FM
  800.              */
  801.             break;
  802.         } else {
  803.             /*
  804.              *  Make it a hard hyphen and fall through. - FM
  805.              */
  806.             buffer[0] = '-';
  807.             i++;
  808.         }
  809.  
  810.         default:
  811.         i++;
  812.         if (text->T.output_utf8 && !isascii(buffer[0])) {
  813.             if ((*buffer & 0xe0) == 0xc0) {
  814.             utf_extra = 1;
  815.             } else if ((*buffer & 0xf0) == 0xe0) {
  816.             utf_extra = 2;
  817.             } else if ((*buffer & 0xf8) == 0xf0) {
  818.             utf_extra = 3;
  819.             } else if ((*buffer & 0xfc) == 0xf8) {
  820.             utf_extra = 4;
  821.             } else if ((*buffer & 0xfe) == 0xfc) {
  822.             utf_extra = 5;
  823.             } else {
  824.              /*
  825.               *  Garbage.
  826.               */
  827.             utf_extra = 0;
  828.             }
  829.             if (strlen(data) < utf_extra) {
  830.             /*
  831.              *  Shouldn't happen.
  832.              */
  833.             utf_extra = 0;
  834.             }
  835.             LastDisplayChar = 'M';
  836.         }
  837.         if (utf_extra) {
  838.             strncpy(&buffer[1], data, utf_extra);
  839.             buffer[utf_extra+1] = '\0';
  840.             addstr(buffer);
  841.             buffer[1] = '\0';
  842.             data += utf_extra;
  843.             utf_extra = 0;
  844.         } else if (HTCJK != NOCJK && !isascii(buffer[0])) {
  845.             /*
  846.              *  For CJK strings, by Masanobu Kimura.
  847.              */
  848.             buffer[1] = *data;
  849.             data++;
  850.             addstr(buffer);
  851.             buffer[1] = '\0';
  852.             /*
  853.              *  For now, load 'M' into LastDisplayChar,
  854.              *  but we should check whether it's white
  855.              *  and if so, use ' '.  I don't know if
  856.              *  there actually are white CJK characters,
  857.              *  and we're loading ' ' for multibyte
  858.              *  spacing characters in this code set,
  859.              *  but this will become an issue when
  860.              *  the development code set's multibyte
  861.              *  character handling is used. - FM
  862.              */
  863.             LastDisplayChar = 'M';
  864.         } else {
  865.             addstr(buffer);
  866.             LastDisplayChar = buffer[0];
  867.         }
  868.     } /* end of switch */
  869.     } /* end of while */
  870.  
  871.     /*
  872.      *  Add the return.
  873.      */
  874.     addch('\n');
  875.  
  876. #ifndef USE_COLOR_STYLE
  877.     stop_underline();
  878.     stop_bold();
  879. #else
  880.     while (current_style < line->numstyles)
  881.     {
  882.     LynxChangeStyle (CStyle.style, CStyle.direction, CStyle.previous);
  883.     current_style++;
  884.     }
  885. #undef CStyle
  886. #endif
  887.     return(0);
  888. }
  889.  
  890. /*    Output the title line
  891. **    ---------------------
  892. */
  893. PRIVATE void display_title ARGS1(
  894.     HText *,    text)
  895. {
  896.     char *title = NULL;
  897.     char percent[20];
  898.     char *cp = NULL;
  899.     unsigned char *tmp = NULL;
  900.     int i = 0, j = 0;
  901.  
  902.     /*
  903.      *  Make sure we have a text structure. - FM
  904.      */
  905.     if (!text)
  906.     return;
  907.  
  908.     lynx_start_title_color ();
  909. #ifdef USE_COLOR_STYLE
  910. /* turn the TITLE style on */
  911.     LynxChangeStyle(s_title, ABS_ON, 0);
  912. #endif /* USE_COLOR_STYLE */
  913.  
  914.     /*
  915.      *  Load the title field. - FM
  916.      */
  917.     StrAllocCopy(title,
  918.          (HTAnchor_title(text->node_anchor) ?
  919.           HTAnchor_title(text->node_anchor) : ""));
  920.  
  921.     /*
  922.      *  There shouldn't be any \n in the title field,
  923.      *  but if there is, lets kill it now.  Also trim
  924.      *  any trailing spaces. - FM
  925.      */
  926.     if ((cp = strchr(title,'\n')) != NULL)
  927.     *cp = '\0';
  928.     i = (*title ? (strlen(title) - 1) : 0);
  929.     while ((i >= 0) && title[i] == ' ')
  930.     title[i--] = '\0';
  931.  
  932.     /*
  933.      *  Generate the page indicator (percent) string.
  934.      */
  935.     if ((text->Lines + 1) > (display_lines)) {
  936.     /*
  937.      *  In a small attempt to correct the number of pages counted....
  938.      *    GAB 07-14-94
  939.      *
  940.      *  In a bigger attempt (hope it holds up 8-)....
  941.      *    FM 02-08-95
  942.      */
  943.     int total_pages =
  944.         (((text->Lines + 1) + (display_lines - 1))/(display_lines));
  945.     int start_of_last_page =
  946.         ((text->Lines + 1) < display_lines) ? 0 :
  947.         ((text->Lines + 1) - display_lines);
  948.  
  949.     sprintf(percent, " (p%d of %d)",
  950.         ((text->top_of_screen >= start_of_last_page) ?
  951.                          total_pages :
  952.                 ((text->top_of_screen + display_lines)/(display_lines))),
  953.         total_pages);
  954.     } else {
  955.     percent[0] = '\0';    /* Null string */
  956.     }
  957.  
  958.     /*
  959.      *  Generate and display the title string, with page indicator
  960.      *  if appropriate, preceded by the toolbar token if appropriate,
  961.      *  and truncated if necessary. - FM & KW
  962.      */
  963.     if (HTCJK != NOCJK) {
  964.     if (*title &&
  965.         (tmp = (unsigned char *)calloc(1, (strlen(title) + 1)))) {
  966.         if (kanji_code == EUC) {
  967.             TO_EUC((unsigned char *)title, tmp);
  968.         } else if (kanji_code == SJIS) {
  969.             TO_SJIS((unsigned char *)title, tmp);
  970.         } else {
  971.             for (i = 0, j = 0; title[i]; i++) {
  972.             if (title[i] != '\033') {
  973.                 tmp[j++] = title[i];
  974.             }
  975.         }
  976.         tmp[j] = '\0';
  977.         }
  978.         StrAllocCopy(title, (CONST char *)tmp);
  979.         FREE(tmp);
  980.     }
  981.     }
  982.     move(0, 0);
  983.     clrtoeol();
  984.     if (text->top_of_screen > 0 && HText_hasToolbar(text)) {
  985.     addch('#');
  986.     }
  987.     i = (LYcols - 1) - strlen(percent) - strlen(title);
  988.     if (i > 0) {
  989.     move(0, i);
  990.     } else {
  991.     /*
  992.      *  Note that this truncation is not taking into
  993.      *  account the possibility that multibyte
  994.      *  characters might be present. - FM
  995.      */
  996.     title[((LYcols - 2) - strlen(percent))] = '\0';
  997.     move(0, 1);
  998.     }
  999.     addstr(title);
  1000.     if (percent[0] != '\0')
  1001.     addstr(percent);
  1002.     addch('\n');
  1003.     FREE(title);
  1004.  
  1005. #ifdef USE_COLOR_STYLE
  1006. /* turn the TITLE style off */
  1007.     LynxChangeStyle(s_title, ABS_OFF, 0);
  1008. #endif /* USE_COLOR_STYLE */
  1009.     lynx_stop_title_color ();
  1010.  
  1011.     return;
  1012. }
  1013.  
  1014. /*    Output a page
  1015. **    -------------
  1016. */
  1017. PRIVATE void display_page ARGS3(
  1018.     HText *,    text,
  1019.     int,        line_number,
  1020.     char *,        target)
  1021. {
  1022.     HTLine * line = NULL;
  1023.     int i;
  1024.     char *cp, tmp[7];
  1025.     int last_screen;
  1026.     TextAnchor *Anchor_ptr = NULL;
  1027.     FormInfo *FormInfo_ptr;
  1028.     BOOL display_flag = FALSE;
  1029.     HTAnchor *link_dest;
  1030.     HTAnchor *link_dest_intl = NULL;
  1031.     static int last_nlinks = 0;
  1032.     static int charset_last_displayed = -1;
  1033.  
  1034.     lynx_mode = NORMAL_LYNX_MODE;
  1035.  
  1036.     if (text == NULL) {
  1037.     /*
  1038.      *  Check whether to force a screen clear to enable scrollback,
  1039.      *  or as a hack to fix a reverse clear screen problem for some
  1040.      *  curses packages. - shf@access.digex.net & seldon@eskimo.com
  1041.      */
  1042.     if (enable_scrollback) {
  1043.         addch('*');
  1044.         refresh();
  1045.         clear();
  1046.     }
  1047.     addstr("\n\nError accessing document!\nNo data available!\n");
  1048.     refresh();
  1049.     nlinks = 0;  /* set number of links to 0 */
  1050.     return;
  1051.     }
  1052.  
  1053.     tmp[0] = tmp[1] = tmp[2] = '\0';
  1054.     text->page_has_target = NO;
  1055.     last_screen = text->Lines - (display_lines - 2);
  1056.     line = text->last_line->prev;
  1057.  
  1058.     /*
  1059.      *  Constrain the line number to be within the document.
  1060.      */
  1061.     if (text->Lines < (display_lines))
  1062.     line_number = 0;
  1063.     else if (line_number > text->Lines)
  1064.     line_number = last_screen;
  1065.     else if (line_number < 0)
  1066.     line_number = 0;
  1067.  
  1068.     for (i = 0, line = text->last_line->next;        /* Find line */
  1069.      i < line_number && (line != text->last_line);
  1070.      i++, line = line->next) {            /* Loop */
  1071. #ifndef VMS
  1072.     if (!LYNoCore) {
  1073.         assert(line->next != NULL);
  1074.     } else if (line->next == NULL) {
  1075.         if (enable_scrollback) {
  1076.         addch('*');
  1077.         refresh();
  1078.         clear();
  1079.         }
  1080.         addstr("\n\nError drawing page!\nBad HText structure!\n");
  1081.         refresh();
  1082.         nlinks = 0;  /* set number of links to 0 */
  1083.         return;
  1084.     }
  1085. #else
  1086.     assert(line->next != NULL);
  1087. #endif /* !VMS */
  1088.     }
  1089.  
  1090.     if (LYlowest_eightbit[current_char_set] <= 255 &&
  1091.     (current_char_set != charset_last_displayed) &&
  1092.     /*
  1093.      *  current_char_set has changed since last invocation,
  1094.      *  and it's not just 7-bit.
  1095.      *  Also we don't want to do this for -dump and -source etc.
  1096.      */
  1097.     LYCursesON) {
  1098.     charset_last_displayed = current_char_set;
  1099. #ifdef EXP_CHARTRANS_AUTOSWITCH
  1100. #ifdef LINUX
  1101.     /*
  1102.      *  Currently implemented only for LINUX
  1103.      */
  1104.     stop_curses();
  1105.     UCChangeTerminalCodepage(current_char_set,
  1106.                  &LYCharSet_UC[current_char_set]);
  1107.     start_curses();
  1108. #endif /* LINUX */
  1109. #endif /* EXP_CHARTRANS_AUTOSWITCH */
  1110.     }
  1111.  
  1112.     /*
  1113.      *  Check whether to force a screen clear to enable scrollback,
  1114.      *  or as a hack to fix a reverse clear screen problem for some
  1115.      *  curses packages. - shf@access.digex.net & seldon@eskimo.com
  1116.      */
  1117.     if (enable_scrollback) {
  1118.     addch('*');
  1119.     refresh();
  1120.     clear();
  1121.     }
  1122.  
  1123.     text->top_of_screen = line_number;
  1124.     display_title(text);  /* will move cursor to top of screen */
  1125.     display_flag=TRUE;
  1126.  
  1127.     /*
  1128.      *  Output the page.
  1129.      */
  1130.     if (line) {
  1131.     char *data;
  1132.     int offset, HitOffset, LenNeeded;
  1133.     for (i = 0; i < (display_lines); i++)  {
  1134.         /*
  1135.          *  Verify and display each line.
  1136.          */
  1137. #ifndef VMS
  1138.         if (!LYNoCore) {
  1139.         assert(line != NULL);
  1140.         } else if (line == NULL) {
  1141.         if (enable_scrollback) {
  1142.             addch('*');
  1143.             refresh();
  1144.             clear();
  1145.         }
  1146.         addstr("\n\nError drawing page!\nBad HText structure!\n");
  1147.         refresh();
  1148.         nlinks = 0;  /* set number of links to 0 */
  1149.         return;
  1150.         }
  1151. #else
  1152.         assert(line != NULL);
  1153. #endif /* !VMS */
  1154.         display_line(line, text);
  1155.  
  1156. #if defined(FANCY_CURSES) || defined(USE_SLANG)
  1157.         /*
  1158.          *  If the target is on this line, recursively
  1159.          *  seek and emphasize it. - FM
  1160.          */
  1161.         data = (char *)line->data;
  1162.         offset = (int)line->offset;
  1163.         while ((target && *target) &&
  1164.            (case_sensitive ?
  1165.             (cp = LYno_attr_mbcs_strstr(data,
  1166.                         target,
  1167.                         text->T.output_utf8,
  1168.                         &HitOffset,
  1169.                         &LenNeeded)) != NULL :
  1170.             (cp = LYno_attr_mbcs_case_strstr(data,
  1171.                              target,
  1172.                         text->T.output_utf8,
  1173.                         &HitOffset,
  1174.                         &LenNeeded)) != NULL) &&
  1175.            ((int)line->offset + LenNeeded) < LYcols) {
  1176.         int itmp = 0;
  1177.         int written = 0;
  1178.         int x_pos = offset + (int)(cp - data);
  1179.         int len = strlen(target);
  1180.         size_t utf_extra = 0;
  1181.         int y;
  1182.  
  1183.         text->page_has_target = YES;
  1184.  
  1185.         /*
  1186.          *  Start the emphasis.
  1187.          */
  1188.         LYstartTargetEmphasis();
  1189.  
  1190.         /*
  1191.          *  Output the target characters.
  1192.          */
  1193.         for (;
  1194.              written < len && (tmp[0] = data[itmp]) != '\0';
  1195.              itmp++)  {
  1196.             if (IsSpecialAttrChar(tmp[0])) {
  1197.             /*
  1198.              *  Ignore special characters.
  1199.              */
  1200.             x_pos--;
  1201.  
  1202.             } else if (cp == &data[itmp]) {
  1203.             /*
  1204.              *  First printable character of target.
  1205.              */
  1206.             move((i + 1), x_pos);
  1207.             if (text->T.output_utf8 && !isascii(tmp[0])) {
  1208.                 if ((*tmp & 0xe0) == 0xc0) {
  1209.                 utf_extra = 1;
  1210.                 } else if ((*tmp & 0xf0) == 0xe0) {
  1211.                 utf_extra = 2;
  1212.                 } else if ((*tmp & 0xf8) == 0xf0) {
  1213.                 utf_extra = 3;
  1214.                 } else if ((*tmp & 0xfc) == 0xf8) {
  1215.                 utf_extra = 4;
  1216.                 } else if ((*tmp & 0xfe) == 0xfc) {
  1217.                 utf_extra = 5;
  1218.                 } else {
  1219.                 /*
  1220.                  *  Garbage.
  1221.                  */
  1222.                 utf_extra = 0;
  1223.                 }
  1224.                 if (strlen(&line->data[itmp+1]) < utf_extra) {
  1225.                 /*
  1226.                  *  Shouldn't happen.
  1227.                  */
  1228.                 utf_extra = 0;
  1229.                 }
  1230.             }
  1231.             if (utf_extra) {
  1232.                 strncpy(&tmp[1], &line->data[itmp+1], utf_extra);
  1233.                 tmp[utf_extra+1] = '\0';
  1234.                 itmp += utf_extra;
  1235.                 addstr(tmp);
  1236.                 tmp[1] = '\0';
  1237.                 written += (utf_extra + 1);
  1238.                 utf_extra = 0;
  1239.             } else if (HTCJK != NOCJK && !isascii(tmp[0])) {
  1240.                 /*
  1241.                  *  For CJK strings, by Masanobu Kimura.
  1242.                  */
  1243.                 tmp[1] = data[++itmp];
  1244.                 addstr(tmp);
  1245.                 tmp[1] = '\0';
  1246.                 written += 2;
  1247.             } else {
  1248.                 addstr(tmp);
  1249.                 written++;
  1250.             }
  1251.  
  1252.             } else if (&data[itmp] > cp) {
  1253.             /*
  1254.              *  Output all the other printable target chars.
  1255.              */
  1256.             if (text->T.output_utf8 && !isascii(tmp[0])) {
  1257.                 if ((*tmp & 0xe0) == 0xc0) {
  1258.                 utf_extra = 1;
  1259.                 } else if ((*tmp & 0xf0) == 0xe0) {
  1260.                 utf_extra = 2;
  1261.                 } else if ((*tmp & 0xf8) == 0xf0) {
  1262.                 utf_extra = 3;
  1263.                 } else if ((*tmp & 0xfc) == 0xf8) {
  1264.                 utf_extra = 4;
  1265.                 } else if ((*tmp & 0xfe) == 0xfc) {
  1266.                 utf_extra = 5;
  1267.                 } else {
  1268.                 /*
  1269.                  *  Garbage.
  1270.                  */
  1271.                 utf_extra = 0;
  1272.                 }
  1273.                 if (strlen(&line->data[itmp+1]) < utf_extra) {
  1274.                 /*
  1275.                  *  Shouldn't happen.
  1276.                  */
  1277.                 utf_extra = 0;
  1278.                 }
  1279.             }
  1280.             if (utf_extra) {
  1281.                 strncpy(&tmp[1], &line->data[itmp+1], utf_extra);
  1282.                 tmp[utf_extra+1] = '\0';
  1283.                 itmp += utf_extra;
  1284.                 addstr(tmp);
  1285.                 tmp[1] = '\0';
  1286.                 written += (utf_extra + 1);
  1287.                 utf_extra = 0;
  1288.             } else if (HTCJK != NOCJK && !isascii(tmp[0])) {
  1289.                 /*
  1290.                  *  For CJK strings, by Masanobu Kimura.
  1291.                  */
  1292.                 tmp[1] = data[++itmp];
  1293.                 addstr(tmp);
  1294.                 tmp[1] = '\0';
  1295.                 written += 2;
  1296.             } else {
  1297.                 addstr(tmp);
  1298.                 written++;
  1299.             }
  1300.             }
  1301.         }
  1302.  
  1303.         /*
  1304.          *  Stop the emphasis, and reset the offset and
  1305.          *  data pointer for our current position in the
  1306.          *  line. - FM
  1307.          */
  1308.         LYstopTargetEmphasis();
  1309.         LYGetYX(y, offset);
  1310.         data = (char *)&data[itmp];
  1311.  
  1312.         /*
  1313.          *  Adjust the cursor position, should we be at
  1314.          *  the end of the line, or not have another hit
  1315.          *  in it. - FM
  1316.          */
  1317.         move((i + 2), 0);
  1318.         }
  1319. #endif /* FANCY CURSES || USE_SLANG */
  1320.  
  1321.         /*
  1322.          *  Stop if this is the last line.  Otherwise, make sure
  1323.          *  display_flag is set and process the next line. - FM
  1324.          */
  1325.         if (line == text->last_line) {
  1326.         /*
  1327.          *  Clear remaining lines of display.
  1328.          */
  1329.         for (i++; i < (display_lines); i++) {
  1330.             move((i + 1), 0);
  1331.             clrtoeol();
  1332.         }
  1333.         break;
  1334.         }
  1335.         display_flag = TRUE;
  1336.         line = line->next;
  1337.     }
  1338.     }
  1339.  
  1340.     text->next_line = line;    /* Line after screen */
  1341.     text->stale = NO;        /* Display is up-to-date */
  1342.  
  1343.     /*
  1344.      *  Add the anchors to Lynx structures.
  1345.      */
  1346.     nlinks = 0;
  1347.     for (Anchor_ptr=text->first_anchor;  Anchor_ptr != NULL &&
  1348.         Anchor_ptr->line_num <= line_number+(display_lines);
  1349.                         Anchor_ptr = Anchor_ptr->next) {
  1350.  
  1351.     if (Anchor_ptr->line_num >= line_number &&
  1352.         Anchor_ptr->line_num < line_number+(display_lines)) {
  1353.         /*
  1354.          *  Load normal hypertext anchors.
  1355.          */
  1356.         if (Anchor_ptr->show_anchor && Anchor_ptr->hightext &&
  1357.             strlen(Anchor_ptr->hightext) > 0 &&
  1358.             (Anchor_ptr->link_type & HYPERTEXT_ANCHOR)) {
  1359.  
  1360.         links[nlinks].hightext    = Anchor_ptr->hightext;
  1361.         links[nlinks].hightext2 = Anchor_ptr->hightext2;
  1362.         links[nlinks].hightext2_offset = Anchor_ptr->hightext2offset;
  1363.         links[nlinks].inUnderline = Anchor_ptr->inUnderline;
  1364.  
  1365.         links[nlinks].anchor_number = Anchor_ptr->number;
  1366.         links[nlinks].anchor_line_num = Anchor_ptr->line_num;
  1367.  
  1368.         link_dest = HTAnchor_followMainLink(
  1369.                          (HTAnchor *)Anchor_ptr->anchor);
  1370.         {
  1371.             /*
  1372.              *    Memory leak fixed 05-27-94
  1373.              *    Garrett Arch Blythe
  1374.              */
  1375.                 auto char *cp_AnchorAddress = NULL;
  1376.             if (traversal)
  1377.                 cp_AnchorAddress = stub_HTAnchor_address(link_dest);
  1378.             else {
  1379. #ifndef DONT_TRACK_INTERNAL_LINKS
  1380.             if (Anchor_ptr->link_type == INTERNAL_LINK_ANCHOR) {
  1381.                 link_dest_intl = HTAnchor_followTypedLink(
  1382.                 (HTAnchor *)Anchor_ptr->anchor, LINK_INTERNAL);
  1383.                 if (link_dest_intl && link_dest_intl != link_dest) {
  1384.  
  1385.                 CTRACE(tfp,
  1386.                     "display_page: unexpected typed link to %s!\n",
  1387.                         link_dest_intl->parent->address);
  1388.                 link_dest_intl = NULL;
  1389.                 }
  1390.             } else
  1391.                 link_dest_intl = NULL;
  1392.             if (link_dest_intl) {
  1393.                 char *cp2 = HTAnchor_address(link_dest_intl);
  1394.                 cp_AnchorAddress = cp2;
  1395.             } else
  1396. #endif
  1397.                 cp_AnchorAddress = HTAnchor_address(link_dest);
  1398.             }
  1399.             FREE(links[nlinks].lname);
  1400.  
  1401.             if (cp_AnchorAddress != NULL)
  1402.             links[nlinks].lname = cp_AnchorAddress;
  1403.             else
  1404.             StrAllocCopy(links[nlinks].lname, empty_string);
  1405.         }
  1406.  
  1407.         links[nlinks].lx = Anchor_ptr->line_pos;
  1408.         links[nlinks].ly = ((Anchor_ptr->line_num + 1) - line_number);
  1409.         if (link_dest_intl)
  1410.             links[nlinks].type = WWW_INTERN_LINK_TYPE;
  1411.         else
  1412.             links[nlinks].type = WWW_LINK_TYPE;
  1413.         links[nlinks].target = empty_string;
  1414.         links[nlinks].form = NULL;
  1415.  
  1416.             nlinks++;
  1417.         display_flag = TRUE;
  1418.  
  1419.         } else if (Anchor_ptr->link_type == INPUT_ANCHOR
  1420.             && Anchor_ptr->input_field->type != F_HIDDEN_TYPE) {
  1421.         /*
  1422.          *  Handle form fields.
  1423.          */
  1424.         lynx_mode = FORMS_LYNX_MODE;
  1425.  
  1426.         FormInfo_ptr = Anchor_ptr->input_field;
  1427.  
  1428.         links[nlinks].anchor_number = Anchor_ptr->number;
  1429.         links[nlinks].anchor_line_num = Anchor_ptr->line_num;
  1430.  
  1431.         links[nlinks].form = FormInfo_ptr;
  1432.         links[nlinks].lx = Anchor_ptr->line_pos;
  1433.         links[nlinks].ly = ((Anchor_ptr->line_num + 1) - line_number);
  1434.         links[nlinks].type = WWW_FORM_LINK_TYPE;
  1435.         links[nlinks].inUnderline = Anchor_ptr->inUnderline;
  1436.         links[nlinks].target = empty_string;
  1437.         StrAllocCopy(links[nlinks].lname, empty_string);
  1438.  
  1439.         if (FormInfo_ptr->type == F_RADIO_TYPE) {
  1440.             if (FormInfo_ptr->num_value)
  1441.             links[nlinks].hightext = checked_radio;
  1442.             else
  1443.             links[nlinks].hightext = unchecked_radio;
  1444.  
  1445.         } else if (FormInfo_ptr->type == F_CHECKBOX_TYPE) {
  1446.             if (FormInfo_ptr->num_value)
  1447.             links[nlinks].hightext = checked_box;
  1448.             else
  1449.             links[nlinks].hightext = unchecked_box;
  1450.  
  1451.         } else if (FormInfo_ptr->type == F_PASSWORD_TYPE) {
  1452.             links[nlinks].hightext = STARS(strlen(FormInfo_ptr->value));
  1453.  
  1454.         } else {  /* TEXT type */
  1455.             links[nlinks].hightext = FormInfo_ptr->value;
  1456.         }
  1457.  
  1458.         /*
  1459.          *  Never a second line on form types.
  1460.          */
  1461.         links[nlinks].hightext2 = NULL;
  1462.         links[nlinks].hightext2_offset = 0;
  1463.  
  1464.         nlinks++;
  1465.             /*
  1466.          *  Bold the link after incrementing nlinks.
  1467.          */
  1468.         highlight(OFF, (nlinks - 1), target);
  1469.  
  1470.         display_flag = TRUE;
  1471.  
  1472.         } else {
  1473.         /*
  1474.          *  Not showing anchor.
  1475.          */
  1476.         if (Anchor_ptr->hightext && *Anchor_ptr->hightext)
  1477.             CTRACE(tfp,
  1478.                 "\nGridText: Not showing link, hightext=%s\n",
  1479.                 Anchor_ptr->hightext);
  1480.         }
  1481.     }
  1482.  
  1483.     if (Anchor_ptr == text->last_anchor)
  1484.         /*
  1485.          *  No more links in document. - FM
  1486.          */
  1487.         break;
  1488.  
  1489.     if (nlinks == MAXLINKS) {
  1490.         /*
  1491.          *  Links array is full.  If interactive, tell user
  1492.          *  to use half-page or two-line scrolling. - FM
  1493.          */
  1494.         if (LYCursesON) {
  1495.         _statusline(MAXLINKS_REACHED);
  1496.         sleep(AlertSecs);
  1497.         }
  1498.         CTRACE(tfp, "\ndisplay_page: MAXLINKS reached.\n");
  1499.         break;
  1500.     }
  1501.     }
  1502.  
  1503.     /*
  1504.      *  Free any un-reallocated links[] entries
  1505.      *  from the previous page draw. - FM
  1506.      */
  1507.     for (i = nlinks; i < last_nlinks; i++)
  1508.     FREE(links[i].lname);
  1509.     last_nlinks = nlinks;
  1510.  
  1511.     /*
  1512.      *  If Anchor_ptr is not NULL and is not pointing to the last
  1513.      *  anchor, then there are anchors farther down in the document,
  1514.      *  and we need to flag this for traversals.
  1515.      */
  1516.     more_links = FALSE;
  1517.     if (traversal && Anchor_ptr) {
  1518.     if (Anchor_ptr->next)
  1519.         more_links = TRUE;
  1520.     }
  1521.  
  1522.     if (!display_flag) {
  1523.     /*
  1524.      *  Nothing on the page.
  1525.      */
  1526.     addstr("\n     Document is empty");
  1527.     }
  1528.  
  1529.     if (HTCJK != NOCJK || text->T.output_utf8 || TRACE) {
  1530.     /*
  1531.      *  For non-multibyte curses.
  1532.      */
  1533.     lynx_force_repaint();
  1534.     }
  1535.     refresh();
  1536.  
  1537. }
  1538.  
  1539.  
  1540. /*            Object Building methods
  1541. **            -----------------------
  1542. **
  1543. **    These are used by a parser to build the text in an object
  1544. */
  1545. PUBLIC void HText_beginAppend ARGS1(
  1546.     HText *,    text)
  1547. {
  1548.     text->permissible_split = 0;
  1549.     text->in_line_1 = YES;
  1550.  
  1551. }
  1552.  
  1553.  
  1554. /*    Add a new line of text
  1555. **    ----------------------
  1556. **
  1557. ** On entry,
  1558. **
  1559. **    split    is zero for newline function, else number of characters
  1560. **        before split.
  1561. **    text->display_on_the_fly
  1562. **        may be set to indicate direct output of the finished line.
  1563. ** On exit,
  1564. **        A new line has been made, justified according to the
  1565. **        current style. Text after the split (if split nonzero)
  1566. **        is taken over onto the next line.
  1567. **
  1568. **        If display_on_the_fly is set, then it is decremented and
  1569. **        the finished line is displayed.
  1570. */
  1571. #define new_line(text) split_line(text, 0)
  1572.  
  1573. PRIVATE void split_line ARGS2(
  1574.     HText *,    text,
  1575.     int,        split)
  1576. {
  1577.     HTStyle * style = text->style;
  1578.     HTLine * temp;
  1579.     int spare;
  1580. #if defined(USE_COLOR_STYLE)
  1581.     int inew;
  1582. #endif
  1583.     int indent = text->in_line_1 ?
  1584.       text->style->indent1st : text->style->leftIndent;
  1585.     TextAnchor * a;
  1586.     int CurLine = text->Lines;
  1587.     int HeadTrim = 0;
  1588.     int SpecialAttrChars = 0;
  1589.     int TailTrim = 0;
  1590.  
  1591.     /*
  1592.      *  Make new line.
  1593.      */
  1594.     HTLine * previous = text->last_line;
  1595.     int ctrl_chars_on_previous_line = 0;
  1596.     char * cp;
  1597.     HTLine * line = (HTLine *)LY_CALLOC(1, LINE_SIZE(MAX_LINE));
  1598.     if (line == NULL)
  1599.     outofmem(__FILE__, "split_line_1");
  1600.  
  1601.     ctrl_chars_on_this_line = 0; /*reset since we are going to a new line*/
  1602.     text->LastChar = ' ';
  1603.  
  1604.     CTRACE(tfp,"GridText: split_line called\n");
  1605.  
  1606.     text->Lines++;
  1607.  
  1608.     previous->next->prev = line;
  1609.     line->prev = previous;
  1610.     line->next = previous->next;
  1611. #if defined(USE_COLOR_STYLE)
  1612. #define LastStyle (previous->numstyles-1)
  1613.     line->numstyles = 0;
  1614.     inew = MAX_STYLES_ON_LINE - 1;
  1615.     spare = previous->numstyles;
  1616.     while (previous->numstyles && inew >= 0) {
  1617.     if (previous->numstyles >= 2 &&
  1618.         previous->styles[LastStyle].style
  1619.         == previous->styles[previous->numstyles-2].style &&
  1620.         previous->styles[LastStyle].horizpos
  1621.         == previous->styles[previous->numstyles-2].horizpos &&
  1622.         ((previous->styles[LastStyle].direction == STACK_OFF &&
  1623.           previous->styles[previous->numstyles-2].direction == STACK_ON) ||
  1624.          (previous->styles[LastStyle].direction == ABS_OFF &&
  1625.           previous->styles[previous->numstyles-2].direction == ABS_ON) ||
  1626.          (previous->styles[LastStyle].direction == ABS_ON &&
  1627.           previous->styles[previous->numstyles-2].direction == ABS_OFF)
  1628.         )) {
  1629.         /*
  1630.          *  Discard pairs of ON/OFF for the same color style, but only
  1631.          *  if they appear at the same position. - kw
  1632.          */
  1633.         previous->numstyles -= 2;
  1634.         if (spare > previous->numstyles)
  1635.         spare = previous->numstyles;
  1636.     } else if (spare > 0 && previous->styles[spare - 1].direction &&
  1637.            previous->numstyles < MAX_STYLES_ON_LINE) {
  1638.         /*
  1639.          *  The index variable spare walks backwards through the
  1640.          *  list of color style changes on the previous line, trying
  1641.          *  to find an ON change which isn't followed by a
  1642.          *  corresponding OFF.  When it finds one, the missing OFF
  1643.          *  change is appended to the end, and an ON change is added
  1644.          *  at the beginning of the current line.  The OFF change
  1645.          *  appended to the previous line may get removed again in
  1646.          *  the next iteration. - kw
  1647.          */
  1648.         line->styles[inew].horizpos = 0;
  1649.         line->styles[inew].direction = ON;
  1650.         line->styles[inew].style = previous->styles[spare - 1].style;
  1651.         inew --;
  1652.         line->numstyles ++;
  1653.         previous->styles[previous->numstyles].style = line->styles[inew + 1].style;
  1654.  
  1655.         previous->styles[previous->numstyles].direction = ABS_OFF;
  1656.         previous->styles[previous->numstyles].horizpos = previous->size;
  1657.         previous->numstyles++;
  1658.         spare --;
  1659.     } else if (spare >= 2 &&
  1660.            previous->styles[spare - 1].style == previous->styles[spare - 2].style &&
  1661.            ((previous->styles[spare - 1].direction == STACK_OFF &&
  1662.              previous->styles[spare - 2].direction == STACK_ON) ||
  1663.             (previous->styles[spare - 1].direction == ABS_OFF &&
  1664.              previous->styles[spare - 2].direction == ABS_ON) ||
  1665.             (previous->styles[spare - 1].direction == STACK_ON &&
  1666.              previous->styles[spare - 2].direction == STACK_OFF) ||
  1667.             (previous->styles[spare - 1].direction == ABS_ON &&
  1668.              previous->styles[spare - 2].direction == ABS_OFF)
  1669.                )) {
  1670.            /*
  1671.         *  Skip pairs of adjacent ON/OFF or OFF/ON changes.
  1672.         */
  1673.         spare -= 2;
  1674.     } else if (spare && !previous->styles[spare - 1].direction) {
  1675.         /*
  1676.          *  Found an OFF change not part of a matched pair.
  1677.          *  Assume it is safer to leave whatever comes before
  1678.          *  it on the previous line alone.  Setting spare to 0
  1679.          *  ensures that it won't be used in a following
  1680.          *  iteration. - kw
  1681.          */
  1682.         spare = 0;
  1683.     } else {
  1684.         /*
  1685.          *  Nothing applied, so we are done with the loop. - kw
  1686.          */
  1687.         break;
  1688.     }
  1689.     }
  1690.     if (previous->numstyles > 0 && previous->styles[LastStyle].direction) {
  1691.  
  1692.     CTRACE(tfp, "%s\n%s%s\n",
  1693.             "........... Too many character styles on line:",
  1694.             "........... ", previous->data);
  1695.     }
  1696.     if (line->numstyles > 0 && line->numstyles < MAX_STYLES_ON_LINE) {
  1697.     int n;
  1698.     inew ++;
  1699.     for (n = line->numstyles; n >= 0; n--)
  1700.         line->styles[n + inew] = line->styles[n];
  1701.     } else
  1702.     if (line->numstyles == 0)
  1703.     /* FIXME: RJP - shouldn't use 0xffffffff for largest integer */
  1704.     line->styles[0].horizpos = 0xffffffff;
  1705.     if (previous->numstyles == 0)
  1706.     previous->styles[0].horizpos = 0xffffffff;
  1707. #endif
  1708.     previous->next = line;
  1709.     text->last_line = line;
  1710.     line->size = 0;
  1711.     line->offset = 0;
  1712.     text->permissible_split = 0;  /* 12/13/93 */
  1713.     line->data[0] = '\0';
  1714.  
  1715.     /*
  1716.      *  If we are not splitting and need an underline char, add it now. - FM
  1717.      */
  1718.     if ((split < 1) &&
  1719.     !(dump_output_immediately && use_underscore) && underline_on) {
  1720.     line->data[line->size++] = LY_UNDERLINE_START_CHAR;
  1721.     line->data[line->size] = '\0';
  1722.     ctrl_chars_on_this_line++;
  1723.     }
  1724.     /*
  1725.      *  If we are not splitting and need a bold char, add it now. - FM
  1726.      */
  1727.     if ((split < 1) && bold_on) {
  1728.     line->data[line->size++] = LY_BOLD_START_CHAR;
  1729.     line->data[line->size] = '\0';
  1730.     ctrl_chars_on_this_line++;
  1731.     }
  1732.  
  1733.     /*
  1734.      *  Split at required point
  1735.      */
  1736.     if (split > 0) {    /* Delete space at "split" splitting line */
  1737.     char *p, *prevdata = previous->data, *linedata = line->data;
  1738.     unsigned int plen;
  1739.     int i;
  1740.  
  1741.     /*
  1742.      *  Split the line. - FM
  1743.      */
  1744.     prevdata[previous->size] = '\0';
  1745.     previous->size = split;
  1746.  
  1747.     /*
  1748.      *  Trim any spaces or soft hyphens from the beginning
  1749.      *  of our new line. - FM
  1750.      */
  1751.     p = prevdata + split;
  1752.     while (*p == ' ' || *p == LY_SOFT_HYPHEN) {
  1753.         p++;
  1754.         HeadTrim++;
  1755.     }
  1756.     plen = strlen(p);
  1757.  
  1758.     /*
  1759.      *  Add underline char if needed. - FM
  1760.      */
  1761.     if (!(dump_output_immediately && use_underscore)) {
  1762.         /*
  1763.          * Make sure our global flag is correct. - FM
  1764.          */
  1765.         underline_on = NO;
  1766.         for (i = (split-1); i >= 0; i--) {
  1767.             if (prevdata[i] == LY_UNDERLINE_END_CHAR) {
  1768.             break;
  1769.         }
  1770.         if (prevdata[i] == LY_UNDERLINE_START_CHAR) {
  1771.             underline_on = YES;
  1772.             break;
  1773.         }
  1774.         }
  1775.         /*
  1776.          *  Act on the global flag if set above. - FM
  1777.          */
  1778.         if (underline_on && *p != LY_UNDERLINE_END_CHAR) {
  1779.             linedata[line->size++] = LY_UNDERLINE_START_CHAR;
  1780.         linedata[line->size] = '\0';
  1781.         ctrl_chars_on_this_line++;
  1782.         SpecialAttrChars++;
  1783.         }
  1784.         for (i = (plen - 1); i >= 0; i--) {
  1785.         if (p[i] == LY_UNDERLINE_START_CHAR) {
  1786.             underline_on = YES;
  1787.             break;
  1788.         }
  1789.         if (p[i] == LY_UNDERLINE_END_CHAR) {
  1790.             underline_on = NO;
  1791.             break;
  1792.         }
  1793.         }
  1794.         for (i = (plen - 1); i >= 0; i--) {
  1795.             if (p[i] == LY_UNDERLINE_START_CHAR ||
  1796.             p[i] == LY_UNDERLINE_END_CHAR) {
  1797.             ctrl_chars_on_this_line++;
  1798.         }
  1799.         }
  1800.     }
  1801.  
  1802.     /*
  1803.      *  Add bold char if needed, first making
  1804.      *  sure that our global flag is correct. - FM
  1805.      */
  1806.     bold_on = NO;
  1807.     for (i = (split - 1); i >= 0; i--) {
  1808.         if (prevdata[i] == LY_BOLD_END_CHAR) {
  1809.         break;
  1810.         }
  1811.         if (prevdata[i] == LY_BOLD_START_CHAR) {
  1812.             bold_on = YES;
  1813.         break;
  1814.         }
  1815.     }
  1816.     /*
  1817.      *  Act on the global flag if set above. - FM
  1818.      */
  1819.     if (bold_on && *p != LY_BOLD_END_CHAR) {
  1820.         linedata[line->size++] = LY_BOLD_START_CHAR;
  1821.         linedata[line->size] = '\0';
  1822.         ctrl_chars_on_this_line++;
  1823.         SpecialAttrChars++;;
  1824.     }
  1825.     for (i = (plen - 1); i >= 0; i--) {
  1826.         if (p[i] == LY_BOLD_START_CHAR) {
  1827.             bold_on = YES;
  1828.         break;
  1829.         }
  1830.         if (p[i] == LY_BOLD_END_CHAR) {
  1831.         bold_on = NO;
  1832.         break;
  1833.         }
  1834.     }
  1835.     for (i = (plen - 1); i >= 0; i--) {
  1836.         if (p[i] == LY_BOLD_START_CHAR ||
  1837.             p[i] == LY_BOLD_END_CHAR ||
  1838.         IS_UTF_EXTRA(p[i]) ||
  1839.         p[i] == LY_SOFT_HYPHEN) {
  1840.             ctrl_chars_on_this_line++;
  1841.         }
  1842.         if (p[i] == LY_SOFT_HYPHEN && text->permissible_split < i) {
  1843.             text->permissible_split = i + 1;
  1844.         }
  1845.     }
  1846.  
  1847.     /*
  1848.      *  Add the data to the new line. - FM
  1849.      */
  1850.     strcat(linedata, p);
  1851.     line->size += plen;
  1852.     }
  1853.  
  1854.     /*
  1855.      *  Economize on space.
  1856.      */
  1857.     while ((previous->size > 0) &&
  1858.     (previous->data[previous->size-1] == ' ')) {
  1859.     /*
  1860.      *  Strip trailers.
  1861.      */
  1862.     previous->data[previous->size-1] = '\0';
  1863.     previous->size--;
  1864.     TailTrim++;
  1865.     }
  1866.     temp = (HTLine *)LY_CALLOC(1, LINE_SIZE(previous->size));
  1867.     if (temp == NULL)
  1868.     outofmem(__FILE__, "split_line_2");
  1869.     memcpy(temp, previous, LINE_SIZE(previous->size));
  1870.     FREE(previous);
  1871.     previous = temp;
  1872.  
  1873.     previous->prev->next = previous;    /* Link in new line */
  1874.     previous->next->prev = previous;    /* Could be same node of course */
  1875.  
  1876.     /*
  1877.      *  Terminate finished line for printing.
  1878.      */
  1879.     previous->data[previous->size] = '\0';
  1880.  
  1881.     /*
  1882.      *  Align left, right or center.
  1883.      */
  1884.     for (cp = previous->data; *cp; cp++) {
  1885.     if (*cp == LY_UNDERLINE_START_CHAR ||
  1886.         *cp == LY_UNDERLINE_END_CHAR ||
  1887.         *cp == LY_BOLD_START_CHAR ||
  1888.         *cp == LY_BOLD_END_CHAR ||
  1889.         IS_UTF_EXTRA(*cp) ||
  1890.         *cp == LY_SOFT_HYPHEN)
  1891.         ctrl_chars_on_previous_line++;
  1892.     }
  1893.     /* @@ first line indent */
  1894.     spare =  (LYcols-1) -
  1895.         (int)style->rightIndent - indent +
  1896.         ctrl_chars_on_previous_line - previous->size -
  1897.         ((previous->size > 0) &&
  1898.          (int)(previous->data[previous->size-1] ==
  1899.                         LY_SOFT_HYPHEN ?
  1900.                              1 : 0));
  1901.  
  1902.     switch (style->alignment) {
  1903.     case HT_CENTER :
  1904.         previous->offset = previous->offset + indent + spare/2;
  1905.         break;
  1906.     case HT_RIGHT :
  1907.         previous->offset = previous->offset + indent + spare;
  1908.         break;
  1909.     case HT_LEFT :
  1910.     case HT_JUSTIFY :        /* Not implemented */
  1911.     default:
  1912.         previous->offset = previous->offset + indent;
  1913.         break;
  1914.     } /* switch */
  1915.  
  1916.     text->chars = text->chars + previous->size + 1;    /* 1 for the line */
  1917.     text->in_line_1 = NO;        /* unless caller sets it otherwise */
  1918.  
  1919.     /*
  1920.      *  If we split the line, adjust the anchor
  1921.      *  structure values for the new line. - FM
  1922.      */
  1923.     if (split > 0) {
  1924.     for (a = text->first_anchor; a; a = a->next) {
  1925.         if (a->line_num == CurLine) {
  1926.         if (a->line_pos >= split) {
  1927.             a->start += (1 + SpecialAttrChars - HeadTrim - TailTrim);
  1928.             a->line_pos -= (split - SpecialAttrChars + HeadTrim);
  1929.             a->line_num = text->Lines;
  1930.         } else if ((a->link_type & HYPERTEXT_ANCHOR) &&
  1931.                (a->line_pos + a->extent) >= split) {
  1932.             a->extent -= (TailTrim + HeadTrim);
  1933.             if (a->extent < 0) {
  1934.                 a->extent = 0;
  1935.             }
  1936.         }
  1937.         }
  1938.     }
  1939.     }
  1940. } /* split_line */
  1941.  
  1942.  
  1943. /*    Allow vertical blank space
  1944. **    --------------------------
  1945. */
  1946. PRIVATE void blank_lines ARGS2(
  1947.     HText *,    text,
  1948.     int,        newlines)
  1949. {
  1950.     BOOL IgnoreSpaces = FALSE;
  1951.  
  1952.     if (!HText_LastLineSize(text, IgnoreSpaces)) { /* No text on current line */
  1953.     HTLine * line = text->last_line->prev;
  1954.     while ((line != text->last_line) &&
  1955.            (HText_TrueLineSize(line, text, IgnoreSpaces) == 0)) {
  1956.         if (newlines == 0)
  1957.             break;
  1958.         newlines--;        /* Don't bother: already blank */
  1959.         line = line->prev;
  1960.     }
  1961.     } else {
  1962.     newlines++;            /* Need also to finish this line */
  1963.     }
  1964.  
  1965.     for (; newlines; newlines--) {
  1966.     new_line(text);
  1967.     }
  1968.     text->in_line_1 = YES;
  1969. }
  1970.  
  1971.  
  1972. /*    New paragraph in current style
  1973. **    ------------------------------
  1974. ** See also: setStyle.
  1975. */
  1976. PUBLIC void HText_appendParagraph ARGS1(
  1977.     HText *,    text)
  1978. {
  1979.     int after = text->style->spaceAfter;
  1980.     int before = text->style->spaceBefore;
  1981.     blank_lines(text, ((after > before) ? after : before));
  1982. }
  1983.  
  1984.  
  1985. /*    Set Style
  1986. **    ---------
  1987. **
  1988. **    Does not filter unnecessary style changes.
  1989. */
  1990. PUBLIC void HText_setStyle ARGS2(
  1991.     HText *,    text,
  1992.     HTStyle *,    style)
  1993. {
  1994.     int after, before;
  1995.  
  1996.     if (!style)
  1997.     return;             /* Safety */
  1998.     after = text->style->spaceAfter;
  1999.     before = style->spaceBefore;
  2000.  
  2001.     CTRACE(tfp, "GridText: Change to style %s\n", style->name);
  2002.  
  2003.     blank_lines (text, ((after > before) ? after : before));
  2004.  
  2005.     text->style = style;
  2006. }
  2007.  
  2008. /*    Append a character to the text object
  2009. **    -------------------------------------
  2010. */
  2011. PUBLIC void HText_appendCharacter ARGS2(
  2012.     HText *,    text,
  2013.     char,        ch)
  2014. {
  2015.     HTLine * line;
  2016.     HTStyle * style;
  2017.     int indent;
  2018.  
  2019.     /*
  2020.      *  Make sure we don't crash on NULLs.
  2021.      */
  2022.     if (!text)
  2023.     return;
  2024.  
  2025.     if (text->halted > 1) {
  2026.     /*
  2027.      *  We should stop outputting more text, because low memory was
  2028.      *  detected.  - kw
  2029.      */
  2030.     if (text->halted == 2) {
  2031.         /*
  2032.          *  But if we haven't done so yet, first append a warning.
  2033.          *  We should still have a few bytes left for that :).
  2034.          *  We temporarily reset test->halted to 0 for this, since
  2035.          *  this function will get called recursively. - kw
  2036.          */
  2037.         text->halted = 0;
  2038.         text->kanji_buf = '\0';
  2039.         HText_appendText(text, " *** MEMORY EXHAUSTED ***");
  2040.     }
  2041.     text->halted = 3;
  2042.     return;
  2043.     }
  2044.     /*
  2045.      *  Make sure we don't hang on escape sequences.
  2046.      */
  2047.     if (ch == '\033' && HTCJK == NOCJK)            /* decimal 27 */
  2048.     return;
  2049. #ifndef USE_SLANG
  2050.     /*
  2051.      *  Block 8-bit chars not allowed by the current display character
  2052.      *  set if they are below what LYlowest_eightbit indicates.
  2053.      *  Slang used its own replacements, so for USE_SLANG blocking here
  2054.      *  is not necessary to protect terminals from those characters.
  2055.      *  They should have been filtered out or translated by an earlier
  2056.      *  processing stage anyway. - kw
  2057.      */
  2058.     if ((unsigned char)ch >= 128 && HTCJK == NOCJK &&
  2059.     !text->T.transp && !text->T.output_utf8 &&
  2060.     (unsigned char)ch < LYlowest_eightbit[current_char_set])
  2061.     return;
  2062. #endif /* USE_SLANG */
  2063.     if ((unsigned char)ch == 155 && HTCJK == NOCJK) {    /* octal 233 */
  2064.     if (!HTPassHighCtrlRaw &&
  2065.         !text->T.transp && !text->T.output_utf8 &&
  2066.         (155 < LYlowest_eightbit[current_char_set]) &&
  2067.         strncmp(LYchar_set_names[current_char_set],
  2068.             "DosLatin1 (cp850)", 17) &&
  2069.         strncmp(LYchar_set_names[current_char_set],
  2070.             "DosLatinUS (cp437)", 18) &&
  2071.         strncmp(LYchar_set_names[current_char_set],
  2072.             "Macintosh (8 bit)", 17) &&
  2073.         strncmp(LYchar_set_names[current_char_set],
  2074.             "NeXT character set", 18)) {
  2075.         return;
  2076.     }
  2077.     }
  2078.  
  2079.     line = text->last_line;
  2080.     style = text->style;
  2081.  
  2082.     indent = text->in_line_1 ? (int)style->indent1st : (int)style->leftIndent;
  2083.  
  2084.     if (HTCJK != NOCJK) {
  2085.     switch(text->state) {
  2086.         case S_text:
  2087.         if (ch == '\033') {
  2088.             /*
  2089.             **  Setting up for CJK escape sequence handling (based on
  2090.             **  Takuya ASADA's (asada@three-a.co.jp) CJK Lynx). - FM
  2091.             */
  2092.             text->state = S_esc;
  2093.             text->kanji_buf = '\0';
  2094.             return;
  2095.         }
  2096.         break;
  2097.  
  2098.         case S_esc:
  2099.         /*
  2100.          *  Expecting '$'or '(' following CJK ESC.
  2101.          */
  2102.         if (ch == '$') {
  2103.             text->state = S_dollar;
  2104.             return;
  2105.         } else if (ch == '(') {
  2106.             text->state = S_paren;
  2107.             return;
  2108.         } else {
  2109.             text->state = S_text;
  2110.         }
  2111.  
  2112.         case S_dollar:
  2113.         /*
  2114.          *  Expecting '@', 'B', 'A' or '(' after CJK "ESC$".
  2115.          */
  2116.         if (ch == '@' || ch == 'B' || ch=='A') {
  2117.             text->state = S_nonascii_text;
  2118.             return;
  2119.         } else if (ch == '(') {
  2120.             text->state = S_dollar_paren;
  2121.             return;
  2122.         } else {
  2123.             text->state = S_text;
  2124.         }
  2125.         break;
  2126.  
  2127.         case S_dollar_paren:
  2128.         /*
  2129.          * Expecting 'C' after CJK "ESC$(".
  2130.          */
  2131.         if (ch == 'C') {
  2132.             text->state = S_nonascii_text;
  2133.             return;
  2134.         } else {
  2135.             text->state = S_text;
  2136.         }
  2137.         break;
  2138.  
  2139.         case S_paren:
  2140.         /*
  2141.          *  Expecting 'B', 'J', 'T' or 'I' after CJK "ESC(".
  2142.          */
  2143.         if (ch == 'B' || ch == 'J' || ch == 'T')  {
  2144.             /*
  2145.              *  Can split here. - FM
  2146.              */
  2147.             text->permissible_split = (int)text->last_line->size;
  2148.             text->state = S_text;
  2149.             return;
  2150.         } else if (ch == 'I')  {
  2151.             text->state = S_jisx0201_text;
  2152.             /*
  2153.              *  Can split here. - FM
  2154.              */
  2155.             text->permissible_split = (int)text->last_line->size;
  2156.             return;
  2157.         } else {
  2158.             text->state = S_text;
  2159.         }
  2160.         break;
  2161.  
  2162.         case S_nonascii_text:
  2163.         /*
  2164.          *  Expecting CJK ESC after non-ASCII text.
  2165.          */
  2166.         if (ch == '\033') {
  2167.             text->state = S_esc;
  2168.             text->kanji_buf = '\0';
  2169.             return;
  2170.         } else {
  2171.             ch |= 0200;
  2172.         }
  2173.         break;
  2174.  
  2175.         /*
  2176.          *  JIS X0201 Kana in JIS support. - by ASATAKU
  2177.          */
  2178.         case S_jisx0201_text:
  2179.         if (ch == '\033') {
  2180.             text->state = S_esc;
  2181.             text->kanji_buf = '\0';
  2182.             return;
  2183.         } else {
  2184.             text->kanji_buf = '\216';
  2185.             ch |= 0200;
  2186.         }
  2187.         break;
  2188.     }
  2189.  
  2190.     if (!text->kanji_buf) {
  2191.         if ((ch & 0200) != 0) {
  2192.         /*
  2193.          *  JIS X0201 Kana in SJIS support. - by ASATAKU
  2194.          */
  2195.             if ((text->kcode == SJIS) &&
  2196.             ((unsigned char)ch >= 0xA1) &&
  2197.             ((unsigned char)ch <= 0xDF)) {
  2198.             unsigned char c = (unsigned char)ch;
  2199.             unsigned char kb = (unsigned char)text->kanji_buf;
  2200.             JISx0201TO0208_SJIS(c,
  2201.                     (unsigned char *)&kb,
  2202.                     (unsigned char *)&c);
  2203.             ch = (char)c;
  2204.             text->kanji_buf = (char)kb;
  2205.             } else {
  2206.             text->kanji_buf = ch;
  2207.             /*
  2208.              *  Can split here. - FM
  2209.              */
  2210.             text->permissible_split = (int)text->last_line->size;
  2211.             return;
  2212.             }
  2213.         }
  2214.     } else {
  2215.         goto check_IgnoreExcess;
  2216.     }
  2217.     } else if (ch == '\033') {
  2218.     return;
  2219.     }
  2220.  
  2221.     if (IsSpecialAttrChar(ch)) {
  2222. #ifndef USE_COLOR_STYLE
  2223.     if (ch == LY_UNDERLINE_START_CHAR) {
  2224.         line->data[line->size++] = LY_UNDERLINE_START_CHAR;
  2225.         line->data[line->size] = '\0';
  2226.         underline_on = ON;
  2227.         if (!(dump_output_immediately && use_underscore))
  2228.         ctrl_chars_on_this_line++;
  2229.         return;
  2230.     } else if (ch == LY_UNDERLINE_END_CHAR) {
  2231.         line->data[line->size++] = LY_UNDERLINE_END_CHAR;
  2232.         line->data[line->size] = '\0';
  2233.         underline_on = OFF;
  2234.         if (!(dump_output_immediately && use_underscore))
  2235.         ctrl_chars_on_this_line++;
  2236.         return;
  2237.     } else if (ch == LY_BOLD_START_CHAR) {
  2238.         line->data[line->size++] = LY_BOLD_START_CHAR;
  2239.         line->data[line->size] = '\0';
  2240.         bold_on = ON;
  2241.         ctrl_chars_on_this_line++;
  2242.         return;
  2243.     } else if (ch == LY_BOLD_END_CHAR) {
  2244.         line->data[line->size++] = LY_BOLD_END_CHAR;
  2245.         line->data[line->size] = '\0';
  2246.         bold_on = OFF;
  2247.         ctrl_chars_on_this_line++;
  2248.         return;
  2249.     } else if (ch == LY_SOFT_HYPHEN) {
  2250.         int i;
  2251.  
  2252.         /*
  2253.          *  Ignore the soft hyphen if it is the first character
  2254.          *  on the line, or if it is preceded by a space or
  2255.          *  hyphen. - FM
  2256.          */
  2257.         if (line->size < 1 || text->permissible_split >= (int)line->size)
  2258.         return;
  2259.  
  2260.         for (i = (text->permissible_split + 1); line->data[i]; i++) {
  2261.         if (!IsSpecialAttrChar((unsigned char)line->data[i]) &&
  2262.             !isspace((unsigned char)line->data[i]) &&
  2263.             (unsigned char)line->data[i] != '-' &&
  2264.             (unsigned char)line->data[i] != HT_NON_BREAK_SPACE &&
  2265.             (unsigned char)line->data[i] != HT_EM_SPACE) {
  2266.             break;
  2267.         }
  2268.         }
  2269.         if (line->data[i] == '\0') {
  2270.         return;
  2271.         }
  2272.     }
  2273. #else
  2274.     return;
  2275. #endif
  2276.     }
  2277.  
  2278.     if (IS_UTF_EXTRA(ch)) {
  2279.     line->data[line->size++] = ch;
  2280.     line->data[line->size] = '\0';
  2281.     ctrl_chars_on_this_line++;
  2282.     return;
  2283.     }
  2284.  
  2285.     /*
  2286.      *  New Line.
  2287.      */
  2288.     if (ch == '\n') {
  2289.         new_line(text);
  2290.         text->in_line_1 = YES;    /* First line of new paragraph */
  2291.         /*
  2292.          *  There are some pages written in
  2293.          *  different kanji codes. - TA & kw
  2294.          */
  2295.         if (HTCJK == JAPANESE)
  2296.         text->kcode = NOKANJI;
  2297.         return;
  2298.     }
  2299.  
  2300.     /*
  2301.      *  Convert EM_SPACE to a space here so that it doesn't get collapsed.
  2302.      */
  2303.     if (ch == HT_EM_SPACE)
  2304.     ch = ' ';
  2305.  
  2306.     /*
  2307.      *  I'm going to cheat here in a BIG way.  Since I know that all
  2308.      *  \r's will be trapped by HTML_put_character I'm going to use
  2309.      *  \r to mean go down a line but don't start a new paragraph.
  2310.      *  i.e. use the second line indenting.
  2311.      */
  2312.     if (ch == '\r') {
  2313.     new_line(text);
  2314.     text->in_line_1 = NO;
  2315.     /*
  2316.      *  There are some pages written in
  2317.      *  different kanji codes. - TA & kw
  2318.      */
  2319.     if (HTCJK == JAPANESE)
  2320.         text->kcode = NOKANJI;
  2321.     return;
  2322.     }
  2323.  
  2324.  
  2325.     /*
  2326.      *  Tabs.
  2327.      */
  2328.     if (ch == '\t') {
  2329.     CONST HTTabStop * Tab;
  2330.     int target;    /* Where to tab to */
  2331.     int here;
  2332.  
  2333.     if (line->size > 0 && line->data[line->size-1] == LY_SOFT_HYPHEN) {
  2334.         /*
  2335.          *  A tab shouldn't follow a soft hyphen, so
  2336.          *  if one does, we'll dump the soft hyphen. - FM
  2337.          */
  2338.         line->data[--line->size] = '\0';
  2339.         ctrl_chars_on_this_line--;
  2340.     }
  2341.     here = (((int)line->size + (int)line->offset) + indent)
  2342.         - ctrl_chars_on_this_line; /* Consider special chars GAB */
  2343.     if (style->tabs) {    /* Use tab table */
  2344.         for (Tab = style->tabs;
  2345.         Tab->position <= here;
  2346.         Tab++)
  2347.         if (!Tab->position) {
  2348.             new_line(text);
  2349.             return;
  2350.         }
  2351.         target = Tab->position;
  2352.     } else if (text->in_line_1) {    /* Use 2nd indent */
  2353.         if (here >= (int)style->leftIndent) {
  2354.             new_line(text); /* wrap */
  2355.         return;
  2356.         } else {
  2357.             target = (int)style->leftIndent;
  2358.         }
  2359.     } else {        /* Default tabs align with left indent mod 8 */
  2360. #ifdef DEFAULT_TABS_8
  2361.         target = (((int)line->offset + (int)line->size + 8) & (-8))
  2362.             + (int)style->leftIndent;
  2363. #else
  2364.         new_line(text);
  2365.         return;
  2366. #endif
  2367.     }
  2368.  
  2369.     if (target > (LYcols-1) - (int)style->rightIndent &&
  2370.         HTOutputFormat != WWW_SOURCE) {
  2371.         new_line(text);
  2372.         return;
  2373.     } else {
  2374.         /*
  2375.          *  Can split here. - FM
  2376.          */
  2377.         text->permissible_split = (int)line->size;
  2378.         if (line->size == 0) {
  2379.             line->offset = line->offset + target - here;
  2380.         } else {
  2381.             for (; here<target; here++) {
  2382.             /* Put character into line */
  2383.             line->data[line->size++] = ' ';
  2384.             line->data[line->size] = '\0';
  2385.             }
  2386.         }
  2387.         return;
  2388.     }
  2389.     /*NOTREACHED*/
  2390.     } /* if tab */
  2391.  
  2392.  
  2393.     if (ch == ' ') {
  2394.     /*
  2395.      *  Can split here. - FM
  2396.      */
  2397.     text->permissible_split = (int)text->last_line->size;
  2398.     /*
  2399.      *  There are some pages written in
  2400.      *  different kanji codes. - TA
  2401.      */
  2402.     if (HTCJK == JAPANESE)
  2403.         text->kcode = NOKANJI;
  2404.     }
  2405.  
  2406.     /*
  2407.      *  Check if we should ignore characters at the wrap point.
  2408.      */
  2409. check_IgnoreExcess:
  2410.     if (text->IgnoreExcess &&
  2411.     ((indent + (int)line->offset + (int)line->size) +
  2412.     (int)style->rightIndent - ctrl_chars_on_this_line) >= (LYcols-1))
  2413.     return;
  2414.  
  2415.     /*
  2416.      *  Check for end of line.
  2417.      */
  2418.     if (((indent + (int)line->offset + (int)line->size) +
  2419.      (int)style->rightIndent - ctrl_chars_on_this_line +
  2420.      ((line->size > 0) &&
  2421.       (int)(line->data[line->size-1] ==
  2422.                 LY_SOFT_HYPHEN ?
  2423.                          1 : 0))) >= (LYcols - 1)) {
  2424.  
  2425.     if (style->wordWrap && HTOutputFormat != WWW_SOURCE) {
  2426.         split_line(text, text->permissible_split);
  2427.         if (ch == ' ') return;    /* Ignore space causing split */
  2428.  
  2429.     }  else if (HTOutputFormat == WWW_SOURCE) {
  2430.          /*
  2431.           *  For source output we don't want to wrap this stuff
  2432.           *  unless absolutely necessary. - LJM
  2433.           *  !
  2434.           *  If we don't wrap here we might get a segmentation fault.
  2435.           *  but let's see what happens
  2436.           */
  2437.         if ((int)line->size >= (int)(MAX_LINE-1))
  2438.            new_line(text);  /* try not to linewrap */
  2439.     } else {
  2440.         /*
  2441.          *  For normal stuff like pre let's go ahead and
  2442.          *  wrap so the user can see all of the text.
  2443.          */
  2444.         new_line(text);
  2445.     }
  2446.     } else if ((int)line->size >= (int)(MAX_LINE-1)) {
  2447.     /*
  2448.      *  Never overrun memory if LYcols is set to a large value - KW
  2449.      */
  2450.     new_line(text);
  2451.     }
  2452.  
  2453.     /*
  2454.      *  Insert normal characters.
  2455.      */
  2456.     if (ch == HT_NON_BREAK_SPACE) {
  2457.     ch = ' ';
  2458.     }
  2459.  
  2460.     if (ch & 0x80)
  2461.     text->have_8bit_chars = YES;
  2462.  
  2463.     {
  2464.     HTFont font = style->font;
  2465.     unsigned char hi, lo, tmp[2];
  2466.  
  2467.     line = text->last_line; /* May have changed */
  2468.     if (HTCJK != NOCJK && text->kanji_buf) {
  2469.         hi = (unsigned char)text->kanji_buf, lo = (unsigned char)ch;
  2470.         if (HTCJK == JAPANESE && text->kcode == NOKANJI) {
  2471.         if (IS_SJIS(hi, lo, text->in_sjis) && IS_EUC(hi, lo)) {
  2472.             text->kcode = NOKANJI;
  2473.         } else if (IS_SJIS(hi, lo, text->in_sjis)) {
  2474.             text->kcode = SJIS;
  2475.         } else if (IS_EUC(hi, lo)) {
  2476.             text->kcode = EUC;
  2477.         }
  2478.         }
  2479.         if (HTCJK == JAPANESE &&
  2480.         (kanji_code == EUC) && (text->kcode == SJIS)) {
  2481.         SJIS_TO_EUC1(hi, lo, tmp);
  2482.         line->data[line->size++] = tmp[0];
  2483.         line->data[line->size++] = tmp[1];
  2484.         } else if (HTCJK == JAPANESE &&
  2485.                (kanji_code == EUC) && (text->kcode == EUC)) {
  2486.         JISx0201TO0208_EUC(hi, lo, &hi, &lo);
  2487.         line->data[line->size++] = hi;
  2488.         line->data[line->size++] = lo;
  2489.         } else if (HTCJK == JAPANESE &&
  2490.                (kanji_code == SJIS) && (text->kcode == EUC)) {
  2491.         EUC_TO_SJIS1(hi, lo, tmp);
  2492.         line->data[line->size++] = tmp[0];
  2493.         line->data[line->size++] = tmp[1];
  2494.         } else {
  2495.         line->data[line->size++] = hi;
  2496.         line->data[line->size++] = lo;
  2497.         }
  2498.         text->kanji_buf = 0;
  2499.     } else if (HTCJK != NOCJK) {
  2500.         line->data[line->size++] = (kanji_code != NOKANJI) ?
  2501.                                 ch :
  2502.                       (font & HT_CAPITALS) ?
  2503.                            TOUPPER(ch) : ch;
  2504.     } else {
  2505.         line->data[line->size++] =    /* Put character into line */
  2506.         font & HT_CAPITALS ? TOUPPER(ch) : ch;
  2507.     }
  2508.     line->data[line->size] = '\0';
  2509.     if (font & HT_DOUBLE)        /* Do again if doubled */
  2510.         HText_appendCharacter(text, HT_NON_BREAK_SPACE);
  2511.         /* NOT a permissible split */
  2512.  
  2513.     if (ch == LY_SOFT_HYPHEN) {
  2514.         ctrl_chars_on_this_line++;
  2515.         /*
  2516.          *  Can split here. - FM
  2517.          */
  2518.         text->permissible_split = (int)text->last_line->size;
  2519.     }
  2520.     }
  2521. }
  2522.  
  2523. #ifdef USE_COLOR_STYLE
  2524. /*  Insert a style change into the current line
  2525. **  -------------------------------------------
  2526. */
  2527. PUBLIC void _internal_HTC ARGS3(HText *,text, int,style, int,dir)
  2528. {
  2529.  HTLine* line;
  2530.  static int last_style = -1;
  2531.  static int last_dir = -1;
  2532.  
  2533.  /* can't change style if we have no text to change style with */
  2534.  if (!text) return;
  2535.  
  2536.  line = text->last_line;
  2537.  
  2538.  if ((style != last_style || dir != last_dir) && line->numstyles < MAX_STYLES_ON_LINE)
  2539.  {
  2540.       line->styles[line->numstyles].horizpos = line->size;
  2541.       line->styles[line->numstyles].style = style;
  2542.       line->styles[line->numstyles].direction = dir;
  2543.       line->numstyles++;
  2544.  }
  2545.  last_style = style;
  2546.  last_dir = dir;
  2547. }
  2548. #endif
  2549.  
  2550.  
  2551.  
  2552. /*    Set LastChar element in the text object.
  2553. **    ----------------------------------------
  2554. */
  2555. PUBLIC void HText_setLastChar ARGS2(
  2556.     HText *,    text,
  2557.     char,        ch)
  2558. {
  2559.     if (!text)
  2560.     return;
  2561.  
  2562.     text->LastChar = ch;
  2563. }
  2564.  
  2565. /*    Get LastChar element in the text object.
  2566. **    ----------------------------------------
  2567. */
  2568. PUBLIC char HText_getLastChar ARGS1(
  2569.     HText *,    text)
  2570. {
  2571.     if (!text)
  2572.     return('\0');
  2573.  
  2574.     return((char)text->LastChar);
  2575. }
  2576.  
  2577. /*    Set IgnoreExcess element in the text object.
  2578. **    --------------------------------------------
  2579. */
  2580. PUBLIC void HText_setIgnoreExcess ARGS2(
  2581.     HText *,    text,
  2582.     BOOL,        ignore)
  2583. {
  2584.     if (!text)
  2585.     return;
  2586.  
  2587.     text->IgnoreExcess = ignore;
  2588. }
  2589.  
  2590. /*        Anchor handling
  2591. **        ---------------
  2592. */
  2593.  
  2594. /*    Start an anchor field
  2595. */
  2596. PUBLIC int HText_beginAnchor ARGS3(
  2597.     HText *,        text,
  2598.     BOOL,            underline,
  2599.     HTChildAnchor *,    anc)
  2600. {
  2601.     char marker[16];
  2602.  
  2603.     TextAnchor * a = (TextAnchor *) calloc(1, sizeof(*a));
  2604.  
  2605.     if (a == NULL)
  2606.     outofmem(__FILE__, "HText_beginAnchor");
  2607.     a->hightext  = NULL;
  2608.     a->hightext2 = NULL;
  2609.     a->start = text->chars + text->last_line->size;
  2610.     a->inUnderline = underline;
  2611.  
  2612.     a->line_num = text->Lines;
  2613.     a->line_pos = text->last_line->size;
  2614.     if (text->last_anchor) {
  2615.     text->last_anchor->next = a;
  2616.     } else {
  2617.     text->first_anchor = a;
  2618.     }
  2619.     a->next = 0;
  2620.     a->anchor = anc;
  2621.     a->extent = 0;
  2622.     a->link_type = HYPERTEXT_ANCHOR;
  2623.     text->last_anchor = a;
  2624.  
  2625. #ifndef DONT_TRACK_INTERNAL_LINKS
  2626.     if (HTAnchor_followTypedLink((HTAnchor*)anc, LINK_INTERNAL)) {
  2627.     a->number = ++(text->last_anchor_number);
  2628.     a->link_type = INTERNAL_LINK_ANCHOR;
  2629.     } else
  2630. #endif
  2631.     if (HTAnchor_followMainLink((HTAnchor*)anc)) {
  2632.     a->number = ++(text->last_anchor_number);
  2633.     } else {
  2634.     a->number = 0;
  2635.     }
  2636.  
  2637.     /*
  2638.      *  If we are doing link_numbering add the link number.
  2639.      */
  2640.     if ((a->number > 0) &&
  2641.     (keypad_mode == LINKS_ARE_NUMBERED ||
  2642.      keypad_mode == LINKS_AND_FORM_FIELDS_ARE_NUMBERED)) {
  2643.     sprintf(marker,"[%d]", a->number);
  2644.     HText_appendText(text, marker);
  2645.     a->start = text->chars + text->last_line->size;
  2646.     a->line_num = text->Lines;
  2647.     a->line_pos = text->last_line->size;
  2648.     }
  2649.  
  2650.     return(a->number);
  2651. }
  2652.  
  2653.  
  2654. PUBLIC void HText_endAnchor ARGS2(
  2655.     HText *,    text,
  2656.     int,        number)
  2657. {
  2658.     TextAnchor *a;
  2659.  
  2660.     /*
  2661.      *  The number argument is set to 0 in HTML.c and
  2662.      *  LYCharUtils.c when we want to end the anchor
  2663.      *  for the immediately preceding HText_beginAnchor()
  2664.      *  call.  If it's greater than 0, we want to handle
  2665.      *  a particular anchor.  This allows us to set links
  2666.      *  for positions indicated by NAME or ID attributes,
  2667.      *  without needing to close any anchor with an HREF
  2668.      *  within which that link might be embedded. - FM
  2669.      */
  2670.     if (number <= 0 || number == text->last_anchor->number) {
  2671.     a = text->last_anchor;
  2672.     } else {
  2673.     for (a = text->first_anchor; a; a = a->next) {
  2674.         if (a->number == number) {
  2675.             break;
  2676.         }
  2677.     }
  2678.     if (a == NULL) {
  2679.         /*
  2680.          *  There's no anchor with that number,
  2681.          *  so we'll default to the last anchor,
  2682.          *  and cross our fingers. - FM
  2683.          */
  2684.         a = text->last_anchor;
  2685.     }
  2686.     }
  2687.  
  2688.     CTRACE(tfp, "HText_endAnchor: number:%d link_type:%d\n",
  2689.             a->number, a->link_type);
  2690.     if (a->link_type == INPUT_ANCHOR) {
  2691.     /*
  2692.      *  Shouldn't happen, but put test here anyway to be safe. - LE
  2693.      */
  2694.  
  2695.     CTRACE(tfp,
  2696.        "HText_endAnchor: internal error: last anchor was input field!\n");
  2697.     return;
  2698.     }
  2699.     if (a->number) {
  2700.     /*
  2701.      *  If it goes somewhere...
  2702.      */
  2703.     int i, j, k, l;
  2704.     BOOL remove_numbers_on_empty =
  2705.         ((keypad_mode == LINKS_ARE_NUMBERED ||
  2706.           keypad_mode == LINKS_AND_FORM_FIELDS_ARE_NUMBERED) &&
  2707.          (text->hiddenlinkflag != HIDDENLINKS_MERGE ||
  2708.           (LYNoISMAPifUSEMAP &&
  2709.            HTAnchor_isISMAPScript(
  2710.            HTAnchor_followMainLink((HTAnchor *)a->anchor)))));
  2711.     HTLine *last = text->last_line;
  2712.     HTLine *prev = text->last_line->prev;
  2713.     int CurBlankExtent = 0;
  2714.     int BlankExtent = 0;
  2715.  
  2716.     /*
  2717.      *  Check if the anchor content has only
  2718.      *  white and special characters, starting
  2719.      *  with the content on the last line. - FM
  2720.      */
  2721.     a->extent += (text->chars + last->size) - a->start -
  2722.              (text->Lines - a->line_num);
  2723.     if (a->extent > last->size) {
  2724.         /*
  2725.          *  The anchor extends over more than one line,
  2726.          *  so set up to check the entire last line. - FM
  2727.          */
  2728.         i = last->size;
  2729.     } else {
  2730.         /*
  2731.          *  The anchor is restricted to the last line,
  2732.          *  so check from the start of the anchor. - FM
  2733.          */
  2734.         i = a->extent;
  2735.     }
  2736.     j = (last->size - i);
  2737.     while (j < last->size) {
  2738.         if (!IsSpecialAttrChar(last->data[j]) &&
  2739.             !isspace((unsigned char)last->data[j]) &&
  2740.         last->data[j] != HT_NON_BREAK_SPACE &&
  2741.         last->data[j] != HT_EM_SPACE)
  2742.         break;
  2743.         i--;
  2744.         j++;
  2745.     }
  2746.     if (i == 0) {
  2747.         if (a->extent > last->size) {
  2748.         /*
  2749.          *  The anchor starts on a preceding line, and
  2750.          *  the last line has only white and special
  2751.          *  characters, so declare the entire extent
  2752.          *  of the last line as blank. - FM
  2753.          */
  2754.         CurBlankExtent = BlankExtent = last->size;
  2755.         } else {
  2756.         /*
  2757.          *  The anchor starts on the last line, and
  2758.          *  has only white or special characters, so
  2759.          *  declare the anchor's extent as blank. - FM
  2760.          */
  2761.         CurBlankExtent = BlankExtent = a->extent;
  2762.         }
  2763.     }
  2764.     /*
  2765.      *  While the anchor starts on a line preceding
  2766.      *  the one we just checked, and the one we just
  2767.      *  checked has only white and special characters,
  2768.      *  check whether the anchor's content on the
  2769.      *  immediately preceding line also has only
  2770.      *  white and special characters. - FM
  2771.      */
  2772.     while (i == 0 && a->extent > CurBlankExtent) {
  2773.         j = prev->size - a->extent + CurBlankExtent;
  2774.         if (j < 0) {
  2775.             /*
  2776.          *  The anchor starts on a preceding line,
  2777.          *  so check all of this line. - FM
  2778.          */
  2779.             j = 0;
  2780.         i = prev->size;
  2781.         } else {
  2782.             /*
  2783.          *  The anchor starts on this line. - FM
  2784.          */
  2785.             i = a->extent - CurBlankExtent;
  2786.         }
  2787.         while (j < prev->size) {
  2788.             if (!IsSpecialAttrChar(prev->data[j]) &&
  2789.             !isspace((unsigned char)prev->data[j]) &&
  2790.             prev->data[j] != HT_NON_BREAK_SPACE &&
  2791.             prev->data[j] != HT_EM_SPACE)
  2792.             break;
  2793.         i--;
  2794.         j++;
  2795.         }
  2796.         if (i == 0) {
  2797.         if (a->extent > (CurBlankExtent + prev->size)) {
  2798.             /*
  2799.              *  This line has only white and special
  2800.              *  characters, so treat its entire extent
  2801.              *  as blank, and decrement the pointer for
  2802.              *  the line to be analyzed. - FM
  2803.              */
  2804.             CurBlankExtent += prev->size;
  2805.             BlankExtent = CurBlankExtent;
  2806.             prev = prev->prev;
  2807.         } else {
  2808.             /*
  2809.              *  The anchor starts on this line, and it
  2810.              *  has only white or special characters, so
  2811.              *  declare the anchor's extent as blank. - FM
  2812.              */
  2813.             BlankExtent = a->extent;
  2814.             break;
  2815.         }
  2816.         }
  2817.     }
  2818.     if (i == 0) {
  2819.         /*
  2820.          *  It's an invisible anchor probably from an ALT=""
  2821.          *  or an ignored ISMAP attribute due to a companion
  2822.          *  USEMAP. - FM
  2823.          */
  2824.         a->show_anchor = NO;
  2825.  
  2826.         /*
  2827.          *  If links are numbered, then try to get rid of the
  2828.          *  numbered bracket and adjust the anchor count. - FM
  2829.          *
  2830.          * Well, let's do this only if -hiddenlinks=merged is not in
  2831.          * effect, or if we can be reasonably sure that
  2832.          * this is the result of an intentional non-generation of
  2833.          * anchor text via NO_ISMAP_IF_USEMAP. In other cases it can
  2834.          * actually be a feature that numbered links alert the viewer
  2835.          * to the presence of a link which is otherwise not selectable -
  2836.          * possibly caused by HTML errors. - kw
  2837.          */
  2838.         if (remove_numbers_on_empty) {
  2839.         HTLine *start;
  2840.         int NumSize = 0;
  2841.         TextAnchor *anc;
  2842.  
  2843.         /*
  2844.          *  Set start->data[j] to the close-square-bracket,
  2845.          *  or to the beginning of the line on which the
  2846.          *  anchor start. - FM
  2847.          */
  2848.         if (prev == last->prev) {
  2849.             /*
  2850.              *  The anchor starts on the last line. - FM
  2851.              */
  2852.             start = last;
  2853.             j = (last->size - a->extent - 1);
  2854.         } else {
  2855.             /*
  2856.              *  The anchor starts on a previous line. - FM
  2857.              */
  2858.             start = prev;
  2859.             prev = prev->prev;
  2860.             j = (start->size - a->extent + CurBlankExtent - 1);
  2861.         }
  2862.         if (j < 0)
  2863.             j = 0;
  2864.         i = j;
  2865.  
  2866.         /*
  2867.          *  If start->data[j] is a close-square-bracket, verify
  2868.          *  that it's the end of the numbered bracket, and if so,
  2869.          *  strip the numbered bracket.  If start->data[j] is not
  2870.          *  a close-square-bracket, check whether we had a wrap
  2871.          *  and the close-square-bracket is at the end of the
  2872.          *  previous line.  If so, strip the numbered bracket
  2873.          *  from that line. - FM
  2874.          */
  2875.         if (start->data[j] == ']') {
  2876.             j--;
  2877.             NumSize++;
  2878.             while (j >= 0 && isdigit((unsigned char)start->data[j])) {
  2879.                 j--;
  2880.             NumSize++;
  2881.             }
  2882.             while (j < 0) {
  2883.                 j++;
  2884.             NumSize--;
  2885.             }
  2886.             if (start->data[j] == '[') {
  2887.                 /*
  2888.              *  The numbered bracket is entirely
  2889.              *  on this line. - FM
  2890.              */
  2891.             NumSize++;
  2892.             k = j + NumSize;
  2893.             while (k < start->size)
  2894.                 start->data[j++] = start->data[k++];
  2895.             if (start != last)
  2896.                 text->chars -= NumSize;
  2897.             for (anc = a; anc; anc = anc->next) {
  2898.                 anc->start -= NumSize;
  2899.                 anc->line_pos -= NumSize;
  2900.             }
  2901.                 start->size = j;
  2902.             start->data[j++] = '\0';
  2903.             while (j < k)
  2904.                  start->data[j++] = '\0';
  2905.             } else if (prev && prev->size > 1) {
  2906.             k = (i + 1);
  2907.             j = (prev->size - 1);
  2908.             while ((j >= 0) &&
  2909.                    (prev->data[j] == LY_BOLD_START_CHAR ||
  2910.                     prev->data[j] == LY_BOLD_END_CHAR ||
  2911.                 prev->data[j] == LY_UNDERLINE_START_CHAR ||
  2912.                     prev->data[j] == LY_UNDERLINE_END_CHAR ||
  2913.                 prev->data[j] == LY_SOFT_HYPHEN))
  2914.                 j--;
  2915.                 i = (j + 1);
  2916.             while (j >= 0 &&
  2917.                    isdigit((unsigned char)prev->data[j])) {
  2918.                 j--;
  2919.                 NumSize++;
  2920.             }
  2921.             while (j < 0) {
  2922.                 j++;
  2923.                 NumSize--;
  2924.             }
  2925.             if (prev->data[j] == '[') {
  2926.                 /*
  2927.                  *  The numbered bracket started on the
  2928.                  *  previous line, and part of it was
  2929.                  *  wrapped to this line. - FM
  2930.                  */
  2931.                 NumSize++;
  2932.                 l = (i - j);
  2933.                 while (i < prev->size)
  2934.                 start->data[j++] = start->data[i++];
  2935.                 text->chars -= l;
  2936.                 for (anc = a; anc; anc = anc->next) {
  2937.                 anc->start -= l;
  2938.                 }
  2939.                 prev->size = j;
  2940.                 prev->data[j] = '\0';
  2941.                 while (j < i)
  2942.                 start->data[j++] = '\0';
  2943.                 j = 0;
  2944.                 i = k;
  2945.                 while (k < start->size)
  2946.                     start->data[j++] = start->data[k++];
  2947.                 if (start != last)
  2948.                     text->chars -= i;
  2949.                 for (anc = a; anc; anc = anc->next) {
  2950.                 anc->start -= i;
  2951.                 anc->line_pos -= i;
  2952.                 }
  2953.                 start->size = j;
  2954.                 start->data[j++] = '\0';
  2955.                 while (j < k)
  2956.                     start->data[j++] = '\0';
  2957.             } else {
  2958.                 /*
  2959.                  *  Shucks!  We didn't find the
  2960.                  *  numbered bracket. - FM
  2961.                  */
  2962.                 a->show_anchor = YES;
  2963.             }
  2964.             } else {
  2965.             /*
  2966.              *  Shucks!  We didn't find the
  2967.              *  numbered bracket. - FM
  2968.              */
  2969.             a->show_anchor = YES;
  2970.             }
  2971.         } else if (prev && prev->size > 2) {
  2972.             j = (prev->size - 1);
  2973.             while ((j >= 0) &&
  2974.                (prev->data[j] == LY_BOLD_START_CHAR ||
  2975.                 prev->data[j] == LY_BOLD_END_CHAR ||
  2976.                 prev->data[j] == LY_UNDERLINE_START_CHAR ||
  2977.                 prev->data[j] == LY_UNDERLINE_END_CHAR ||
  2978.                 prev->data[j] == LY_SOFT_HYPHEN))
  2979.                 j--;
  2980.             if (j < 0)
  2981.                 j = 0;
  2982.             i = (j + 1);
  2983.             if ((j > 2) &&
  2984.                 (prev->data[j] == ']' &&
  2985.              isdigit((unsigned char)prev->data[j - 1]))) {
  2986.                 j--;
  2987.             NumSize++;
  2988.                 k = (j + 1);
  2989.             while (j >= 0 &&
  2990.                    isdigit((unsigned char)prev->data[j])) {
  2991.                 j--;
  2992.                 NumSize++;
  2993.             }
  2994.             while (j < 0) {
  2995.                 j++;
  2996.                 NumSize--;
  2997.             }
  2998.             if (prev->data[j] == '[') {
  2999.                 /*
  3000.                  *  The numbered bracket is all on the
  3001.                  *  previous line, and the anchor content
  3002.                  *  was wrapped to the last line. - FM
  3003.                  */
  3004.                 NumSize++;
  3005.                 k = j + NumSize;
  3006.                 while (k < prev->size)
  3007.                 prev->data[j++] = prev->data[k++];
  3008.                 text->chars -= NumSize;
  3009.                 for (anc = a; anc; anc = anc->next) {
  3010.                 anc->start -= NumSize;
  3011.                 }
  3012.                 prev->size = j;
  3013.                 prev->data[j++] = '\0';
  3014.                 while (j < k)
  3015.                 start->data[j++] = '\0';
  3016.             } else {
  3017.                 /*
  3018.                  *  Shucks!  We didn't find the
  3019.                  *  numbered bracket. - FM
  3020.                  */
  3021.                 a->show_anchor = YES;
  3022.             }
  3023.             } else {
  3024.             /*
  3025.              *  Shucks!  We didn't find the
  3026.              *  numbered bracket. - FM
  3027.              */
  3028.                 a->show_anchor = YES;
  3029.             }
  3030.         } else {
  3031.             /*
  3032.              *  Shucks!  We didn't find the
  3033.              *  numbered bracket. - FM
  3034.              */
  3035.             a->show_anchor = YES;
  3036.         }
  3037.         }
  3038.     } else {
  3039.         /*
  3040.          *  The anchor's content is not restricted to only
  3041.          *  white and special characters, so we'll show it
  3042.          *  as a link. - FM
  3043.          */
  3044.         a->show_anchor = YES;
  3045.     }
  3046.     if (a->show_anchor == NO) {
  3047.         /*
  3048.          *  The anchor's content is restricted to white
  3049.          *  and special characters, so set it's number
  3050.          *  and extent to zero, decrement the visible
  3051.          *  anchor number counter, and add this anchor
  3052.          *  to the hidden links list. - FM
  3053.          */
  3054.         a->extent = 0;
  3055.         if (text->hiddenlinkflag != HIDDENLINKS_MERGE) {
  3056.         a->number = 0;
  3057.             text->last_anchor_number--;
  3058.         HText_AddHiddenLink(text, a);
  3059.         }
  3060.     } else {
  3061.         /*
  3062.          *  The anchor's content is not restricted to white
  3063.          *  and special characters, so we'll display the
  3064.          *  content, but shorten it's extent by any trailing
  3065.          *  blank lines we've detected. - FM
  3066.          */
  3067.         a->extent -= ((BlankExtent < a->extent) ?
  3068.                     BlankExtent : 0);
  3069.     }
  3070.     } else {
  3071.     /*
  3072.      *  It's a named anchor without an HREF, so it
  3073.      *  should be registered but not shown as a
  3074.      *  link. - FM
  3075.      */
  3076.     a->show_anchor = NO;
  3077.     a->extent = 0;
  3078.     }
  3079. }
  3080.  
  3081.  
  3082. PUBLIC void HText_appendText ARGS2(
  3083.     HText *,    text,
  3084.     CONST char *,    str)
  3085. {
  3086.     CONST char *p;
  3087.  
  3088.     if (str == NULL)
  3089.     return;
  3090.  
  3091.     if (text->halted == 3)
  3092.     return;
  3093.  
  3094.     for (p = str; *p; p++) {
  3095.     HText_appendCharacter(text, *p);
  3096.     }
  3097. }
  3098.  
  3099.  
  3100. PRIVATE void remove_special_attr_chars ARGS1(
  3101.     char *,        buf)
  3102. {
  3103.     register char *cp;
  3104.  
  3105.     for (cp = buf; *cp != '\0' ; cp++) {
  3106.     /*
  3107.      *  Don't print underline chars.
  3108.      */
  3109.     if (!IsSpecialAttrChar(*cp)) {
  3110.        *buf = *cp,
  3111.        buf++;
  3112.     }
  3113.     }
  3114.     *buf = '\0';
  3115. }
  3116.  
  3117.  
  3118. /*
  3119. **  This function trims blank lines from the end of the document, and
  3120. **  then gets the hightext from the text by finding the char position,
  3121. **  and brings the anchors in line with the text by adding the text
  3122. **  offset to each of the anchors.
  3123. */
  3124. PUBLIC void HText_endAppend ARGS1(
  3125.     HText *,    text)
  3126. {
  3127.     int cur_line, cur_char, cur_shift;
  3128.     TextAnchor *anchor_ptr;
  3129.     HTLine *line_ptr;
  3130.     unsigned char ch;
  3131.  
  3132.     if (!text)
  3133.     return;
  3134.  
  3135.     CTRACE(tfp,"Gridtext: Entering HText_endAppend\n");
  3136.  
  3137.     /*
  3138.      *  Create a  blank line at the bottom.
  3139.      */
  3140.     new_line(text);
  3141.  
  3142.     if (text->halted) {
  3143.     /*
  3144.      *  If output was stopped because memory was low, and we made
  3145.      *  it to the end of the document, reset those flags and hope
  3146.      *  things are better now. - kw
  3147.      */
  3148.     LYFakeZap(NO);
  3149.     text->halted = 0;
  3150.     }
  3151.  
  3152.     /*
  3153.      *  Get the first line.
  3154.      */
  3155.     line_ptr = text->last_line->next;
  3156.     cur_char = line_ptr->size;
  3157.     cur_line = 0;
  3158.     cur_shift = 0;
  3159.  
  3160.     /*
  3161.      *  Remove the blank lines at the end of document.
  3162.      */
  3163.     while (text->last_line->data[0] == '\0' && text->Lines > 2) {
  3164.     HTLine *next_to_the_last_line = text->last_line->prev;
  3165.  
  3166.  
  3167.     CTRACE(tfp, "GridText: Removing bottom blank line: %s\n",
  3168.                 text->last_line->data);
  3169.     /*
  3170.      *  line_ptr points to the first line.
  3171.      */
  3172.     next_to_the_last_line->next = line_ptr;
  3173.     line_ptr->prev = next_to_the_last_line;
  3174.     FREE(text->last_line);
  3175.     text->last_line = next_to_the_last_line;
  3176.     text->Lines--;
  3177. #ifdef NOTUSED_BAD_FOR_SCREEN
  3178.     CTRACE(tfp, "GridText: New bottom line: %s\n",
  3179.                 text->last_line->data);
  3180. #endif
  3181.     }
  3182.  
  3183.     /*
  3184.      *  Fix up the anchor structure values and
  3185.      *  create the hightext strings. - FM
  3186.      */
  3187.     for (anchor_ptr = text->first_anchor;
  3188.      anchor_ptr; anchor_ptr=anchor_ptr->next) {
  3189. re_parse:
  3190.     /*
  3191.      *  Find the right line.
  3192.      */
  3193.     for (; anchor_ptr->start >= cur_char;
  3194.            line_ptr = line_ptr->next,
  3195.            cur_char += line_ptr->size+1,
  3196.            cur_line++) {
  3197.         ; /* null body */
  3198.     }
  3199.     if (anchor_ptr->start == cur_char) {
  3200.         anchor_ptr->line_pos = line_ptr->size;
  3201.     } else {
  3202.         anchor_ptr->line_pos = anchor_ptr->start -
  3203.                    (cur_char - line_ptr->size);
  3204.     }
  3205.     if (anchor_ptr->line_pos < 0)
  3206.         anchor_ptr->line_pos = 0;
  3207.  
  3208.     CTRACE(tfp, "Gridtext: Anchor found on line:%d col:%d\n",
  3209.                 cur_line, anchor_ptr->line_pos);
  3210.  
  3211.     /*
  3212.      *  Strip off any spaces or SpecialAttrChars at the beginning,
  3213.      *  if they exist, but only on HYPERTEXT_ANCHORS.
  3214.      */
  3215.     if (anchor_ptr->link_type & HYPERTEXT_ANCHOR) {
  3216.         ch = (unsigned char)line_ptr->data[anchor_ptr->line_pos];
  3217.         while (isspace(ch) ||
  3218.                IsSpecialAttrChar(ch)) {
  3219.         anchor_ptr->line_pos++;
  3220.         anchor_ptr->extent--;
  3221.         cur_shift++;
  3222.         ch = (unsigned char)line_ptr->data[anchor_ptr->line_pos];
  3223.         }
  3224.     }
  3225.     if (anchor_ptr->extent < 0) {
  3226.         anchor_ptr->extent = 0;
  3227.     }
  3228. #ifdef NOTUSED_BAD_FOR_SCREEN
  3229.     CTRACE(tfp, "anchor text: '%s'   pos: %d\n",
  3230.                 line_ptr->data, anchor_ptr->line_pos);
  3231. #endif
  3232.     /*
  3233.      *  If the link begins with an end of line and we have more
  3234.      *  lines, then start the highlighting on the next line. - FM
  3235.      */
  3236.     if (anchor_ptr->line_pos >= strlen(line_ptr->data) &&
  3237.         cur_line < text->Lines) {
  3238.         anchor_ptr->start += (cur_shift + 1);
  3239.         cur_shift = 0;
  3240.         CTRACE(tfp, "found anchor at end of line\n");
  3241.         goto re_parse;
  3242.     }
  3243.     cur_shift = 0;
  3244. #ifdef NOTUSED_BAD_FOR_SCREEN
  3245.     CTRACE(tfp, "anchor text: '%s'   pos: %d\n",
  3246.                 line_ptr->data, anchor_ptr->line_pos);
  3247. #endif
  3248.     /*
  3249.      *  Copy the link name into the data structure.
  3250.      */
  3251.     if (line_ptr->data &&
  3252.         anchor_ptr->extent > 0 && anchor_ptr->line_pos >= 0) {
  3253.         StrnAllocCopy(anchor_ptr->hightext,
  3254.               &line_ptr->data[anchor_ptr->line_pos],
  3255.               anchor_ptr->extent);
  3256.     } else {
  3257.         StrAllocCopy(anchor_ptr->hightext, "");
  3258.     }
  3259.  
  3260.     /*
  3261.      *  If true the anchor extends over two lines,
  3262.      *  copy that into the data structure.
  3263.      */
  3264.     if (anchor_ptr->extent > strlen(anchor_ptr->hightext)) {
  3265.         HTLine *line_ptr2 = line_ptr->next;
  3266.         /*
  3267.          *  Double check that we have a line pointer,
  3268.          *  and if so, copy into hightext2.
  3269.          */
  3270.         if (line_ptr2) {
  3271.         StrnAllocCopy(anchor_ptr->hightext2,
  3272.                   line_ptr2->data,
  3273.                   (anchor_ptr->extent -
  3274.                    strlen(anchor_ptr->hightext)));
  3275.             anchor_ptr->hightext2offset = line_ptr2->offset;
  3276.         remove_special_attr_chars(anchor_ptr->hightext2);
  3277.         if (anchor_ptr->link_type & HYPERTEXT_ANCHOR) {
  3278.             LYTrimTrailing(anchor_ptr->hightext2);
  3279.             if (anchor_ptr->hightext2[0] == '\0') {
  3280.             FREE(anchor_ptr->hightext2);
  3281.             anchor_ptr->hightext2offset = 0;
  3282.             }
  3283.         }
  3284.         }
  3285.     }
  3286.     remove_special_attr_chars(anchor_ptr->hightext);
  3287.     if (anchor_ptr->link_type & HYPERTEXT_ANCHOR) {
  3288.         LYTrimTrailing(anchor_ptr->hightext);
  3289.     }
  3290.  
  3291.     /*
  3292.      *  Subtract any formatting characters from the x position
  3293.      *  of the link.
  3294.      */
  3295.     if (anchor_ptr->line_pos > 0) {
  3296.         register int offset = 0, i = 0;
  3297.         for (; i < anchor_ptr->line_pos; i++)
  3298.         if (IS_UTF_EXTRA(line_ptr->data[i]) ||
  3299.             IsSpecialAttrChar(line_ptr->data[i]))
  3300.             offset++;
  3301.         anchor_ptr->line_pos -= offset;
  3302.     }
  3303.  
  3304.     /*
  3305.      *  Add the offset, and set the line number.
  3306.      */
  3307.     anchor_ptr->line_pos += line_ptr->offset;
  3308.     anchor_ptr->line_num  = cur_line;
  3309.  
  3310.     CTRACE(tfp, "GridText: adding link on line %d in HText_endAppend\n",
  3311.             cur_line);
  3312.  
  3313.     /*
  3314.      *  If this is the last anchor, we're done!
  3315.      */
  3316.     if (anchor_ptr == text->last_anchor)
  3317.         break;
  3318.     }
  3319. }
  3320.  
  3321.  
  3322. /*    Dump diagnostics to tfp
  3323. */
  3324. PUBLIC void HText_dump ARGS1(
  3325.     HText *,    text GCC_UNUSED)
  3326. {
  3327.     fprintf(tfp, "HText: Dump called\n");
  3328. }
  3329.  
  3330.  
  3331. /*    Return the anchor associated with this node
  3332. */
  3333. PUBLIC HTParentAnchor * HText_nodeAnchor ARGS1(
  3334.     HText *,    text)
  3335. {
  3336.     return text->node_anchor;
  3337. }
  3338.  
  3339. /*                GridText specials
  3340. **                =================
  3341. */
  3342. /*
  3343.  *  HTChildAnchor() returns the anchor with index N.
  3344.  *  The index corresponds to the [number] we print for the anchor.
  3345.  */
  3346. PUBLIC HTChildAnchor * HText_childNumber ARGS1(
  3347.     int,        number)
  3348. {
  3349.     TextAnchor * a;
  3350.  
  3351.     if (!(HTMainText && HTMainText->first_anchor) || number <= 0)
  3352.     return (HTChildAnchor *)0;    /* Fail */
  3353.  
  3354.     for (a = HTMainText->first_anchor; a; a = a->next) {
  3355.     if (a->number == number)
  3356.         return(a->anchor);
  3357.     }
  3358.     return (HTChildAnchor *)0;    /* Fail */
  3359. }
  3360.  
  3361. /*
  3362.  *  HText_FormDescNumber() returns a description of the form field
  3363.  *  with index N.  The index corresponds to the [number] we print
  3364.  *  for the field. - FM & LE
  3365.  */
  3366. PUBLIC void HText_FormDescNumber ARGS2(
  3367.     int,        number,
  3368.     char **,    desc)
  3369. {
  3370.     TextAnchor * a;
  3371.  
  3372.     if (!desc)
  3373.     return;
  3374.  
  3375.     if (!(HTMainText && HTMainText->first_anchor) || number <= 0) {
  3376.      *desc = "unknown field or link";
  3377.      return;
  3378.     }
  3379.  
  3380.     for (a = HTMainText->first_anchor; a; a = a->next) {
  3381.     if (a->number == number) {
  3382.         if (!(a->input_field && a->input_field->type)) {
  3383.             *desc = "unknown field or link";
  3384.         return;
  3385.         }
  3386.         break;
  3387.     }
  3388.     }
  3389.  
  3390.     switch (a->input_field->type) {
  3391.     case F_TEXT_TYPE:
  3392.         *desc = "text entry field";
  3393.         return;
  3394.     case F_PASSWORD_TYPE:
  3395.         *desc = "password entry field";
  3396.         return;
  3397.     case F_CHECKBOX_TYPE:
  3398.         *desc = "checkbox";
  3399.         return;
  3400.     case F_RADIO_TYPE:
  3401.         *desc = "radio button";
  3402.         return;
  3403.     case F_SUBMIT_TYPE:
  3404.         *desc = "submit button";
  3405.         return;
  3406.     case F_RESET_TYPE:
  3407.         *desc = "reset button";
  3408.         return;
  3409.     case F_OPTION_LIST_TYPE:
  3410.         *desc = "popup menu";
  3411.         return;
  3412.     case F_HIDDEN_TYPE:
  3413.         *desc = "hidden form field";
  3414.         return;
  3415.     case F_TEXTAREA_TYPE:
  3416.         *desc = "text entry area";
  3417.         return;
  3418.     case F_RANGE_TYPE:
  3419.         *desc = "range entry field";
  3420.         return;
  3421.     case F_FILE_TYPE:
  3422.         *desc = "file entry field";
  3423.         return;
  3424.     case F_TEXT_SUBMIT_TYPE:
  3425.         *desc = "text-submit field";
  3426.         return;
  3427.     case F_IMAGE_SUBMIT_TYPE:
  3428.         *desc = "image-submit button";
  3429.         return;
  3430.     case F_KEYGEN_TYPE:
  3431.         *desc = "keygen field";
  3432.         return;
  3433.     default:
  3434.         *desc = "unknown form field";
  3435.         return;
  3436.     }
  3437. }
  3438.  
  3439. /*
  3440.  *  HTGetLinkInfo returns some link info based on the number.
  3441.  *
  3442.  *  If want_go is not NULL, caller requests to know a line number for
  3443.  *  the link indicated by number.  It will be returned in *go_line, and
  3444.  *  *linknum will be set to an index into the links[] array, to use after
  3445.  *  the line in *line has been made the new top screen line.
  3446.  *  *hightext and *lname are unchanged. - KW
  3447.  *
  3448.  *  If want_go is 0 and the number doesn't represent an input field, info
  3449.  *  on the link indicated by number is deposited in *hightext and *lname.
  3450.  */
  3451. PUBLIC int HTGetLinkInfo ARGS6(
  3452.     int,        number,
  3453.     int,        want_go,
  3454.     int *,        go_line,
  3455.     int *,        linknum,
  3456.     char **,    hightext,
  3457.     char **,    lname)
  3458. {
  3459.     TextAnchor *a;
  3460.     HTAnchor *link_dest;
  3461. #ifndef DONT_TRACK_INTERNAL_LINKS
  3462.     HTAnchor *link_dest_intl = NULL;
  3463. #endif
  3464.     int anchors_this_line = 0, anchors_this_screen = 0;
  3465.     int prev_anchor_line = -1, prev_prev_anchor_line = -1;
  3466.  
  3467.     if (!HTMainText)
  3468.     return(NO);
  3469.  
  3470.     for (a = HTMainText->first_anchor; a; a = a->next) {
  3471.     /*
  3472.      *  Count anchors, first on current line if there is more
  3473.      *  than one.  We have to count all links, including form
  3474.      *  field anchors and others with a->number == 0, because
  3475.      *  they are or will be included in the links[] array.
  3476.      *  The exceptions are hidden form fields and anchors with
  3477.      *  show_anchor not set, because they won't appear in links[]
  3478.      *  and don't count towards nlinks. - KW
  3479.      */
  3480.     if ((a->show_anchor) &&
  3481.         (a->link_type != INPUT_ANCHOR ||
  3482.          a->input_field->type != F_HIDDEN_TYPE)) {
  3483.         if (a->line_num == prev_anchor_line) {
  3484.         anchors_this_line++;
  3485.         } else {
  3486.         /*
  3487.          *  This anchor is on a different line than the previous one.
  3488.          *  Remember which was the line number of the previous anchor,
  3489.          *  for use in screen positioning later. - KW
  3490.          */
  3491.         anchors_this_line = 1;
  3492.         prev_prev_anchor_line = prev_anchor_line;
  3493.         prev_anchor_line = a->line_num;
  3494.         }
  3495.         if (a->line_num >= HTMainText->top_of_screen) {
  3496.         /*
  3497.          *  Count all anchors starting with the top line of the
  3498.          *  currently displayed screen.  Just keep on counting
  3499.          *  beyond this screen's bottom line - we'll know whether
  3500.          *  a found anchor is below the current screen by a check
  3501.          *  against nlinks later. - KW
  3502.          */
  3503.         anchors_this_screen++;
  3504.         }
  3505.     }
  3506.  
  3507.     if (a->number == number) {
  3508.         /*
  3509.          *  We found it.  Now process it, depending
  3510.          *  on what kind of info is requested. - KW
  3511.          */
  3512.         if (want_go || a->link_type == INPUT_ANCHOR) {
  3513.         if (a->show_anchor == NO) {
  3514.             /*
  3515.              *  The number requested has been assigned to an anchor
  3516.              *  without any selectable text, so we cannot position
  3517.              *  on it.  The code for suppressing such anchors in
  3518.              *  HText_endAnchor() may not have applied, or it may
  3519.              *  have failed.  Return a failure indication so that
  3520.              *  the user will notice that something is wrong,
  3521.              *  instead of positioning on some other anchor which
  3522.              *  might result in inadvertent activation. - KW
  3523.              */
  3524.             return(NO);
  3525.         }
  3526.             if (anchors_this_screen > 0 &&
  3527.             anchors_this_screen <= nlinks &&
  3528.             a->line_num >= HTMainText->top_of_screen &&
  3529.             a->line_num < HTMainText->top_of_screen+(display_lines)) {
  3530.             /*
  3531.              *  If the requested anchor is within the current screen,
  3532.              *  just set *go_line so that the screen window won't move
  3533.              *  (keep it as it is), and set *linknum to the index of
  3534.              *  this link in the current links[] array. - KW
  3535.              */
  3536.             *go_line = HTMainText->top_of_screen;
  3537.             if (linknum)
  3538.                 *linknum = anchors_this_screen - 1;
  3539.         } else {
  3540.             /*
  3541.              *  if the requested anchor is not within the currently
  3542.              *  displayed screen, set *go_line such that the top line
  3543.              *  will be either
  3544.              *  (1) the line immediately below the previous
  3545.              *      anchor, or
  3546.              *  (2) about one third of a screenful above the line
  3547.              *      with the target, or
  3548.              *  (3) the first line of the document -
  3549.              *  whichever comes last.  In all cases the line with our
  3550.              *  target will end up being the first line with any links
  3551.              *  on the new screen, so that we can use the
  3552.              *  anchors_this_line counter to point to the anchor in
  3553.              *  the new links[] array.  - kw
  3554.              */
  3555.             int max_offset = SEARCH_GOAL_LINE - 1;
  3556.             if (max_offset < 0)
  3557.             max_offset = 0;
  3558.             else if (max_offset >= display_lines)
  3559.             max_offset = display_lines - 1;
  3560.             *go_line = prev_anchor_line - max_offset;
  3561.             if (*go_line <= prev_prev_anchor_line)
  3562.                 *go_line = prev_prev_anchor_line + 1;
  3563.             if (*go_line < 0)
  3564.                 *go_line = 0;
  3565.             if (linknum)
  3566.                 *linknum = anchors_this_line - 1;
  3567.             }
  3568.             return(LINK_LINE_FOUND);
  3569.         } else {
  3570.         *hightext= a->hightext;
  3571.         link_dest = HTAnchor_followMainLink((HTAnchor *)a->anchor);
  3572.         {
  3573.             char *cp_freeme = NULL;
  3574.             if (traversal) {
  3575.             cp_freeme = stub_HTAnchor_address(link_dest);
  3576.             } else {
  3577. #ifndef DONT_TRACK_INTERNAL_LINKS
  3578.             if (a->link_type == INTERNAL_LINK_ANCHOR) {
  3579.                 link_dest_intl = HTAnchor_followTypedLink(
  3580.                 (HTAnchor *)a->anchor, LINK_INTERNAL);
  3581.                 if (link_dest_intl && link_dest_intl != link_dest) {
  3582.  
  3583.                 CTRACE(tfp, "HTGetLinkInfo: unexpected typed link to %s!\n",
  3584.                         link_dest_intl->parent->address);
  3585.                 link_dest_intl = NULL;
  3586.                 }
  3587.             }
  3588.             if (link_dest_intl) {
  3589.                 char *cp2 = HTAnchor_address(link_dest_intl);
  3590.                 FREE(*lname);
  3591.                 *lname = cp2;
  3592.                 return(WWW_INTERN_LINK_TYPE);
  3593.             } else
  3594. #endif
  3595.                 cp_freeme = HTAnchor_address(link_dest);
  3596.             }
  3597.             StrAllocCopy(*lname, cp_freeme);
  3598.             FREE(cp_freeme);
  3599.         }
  3600.         return(WWW_LINK_TYPE);
  3601.         }
  3602.     }
  3603.     }
  3604.     return(NO);
  3605. }
  3606.  
  3607. /*
  3608.  *  This function finds the line indicated by line_num in the
  3609.  *  HText structure indicated by text, and searches that line
  3610.  *  for the first hit with the string indicated by target.  If
  3611.  *  there is no hit, FALSE is returned.  If there is a hit, then
  3612.  *  a copy of the line starting at that first hit is loaded into
  3613.  *  *data with all IsSpecial characters stripped, it's offset and
  3614.  *  the printable target length (without IsSpecial, or extra CJK
  3615.  *  or utf8 characters) are loaded into *offset and *tLen, and
  3616.  *  TRUE is returned. - FM
  3617.  */
  3618. PUBLIC BOOL HText_getFirstTargetInLine ARGS7(
  3619.     HText *,    text,
  3620.     int,        line_num,
  3621.     BOOL,        utf_flag,
  3622.     int *,        offset,
  3623.     int *,        tLen,
  3624.     char **,    data,
  3625.     char *,        target)
  3626. {
  3627.     HTLine *line;
  3628.     char *LineData;
  3629.     int LineOffset, HitOffset, LenNeeded, i;
  3630.     char *cp;
  3631.  
  3632.     /*
  3633.      *  Make sure we have an HText structure, that line_num is
  3634.      *  in its range, and that we have a target string. - FM
  3635.      */
  3636.     if (!(text && line_num >= 0 && line_num <= text->Lines &&
  3637.       target && *target))
  3638.     return(FALSE);
  3639.  
  3640.     /*
  3641.      *  Find the line and set up its data and offset - FM
  3642.      */
  3643.     for (i = 0, line = text->last_line->next;
  3644.      i < line_num && (line != text->last_line);
  3645.      i++, line = line->next) {
  3646.     if (line->next == NULL) {
  3647.         return(FALSE);
  3648.     }
  3649.     }
  3650.     if (!line && line->data[0])
  3651.     return(FALSE);
  3652.     LineData = (char *)line->data;
  3653.     LineOffset = (int)line->offset;
  3654.  
  3655.     /*
  3656.      *  If the target is on the line, load the offset of
  3657.      *  its first character and the subsequent line data,
  3658.      *  strip any special characters from the loaded line
  3659.      *  data, and return TRUE. - FM
  3660.      */
  3661.     if ((case_sensitive ?
  3662.      (cp = LYno_attr_mbcs_strstr(LineData,
  3663.                      target,
  3664.                      utf_flag,
  3665.                      &HitOffset,
  3666.                      &LenNeeded)) != NULL :
  3667.      (cp = LYno_attr_mbcs_case_strstr(LineData,
  3668.                      target,
  3669.                      utf_flag,
  3670.                      &HitOffset,
  3671.                      &LenNeeded)) != NULL) &&
  3672.     (LineOffset + LenNeeded) < LYcols) {
  3673.     /*
  3674.      *  We had a hit so load the results,
  3675.      *  remove IsSpecial characters from
  3676.      *  the allocated data string, and
  3677.      *  return TRUE. - FM
  3678.      */
  3679.     *offset = (LineOffset + HitOffset);
  3680.     *tLen = (LenNeeded - HitOffset);
  3681.      StrAllocCopy(*data, cp);
  3682.      remove_special_attr_chars(*data);
  3683.      return(TRUE);
  3684.     }
  3685.  
  3686.     /*
  3687.      *  The line does not contain the target. - FM
  3688.      */
  3689.     return(FALSE);
  3690. }
  3691.  
  3692. /*
  3693.  *  HText_getNumOfLines returns the number of lines in the
  3694.  *  current document.
  3695.  */
  3696. PUBLIC int HText_getNumOfLines NOARGS
  3697. {
  3698.     return(HTMainText ? HTMainText->Lines : 0);
  3699. }
  3700.  
  3701. /*
  3702.  *  HText_getTitle returns the title of the
  3703.  *  current document.
  3704.  */
  3705. PUBLIC char * HText_getTitle NOARGS
  3706. {
  3707.     return(HTMainText ?
  3708.       (char *) HTAnchor_title(HTMainText->node_anchor) : NULL);
  3709. }
  3710.  
  3711. #ifdef USE_HASH
  3712. PUBLIC char *HText_getStyle NOARGS
  3713. {
  3714.    return(HTMainText ?
  3715.       (char *) HTAnchor_style(HTMainText->node_anchor) : NULL);
  3716. }
  3717. #endif
  3718.  
  3719. /*
  3720.  *  HText_getSugFname returns the suggested filename of the current
  3721.  *  document (normally derived from a Content-Disposition header with
  3722.  *  attachment; filename=name.suffix). - FM
  3723.  */
  3724. PUBLIC CONST char * HText_getSugFname NOARGS
  3725. {
  3726.     return(HTMainText ?
  3727.       HTAnchor_SugFname(HTMainText->node_anchor) : 0);
  3728. }
  3729.  
  3730. /*
  3731.  *  HTCheckFnameForCompression receives the address of an allocated
  3732.  *  string containing a filename, and an anchor pointer, and expands
  3733.  *  or truncates the string's suffix if appropriate, based on whether
  3734.  *  the anchor indicates that the file is compressed.  We assume
  3735.  *  that the file was not uncompressed (as when downloading), and
  3736.  *  believe the headers about whether it's compressed or not. - FM
  3737.  *
  3738.  *  Added third arg - if strip_ok is FALSE, we don't trust the anchor
  3739.  *  info enough to remove a compression suffix if the anchor object
  3740.  *  does not indicate compression. - kw
  3741.  */
  3742. PUBLIC void HTCheckFnameForCompression ARGS3(
  3743.     char **,        fname,
  3744.     HTParentAnchor *,    anchor,
  3745.     BOOL,            strip_ok)
  3746. {
  3747.     char *fn = *fname;
  3748.     char *dot = NULL, *cp = NULL;
  3749.     CONST char *ct = NULL;
  3750.     CONST char *ce = NULL;
  3751.     BOOLEAN method = 0;
  3752.  
  3753.     /*
  3754.      *  Make sure we have a string and anchor. - FM
  3755.      */
  3756.     if (!(fn && *fn && anchor))
  3757.     return;
  3758.  
  3759.     /*
  3760.      *  Make sure we have a file, not directory, name. -FM
  3761.      */
  3762.     if ((cp = strrchr(fn, '/')) != NULL) {
  3763.     fn = (cp +1);
  3764.     if (*fn == '\0') {
  3765.         return;
  3766.     }
  3767.     }
  3768.  
  3769.     /*
  3770.      *  Check the anchor's content_type and content_encoding
  3771.      *  elements for a gzip or Unix compressed file. - FM
  3772.      */
  3773.     ct = HTAnchor_content_type(anchor);
  3774.     ce = HTAnchor_content_encoding(anchor);
  3775.     if (ce == NULL) {
  3776.     /*
  3777.      *  No Content-Encoding, so check
  3778.      *  the Content-Type. - FM
  3779.      */
  3780.     if (!strncasecomp((ct ? ct : ""), "application/gzip", 16) ||
  3781.         !strncasecomp((ct ? ct : ""), "application/x-gzip", 18)) {
  3782.         method = 1;
  3783.     } else if (!strncasecomp((ct ? ct : ""),
  3784.                  "application/compress", 20) ||
  3785.            !strncasecomp((ct ? ct : ""),
  3786.                  "application/x-compress", 22)) {
  3787.         method = 2;
  3788.     }
  3789.     } else if (!strcasecomp(ce, "gzip") ||
  3790.            !strcasecomp(ce, "x-gzip")) {
  3791.         /*
  3792.          *  It's gzipped. - FM
  3793.          */
  3794.         method = 1;
  3795.     } else if (!strcasecomp(ce, "compress") ||
  3796.            !strcasecomp(ce, "x-compress")) {
  3797.         /*
  3798.          *  It's Unix compressed. - FM
  3799.          */
  3800.         method = 2;
  3801.     }
  3802.  
  3803.     /*
  3804.      *  If no Content-Encoding has been detected via the anchor
  3805.      *  pointer, but strip_ok is not set, there is nothing left
  3806.      *  to do. - kw
  3807.      */
  3808.     if (method == 0 && !strip_ok)
  3809.     return;
  3810.  
  3811.     /*
  3812.      *  Seek the last dot, and check whether
  3813.      *  we have a gzip or compress suffix. - FM
  3814.      */
  3815.     if ((dot = strrchr(fn, '.')) != NULL) {
  3816.     if (!strcasecomp(dot, ".tgz") ||
  3817.         !strcasecomp(dot, ".gz") ||
  3818.         !strcasecomp(dot, ".Z")) {
  3819.         if (!method) {
  3820.             /*
  3821.          *  It has a suffix which signifies a gzipped
  3822.          *  or compressed file for us, but the anchor
  3823.          *  claims otherwise, so tweak the suffix. - FM
  3824.          */
  3825.             cp = (dot + 1);
  3826.         *dot = '\0';
  3827.         if (!strcasecomp(cp, "tgz")) {
  3828.             StrAllocCat(*fname, ".tar");
  3829.         }
  3830.         }
  3831.         return;
  3832.     }
  3833.     if (strlen(dot) > 4) {
  3834.         cp = ((dot + strlen(dot)) - 3);
  3835.         if (!strcasecomp(cp, "-gz") ||
  3836.         !strcasecomp(cp, "_gz")) {
  3837.         if (!method) {
  3838.                 /*
  3839.              *  It has a tail which signifies a gzipped
  3840.              *  file for us, but the anchor claims otherwise,
  3841.              *  so tweak the suffix. - FM
  3842.              */
  3843.             *dot = '\0';
  3844.         } else {
  3845.             /*
  3846.              *  The anchor claims it's gzipped, and we
  3847.              *  believe it, so force this tail to the
  3848.              *  conventional suffix. - FM
  3849.              */
  3850. #ifdef VMS
  3851.             *cp = '-';
  3852. #else
  3853.             *cp = '.';
  3854. #endif /* VMS */
  3855.             cp++;
  3856.             *cp = TOLOWER(*cp);
  3857.             cp++;
  3858.             *cp = TOLOWER(*cp);
  3859.         }
  3860.         return;
  3861.         }
  3862.     }
  3863.     if (strlen(dot) > 3) {
  3864.         cp = ((dot + strlen(dot)) - 2);
  3865.         if (!strcasecomp(cp, "-Z") ||
  3866.         !strcasecomp(cp, "_Z")) {
  3867.         if (!method) {
  3868.                 /*
  3869.              *  It has a tail which signifies a compressed
  3870.              *  file for us, but the anchor claims otherwise,
  3871.              *  so tweak the suffix. - FM
  3872.              */
  3873.             *dot = '\0';
  3874.         } else {
  3875.             /*
  3876.              *  The anchor claims it's compressed, and
  3877.              *  we believe it, so force this tail to the
  3878.              *  conventional suffix. - FM
  3879.              */
  3880. #ifdef VMS
  3881.             *cp = '-';
  3882. #else
  3883.             *cp = '.';
  3884. #endif /* VMS */
  3885.             cp++;
  3886.             *cp = TOUPPER(*cp);
  3887.         }
  3888.         return;
  3889.         }
  3890.     }
  3891.     }
  3892.     if (!method) {
  3893.     /*
  3894.      *  Don't know what compression method
  3895.      *  was used, if any, so we won't do
  3896.      *  anything. - FM
  3897.      */
  3898.     return;
  3899.     }
  3900.  
  3901.     /*
  3902.      *  Add the appropriate suffix. - FM
  3903.      */
  3904.     if (!dot) {
  3905.     StrAllocCat(*fname, ((method == 1) ? ".gz" : ".Z"));
  3906.     return;
  3907.     }
  3908.     dot++;
  3909.     if (*dot == '\0') {
  3910.     StrAllocCat(*fname, ((method == 1) ? "gz" : "Z"));
  3911.     return;
  3912.     }
  3913. #ifdef VMS
  3914.     StrAllocCat(*fname, ((method == 1) ? "-gz" : "-Z"));
  3915. #else
  3916.     StrAllocCat(*fname, ((method == 1) ? ".gz" : ".Z"));
  3917. #endif /* !VMS */
  3918.     return;
  3919. }
  3920.  
  3921. /*
  3922.  *  HText_getLastModified returns the Last-Modified header
  3923.  *  if available, for the current document. - FM
  3924.  */
  3925. PUBLIC char * HText_getLastModified NOARGS
  3926. {
  3927.     return(HTMainText ?
  3928.       (char *) HTAnchor_last_modified(HTMainText->node_anchor) : NULL);
  3929. }
  3930.  
  3931. /*
  3932.  *  HText_getDate returns the Date header
  3933.  *  if available, for the current document. - FM
  3934.  */
  3935. PUBLIC char * HText_getDate NOARGS
  3936. {
  3937.     return(HTMainText ?
  3938.       (char *) HTAnchor_date(HTMainText->node_anchor) : NULL);
  3939. }
  3940.  
  3941. /*
  3942.  *  HText_getServer returns the Server header
  3943.  *  if available, for the current document. - FM
  3944.  */
  3945. PUBLIC char * HText_getServer NOARGS
  3946. {
  3947.     return(HTMainText ?
  3948.       (char *)HTAnchor_server(HTMainText->node_anchor) : NULL);
  3949. }
  3950.  
  3951. /*
  3952.  *  HText_pageDisplay displays a screen of text
  3953.  *  starting from the line 'line_num'-1
  3954.  *  this is the primary call for lynx
  3955.  */
  3956. PUBLIC void HText_pageDisplay ARGS2(
  3957.     int,        line_num,
  3958.     char *,        target)
  3959. {
  3960.     display_page(HTMainText, line_num-1, target);
  3961.  
  3962.     is_www_index = HTAnchor_isIndex(HTMainAnchor);
  3963. }
  3964.  
  3965. /*
  3966.  *  Return YES if we have a whereis search target on the displayed
  3967.  *  page. - kw
  3968.  */
  3969. PUBLIC BOOL HText_pageHasPrevTarget NOARGS
  3970. {
  3971.     if (!HTMainText)
  3972.     return NO;
  3973.     else
  3974.     return HTMainText->page_has_target;
  3975. }
  3976.  
  3977. /*
  3978.  *  HText_LinksInLines returns the number of links in the
  3979.  *  'Lines' number of lines beginning with 'line_num'-1. - FM
  3980.  */
  3981. PUBLIC int HText_LinksInLines ARGS3(
  3982.     HText *,    text,
  3983.     int,        line_num,
  3984.     int,        Lines)
  3985. {
  3986.     int total = 0;
  3987.     int start = (line_num - 1);
  3988.     int end = (start + Lines);
  3989.     TextAnchor *Anchor_ptr = NULL;
  3990.  
  3991.     if (!text)
  3992.     return total;
  3993.  
  3994.     for (Anchor_ptr = text->first_anchor;
  3995.         Anchor_ptr != NULL && Anchor_ptr->line_num <= end;
  3996.             Anchor_ptr = Anchor_ptr->next) {
  3997.     if (Anchor_ptr->line_num >= start &&
  3998.         Anchor_ptr->line_num < end &&
  3999.         Anchor_ptr->show_anchor &&
  4000.         (Anchor_ptr->link_type != INPUT_ANCHOR ||
  4001.          Anchor_ptr->input_field->type != F_HIDDEN_TYPE))
  4002.         ++total;
  4003.     if (Anchor_ptr == text->last_anchor)
  4004.         break;
  4005.     }
  4006.  
  4007.     return total;
  4008. }
  4009.  
  4010. PUBLIC void HText_setStale ARGS1(
  4011.     HText *,    text)
  4012. {
  4013.     text->stale = YES;
  4014. }
  4015.  
  4016. PUBLIC void HText_refresh ARGS1(
  4017.     HText *,    text)
  4018. {
  4019.     if (text->stale)
  4020.     display_page(text, text->top_of_screen, "");
  4021. }
  4022.  
  4023. PUBLIC int HText_sourceAnchors ARGS1(
  4024.     HText *,    text)
  4025. {
  4026.     return (text ? text->last_anchor_number : -1);
  4027. }
  4028.  
  4029. PUBLIC BOOL HText_canScrollUp ARGS1(
  4030.     HText *,    text)
  4031. {
  4032.     return (text->top_of_screen != 0);
  4033. }
  4034.  
  4035. PUBLIC BOOL HText_canScrollDown NOARGS
  4036. {
  4037.     HText * text = HTMainText;
  4038.  
  4039.     return ((text->top_of_screen + display_lines) < text->Lines+1);
  4040. }
  4041.  
  4042. /*        Scroll actions
  4043. */
  4044. PUBLIC void HText_scrollTop ARGS1(
  4045.     HText *,    text)
  4046. {
  4047.     display_page(text, 0, "");
  4048. }
  4049.  
  4050. PUBLIC void HText_scrollDown ARGS1(
  4051.     HText *,    text)
  4052. {
  4053.     display_page(text, text->top_of_screen + display_lines, "");
  4054. }
  4055.  
  4056. PUBLIC void HText_scrollUp ARGS1(
  4057.     HText *,    text)
  4058. {
  4059.     display_page(text, text->top_of_screen - display_lines, "");
  4060. }
  4061.  
  4062. PUBLIC void HText_scrollBottom ARGS1(
  4063.     HText *,    text)
  4064. {
  4065.     display_page(text, text->Lines - display_lines, "");
  4066. }
  4067.  
  4068.  
  4069. /*        Browsing functions
  4070. **        ==================
  4071. */
  4072.  
  4073. /* Bring to front and highlight it
  4074. */
  4075. PRIVATE int line_for_char ARGS2(
  4076.     HText *,    text,
  4077.     int,        char_num)
  4078. {
  4079.     int line_number = 0;
  4080.     int characters = 0;
  4081.     HTLine * line = text->last_line->next;
  4082.     for (;;) {
  4083.     if (line == text->last_line) return 0;    /* Invalid */
  4084.     characters = characters + line->size + 1;
  4085.     if (characters > char_num) return line_number;
  4086.     line_number ++;
  4087.     line = line->next;
  4088.     }
  4089. }
  4090.  
  4091. PUBLIC BOOL HText_select ARGS1(
  4092.     HText *,    text)
  4093. {
  4094.     if (text != HTMainText) {
  4095.     HTMainText = text;
  4096.     HTMainAnchor = text->node_anchor;
  4097.  
  4098.     /*
  4099.      *  Reset flag for whereis search string - cannot be true here
  4100.      *  since text is not our HTMainText. - kw
  4101.      */
  4102.     if (text)
  4103.         text->page_has_target = NO;
  4104.     /*
  4105.      *  Make this text the most current in the loaded texts list. - FM
  4106.      */
  4107.     if (loaded_texts && HTList_removeObject(loaded_texts, text))
  4108.         HTList_addObject(loaded_texts, text);
  4109.       /* let lynx do it */
  4110.     /* display_page(text, text->top_of_screen, ""); */
  4111.     }
  4112.     return YES;
  4113. }
  4114.  
  4115. /*
  4116.  *  This function returns TRUE if doc's post_data, address
  4117.  *  and isHEAD elements are identical to those of a loaded
  4118.  *  (memory cached) text. - FM
  4119.  */
  4120. PUBLIC BOOL HText_POSTReplyLoaded ARGS1(
  4121.     document *,    doc)
  4122. {
  4123.     HText *text = NULL;
  4124.     HTList *cur = loaded_texts;
  4125.     char *post_data, *address;
  4126.     BOOL is_head;
  4127.  
  4128.     /*
  4129.      *  Make sure we have the structures. - FM
  4130.      */
  4131.     if (!cur || !doc)
  4132.     return(FALSE);
  4133.  
  4134.     /*
  4135.      *  Make sure doc is for a POST reply. - FM
  4136.      */
  4137.     if ((post_data = doc->post_data) == NULL ||
  4138.     (address = doc->address) == NULL)
  4139.     return(FALSE);
  4140.     is_head = doc->isHEAD;
  4141.  
  4142.     /*
  4143.      *  Loop through the loaded texts looking for a
  4144.      *  POST reply match. - FM
  4145.      */
  4146.     while (NULL != (text = (HText *)HTList_nextObject(cur))) {
  4147.     if (text->node_anchor &&
  4148.         text->node_anchor->post_data &&
  4149.         !strcmp(post_data, text->node_anchor->post_data) &&
  4150.         text->node_anchor->address &&
  4151.         !strcmp(address, text->node_anchor->address) &&
  4152.         is_head == text->node_anchor->isHEAD) {
  4153.         return(TRUE);
  4154.     }
  4155.     }
  4156.  
  4157.     return(FALSE);
  4158. }
  4159.  
  4160. PUBLIC BOOL HTFindPoundSelector ARGS1(
  4161.     char *,        selector)
  4162. {
  4163.     TextAnchor * a;
  4164.  
  4165.     for (a=HTMainText->first_anchor; a; a=a->next) {
  4166.  
  4167.     if (a->anchor && a->anchor->tag)
  4168.         if (!strcmp(a->anchor->tag, selector)) {
  4169.  
  4170.         www_search_result = a->line_num+1;
  4171.  
  4172.         CTRACE(tfp,
  4173.                "HText: Selecting anchor [%d] at character %d, line %d\n",
  4174.                      a->number, a->start, www_search_result);
  4175.         if (!strcmp(selector, LYToolbarName))
  4176.             --www_search_result;
  4177.  
  4178.          return(YES);
  4179.         }
  4180.     }
  4181.  
  4182.     return(NO);
  4183.  
  4184. }
  4185.  
  4186. PUBLIC BOOL HText_selectAnchor ARGS2(
  4187.     HText *,        text,
  4188.     HTChildAnchor *,    anchor)
  4189. {
  4190.     TextAnchor * a;
  4191.  
  4192. /* This is done later, hence HText_select is unused in GridText.c
  4193.    Should it be the contrary ? @@@
  4194.     if (text != HTMainText) {
  4195.     HText_select(text);
  4196.     }
  4197. */
  4198.  
  4199.     for (a=text->first_anchor; a; a=a->next) {
  4200.     if (a->anchor == anchor) break;
  4201.     }
  4202.     if (!a) {
  4203.     CTRACE(tfp, "HText: No such anchor in this text!\n");
  4204.     return NO;
  4205.     }
  4206.  
  4207.     if (text != HTMainText) {        /* Comment out by ??? */
  4208.     HTMainText = text;        /* Put back in by tbl 921208 */
  4209.     HTMainAnchor = text->node_anchor;
  4210.     }
  4211.  
  4212.     {
  4213.      int l = line_for_char(text, a->start);
  4214.      CTRACE(tfp,
  4215.         "HText: Selecting anchor [%d] at character %d, line %d\n",
  4216.         a->number, a->start, l);
  4217.  
  4218.     if ( !text->stale &&
  4219.          (l >= text->top_of_screen) &&
  4220.          ( l < text->top_of_screen + display_lines+1))
  4221.              return YES;
  4222.  
  4223.     www_search_result = l - (display_lines/3); /* put in global variable */
  4224.     }
  4225.  
  4226.     return YES;
  4227. }
  4228.  
  4229.  
  4230. /*        Editing functions        - NOT IMPLEMENTED
  4231. **        =================
  4232. **
  4233. **    These are called from the application. There are many more functions
  4234. **    not included here from the original text object.
  4235. */
  4236.  
  4237. /*    Style handling:
  4238. */
  4239. /*    Apply this style to the selection
  4240. */
  4241. PUBLIC void HText_applyStyle ARGS2(
  4242.     HText *,    me GCC_UNUSED,
  4243.     HTStyle *,    style GCC_UNUSED)
  4244. {
  4245.  
  4246. }
  4247.  
  4248.  
  4249. /*    Update all text with changed style.
  4250. */
  4251. PUBLIC void HText_updateStyle ARGS2(
  4252.     HText *,    me GCC_UNUSED,
  4253.     HTStyle *,    style GCC_UNUSED)
  4254. {
  4255.  
  4256. }
  4257.  
  4258.  
  4259. /*    Return style of  selection
  4260. */
  4261. PUBLIC HTStyle * HText_selectionStyle ARGS2(
  4262.     HText *,        me GCC_UNUSED,
  4263.     HTStyleSheet *,        sheet GCC_UNUSED)
  4264. {
  4265.     return 0;
  4266. }
  4267.  
  4268.  
  4269. /*    Paste in styled text
  4270. */
  4271. PUBLIC void HText_replaceSel ARGS3(
  4272.     HText *,    me GCC_UNUSED,
  4273.     CONST char *,    aString GCC_UNUSED,
  4274.     HTStyle *,    aStyle GCC_UNUSED)
  4275. {
  4276. }
  4277.  
  4278.  
  4279. /*    Apply this style to the selection and all similarly formatted text
  4280. **    (style recovery only)
  4281. */
  4282. PUBLIC void HTextApplyToSimilar ARGS2(
  4283.     HText *,    me GCC_UNUSED,
  4284.     HTStyle *,    style GCC_UNUSED)
  4285. {
  4286.  
  4287. }
  4288.  
  4289.  
  4290. /*    Select the first unstyled run.
  4291. **    (style recovery only)
  4292. */
  4293. PUBLIC void HTextSelectUnstyled ARGS2(
  4294.     HText *,        me GCC_UNUSED,
  4295.     HTStyleSheet *,        sheet GCC_UNUSED)
  4296. {
  4297.  
  4298. }
  4299.  
  4300.  
  4301. /*    Anchor handling:
  4302. */
  4303. PUBLIC void HText_unlinkSelection ARGS1(
  4304.     HText *,    me GCC_UNUSED)
  4305. {
  4306.  
  4307. }
  4308.  
  4309. PUBLIC HTAnchor * HText_referenceSelected ARGS1(
  4310.     HText *,    me GCC_UNUSED)
  4311. {
  4312.      return 0;
  4313. }
  4314.  
  4315.  
  4316. PUBLIC int HText_getTopOfScreen NOARGS
  4317. {
  4318.       HText * text = HTMainText;
  4319.       return text->top_of_screen;
  4320. }
  4321.  
  4322. PUBLIC int HText_getLines ARGS1(
  4323.     HText *,    text)
  4324. {
  4325.       return text->Lines;
  4326. }
  4327.  
  4328. PUBLIC HTAnchor * HText_linkSelTo ARGS2(
  4329.     HText *,    me GCC_UNUSED,
  4330.     HTAnchor *,    anchor GCC_UNUSED)
  4331. {
  4332.     return 0;
  4333. }
  4334.  
  4335. /*
  4336.  *  Utility for freeing the list of previous isindex and whereis queries. - FM
  4337.  */
  4338. PUBLIC void HTSearchQueries_free NOARGS
  4339. {
  4340.     char *query;
  4341.     HTList *cur = search_queries;
  4342.  
  4343.     if (!cur)
  4344.     return;
  4345.  
  4346.     while (NULL != (query = (char *)HTList_nextObject(cur))) {
  4347.     FREE(query);
  4348.     }
  4349.     HTList_delete(search_queries);
  4350.     search_queries = NULL;
  4351.     return;
  4352. }
  4353.  
  4354. /*
  4355.  *  Utility for listing isindex and whereis queries, making
  4356.  *  any repeated queries the most current in the list. - FM
  4357.  */
  4358. PUBLIC void HTAddSearchQuery ARGS1(
  4359.     char *,        query)
  4360. {
  4361.     char *new;
  4362.     char *old;
  4363.     HTList *cur;
  4364.  
  4365.     if (!(query && *query))
  4366.     return;
  4367.  
  4368.     if ((new = (char *)calloc(1, (strlen(query) + 1))) == NULL)
  4369.     outofmem(__FILE__, "HTAddSearchQuery");
  4370.     strcpy(new, query);
  4371.  
  4372.     if (!search_queries) {
  4373.     search_queries = HTList_new();
  4374.     atexit(HTSearchQueries_free);
  4375.     HTList_addObject(search_queries, new);
  4376.     return;
  4377.     }
  4378.  
  4379.     cur = search_queries;
  4380.     while (NULL != (old = (char *)HTList_nextObject(cur))) {
  4381.     if (!strcmp(old, new)) {
  4382.         HTList_removeObject(search_queries, old);
  4383.         FREE(old);
  4384.         break;
  4385.     }
  4386.     }
  4387.     HTList_addObject(search_queries, new);
  4388.  
  4389.     return;
  4390. }
  4391.  
  4392. PUBLIC int do_www_search ARGS1(
  4393.     document *,    doc)
  4394. {
  4395.     char searchstring[256], temp[256], *cp, *tmpaddress = NULL;
  4396.     int ch, recall;
  4397.     int QueryTotal;
  4398.     int QueryNum;
  4399.     BOOLEAN PreviousSearch = FALSE;
  4400.  
  4401.     /*
  4402.      *  Load the default query buffer
  4403.      */
  4404.     if ((cp=strchr(doc->address, '?')) != NULL) {
  4405.     /*
  4406.      *  This is an index from a previous search.
  4407.      *  Use its query as the default.
  4408.      */
  4409.     PreviousSearch = TRUE;
  4410.     strcpy(searchstring, ++cp);
  4411.     for (cp=searchstring; *cp; cp++)
  4412.         if (*cp == '+')
  4413.             *cp = ' ';
  4414.     HTUnEscape(searchstring);
  4415.     strcpy(temp, searchstring);
  4416.     /*
  4417.      *  Make sure it's treated as the most recent query. - FM
  4418.      */
  4419.     HTAddSearchQuery(searchstring);
  4420.     } else {
  4421.     /*
  4422.      *  New search; no default.
  4423.      */
  4424.     searchstring[0] = '\0';
  4425.     temp[0] = '\0';
  4426.     }
  4427.  
  4428.     /*
  4429.      *  Prompt for a query string.
  4430.      */
  4431.     if (searchstring[0] == '\0') {
  4432.     if (HTMainAnchor->isIndexPrompt)
  4433.         _statusline(HTMainAnchor->isIndexPrompt);
  4434.     else
  4435.         _statusline(ENTER_DATABASE_QUERY);
  4436.     } else
  4437.     _statusline(EDIT_CURRENT_QUERY);
  4438.     QueryTotal = (search_queries ? HTList_count(search_queries) : 0);
  4439.     recall = (((PreviousSearch && QueryTotal >= 2) ||
  4440.            (!PreviousSearch && QueryTotal >= 1)) ? RECALL : NORECALL);
  4441.     QueryNum = QueryTotal;
  4442. get_query:
  4443.     if ((ch=LYgetstr(searchstring, VISIBLE,
  4444.              sizeof(searchstring), recall)) < 0 ||
  4445.     *searchstring == '\0' || ch == UPARROW || ch == DNARROW) {
  4446.     if (recall && ch == UPARROW) {
  4447.         if (PreviousSearch) {
  4448.             /*
  4449.          *  Use the second to last query in the list. - FM
  4450.          */
  4451.             QueryNum = 1;
  4452.         PreviousSearch = FALSE;
  4453.         } else {
  4454.             /*
  4455.          *  Go back to the previous query in the list. - FM
  4456.          */
  4457.             QueryNum++;
  4458.         }
  4459.         if (QueryNum >= QueryTotal)
  4460.             /*
  4461.          *  Roll around to the last query in the list. - FM
  4462.          */
  4463.             QueryNum = 0;
  4464.         if ((cp=(char *)HTList_objectAt(search_queries,
  4465.                         QueryNum)) != NULL) {
  4466.         strcpy(searchstring, cp);
  4467.         if (*temp && !strcmp(temp, searchstring)) {
  4468.             _statusline(EDIT_CURRENT_QUERY);
  4469.         } else if ((*temp && QueryTotal == 2) ||
  4470.                (!(*temp) && QueryTotal == 1)) {
  4471.             _statusline(EDIT_THE_PREV_QUERY);
  4472.         } else {
  4473.             _statusline(EDIT_A_PREV_QUERY);
  4474.         }
  4475.         goto get_query;
  4476.         }
  4477.     } else if (recall && ch == DNARROW) {
  4478.         if (PreviousSearch) {
  4479.             /*
  4480.          *  Use the first query in the list. - FM
  4481.          */
  4482.             QueryNum = QueryTotal - 1;
  4483.         PreviousSearch = FALSE;
  4484.         } else {
  4485.             /*
  4486.          *  Advance to the next query in the list. - FM
  4487.          */
  4488.             QueryNum--;
  4489.         }
  4490.         if (QueryNum < 0)
  4491.             /*
  4492.          *  Roll around to the first query in the list. - FM
  4493.          */
  4494.         QueryNum = QueryTotal - 1;
  4495.         if ((cp=(char *)HTList_objectAt(search_queries,
  4496.                         QueryNum)) != NULL) {
  4497.         strcpy(searchstring, cp);
  4498.         if (*temp && !strcmp(temp, searchstring)) {
  4499.             _statusline(EDIT_CURRENT_QUERY);
  4500.         } else if ((*temp && QueryTotal == 2) ||
  4501.                (!(*temp) && QueryTotal == 1)) {
  4502.             _statusline(EDIT_THE_PREV_QUERY);
  4503.         } else {
  4504.             _statusline(EDIT_A_PREV_QUERY);
  4505.         }
  4506.         goto get_query;
  4507.         }
  4508.     }
  4509.  
  4510.     /*
  4511.      *  Search cancelled.
  4512.      */
  4513.     _statusline(CANCELLED);
  4514.     sleep(InfoSecs);
  4515.     return(NULLFILE);
  4516.     }
  4517.  
  4518.     /*
  4519.      *  Strip leaders and trailers. - FM
  4520.      */
  4521.     LYTrimLeading(searchstring);
  4522.     if (!(*searchstring)) {
  4523.     _statusline(CANCELLED);
  4524.     sleep(InfoSecs);
  4525.     return(NULLFILE);
  4526.     }
  4527.     LYTrimTrailing(searchstring);
  4528.  
  4529.     /*
  4530.      *  Don't resubmit the same query unintentionally.
  4531.      */
  4532.     if (!LYforce_no_cache && 0 == strcmp(temp, searchstring)) {
  4533.     _statusline(USE_C_R_TO_RESUB_CUR_QUERY);
  4534.     sleep(MessageSecs);
  4535.     return(NULLFILE);
  4536.     }
  4537.  
  4538.     /*
  4539.      *  Add searchstring to the query list,
  4540.      *  or make it the most current. - FM
  4541.      */
  4542.     HTAddSearchQuery(searchstring);
  4543.  
  4544.     /*
  4545.      *  Show the URL with the new query.
  4546.      */
  4547.     if ((cp=strchr(doc->address, '?')) != NULL)
  4548.     *cp = '\0';
  4549.     StrAllocCopy(tmpaddress, doc->address);
  4550.     StrAllocCat(tmpaddress, "?");
  4551.     StrAllocCat(tmpaddress, searchstring);
  4552.     user_message(WWW_WAIT_MESSAGE, tmpaddress);
  4553. #ifndef VMS
  4554. #ifdef SYSLOG_REQUESTED_URLS
  4555.     syslog(LOG_INFO|LOG_LOCAL5, "%s", tmpaddress);
  4556. #endif /* SYSLOG_REQUESTED_URLS */
  4557. #endif /* !VMS */
  4558.     FREE(tmpaddress);
  4559.     if (cp)
  4560.     *cp = '?';
  4561.  
  4562.     /*
  4563.      *  OK, now we do the search.
  4564.      */
  4565.     if (HTSearch(searchstring, HTMainAnchor)) {
  4566.     /*
  4567.      *    Memory leak fixed.
  4568.      *    05-28-94 Lynx 2-3-1 Garrett Arch Blythe
  4569.      */
  4570.     auto char *cp_freeme = NULL;
  4571.     if (traversal)
  4572.         cp_freeme = stub_HTAnchor_address((HTAnchor *)HTMainAnchor);
  4573.     else
  4574.         cp_freeme = HTAnchor_address((HTAnchor *)HTMainAnchor);
  4575.     StrAllocCopy(doc->address, cp_freeme);
  4576.     FREE(cp_freeme);
  4577.  
  4578.     CTRACE(tfp,"\ndo_www_search: newfile: %s\n",doc->address);
  4579.  
  4580.     /*
  4581.      *  Yah, the search succeeded.
  4582.      */
  4583.     return(NORMAL);
  4584.     }
  4585.  
  4586.     /*
  4587.      *  Either the search failed (Yuk), or we got redirection.
  4588.      *  If it's redirection, use_this_url_instead is set, and
  4589.      *  mainloop() will deal with it such that security features
  4590.      *  and restrictions are checked before acting on the URL, or
  4591.      *  rejecting it. - FM
  4592.      */
  4593.     return(NOT_FOUND);
  4594. }
  4595.  
  4596. /*
  4597.  *  Print the contents of the file in HTMainText to
  4598.  *  the file descriptor fp.
  4599.  *  If is_reply is TRUE add ">" to the beginning of each
  4600.  *  line to specify the file is a reply to message.
  4601.  */
  4602. PUBLIC void print_wwwfile_to_fd ARGS2(
  4603.     FILE *,        fp,
  4604.     int,        is_reply)
  4605. {
  4606.     register int i;
  4607.     HTLine * line;
  4608. #ifdef VMS
  4609.     extern BOOLEAN HadVMSInterrupt;
  4610. #endif /* VMS */
  4611.  
  4612.     if (!HTMainText)
  4613.     return;
  4614.  
  4615.     line = HTMainText->last_line->next;
  4616.     for (;; line = line->next) {
  4617.     /*
  4618.      *  Add news-style quotation if requested. - FM
  4619.      */
  4620.     if (is_reply) {
  4621.         fputc('>',fp);
  4622.     }
  4623.  
  4624.         /*
  4625.      *  Add offset.
  4626.      */
  4627.     for (i = 0; i < (int)line->offset; i++) {
  4628.          fputc(' ', fp);
  4629.     }
  4630.  
  4631.     /*
  4632.      *  Add data.
  4633.      */
  4634.     for (i = 0; line->data[i] != '\0'; i++) {
  4635.         if (!IsSpecialAttrChar(line->data[i])) {
  4636.         fputc(line->data[i],fp);
  4637.         } else if (line->data[i] == LY_SOFT_HYPHEN &&
  4638.         line->data[i + 1] == '\0') { /* last char on line */
  4639.         if (dump_output_immediately &&
  4640.             LYRawMode &&
  4641.             LYlowest_eightbit[current_char_set] <= 173 &&
  4642.             (current_char_set == 0 ||
  4643.              LYCharSet_UC[current_char_set].enc == UCT_ENC_8859 ||
  4644.              LYCharSet_UC[current_char_set].like8859 &
  4645.                   UCT_R_8859SPECL)) {
  4646.             fputc(0xad, fp); /* the iso8859 byte for SHY */
  4647.         } else {
  4648.             fputc('-', fp);
  4649.         }
  4650.         } else if (dump_output_immediately && use_underscore) {
  4651.         switch (line->data[i]) {
  4652.             case LY_UNDERLINE_START_CHAR:
  4653.             case LY_UNDERLINE_END_CHAR:
  4654.             fputc('_', fp);
  4655.             break;
  4656.             case LY_BOLD_START_CHAR:
  4657.             case LY_BOLD_END_CHAR:
  4658.             break;
  4659.         }
  4660.         }
  4661.     }
  4662.  
  4663.     /*
  4664.      *  Add the return.
  4665.      */
  4666.     fputc('\n',fp);
  4667.  
  4668.     if (line == HTMainText->last_line)
  4669.         break;
  4670.  
  4671. #ifdef VMS
  4672.     if (HadVMSInterrupt)
  4673.         break;
  4674. #endif /* VMS */
  4675.     }
  4676.  
  4677. }
  4678.  
  4679. /*
  4680.  *  Print the contents of the file in HTMainText to
  4681.  *  the file descriptor fp.
  4682.  *  First output line is "thelink", ie, the URL for this file.
  4683.  */
  4684. PUBLIC void print_crawl_to_fd ARGS3(
  4685.     FILE *,        fp,
  4686.     char *,        thelink,
  4687.     char *,        thetitle)
  4688. {
  4689.     register int i;
  4690.     HTLine * line;
  4691. #ifdef VMS
  4692.     extern BOOLEAN HadVMSInterrupt;
  4693. #endif /* VMS */
  4694.  
  4695.     if (!HTMainText)
  4696.     return;
  4697.  
  4698.     line = HTMainText->last_line->next;
  4699.     fprintf(fp, "THE_URL:%s\n", thelink);
  4700.     if (thetitle != NULL) {
  4701.     fprintf(fp, "THE_TITLE:%s\n", thetitle);
  4702.     }
  4703.  
  4704.     for (;; line = line->next) {
  4705.     /*
  4706.      *  Add offset.
  4707.      */
  4708.     for (i = 0; i < (int)line->offset; i++) {
  4709.         fputc(' ', fp);
  4710.     }
  4711.  
  4712.     /*
  4713.      *  Add data.
  4714.      */
  4715.     for (i = 0; line->data[i] != '\0'; i++) {
  4716.         if (!IsSpecialAttrChar(line->data[i])) {
  4717.         fputc(line->data[i], fp);
  4718.          } else if (line->data[i] == LY_SOFT_HYPHEN &&
  4719.          line->data[i + 1] == '\0') { /* last char on line */
  4720.          if (dump_output_immediately &&
  4721.              LYRawMode &&
  4722.              LYlowest_eightbit[current_char_set] <= 173 &&
  4723.              (current_char_set == 0 ||
  4724.               LYCharSet_UC[current_char_set].enc == UCT_ENC_8859 ||
  4725.               LYCharSet_UC[current_char_set].like8859 &
  4726.                    UCT_R_8859SPECL)) {
  4727.              fputc(0xad, fp); /* the iso8859 byte for SHY */
  4728.          } else {
  4729.              fputc('-', fp);
  4730.          }
  4731.          }
  4732.     }
  4733.  
  4734.     /*
  4735.      *  Add the return.
  4736.      */
  4737.     fputc('\n',fp);
  4738.  
  4739.     if (line == HTMainText->last_line) {
  4740.         break;
  4741.     }
  4742.     }
  4743.  
  4744.     /*
  4745.      *  Add the References list if appropriate
  4746.      */
  4747.     if ((nolist == FALSE) &&
  4748.     (keypad_mode == LINKS_ARE_NUMBERED ||
  4749.      keypad_mode == LINKS_AND_FORM_FIELDS_ARE_NUMBERED)) {
  4750.     printlist(fp,FALSE);
  4751.     }
  4752.  
  4753. #ifdef VMS
  4754.     HadVMSInterrupt = FALSE;
  4755. #endif /* VMS */
  4756. }
  4757.  
  4758. PRIVATE void adjust_search_result ARGS3(
  4759.     document *,    doc,
  4760.     int,    tentative_result,
  4761.     int,    start_line)
  4762. {
  4763.     if (tentative_result > 0) {
  4764.     int anch_line = -1;
  4765.     TextAnchor * a;
  4766.     int nl_closest = -1;
  4767.     int goal = SEARCH_GOAL_LINE;
  4768.     int max_offset;
  4769.     BOOL on_screen = (tentative_result > HTMainText->top_of_screen &&
  4770.         tentative_result <= HTMainText->top_of_screen+display_lines);
  4771.     if (goal < 1)
  4772.         goal = 1;
  4773.     else if (goal > display_lines)
  4774.         goal = display_lines;
  4775.     max_offset = goal - 1;
  4776.  
  4777.     if (on_screen && nlinks > 0) {
  4778.         int i;
  4779.         for (i = 0; i < nlinks; i++) {
  4780.         if (doc->line + links[i].ly - 1 <= tentative_result)
  4781.             nl_closest = i;
  4782.         if (doc->line + links[i].ly - 1 >= tentative_result)
  4783.             break;
  4784.         }
  4785.         if (nl_closest >= 0 &&
  4786.         doc->line + links[nl_closest].ly - 1 == tentative_result) {
  4787.         www_search_result = doc->line;
  4788.         doc->link = nl_closest;
  4789.         return;
  4790.         }
  4791.     }
  4792.  
  4793.     /* find last anchor before or on target line */
  4794.     for (a = HTMainText->first_anchor;
  4795.          a && a->line_num <= tentative_result-1; a = a->next) {
  4796.         anch_line = a->line_num + 1;
  4797.     }
  4798.     /* position such that the anchor found is on first screen line,
  4799.        if it is not too far above the target line; but also try to
  4800.        make sure we move forward. */
  4801.     if (anch_line >= 0 &&
  4802.         anch_line >= tentative_result - max_offset &&
  4803.         (anch_line > start_line ||
  4804.         tentative_result <= HTMainText->top_of_screen)) {
  4805.         www_search_result = anch_line;
  4806.     } else
  4807.     if (tentative_result - start_line > 0 &&
  4808.         tentative_result - (start_line + 1) <= max_offset) {
  4809.         www_search_result = start_line + 1;
  4810.     } else
  4811.     if (tentative_result > HTMainText->top_of_screen &&
  4812.         tentative_result <= start_line && /* have wrapped */
  4813.         tentative_result <= HTMainText->top_of_screen+goal) {
  4814.         www_search_result = HTMainText->top_of_screen + 1;
  4815.     } else
  4816.     if (tentative_result <= goal)
  4817.         www_search_result = 1;
  4818.     else
  4819.         www_search_result = tentative_result - max_offset;
  4820.     if (www_search_result == doc->line) {
  4821.         if (nl_closest >= 0) {
  4822.         doc->link = nl_closest;
  4823.         return;
  4824.         }
  4825.     }
  4826.     }
  4827. }
  4828.  
  4829. PUBLIC void www_user_search ARGS3(
  4830.     int,        start_line,
  4831.     document *,    doc,
  4832.     char *,        target)
  4833. {
  4834.     register HTLine * line;
  4835.     register int count;
  4836.     int tentative_result = -1;
  4837.     TextAnchor *a;
  4838.     OptionType *option;
  4839.     char *stars = NULL, *cp;
  4840.  
  4841.     if (!HTMainText) {
  4842.     return;
  4843.     }
  4844.  
  4845.     /*
  4846.      *  Advance to the start line.
  4847.      */
  4848.     line = HTMainText->last_line->next;
  4849.     for (count = 1; count <= start_line; line = line->next, count++) {
  4850.     if (line == HTMainText->last_line) {
  4851.         line = HTMainText->last_line->next; /* set to first line */
  4852.         count = 1;
  4853.         break;
  4854.     }
  4855.     }
  4856.     a = HTMainText->first_anchor;
  4857.     while (a && a->line_num < count - 1) {
  4858.     a = a->next;
  4859.     }
  4860.  
  4861.     for (;;) {
  4862.     while ((a != NULL) && a->line_num == (count - 1)) {
  4863.         if (a->show_anchor &&
  4864.         (a->link_type != INPUT_ANCHOR ||
  4865.          a->input_field->type != F_HIDDEN_TYPE)) {
  4866.         if (((a->hightext != NULL && case_sensitive == TRUE) &&
  4867.              LYno_attr_char_strstr(a->hightext, target)) ||
  4868.             ((a->hightext != NULL && case_sensitive == FALSE) &&
  4869.              LYno_attr_char_case_strstr(a->hightext, target))) {
  4870.             adjust_search_result(doc, count, start_line);
  4871.             return;
  4872.         }
  4873.         if (((a->hightext2 != NULL && case_sensitive == TRUE) &&
  4874.              LYno_attr_char_strstr(a->hightext2, target)) ||
  4875.             ((a->hightext2 != NULL && case_sensitive == FALSE) &&
  4876.              LYno_attr_char_case_strstr(a->hightext2, target))) {
  4877.             adjust_search_result(doc, count, start_line);
  4878.             return;
  4879.         }
  4880.  
  4881.         /*
  4882.          *  Search the relevant form fields, taking the
  4883.          *  case_sensitive setting into account. - FM
  4884.          */
  4885.         if ((a->input_field != NULL && a->input_field->value != NULL) &&
  4886.             a->input_field->type != F_HIDDEN_TYPE) {
  4887.             if (a->input_field->type == F_PASSWORD_TYPE) {
  4888.                 /*
  4889.              *  Check the actual, hidden password, and then
  4890.              *  the displayed string. - FM
  4891.              */
  4892.             if (((case_sensitive == TRUE) &&
  4893.                  LYno_attr_char_strstr(a->input_field->value,
  4894.                            target)) ||
  4895.                 ((case_sensitive == FALSE) &&
  4896.                  LYno_attr_char_case_strstr(a->input_field->value,
  4897.                             target))) {
  4898.                 adjust_search_result(doc, count, start_line);
  4899.                 return;
  4900.             }
  4901.             StrAllocCopy(stars, a->input_field->value);
  4902.             for (cp = stars; *cp != '\0'; cp++)
  4903.                 *cp = '*';
  4904.             if (((case_sensitive == TRUE) &&
  4905.                  LYno_attr_char_strstr(stars, target)) ||
  4906.                 ((case_sensitive == FALSE) &&
  4907.                  LYno_attr_char_case_strstr(stars, target))) {
  4908.                 FREE(stars);
  4909.                 adjust_search_result(doc, count, start_line);
  4910.                 return;
  4911.             }
  4912.             FREE(stars);
  4913.            } else if (a->input_field->type == F_OPTION_LIST_TYPE) {
  4914.             /*
  4915.              *  Search the option strings that are displayed
  4916.              *  when the popup is invoked. - FM
  4917.              */
  4918.             option = a->input_field->select_list;
  4919.             while (option != NULL) {
  4920.                 if (((option->name != NULL &&
  4921.                   case_sensitive == TRUE) &&
  4922.                  LYno_attr_char_strstr(option->name,
  4923.                                target)) ||
  4924.                 ((option->name != NULL &&
  4925.                   case_sensitive == FALSE) &&
  4926.                  LYno_attr_char_case_strstr(option->name,
  4927.                                 target))) {
  4928.                 adjust_search_result(doc, count, start_line);
  4929.                 return;
  4930.                 }
  4931.                 option = option->next;
  4932.             }
  4933.             } else if (a->input_field->type == F_RADIO_TYPE) {
  4934.             /*
  4935.              *  Search for checked or unchecked parens. - FM
  4936.              */
  4937.                 if (a->input_field->num_value) {
  4938.                 cp = checked_radio;
  4939.             } else {
  4940.                 cp = unchecked_radio;
  4941.             }
  4942.             if (((case_sensitive == TRUE) &&
  4943.                  LYno_attr_char_strstr(cp, target)) ||
  4944.                 ((case_sensitive == FALSE) &&
  4945.                  LYno_attr_char_case_strstr(cp, target))) {
  4946.                 adjust_search_result(doc, count, start_line);
  4947.                 return;
  4948.             }
  4949.             } else if (a->input_field->type == F_CHECKBOX_TYPE) {
  4950.             /*
  4951.              *  Search for checked or unchecked
  4952.              *  square brackets. - FM
  4953.              */
  4954.                 if (a->input_field->num_value) {
  4955.                 cp = checked_box;
  4956.             } else {
  4957.                 cp = unchecked_box;
  4958.             }
  4959.             if (((case_sensitive == TRUE) &&
  4960.                  LYno_attr_char_strstr(cp, target)) ||
  4961.                 ((case_sensitive == FALSE) &&
  4962.                  LYno_attr_char_case_strstr(cp, target))) {
  4963.                 adjust_search_result(doc, count, start_line);
  4964.                 return;
  4965.             }
  4966.             } else {
  4967.                 /*
  4968.              *  Check the values intended for display.
  4969.              *  May have been found already via the
  4970.              *  hightext search, but make sure here
  4971.              *  that the entire value is searched. - FM
  4972.              */
  4973.             if (((case_sensitive == TRUE) &&
  4974.                  LYno_attr_char_strstr(a->input_field->value,
  4975.                            target)) ||
  4976.                 ((case_sensitive == FALSE) &&
  4977.                  LYno_attr_char_case_strstr(a->input_field->value,
  4978.                             target))) {
  4979.                 adjust_search_result(doc, count, start_line);
  4980.                 return;
  4981.             }
  4982.             }
  4983.         }
  4984.         }
  4985.         a = a->next;
  4986.     }
  4987.     if (a != NULL && a->line_num <= (count - 1)) {
  4988.         a = a->next;
  4989.     }
  4990.  
  4991.     if (case_sensitive && LYno_attr_char_strstr(line->data, target)) {
  4992.         tentative_result = count;
  4993.         break;
  4994.     } else if (!case_sensitive &&
  4995.            LYno_attr_char_case_strstr(line->data, target)) {
  4996.         tentative_result = count;
  4997.         break;
  4998.     } else if (line == HTMainText->last_line) {  /* next line */
  4999.         break;
  5000.     } else {            /* end */
  5001.         line = line->next;
  5002.         count++;
  5003.     }
  5004.     }
  5005.     if (tentative_result > 0) {
  5006.     adjust_search_result(doc, tentative_result, start_line);
  5007.     return;
  5008.     }
  5009.  
  5010.     /*
  5011.      *  Search from the beginning.
  5012.      */
  5013.     line = HTMainText->last_line->next; /* set to first line */
  5014.     count = 1;
  5015.     a = HTMainText->first_anchor;
  5016.     while (a && a->line_num < count - 1) {
  5017.     a = a->next;
  5018.     }
  5019.  
  5020.     for (;;) {
  5021.     while ((a != NULL) && a->line_num == (count - 1)) {
  5022.         if (a->show_anchor &&
  5023.         (a->link_type != INPUT_ANCHOR ||
  5024.          a->input_field->type != F_HIDDEN_TYPE)) {
  5025.         if (((a->hightext != NULL && case_sensitive == TRUE) &&
  5026.              LYno_attr_char_strstr(a->hightext, target)) ||
  5027.             ((a->hightext != NULL && case_sensitive == FALSE) &&
  5028.              LYno_attr_char_case_strstr(a->hightext, target))) {
  5029.             adjust_search_result(doc, count, start_line);
  5030.             return;
  5031.         }
  5032.         if (((a->hightext2 != NULL && case_sensitive == TRUE) &&
  5033.              LYno_attr_char_strstr(a->hightext2, target)) ||
  5034.             ((a->hightext2 != NULL && case_sensitive == FALSE) &&
  5035.              LYno_attr_char_case_strstr(a->hightext2, target))) {
  5036.             adjust_search_result(doc, count, start_line);
  5037.             return;
  5038.         }
  5039.  
  5040.         /*
  5041.          *  Search the relevant form fields, taking the
  5042.          *  case_sensitive setting into account. - FM
  5043.          */
  5044.         if ((a->input_field != NULL && a->input_field->value != NULL) &&
  5045.             a->input_field->type != F_HIDDEN_TYPE) {
  5046.             if (a->input_field->type == F_PASSWORD_TYPE) {
  5047.                 /*
  5048.              *  Check the actual, hidden password, and then
  5049.              *  the displayed string. - FM
  5050.              */
  5051.             if (((case_sensitive == TRUE) &&
  5052.                  LYno_attr_char_strstr(a->input_field->value,
  5053.                            target)) ||
  5054.                 ((case_sensitive == FALSE) &&
  5055.                  LYno_attr_char_case_strstr(a->input_field->value,
  5056.                             target))) {
  5057.                 adjust_search_result(doc, count, start_line);
  5058.                 return;
  5059.             }
  5060.             StrAllocCopy(stars, a->input_field->value);
  5061.             for (cp = stars; *cp != '\0'; cp++)
  5062.                 *cp = '*';
  5063.             if (((case_sensitive == TRUE) &&
  5064.                  LYno_attr_char_strstr(stars, target)) ||
  5065.                 ((case_sensitive == FALSE) &&
  5066.                  LYno_attr_char_case_strstr(stars, target))) {
  5067.                 FREE(stars);
  5068.                 adjust_search_result(doc, count, start_line);
  5069.                 return;
  5070.             }
  5071.             FREE(stars);
  5072.            } else if (a->input_field->type == F_OPTION_LIST_TYPE) {
  5073.             /*
  5074.              *  Search the option strings that are displayed
  5075.              *  when the popup is invoked. - FM
  5076.              */
  5077.             option = a->input_field->select_list;
  5078.             while (option != NULL) {
  5079.                 if (((option->name != NULL &&
  5080.                   case_sensitive == TRUE) &&
  5081.                  LYno_attr_char_strstr(option->name,
  5082.                                target)) ||
  5083.                 ((option->name != NULL &&
  5084.                   case_sensitive == FALSE) &&
  5085.                  LYno_attr_char_case_strstr(option->name,
  5086.                                 target))) {
  5087.                 adjust_search_result(doc, count, start_line);
  5088.                 return;
  5089.                 }
  5090.                 option = option->next;
  5091.             }
  5092.             } else if (a->input_field->type == F_RADIO_TYPE) {
  5093.             /*
  5094.              *  Search for checked or unchecked parens. - FM
  5095.              */
  5096.                 if (a->input_field->num_value) {
  5097.                 cp = checked_radio;
  5098.             } else {
  5099.                 cp = unchecked_radio;
  5100.             }
  5101.             if (((case_sensitive == TRUE) &&
  5102.                  LYno_attr_char_strstr(cp, target)) ||
  5103.                 ((case_sensitive == FALSE) &&
  5104.                  LYno_attr_char_case_strstr(cp, target))) {
  5105.                 adjust_search_result(doc, count, start_line);
  5106.                 return;
  5107.             }
  5108.             } else if (a->input_field->type == F_CHECKBOX_TYPE) {
  5109.             /*
  5110.              *  Search for checked or unchecked
  5111.              *  square brackets. - FM
  5112.              */
  5113.                 if (a->input_field->num_value) {
  5114.                 cp = checked_box;
  5115.             } else {
  5116.                 cp = unchecked_box;
  5117.             }
  5118.             if (((case_sensitive == TRUE) &&
  5119.                  LYno_attr_char_strstr(cp, target)) ||
  5120.                 ((case_sensitive == FALSE) &&
  5121.                  LYno_attr_char_case_strstr(cp, target))) {
  5122.                 adjust_search_result(doc, count, start_line);
  5123.                 return;
  5124.             }
  5125.             } else {
  5126.                 /*
  5127.              *  Check the values intended for display.
  5128.              *  May have been found already via the
  5129.              *  hightext search, but make sure here
  5130.              *  that the entire value is searched. - FM
  5131.              */
  5132.             if (((case_sensitive == TRUE) &&
  5133.                  LYno_attr_char_strstr(a->input_field->value,
  5134.                            target)) ||
  5135.                 ((case_sensitive == FALSE) &&
  5136.                  LYno_attr_char_case_strstr(a->input_field->value,
  5137.                             target))) {
  5138.                 adjust_search_result(doc, count, start_line);
  5139.                 return;
  5140.             }
  5141.             }
  5142.         }
  5143.         }
  5144.         a = a->next;
  5145.     }
  5146.     if (a != NULL && a->line_num <= (count - 1)) {
  5147.         a = a->next;
  5148.     }
  5149.  
  5150.         if (case_sensitive && LYno_attr_char_strstr(line->data, target)) {
  5151.             tentative_result=count;
  5152.         break;
  5153.         } else if (!case_sensitive &&
  5154.                LYno_attr_char_case_strstr(line->data, target)) {
  5155.             tentative_result = count;
  5156.         break;
  5157.         } else if (count > start_line) {  /* next line */
  5158.         _user_message(STRING_NOT_FOUND, target);
  5159.         sleep(MessageSecs);
  5160.             return;            /* end */
  5161.         } else {
  5162.             line = line->next;
  5163.         count++;
  5164.     }
  5165.     }
  5166.     if (tentative_result > 0) {
  5167.     adjust_search_result(doc, tentative_result, start_line);
  5168.     }
  5169. }
  5170.  
  5171. PUBLIC void user_message ARGS2(
  5172.     CONST char *,    message,
  5173.     CONST char *,    argument)
  5174. {
  5175.     char *temp = NULL;
  5176.     char temp_arg[256];
  5177.  
  5178.     if (message == NULL) {
  5179.     mustshow = FALSE;
  5180.     return;
  5181.     }
  5182.  
  5183.     /*
  5184.      *  Make sure we don't overrun any buffers.
  5185.      */
  5186.     LYstrncpy(temp_arg, ((argument == NULL) ? "" : argument), 255);
  5187.     temp_arg[255] = '\0';
  5188.     temp = (char *)malloc(strlen(message) + strlen(temp_arg) + 1);
  5189.     if (temp == NULL)
  5190.     outofmem(__FILE__, "user_message");
  5191.     sprintf(temp, message, temp_arg);
  5192.  
  5193.     statusline(temp);
  5194.  
  5195.     FREE(temp);
  5196.     return;
  5197. }
  5198.  
  5199. /*
  5200.  *  HText_getOwner returns the owner of the
  5201.  *  current document.
  5202.  */
  5203. PUBLIC char * HText_getOwner NOARGS
  5204. {
  5205.     return(HTMainText ?
  5206.        (char *)HTAnchor_owner(HTMainText->node_anchor) : NULL);
  5207. }
  5208.  
  5209. /*
  5210. *   HText_setMainTextOwner sets the owner for the
  5211.  *  current document.
  5212.  */
  5213. PUBLIC void HText_setMainTextOwner ARGS1(
  5214.     CONST char *,    owner)
  5215. {
  5216.     if (!HTMainText)
  5217.     return;
  5218.  
  5219.     HTAnchor_setOwner(HTMainText->node_anchor, owner);
  5220. }
  5221.  
  5222. /*
  5223.  *  HText_getRevTitle returns the RevTitle element of the
  5224.  *  current document, used as the subject for mailto comments
  5225.  *  to the owner.
  5226.  */
  5227. PUBLIC char * HText_getRevTitle NOARGS
  5228. {
  5229.     return(HTMainText ?
  5230.        (char *)HTAnchor_RevTitle(HTMainText->node_anchor) : NULL);
  5231. }
  5232.  
  5233. /*
  5234.  *  HText_getContentBase returns the Content-Base header
  5235.  *  of the current document.
  5236.  */
  5237. PUBLIC CONST char * HText_getContentBase NOARGS
  5238. {
  5239.     return(HTMainText ?
  5240.        HTAnchor_content_base(HTMainText->node_anchor) : 0);
  5241. }
  5242.  
  5243. /*
  5244.  *  HText_getContentLocation returns the Content-Location header
  5245.  *  of the current document.
  5246.  */
  5247. PUBLIC CONST char * HText_getContentLocation NOARGS
  5248. {
  5249.     return(HTMainText ?
  5250.        HTAnchor_content_location(HTMainText->node_anchor) : 0);
  5251. }
  5252.  
  5253. PUBLIC void HTuncache_current_document NOARGS
  5254. {
  5255.     /*
  5256.      *  Should remove current document from memory.
  5257.      */
  5258.     if (HTMainText) {
  5259.     HTParentAnchor * htmain_anchor = HTMainText->node_anchor;
  5260.  
  5261.     if (htmain_anchor) {
  5262.         if (!(HTOutputFormat && HTOutputFormat == WWW_SOURCE)) {
  5263.         FREE(htmain_anchor->UCStages);
  5264.         }
  5265.     }
  5266.     CTRACE(tfp, "\rHTuncache.. freeing document for '%s'%s\n",
  5267.                 ((htmain_anchor &&
  5268.                   htmain_anchor->address) ?
  5269.                    htmain_anchor->address : "unknown anchor"),
  5270.                 ((htmain_anchor &&
  5271.                   htmain_anchor->post_data) ?
  5272.                       " with POST data" : ""));
  5273.     HTList_removeObject(loaded_texts, HTMainText);
  5274.     HText_free(HTMainText);
  5275.     HTMainText = NULL;
  5276.     } else {
  5277.     CTRACE(tfp, "HTuncache.. HTMainText already is NULL!\n");
  5278.     }
  5279. }
  5280.  
  5281. PUBLIC int HTisDocumentSource NOARGS
  5282. {
  5283.     return(HTMainText->source);
  5284. }
  5285.  
  5286. PUBLIC char * HTLoadedDocumentURL NOARGS
  5287. {
  5288.     if (!HTMainText)
  5289.     return ("");
  5290.  
  5291.     if (HTMainText->node_anchor && HTMainText->node_anchor->address)
  5292.     return(HTMainText->node_anchor->address);
  5293.     else
  5294.     return ("");
  5295. }
  5296.  
  5297. PUBLIC char * HTLoadedDocumentPost_data NOARGS
  5298. {
  5299.     if (!HTMainText)
  5300.     return ("");
  5301.  
  5302.     if (HTMainText->node_anchor && HTMainText->node_anchor->post_data)
  5303.     return(HTMainText->node_anchor->post_data);
  5304.     else
  5305.     return ("");
  5306. }
  5307.  
  5308. PUBLIC char * HTLoadedDocumentTitle NOARGS
  5309. {
  5310.     if (!HTMainText)
  5311.     return ("");
  5312.  
  5313.     if (HTMainText->node_anchor && HTMainText->node_anchor->title)
  5314.     return(HTMainText->node_anchor->title);
  5315.     else
  5316.     return ("");
  5317. }
  5318.  
  5319. PUBLIC BOOLEAN HTLoadedDocumentIsHEAD NOARGS
  5320. {
  5321.     if (!HTMainText)
  5322.     return (FALSE);
  5323.  
  5324.     if (HTMainText->node_anchor && HTMainText->node_anchor->isHEAD)
  5325.     return(HTMainText->node_anchor->isHEAD);
  5326.     else
  5327.     return (FALSE);
  5328. }
  5329.  
  5330. PUBLIC BOOLEAN HTLoadedDocumentIsSafe NOARGS
  5331. {
  5332.     if (!HTMainText)
  5333.     return (FALSE);
  5334.  
  5335.     if (HTMainText->node_anchor && HTMainText->node_anchor->safe)
  5336.     return(HTMainText->node_anchor->safe);
  5337.     else
  5338.     return (FALSE);
  5339. }
  5340.  
  5341. PUBLIC char * HTLoadedDocumentCharset NOARGS
  5342. {
  5343.     if (!HTMainText)
  5344.     return (NULL);
  5345.  
  5346.     if (HTMainText->node_anchor && HTMainText->node_anchor->charset)
  5347.     return(HTMainText->node_anchor->charset);
  5348.     else
  5349.     return (NULL);
  5350. }
  5351.  
  5352. PUBLIC BOOL HTLoadedDocumentEightbit NOARGS
  5353. {
  5354.     if (!HTMainText)
  5355.     return (NO);
  5356.     else
  5357.     return (HTMainText->have_8bit_chars);
  5358. }
  5359.  
  5360. PUBLIC void HText_setNodeAnchorBookmark ARGS1(
  5361.     CONST char *,    bookmark)
  5362. {
  5363.     if (!HTMainText)
  5364.     return;
  5365.  
  5366.     if (HTMainText->node_anchor)
  5367.     HTAnchor_setBookmark(HTMainText->node_anchor, bookmark);
  5368. }
  5369.  
  5370. PUBLIC char * HTLoadedDocumentBookmark NOARGS
  5371. {
  5372.     if (!HTMainText)
  5373.     return (NULL);
  5374.  
  5375.     if (HTMainText->node_anchor && HTMainText->node_anchor->bookmark)
  5376.     return(HTMainText->node_anchor->bookmark);
  5377.     else
  5378.     return (NULL);
  5379. }
  5380.  
  5381. PUBLIC int HText_LastLineSize ARGS2(
  5382.     HText *,    text,
  5383.     BOOL,        IgnoreSpaces)
  5384. {
  5385.     if (!text || !text->last_line || !text->last_line->size)
  5386.     return 0;
  5387.     return HText_TrueLineSize(text->last_line, text, IgnoreSpaces);
  5388. }
  5389.  
  5390. PUBLIC int HText_PreviousLineSize ARGS2(
  5391.     HText *,    text,
  5392.     BOOL,        IgnoreSpaces)
  5393. {
  5394.     HTLine * line;
  5395.  
  5396.     if (!text || !text->last_line)
  5397.     return 0;
  5398.     if (!(line = text->last_line->prev))
  5399.     return 0;
  5400.     return HText_TrueLineSize(line, text, IgnoreSpaces);
  5401. }
  5402.  
  5403. PRIVATE int HText_TrueLineSize ARGS3(
  5404.     HTLine *,    line,
  5405.     HText *,    text,
  5406.     BOOL,        IgnoreSpaces)
  5407. {
  5408.     size_t i;
  5409.     int true_size = 0;
  5410.  
  5411.     if (!(line && line->size))
  5412.     return 0;
  5413.  
  5414.     if (IgnoreSpaces) {
  5415.     for (i = 0; i < line->size; i++) {
  5416.         if (!IsSpecialAttrChar((unsigned char)line->data[i]) &&
  5417.         (!(text && text->T.output_utf8) ||
  5418.          (unsigned char)line->data[i] < 128 ||
  5419.          ((unsigned char)(line->data[i] & 0xc0) == 0xc0)) &&
  5420.             !isspace((unsigned char)line->data[i]) &&
  5421.         (unsigned char)line->data[i] != HT_NON_BREAK_SPACE &&
  5422.         (unsigned char)line->data[i] != HT_EM_SPACE) {
  5423.         true_size++;
  5424.         }
  5425.     }
  5426.     } else {
  5427.     for (i = 0; i < line->size; i++) {
  5428.         if (!IsSpecialAttrChar(line->data[i]) &&
  5429.         (!(text && text->T.output_utf8) ||
  5430.          (unsigned char)line->data[i] < 128 ||
  5431.          ((unsigned char)(line->data[i] & 0xc0) == 0xc0))) {
  5432.         true_size++;
  5433.         }
  5434.     }
  5435.     }
  5436.     return true_size;
  5437. }
  5438.  
  5439. PUBLIC void HText_NegateLineOne ARGS1(
  5440.     HText *,    text)
  5441. {
  5442.     if (text) {
  5443.     text->in_line_1 = NO;
  5444.     }
  5445.     return;
  5446. }
  5447.  
  5448. /*
  5449.  *  This function is for removing the first of two
  5450.  *  successive blank lines.  It should be called after
  5451.  *  checking the situation with HText_LastLineSize()
  5452.  *  and HText_PreviousLineSize().  Any characters in
  5453.  *  the removed line (i.e., control characters, or it
  5454.  *  wouldn't have tested blank) should have been
  5455.  *  reiterated by split_line() in the retained blank
  5456.  *  line. - FM
  5457.  */
  5458. PUBLIC void HText_RemovePreviousLine ARGS1(
  5459.     HText *,    text)
  5460. {
  5461.     HTLine *line, *previous;
  5462.     char *data;
  5463.  
  5464.     if (!(text && text->Lines > 1))
  5465.     return;
  5466.  
  5467.     line = text->last_line->prev;
  5468.     data = line->data;
  5469.     previous = line->prev;
  5470.     previous->next = text->last_line;
  5471.     text->last_line->prev = previous;
  5472.     text->chars -= ((data && *data == '\0') ?
  5473.                       1 : strlen(line->data) + 1);
  5474.     text->Lines--;
  5475.     FREE(line);
  5476. }
  5477.  
  5478. /*
  5479.  *  NOTE: This function presently is correct only if the
  5480.  *      alignment is HT_LEFT.  The offset is still zero,
  5481.  *      because that's not determined for HT_CENTER or
  5482.  *      HT_RIGHT until subsequent characters are received
  5483.  *      and split_line() is called. - FM
  5484.  */
  5485. PUBLIC int HText_getCurrentColumn ARGS1(
  5486.     HText *,    text)
  5487. {
  5488.     int column = 0;
  5489.     BOOL IgnoreSpaces = FALSE;
  5490.  
  5491.     if (text) {
  5492.     column = (text->in_line_1 ?
  5493.           (int)text->style->indent1st : (int)text->style->leftIndent)
  5494.           + HText_LastLineSize(text, IgnoreSpaces)
  5495.           + (int)text->last_line->offset;
  5496.     }
  5497.     return column;
  5498. }
  5499.  
  5500. PUBLIC int HText_getMaximumColumn ARGS1(
  5501.     HText *,    text)
  5502. {
  5503.     int column = (LYcols-2);
  5504.     if (text) {
  5505.     column = ((int)text->style->rightIndent ? (LYcols-2) :
  5506.           ((LYcols-1) - (int)text->style->rightIndent));
  5507.     }
  5508.     return column;
  5509. }
  5510.  
  5511. /*
  5512.  *  NOTE: This function uses HText_getCurrentColumn() which
  5513.  *      presently is correct only if the alignment is
  5514.  *      HT_LEFT. - FM
  5515.  */
  5516. PUBLIC void HText_setTabID ARGS2(
  5517.     HText *,    text,
  5518.     CONST char *,    name)
  5519. {
  5520.     HTTabID * Tab = NULL;
  5521.     HTList * cur = text->tabs;
  5522.     HTList * last = NULL;
  5523.  
  5524.     if (!text || !name || !*name)
  5525.     return;
  5526.  
  5527.     if (!cur) {
  5528.     cur = text->tabs = HTList_new();
  5529.     } else {
  5530.     while (NULL != (Tab = (HTTabID *)HTList_nextObject(cur))) {
  5531.         if (Tab->name && !strcmp(Tab->name, name))
  5532.             return; /* Already set.  Keep the first value. */
  5533.         last = cur;
  5534.     }
  5535.     if (last)
  5536.         cur = last;
  5537.     }
  5538.     if (!Tab) { /* New name.  Create a new node */
  5539.     Tab = (HTTabID *)calloc(1, sizeof(HTTabID));
  5540.     if (Tab == NULL)
  5541.         outofmem(__FILE__, "HText_setTabID");
  5542.     HTList_addObject(cur, Tab);
  5543.     StrAllocCopy(Tab->name, name);
  5544.     }
  5545.     Tab->column = HText_getCurrentColumn(text);
  5546.     return;
  5547. }
  5548.  
  5549. PUBLIC int HText_getTabIDColumn ARGS2(
  5550.     HText *,    text,
  5551.     CONST char *,    name)
  5552. {
  5553.     int column = 0;
  5554.     HTTabID * Tab;
  5555.     HTList * cur = text->tabs;
  5556.  
  5557.     if (text && name && *name && cur) {
  5558.     while (NULL != (Tab = (HTTabID *)HTList_nextObject(cur))) {
  5559.         if (Tab->name && !strcmp(Tab->name, name))
  5560.             break;
  5561.     }
  5562.     if (Tab)
  5563.         column = Tab->column;
  5564.     }
  5565.     return column;
  5566. }
  5567.  
  5568. /*
  5569.  *  This function is for saving the address of a link
  5570.  *  which had an attribute in the markup that resolved
  5571.  *  to a URL (i.e., not just a NAME or ID attribute),
  5572.  *  but was found in HText_endAnchor() to have no visible
  5573.  *  content for use as a link name.  It loads the address
  5574.  *  into text->hidden_links, whose count can be determined
  5575.  *  via HText_HiddenLinks(), below.  The addresses can be
  5576.  *  retrieved via HText_HiddenLinkAt(), below, based on
  5577.  *  count. - FM
  5578.  */
  5579. PRIVATE void HText_AddHiddenLink ARGS2(
  5580.     HText *,    text,
  5581.     TextAnchor *,    textanchor)
  5582. {
  5583.     HTAnchor *dest;
  5584.  
  5585.     /*
  5586.      *  Make sure we have an HText structure and anchor. - FM
  5587.      */
  5588.     if (!(text && textanchor && textanchor->anchor))
  5589.     return;
  5590.  
  5591.     /*
  5592.      *  Create the hidden links list
  5593.      *  if it hasn't been already. - FM
  5594.      */
  5595.     if (text->hidden_links == NULL)
  5596.     text->hidden_links = HTList_new();
  5597.  
  5598.     /*
  5599.      *  Store the address, in reverse list order
  5600.      *  so that first in will be first out on
  5601.      *  retrievals. - FM
  5602.      */
  5603.     if ((dest = HTAnchor_followMainLink((HTAnchor *)textanchor->anchor)) &&
  5604.     (text->hiddenlinkflag != HIDDENLINKS_IGNORE ||
  5605.      HTList_isEmpty(text->hidden_links)))
  5606.     HTList_appendObject(text->hidden_links, HTAnchor_address(dest));
  5607.  
  5608.     return;
  5609. }
  5610.  
  5611. /*
  5612.  *  This function returns the number of addresses
  5613.  *  that are loaded in text->hidden_links. - FM
  5614.  */
  5615. PUBLIC int HText_HiddenLinkCount ARGS1(
  5616.     HText *,    text)
  5617. {
  5618.     int count = 0;
  5619.  
  5620.     if (text && text->hidden_links)
  5621.     count = HTList_count((HTList *)text->hidden_links);
  5622.  
  5623.     return(count);
  5624. }
  5625.  
  5626. /*
  5627.  *  This function returns the address, corresponding to
  5628.  *  a hidden link, at the position (zero-based) in the
  5629.  *  text->hidden_links list of the number argument. - FM
  5630.  */
  5631. PUBLIC char * HText_HiddenLinkAt ARGS2(
  5632.     HText *,    text,
  5633.     int,        number)
  5634. {
  5635.     char *href = NULL;
  5636.  
  5637.     if (text && text->hidden_links && number >= 0)
  5638.     href = (char *)HTList_objectAt((HTList *)text->hidden_links, number);
  5639.  
  5640.     return(href);
  5641. }
  5642.  
  5643.  
  5644. /*
  5645.  *  Form methods
  5646.  *    These routines are used to build forms consisting
  5647.  *    of input fields
  5648.  */
  5649. PRIVATE int HTFormMethod;
  5650. PRIVATE char * HTFormAction = NULL;
  5651. PRIVATE char * HTFormEnctype = NULL;
  5652. PRIVATE char * HTFormTitle = NULL;
  5653. PRIVATE char * HTFormAcceptCharset = NULL;
  5654. PRIVATE BOOLEAN HTFormDisabled = FALSE;
  5655. PRIVATE PerFormInfo * HTCurrentForm;
  5656.  
  5657. PUBLIC void HText_beginForm ARGS5(
  5658.     char *,        action,
  5659.     char *,        method,
  5660.     char *,        enctype,
  5661.     char *,        title,
  5662.     CONST char *,    accept_cs)
  5663. {
  5664.     PerFormInfo * newform;
  5665.     HTFormMethod = URL_GET_METHOD;
  5666.     HTFormNumber++;
  5667.     HTFormFields = 0;
  5668.     HTFormDisabled = FALSE;
  5669.  
  5670.     /*
  5671.      *  Check the ACTION. - FM
  5672.      */
  5673.     if (action != NULL) {
  5674.     if (!strncmp(action, "mailto:", 7)) {
  5675.         HTFormMethod = URL_MAIL_METHOD;
  5676.     }
  5677.     StrAllocCopy(HTFormAction, action);
  5678.     }
  5679.     else
  5680.     StrAllocCopy(HTFormAction, HTLoadedDocumentURL());
  5681.  
  5682.     /*
  5683.      *  Check the METHOD. - FM
  5684.      */
  5685.     if (method != NULL && HTFormMethod != URL_MAIL_METHOD)
  5686.     if (!strcasecomp(method,"post") || !strcasecomp(method,"pget"))
  5687.         HTFormMethod = URL_POST_METHOD;
  5688.  
  5689.     /*
  5690.      *  Check the ENCTYPE. - FM
  5691.      */
  5692.     if ((enctype != NULL) && *enctype) {
  5693.     StrAllocCopy(HTFormEnctype, enctype);
  5694.     if (HTFormMethod != URL_MAIL_METHOD &&
  5695.         !strncasecomp(enctype, "multipart/form-data", 19))
  5696.         HTFormMethod = URL_POST_METHOD;
  5697.     } else {
  5698.     FREE(HTFormEnctype);
  5699.     }
  5700.  
  5701.     /*
  5702.      *  Check the TITLE. - FM
  5703.      */
  5704.     if ((title != NULL) && *title)
  5705.     StrAllocCopy(HTFormTitle, title);
  5706.     else
  5707.     FREE(HTFormTitle);
  5708.  
  5709.     /*
  5710.      *  Check for an ACCEPT_CHARSET.  If present, store it and
  5711.      *  convert to lowercase and collapse spaces. - kw
  5712.      */
  5713.     if (accept_cs != NULL) {
  5714.     StrAllocCopy(HTFormAcceptCharset, accept_cs);
  5715.     LYRemoveBlanks(HTFormAcceptCharset);
  5716.     LYLowerCase(HTFormAcceptCharset);
  5717.     }
  5718.  
  5719.     /*
  5720.      *  Create a new "PerFormInfo" structure to hold info on the current
  5721.      *  form.  The HTForm* variables could all migrate there, currently
  5722.      *  this isn't done (yet?) but it might be less confusing.
  5723.      *  Currently the only data saved in this structure that will actually
  5724.      *  be used is the accept_cs string.
  5725.      *  This will be appended to the forms list kept by the HText object
  5726.      *  if and when we reach a HText_endForm. - kw
  5727.      */
  5728.     newform = (PerFormInfo *)calloc(1, sizeof(PerFormInfo));
  5729.     if (newform == NULL)
  5730.     outofmem(__FILE__,"HText_beginForm");
  5731.     newform->number = HTFormNumber;
  5732.  
  5733.     PerFormInfo_free(HTCurrentForm); /* shouldn't happen here - kw */
  5734.     HTCurrentForm = newform;
  5735.  
  5736.     CTRACE(tfp, "BeginForm: action:%s Method:%d%s%s%s%s%s%s\n",
  5737.         HTFormAction, HTFormMethod,
  5738.         (HTFormTitle ? " Title:" : ""),
  5739.         (HTFormTitle ? HTFormTitle : ""),
  5740.         (HTFormEnctype ? " Enctype:" : ""),
  5741.         (HTFormEnctype ? HTFormEnctype : ""),
  5742.         (HTFormAcceptCharset ? " Accept-charset:" : ""),
  5743.         (HTFormAcceptCharset ? HTFormAcceptCharset : ""));
  5744. }
  5745.  
  5746. PUBLIC void HText_endForm ARGS1(
  5747.     HText *,    text)
  5748. {
  5749.     if (HTFormFields == 1 && text && text->first_anchor) {
  5750.     /*
  5751.      *  Support submission of a single text input field in
  5752.      *  the form via <return> instead of a submit button. - FM
  5753.      */
  5754.     TextAnchor * a = text->first_anchor;
  5755.     /*
  5756.      *  Go through list of anchors and get our input field. - FM
  5757.      */
  5758.     while (a) {
  5759.         if (a->link_type == INPUT_ANCHOR &&
  5760.             a->input_field->number == HTFormNumber &&
  5761.         a->input_field->type == F_TEXT_TYPE) {
  5762.         /*
  5763.          *  Got it.  Make it submitting. - FM
  5764.          */
  5765.         a->input_field->submit_action = NULL;
  5766.         StrAllocCopy(a->input_field->submit_action, HTFormAction);
  5767.         if (HTFormEnctype != NULL)
  5768.             StrAllocCopy(a->input_field->submit_enctype,
  5769.                  HTFormEnctype);
  5770.         if (HTFormTitle != NULL)
  5771.             StrAllocCopy(a->input_field->submit_title, HTFormTitle);
  5772.         a->input_field->submit_method = HTFormMethod;
  5773.         a->input_field->type = F_TEXT_SUBMIT_TYPE;
  5774.         if (HTFormDisabled)
  5775.             a->input_field->disabled = TRUE;
  5776.         break;
  5777.         }
  5778.         if (a == text->last_anchor)
  5779.             break;
  5780.         a = a->next;
  5781.     }
  5782.     }
  5783.     /*
  5784.      *  Append info on the current form to the HText object's list of
  5785.      *  forms.
  5786.      *  HText_beginInput call will have set some of the data in the
  5787.      *  PerFormInfo structure (if there were any form fields at all),
  5788.      *  we also fill in the ACCEPT-CHARSET data now (this could have
  5789.      *  been done earlier). - kw
  5790.      */
  5791.     if (HTCurrentForm) {
  5792.     if (HTFormDisabled)
  5793.         HTCurrentForm->disabled = TRUE;
  5794.     HTCurrentForm->accept_cs = HTFormAcceptCharset;
  5795.     HTFormAcceptCharset = NULL;
  5796.     if (!text->forms)
  5797.         text->forms = HTList_new();
  5798.     HTList_appendObject(text->forms, HTCurrentForm);
  5799.     HTCurrentForm = NULL;
  5800.     } else {
  5801.     CTRACE(tfp, "endForm:    HTCurrentForm is missing!\n");
  5802.     }
  5803.  
  5804.     FREE(HTCurSelectGroup);
  5805.     FREE(HTCurSelectGroupSize);
  5806.     FREE(HTCurSelectedOptionValue);
  5807.     FREE(HTFormAction);
  5808.     FREE(HTFormEnctype);
  5809.     FREE(HTFormTitle);
  5810.     FREE(HTFormAcceptCharset);
  5811.     HTFormFields = 0;
  5812.     HTFormDisabled = FALSE;
  5813. }
  5814.  
  5815. PUBLIC void HText_beginSelect ARGS4(
  5816.     char *,        name,
  5817.     int,        name_cs,
  5818.     BOOLEAN,    multiple,
  5819.     char *,        size)
  5820. {
  5821.    /*
  5822.     *  Save the group name.
  5823.     */
  5824.    StrAllocCopy(HTCurSelectGroup, name);
  5825.    HTCurSelectGroupCharset = name_cs;
  5826.  
  5827.    /*
  5828.     *  If multiple then all options are actually checkboxes.
  5829.     */
  5830.    if (multiple)
  5831.       HTCurSelectGroupType = F_CHECKBOX_TYPE;
  5832.    /*
  5833.     *  If not multiple then all options are radio buttons.
  5834.     */
  5835.    else
  5836.       HTCurSelectGroupType = F_RADIO_TYPE;
  5837.  
  5838.     /*
  5839.      *  Length of an option list.
  5840.      */
  5841.     StrAllocCopy(HTCurSelectGroupSize, size);
  5842.  
  5843.     CTRACE(tfp,"HText_beginSelect: name=%s type=%d size=%s\n",
  5844.            ((HTCurSelectGroup == NULL) ?
  5845.                   "<NULL>" : HTCurSelectGroup),
  5846.         HTCurSelectGroupType,
  5847.            ((HTCurSelectGroupSize == NULL) ?
  5848.                       "<NULL>" : HTCurSelectGroupSize));
  5849.     CTRACE(tfp,"HText_beginSelect: name_cs=%d \"%s\"\n",
  5850.         HTCurSelectGroupCharset,
  5851.         (HTCurSelectGroupCharset >= 0 ?
  5852.          LYCharSet_UC[HTCurSelectGroupCharset].MIMEname : "<UNKNOWN>"));
  5853. }
  5854.  
  5855. /*
  5856. **  This function returns the number of the option whose
  5857. **  value currently is being accumulated for a select
  5858. **  block. - LE && FM
  5859. */
  5860. PUBLIC int HText_getOptionNum ARGS1(
  5861.     HText *,    text)
  5862. {
  5863.     TextAnchor *a;
  5864.     OptionType *op;
  5865.     int n = 1; /* start count at 1 */
  5866.  
  5867.     if (!(text && text->last_anchor))
  5868.     return(0);
  5869.  
  5870.     a = text->last_anchor;
  5871.     if (!(a->link_type == INPUT_ANCHOR && a->input_field &&
  5872.       a->input_field->type == F_OPTION_LIST_TYPE))
  5873.     return(0);
  5874.  
  5875.     for (op = a->input_field->select_list; op; op = op->next)
  5876.     n++;
  5877.     CTRACE(tfp, "HText_getOptionNum: Got number '%d'.\n", n);
  5878.     return(n);
  5879. }
  5880.  
  5881. /*
  5882. **  This function checks for a numbered option pattern
  5883. **  as the prefix for an option value.  If present, and
  5884. **  we are in the correct keypad mode, it returns a
  5885. **  pointer to the actual value, following that prefix.
  5886. **  Otherwise, it returns the original pointer.
  5887. */
  5888. PRIVATE char * HText_skipOptionNumPrefix ARGS1(
  5889.     char *,        opname)
  5890. {
  5891.     /*
  5892.      *  Check if we are in the correct keypad mode.
  5893.      */
  5894.     if (keypad_mode == LINKS_AND_FORM_FIELDS_ARE_NUMBERED) {
  5895.     /*
  5896.      *  Skip the option number embedded in the option name so the
  5897.      *  extra chars won't mess up cgi scripts processing the value.
  5898.      *  The format is (nnn)__ where nnn is a number and there is a
  5899.      *  minimum of 5 chars (no underscores if (nnn) exceeds 5 chars).
  5900.      *  See HTML.c.  If the chars don't exactly match this format,
  5901.      *  just use all of opname.  - LE
  5902.      */
  5903.     char *cp = opname;
  5904.  
  5905.     if ((cp && *cp && *cp++ == '(') &&
  5906.         *cp && isdigit(*cp++)) {
  5907.         while (*cp && isdigit(*cp))
  5908.             ++cp;
  5909.         if (*cp && *cp++ == ')') {
  5910.         int i = (cp - opname);
  5911.  
  5912.         while (i < 5) {
  5913.             if (*cp != '_')
  5914.                 break;
  5915.             i++;
  5916.             cp++;
  5917.         }
  5918.         if (i < 5 ) {
  5919.             cp = opname;
  5920.         }
  5921.         } else {
  5922.             cp = opname;
  5923.         }
  5924.     } else {
  5925.         cp = opname;
  5926.     }
  5927.     return(cp);
  5928.     }
  5929.  
  5930.     return(opname);
  5931. }
  5932.  
  5933. /*
  5934. **  We couldn't set the value field for the previous option
  5935. **  tag so we have to do it now.  Assume that the last anchor
  5936. **  was the previous options tag.
  5937. */
  5938. PUBLIC char * HText_setLastOptionValue ARGS7(
  5939.     HText *,    text,
  5940.     char *,        value,
  5941.     char*,        submit_value,
  5942.     int,        order,
  5943.     BOOLEAN,    checked,
  5944.     int,        val_cs,
  5945.     int,        submit_val_cs)
  5946. {
  5947.     char *cp, *cp1;
  5948.     char *ret_Value = NULL;
  5949.     unsigned char *tmp = NULL;
  5950.     int number = 0, i, j;
  5951.  
  5952.     if (!(text && text->last_anchor &&
  5953.       text->last_anchor->link_type == INPUT_ANCHOR)) {
  5954.     CTRACE(tfp, "HText_setLastOptionValue: invalid call!  value:%s!\n",
  5955.             (value ? value : "<NULL>"));
  5956.     return NULL;
  5957.     }
  5958.  
  5959.     CTRACE(tfp, "Entering HText_setLastOptionValue: value:%s, checked:%s\n",
  5960.         value, (checked ? "on" : "off"));
  5961.  
  5962.     /*
  5963.      *  Strip end spaces, newline is also whitespace.
  5964.      */
  5965.     if (*value) {
  5966.     cp = &value[strlen(value)-1];
  5967.     while ((cp >= value) && (isspace((unsigned char)*cp) ||
  5968.                  IsSpecialAttrChar((unsigned char)*cp)))
  5969.         cp--;
  5970.     *(cp+1) = '\0';
  5971.     }
  5972.  
  5973.     /*
  5974.      *  Find first non space
  5975.      */
  5976.     cp = value;
  5977.     while (isspace((unsigned char)*cp) ||
  5978.        IsSpecialAttrChar((unsigned char)*cp))
  5979.     cp++;
  5980.     if (HTCurSelectGroupType == F_RADIO_TYPE &&
  5981.     LYSelectPopups &&
  5982.     keypad_mode == LINKS_AND_FORM_FIELDS_ARE_NUMBERED) {
  5983.     /*
  5984.      *  Collapse any space between the popup option
  5985.      *  prefix and actual value. - FM
  5986.      */
  5987.     if ((cp1 = HText_skipOptionNumPrefix(cp)) > cp) {
  5988.         i = 0, j = (cp1 - cp);
  5989.         while (isspace((unsigned char)cp1[i]) ||
  5990.            IsSpecialAttrChar((unsigned char)cp1[i])) {
  5991.         i++;
  5992.         }
  5993.         if (i > 0) {
  5994.         while (cp1[i] != '\0')
  5995.             cp[j++] = cp1[i++];
  5996.         cp[j] = '\0';
  5997.         }
  5998.     }
  5999.     }
  6000.  
  6001.     if (HTCurSelectGroupType == F_CHECKBOX_TYPE) {
  6002.     StrAllocCopy(text->last_anchor->input_field->value, cp);
  6003.     text->last_anchor->input_field->value_cs = val_cs;
  6004.     /*
  6005.      *  Put the text on the screen as well.
  6006.      */
  6007.     HText_appendText(text, cp);
  6008.  
  6009.     } else if (LYSelectPopups == FALSE) {
  6010.     StrAllocCopy(text->last_anchor->input_field->value,
  6011.              (submit_value ? submit_value : cp));
  6012.     text->last_anchor->input_field->value_cs = (submit_value ?
  6013.                             submit_val_cs : val_cs);
  6014.     /*
  6015.      *  Put the text on the screen as well.
  6016.      */
  6017.     HText_appendText(text, cp);
  6018.  
  6019.     } else {
  6020.     /*
  6021.      *  Create a linked list of option values.
  6022.      */
  6023.     OptionType * op_ptr = text->last_anchor->input_field->select_list;
  6024.     OptionType * new_ptr = NULL;
  6025.     BOOLEAN first_option = FALSE;
  6026.  
  6027.     /*
  6028.      *  Deal with newlines or tabs.
  6029.      */
  6030.     convert_to_spaces(value, FALSE);
  6031.  
  6032.     if (!op_ptr) {
  6033.         /*
  6034.          *  No option items yet.
  6035.          */
  6036.         if (text->last_anchor->input_field->type != F_OPTION_LIST_TYPE) {
  6037.         CTRACE(tfp, "HText_setLastOptionValue: last input_field not F_OPTION_LIST_TYPE (%d)\n",
  6038.                 F_OPTION_LIST_TYPE);
  6039.         CTRACE(tfp, "                          but %d, ignoring!\n",
  6040.                 text->last_anchor->input_field->type);
  6041.         return NULL;
  6042.         }
  6043.  
  6044.         new_ptr = text->last_anchor->input_field->select_list =
  6045.                 (OptionType *)calloc(1, sizeof(OptionType));
  6046.         if (new_ptr == NULL)
  6047.             outofmem(__FILE__, "HText_setLastOptionValue");
  6048.  
  6049.         first_option = TRUE;
  6050.     } else {
  6051.         while (op_ptr->next) {
  6052.         number++;
  6053.         op_ptr=op_ptr->next;
  6054.         }
  6055.         number++;  /* add one more */
  6056.  
  6057.         op_ptr->next = new_ptr =
  6058.                 (OptionType *)calloc(1, sizeof(OptionType));
  6059.         if (new_ptr == NULL)
  6060.             outofmem(__FILE__, "HText_setLastOptionValue");
  6061.     }
  6062.  
  6063.     new_ptr->name = NULL;
  6064.     new_ptr->cp_submit_value = NULL;
  6065.     new_ptr->next = NULL;
  6066.     for (i = 0, j = 0; cp[i]; i++) {
  6067.         if (cp[i] == HT_NON_BREAK_SPACE ||
  6068.             cp[i] == HT_EM_SPACE) {
  6069.         cp[j++] = ' ';
  6070.         } else if (cp[i] != LY_SOFT_HYPHEN &&
  6071.                !IsSpecialAttrChar((unsigned char)cp[i])) {
  6072.         cp[j++] = cp[i];
  6073.         }
  6074.     }
  6075.     cp[j] = '\0';
  6076.     if (HTCJK != NOCJK) {
  6077.         if (cp &&
  6078.             (tmp = (unsigned char *)calloc(1, strlen(cp)+1))) {
  6079.         if (kanji_code == EUC) {
  6080.             TO_EUC((unsigned char *)cp, tmp);
  6081.             val_cs = current_char_set;
  6082.         } else if (kanji_code == SJIS) {
  6083.             TO_SJIS((unsigned char *)cp, tmp);
  6084.             val_cs = current_char_set;
  6085.         } else {
  6086.             for (i = 0, j = 0; cp[i]; i++) {
  6087.                 if (cp[i] != '\033') {
  6088.                 tmp[j++] = cp[i];
  6089.             }
  6090.             }
  6091.         }
  6092.         StrAllocCopy(new_ptr->name, (CONST char *)tmp);
  6093.         FREE(tmp);
  6094.         }
  6095.     } else {
  6096.         StrAllocCopy(new_ptr->name, cp);
  6097.     }
  6098.     StrAllocCopy(new_ptr->cp_submit_value,
  6099.              (submit_value ? submit_value :
  6100.               HText_skipOptionNumPrefix(new_ptr->name)));
  6101.     new_ptr->value_cs = (submit_value ? submit_val_cs : val_cs);
  6102.  
  6103.     if (first_option) {
  6104.         StrAllocCopy(HTCurSelectedOptionValue, new_ptr->name);
  6105.         text->last_anchor->input_field->num_value = 0;
  6106.         /*
  6107.          *  If this is the first option in a popup select list,
  6108.          *  HText_beginInput may have allocated the value and
  6109.          *  cp_submit_value fields, so free them now to avoid
  6110.          *  a memory leak. - kw
  6111.          */
  6112.         FREE(text->last_anchor->input_field->value);
  6113.         FREE(text->last_anchor->input_field->cp_submit_value);
  6114.  
  6115.         text->last_anchor->input_field->value =
  6116.         text->last_anchor->input_field->select_list->name;
  6117.         text->last_anchor->input_field->orig_value =
  6118.         text->last_anchor->input_field->select_list->name;
  6119.         text->last_anchor->input_field->cp_submit_value =
  6120.         text->last_anchor->input_field->select_list->cp_submit_value;
  6121.         text->last_anchor->input_field->orig_submit_value =
  6122.         text->last_anchor->input_field->select_list->cp_submit_value;
  6123.         text->last_anchor->input_field->value_cs =
  6124.         new_ptr->value_cs;
  6125.     } else {
  6126.         int newlen = strlen(new_ptr->name);
  6127.         int curlen = strlen(HTCurSelectedOptionValue);
  6128.         /*
  6129.          *  Make the selected Option Value as long as
  6130.          *  the longest option.
  6131.          */
  6132.         if (newlen > curlen)
  6133.         StrAllocCat(HTCurSelectedOptionValue,
  6134.                 UNDERSCORES(newlen-curlen));
  6135.     }
  6136.  
  6137.     if (checked) {
  6138.         int curlen = strlen(new_ptr->name);
  6139.         int newlen = strlen(HTCurSelectedOptionValue);
  6140.         /*
  6141.          *  Set the default option as this one.
  6142.          */
  6143.         text->last_anchor->input_field->num_value = number;
  6144.         text->last_anchor->input_field->value = new_ptr->name;
  6145.         text->last_anchor->input_field->orig_value = new_ptr->name;
  6146.         text->last_anchor->input_field->cp_submit_value =
  6147.                    new_ptr->cp_submit_value;
  6148.         text->last_anchor->input_field->orig_submit_value =
  6149.                    new_ptr->cp_submit_value;
  6150.         text->last_anchor->input_field->value_cs =
  6151.         new_ptr->value_cs;
  6152.         StrAllocCopy(HTCurSelectedOptionValue, new_ptr->name);
  6153.         if (newlen > curlen)
  6154.         StrAllocCat(HTCurSelectedOptionValue,
  6155.                 UNDERSCORES(newlen-curlen));
  6156.     }
  6157.  
  6158.     /*
  6159.      *  Return the selected Option value to be sent to the screen.
  6160.      */
  6161.     if (order == LAST_ORDER) {
  6162.         /*
  6163.          *  Change the value.
  6164.          */
  6165.         text->last_anchor->input_field->size =
  6166.                 strlen(HTCurSelectedOptionValue);
  6167.         ret_Value = HTCurSelectedOptionValue;
  6168.     }
  6169.     }
  6170.  
  6171.     if (TRACE) {
  6172.     fprintf(tfp,"HText_setLastOptionValue:%s value=%s",
  6173.         (order == LAST_ORDER) ? " LAST_ORDER" : "",
  6174.         value);
  6175.     fprintf(tfp,"            val_cs=%d \"%s\"",
  6176.             val_cs,
  6177.             (val_cs >= 0 ?
  6178.              LYCharSet_UC[val_cs].MIMEname : "<UNKNOWN>"));
  6179.     if (submit_value) {
  6180.         fprintf(tfp, " (submit_val_cs %d \"%s\") submit_value%s=%s\n",
  6181.             submit_val_cs,
  6182.             (submit_val_cs >= 0 ?
  6183.              LYCharSet_UC[submit_val_cs].MIMEname : "<UNKNOWN>"),
  6184.             (HTCurSelectGroupType == F_CHECKBOX_TYPE) ?
  6185.                                           "(ignored)" : "",
  6186.             submit_value);
  6187.     }
  6188.     else {
  6189.         fprintf(tfp,"\n");
  6190.     }
  6191.     }
  6192.     return(ret_Value);
  6193. }
  6194.  
  6195. /*
  6196.  *  Assign a form input anchor.
  6197.  *  Returns the number of characters to leave
  6198.  *  blank so that the input field can fit.
  6199.  */
  6200. PUBLIC int HText_beginInput ARGS3(
  6201.     HText *,        text,
  6202.     BOOL,            underline,
  6203.     InputFieldData *,    I)
  6204. {
  6205.  
  6206.     TextAnchor * a = (TextAnchor *) calloc(1, sizeof(*a));
  6207.     FormInfo * f = (FormInfo *) calloc(1, sizeof(*f));
  6208.     CONST char *cp_option = NULL;
  6209.     char *IValue = NULL;
  6210.     unsigned char *tmp = NULL;
  6211.     int i, j;
  6212.  
  6213.     CTRACE(tfp,"Entering HText_beginInput\n");
  6214.  
  6215.     if (a == NULL || f == NULL)
  6216.     outofmem(__FILE__, "HText_beginInput");
  6217.  
  6218.     a->start = text->chars + text->last_line->size;
  6219.     a->inUnderline = underline;
  6220.     a->line_pos = text->last_line->size;
  6221.  
  6222.  
  6223.     /*
  6224.      *  If this is a radio button, or an OPTION we're converting
  6225.      *  to a radio button, and it's the first with this name, make
  6226.      *  sure it's checked by default.  Otherwise, if it's checked,
  6227.      *  uncheck the default or any preceding radio button with this
  6228.      *  name that was checked. - FM
  6229.      */
  6230.     if (I->type != NULL && !strcmp(I->type,"OPTION") &&
  6231.     HTCurSelectGroupType == F_RADIO_TYPE && LYSelectPopups == FALSE) {
  6232.     I->type = "RADIO";
  6233.     I->name = HTCurSelectGroup;
  6234.     I->name_cs = HTCurSelectGroupCharset;
  6235.     }
  6236.     if (I->name && I->type && !strcasecomp(I->type, "radio")) {
  6237.     if (!text->last_anchor) {
  6238.         I->checked = TRUE;
  6239.     } else {
  6240.         TextAnchor * b = text->first_anchor;
  6241.         int i2 = 0;
  6242.         while (b) {
  6243.             if (b->link_type == INPUT_ANCHOR &&
  6244.             b->input_field->type == F_RADIO_TYPE &&
  6245.             b->input_field->number == HTFormNumber) {
  6246.             if (!strcmp(b->input_field->name, I->name)) {
  6247.             if (I->checked && b->input_field->num_value) {
  6248.                 b->input_field->num_value = 0;
  6249.                 StrAllocCopy(b->input_field->orig_value, "0");
  6250.                 break;
  6251.             }
  6252.             i2++;
  6253.             }
  6254.         }
  6255.         if (b == text->last_anchor) {
  6256.             if (i2 == 0)
  6257.                I->checked = TRUE;
  6258.             break;
  6259.         }
  6260.         b = b->next;
  6261.         }
  6262.     }
  6263.     }
  6264.  
  6265.     a->next = 0;
  6266.     a->anchor = NULL;
  6267.     a->link_type = INPUT_ANCHOR;
  6268.     a->show_anchor = YES;
  6269.  
  6270.     a->hightext = NULL;
  6271.     a->extent = 2;
  6272.  
  6273.     a->input_field = f;
  6274.  
  6275.     f->select_list = 0;
  6276.     f->number = HTFormNumber;
  6277.     f->disabled = (HTFormDisabled ? TRUE : I->disabled);
  6278.     f->no_cache = NO;
  6279.  
  6280.     HTFormFields++;
  6281.  
  6282.  
  6283.     /*
  6284.      *  Set the no_cache flag if the METHOD is POST. - FM
  6285.      */
  6286.     if (HTFormMethod == URL_POST_METHOD)
  6287.     f->no_cache = TRUE;
  6288.  
  6289.     /*
  6290.      *  Set up VALUE.
  6291.      */
  6292.     if (I->value)
  6293.     StrAllocCopy(IValue, I->value);
  6294.     if (IValue && HTCJK != NOCJK) {
  6295.     if ((tmp = (unsigned char *)calloc(1, (strlen(IValue) + 1)))) {
  6296.         if (kanji_code == EUC) {
  6297.         TO_EUC((unsigned char *)IValue, tmp);
  6298.         I->value_cs = current_char_set;
  6299.         } else if (kanji_code == SJIS) {
  6300.         TO_SJIS((unsigned char *)IValue, tmp);
  6301.         I->value_cs = current_char_set;
  6302.         } else {
  6303.         for (i = 0, j = 0; IValue[i]; i++) {
  6304.             if (IValue[i] != '\033') {
  6305.                 tmp[j++] = IValue[i];
  6306.             }
  6307.         }
  6308.         }
  6309.         StrAllocCopy(IValue, (CONST char *)tmp);
  6310.         FREE(tmp);
  6311.     }
  6312.     }
  6313.  
  6314.     /*
  6315.      *  Special case of OPTION.
  6316.      *  Is handled above if radio type and LYSelectPopups is FALSE.
  6317.      */
  6318.     /* set the values and let the parsing below do the work */
  6319.     if (I->type != NULL && !strcmp(I->type,"OPTION")) {
  6320.     cp_option = I->type;
  6321.     if (HTCurSelectGroupType == F_RADIO_TYPE)
  6322.         I->type = "OPTION_LIST";
  6323.     else
  6324.         I->type = "CHECKBOX";
  6325.     I->name = HTCurSelectGroup;
  6326.     I->name_cs = HTCurSelectGroupCharset;
  6327.  
  6328.     /*
  6329.      *  The option's size parameter actually gives the length and not
  6330.      *    the width of the list.  Perform the conversion here
  6331.      *    and get rid of the allocated HTCurSelect....
  6332.      *  0 is ok as it means any length (arbitrary decision).
  6333.      */
  6334.     if (HTCurSelectGroupSize != NULL) {
  6335.         f->size_l = atoi(HTCurSelectGroupSize);
  6336.         FREE(HTCurSelectGroupSize);
  6337.     }
  6338.     }
  6339.  
  6340.     /*
  6341.      *  Set SIZE.
  6342.      */
  6343.     if (I->size != NULL) {
  6344.     f->size = atoi(I->size);
  6345.     /*
  6346.      *  Leave at zero for option lists.
  6347.      */
  6348.     if (f->size == 0 && cp_option == NULL) {
  6349.        f->size = 20;  /* default */
  6350.     }
  6351.     } else {
  6352.     f->size = 20;  /* default */
  6353.     }
  6354.  
  6355.     /*
  6356.      *  Set MAXLENGTH.
  6357.      */
  6358.     if (I->maxlength != NULL) {
  6359.     f->maxlength = atoi(I->maxlength);
  6360.     } else {
  6361.     f->maxlength = 0;  /* 0 means infinite */
  6362.     }
  6363.  
  6364.     /*
  6365.      *  Set CHECKED
  6366.      *  (num_value is only relevant to check and radio types).
  6367.      */
  6368.     if (I->checked == TRUE)
  6369.     f->num_value = 1;
  6370.     else
  6371.     f->num_value = 0;
  6372.  
  6373.     /*
  6374.      *  Set TYPE.
  6375.      */
  6376.     if (I->type != NULL) {
  6377.     if (!strcasecomp(I->type,"password")) {
  6378.         f->type = F_PASSWORD_TYPE;
  6379.     } else if (!strcasecomp(I->type,"checkbox")) {
  6380.         f->type = F_CHECKBOX_TYPE;
  6381.     } else if (!strcasecomp(I->type,"radio")) {
  6382.         f->type = F_RADIO_TYPE;
  6383.     } else if (!strcasecomp(I->type,"submit")) {
  6384.         f->type = F_SUBMIT_TYPE;
  6385.     } else if (!strcasecomp(I->type,"image")) {
  6386.         f->type = F_IMAGE_SUBMIT_TYPE;
  6387.     } else if (!strcasecomp(I->type,"reset")) {
  6388.         f->type = F_RESET_TYPE;
  6389.     } else if (!strcasecomp(I->type,"OPTION_LIST")) {
  6390.         f->type = F_OPTION_LIST_TYPE;
  6391.     } else if (!strcasecomp(I->type,"hidden")) {
  6392.         f->type = F_HIDDEN_TYPE;
  6393.         HTFormFields--;
  6394.         f->size = 0;
  6395.     } else if (!strcasecomp(I->type,"textarea")) {
  6396.         f->type = F_TEXTAREA_TYPE;
  6397.     } else if (!strcasecomp(I->type,"range")) {
  6398.         f->type = F_RANGE_TYPE;
  6399.     } else if (!strcasecomp(I->type,"file")) {
  6400.         f->type = F_FILE_TYPE;
  6401.     } else if (!strcasecomp(I->type,"keygen")) {
  6402.         f->type = F_KEYGEN_TYPE;
  6403.     } else {
  6404.         /*
  6405.          *  Note that TYPE="scribble" defaults to TYPE="text". - FM
  6406.          */
  6407.         f->type = F_TEXT_TYPE; /* default */
  6408.     }
  6409.     } else {
  6410.     f->type = F_TEXT_TYPE;
  6411.     }
  6412.  
  6413.     /*
  6414.      *  Set NAME.
  6415.      */
  6416.     if (I->name != NULL) {
  6417.     StrAllocCopy(f->name,I->name);
  6418.     f->name_cs = I->name_cs;
  6419.     } else {
  6420.     if (f->type == F_RESET_TYPE ||
  6421.         f->type == F_SUBMIT_TYPE ||
  6422.         f->type == F_IMAGE_SUBMIT_TYPE) {
  6423.         /*
  6424.          *  Set name to empty string.
  6425.          */
  6426.         StrAllocCopy(f->name, "");
  6427.     } else {
  6428.         /*
  6429.          *  Error!  NAME must be present.
  6430.          */
  6431.         CTRACE(tfp,
  6432.           "GridText: No name present in input field; not displaying\n");
  6433.         FREE(a);
  6434.         FREE(f);
  6435.         FREE(IValue);
  6436.         return(0);
  6437.     }
  6438.     }
  6439.  
  6440.     /*
  6441.      *  Add this anchor to the anchor list
  6442.      */
  6443.     if (text->last_anchor) {
  6444.     text->last_anchor->next = a;
  6445.     } else {
  6446.     text->first_anchor = a;
  6447.     }
  6448.  
  6449.     /*
  6450.      *  Set VALUE, if it exists.  Otherwise, if it's not
  6451.      *  an option list make it a zero-length string. - FM
  6452.      */
  6453.     if (IValue != NULL) {
  6454.     /*
  6455.      *  OPTION VALUE is not actually the value to be seen but is to
  6456.      *    be sent....
  6457.      */
  6458.     if (f->type == F_OPTION_LIST_TYPE ||
  6459.         f->type == F_CHECKBOX_TYPE) {
  6460.         /*
  6461.          *  Fill both with the value.  The f->value may be
  6462.          *  overwritten in HText_setLastOptionValue....
  6463.          */
  6464.         StrAllocCopy(f->value, IValue);
  6465.         StrAllocCopy(f->cp_submit_value, IValue);
  6466.     } else {
  6467.         StrAllocCopy(f->value, IValue);
  6468.     }
  6469.     f->value_cs = I->value_cs;
  6470.     } else if (f->type != F_OPTION_LIST_TYPE) {
  6471.     StrAllocCopy(f->value, "");
  6472.     /*
  6473.      *  May be an empty INPUT field.  The text entered will then
  6474.      *  probably be in the current display character set. - kw
  6475.      */
  6476.     f->value_cs = current_char_set;
  6477.     }
  6478.  
  6479.     /*
  6480.      *  Run checks and fill in necessary values.
  6481.      */
  6482.     if (f->type == F_RESET_TYPE) {
  6483.     if (f->value && *f->value != '\0') {
  6484.         f->size = strlen(f->value);
  6485.     } else {
  6486.         StrAllocCopy(f->value, "Reset");
  6487.         f->size = 5;
  6488.     }
  6489.     } else if (f->type == F_IMAGE_SUBMIT_TYPE ||
  6490.            f->type == F_SUBMIT_TYPE) {
  6491.     if (f->value && *f->value != '\0') {
  6492.         f->size = strlen(f->value);
  6493.     } else if (f->type == F_IMAGE_SUBMIT_TYPE) {
  6494.         StrAllocCopy(f->value, "[IMAGE]-Submit");
  6495.         f->size = 14;
  6496.     } else {
  6497.         StrAllocCopy(f->value, "Submit");
  6498.         f->size = 6;
  6499.     }
  6500.     f->submit_action = NULL;
  6501.     StrAllocCopy(f->submit_action, HTFormAction);
  6502.     if (HTFormEnctype != NULL)
  6503.         StrAllocCopy(f->submit_enctype, HTFormEnctype);
  6504.     if (HTFormTitle != NULL)
  6505.         StrAllocCopy(f->submit_title, HTFormTitle);
  6506.     f->submit_method = HTFormMethod;
  6507.  
  6508.     } else if (f->type == F_RADIO_TYPE || f->type == F_CHECKBOX_TYPE) {
  6509.     f->size=3;
  6510.     if (IValue == NULL)
  6511.        StrAllocCopy(f->value, (f->type == F_CHECKBOX_TYPE ? "on" : ""));
  6512.  
  6513.     }
  6514.     FREE(IValue);
  6515.  
  6516.     /*
  6517.      *  Set original values.
  6518.      */
  6519.     if (f->type == F_RADIO_TYPE || f->type == F_CHECKBOX_TYPE ) {
  6520.     if (f->num_value)
  6521.         StrAllocCopy(f->orig_value, "1");
  6522.     else
  6523.         StrAllocCopy(f->orig_value, "0");
  6524.     } else if (f->type == F_OPTION_LIST_TYPE) {
  6525.     f->orig_value = NULL;
  6526.     } else {
  6527.     StrAllocCopy(f->orig_value, f->value);
  6528.     }
  6529.  
  6530.     /*
  6531.      *  Store accept-charset if present, converting to lowercase
  6532.      *  and collapsing spaces. - kw
  6533.      */
  6534.     if (I->accept_cs) {
  6535.     StrAllocCopy(f->accept_cs, I->accept_cs);
  6536.     LYRemoveBlanks(f->accept_cs);
  6537.     LYLowerCase(f->accept_cs);
  6538.     }
  6539.  
  6540.     /*
  6541.      *  Add numbers to form fields if needed. - LE & FM
  6542.      */
  6543.     switch (f->type) {
  6544.     /*
  6545.      *  Do not supply number for hidden fields, nor
  6546.      *  for types that are not yet implemented.
  6547.      */
  6548.     case F_HIDDEN_TYPE:
  6549.     case F_FILE_TYPE:
  6550.     case F_RANGE_TYPE:
  6551.     case F_KEYGEN_TYPE:
  6552.         a->number = 0;
  6553.         break;
  6554.  
  6555.     default:
  6556.         if (keypad_mode == LINKS_AND_FORM_FIELDS_ARE_NUMBERED)
  6557.         a->number = ++(text->last_anchor_number);
  6558.         else
  6559.         a->number = 0;
  6560.         break;
  6561.     }
  6562.     if (keypad_mode == LINKS_AND_FORM_FIELDS_ARE_NUMBERED && a->number > 0) {
  6563.     char marker[16];
  6564.  
  6565.     if (f->type != F_OPTION_LIST_TYPE)
  6566.         /*
  6567.          *  '[' was already put out for a popup menu
  6568.          *  designator.  See HTML.c.
  6569.          */
  6570.         HText_appendCharacter(text, '[');
  6571.     sprintf(marker,"%d]", a->number);
  6572.     HText_appendText(text, marker);
  6573.     if (f->type == F_OPTION_LIST_TYPE)
  6574.         /*
  6575.          *  Add option list designation char.
  6576.          */
  6577.         HText_appendCharacter(text, '[');
  6578.     a->start = text->chars + text->last_line->size;
  6579.     a->line_pos = text->last_line->size;
  6580.     }
  6581.  
  6582.     /*
  6583.      *  Restrict SIZE to maximum allowable size.
  6584.      */
  6585.     switch (f->type) {
  6586.     int MaximumSize;
  6587.  
  6588.     case F_SUBMIT_TYPE:
  6589.     case F_IMAGE_SUBMIT_TYPE:
  6590.     case F_RESET_TYPE:
  6591.     case F_TEXT_TYPE:
  6592.     case F_TEXTAREA_TYPE:
  6593.         /*
  6594.          *  For submit and reset buttons, and for text entry
  6595.          *  fields and areas, we limit the size element to that
  6596.          *  of one line for the current style because that's
  6597.          *  the most we could highlight on overwrites, and/or
  6598.          *  handle in the line editor.  The actual values for
  6599.          *  text entry lines can be long, and will be scrolled
  6600.          *  horizontally within the editing window. - FM
  6601.          */
  6602.         MaximumSize = (LYcols - 1) -
  6603.               (int)text->style->leftIndent -
  6604.               (int)text->style->rightIndent;
  6605.  
  6606.         /*
  6607.          *  If we are numbering form links, take that into
  6608.          *  account as well. - FM
  6609.          */
  6610.         if (keypad_mode == LINKS_AND_FORM_FIELDS_ARE_NUMBERED)
  6611.             MaximumSize -= ((a->number/10) + 3);
  6612.         if (f->size > MaximumSize)
  6613.             f->size = MaximumSize;
  6614.  
  6615.         /*
  6616.          *  Save value for submit/reset buttons so they
  6617.          *  will be visible when printing the page. - LE
  6618.          */
  6619.         I->value = f->value;
  6620.         break;
  6621.  
  6622.  
  6623.     default:
  6624.         /*
  6625.          *  For all other fields we limit the size element to
  6626.          *  10 less than the screen width, because either they
  6627.          *  are types with small placeholders, and/or are a
  6628.          *  type which is handled via a popup window. - FM
  6629.          */
  6630.         if (f->size > LYcols-10)
  6631.         f->size = LYcols-10;  /* maximum */
  6632.         break;
  6633.     }
  6634.  
  6635.     /*
  6636.      *  Add this anchor to the anchor list
  6637.      */
  6638.     text->last_anchor = a;
  6639.  
  6640.     if (HTCurrentForm) {    /* should always apply! - kw */
  6641.     if (!HTCurrentForm->first_field) {
  6642.         HTCurrentForm->first_field = f;
  6643.     }
  6644.     HTCurrentForm->last_field = f;
  6645.     HTCurrentForm->nfields++; /* will count hidden fields - kw */
  6646.     /*
  6647.      *  Propagate form field's accept-charset attribute to enclosing
  6648.      *  form if the form itself didn't have an accept-charset - kw
  6649.      */
  6650.     if (f->accept_cs && !HTFormAcceptCharset) {
  6651.         StrAllocCopy(HTFormAcceptCharset, f->accept_cs);
  6652.     }
  6653.     if (!text->forms) {
  6654.         text->forms = HTList_new();
  6655.     }
  6656.     } else {
  6657.     CTRACE(tfp, "beginInput: HTCurrentForm is missing!\n");
  6658.     }
  6659.  
  6660.     CTRACE(tfp, "Input link: name=%s\nvalue=%s\nsize=%d\n",
  6661.             f->name,
  6662.             ((f->value != NULL) ? f->value : ""),
  6663.             f->size);
  6664.     CTRACE(tfp, "Input link: name_cs=%d \"%s\" (from %d \"%s\")\n",
  6665.             f->name_cs,
  6666.             (f->name_cs >= 0 ?
  6667.              LYCharSet_UC[f->name_cs].MIMEname : "<UNKNOWN>"),
  6668.             I->name_cs,
  6669.             (I->name_cs >= 0 ?
  6670.              LYCharSet_UC[I->name_cs].MIMEname : "<UNKNOWN>"));
  6671.     CTRACE(tfp, "            value_cs=%d \"%s\" (from %d \"%s\")\n",
  6672.             f->value_cs,
  6673.             (f->value_cs >= 0 ?
  6674.              LYCharSet_UC[f->value_cs].MIMEname : "<UNKNOWN>"),
  6675.             I->value_cs,
  6676.             (I->value_cs >= 0 ?
  6677.              LYCharSet_UC[I->value_cs].MIMEname : "<UNKNOWN>"));
  6678.  
  6679.     /*
  6680.      *  Return the SIZE of the input field.
  6681.      */
  6682.     return(f->size);
  6683. }
  6684.  
  6685. /*
  6686.  *  Get a translation (properly: transcoding) quality, factoring in
  6687.  *  our ability to translate (an UCTQ_t) and a possible q parameter
  6688.  *  on the given charset string, for cs_from -> givenmime.
  6689.  *  The parsed input string will be mutilated on exit(!).
  6690.  *  Note that results are not normalised to 1.0, but results from
  6691.  *  different calls of this function can be compared. - kw
  6692.  *
  6693.  *  Obsolete, it was planned to use here a quality parametr UCTQ_t,
  6694.  *  which is boolean now.
  6695.  */
  6696. PRIVATE double get_trans_q ARGS2(
  6697.     int,        cs_from,
  6698.     char *,        givenmime)
  6699. {
  6700.     double df = 1.0;
  6701.     BOOL tq;
  6702.     char *p;
  6703.     if (!givenmime || !(*givenmime))
  6704.     return 0.0;
  6705.     if ((p = strchr(givenmime,';')) != NULL) {
  6706.     *p++ = '\0';
  6707.     }
  6708.     if (!strcmp(givenmime, "*"))
  6709.     tq = UCCanTranslateFromTo(cs_from,
  6710.                   UCGetLYhndl_byMIME("utf-8"));
  6711.     else
  6712.     tq = UCCanTranslateFromTo(cs_from,
  6713.                   UCGetLYhndl_byMIME(givenmime));
  6714.     if (!tq)
  6715.     return 0.0;
  6716.     if (p && *p) {
  6717.     char *pair, *field = p, *pval, *ptok;
  6718.     /* Get all the parameters to the Charset */
  6719.     while ((pair = HTNextTok(&field, ";", "\"", NULL)) != NULL) {
  6720.         if ((ptok = HTNextTok(&pair, "= ", NULL, NULL)) != NULL &&
  6721.         (pval = HTNextField(&pair)) != NULL) {
  6722.         if (0==strcasecomp(ptok,"q")) {
  6723.             df = strtod(pval, NULL);
  6724.             break;
  6725.         }
  6726.         }
  6727.     }
  6728.     return (df * tq);
  6729.     } else {
  6730.     return tq;
  6731.     }
  6732. }
  6733.  
  6734. /*
  6735.  *  Find the best charset for submission, if we have an ACCEPT_CHARSET
  6736.  *  list.  It factors in how well we can translate (just as guess, and
  6737.  *  not a very good one..) and possible  ";q=" factors.  Yes this is
  6738.  *  more general than it needs to be here.
  6739.  *
  6740.  *  Input is cs_in and acceptstring.
  6741.  *
  6742.  *  Will return charset handle as int.
  6743.  *  best_csname will point to a newly allocated MIME string for the
  6744.  *  charset corresponding to the return value if return value >= 0.
  6745.  *  - kw
  6746.  */
  6747. PRIVATE int find_best_target_cs ARGS3(
  6748.     char **,        best_csname,
  6749.     int,        cs_from,
  6750.     CONST char *,    acceptstring)
  6751. {
  6752.     char *paccept = NULL;
  6753.     double bestq = -1.0;
  6754.     char *bestmime = NULL;
  6755.     char *field, *nextfield;
  6756.     StrAllocCopy(paccept, acceptstring);
  6757.     nextfield = paccept;
  6758.     while ((field = HTNextTok(&nextfield, ",", "\"", NULL)) != NULL) {
  6759.     double q;
  6760.     if (*field != '\0') {
  6761.         /* Get the Charset*/
  6762.         q = get_trans_q(cs_from, field);
  6763.         if (q > bestq) {
  6764.         bestq = q;
  6765.         bestmime = field;
  6766.         }
  6767.     }
  6768.     }
  6769.     if (bestmime) {
  6770.     if (!strcmp(bestmime, "*")) /* non-standard for HTML attribute.. */
  6771.         StrAllocCopy(*best_csname, "utf-8");
  6772.     else
  6773.         StrAllocCopy(*best_csname, bestmime);
  6774.     FREE(paccept);
  6775.     if (bestq > 0)
  6776.         return (UCGetLYhndl_byMIME(*best_csname));
  6777.     else
  6778.         return (-1);
  6779.     }
  6780.     FREE(paccept);
  6781.     return (-1);
  6782. }
  6783.  
  6784. PUBLIC void HText_SubmitForm ARGS4(
  6785.     FormInfo *,    submit_item,
  6786.     document *,    doc,
  6787.     char *,        link_name,
  6788.     char *,        link_value)
  6789. {
  6790.     TextAnchor *anchor_ptr;
  6791.     int form_number = submit_item->number;
  6792.     FormInfo *form_ptr;
  6793.     PerFormInfo *thisform;
  6794.     int len;
  6795.     char *query = NULL;
  6796.     char *escaped1 = NULL, *escaped2 = NULL;
  6797.     int first_one = 1;
  6798.     char *last_textarea_name = NULL;
  6799.     int textarea_lineno = 0;
  6800.     char *previous_blanks = NULL;
  6801.     BOOLEAN PlainText = FALSE;
  6802.     BOOLEAN SemiColon = FALSE;
  6803.     char *Boundary = NULL;
  6804.     char *MultipartContentType = NULL;
  6805.     int target_cs = -1;
  6806.     CONST char *out_csname;
  6807.     CONST char *target_csname = NULL;
  6808.     char *name_used = "";
  6809.     BOOL form_has_8bit = NO, form_has_special = NO;
  6810.     BOOL field_has_8bit = NO, field_has_special = NO;
  6811.     BOOL name_has_8bit = NO, name_has_special = NO;
  6812.     BOOL have_accept_cs = NO;
  6813.     BOOL success;
  6814.     BOOL had_chartrans_warning = NO;
  6815.     char *val_used = "";
  6816.     char *copied_val_used = NULL;
  6817.     char *copied_name_used = NULL;
  6818.  
  6819.     if (!HTMainText)
  6820.     return;
  6821.  
  6822.     thisform = HTList_objectAt(HTMainText->forms, form_number - 1);
  6823.     /*  Sanity check */
  6824.     if (!thisform) {
  6825.     CTRACE(tfp, "SubmitForm: form %d not in HTMainText's list!\n",
  6826.             form_number);
  6827.     } else if (thisform->number != form_number) {
  6828.     CTRACE(tfp, "SubmitForm: failed sanity check, %d!=%d !\n",
  6829.             thisform->number, form_number);
  6830.     thisform = NULL;
  6831.     }
  6832.  
  6833.     if (submit_item->submit_action) {
  6834.     /*
  6835.      *  If we're mailing, make sure it's a mailto ACTION. - FM
  6836.      */
  6837.     if ((submit_item->submit_method == URL_MAIL_METHOD) &&
  6838.         strncmp(submit_item->submit_action, "mailto:", 7)) {
  6839.         HTAlert(BAD_FORM_MAILTO);
  6840.         return;
  6841.     }
  6842.  
  6843.     /*
  6844.      *  Set length plus breathing room.
  6845.      */
  6846.     len = strlen(submit_item->submit_action) + 2048;
  6847.     } else {
  6848.     return;
  6849.     }
  6850.  
  6851.     /*
  6852.      *  Check the ENCTYPE and set up the appropriate variables. - FM
  6853.      */
  6854.     if (submit_item->submit_enctype &&
  6855.     !strncasecomp(submit_item->submit_enctype, "text/plain", 10)) {
  6856.     /*
  6857.      *  Do not hex escape, and use physical newlines
  6858.      *  to separate name=value pairs. - FM
  6859.      */
  6860.     PlainText = TRUE;
  6861.     } else if (submit_item->submit_enctype &&
  6862.            !strncasecomp(submit_item->submit_enctype,
  6863.                  "application/sgml-form-urlencoded", 32)) {
  6864.     /*
  6865.      *  Use semicolons instead of ampersands as the
  6866.      *  separators for name=value pairs. - FM
  6867.      */
  6868.     SemiColon = TRUE;
  6869.     } else if (submit_item->submit_enctype &&
  6870.            !strncasecomp(submit_item->submit_enctype,
  6871.                  "multipart/form-data", 19)) {
  6872.     /*
  6873.      *  Use the multipart MIME format.  We should generate
  6874.      *  a boundary string which we are sure doesn't occur
  6875.      *  in the content, but for now we'll just assume that
  6876.      *  this string doesn't. - FM
  6877.      */
  6878.     Boundary = "xnyLAaB03X";
  6879.     }
  6880.  
  6881.     /*
  6882.      *  Determine in what character encoding (aka. charset) we should
  6883.      *  submit.  We call this target_cs and the MIME name for it
  6884.      *  target_csname.
  6885.      *  TODO:   - actually use ACCEPT-CHARSET stuff from FORM
  6886.      *  TODO:   - deal with list in ACCEPT-CHARSET, find a "best"
  6887.      *          charset to submit
  6888.      */
  6889.  
  6890.     /* Look at ACCEPT-CHARSET on the submitting field if we have one. */
  6891.     if (thisform && submit_item->accept_cs &&
  6892.     strcasecomp(submit_item->accept_cs, "UNKNOWN")) {
  6893.     have_accept_cs = YES;
  6894.     target_cs = find_best_target_cs(&thisform->thisacceptcs,
  6895.                     current_char_set,
  6896.                     submit_item->accept_cs);
  6897.     }
  6898.     /* Look at ACCEPT-CHARSET on form as a whole if submitting field
  6899.      * didn't have one. */
  6900.     if (thisform && !have_accept_cs && thisform->accept_cs &&
  6901.     strcasecomp(thisform->accept_cs, "UNKNOWN")) {
  6902.     have_accept_cs = YES;
  6903.     target_cs = find_best_target_cs(&thisform->thisacceptcs,
  6904.                     current_char_set,
  6905.                     thisform->accept_cs);
  6906.     }
  6907.     if (have_accept_cs && (target_cs >= 0) && thisform->thisacceptcs) {
  6908.     target_csname = thisform->thisacceptcs;
  6909.     }
  6910.  
  6911.     if (target_cs < 0 &&
  6912.     HTMainText->node_anchor->charset &&
  6913.     *HTMainText->node_anchor->charset) {
  6914.     target_cs = UCGetLYhndl_byMIME(HTMainText->node_anchor->charset);
  6915.     if (target_cs >= 0) {
  6916.         target_csname = HTMainText->node_anchor->charset;
  6917.     } else {
  6918.         target_cs = UCLYhndl_for_unspec;
  6919.         if (target_cs >= 0)
  6920.         target_csname = LYCharSet_UC[target_cs].MIMEname;
  6921.     }
  6922.     }
  6923.     if (target_cs < 0) {
  6924.     target_cs = UCLYhndl_for_unspec;
  6925.     }
  6926.  
  6927.     /*
  6928.      *  Go through list of anchors and get size first.
  6929.      */
  6930.     /*
  6931.      *  also get a "max." charset parameter - kw
  6932.      */
  6933.     anchor_ptr = HTMainText->first_anchor;
  6934.     while (anchor_ptr) {
  6935.     if (anchor_ptr->link_type == INPUT_ANCHOR) {
  6936.         if (anchor_ptr->input_field->number == form_number) {
  6937.  
  6938.         char *p;
  6939.         char * val;
  6940.             form_ptr = anchor_ptr->input_field;
  6941.         val = form_ptr->cp_submit_value != NULL ?
  6942.                         form_ptr->cp_submit_value : form_ptr->value;
  6943.         field_has_8bit = NO;
  6944.         field_has_special = NO;
  6945.  
  6946.             len += (strlen(form_ptr->name) + (Boundary ? 100 : 10));
  6947.         /*
  6948.          *  Calculate by the option submit value if present.
  6949.          */
  6950.         if (form_ptr->cp_submit_value != NULL) {
  6951.             len += (strlen(form_ptr->cp_submit_value) + 10);
  6952.         } else {
  6953.                 len += (strlen(form_ptr->value) + 10);
  6954.         }
  6955.             len += 32; /* plus and ampersand + safety net */
  6956.  
  6957.         for (p = val;
  6958.              p && *p && !(field_has_8bit && field_has_special);
  6959.              p++)
  6960.             if ((*p == HT_NON_BREAK_SPACE) ||
  6961.             (*p == HT_EM_SPACE) ||
  6962.             (*p == LY_SOFT_HYPHEN)) {
  6963.             field_has_special = YES;
  6964.             } else if ((*p & 0x80) != 0) {
  6965.             field_has_8bit = YES;
  6966.             }
  6967.         for (p = form_ptr->name;
  6968.              p && *p && !(name_has_8bit && name_has_special);
  6969.              p++)
  6970.             if ((*p == HT_NON_BREAK_SPACE) ||
  6971.             (*p == HT_EM_SPACE) ||
  6972.             (*p == LY_SOFT_HYPHEN)) {
  6973.             name_has_special = YES;
  6974.             } else if ((*p & 0x80) != 0) {
  6975.             name_has_8bit = YES;
  6976.             }
  6977.  
  6978.         if (field_has_8bit || name_has_8bit)
  6979.             form_has_8bit = YES;
  6980.         if (field_has_special || name_has_special)
  6981.             form_has_special = YES;
  6982.  
  6983.         if (!field_has_8bit && !field_has_special) {
  6984.             /* already ok */
  6985.         } else if (target_cs < 0) {
  6986.             /* already confused */
  6987.         } else if (!field_has_8bit &&
  6988.             (LYCharSet_UC[target_cs].enc == UCT_ENC_8859 ||
  6989.              (LYCharSet_UC[target_cs].like8859 & UCT_R_8859SPECL))) {
  6990.             /* those specials will be trivial */
  6991.         } else if (UCNeedNotTranslate(form_ptr->value_cs, target_cs)) {
  6992.             /* already ok */
  6993.         } else if (UCCanTranslateFromTo(form_ptr->value_cs, target_cs)) {
  6994.             /* also ok */
  6995.         } else if (UCCanTranslateFromTo(target_cs, form_ptr->value_cs)) {
  6996.             target_cs = form_ptr->value_cs;    /* try this */
  6997.             target_csname = NULL; /* will be set after loop */
  6998.         } else {
  6999.             target_cs = -1; /* don't know what to do */
  7000.         }
  7001.  
  7002.         /*  Same for name */
  7003.         if (!name_has_8bit && !name_has_special) {
  7004.             /* already ok */
  7005.         } else if (target_cs < 0) {
  7006.             /* already confused */
  7007.         } else if (!name_has_8bit &&
  7008.             (LYCharSet_UC[target_cs].enc == UCT_ENC_8859 ||
  7009.              (LYCharSet_UC[target_cs].like8859 & UCT_R_8859SPECL))) {
  7010.             /* those specials will be trivial */
  7011.         } else if (UCNeedNotTranslate(form_ptr->name_cs, target_cs)) {
  7012.             /* already ok */
  7013.         } else if (UCCanTranslateFromTo(form_ptr->name_cs, target_cs)) {
  7014.             /* also ok */
  7015.         } else if (UCCanTranslateFromTo(target_cs, form_ptr->name_cs)) {
  7016.             target_cs = form_ptr->value_cs;    /* try this */
  7017.             target_csname = NULL; /* will be set after loop */
  7018.         } else {
  7019.             target_cs = -1; /* don't know what to do */
  7020.         }
  7021.  
  7022.         } else if (anchor_ptr->input_field->number > form_number) {
  7023.             break;
  7024.         }
  7025.     }
  7026.  
  7027.     if (anchor_ptr == HTMainText->last_anchor)
  7028.         break;
  7029.  
  7030.     anchor_ptr = anchor_ptr->next;
  7031.     }
  7032.  
  7033.     if (target_csname == NULL && target_cs >= 0) {
  7034.     if (form_has_8bit) {
  7035.         target_csname = LYCharSet_UC[target_cs].MIMEname;
  7036.     } else if (form_has_special) {
  7037.         target_csname = LYCharSet_UC[target_cs].MIMEname;
  7038.     } else {
  7039.         target_csname = "us-ascii";
  7040.     }
  7041.     }
  7042.     /*
  7043.      *  Get query ready.
  7044.      */
  7045.     query = (char *)calloc(1, len);
  7046.     if (query == NULL)
  7047.     outofmem(__FILE__, "HText_SubmitForm");
  7048.  
  7049.     if (submit_item->submit_method == URL_GET_METHOD && Boundary == NULL) {
  7050.     strcpy (query, submit_item->submit_action);
  7051.     /*
  7052.      *  Method is GET.  Clip out any anchor in the current URL.
  7053.      */
  7054.     strtok (query, "#");
  7055.     /*
  7056.      *  Clip out any old query in the current URL.
  7057.      */
  7058.     strtok (query, "?");
  7059.     /*
  7060.      *  Add the lead question mark for the new URL.
  7061.      */
  7062.     strcat(query,"?");
  7063.     } else {
  7064.     query[0] = '\0';
  7065.     /*
  7066.      *  We are submitting POST content to a server,
  7067.      *  so load the post_content_type element. - FM
  7068.      */
  7069.     if (SemiColon == TRUE) {
  7070.         StrAllocCopy(doc->post_content_type,
  7071.              "application/sgml-form-urlencoded");
  7072.     } else if (PlainText == TRUE) {
  7073.         StrAllocCopy(doc->post_content_type,
  7074.              "text/plain");
  7075.     } else if (Boundary != NULL) {
  7076.         StrAllocCopy(doc->post_content_type,
  7077.              "multipart/form-data; boundary=");
  7078.         StrAllocCat(doc->post_content_type, Boundary);
  7079.     } else {
  7080.         StrAllocCopy(doc->post_content_type,
  7081.              "application/x-www-form-urlencoded");
  7082.     }
  7083.  
  7084.     /*
  7085.      *  If the ENCTYPE is not multipart/form-data, append the
  7086.      *  charset we'll be sending to the post_content_type, IF
  7087.      *  (1) there was an explicit accept-charset attribute, OR
  7088.      *  (2) we have 8-bit or special chars, AND the document had
  7089.      *      an explicit (recognized and accepted) charset parameter,
  7090.      *    AND it or target_csname is different from iso-8859-1,
  7091.      *      OR
  7092.      *  (3) we have 8-bit or special chars, AND the document had
  7093.      *      no explicit (recognized and accepted) charset parameter,
  7094.      *    AND target_cs is different from the currently effective
  7095.      *    assumed charset (which should have been set by the user
  7096.      *    so that it reflects what the server is sending, if the
  7097.      *    document is rendered correctly).
  7098.      *  For multipart/form-data the equivalent will be done later,
  7099.      *  separately for each form field. - kw
  7100.      */
  7101.     if (have_accept_cs ||
  7102.         (form_has_8bit || form_has_special)) {
  7103.         if (target_cs >= 0 && target_csname) {
  7104.         if (Boundary == NULL) {
  7105.             if ((HTMainText->node_anchor->charset &&
  7106.              (strcmp(HTMainText->node_anchor->charset,
  7107.                  "iso-8859-1") ||
  7108.               strcmp(target_csname, "iso-8859-1"))) ||
  7109.             (!HTMainText->node_anchor->charset &&
  7110.              target_cs != UCLYhndl_for_unspec)) {
  7111.             StrAllocCat(doc->post_content_type, "; charset=");
  7112.             StrAllocCat(doc->post_content_type, target_csname);
  7113.             }
  7114.         }
  7115.         } else {
  7116.         had_chartrans_warning = YES;
  7117.         _user_message(
  7118.             CANNOT_TRANSCODE_FORM,
  7119.             target_csname ? target_csname : "UNKNOWN");
  7120.         sleep(AlertSecs);
  7121.         }
  7122.     }
  7123.     }
  7124.  
  7125.  
  7126.     out_csname = target_csname;
  7127.  
  7128.     /*
  7129.      *  Reset anchor->ptr.
  7130.      */
  7131.     anchor_ptr = HTMainText->first_anchor;
  7132.     /*
  7133.      *  Go through list of anchors and assemble URL query.
  7134.      */
  7135.     while (anchor_ptr) {
  7136.     if (anchor_ptr->link_type == INPUT_ANCHOR) {
  7137.         if (anchor_ptr->input_field->number == form_number) {
  7138.         char *p;
  7139.         int out_cs;
  7140.         form_ptr = anchor_ptr->input_field;
  7141.  
  7142.         if (form_ptr->type != F_TEXTAREA_TYPE)
  7143.             textarea_lineno = 0;
  7144.  
  7145.         switch(form_ptr->type) {
  7146.             case F_RESET_TYPE:
  7147.             break;
  7148.             case F_SUBMIT_TYPE:
  7149.             case F_TEXT_SUBMIT_TYPE:
  7150.             case F_IMAGE_SUBMIT_TYPE:
  7151.             if (!(form_ptr->name && *form_ptr->name != '\0' &&
  7152.               !strcmp(form_ptr->name, link_name))) {
  7153.             CTRACE(tfp,
  7154.                     "SubmitForm: skipping submit field with ");
  7155.             CTRACE(tfp, "name \"%s\" for link_name \"%s\", %s.",
  7156.                     form_ptr->name ? form_ptr->name : "???",
  7157.                     link_name ? link_name : "???",
  7158.                     (form_ptr->name && *form_ptr->name) ?
  7159.                     "not current link" : "no field name");
  7160.             break;
  7161.             }
  7162.             if (!(form_ptr->type == F_TEXT_SUBMIT_TYPE ||
  7163.             (form_ptr->value && *form_ptr->value != '\0' &&
  7164.              !strcmp(form_ptr->value, link_value)))) {
  7165.             if (TRACE) {
  7166.                 fprintf(tfp,
  7167.                     "SubmitForm: skipping submit field with ");
  7168.                 fprintf(tfp,
  7169.                     "name \"%s\" for link_name \"%s\", %s!",
  7170.                     form_ptr->name ? form_ptr->name : "???",
  7171.                     link_name ? link_name : "???",
  7172.                     "values are different");
  7173.             }
  7174.             break;
  7175.             }
  7176.             /*  fall through  */
  7177.             case F_RADIO_TYPE:
  7178.         case F_CHECKBOX_TYPE:
  7179.         case F_TEXTAREA_TYPE:
  7180.         case F_PASSWORD_TYPE:
  7181.             case F_TEXT_TYPE:
  7182.         case F_OPTION_LIST_TYPE:
  7183.         case F_HIDDEN_TYPE:
  7184.             /*
  7185.              *    Be sure to actually look at the option submit value.
  7186.              */
  7187.             if (form_ptr->cp_submit_value != NULL) {
  7188.             val_used = form_ptr->cp_submit_value;
  7189.             } else {
  7190.             val_used = form_ptr->value;
  7191.             }
  7192.  
  7193.             /*
  7194.              *  Charset-translate value now, because we need
  7195.              *  to know the charset parameter for multipart
  7196.              *  bodyparts. - kw
  7197.              */
  7198.             field_has_8bit = NO;
  7199.             field_has_special = NO;
  7200.             for (p = val_used;
  7201.              p && *p && !(field_has_8bit && field_has_special);
  7202.              p++) {
  7203.             if ((*p == HT_NON_BREAK_SPACE) ||
  7204.                 (*p == HT_EM_SPACE) ||
  7205.                 (*p == LY_SOFT_HYPHEN)) {
  7206.                 field_has_special = YES;
  7207.             } else if ((*p & 0x80) != 0) {
  7208.                 field_has_8bit = YES;
  7209.             }
  7210.             }
  7211.  
  7212.             if (field_has_8bit || field_has_special) {
  7213.             /*  We should translate back. */
  7214.             StrAllocCopy(copied_val_used, val_used);
  7215.             success = LYUCTranslateBackFormData(&copied_val_used,
  7216.                             form_ptr->value_cs,
  7217.                             target_cs, PlainText);
  7218.             CTRACE(tfp, "SubmitForm: field \"%s\" %d %s -> %d %s %s\n",
  7219.                     form_ptr->name ? form_ptr->name : "",
  7220.                     form_ptr->value_cs,
  7221.                     form_ptr->value_cs >= 0 ?
  7222.                     LYCharSet_UC[form_ptr->value_cs].MIMEname :
  7223.                                       "???",
  7224.                     target_cs,
  7225.                     target_csname ? target_csname : "???",
  7226.                     success ? "OK" : "FAILED");
  7227.             if (success) {
  7228.                 val_used = copied_val_used;
  7229.             }
  7230.             } else {  /* We can use the value directly. */
  7231.             CTRACE(tfp, "SubmitForm: field \"%s\" %d %s OK\n",
  7232.                     form_ptr->name ? form_ptr->name : "",
  7233.                     target_cs,
  7234.                     target_csname ? target_csname : "???");
  7235.             success = YES;
  7236.             }
  7237.             if (!success) {
  7238.             if (!had_chartrans_warning) {
  7239.                 had_chartrans_warning = YES;
  7240.                 _user_message(
  7241.                 CANNOT_TRANSCODE_FORM,
  7242.                 target_csname ? target_csname : "UNKNOWN");
  7243.                 sleep(AlertSecs);
  7244.             }
  7245.             out_cs = form_ptr->value_cs;
  7246.             } else {
  7247.             out_cs = target_cs;
  7248.             }
  7249.             if (out_cs >= 0)
  7250.             out_csname = LYCharSet_UC[out_cs].MIMEname;
  7251.             if (Boundary) {
  7252.             if (!success && form_ptr->value_cs < 0) {
  7253.                 /*  This is weird. */
  7254.                 StrAllocCopy(MultipartContentType,
  7255.                      "\r\nContent-Type: text/plain; charset=");
  7256.                 StrAllocCat(MultipartContentType, "UNKNOWN-8BIT");
  7257.             } else if (!success) {
  7258.                 StrAllocCopy(MultipartContentType,
  7259.                      "\r\nContent-Type: text/plain; charset=");
  7260.                 StrAllocCat(MultipartContentType, out_csname);
  7261.                 target_csname = NULL;
  7262.             } else {
  7263.                 if (!target_csname) {
  7264.                 target_csname = LYCharSet_UC[target_cs].MIMEname;
  7265.                 }
  7266.                 StrAllocCopy(MultipartContentType,
  7267.                      "\r\nContent-Type: text/plain; charset=");
  7268.                 StrAllocCat(MultipartContentType, out_csname);
  7269.             }
  7270.             }
  7271.  
  7272.             /*
  7273.              *  Charset-translate name now, because we need
  7274.              *  to know the charset parameter for multipart
  7275.              *  bodyparts. - kw
  7276.              */
  7277.             if (form_ptr->type == F_TEXTAREA_TYPE) {
  7278.             textarea_lineno++;
  7279.             if (textarea_lineno > 1 &&
  7280.                 last_textarea_name && form_ptr->name &&
  7281.                 !strcmp(last_textarea_name, form_ptr->name)) {
  7282.                 break;
  7283.             }
  7284.             }
  7285.             name_used = (form_ptr->name ?
  7286.                  form_ptr->name : "");
  7287.  
  7288.             name_has_8bit = NO;
  7289.             name_has_special = NO;
  7290.             for (p = name_used;
  7291.              p && *p && !(name_has_8bit && name_has_special);
  7292.              p++) {
  7293.             if ((*p == HT_NON_BREAK_SPACE) ||
  7294.                 (*p == HT_EM_SPACE) ||
  7295.                 (*p == LY_SOFT_HYPHEN)) {
  7296.                 name_has_special = YES;
  7297.             } else if ((*p & 0x80) != 0) {
  7298.                 name_has_8bit = YES;
  7299.             }
  7300.             }
  7301.  
  7302.             if (name_has_8bit || name_has_special) {
  7303.             /*  We should translate back. */
  7304.             StrAllocCopy(copied_name_used, name_used);
  7305.             success = LYUCTranslateBackFormData(&copied_name_used,
  7306.                             form_ptr->name_cs,
  7307.                             target_cs, PlainText);
  7308.             CTRACE(tfp, "SubmitForm: name \"%s\" %d %s -> %d %s %s\n",
  7309.                     form_ptr->name ? form_ptr->name : "",
  7310.                     form_ptr->name_cs,
  7311.                     form_ptr->name_cs >= 0 ?
  7312.                     LYCharSet_UC[form_ptr->name_cs].MIMEname :
  7313.                                       "???",
  7314.                     target_cs,
  7315.                     target_csname ? target_csname : "???",
  7316.                     success ? "OK" : "FAILED");
  7317.             if (success) {
  7318.                 name_used = copied_name_used;
  7319.             }
  7320.             if (Boundary) {
  7321.                 if (!success) {
  7322.                 StrAllocCopy(MultipartContentType, "");
  7323.                 target_csname = NULL;
  7324.                 } else {
  7325.                 if (!target_csname)
  7326.                     target_csname = LYCharSet_UC[target_cs].MIMEname;
  7327.                 }
  7328.             }
  7329.             } else {  /* We can use the name directly. */
  7330.             CTRACE(tfp, "SubmitForm: name \"%s\" %d %s OK\n",
  7331.                     form_ptr->name ? form_ptr->name : "",
  7332.                     target_cs,
  7333.                     target_csname ? target_csname : "???");
  7334.             success = YES;
  7335.             if (Boundary) {
  7336.                 StrAllocCopy(copied_name_used, name_used);
  7337.             }
  7338.             }
  7339.             if (!success) {
  7340.             if (!had_chartrans_warning) {
  7341.                 had_chartrans_warning = YES;
  7342.                 _user_message(
  7343.                 CANNOT_TRANSCODE_FORM,
  7344.                 target_csname ? target_csname : "UNKNOWN");
  7345.                 sleep(AlertSecs);
  7346.             }
  7347.             }
  7348.             if (Boundary) {
  7349.             /*
  7350.              *  According to RFC 1867, Non-ASCII field names
  7351.              *  "should be encoded according to the prescriptions
  7352.              *  of RFC 1522 [...].  I don't think RFC 1522 actually
  7353.              *  is meant to apply to parameters like this, and it
  7354.              *  is unknown whether any server would make sense of
  7355.              *  it, so for now just use some quoting/escaping and
  7356.              *  otherwise leave 8-bit values as they are.
  7357.              *  Non-ASCII characters in form field names submitted
  7358.              *  as multipart/form-data can only occur if the form
  7359.              *  provider specifically asked for it anyway. - kw
  7360.              */
  7361.             HTMake822Word(&copied_name_used);
  7362.             name_used = copied_name_used;
  7363.             }
  7364.  
  7365.             break;
  7366.         default:
  7367.             CTRACE(tfp, "SubmitForm: What type is %d?\n",
  7368.                 form_ptr->type);
  7369.         }
  7370.  
  7371.         switch(form_ptr->type) {
  7372.  
  7373.         case F_RESET_TYPE:
  7374.             break;
  7375.  
  7376.         case F_SUBMIT_TYPE:
  7377.         case F_TEXT_SUBMIT_TYPE:
  7378.         case F_IMAGE_SUBMIT_TYPE:
  7379.             /*
  7380.              *  If it has a non-zero length name (e.g., because
  7381.              *  it's IMAGE_SUBMIT_TYPE to be handled homologously
  7382.              *  to an image map, or a SUBMIT_TYPE in a set of
  7383.              *  multiple submit buttons, or a single type="text"
  7384.              *  that's been converted to a TEXT_SUBMIT_TYPE),
  7385.              *  include the name=value pair, or fake name.x=0 and
  7386.              *  name.y=0 pairs for IMAGE_SUBMIT_TYPE. - FM
  7387.              */
  7388.             if ((form_ptr->name && *form_ptr->name != '\0' &&
  7389.             !strcmp(form_ptr->name, link_name)) &&
  7390.                (form_ptr->type == F_TEXT_SUBMIT_TYPE ||
  7391.             (form_ptr->value && *form_ptr->value != '\0' &&
  7392.              !strcmp(form_ptr->value, link_value)))) {
  7393.             int cdisp_name_startpos = 0;
  7394.             if (first_one) {
  7395.                 if (Boundary) {
  7396.                 sprintf(&query[strlen(query)],
  7397.                     "--%s\r\n", Boundary);
  7398.                 }
  7399.                 first_one=FALSE;
  7400.             } else {
  7401.                 if (PlainText) {
  7402.                 strcat(query, "\n");
  7403.                 } else if (SemiColon) {
  7404.                 strcat(query, ";");
  7405.                 } else if (Boundary) {
  7406.                 sprintf(&query[strlen(query)],
  7407.                     "\r\n--%s\r\n", Boundary);
  7408.                 } else {
  7409.                 strcat(query, "&");
  7410.                 }
  7411.             }
  7412.  
  7413.             if (PlainText) {
  7414.                 StrAllocCopy(escaped1, name_used);
  7415.             } else if (Boundary) {
  7416.                 StrAllocCopy(escaped1,
  7417.                     "Content-Disposition: form-data; name=");
  7418.                 cdisp_name_startpos = strlen(escaped1);
  7419.                 StrAllocCat(escaped1, name_used);
  7420.                 if (MultipartContentType)
  7421.                 StrAllocCat(escaped1, MultipartContentType);
  7422.                 StrAllocCat(escaped1, "\r\n\r\n");
  7423.             } else {
  7424.                 escaped1 = HTEscapeSP(name_used, URL_XALPHAS);
  7425.             }
  7426.  
  7427.             if (PlainText || Boundary) {
  7428.                 StrAllocCopy(escaped2,
  7429.                      (val_used ?
  7430.                       val_used : ""));
  7431.             } else {
  7432.                 escaped2 = HTEscapeSP(val_used, URL_XALPHAS);
  7433.             }
  7434.  
  7435.             if (form_ptr->type == F_IMAGE_SUBMIT_TYPE) {
  7436.                 /*
  7437.                  *  It's a clickable image submit button.
  7438.                  *  Fake a 0,0 coordinate pair, which
  7439.                  *  typically returns the image's default. - FM
  7440.                  */
  7441.                 if (Boundary) {
  7442.                 escaped1[cdisp_name_startpos] = '\0';
  7443.                 sprintf(&query[strlen(query)],
  7444.                     "%s.x\r\n\r\n0\r\n--%s\r\n%s.y\r\n\r\n0",
  7445.                     escaped1,
  7446.                     Boundary,
  7447.                     escaped1);
  7448.                 } else {
  7449.                 sprintf(&query[strlen(query)],
  7450.                     "%s.x=0%s%s.y=0%s",
  7451.                     escaped1,
  7452.                     (PlainText ?
  7453.                           "\n" : (SemiColon ?
  7454.                                 ";" : "&")),
  7455.                     escaped1,
  7456.                     ((PlainText && *escaped1) ?
  7457.                                  "\n" : ""));
  7458.                 }
  7459.             } else {
  7460.                 /*
  7461.                  *  It's a standard submit button.
  7462.                  *  Use the name=value pair. = FM
  7463.                  */
  7464.                 sprintf(&query[strlen(query)],
  7465.                     "%s%s%s%s%s",
  7466.                     escaped1,
  7467.                     (Boundary ?
  7468.                        "" : "="),
  7469.                     (PlainText ?
  7470.                       "\n" : ""),
  7471.                     escaped2,
  7472.                     ((PlainText && *escaped2) ?
  7473.                              "\n" : ""));
  7474.             }
  7475.             FREE(escaped1);
  7476.             FREE(escaped2);
  7477.             }
  7478.             FREE(copied_name_used);
  7479.             FREE(copied_val_used);
  7480.             break;
  7481.  
  7482.         case F_RADIO_TYPE:
  7483.         case F_CHECKBOX_TYPE:
  7484.             /*
  7485.              *  Only add if selected.
  7486.              */
  7487.             if (form_ptr->num_value) {
  7488.             if (first_one) {
  7489.                 if (Boundary) {
  7490.                 sprintf(&query[strlen(query)],
  7491.                     "--%s\r\n", Boundary);
  7492.                 }
  7493.                 first_one=FALSE;
  7494.             } else {
  7495.                 if (PlainText) {
  7496.                 strcat(query, "\n");
  7497.                 } else if (SemiColon) {
  7498.                 strcat(query, ";");
  7499.                 } else if (Boundary) {
  7500.                 sprintf(&query[strlen(query)],
  7501.                     "\r\n--%s\r\n", Boundary);
  7502.                 } else {
  7503.                 strcat(query, "&");
  7504.                 }
  7505.             }
  7506.  
  7507.             if (PlainText) {
  7508.                 StrAllocCopy(escaped1, name_used);
  7509.             } else if (Boundary) {
  7510.                 StrAllocCopy(escaped1,
  7511.                      "Content-Disposition: form-data; name=");
  7512.                 StrAllocCat(escaped1,
  7513.                     name_used);
  7514.                 if (MultipartContentType)
  7515.                 StrAllocCat(escaped1, MultipartContentType);
  7516.                 StrAllocCat(escaped1, "\r\n\r\n");
  7517.             } else {
  7518.                 escaped1 = HTEscapeSP(name_used, URL_XALPHAS);
  7519.             }
  7520.             if (PlainText || Boundary) {
  7521.                 StrAllocCopy(escaped2,
  7522.                      (val_used ?
  7523.                       val_used : ""));
  7524.             } else {
  7525.                 escaped2 = HTEscapeSP(val_used, URL_XALPHAS);
  7526.             }
  7527.  
  7528.             sprintf(&query[strlen(query)],
  7529.                 "%s%s%s%s%s",
  7530.                 escaped1,
  7531.                 (Boundary ?
  7532.                        "" : "="),
  7533.                 (PlainText ?
  7534.                       "\n" : ""),
  7535.                 escaped2,
  7536.                 ((PlainText && *escaped2) ?
  7537.                              "\n" : ""));
  7538.             FREE(escaped1);
  7539.             FREE(escaped2);
  7540.             }
  7541.             FREE(copied_name_used);
  7542.             FREE(copied_val_used);
  7543.             break;
  7544.  
  7545.         case F_TEXTAREA_TYPE:
  7546.             if (PlainText || Boundary) {
  7547.             StrAllocCopy(escaped2,
  7548.                      (val_used ?
  7549.                       val_used : ""));
  7550.             } else {
  7551.             escaped2 = HTEscapeSP(val_used, URL_XALPHAS);
  7552.             }
  7553.  
  7554.             if (!last_textarea_name ||
  7555.             strcmp(last_textarea_name, form_ptr->name)) {
  7556.             textarea_lineno = 1;
  7557.             /*
  7558.              *  Names are different so this is the first
  7559.              *  textarea or a different one from any before
  7560.              *  it.
  7561.              */
  7562.             if (Boundary) {
  7563.                 StrAllocCopy(previous_blanks, "\r\n");
  7564.             } else {
  7565.                 FREE(previous_blanks);
  7566.             }
  7567.             if (first_one) {
  7568.                 if (Boundary) {
  7569.                 sprintf(&query[strlen(query)],
  7570.                     "--%s\r\n", Boundary);
  7571.                 }
  7572.                 first_one=FALSE;
  7573.             } else {
  7574.                 if (PlainText) {
  7575.                 strcat(query, "\n");
  7576.                 } else if (SemiColon) {
  7577.                 strcat(query, ";");
  7578.                 } else if (Boundary) {
  7579.                 sprintf(&query[strlen(query)],
  7580.                     "\r\n--%s\r\n", Boundary);
  7581.                 } else {
  7582.                 strcat(query, "&");
  7583.                 }
  7584.             }
  7585.             if (PlainText) {
  7586.                 StrAllocCopy(escaped1, name_used);
  7587.             } else if (Boundary) {
  7588.                 StrAllocCopy(escaped1,
  7589.                     "Content-Disposition: form-data; name=");
  7590.                 StrAllocCat(escaped1, name_used);
  7591.                 if (MultipartContentType)
  7592.                 StrAllocCat(escaped1, MultipartContentType);
  7593.                 StrAllocCat(escaped1, "\r\n\r\n");
  7594.             } else {
  7595.                 escaped1 = HTEscapeSP(name_used, URL_XALPHAS);
  7596.             }
  7597.             sprintf(&query[strlen(query)],
  7598.                 "%s%s%s%s%s",
  7599.                 escaped1,
  7600.                 (Boundary ?
  7601.                        "" : "="),
  7602.                 (PlainText ?
  7603.                       "\n" : ""),
  7604.                 escaped2,
  7605.                 ((PlainText && *escaped2) ?
  7606.                              "\n" : ""));
  7607.             FREE(escaped1);
  7608.             last_textarea_name = form_ptr->name;
  7609.             } else {
  7610.             /*
  7611.              *  This is a continuation of a previous textarea
  7612.              *  add %0a (\n) and the escaped string.
  7613.              */
  7614.             if (escaped2[0] != '\0') {
  7615.                 if (previous_blanks) {
  7616.                 strcat(query, previous_blanks);
  7617.                 FREE(previous_blanks);
  7618.                 }
  7619.                 if (PlainText) {
  7620.                 sprintf(&query[strlen(query)], "%s\n",
  7621.                                    escaped2);
  7622.                 } else if (Boundary) {
  7623.                 sprintf(&query[strlen(query)], "%s\r\n",
  7624.                                    escaped2);
  7625.                 } else {
  7626.                 sprintf(&query[strlen(query)], "%%0a%s",
  7627.                                    escaped2);
  7628.                 }
  7629.             } else {
  7630.                 if (PlainText) {
  7631.                 StrAllocCat(previous_blanks, "\n");
  7632.                 } else if (Boundary) {
  7633.                 StrAllocCat(previous_blanks, "\r\n");
  7634.                 } else {
  7635.                 StrAllocCat(previous_blanks, "%0a");
  7636.                 }
  7637.             }
  7638.             }
  7639.             FREE(escaped2);
  7640.             FREE(copied_val_used);
  7641.             break;
  7642.  
  7643.         case F_PASSWORD_TYPE:
  7644.         case F_TEXT_TYPE:
  7645.         case F_OPTION_LIST_TYPE:
  7646.         case F_HIDDEN_TYPE:
  7647.             if (first_one) {
  7648.             if (Boundary) {
  7649.                 sprintf(&query[strlen(query)],
  7650.                     "--%s\r\n", Boundary);
  7651.             }
  7652.             first_one=FALSE;
  7653.             } else {
  7654.             if (PlainText) {
  7655.                 strcat(query, "\n");
  7656.             } else if (SemiColon) {
  7657.                 strcat(query, ";");
  7658.             } else if (Boundary) {
  7659.                 sprintf(&query[strlen(query)],
  7660.                     "\r\n--%s\r\n", Boundary);
  7661.             } else {
  7662.                 strcat(query, "&");
  7663.             }
  7664.             }
  7665.  
  7666.             if (PlainText) {
  7667.                StrAllocCopy(escaped1, name_used);
  7668.             } else if (Boundary) {
  7669.             StrAllocCopy(escaped1,
  7670.                     "Content-Disposition: form-data; name=");
  7671.             StrAllocCat(escaped1, name_used);
  7672.             if (MultipartContentType)
  7673.                 StrAllocCat(escaped1, MultipartContentType);
  7674.             StrAllocCat(escaped1, "\r\n\r\n");
  7675.             } else {
  7676.             escaped1 = HTEscapeSP(name_used, URL_XALPHAS);
  7677.             }
  7678.  
  7679.             if (PlainText || Boundary) {
  7680.             StrAllocCopy(escaped2,
  7681.                      (val_used ?
  7682.                       val_used : ""));
  7683.             } else {
  7684.             escaped2 = HTEscapeSP(val_used, URL_XALPHAS);
  7685.             }
  7686.  
  7687.             sprintf(&query[strlen(query)],
  7688.                 "%s%s%s%s%s",
  7689.                 escaped1,
  7690.                 (Boundary ?
  7691.                    "" : "="),
  7692.                 (PlainText ?
  7693.                   "\n" : ""),
  7694.                 escaped2,
  7695.                 ((PlainText && *escaped2) ?
  7696.                          "\n" : ""));
  7697.             FREE(escaped1);
  7698.             FREE(escaped2);
  7699.             FREE(copied_name_used);
  7700.             FREE(copied_val_used);
  7701.             break;
  7702.             }
  7703.         } else if (anchor_ptr->input_field->number > form_number) {
  7704.             break;
  7705.         }
  7706.     }
  7707.  
  7708.     if (anchor_ptr == HTMainText->last_anchor)
  7709.         break;
  7710.  
  7711.     anchor_ptr = anchor_ptr->next;
  7712.     }
  7713.     FREE(copied_name_used);
  7714.     if (Boundary) {
  7715.     sprintf(&query[strlen(query)], "\r\n--%s--\r\n", Boundary);
  7716.     }
  7717.     FREE(previous_blanks);
  7718.  
  7719.     if (submit_item->submit_method == URL_MAIL_METHOD) {
  7720.     _user_message("Submitting %s", submit_item->submit_action);
  7721.     CTRACE(tfp, "\nGridText - mailto_address: %s\n",
  7722.                 (submit_item->submit_action+7));
  7723.     CTRACE(tfp, "GridText - mailto_subject: %s\n",
  7724.                 ((submit_item->submit_title &&
  7725.                   *submit_item->submit_title) ?
  7726.                   (submit_item->submit_title) :
  7727.                     (HText_getTitle() ?
  7728.                          HText_getTitle() : "")));
  7729.     CTRACE(tfp,"GridText - mailto_content: %s\n",query);
  7730.     sleep(MessageSecs);
  7731.     mailform((submit_item->submit_action+7),
  7732.          ((submit_item->submit_title &&
  7733.            *submit_item->submit_title) ?
  7734.            (submit_item->submit_title) :
  7735.                  (HText_getTitle() ?
  7736.                   HText_getTitle() : "")),
  7737.          query,
  7738.          doc->post_content_type);
  7739.     FREE(query);
  7740.     FREE(doc->post_content_type);
  7741.     return;
  7742.     } else {
  7743.     _statusline(SUBMITTING_FORM);
  7744.     }
  7745.  
  7746.     if (submit_item->submit_method == URL_POST_METHOD || Boundary) {
  7747.     StrAllocCopy(doc->post_data, query);
  7748.     CTRACE(tfp,"GridText - post_data: %s\n",doc->post_data);
  7749.     StrAllocCopy(doc->address, submit_item->submit_action);
  7750.     FREE(query);
  7751.     return;
  7752.     } else { /* GET_METHOD */
  7753.     StrAllocCopy(doc->address, query);
  7754.     FREE(doc->post_data);
  7755.     FREE(doc->post_content_type);
  7756.     FREE(query);
  7757.     return;
  7758.     }
  7759. }
  7760.  
  7761. PUBLIC void HText_DisableCurrentForm NOARGS
  7762. {
  7763.     TextAnchor * anchor_ptr;
  7764.  
  7765.     HTFormDisabled = TRUE;
  7766.     if (!HTMainText)
  7767.     return;
  7768.  
  7769.     /*
  7770.      *  Go through list of anchors and set the disabled flag.
  7771.      */
  7772.     anchor_ptr = HTMainText->first_anchor;
  7773.     while (anchor_ptr) {
  7774.     if (anchor_ptr->link_type == INPUT_ANCHOR &&
  7775.         anchor_ptr->input_field->number == HTFormNumber) {
  7776.  
  7777.         anchor_ptr->input_field->disabled = TRUE;
  7778.     }
  7779.  
  7780.     if (anchor_ptr == HTMainText->last_anchor)
  7781.         break;
  7782.  
  7783.  
  7784.     anchor_ptr = anchor_ptr->next;
  7785.     }
  7786.  
  7787.     return;
  7788. }
  7789.  
  7790. PUBLIC void HText_ResetForm ARGS1(
  7791.     FormInfo *,    form)
  7792. {
  7793.     TextAnchor * anchor_ptr;
  7794.  
  7795.     _statusline(RESETTING_FORM);
  7796.     if (!HTMainText)
  7797.     return;
  7798.  
  7799.     /*
  7800.      *  Go through list of anchors and reset values.
  7801.      */
  7802.     anchor_ptr = HTMainText->first_anchor;
  7803.     while (anchor_ptr) {
  7804.     if (anchor_ptr->link_type == INPUT_ANCHOR) {
  7805.         if (anchor_ptr->input_field->number == form->number) {
  7806.  
  7807.          if (anchor_ptr->input_field->type == F_RADIO_TYPE ||
  7808.              anchor_ptr->input_field->type == F_CHECKBOX_TYPE) {
  7809.  
  7810.             if (anchor_ptr->input_field->orig_value[0] == '0')
  7811.                 anchor_ptr->input_field->num_value = 0;
  7812.             else
  7813.                 anchor_ptr->input_field->num_value = 1;
  7814.  
  7815.          } else if (anchor_ptr->input_field->type ==
  7816.                 F_OPTION_LIST_TYPE) {
  7817.             anchor_ptr->input_field->value =
  7818.                 anchor_ptr->input_field->orig_value;
  7819.  
  7820.             anchor_ptr->input_field->cp_submit_value =
  7821.                 anchor_ptr->input_field->orig_submit_value;
  7822.  
  7823.              } else {
  7824.             StrAllocCopy(anchor_ptr->input_field->value,
  7825.                     anchor_ptr->input_field->orig_value);
  7826.          }
  7827.          } else if (anchor_ptr->input_field->number > form->number) {
  7828.          break;
  7829.          }
  7830.  
  7831.     }
  7832.  
  7833.     if (anchor_ptr == HTMainText->last_anchor)
  7834.         break;
  7835.  
  7836.  
  7837.     anchor_ptr = anchor_ptr->next;
  7838.     }
  7839. }
  7840.  
  7841. PUBLIC void HText_activateRadioButton ARGS1(
  7842.     FormInfo *,    form)
  7843. {
  7844.     TextAnchor * anchor_ptr;
  7845.     int form_number = form->number;
  7846.  
  7847.     if (!HTMainText)
  7848.     return;
  7849.     anchor_ptr = HTMainText->first_anchor;
  7850.     while (anchor_ptr) {
  7851.     if (anchor_ptr->link_type == INPUT_ANCHOR &&
  7852.         anchor_ptr->input_field->type == F_RADIO_TYPE) {
  7853.  
  7854.         if (anchor_ptr->input_field->number == form_number) {
  7855.  
  7856.             /* if it has the same name and its on */
  7857.              if (!strcmp(anchor_ptr->input_field->name, form->name) &&
  7858.              anchor_ptr->input_field->num_value) {
  7859.             anchor_ptr->input_field->num_value = 0;
  7860.             break;
  7861.              }
  7862.         } else if (anchor_ptr->input_field->number > form_number) {
  7863.                 break;
  7864.         }
  7865.  
  7866.     }
  7867.  
  7868.     if (anchor_ptr == HTMainText->last_anchor)
  7869.         break;
  7870.  
  7871.     anchor_ptr = anchor_ptr->next;
  7872.    }
  7873.  
  7874.    form->num_value = 1;
  7875. }
  7876.  
  7877. /*
  7878.  *    Purpose:    Free all currently loaded HText objects in memory.
  7879.  *    Arguments:    void
  7880.  *    Return Value:    void
  7881.  *    Remarks/Portability/Dependencies/Restrictions:
  7882.  *        Usage of this function should really be limited to program
  7883.  *            termination.
  7884.  *    Revision History:
  7885.  *        05-27-94    created Lynx 2-3-1 Garrett Arch Blythe
  7886.  */
  7887. PRIVATE void free_all_texts NOARGS
  7888. {
  7889.     HText *cur = NULL;
  7890.  
  7891.     if (!loaded_texts)
  7892.     return;
  7893.  
  7894.     /*
  7895.      *  Simply loop through the loaded texts list killing them off.
  7896.      */
  7897.     while (loaded_texts && !HTList_isEmpty(loaded_texts)) {
  7898.     if ((cur = (HText *)HTList_removeLastObject(loaded_texts)) != NULL) {
  7899.         HText_free(cur);
  7900.     }
  7901.     }
  7902.  
  7903.     /*
  7904.      *  Get rid of the text list.
  7905.      */
  7906.     if (loaded_texts) {
  7907.     HTList_delete(loaded_texts);
  7908.     }
  7909.  
  7910.     /*
  7911.      *  Insurance for bad HTML.
  7912.      */
  7913.     FREE(HTCurSelectGroup);
  7914.     FREE(HTCurSelectGroupSize);
  7915.     FREE(HTCurSelectedOptionValue);
  7916.     FREE(HTFormAction);
  7917.     FREE(HTFormEnctype);
  7918.     FREE(HTFormTitle);
  7919.     FREE(HTFormAcceptCharset);
  7920.     PerFormInfo_free(HTCurrentForm);
  7921.  
  7922.     return;
  7923. }
  7924.  
  7925. /*
  7926. **  stub_HTAnchor_address is like HTAnchor_address, but it returns the
  7927. **  parent address for child links.  This is only useful for traversal's
  7928. **  where one does not want to index a text file N times, once for each
  7929. **  of N internal links.  Since the parent link has already been taken,
  7930. **  it won't go again, hence the (incorrect) links won't cause problems.
  7931. */
  7932. PUBLIC char * stub_HTAnchor_address ARGS1(
  7933.     HTAnchor *,    me)
  7934. {
  7935.     char *addr = NULL;
  7936.     if (me)
  7937.     StrAllocCopy (addr, me->parent->address);
  7938.     return addr;
  7939. }
  7940.  
  7941. PUBLIC void HText_setToolbar ARGS1(
  7942.     HText *,    text)
  7943. {
  7944.     if (text)
  7945.     text->toolbar = TRUE;
  7946.     return;
  7947. }
  7948.  
  7949. PUBLIC BOOL HText_hasToolbar ARGS1(
  7950.     HText *,    text)
  7951. {
  7952.     return ((text && text->toolbar) ? TRUE : FALSE);
  7953. }
  7954.  
  7955. PUBLIC void HText_setNoCache ARGS1(
  7956.     HText *,    text)
  7957. {
  7958.     if (text)
  7959.     text->no_cache = TRUE;
  7960.     return;
  7961. }
  7962.  
  7963. PUBLIC BOOL HText_hasNoCacheSet ARGS1(
  7964.     HText *,    text)
  7965. {
  7966.     return ((text && text->no_cache) ? TRUE : FALSE);
  7967. }
  7968.  
  7969. PUBLIC BOOL HText_hasUTF8OutputSet ARGS1(
  7970.     HText *,    text)
  7971. {
  7972.     return ((text && text->T.output_utf8) ? TRUE : FALSE);
  7973. }
  7974.  
  7975. /*
  7976. **  Check charset and set the kcode element. - FM
  7977. **  Info on the input charset may be passed in in two forms,
  7978. **  as a string (if given explicitly) and as a pointer to
  7979. **  a LYUCcharset (from chartrans mechanism); either can be NULL.
  7980. **  For Japanese the kcode will be reset at a space or explicit
  7981. **  line or paragraph break, so what we set here may not last for
  7982. **  long.  It's potentially more important not to set HTCJK to
  7983. **  NOCJK unless we are sure. - kw
  7984. */
  7985. PUBLIC void HText_setKcode ARGS3(
  7986.     HText *,    text,
  7987.     CONST char *,    charset,
  7988.     LYUCcharset *,    p_in)
  7989. {
  7990.     if (!text)
  7991.     return;
  7992.  
  7993.     /*
  7994.     **  Check whether we have some kind of info. - kw
  7995.     */
  7996.     if (!charset && !p_in) {
  7997.     return;
  7998.     }
  7999.     /*
  8000.     **  If no explicit charset string, use the implied one. - kw
  8001.     */
  8002.     if (!charset || *charset == '\0') {
  8003.     charset = p_in->MIMEname;
  8004.     }
  8005.     /*
  8006.     **  Check whether we have a specified charset. - FM
  8007.     */
  8008.     if (!charset || *charset == '\0') {
  8009.     return;
  8010.     }
  8011.  
  8012.     /*
  8013.     **  We've included the charset, and not forced a download offer,
  8014.     **  only if the currently selected character set can handle it,
  8015.     **  so check the charset value and set the text->kcode element
  8016.     **  appropriately. - FM
  8017.     */
  8018.     if (!strcmp(charset, "shift_jis") ||
  8019.     !strcmp(charset, "x-shift-jis")) {
  8020.     text->kcode = SJIS;
  8021.     } else if ((p_in && (p_in->enc == UCT_ENC_CJK)) ||
  8022.            !strcmp(charset, "euc-jp") ||
  8023.            !strncmp(charset, "x-euc-", 6) ||
  8024.            !strcmp(charset, "iso-2022-jp") ||
  8025.            !strcmp(charset, "iso-2022-jp-2") ||
  8026.            !strcmp(charset, "euc-kr") ||
  8027.            !strcmp(charset, "iso-2022-kr") ||
  8028.            !strcmp(charset, "big5") ||
  8029.            !strcmp(charset, "cn-big5") ||
  8030.            !strcmp(charset, "euc-cn") ||
  8031.            !strcmp(charset, "gb2312") ||
  8032.            !strncmp(charset, "cn-gb", 5) ||
  8033.            !strcmp(charset, "iso-2022-cn")) {
  8034.     text->kcode = EUC;
  8035.     } else {
  8036.     /*
  8037.     **  If we get to here, it's not CJK, so disable that if
  8038.     **  it is enabled.  But only if we are quite sure. - FM & kw
  8039.     */
  8040.     text->kcode = NOKANJI;
  8041.     if (HTCJK != NOCJK) {
  8042.         if (!p_in || p_in->enc != UCT_ENC_CJK)
  8043.         HTCJK = NOCJK;
  8044.     }
  8045.     }
  8046.  
  8047.     return;
  8048. }
  8049.  
  8050. /*
  8051. **  Set a permissible split at the current end of the last line. - FM
  8052. */
  8053. PUBLIC void HText_setBreakPoint ARGS1(
  8054.     HText *,    text)
  8055. {
  8056.     if (!text)
  8057.     return;
  8058.  
  8059.     /*
  8060.      *  Can split here. - FM
  8061.      */
  8062.     text->permissible_split = (int)text->last_line->size;
  8063.  
  8064.     return;
  8065. }
  8066.  
  8067. /*
  8068. **  This function determines whether a document which
  8069. **  would be sought via the a URL that has a fragment
  8070. **  directive appended is otherwise identical to the
  8071. **  currently loaded document, and if so, returns
  8072. **  FALSE, so that any no_cache directives can be
  8073. **  overridden "safely", on the grounds that we are
  8074. **  simply acting on the equivalent of a paging
  8075. **  command.  Otherwise, it returns TRUE, i.e, that
  8076. **  the target document might differ from the current,
  8077. **  based on any caching directives or analyses which
  8078. **  claimed or suggested this. - FM
  8079. */
  8080. PUBLIC BOOL HText_AreDifferent ARGS2(
  8081.     HTParentAnchor *,    anchor,
  8082.     CONST char *,        full_address)
  8083. {
  8084.     HTParentAnchor *MTanc;
  8085.     char *MTaddress;
  8086.     char *MTpound;
  8087.     char *TargetPound;
  8088.  
  8089.     /*
  8090.      *  Do we have a loaded document and both
  8091.      *  arguments for this function?
  8092.      */
  8093.     if (!(HTMainText && anchor && full_address))
  8094.     return TRUE;
  8095.  
  8096.     /*
  8097.      *  Do we have both URLs?
  8098.      */
  8099.     MTanc = HTMainText->node_anchor;
  8100.     if (!(MTanc->address && anchor->address))
  8101.     return (TRUE);
  8102.  
  8103.     /*
  8104.      *  Do we have a fragment associated with the target?
  8105.      */
  8106.     if ((TargetPound = strchr(full_address, '#')) == NULL)
  8107.     return (TRUE);
  8108.  
  8109.     /*
  8110.      *  Always treat client-side image map menus
  8111.      *  as potentially stale, so we'll create a
  8112.      *  fresh menu from the LynxMaps HTList.
  8113.      */
  8114.     if (!strncasecomp(anchor->address, "LYNXIMGMAP:", 11))
  8115.     return (TRUE);
  8116.  
  8117.     /*
  8118.      *  Do the docs differ in the type of request?
  8119.      */
  8120.     if (MTanc->isHEAD != anchor->isHEAD)
  8121.     return (TRUE);
  8122.  
  8123.     /*
  8124.      *  Are the actual URLs different, after factoring
  8125.      *  out a "LYNXIMGMAP:" leader in the MainText URL
  8126.      *  and its fragment, if present?
  8127.      */
  8128.     MTaddress = (strncasecomp(MTanc->address,
  8129.                   "LYNXIMGMAP:", 11) ?
  8130.                   MTanc->address : (MTanc->address + 11));
  8131.     if ((MTpound = strchr(MTaddress, '#')) != NULL)
  8132.     *MTpound = '\0';
  8133.     if (strcmp(MTaddress, anchor->address)) {
  8134.     if (MTpound != NULL) {
  8135.         *MTpound = '#';
  8136.     }
  8137.     return(TRUE);
  8138.     }
  8139.     if (MTpound != NULL) {
  8140.     *MTpound = '#';
  8141.     }
  8142.  
  8143.     /*
  8144.      *  If the MainText is not an image map menu,
  8145.      *  do the docs have different POST contents?
  8146.      */
  8147.     if (MTaddress == MTanc->address) {
  8148.     if (MTanc->post_data) {
  8149.         if (anchor->post_data) {
  8150.         if (strcmp(MTanc->post_data, anchor->post_data)) {
  8151.             /*
  8152.              *  Both have contents, and they differ.
  8153.              */
  8154.             return(TRUE);
  8155.         }
  8156.         } else {
  8157.         /*
  8158.          *  The loaded document has content, but the
  8159.          *  target doesn't, so they're different.
  8160.          */
  8161.         return(TRUE);
  8162.         }
  8163.     } else if (anchor->post_data) {
  8164.         /*
  8165.          *  The loaded document does not have content, but
  8166.          *  the target does, so they're different.
  8167.          */
  8168.         return(TRUE);
  8169.     }
  8170.     }
  8171.  
  8172.     /*
  8173.      *  We'll assume the target is a position in the currently
  8174.      *  displayed document, and thus can ignore any header, META,
  8175.      *  or other directives not to use a cached rendition. - FM
  8176.      */
  8177.     return(FALSE);
  8178. }
  8179.