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 / LYCharUtils.c < prev    next >
C/C++ Source or Header  |  1998-05-10  |  97KB  |  3,826 lines

  1. /*
  2. **  Functions associated with LYCharSets.c and the Lynx version of HTML.c - FM
  3. **  ==========================================================================
  4. */
  5. #include <HTUtils.h>
  6. #include <tcp.h>
  7. #include <SGML.h>
  8.  
  9. #define Lynx_HTML_Handler
  10. #include <HTChunk.h>
  11. #include <HText.h>
  12. #include <HTStyle.h>
  13. #include <HTMIME.h>
  14. #include <HTML.h>
  15.  
  16. #include <HTCJK.h>
  17. #include <HTAtom.h>
  18. #include <HTMLGen.h>
  19. #include <HTParse.h>
  20. #include <UCMap.h>
  21. #include <UCDefs.h>
  22. #include <UCAux.h>
  23.  
  24. #include <LYGlobalDefs.h>
  25. #include <LYCharUtils.h>
  26. #include <LYCharSets.h>
  27.  
  28. #include <HTAlert.h>
  29. #include <HTFont.h>
  30. #include <HTForms.h>
  31. #include <HTNestedList.h>
  32. #include <GridText.h>
  33. #include <LYSignal.h>
  34. #include <LYStrings.h>
  35. #include <LYUtils.h>
  36. #include <LYMap.h>
  37. #include <LYBookmark.h>
  38. #include <LYCurses.h>
  39. #include <LYCookie.h>
  40.  
  41. #ifdef VMS
  42. #include <HTVMSUtils.h>
  43. #endif /* VMS */
  44. #ifdef DOSPATH
  45. #include <HTDOS.h>
  46. #endif
  47.  
  48. #include <LYexit.h>
  49. #include <LYLeaks.h>
  50.  
  51. #define FREE(x) if (x) {free(x); x = NULL;}
  52.  
  53. extern BOOL HTPassEightBitRaw;
  54. extern BOOL HTPassEightBitNum;
  55. extern BOOL HTPassHighCtrlRaw;
  56. extern BOOL HTPassHighCtrlNum;
  57. extern HTkcode kanji_code;
  58. extern HTCJKlang HTCJK;
  59.  
  60. /*
  61.  *  Used for nested lists. - FM
  62.  */
  63. PUBLIC int OL_CONTINUE = -29999;     /* flag for whether CONTINUE is set */
  64. PUBLIC int OL_VOID = -29998;         /* flag for whether a count is set */
  65.  
  66.  
  67. /*
  68. **  This function converts any ampersands in allocated
  69. **  strings to "&".  If isTITLE is TRUE, it also
  70. **  converts any angle-brackets to "<" or ">". - FM
  71. */
  72. PUBLIC void LYEntify ARGS2(
  73.     char **,    str,
  74.     BOOLEAN,    isTITLE)
  75. {
  76.     char *p = *str;
  77.     char *q = NULL, *cp = NULL;
  78.     int amps = 0, lts = 0, gts = 0;
  79.  
  80.     if (p == NULL || *p == '\0')
  81.     return;
  82.  
  83.     /*
  84.      *    Count the ampersands. - FM
  85.      */
  86.     while ((*p != '\0') && (q = strchr(p, '&')) != NULL) {
  87.     amps++;
  88.     p = (q + 1);
  89.     }
  90.  
  91.     /*
  92.      *    Count the left-angle-brackets, if needed. - FM
  93.      */
  94.     if (isTITLE == TRUE) {
  95.     p = *str;
  96.     while ((*p != '\0') && (q = strchr(p, '<')) != NULL) {
  97.         lts++;
  98.         p = (q + 1);
  99.     }
  100.     }
  101.  
  102.     /*
  103.      *    Count the right-angle-brackets, if needed. - FM
  104.      */
  105.     if (isTITLE == TRUE) {
  106.     p = *str;
  107.     while ((*p != '\0') && (q = strchr(p, '>')) != NULL) {
  108.         gts++;
  109.         p = (q + 1);
  110.     }
  111.     }
  112.  
  113.     /*
  114.      *    Check whether we need to convert anything. - FM
  115.      */
  116.     if (amps == 0 && lts == 0 && gts == 0)
  117.     return;
  118.  
  119.     /*
  120.      *    Allocate space and convert. - FM
  121.      */
  122.     q = (char *)calloc(1,
  123.              (strlen(*str) + (4 * amps) + (3 * lts) + (3 * gts) + 1));
  124.     if ((cp = q) == NULL)
  125.     outofmem(__FILE__, "LYEntify");
  126.     for (p = *str; *p; p++) {
  127.     if (*p == '&') {
  128.         *q++ = '&';
  129.         *q++ = 'a';
  130.         *q++ = 'm';
  131.         *q++ = 'p';
  132.         *q++ = ';';
  133.     } else if (isTITLE && *p == '<') {
  134.         *q++ = '&';
  135.         *q++ = 'l';
  136.         *q++ = 't';
  137.         *q++ = ';';
  138.     } else if (isTITLE && *p == '>') {
  139.         *q++ = '&';
  140.         *q++ = 'g';
  141.         *q++ = 't';
  142.         *q++ = ';';
  143.     } else {
  144.         *q++ = *p;
  145.     }
  146.     }
  147.     StrAllocCopy(*str, cp);
  148.     FREE(cp);
  149. }
  150.  
  151. /*
  152. **  This function trims characters <= that of a space (32),
  153. **  including HT_NON_BREAK_SPACE (1) and HT_EM_SPACE (2),
  154. **  but not ESC, from the heads of strings. - FM
  155. */
  156. PUBLIC void LYTrimHead ARGS1(
  157.     char *, str)
  158. {
  159.     int i = 0, j;
  160.  
  161.     if (!str || *str == '\0')
  162.     return;
  163.  
  164.     while (str[i] != '\0' && WHITE(str[i]) && (unsigned char)str[i] != 27)
  165.     i++;
  166.     if (i > 0) {
  167.     for (j = 0; str[i] != '\0'; i++) {
  168.         str[j++] = str[i];
  169.     }
  170.     str[j] = '\0';
  171.     }
  172. }
  173.  
  174. /*
  175. **  This function trims characters <= that of a space (32),
  176. **  including HT_NON_BREAK_SPACE (1), HT_EM_SPACE (2), and
  177. **  ESC from the tails of strings. - FM
  178. */
  179. PUBLIC void LYTrimTail ARGS1(
  180.     char *, str)
  181. {
  182.     int i;
  183.  
  184.     if (!str || *str == '\0')
  185.     return;
  186.  
  187.     i = (strlen(str) - 1);
  188.     while (i >= 0) {
  189.     if (WHITE(str[i]))
  190.         str[i] = '\0';
  191.     else
  192.         break;
  193.     i--;
  194.     }
  195. }
  196.  
  197. /*
  198. ** This function should receive a pointer to the start
  199. ** of a comment.  It returns a pointer to the end ('>')
  200. ** character of comment, or it's best guess if the comment
  201. ** is invalid. - FM
  202. */
  203. PUBLIC char *LYFindEndOfComment ARGS1(
  204.     char *, str)
  205. {
  206.     char *cp, *cp1;
  207.     enum comment_state { start1, start2, end1, end2 } state;
  208.  
  209.     if (str == NULL)
  210.     /*
  211.      *  We got NULL, so return NULL. - FM
  212.      */
  213.     return NULL;
  214.  
  215.     if (strncmp(str, "<!--", 4))
  216.     /*
  217.      *  We don't have the start of a comment, so
  218.      *  return the beginning of the string. - FM
  219.      */
  220.     return str;
  221.  
  222.     cp = (str + 4);
  223.     if (*cp =='>')
  224.     /*
  225.      * It's an invalid comment, so
  226.      * return this end character. - FM
  227.      */
  228.     return cp;
  229.  
  230.     if ((cp1 = strchr(cp, '>')) == NULL)
  231.     /*
  232.      *  We don't have an end character, so
  233.      *  return the beginning of the string. - FM
  234.      */
  235.     return str;
  236.  
  237.     if (*cp == '-')
  238.     /*
  239.      *  Ugh, it's a "decorative" series of dashes,
  240.      *  so return the next end character. - FM
  241.      */
  242.     return cp1;
  243.  
  244.     /*
  245.      *    OK, we're ready to start parsing. - FM
  246.      */
  247.     state = start2;
  248.     while (*cp != '\0') {
  249.     switch (state) {
  250.         case start1:
  251.         if (*cp == '-')
  252.             state = start2;
  253.         else
  254.             /*
  255.              *    Invalid comment, so return the first
  256.              *    '>' from the start of the string. - FM
  257.              */
  258.             return cp1;
  259.         break;
  260.  
  261.         case start2:
  262.         if (*cp == '-')
  263.             state = end1;
  264.         break;
  265.  
  266.         case end1:
  267.         if (*cp == '-')
  268.             state = end2;
  269.         else
  270.             /*
  271.              *    Invalid comment, so return the first
  272.              *    '>' from the start of the string. - FM
  273.              */
  274.             return cp1;
  275.         break;
  276.  
  277.         case end2:
  278.         if (*cp == '>')
  279.             /*
  280.              *    Valid comment, so return the end character. - FM
  281.              */
  282.             return cp;
  283.         if (*cp == '-') {
  284.             state = start1;
  285.         } else if (!(WHITE(*cp) && (unsigned char)*cp != 27)) {
  286.             /*
  287.              *    Invalid comment, so return the first
  288.              *    '>' from the start of the string. - FM
  289.              */
  290.             return cp1;
  291.          }
  292.         break;
  293.  
  294.         default:
  295.         break;
  296.     }
  297.     cp++;
  298.     }
  299.  
  300.     /*
  301.      *    Invalid comment, so return the first
  302.      *    '>' from the start of the string. - FM
  303.      */
  304.     return cp1;
  305. }
  306.  
  307. /*
  308. **  If an HREF, itself or if resolved against a base,
  309. **  represents a file URL, and the host is defaulted,
  310. **  force in "//localhost".  We need this until
  311. **  all the other Lynx code which performs security
  312. **  checks based on the "localhost" string is changed
  313. **  to assume "//localhost" when a host field is not
  314. **  present in file URLs - FM
  315. */
  316. PUBLIC void LYFillLocalFileURL ARGS2(
  317.     char **,    href,
  318.     char *,     base)
  319. {
  320.     char * temp = NULL;
  321.  
  322.     if (*href == NULL || *(*href) == '\0')
  323.     return;
  324.  
  325.     if (!strcmp(*href, "//") || !strncmp(*href, "///", 3)) {
  326.     if (base != NULL && !strncmp(base, "file:", 5)) {
  327.         StrAllocCopy(temp, "file:");
  328.         StrAllocCat(temp, *href);
  329.         StrAllocCopy(*href, temp);
  330.     }
  331.     }
  332.     if (!strncmp(*href, "file:", 5)) {
  333.     if (*(*href+5) == '\0') {
  334.         StrAllocCat(*href, "//localhost");
  335.     } else if (!strcmp(*href, "file://")) {
  336.         StrAllocCat(*href, "localhost");
  337.     } else if (!strncmp(*href, "file:///", 8)) {
  338.         StrAllocCopy(temp, (*href+7));
  339.         StrAllocCopy(*href, "file://localhost");
  340.         StrAllocCat(*href, temp);
  341.     } else if (!strncmp(*href, "file:/", 6) && *(*href+6) != '/') {
  342.         StrAllocCopy(temp, (*href+5));
  343.         StrAllocCopy(*href, "file://localhost");
  344.         StrAllocCat(*href, temp);
  345.     }
  346.     }
  347.  
  348.     /*
  349.      * No path in a file://localhost URL means a
  350.      * directory listing for the current default. - FM
  351.      */
  352.     if (!strcmp(*href, "file://localhost")) {
  353.     char *temp2;
  354. #ifdef VMS
  355.     temp2 = HTVMS_wwwName(getenv("PATH"));
  356. #else
  357.     char curdir[DIRNAMESIZE];
  358. #if HAVE_GETCWD
  359.     getcwd (curdir, DIRNAMESIZE);
  360. #else
  361.     getwd (curdir);
  362. #endif /* NO_GETCWD */
  363. #ifdef DOSPATH
  364.     temp2 = HTDOS_wwwName(curdir);
  365. #else
  366.     temp2 = curdir;
  367. #endif /* DOSPATH */
  368. #endif /* VMS */
  369.     if (temp2[0] != '/')
  370.         StrAllocCat(*href, "/");
  371.     /*
  372.      *  Check for pathological cases - current dir has chars which
  373.      *  MUST BE URL-escaped - kw
  374.      */
  375.     if (strchr(temp2, '%') != NULL || strchr(temp2, '#') != NULL) {
  376.         FREE(temp);
  377.         temp = HTEscape(temp2, URL_PATH);
  378.         StrAllocCat(*href, temp);
  379.     } else {
  380.         StrAllocCat(*href, temp2);
  381.     }
  382.     }
  383.  
  384. #ifdef VMS
  385.     /*
  386.      * On VMS, a file://localhost/ URL means
  387.      * a listing for the login directory. - FM
  388.      */
  389.     if (!strcmp(*href, "file://localhost/"))
  390.     StrAllocCat(*href, (HTVMS_wwwName((char *)Home_Dir())+1));
  391. #endif /* VMS */
  392.  
  393.     FREE(temp);
  394.     return;
  395. }
  396.  
  397. /*
  398. **  This function writes a line with a META tag to an open file,
  399. **  which will specify a charset parameter to use when the file is
  400. **  read back in.  It is meant for temporary HTML files used by the
  401. **  various special pages which may show titles of documents.  When those
  402. **  files are created, the title strings normally have been translated and
  403. **  expanded to the display character set, so we have to make sure they
  404. **  don't get translated again.
  405. **  If the user has changed the display character set during the lifetime
  406. **  of the Lynx session (or, more exactly, during the time the title
  407. **  strings to be written were generated), they may now have different
  408. **  character encodings and there is currently no way to get it all right.
  409. **  To change this, we would have to add a variable for each string which
  410. **  keeps track of its character encoding.
  411. **  But at least we can try to ensure that reading the file after future
  412. **  display character set changes will give reasonable output.
  413. **
  414. **  The META tag is not written if the display character set (passed as
  415. **  disp_chndl) already corresponds to the charset assumption that
  416. **  would be made when the file is read. - KW
  417. **
  418. **  Currently this function is used for temporary files like "Lynx Info Page"
  419. **  and for one permanent - bookmarks (so it may be a problem if you change
  420. **  the display charset later: new bookmark entries may be mistranslated).
  421. **                                 - LP
  422. */
  423. PUBLIC void LYAddMETAcharsetToFD ARGS2(
  424.     FILE *,     fd,
  425.     int,        disp_chndl)
  426. {
  427.     if (disp_chndl == -1)
  428.     /*
  429.      *  -1 means use current_char_set.
  430.      */
  431.     disp_chndl = current_char_set;
  432.  
  433.     if (fd == NULL || disp_chndl < 0)
  434.     /*
  435.      *  Should not happen.
  436.      */
  437.     return;
  438.  
  439.     if (UCLYhndl_HTFile_for_unspec == disp_chndl)
  440.     /*
  441.      *  Not need to do, so we don't.
  442.      */
  443.     return;
  444.  
  445.     if (LYCharSet_UC[disp_chndl].enc == UCT_ENC_7BIT)
  446.     /*
  447.      *  There shouldn't be any 8-bit characters in this case.
  448.      */
  449.     return;
  450.  
  451.     /*
  452.      *    In other cases we don't know because UCLYhndl_for_unspec may
  453.      *    change during the lifetime of the file (by toggling raw mode
  454.      *    or changing the display character set), so proceed.
  455.      */
  456.     fprintf(fd, "<META %s content=\"text/html;charset=%s\">\n",
  457.         "http-equiv=\"content-type\"",
  458.         LYCharSet_UC[disp_chndl].MIMEname);
  459. }
  460.  
  461. /*
  462. ** This function returns OL TYPE="A" strings in
  463. ** the range of " A." (1) to "ZZZ." (18278). - FM
  464. */
  465. PUBLIC char *LYUppercaseA_OL_String ARGS1(
  466.     int, seqnum)
  467. {
  468.     static char OLstring[8];
  469.  
  470.     if (seqnum <= 1 ) {
  471.     strcpy(OLstring, " A.");
  472.     return OLstring;
  473.     }
  474.     if (seqnum < 27) {
  475.     sprintf(OLstring, " %c.", (seqnum + 64));
  476.     return OLstring;
  477.     }
  478.     if (seqnum < 703) {
  479.     sprintf(OLstring, "%c%c.", ((seqnum-1)/26 + 64),
  480.         (seqnum - ((seqnum-1)/26)*26 + 64));
  481.     return OLstring;
  482.     }
  483.     if (seqnum < 18279) {
  484.     sprintf(OLstring, "%c%c%c.", ((seqnum-27)/676 + 64),
  485.         (((seqnum - ((seqnum-27)/676)*676)-1)/26 + 64),
  486.         (seqnum - ((seqnum-1)/26)*26 + 64));
  487.     return OLstring;
  488.     }
  489.     strcpy(OLstring, "ZZZ.");
  490.     return OLstring;
  491. }
  492.  
  493. /*
  494. ** This function returns OL TYPE="a" strings in
  495. ** the range of " a." (1) to "zzz." (18278). - FM
  496. */
  497. PUBLIC char *LYLowercaseA_OL_String ARGS1(
  498.     int, seqnum)
  499. {
  500.     static char OLstring[8];
  501.  
  502.     if (seqnum <= 1 ) {
  503.     strcpy(OLstring, " a.");
  504.     return OLstring;
  505.     }
  506.     if (seqnum < 27) {
  507.     sprintf(OLstring, " %c.", (seqnum + 96));
  508.     return OLstring;
  509.     }
  510.     if (seqnum < 703) {
  511.     sprintf(OLstring, "%c%c.", ((seqnum-1)/26 + 96),
  512.         (seqnum - ((seqnum-1)/26)*26 + 96));
  513.     return OLstring;
  514.     }
  515.     if (seqnum < 18279) {
  516.     sprintf(OLstring, "%c%c%c.", ((seqnum-27)/676 + 96),
  517.         (((seqnum - ((seqnum-27)/676)*676)-1)/26 + 96),
  518.         (seqnum - ((seqnum-1)/26)*26 + 96));
  519.     return OLstring;
  520.     }
  521.     strcpy(OLstring, "zzz.");
  522.     return OLstring;
  523. }
  524.  
  525. /*
  526. ** This function returns OL TYPE="I" strings in the
  527. ** range of " I." (1) to "MMM." (3000).- FM
  528. */
  529. PUBLIC char *LYUppercaseI_OL_String ARGS1(
  530.     int, seqnum)
  531. {
  532.     static char OLstring[8];
  533.     int Arabic = seqnum;
  534.  
  535.     if (Arabic >= 3000) {
  536.     strcpy(OLstring, "MMM.");
  537.     return OLstring;
  538.     }
  539.  
  540.     switch(Arabic) {
  541.     case 1:
  542.     strcpy(OLstring, " I.");
  543.     return OLstring;
  544.     case 5:
  545.     strcpy(OLstring, " V.");
  546.     return OLstring;
  547.     case 10:
  548.     strcpy(OLstring, " X.");
  549.     return OLstring;
  550.     case 50:
  551.     strcpy(OLstring, " L.");
  552.     return OLstring;
  553.     case 100:
  554.     strcpy(OLstring, " C.");
  555.     return OLstring;
  556.     case 500:
  557.     strcpy(OLstring, " D.");
  558.     return OLstring;
  559.     case 1000:
  560.     strcpy(OLstring, " M.");
  561.     return OLstring;
  562.     default:
  563.     OLstring[0] = '\0';
  564.     break;
  565.     }
  566.  
  567.     while (Arabic >= 1000) {
  568.     strcat(OLstring, "M");
  569.     Arabic -= 1000;
  570.     }
  571.  
  572.     if (Arabic >= 900) {
  573.     strcat(OLstring, "CM");
  574.     Arabic -= 900;
  575.     }
  576.  
  577.     if (Arabic >= 500) {
  578.     strcat(OLstring, "D");
  579.     Arabic -= 500;
  580.     while (Arabic >= 500) {
  581.         strcat(OLstring, "C");
  582.         Arabic -= 10;
  583.     }
  584.     }
  585.  
  586.     if (Arabic >= 400) {
  587.     strcat(OLstring, "CD");
  588.     Arabic -= 400;
  589.     }
  590.  
  591.     while (Arabic >= 100) {
  592.     strcat(OLstring, "C");
  593.     Arabic -= 100;
  594.     }
  595.  
  596.     if (Arabic >= 90) {
  597.     strcat(OLstring, "XC");
  598.     Arabic -= 90;
  599.     }
  600.  
  601.     if (Arabic >= 50) {
  602.     strcat(OLstring, "L");
  603.     Arabic -= 50;
  604.     while (Arabic >= 50) {
  605.         strcat(OLstring, "X");
  606.         Arabic -= 10;
  607.     }
  608.     }
  609.  
  610.     if (Arabic >= 40) {
  611.     strcat(OLstring, "XL");
  612.     Arabic -= 40;
  613.     }
  614.  
  615.     while (Arabic > 10) {
  616.     strcat(OLstring, "X");
  617.     Arabic -= 10;
  618.     }
  619.  
  620.     switch (Arabic) {
  621.     case 1:
  622.     strcat(OLstring, "I.");
  623.     break;
  624.     case 2:
  625.     strcat(OLstring, "II.");
  626.     break;
  627.     case 3:
  628.     strcat(OLstring, "III.");
  629.     break;
  630.     case 4:
  631.     strcat(OLstring, "IV.");
  632.     break;
  633.     case 5:
  634.     strcat(OLstring, "V.");
  635.     break;
  636.     case 6:
  637.     strcat(OLstring, "VI.");
  638.     break;
  639.     case 7:
  640.     strcat(OLstring, "VII.");
  641.     break;
  642.     case 8:
  643.     strcat(OLstring, "VIII.");
  644.     break;
  645.     case 9:
  646.     strcat(OLstring, "IX.");
  647.     break;
  648.     case 10:
  649.     strcat(OLstring, "X.");
  650.     break;
  651.     default:
  652.     strcat(OLstring, ".");
  653.     break;
  654.     }
  655.  
  656.     return OLstring;
  657. }
  658.  
  659. /*
  660. ** This function returns OL TYPE="i" strings in
  661. ** range of " i." (1) to "mmm." (3000).- FM
  662. */
  663. PUBLIC char *LYLowercaseI_OL_String ARGS1(
  664.     int, seqnum)
  665. {
  666.     static char OLstring[8];
  667.     int Arabic = seqnum;
  668.  
  669.     if (Arabic >= 3000) {
  670.     strcpy(OLstring, "mmm.");
  671.     return OLstring;
  672.     }
  673.  
  674.     switch(Arabic) {
  675.     case 1:
  676.     strcpy(OLstring, " i.");
  677.     return OLstring;
  678.     case 5:
  679.     strcpy(OLstring, " v.");
  680.     return OLstring;
  681.     case 10:
  682.     strcpy(OLstring, " x.");
  683.     return OLstring;
  684.     case 50:
  685.     strcpy(OLstring, " l.");
  686.     return OLstring;
  687.     case 100:
  688.     strcpy(OLstring, " c.");
  689.     return OLstring;
  690.     case 500:
  691.     strcpy(OLstring, " d.");
  692.     return OLstring;
  693.     case 1000:
  694.     strcpy(OLstring, " m.");
  695.     return OLstring;
  696.     default:
  697.     OLstring[0] = '\0';
  698.     break;
  699.     }
  700.  
  701.     while (Arabic >= 1000) {
  702.     strcat(OLstring, "m");
  703.     Arabic -= 1000;
  704.     }
  705.  
  706.     if (Arabic >= 900) {
  707.     strcat(OLstring, "cm");
  708.     Arabic -= 900;
  709.     }
  710.  
  711.     if (Arabic >= 500) {
  712.     strcat(OLstring, "d");
  713.     Arabic -= 500;
  714.     while (Arabic >= 500) {
  715.         strcat(OLstring, "c");
  716.         Arabic -= 10;
  717.     }
  718.     }
  719.  
  720.     if (Arabic >= 400) {
  721.     strcat(OLstring, "cd");
  722.     Arabic -= 400;
  723.     }
  724.  
  725.     while (Arabic >= 100) {
  726.     strcat(OLstring, "c");
  727.     Arabic -= 100;
  728.     }
  729.  
  730.     if (Arabic >= 90) {
  731.     strcat(OLstring, "xc");
  732.     Arabic -= 90;
  733.     }
  734.  
  735.     if (Arabic >= 50) {
  736.     strcat(OLstring, "l");
  737.     Arabic -= 50;
  738.     while (Arabic >= 50) {
  739.         strcat(OLstring, "x");
  740.         Arabic -= 10;
  741.     }
  742.     }
  743.  
  744.     if (Arabic >= 40) {
  745.     strcat(OLstring, "xl");
  746.     Arabic -= 40;
  747.     }
  748.  
  749.     while (Arabic > 10) {
  750.     strcat(OLstring, "x");
  751.     Arabic -= 10;
  752.     }
  753.  
  754.     switch (Arabic) {
  755.     case 1:
  756.     strcat(OLstring, "i.");
  757.     break;
  758.     case 2:
  759.     strcat(OLstring, "ii.");
  760.     break;
  761.     case 3:
  762.     strcat(OLstring, "iii.");
  763.     break;
  764.     case 4:
  765.     strcat(OLstring, "iv.");
  766.     break;
  767.     case 5:
  768.     strcat(OLstring, "v.");
  769.     break;
  770.     case 6:
  771.     strcat(OLstring, "vi.");
  772.     break;
  773.     case 7:
  774.     strcat(OLstring, "vii.");
  775.     break;
  776.     case 8:
  777.     strcat(OLstring, "viii.");
  778.     break;
  779.     case 9:
  780.     strcat(OLstring, "ix.");
  781.     break;
  782.     case 10:
  783.     strcat(OLstring, "x.");
  784.     break;
  785.     default:
  786.     strcat(OLstring, ".");
  787.     break;
  788.     }
  789.  
  790.     return OLstring;
  791. }
  792.  
  793. /*
  794. **  This function initializes the Ordered List counter. - FM
  795. */
  796. PUBLIC void LYZero_OL_Counter ARGS1(
  797.     HTStructured *,     me)
  798. {
  799.     int i;
  800.  
  801.     if (!me)
  802.     return;
  803.  
  804.     for (i = 0; i < 12; i++) {
  805.     me->OL_Counter[i] = OL_VOID;
  806.     me->OL_Type[i] = '1';
  807.     }
  808.  
  809.     me->Last_OL_Count = 0;
  810.     me->Last_OL_Type = '1';
  811.  
  812.     return;
  813. }
  814.  
  815. /*
  816. **  This function is used by the HTML Structured object. - KW
  817. */
  818. PUBLIC void LYGetChartransInfo ARGS1(
  819.     HTStructured *,     me)
  820. {
  821.     me->UCLYhndl = HTAnchor_getUCLYhndl(me->node_anchor,
  822.                     UCT_STAGE_STRUCTURED);
  823.     if (me->UCLYhndl < 0) {
  824.     int chndl = HTAnchor_getUCLYhndl(me->node_anchor, UCT_STAGE_HTEXT);
  825.  
  826.     if (chndl < 0) {
  827.         chndl = current_char_set;
  828.         HTAnchor_setUCInfoStage(me->node_anchor, chndl,
  829.                     UCT_STAGE_HTEXT,
  830.                     UCT_SETBY_STRUCTURED);
  831.     }
  832.     HTAnchor_setUCInfoStage(me->node_anchor, chndl,
  833.                 UCT_STAGE_STRUCTURED,
  834.                 UCT_SETBY_STRUCTURED);
  835.     me->UCLYhndl = HTAnchor_getUCLYhndl(me->node_anchor,
  836.                         UCT_STAGE_STRUCTURED);
  837.     }
  838.     me->UCI = HTAnchor_getUCInfoStage(me->node_anchor,
  839.                       UCT_STAGE_STRUCTURED);
  840. }
  841.  
  842. #if NOTUSED_FOTEMODS
  843. /*
  844. **  This function reallocates an allocated string and converts
  845. **  characters for the current display character set.  It assumes
  846. **  that invalid control characters have been dealt with by the
  847. **  SGML (or other initial) parser of the document input stream
  848. **  (i.e. are present only if elements or global flags have been
  849. **  set to allow them), and that otherwise this is a copy of the
  850. **  string with the charset of the input stream.  It handles Lynx
  851. **  special characters based on the 'me' structure's element values
  852. **  (the me->UsePlainSpace and me->HiddenValue elements, and its
  853. **  chartrans related elements), and calls to other functions which
  854. **  return structure element values.  HTChunk functions are used to
  855. **  keep memory allocations at a minimum. - FM
  856. */
  857. PUBLIC void LYExpandString ARGS2(
  858.     HTStructured *,     me,
  859.     char **,        str)
  860. {
  861.     char *p = *str;
  862.     HTChunk *s;
  863.     BOOLEAN plain_space, hidden;
  864.     char c;
  865.     unsigned char c_unsign;
  866.     char saved_char_in = '\0';
  867.     BOOLEAN chk;
  868.     UCode_t code, uck;
  869.     char replace_buf [64];
  870.     char utf_buf[8], utf_count = 0;
  871.     char *utf_buf_p = utf_buf;
  872.     UCode_t utf_char = 0, value;
  873.     CONST char *name;
  874.     int i, j, high, low, diff = 0;
  875.  
  876.     /*
  877.     **    Don't do anything if we have no structure
  878.     **    or string, or are in CJK mode. - FM
  879.     */
  880.     if (!me || !p || *p == '\0' ||
  881.     HTCJK != NOCJK)
  882.     return;
  883.  
  884.     /*
  885.     **    Set "convenience copies" of me structure
  886.     **    elements. - FM
  887.     */
  888.     plain_space = me->UsePlainSpace;
  889.     hidden = me->HiddenValue;
  890.  
  891.     /*
  892.     **    Check for special input charsets - FM
  893.     */
  894.     if (!strcmp(me->inUCI->MIMEname, "x-transparent")) {
  895.     /*
  896.     **  Conversions not intended. - FM
  897.     */
  898.     return;
  899.     }
  900.     if (!strcmp(me->inUCI->MIMEname, "mnemonic") ||
  901.     !strcmp(me->inUCI->MIMEname, "mnemonic+ascii+0")) {
  902.     /*
  903.     **  All ASCII representations of Unicode characters,
  904.     **  and we have no reverse translation code for the
  905.     **  multibyte characters, so punt. - FM
  906.     */
  907.     return;
  908.     }
  909.     if (me->inUCLYhndl < 0 || me->outUCLYhndl < 0) {
  910.     /*
  911.     **  The chartrans procedure failed, so we don't
  912.     **  do anything, and hope for the best. - FM
  913.     */
  914.     CTRACE(tfp, "LYExpandString: Bad in (%d) or out (%d) handle(s).\n",
  915.             me->inUCLYhndl, me->outUCLYhndl);
  916.     return;
  917.     }
  918.  
  919.     /*
  920.     **    Zero the UTF-8 multibytes buffer. - FM
  921.     */
  922.     utf_buf[0] = utf_buf[6] = utf_buf[7] = '\0';
  923.  
  924.     /*
  925.     **    Set up an HTChunk for accumulating the expanded copy
  926.     **    of the string, so that allocations are done in 128
  927.     **    byte increments, only as required. - FM
  928.     */
  929.     s = HTChunkCreate(128);
  930.  
  931.     /*
  932.     **    Check each character in the original string,
  933.     **    and add the characters or substitutions to
  934.     **    our clean copy. - FM
  935.     */
  936.     for (i = 0; p[i]; i++) {
  937.     /*
  938.     **  Make sure the character is handled as Unicode
  939.     **  whenever that's appropriate.  - FM
  940.     */
  941.     c = p[i];
  942.     c_unsign = (unsigned char)c;
  943.     code = (UCode_t)c_unsign;
  944.     saved_char_in = '\0';
  945.     /*
  946.     **  Combine any UTF-8 multibytes into Unicode
  947.     **  to check for special characters. - FM
  948.     */
  949.     if (me->T.decode_utf8) {
  950.         /*
  951.         **    Our input charset is UTF-8, so check
  952.         **    for non-ASCII characters. - FM
  953.         */
  954.         if (c_unsign > 127) {
  955.         /*
  956.         **  We have an octet from a multibyte character. - FM
  957.         */
  958.         if (utf_count > 0 && (c & 0xc0) == 0x80) {
  959.             /*
  960.             **    Adjust the UCode_t value, add the octet
  961.             **    to the buffer, and decrement the byte
  962.             **    count. - FM
  963.             */
  964.             utf_char = (utf_char << 6) | (c & 0x3f);
  965.             utf_count--;
  966.             *utf_buf_p = c;
  967.             utf_buf_p++;
  968.             if (utf_count == 0) {
  969.             /*
  970.             **  We have all of the bytes, so terminate
  971.             **  the buffer and set 'code' to the UCode_t
  972.             **  value. - FM
  973.             */
  974.             *utf_buf_p = '\0';
  975.             code = utf_char;
  976.             /*
  977.             **  Set up the monobyte character
  978.             **  values or non-character flags
  979.             **  and fall through. - FM
  980.             */
  981.             if (code > 0 && code < 256) {
  982.                 c = ((char)(code & 0xff));
  983.                 c_unsign = (unsigned char)c;
  984.             }
  985.             } else {
  986.             /*
  987.             **  Get the next byte. - FM
  988.             */
  989.             continue;
  990.             }
  991.         } else {
  992.             /*
  993.             **    Start handling a new multibyte character. - FM
  994.             */
  995.             utf_buf[0] = c;
  996.             utf_buf_p = &utf_buf[1];
  997.             if ((c & 0xe0) == 0xc0) {
  998.             utf_count = 1;
  999.             utf_char = (c & 0x1f);
  1000.             } else if ((c & 0xf0) == 0xe0) {
  1001.             utf_count = 2;
  1002.             utf_char = (c & 0x0f);
  1003.             } else if ((c & 0xf8) == 0xf0) {
  1004.             utf_count = 3;
  1005.             utf_char = (c & 0x07);
  1006.             } else if ((c & 0xfc) == 0xf8) {
  1007.             utf_count = 4;
  1008.             utf_char = (c & 0x03);
  1009.             } else if ((c & 0xfe) == 0xfc) {
  1010.             utf_count = 5;
  1011.             utf_char = (c & 0x01);
  1012.             } else {
  1013.             /*
  1014.             **  We got garbage, even though it should
  1015.             **  have been filtered out by the SGML or
  1016.             **  input stream parser, so we'll ignore
  1017.             **  it. - FM
  1018.             */
  1019.             utf_count = 0;
  1020.             utf_buf[0] = '\0';
  1021.             utf_buf_p = utf_buf;
  1022.             }
  1023.             /*
  1024.             **    Get the next byte. - FM
  1025.             */
  1026.             continue;
  1027.         }
  1028.         } else if (utf_count > 0) {
  1029.         /*
  1030.         **  Got an ASCII character when expecting
  1031.         **  UTF-8 multibytes, so ignore the buffered
  1032.         **  multibyte characters and fall through with
  1033.         **  the current ASCII character. - FM
  1034.         */
  1035.         utf_count = 0;
  1036.         utf_buf[0] = '\0';
  1037.         utf_buf_p = utf_buf;
  1038.         code = (UCode_t)c_unsign;
  1039.         } else {
  1040.         /*
  1041.         **  Got a valid ASCII character, so fall
  1042.         **  through with it. - FM
  1043.         */
  1044.         code = (UCode_t)c_unsign;
  1045.         }
  1046.     }
  1047.     /*
  1048.     **  Convert characters from non-UTF-8 charsets
  1049.     **  to Unicode (if appropriate). - FM
  1050.     */
  1051.     if (!(me->T.decode_utf8 &&
  1052.           (unsigned char)p[i] > 127)) {
  1053. #ifdef NOTDEFINED
  1054.         if (me->T.strip_raw_char_in)
  1055.         saved_char_in = c;
  1056. #endif /* NOTDEFINED */
  1057.         if (me->T.trans_to_uni &&
  1058.         (code >= LYlowest_eightbit[me->inUCLYhndl] ||
  1059.          (code < 32 && code != 0 &&
  1060.           me->T.trans_C0_to_uni))) {
  1061.         /*
  1062.         **  Convert the octet to Unicode. - FM
  1063.         */
  1064.         code = (UCode_t)UCTransToUni(c, me->inUCLYhndl);
  1065.         if (code > 0) {
  1066.             saved_char_in = c;
  1067.             if (code < 256) {
  1068.             c = ((char)(code & 0xff));
  1069.             c_unsign = (unsigned char)c;
  1070.             }
  1071.         }
  1072.         } else if (code < 32 && code != 0 &&
  1073.                me->T.trans_C0_to_uni) {
  1074.         /*
  1075.         **  Quote from SGML.c:
  1076.         **    "This else if may be too ugly to keep. - KW"
  1077.         */
  1078.         if (me->T.trans_from_uni &&
  1079.             (((code = UCTransToUni(c, me->inUCLYhndl)) >= 32) ||
  1080.              (me->T.transp &&
  1081.               (code = UCTransToUni(c, me->inUCLYhndl)) > 0))) {
  1082.             saved_char_in = c;
  1083.             if (code < 256) {
  1084.             c = ((char)(code & 0xff));
  1085.             c_unsign = (unsigned char)c;
  1086.             }
  1087.         } else {
  1088.             uck = -1;
  1089.             if (me->T.transp) {
  1090.             uck = UCTransCharStr(replace_buf, 60, c,
  1091.                          me->inUCLYhndl,
  1092.                          me->inUCLYhndl, NO);
  1093.             }
  1094.             if (!me->T.transp || uck < 0) {
  1095.             uck = UCTransCharStr(replace_buf, 60, c,
  1096.                          me->inUCLYhndl,
  1097.                          me->outUCLYhndl, YES);
  1098.             }
  1099.             if (uck == 0) {
  1100.             continue;
  1101.             } else if (uck < 0) {
  1102.             utf_buf[0] = '\0';
  1103.             code = (unsigned char)c;
  1104.             } else {
  1105.             c = replace_buf[0];
  1106.             if (c && replace_buf[1]) {
  1107.                 HTChunkPuts(s, replace_buf);
  1108.                 continue;
  1109.             }
  1110.             }
  1111.             utf_buf[0] = '\0';
  1112.             code = (unsigned char)c;
  1113.         } /*  Next line end of ugly stuff for C0. - KW */
  1114.         } else {
  1115.         utf_buf[0] = '\0';
  1116.         code = (unsigned char)c;
  1117.         }
  1118.     }
  1119.     /*
  1120.     **  Ignore low ISO 646 7-bit control characters
  1121.     **  if they sneaked through (should have been
  1122.     **  filtered by the parser). - FM
  1123.     */
  1124.     if (code < 32 &&
  1125.         c != 9 && c != 10 && c != 13) {
  1126.         continue;
  1127.     }
  1128.     /*
  1129.     **  Ignore 127 if we don't have HTPassHighCtrlRaw
  1130.     **  and it sneaked through (should have been
  1131.     **  filtered by the parser). - FM
  1132.     */
  1133.     if (c == 127 &&
  1134.         !(me->T.transp ||
  1135.           code >= LYlowest_eightbit[me->inUCLYhndl])) {
  1136.         continue;
  1137.     }
  1138.     /*
  1139.     **  Ignore 8-bit control characters 128 - 159 if we don't
  1140.     **  have HTPassHighCtrlRaw set and they sneaked through
  1141.     **  (should have been filtered by the parser). - FM
  1142.     */
  1143.     if (code > 127 && code < 160 &&
  1144.         !(me->T.transp ||
  1145.           code >= LYlowest_eightbit[me->inUCLYhndl])) {
  1146.         continue;
  1147.     }
  1148.     /*
  1149.     **  For 160 (nbsp), substitute Lynx special character
  1150.     **  (or a space if plain_space or hidden is set) if
  1151.     **  HTPassHighCtrlRaw is not set. - FM
  1152.     */
  1153.     if (code == 160) {
  1154.         if (!me->T.pass_160_173_raw) {
  1155.         if (plain_space || hidden) {
  1156.             HTChunkPutc(s, ' ');
  1157.         } else {
  1158.             HTChunkPutc(s, HT_NON_BREAK_SPACE);
  1159.         }
  1160.         } else if (!me->T.output_utf8) {
  1161.         HTChunkPutc(s, ((char)(code & 0xff)));
  1162.         } else if (me->T.decode_utf8 && *utf_buf) {
  1163.         HTChunkPuts(s, utf_buf);
  1164.         utf_buf[0] == '\0';
  1165.         utf_buf_p = utf_buf;
  1166.         } else {
  1167.         HTChunkPutUtf8Char(s, code);
  1168.         }
  1169.         continue;
  1170.     }
  1171.     /*
  1172.     **  For 173 (shy), substitute Lynx special character
  1173.     **  (or skip it if plain_space or hidden is set) if
  1174.     **  HTPassHighCtrlRaw is not set. - FM
  1175.     */
  1176.     if (code == 173) {
  1177.         if (!me->T.pass_160_173_raw) {
  1178.         if (!(plain_space || hidden)) {
  1179.             HTChunkPutc(s, LY_SOFT_HYPHEN);
  1180.         }
  1181.         } else if (!me->T.output_utf8) {
  1182.         HTChunkPutc(s, ((char)(code & 0xff)));
  1183.         } else if (me->T.decode_utf8 && *utf_buf) {
  1184.         HTChunkPuts(s, utf_buf);
  1185.         utf_buf[0] == '\0';
  1186.         utf_buf_p = utf_buf;
  1187.         } else {
  1188.         HTChunkPutUtf8Char(s, code);
  1189.         }
  1190.         continue;
  1191.     }
  1192.     /*
  1193.     **  For 8194 (ensp), 8195 (emsp), or 8201 (thinsp), use
  1194.     **  an ASCII space (32) if plain_space or hidden is TRUE,
  1195.     **  otherwise use the Lynx special character. - FM
  1196.     */
  1197.     if (code == 8194 || code == 8195 || code == 8201) {
  1198.         if (plain_space || hidden) {
  1199.         HTChunkPutc(s, ' ');
  1200.         } else {
  1201.         HTChunkPutc(s, HT_EM_SPACE);
  1202.         }
  1203.         if (me->T.decode_utf8 && *utf_buf) {
  1204.         utf_buf[0] == '\0';
  1205.         utf_buf_p = utf_buf;
  1206.         }
  1207.         continue;
  1208.     }
  1209.     /*
  1210.     **  If we want the raw character, pass it now. - FM
  1211.     */
  1212.     if (me->T.use_raw_char_in && saved_char_in) {
  1213.         HTChunkPutc(s, saved_char_in);
  1214.         continue;
  1215.     }
  1216.     /*
  1217.     **  Seek a translation from the chartrans tables.
  1218.     */
  1219.     if ((chk = (me->T.trans_from_uni && code >= 160)) &&
  1220.         (uck = UCTransUniChar(code, me->outUCLYhndl)) >= 32 &&
  1221.         uck < 256 &&
  1222.         (uck < 127 ||
  1223.          uck >= LYlowest_eightbit[me->outUCLYhndl])) {
  1224.         HTChunkPutc(s, ((char)(uck & 0xff)));
  1225.         continue;
  1226.     } else if (chk &&
  1227.            (uck == -4 ||
  1228.             (me->T.repl_translated_C0 &&
  1229.              uck > 0 && uck < 32)) &&
  1230.            /*
  1231.            **  Not found; look for replacement string.
  1232.            */
  1233.            (uck = UCTransUniCharStr(replace_buf,
  1234.                         60, code,
  1235.                         me->outUCLYhndl,
  1236.                         0) >= 0)) {
  1237.         /*
  1238.         **    Got a replacement string.
  1239.         */
  1240.         HTChunkPuts(s, replace_buf);
  1241.         continue;
  1242.     }
  1243.     /*
  1244.     **  If we want raw UTF-8, output that now. - FM
  1245.     */
  1246.     if (me->T.output_utf8 &&
  1247.         code > 127 && code <= 0x7fffffffL) {
  1248.         if (me->T.decode_utf8 && *utf_buf) {
  1249.         HTChunkPuts(s, utf_buf);
  1250.         utf_buf[0] == '\0';
  1251.         utf_buf_p = utf_buf;
  1252.         } else {
  1253.         HTChunkPutUtf8Char(s, code);
  1254.         }
  1255.         continue;
  1256.     }
  1257.     /*
  1258.     **  If it's any other (> 160) 8-bit character
  1259.     **  and we have not set HTPassEightBitRaw
  1260.     **  nor have the "ISO Latin 1" character set selected,
  1261.     **  back translate for our character set. - FM
  1262.     */
  1263.     if (code > 160 && code < 256 &&
  1264.          me->outUCLYhndl != 0 &&
  1265.          (!(HTPassEightBitRaw ||
  1266.         (me->T.do_8bitraw && !me->T.trans_from_uni)))) {
  1267.         value = (code - 160);
  1268.         name = HTMLGetEntityName(value);
  1269.         for (low = 0, high = HTML_dtd.number_of_entities;
  1270.          high > low;
  1271.          diff < 0 ? (low = j+1) : (high = j)) {
  1272.         /*
  1273.         **  Binary search.
  1274.         */
  1275.         j = (low + (high-low)/2);
  1276.         diff = strcmp(HTML_dtd.entity_names[j], name);
  1277.         if (diff == 0) {
  1278.             HTChunkPuts(s, LYCharSets[me->outUCLYhndl][j]);
  1279.             break;
  1280.         }
  1281.         }
  1282.         if (diff == 0) {
  1283.         continue;
  1284.         }
  1285.     }
  1286.     /*
  1287.     **  If it's ASCII at this point, use it. - FM
  1288.     */
  1289.     if (code < 127 && code > 0) {
  1290.         HTChunkPutc(s, ((char)(code & 0xff)));
  1291.         continue;
  1292.     }
  1293.     /*
  1294.     **  At this point, if we should have translated, the
  1295.     **  translation has failed.  We should have sent UTF-8
  1296.     **  output to the parser already, but what the heck,
  1297.     **  try again. - FM
  1298.     */
  1299.     if (me->T.output_utf8 && *utf_buf) {
  1300.         HTChunkPuts(s, utf_buf);
  1301.         utf_buf[0] == '\0';
  1302.         utf_buf_p = utf_buf;
  1303.         continue;
  1304.     }
  1305. #ifdef NOTDEFINED
  1306.     /*
  1307.     **  Check for a strippable koi8-r 8-bit character. - FM
  1308.     */
  1309.     if (me->T.strip_raw_char_in &&
  1310.         (unsigned char)saved_char_in >= 192 &&
  1311.         (unsigned char)saved_char_in < 255 &&
  1312.         saved_char_in) {
  1313.         /*
  1314.         **    KOI8 special: strip high bit, gives (somewhat) readable
  1315.         **    ASCII or KOI7 - it was constructed that way! - KW
  1316.         */
  1317.         HTChunkPutc(s, (saved_char_in & 0x7f));
  1318.         continue;
  1319.     }
  1320. #endif /* NOTDEFINED */
  1321.     /*
  1322.     **  Ignore 8204 (zwnj), 8205 (zwj)
  1323.     **  8206 (lrm), and 8207 (rlm),
  1324.     **  if we get to here. - FM
  1325.     */
  1326.     if (code == 8204 || code == 8205 ||
  1327.         code == 8206 || code == 8207) {
  1328.         CTRACE(tfp, "LYExpandString: Ignoring '%ld'.\n", code);
  1329.         if (me->T.decode_utf8 && *utf_buf) {
  1330.         utf_buf[0] == '\0';
  1331.         utf_buf_p = utf_buf;
  1332.         }
  1333.         continue;
  1334.     }
  1335.     /*
  1336.     **  If we don't actually want the character,
  1337.     **  make it safe and output that now. - FM
  1338.     */
  1339.     if ((c_unsign > 0 &&
  1340.          c_unsign < LYlowest_eightbit[me->outUCLYhndl]) ||
  1341.         (me->T.trans_from_uni && !HTPassEightBitRaw)) {
  1342.         /*
  1343.         **    If we do not have the "7-bit approximations" as our
  1344.         **    output character set (in which case we did it already)
  1345.         **    seek a translation for that.  Otherwise, or if the
  1346.         **    translation fails, use UHHH notation. - FM
  1347.         */
  1348.         if ((chk = (me->outUCLYhndl !=
  1349.             UCGetLYhndl_byMIME("us-ascii"))) &&
  1350.         (uck = UCTransUniChar(code,
  1351.                       UCGetLYhndl_byMIME("us-ascii")))
  1352.                       >= 32 && uck < 127) {
  1353.         /*
  1354.         **  Got an ASCII character (yippey). - FM
  1355.         */
  1356.         c = ((char)(uck & 0xff));
  1357.         HTChunkPutc(s, c);
  1358.         continue;
  1359.         } else if ((uck == -4) &&
  1360.                (uck = UCTransUniCharStr(replace_buf,
  1361.                         60, code,
  1362.                         UCGetLYhndl_byMIME("us-ascii"),
  1363.                         0) >= 0)) {
  1364.         /*
  1365.         **  Got a replacement string (yippey). - FM
  1366.         */
  1367.         HTChunkPuts(s, replace_buf);
  1368.         continue;
  1369.         } else {
  1370.         /*
  1371.         **  Out of luck, so use the UHHH notation (ugh). - FM
  1372.         */
  1373.         sprintf(replace_buf, "U%.2lX", code);
  1374.         HTChunkPuts(s, replace_buf);
  1375.         continue;
  1376.         }
  1377.     }
  1378.     /*
  1379.     **  If we get to here and have a monobyte character,
  1380.     **  pass it. - FM
  1381.     */
  1382.     if (c_unsign > 0 && c_unsign < 256) {
  1383.         HTChunkPutc(s, c);
  1384.     }
  1385.     }
  1386.  
  1387.     /*
  1388.     **    Terminate the expanded string,
  1389.     **    replace the original, and free
  1390.     **    the chunk. - FM
  1391.     */
  1392.     HTChunkTerminate(s);
  1393.     StrAllocCopy(*str, s->data);
  1394.     HTChunkFree(s);
  1395. }
  1396. #endif /* NOTUSED_FOTEMODS */
  1397.  
  1398. /*
  1399. ** Get UCS character code for one character from UTF-8 encoded string.
  1400. **
  1401. ** On entry:
  1402. **    *ppuni should point to beginning of UTF-8 encoding character
  1403. ** On exit:
  1404. **    *ppuni is advanced to point to the last byte of UTF-8 sequence,
  1405. **        if there was a valid one; otherwise unchanged.
  1406. ** returns the UCS value
  1407. ** returns negative value on error (invalid UTF-8 sequence)
  1408. */
  1409. PRIVATE UCode_t UCGetUniFromUtf8String ARGS1(char **, ppuni)
  1410. {
  1411.     UCode_t uc_out = 0;
  1412.     char * p = *ppuni;
  1413.     int utf_count, i;
  1414.     if (!(**ppuni&0x80))
  1415.     return (UCode_t) **ppuni; /* ASCII range character */
  1416.     else if (!(**ppuni&0x40))
  1417.     return (-1);        /* not a valid UTF-8 start */
  1418.     if ((*p & 0xe0) == 0xc0) {
  1419.     utf_count = 1;
  1420.     } else if ((*p & 0xf0) == 0xe0) {
  1421.     utf_count = 2;
  1422.     } else if ((*p & 0xf8) == 0xf0) {
  1423.     utf_count = 3;
  1424.     } else if ((*p & 0xfc) == 0xf8) {
  1425.     utf_count = 4;
  1426.     } else if ((*p & 0xfe) == 0xfc) {
  1427.     utf_count = 5;
  1428.     } else { /* garbage */
  1429.     return (-1);
  1430.     }
  1431.     for (p = *ppuni, i = 0; i < utf_count ; i++) {
  1432.     if ((*(++p) & 0xc0) != 0x80)
  1433.         return (-1);
  1434.     }
  1435.     p = *ppuni;
  1436.     switch (utf_count) {
  1437.     case 1:
  1438.     uc_out = (((*p&0x1f) << 6) | (*(p+1)&0x3f));
  1439.     break;
  1440.     case 2:
  1441.     uc_out = (((((*p&0x0f) << 6) | (*(p+1)&0x3f)) << 6) | (*(p+2)&0x3f));
  1442.     break;
  1443.     case 3:
  1444.     uc_out = (((((((*p&0x07) << 6) | (*(p+1)&0x3f)) << 6) | (*(p+2)&0x3f)) << 6)
  1445.         | (*(p+3)&0x3f));
  1446.     break;
  1447.     case 4:
  1448.     uc_out = (((((((((*p&0x03) << 6) | (*(p+1)&0x3f)) << 6) | (*(p+2)&0x3f)) << 6)
  1449.           | (*(p+3)&0x3f)) << 6) | (*(p+4)&0x3f));
  1450.     break;
  1451.     case 5:
  1452.     uc_out = (((((((((((*p&0x01) << 6) | (*(p+1)&0x3f)) << 6) | (*(p+2)&0x3f)) << 6)
  1453.           | (*(p+3)&0x3f)) << 6) | (*(p+4)&0x3f)) << 6) | (*(p+5)&0x3f));
  1454.     break;
  1455.     }
  1456.     *ppuni = p + utf_count;
  1457.     return uc_out;
  1458. }
  1459.  
  1460. /*
  1461.  *  Given an UCS character code, will fill buffer passed in as q with
  1462.  *  the code's UTF-8 encoding.
  1463.  *  If terminate = YES, terminates string on success and returns pointer
  1464.  *            to beginning.
  1465.  *  If terminate = NO,    does not terminate string, and returns pointer
  1466.  *            next char after the UTF-8 put into buffer.
  1467.  *  On failure, including invalid code or 7-bit code, returns NULL.
  1468.  */
  1469. PRIVATE char * UCPutUtf8ToBuffer ARGS3(char *, q, UCode_t, code, BOOL, terminate)
  1470. {
  1471.     char *q_in = q;
  1472.     if (!q)
  1473.     return NULL;
  1474.     if (code > 127 && code < 0x7fffffffL) {
  1475.     if (code < 0x800L) {
  1476.         *q++ = (char)(0xc0 | (code>>6));
  1477.         *q++ = (char)(0x80 | (0x3f & (code)));
  1478.     } else if (code < 0x10000L) {
  1479.         *q++ = (char)(0xe0 | (code>>12));
  1480.         *q++ = (char)(0x80 | (0x3f & (code>>6)));
  1481.         *q++ = (char)(0x80 | (0x3f & (code)));
  1482.     } else if (code < 0x200000L) {
  1483.         *q++ = (char)(0xf0 | (code>>18));
  1484.         *q++ = (char)(0x80 | (0x3f & (code>>12)));
  1485.         *q++ = (char)(0x80 | (0x3f & (code>>6)));
  1486.         *q++ = (char)(0x80 | (0x3f & (code)));
  1487.     } else if (code < 0x4000000L) {
  1488.         *q++ = (char)(0xf8 | (code>>24));
  1489.         *q++ = (char)(0x80 | (0x3f & (code>>18)));
  1490.         *q++ = (char)(0x80 | (0x3f & (code>>12)));
  1491.         *q++ = (char)(0x80 | (0x3f & (code>>6)));
  1492.         *q++ = (char)(0x80 | (0x3f & (code)));
  1493.     } else {
  1494.         *q++ = (char)(0xfc | (code>>30));
  1495.         *q++ = (char)(0x80 | (0x3f & (code>>24)));
  1496.         *q++ = (char)(0x80 | (0x3f & (code>>18)));
  1497.         *q++ = (char)(0x80 | (0x3f & (code>>12)));
  1498.         *q++ = (char)(0x80 | (0x3f & (code>>6)));
  1499.         *q++ = (char)(0x80 | (0x3f & (code)));
  1500.     }
  1501.     } else {
  1502.     return NULL;
  1503.     }
  1504.     if (terminate) {
  1505.     *q = '\0';
  1506.     return q_in;
  1507.     } else {
  1508.     return q;
  1509.     }
  1510. }
  1511.  
  1512.     /* as in HTParse.c, saves some calls - kw */
  1513. PRIVATE char *hex = "0123456789ABCDEF";
  1514.  
  1515. /*
  1516.  *      Any raw 8-bit or multibyte characters already have been
  1517.  *      handled in relation to the display character set
  1518.  *      in SGML_character(), including named and numeric entities.
  1519.  *
  1520. **  This function used for translations HTML special fields inside tags
  1521. **  (ALT=, VALUE=, etc.) from charset `cs_from' to charset `cs_to'.
  1522. **  It also unescapes non-ASCII characters from URL (#fragments !)
  1523. **  if st_URL is active.
  1524. **
  1525. **  If `do_ent' is YES, it converts named entities
  1526. **  and numeric character references (NCRs) to their `cs_to' replacements.
  1527. **
  1528. **  Named entities converted to unicodes.  NCRs (unicodes) converted
  1529. **  by UCdomap.c chartrans functions.
  1530. **  ???NCRs with values in the ISO-8859-1 range 160-255 may be converted
  1531. **  to their HTML entity names (via old-style entities) and then translated
  1532. **  according to the LYCharSets.c array for `cs_out'???.
  1533. **
  1534. **  Some characters (see descriptions in `put_special_unicodes' from SGML.c)
  1535. **  translated in relation with the state of boolean variables
  1536. **  `use_lynx_specials', `plain_space' and `hidden'. It is not clear yet:
  1537. **
  1538. **  If plain_space is TRUE, nbsp (160) will be treated as an ASCII
  1539. **  space (32).  If hidden is TRUE, entities will be translated
  1540. **  (if `do_ent' is YES) but escape sequences will be passed unaltered.
  1541. **  If `hidden' is FALSE, some characters are converted to Lynx special
  1542. **  codes (see `put_special_unicodes') or ASCII space if `plain_space'
  1543. **  applies).  @@ is `use_lynx_specials' needed, does it have any effect? @@
  1544. **  If `use_lynx_specials' is YES, translate byte values 160 and 173
  1545. **  meaning U+00A0 and U+00AD given as or converted from raw char input
  1546. **  are converted to HT_NON_BREAK_SPACE and LY_SOFT_HYPHEN, respectively
  1547. **  (unless input and output charset are both iso-8859-1, for compatibility
  1548. **  with previous usage in HTML.c) even if `hidden' or `plain_space' is set.
  1549. **
  1550. **  If `Back' is YES, the reverse is done instead i.e. Lynx special codes
  1551. **  in the input are translated back to character values.
  1552. **
  1553. **  If `Back' is YES, an attempt is made to use UCReverseTransChar() for
  1554. **  back translation which may be more efficient. (?)
  1555. **
  1556. **  If `stype' is st_URL, non-ASCII characters are URL-encoded instead.
  1557. **  The sequence of bytes being URL-encoded is the raw input character if
  1558. **  we couldn't translate it from `cs_in' (CJK etc.); otherwise it is the
  1559. **  UTF-8 representation if either `cs_to' requires this or if the
  1560. **  character's Unicode value is > 255, otherwise it should be the iso-8859-1
  1561. **  representation.
  1562. **  No general URL-encoding occurs for displayable ASCII characters and
  1563. **  spaces and some C0 controls valid in HTML (LF, TAB), it is expected
  1564. **  that other functions will take care of that as appropriate.
  1565. **
  1566. **  Escape characters (0x1B, '\033') are
  1567. **  - URL-encoded    if `stype'  is st_URL,     otherwise
  1568. **  - dropped        if `stype'  is st_other, otherwise (i.e. st_HTML)
  1569. **  - passed        if `hidden' is TRUE or HTCJK is set, otherwise
  1570. **  - dropped.
  1571. **
  1572. **  (If `stype' is st_URL or st_other most of the parameters really predefined:
  1573. **  cs_from=cs_to, use_lynx_specials=plain_space=NO, and hidden=YES)
  1574. **
  1575. **
  1576. **  Returns pointer to the char** passed in
  1577. **         if string translated or translation unnecessary,
  1578. **        NULL otherwise
  1579. **         (in which case something probably went wrong.)
  1580. */
  1581.  
  1582. PRIVATE char ** LYUCFullyTranslateString_1 ARGS9(
  1583.     char **,    str,
  1584.     int,        cs_from,
  1585.     int,        cs_to,
  1586.     BOOLEAN,    do_ent,
  1587.     BOOL,        use_lynx_specials,
  1588.     BOOLEAN,    plain_space,
  1589.     BOOLEAN,    hidden,
  1590.     BOOL,        Back,
  1591.     CharUtil_st,    stype)
  1592. {
  1593.     char * p;
  1594.     char *q, *qs;
  1595.     HTChunk *chunk = NULL;
  1596.     char * cp = 0;
  1597.     char cpe = 0;
  1598.     char *esc = NULL;
  1599.     char replace_buf [64];
  1600.     int uck;
  1601.     int lowest_8;
  1602.     UCode_t code = 0;
  1603.     long int lcode;
  1604.     BOOL output_utf8 = 0, repl_translated_C0 = 0;
  1605.     size_t len;
  1606.     CONST char * name = NULL;
  1607.     BOOLEAN no_bytetrans;
  1608.     UCTransParams T;
  1609.     BOOL from_is_utf8 = FALSE;
  1610.     char * puni;
  1611.     enum _state
  1612.     { S_text, S_esc, S_dollar, S_paren, S_nonascii_text, S_dollar_paren,
  1613.     S_trans_byte, S_check_ent, S_ncr, S_check_uni, S_named, S_check_name,
  1614.     S_recover,
  1615.     S_got_oututf8, S_got_outstring, S_put_urlstring,
  1616.     S_got_outchar, S_put_urlchar, S_next_char, S_done} state = S_text;
  1617.     enum _parsing_what
  1618.     { P_text, P_utf8, P_hex, P_decimal, P_named
  1619.     } what = P_text;
  1620.  
  1621.     /*
  1622.     **    Make sure we have a non-empty string. - FM
  1623.     */
  1624.     if (!str || *str == NULL || **str == '\0')
  1625.     return str;
  1626.  
  1627.     /*
  1628.      * FIXME: something's wrong with the limit checks here (clearing the
  1629.      * buffer helps).
  1630.      */
  1631.     memset(replace_buf, 0, sizeof(replace_buf));
  1632.  
  1633.     /*
  1634.     **    Don't do byte translation
  1635.     **    if original AND target character sets
  1636.     **    are both iso-8859-1,
  1637.     **    or if we are in CJK mode.
  1638.     */
  1639.     no_bytetrans = ((cs_to <= 0 && cs_from == cs_to) ||
  1640.             HTCJK != NOCJK);
  1641.  
  1642.     /* No need to translate or examine the string any further */
  1643.     if (!no_bytetrans)
  1644.     no_bytetrans = (!use_lynx_specials && !Back &&
  1645.             UCNeedNotTranslate(cs_from, cs_to));
  1646.  
  1647.     /*
  1648.     **    Save malloc/calloc overhead in simple case - kw
  1649.     */
  1650.     if (do_ent && hidden && (stype != st_URL) && (strchr(*str, '&') == NULL))
  1651.     do_ent = FALSE;
  1652.  
  1653.     /* Can't do, caller should figure out what to do... */
  1654.     if (!UCCanTranslateFromTo(cs_from, cs_to)) {
  1655.     if (cs_to < 0)
  1656.         return NULL;
  1657.     if (!do_ent && no_bytetrans)
  1658.         return NULL;
  1659.     no_bytetrans = TRUE;
  1660.     } else if (cs_to < 0) {
  1661.     do_ent = FALSE;
  1662.     }
  1663.  
  1664.     if (!do_ent && no_bytetrans)
  1665.     return str;
  1666.     p = *str;
  1667.  
  1668.     if (!no_bytetrans) {
  1669.     UCTransParams_clear(&T);
  1670.     UCSetTransParams(&T, cs_from, &LYCharSet_UC[cs_from],
  1671.              cs_to, &LYCharSet_UC[cs_to]);
  1672.     from_is_utf8 = (LYCharSet_UC[cs_from].enc == UCT_ENC_UTF8);
  1673.     output_utf8 = T.output_utf8;
  1674.     repl_translated_C0 = T.repl_translated_C0;
  1675.     puni = p;
  1676.     } else if (do_ent) {
  1677.     output_utf8 = (LYCharSet_UC[cs_to].enc == UCT_ENC_UTF8 ||
  1678.                HText_hasUTF8OutputSet(HTMainText));
  1679.     repl_translated_C0 = (LYCharSet_UC[cs_to].enc == UCT_ENC_8BIT_C0);
  1680.     }
  1681.  
  1682.     lowest_8 = LYlowest_eightbit[cs_to];
  1683.  
  1684.     /*
  1685.     **    Create a buffer string seven times the length of the original,
  1686.     **    so we have plenty of room for expansions. - FM
  1687.     */
  1688.     len = strlen(p) + 16;
  1689.     q = p;
  1690.  
  1691.     qs = q;
  1692.  
  1693. /*  Create the HTChunk only if we need it */
  1694. #define CHUNK (chunk ? chunk : (chunk = HTChunkCreate2(128, len+1)))
  1695.  
  1696. #define REPLACE_STRING(s) \
  1697.         if (q != qs) HTChunkPutb(CHUNK, qs, q-qs); \
  1698.         HTChunkPuts(CHUNK, s); \
  1699.         qs = q = *str
  1700.  
  1701. #define REPLACE_CHAR(c) if (q > p) { \
  1702.         HTChunkPutb(CHUNK, qs, q-qs); \
  1703.         qs = q = *str; \
  1704.         *q++ = c; \
  1705.         } else \
  1706.         *q++ = c
  1707.  
  1708.     /*
  1709.     *  Loop through string, making conversions as needed.
  1710.     *
  1711.     *  The while() checks for a non-'\0' char only for the normal
  1712.     *  text states since other states may temporarily modify p or *p
  1713.     *  (which should be restored before S_done!) - kw
  1714.     */
  1715.  
  1716.     while (*p || (state != S_text && state != S_nonascii_text)) {
  1717.     switch(state) {
  1718.     case S_text:
  1719.         code = (unsigned char)(*p);
  1720.         if (*p == '\033') {
  1721.         if ((HTCJK != NOCJK && !hidden) || stype != st_HTML) {
  1722.             state = S_esc;
  1723.             if (stype == st_URL) {
  1724.             REPLACE_STRING("%1B");
  1725.             p++;
  1726.             continue;
  1727.             } else if (stype != st_HTML) {
  1728.             p++;
  1729.             continue;
  1730.             } else {
  1731.             *q++ = *p++;
  1732.             continue;
  1733.             }
  1734.         } else if (!hidden) {
  1735.             /*
  1736.             **    CJK handling not on, and not a hidden INPUT,
  1737.             **    so block escape. - FM
  1738.             */
  1739.             state = S_next_char;
  1740.         } else {
  1741.             state = S_trans_byte;
  1742.         }
  1743.         } else {
  1744.         state = (do_ent ? S_check_ent : S_trans_byte);
  1745.         }
  1746.         break;
  1747.  
  1748.     case S_esc:
  1749.         if (*p == '$') {
  1750.         state = S_dollar;
  1751.         *q++ = *p++;
  1752.         continue;
  1753.         } else if (*p == '(') {
  1754.         state = S_paren;
  1755.         *q++ = *p++;
  1756.         continue;
  1757.         } else {
  1758.         state = S_text;
  1759.         }
  1760.  
  1761.     case S_dollar:
  1762.         if (*p == '@' || *p == 'B' || *p == 'A') {
  1763.         state = S_nonascii_text;
  1764.         *q++ = *p++;
  1765.         continue;
  1766.         } else if (*p == '(') {
  1767.         state = S_dollar_paren;
  1768.         *q++ = *p++;
  1769.         continue;
  1770.         } else {
  1771.         state = S_text;
  1772.         }
  1773.         break;
  1774.  
  1775.     case S_dollar_paren:
  1776.         if (*p == 'C') {
  1777.         state = S_nonascii_text;
  1778.         *q++ = *p++;
  1779.         continue;
  1780.         } else {
  1781.         state = S_text;
  1782.         }
  1783.         break;
  1784.  
  1785.     case S_paren:
  1786.         if (*p == 'B' || *p == 'J' || *p == 'T')  {
  1787.         state = S_text;
  1788.         *q++ = *p++;
  1789.         continue;
  1790.         } else if (*p == 'I') {
  1791.         state = S_nonascii_text;
  1792.         *q++ = *p++;
  1793.         continue;
  1794.         } else {
  1795.         state = S_text;
  1796.         }
  1797.         break;
  1798.  
  1799.     case S_nonascii_text:
  1800.         if (*p == '\033') {
  1801.         if ((HTCJK != NOCJK && !hidden) || stype != st_HTML) {
  1802.             state = S_esc;
  1803.             if (stype == st_URL) {
  1804.             REPLACE_STRING("%1B");
  1805.             p++;
  1806.             continue;
  1807.             } else if (stype != st_HTML) {
  1808.             p++;
  1809.             continue;
  1810.             }
  1811.         }
  1812.         }
  1813.         *q++ = *p++;
  1814.         continue;
  1815.  
  1816.     case S_trans_byte:
  1817.         /*    character translation goes here  */
  1818.         /*
  1819.         **    Don't do anything if we have no string,
  1820.         **    or if original AND target character sets
  1821.         **    are both iso-8859-1,
  1822.         **    or if we are in CJK mode.
  1823.         */
  1824.         if (*p == '\0' || no_bytetrans) {
  1825.         state = S_got_outchar;
  1826.         break;
  1827.         }
  1828.  
  1829.         if (Back) {
  1830.         int rev_c;
  1831.         if ((*p) == HT_NON_BREAK_SPACE ||
  1832.             (*p) == HT_EM_SPACE) {
  1833.             if (plain_space) {
  1834.             code = *p = ' ';
  1835.             state = S_got_outchar;
  1836.             break;
  1837.             } else {
  1838.             *p = 160;
  1839.             code = 160;
  1840.             if (LYCharSet_UC[cs_to].enc == UCT_ENC_8859 ||
  1841.                 (LYCharSet_UC[cs_to].like8859 & UCT_R_8859SPECL)) {
  1842.                 state = S_got_outchar;
  1843.                 break;
  1844.             }
  1845.             }
  1846.         } else if ((*p) == LY_SOFT_HYPHEN) {
  1847.             *p = 173;
  1848.             code = 173;
  1849.             if (LYCharSet_UC[cs_to].enc == UCT_ENC_8859 ||
  1850.             (LYCharSet_UC[cs_to].like8859 & UCT_R_8859SPECL)) {
  1851.             state = S_got_outchar;
  1852.             break;
  1853.             }
  1854.         } else if (code < 127 || T.transp) {
  1855.             state = S_got_outchar;
  1856.             break;
  1857.         }
  1858.         rev_c = UCReverseTransChar(*p, cs_to, cs_from);
  1859.         if (rev_c > 127) {
  1860.             *p = rev_c;
  1861.             code = rev_c;
  1862.             state = S_got_outchar;
  1863.             break;
  1864.         }
  1865.         } else if (code < 127) {
  1866.         state = S_got_outchar;
  1867.         break;
  1868.         }
  1869.  
  1870.         if (from_is_utf8) {
  1871.         if (((*p)&0xc0)==0xc0) {
  1872.             puni = p;
  1873.             code = UCGetUniFromUtf8String(&puni);
  1874.             if (code <= 0) {
  1875.             code = (unsigned char)(*p);
  1876.             } else {
  1877.             what = P_utf8;
  1878.             }
  1879.         }
  1880.         } else if (use_lynx_specials && !Back &&
  1881.                (code == 160 || code == 173) &&
  1882.                (LYCharSet_UC[cs_from].enc == UCT_ENC_8859 ||
  1883.             (LYCharSet_UC[cs_from].like8859 & UCT_R_8859SPECL))) {
  1884.         if (code == 160)
  1885.             code = *p = HT_NON_BREAK_SPACE;
  1886.         else if (code == 173)
  1887.             code = *p = LY_SOFT_HYPHEN;
  1888.         state = S_got_outchar;
  1889.         break;
  1890.         } else if (T.trans_to_uni) {
  1891.         code = UCTransToUni(*p, cs_from);
  1892.         if (code <= 0) {
  1893.             /* What else can we do? */
  1894.             code = (unsigned char)(*p);
  1895.         }
  1896. #ifdef NOTUSED_FOTEMODS
  1897.         } else if (T.strip_raw_char_in &&
  1898.                (unsigned char)(*p) >= 0xc0 &&
  1899.                (unsigned char)(*p) < 255) {
  1900.         code = ((*p & 0x7f));
  1901.         state = S_got_outchar;
  1902.         break;
  1903. #endif /* NOTUSED_FOTEMODS */
  1904.         } else if (!T.trans_from_uni) {
  1905.         state = S_got_outchar;
  1906.         break;
  1907.         }
  1908.         /*
  1909.             **    Substitute Lynx special character for
  1910.             **    160 (nbsp) if use_lynx_specials is set.
  1911.             */
  1912.         if (use_lynx_specials && !Back &&
  1913.         (code == 160 || code == 173)) {
  1914.         code = ((code==160 ? HT_NON_BREAK_SPACE : LY_SOFT_HYPHEN));
  1915.         state = S_got_outchar;
  1916.         break;
  1917.         }
  1918.  
  1919.         state = S_check_uni;
  1920.         break;
  1921.  
  1922.     case S_check_ent:
  1923.         if (*p == '&') {
  1924.         char * pp = p + 1;
  1925.         len = strlen(pp);
  1926.         /*
  1927.         **  Check for a numeric entity. - FM
  1928.         */
  1929.         if (*pp == '#' && len > 2 &&
  1930.             (*(pp+1) == 'x' || *(pp+1) == 'X') &&
  1931.             (unsigned char)*(pp+2) < 127 &&
  1932.             isxdigit((unsigned char)*(pp+2))) {
  1933.             what = P_hex;
  1934.             state = S_ncr;
  1935.         } else if (*pp == '#' && len > 2 &&
  1936.                (unsigned char)*(pp+1) < 127 &&
  1937.                isdigit((unsigned char)*(pp+1))) {
  1938.             what = P_decimal;
  1939.             state = S_ncr;
  1940.         } else if ((unsigned char)*pp < 127 &&
  1941.                isalpha((unsigned char)*pp)) {
  1942.             what = P_named;
  1943.             state = S_named;
  1944.         } else {
  1945.             state = S_trans_byte;
  1946.         }
  1947.         } else {
  1948.         state = S_trans_byte;
  1949.         }
  1950.         break;
  1951.  
  1952.     case S_ncr:
  1953.         if (what == P_hex) {
  1954.             p += 3;
  1955.         } else {    /* P_decimal */
  1956.             p += 2;
  1957.         }
  1958.         cp = p;
  1959.         while (*p && (unsigned char)*p < 127 &&
  1960.                (what == P_hex ? isxdigit((unsigned char)*p) :
  1961.                     isdigit((unsigned char)*p))) {
  1962.             p++;
  1963.         }
  1964.         /*
  1965.         **  Save the terminator and isolate the digit(s). - FM
  1966.         */
  1967.         cpe = *p;
  1968.         if (*p)
  1969.             *p++ = '\0';
  1970.         /*
  1971.         ** Show the numeric entity if the value:
  1972.         **  (1) Is greater than 255 and unhandled Unicode.
  1973.         **  (2) Is less than 32, and not valid and we don't
  1974.         **    have HTCJK set.
  1975.         **  (3) Is 127 and we don't have HTPassHighCtrlRaw
  1976.         **    or HTCJK set.
  1977.         **  (4) Is 128 - 159 and we don't have HTPassHighCtrlNum set.
  1978.         */
  1979.         if ((((what == P_hex) ? sscanf(cp, "%lx", &lcode) :
  1980.                     sscanf(cp, "%ld", &lcode)) != 1) ||
  1981.             lcode > 0x7fffffffL || lcode < 0) {
  1982.             state = S_recover;
  1983.             break;
  1984.         } else {
  1985.             code = lcode;
  1986.             if ((code == 1) ||
  1987.                (code > 129 && code < 156)) {
  1988.             /*
  1989.             ** Assume these are Microsoft code points, inflicted on
  1990.             ** us by FrontPage.  - FM
  1991.             **
  1992.             ** MS FrontPage uses syntax like ™ in 128-159
  1993.             ** range and doesn't follow Unicode standards for this
  1994.             ** area.  Windows-1252 codepoints are assumed here.
  1995.             */
  1996.             switch (code) {
  1997.             case 1:
  1998.                 /*
  1999.                 **    WHITE SMILING FACE
  2000.                 */
  2001.                 code = 0x263a;
  2002.                 break;
  2003.             case 130:
  2004.                 /*
  2005.                 **    SINGLE LOW-9 QUOTATION MARK (sbquo)
  2006.                 */
  2007.                 code = 0x201a;
  2008.                 break;
  2009.             case 132:
  2010.                 /*
  2011.                 **    DOUBLE LOW-9 QUOTATION MARK (bdquo)
  2012.                 */
  2013.                 code = 0x201e;
  2014.                 break;
  2015.             case 133:
  2016.                 /*
  2017.                 **    HORIZONTAL ELLIPSIS (hellip)
  2018.                 */
  2019.                 code = 0x2026;
  2020.                 break;
  2021.             case 134:
  2022.                 /*
  2023.                 **    DAGGER (dagger)
  2024.                 */
  2025.                 code = 0x2020;
  2026.                 break;
  2027.             case 135:
  2028.                 /*
  2029.                 **    DOUBLE DAGGER (Dagger)
  2030.                 */
  2031.                 code = 0x2021;
  2032.                 break;
  2033.             case 137:
  2034.                 /*
  2035.                 **    PER MILLE SIGN (permil)
  2036.                 */
  2037.                 code = 0x2030;
  2038.                 break;
  2039.             case 139:
  2040.                 /*
  2041.                 **    SINGLE LEFT-POINTING ANGLE QUOTATION MARK
  2042.                 **    (lsaquo)
  2043.                 */
  2044.                 code = 0x2039;
  2045.                 break;
  2046.             case 145:
  2047.                 /*
  2048.                 **    LEFT SINGLE QUOTATION MARK (lsquo)
  2049.                 */
  2050.                 code = 0x2018;
  2051.                 break;
  2052.             case 146:
  2053.                 /*
  2054.                 **    RIGHT SINGLE QUOTATION MARK (rsquo)
  2055.                 */
  2056.                 code = 0x2019;
  2057.                 break;
  2058.             case 147:
  2059.                 /*
  2060.                 **    LEFT DOUBLE QUOTATION MARK (ldquo)
  2061.                 */
  2062.                 code = 0x201c;
  2063.                 break;
  2064.             case 148:
  2065.                 /*
  2066.                 **    RIGHT DOUBLE QUOTATION MARK (rdquo)
  2067.                 */
  2068.                 code = 0x201d;
  2069.                 break;
  2070.             case 149:
  2071.                 /*
  2072.                 **    BULLET (bull)
  2073.                 */
  2074.                 code = 0x2022;
  2075.                 break;
  2076.             case 150:
  2077.                 /*
  2078.                 **    EN DASH (ndash)
  2079.                 */
  2080.                 code = 0x2013;
  2081.                 break;
  2082.             case 151:
  2083.                 /*
  2084.                 **    EM DASH (mdash)
  2085.                 */
  2086.                 code = 0x2014;
  2087.                 break;
  2088.             case 152:
  2089.                 /*
  2090.                 **    SMALL TILDE (tilde)
  2091.                 */
  2092.                 code = 0x02dc;
  2093.                 break;
  2094.             case 153:
  2095.                 /*
  2096.                 **    TRADE MARK SIGN (trade)
  2097.                 */
  2098.                 code = 0x2122;
  2099.                 break;
  2100.             case 155:
  2101.                 /*
  2102.                 **    SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
  2103.                 **    (rsaquo)
  2104.                 */
  2105.                 code = 0x203a;
  2106.                 break;
  2107.             default:
  2108.                 /*
  2109.                 **    Do not attempt a conversion
  2110.                 **    to valid Unicode values.
  2111.                 */
  2112.                 break;
  2113.                }
  2114.             }
  2115.             state = S_check_uni;
  2116.         }
  2117.         break;
  2118.  
  2119.     case S_check_uni:
  2120.         /*
  2121.         ** Show the numeric entity if the value:
  2122.         **  (2) Is less than 32, and not valid and we don't
  2123.         **    have HTCJK set.
  2124.         **  (3) Is 127 and we don't have HTPassHighCtrlRaw
  2125.         **    or HTCJK set.
  2126.         **  (4) Is 128 - 159 and we don't have HTPassHighCtrlNum set.
  2127.         */
  2128.          if ((code < 32 &&
  2129.              code != 9 && code != 10 && code != 13 &&
  2130.              HTCJK == NOCJK) ||
  2131.             (code == 127 &&
  2132.              !(HTPassHighCtrlRaw || HTCJK != NOCJK)) ||
  2133.             (code > 127 && code < 160 &&
  2134.              !HTPassHighCtrlNum)) {
  2135.              state = S_recover;
  2136.              break;
  2137.          }
  2138.         /*
  2139.         **  Convert the value as an unsigned char,
  2140.         **  hex escaped if isURL is set and it's
  2141.         **  8-bit, and then recycle the terminator
  2142.         **  if it is not a semicolon. - FM
  2143.         */
  2144.         if (code > 159 && stype == st_URL) {
  2145.             state = S_got_oututf8;
  2146.             break;
  2147.         }
  2148.             /*
  2149.             **    For 160 (nbsp), use that value if it's
  2150.             **    a hidden INPUT, otherwise use an ASCII
  2151.             **    space (32) if plain_space is TRUE,
  2152.             **    otherwise use the Lynx special character. - FM
  2153.             */
  2154.         if (code == 160) {
  2155.             if (hidden) {
  2156.             ;
  2157.             } else if (plain_space) {
  2158.             code = ' ';
  2159.             } else {
  2160.             code = HT_NON_BREAK_SPACE;
  2161.             }
  2162.             state = S_got_outchar;
  2163.             break;
  2164.         }
  2165.         /*
  2166.             **    For 173 (shy), use that value if it's
  2167.             **    a hidden INPUT, otherwise ignore it
  2168.             **    if plain_space is TRUE, otherwise use
  2169.             **    the Lynx special character. - FM
  2170.             */
  2171.         if (code == 173) {
  2172.             if (plain_space) {
  2173.             replace_buf[0] = '\0';
  2174.             state = S_got_outstring;
  2175.             break;
  2176.             } else if (Back &&
  2177.                    !(LYCharSet_UC[cs_to].enc == UCT_ENC_8859 ||
  2178.                  (LYCharSet_UC[cs_to].like8859 &
  2179.                            UCT_R_8859SPECL))) {
  2180.             ;    /* nothing, may be translated later */
  2181.             } else if (hidden || Back) {
  2182.             state = S_got_outchar;
  2183.             break;
  2184.             } else {
  2185.             code = LY_SOFT_HYPHEN;
  2186.             state = S_got_outchar;
  2187.             break;
  2188.             }
  2189.         }
  2190.         /*
  2191.         **  Seek a translation from the chartrans tables.
  2192.         */
  2193.         if ((uck = UCTransUniChar(code,
  2194.                       cs_to)) >= 32 &&
  2195.             uck < 256 &&
  2196.             (uck < 127 || uck >= lowest_8)) {
  2197.             code = uck;
  2198.             state = S_got_outchar;
  2199.             break;
  2200.         } else if ((uck == -4 ||
  2201.                 (repl_translated_C0 &&
  2202.                  uck > 0 && uck < 32)) &&
  2203.                /*
  2204.                **  Not found; look for replacement string.
  2205.                */
  2206.                (uck = UCTransUniCharStr(replace_buf,
  2207.                             60, code,
  2208.                             cs_to,
  2209.                             0) >= 0)) {
  2210.             state = S_got_outstring;
  2211.             break;
  2212.         }
  2213.         if (output_utf8 &&
  2214.             code > 127 && code < 0x7fffffffL) {
  2215.             state = S_got_oututf8;
  2216.             break;
  2217.         }
  2218.         /*
  2219.         **  For 8194 (ensp), 8195 (emsp), or 8201 (thinsp),
  2220.         **  use the character reference if it's a hidden INPUT,
  2221.         **  otherwise use an ASCII space (32) if plain_space is
  2222.         **  TRUE, otherwise use the Lynx special character. - FM
  2223.         */
  2224.         if (code == 8194 || code == 8195 || code == 8201) {
  2225.             if (hidden) {
  2226.             state = S_recover;
  2227.             } else if (plain_space) {
  2228.             code = ' ';
  2229.             state = S_got_outchar;
  2230.             } else {
  2231.             code = HT_EM_SPACE;
  2232.             state = S_got_outchar;
  2233.             }
  2234.             break;
  2235.             /*
  2236.             **    Ignore 8204 (zwnj), 8205 (zwj)
  2237.             **    8206 (lrm), and 8207 (rlm),
  2238.             **    for now, if we got this far without
  2239.             **    finding a representation for them.
  2240.             */
  2241.         } else if (code == 8204 || code == 8205 ||
  2242.                code == 8206 || code == 8207) {
  2243.             CTRACE(tfp, "LYUCFullyTranslateString: Ignoring '%ld'.\n", code);
  2244.             replace_buf[0] = '\0';
  2245.             state = S_got_outstring;
  2246.             break;
  2247.             /*
  2248.             **    Show the numeric entity if the value:
  2249.             **    (1) Is greater than 255 and unhandled Unicode.
  2250.             */
  2251.         } else if (code > 255) {
  2252.             /*
  2253.             **  Illegal or not yet handled value.
  2254.             **  Return "&#" verbatim and continue
  2255.             **  from there. - FM
  2256.             */
  2257.             state = S_recover;
  2258.             break;
  2259.         /*
  2260.         **  If it's ASCII, or is 8-bit but HTPassEightBitNum
  2261.         **  is set or the character set is "ISO Latin 1",
  2262.         **  use it's value. - FM
  2263.         */
  2264.         } else if (code < 161 ||
  2265.                (code < 256 &&
  2266.                 (HTPassEightBitNum ||
  2267.                  !strncmp(LYchar_set_names[cs_to],
  2268.                       "ISO Latin 1", 11)))) {
  2269.             /*
  2270.             **    No conversion needed.
  2271.             */
  2272.             state = S_got_outchar;
  2273.             break;
  2274.             /*
  2275.             **    If we get to here, convert and handle
  2276.             **    the character as a named entity. - FM
  2277.             */
  2278.         } else {
  2279.             name = HTMLGetEntityName(code - 160);
  2280.             state = S_check_name;
  2281.             break;
  2282.         }
  2283.  
  2284.     case S_recover:
  2285.         if (what == P_decimal || what == P_hex) {
  2286.             /*
  2287.             **    Illegal or not yet handled value.
  2288.             **    Return "&#" verbatim and continue
  2289.             **    from there. - FM
  2290.             */
  2291.             *q++ = '&';
  2292.             *q++ = '#';
  2293.             if (what == P_hex)
  2294.             *q++ = 'x';
  2295.             if (cpe != '\0')
  2296.             *(p-1) = cpe;
  2297.             p = cp;
  2298.             state = S_done;
  2299.         } else if (what == P_named) {
  2300.         *cp = cpe;
  2301.         *q++ = '&';
  2302.         state = S_done;
  2303. #ifdef NOTUSED_FOTEMODS
  2304.         } else if (T.strip_raw_char_in &&
  2305.         (unsigned char)(*p) >= 0xc0 &&
  2306.         (unsigned char)(*p) < 255) {
  2307.         code = (((*p) & 0x7f));
  2308.         state = S_got_outchar;
  2309. #endif /* NOTUSED_FOTEMODS */
  2310.         } else if (!T.output_utf8 && stype == st_HTML && !hidden &&
  2311.         !(HTPassEightBitRaw &&
  2312.          (unsigned char)(*p) >= lowest_8)) {
  2313.         sprintf(replace_buf, "U%.2lX", code);
  2314.         state = S_got_outstring;
  2315.         } else {
  2316.         puni = p;
  2317.         code = (unsigned char)(*p);
  2318.         state = S_got_outchar;
  2319.         }
  2320.         break;
  2321.  
  2322.     case S_named:
  2323.         cp = ++p;
  2324.         while (*cp && (unsigned char)*cp < 127 &&
  2325.            isalnum((unsigned char)*cp))
  2326.         cp++;
  2327.         cpe = *cp;
  2328.         *cp = '\0';
  2329. /*        ppuni = cp - 1; */
  2330.         name = p;
  2331.         state = S_check_name;
  2332.         break;
  2333.  
  2334.     case S_check_name:
  2335.         /*
  2336.         **    Seek the Unicode value for the named entity.
  2337.         **
  2338.         **    !!!! We manually recover the case of '=' terminator which
  2339.         **    is commonly found on query to CGI-scripts
  2340.         **    enclosed as href= URLs like  "somepath/?x=1&yz=2"
  2341.         **    Without this dirty fix, submission of such URLs was broken
  2342.         **    if &yz string happened to be a recognized entity name. - LP
  2343.         */
  2344.        if ( ((code = HTMLGetEntityUCValue(name)) > 0) &&
  2345.         !((cpe == '=') && (stype == st_URL)) ) {
  2346.         state = S_check_uni;
  2347.         break;
  2348.         }
  2349.         /*
  2350.         **    Didn't find the entity.
  2351.         **    Return verbatim.
  2352.         */
  2353.         state = S_recover;
  2354.         break;
  2355.  
  2356.                 /* * * O U T P U T   S T A T E S * * */
  2357.  
  2358.     case S_got_oututf8:
  2359.         if (code > 255 ||
  2360.         (code >= 128 && LYCharSet_UC[cs_to].enc == UCT_ENC_UTF8)) {
  2361.         UCPutUtf8ToBuffer(replace_buf, code, YES);
  2362.         state = S_got_outstring;
  2363.         } else {
  2364.         state = S_got_outchar;
  2365.         }
  2366.         break;
  2367.     case S_got_outstring:
  2368.         if (what == P_decimal || what == P_hex) {
  2369.         if (cpe != ';' && cpe != '\0')
  2370.             *(--p) = cpe;
  2371.         p--;
  2372.         } else if (what == P_named) {
  2373.         *cp = cpe;
  2374.         p = (*cp != ';') ? (cp - 1) : cp;
  2375.         } else if (what == P_utf8) {
  2376.         p = puni;
  2377.         }
  2378.         if (replace_buf[0] == '\0') {
  2379.         state = S_next_char;
  2380.         break;
  2381.         }
  2382.         if (stype == st_URL) {
  2383.         code = replace_buf[0]; /* assume string OK if first char is */
  2384.         if (code >= 127 ||
  2385.             (code < 32 && (code != 9 && code != 10 && code != 0))) {
  2386.             state = S_put_urlstring;
  2387.         }
  2388.         }
  2389.         REPLACE_STRING(replace_buf);
  2390.         state = S_next_char;
  2391.         break;
  2392.     case S_put_urlstring:
  2393.         esc = HTEscape(replace_buf, URL_XALPHAS);
  2394.         REPLACE_STRING(esc);
  2395.         FREE(esc);
  2396.         state = S_next_char;
  2397.         break;
  2398.     case S_got_outchar:
  2399.         if (what == P_decimal || what == P_hex) {
  2400.         if (cpe != ';' && cpe != '\0')
  2401.             *(--p) = cpe;
  2402.         p--;
  2403.         } else if (what == P_named) {
  2404.         *cp = cpe;
  2405.         p = (*cp != ';') ? (cp - 1) : cp;
  2406.         } else if (what == P_utf8) {
  2407.         p = puni;
  2408.         }
  2409.         if (stype == st_URL &&
  2410.         /*    Not a full HTEscape, only for 8bit and ctrl chars */
  2411.         (code >= 127 ||
  2412.          (code < 32 && (code != 9 && code != 10)))) {
  2413.             state = S_put_urlchar;
  2414.             break;
  2415.         } else if (!hidden && code == 10 && *p == 10
  2416.                && q != qs && *(q-1) == 13) {
  2417.         /*
  2418.         **  If this is not a hidden string, and the current char is
  2419.         **  the LF ('\n') of a CRLF pair, drop the CR ('\r'). - KW
  2420.         */
  2421.         *(q-1) = *p++;
  2422.         state = S_done;
  2423.         break;
  2424.         }
  2425.         *q++ = (char)code;
  2426.         state = S_next_char;
  2427.         break;
  2428.     case S_put_urlchar:
  2429.         *q++ = '%';
  2430.         REPLACE_CHAR(hex[(code >> 4) & 15]);
  2431.         REPLACE_CHAR(hex[(code & 15)]);
  2432.                 /* fall through */
  2433.     case S_next_char:
  2434.         p++;        /* fall through */
  2435.     case S_done:
  2436.         state = S_text;
  2437.         what = P_text;
  2438.                 /* for next round */
  2439.     }
  2440.     }
  2441.  
  2442.     *q = '\0';
  2443.     if (chunk) {
  2444.     HTChunkPutb(CHUNK, qs, q-qs + 1); /* also terminates */
  2445.     if (stype == st_URL) {
  2446.         LYTrimHead(chunk->data);
  2447.         LYTrimTail(chunk->data);
  2448.     }
  2449.     StrAllocCopy(*str, chunk->data);
  2450.     HTChunkFree(chunk);
  2451.     } else {
  2452.     if (stype == st_URL) {
  2453.         LYTrimHead(qs);
  2454.         LYTrimTail(qs);
  2455.     }
  2456.     }
  2457.     return str;
  2458. }
  2459.  
  2460. #undef REPLACE_CHAR
  2461. #undef REPLACE_STRING
  2462.  
  2463. PUBLIC BOOL LYUCFullyTranslateString ARGS7(
  2464.     char **, str,
  2465.     int,    cs_from,
  2466.     int,    cs_to,
  2467.     BOOL,    use_lynx_specials,
  2468.     BOOLEAN,    plain_space,
  2469.     BOOLEAN,    hidden,
  2470.     CharUtil_st,    stype)
  2471. {
  2472.     BOOL ret = YES;
  2473.     /* May reallocate *str even if cs_to == 0 */
  2474.     if (!LYUCFullyTranslateString_1(str, cs_from, cs_to, TRUE,
  2475.                     use_lynx_specials, plain_space, hidden,
  2476.                     NO, stype)) {
  2477.     ret = NO;
  2478.     }
  2479.     return ret;
  2480. }
  2481.  
  2482. PUBLIC BOOL LYUCTranslateBackFormData ARGS4(
  2483.     char **, str,
  2484.     int,    cs_from,
  2485.     int,    cs_to,
  2486.     BOOLEAN,    plain_space)
  2487. {
  2488.     char ** ret;
  2489.     /* May reallocate *str */
  2490.     ret = (LYUCFullyTranslateString_1(str, cs_from, cs_to, FALSE,
  2491.                        NO, plain_space, YES,
  2492.                        YES, st_HTML));
  2493.     return (ret != NULL);
  2494. }
  2495.  
  2496. /*
  2497. **  This function processes META tags in HTML streams. - FM
  2498. */
  2499. PUBLIC void LYHandleMETA ARGS4(
  2500.     HTStructured *,     me,
  2501.     CONST BOOL*,        present,
  2502.     CONST char **,        value,
  2503.     char **,        include)
  2504. {
  2505.     char *http_equiv = NULL, *name = NULL, *content = NULL;
  2506.     char *href = NULL, *id_string = NULL, *temp = NULL;
  2507.     char *cp, *cp0, *cp1 = NULL;
  2508.     int url_type = 0;
  2509.  
  2510.     if (!me || !present)
  2511.     return;
  2512.  
  2513.     /*
  2514.      *    Load the attributes for possible use by Lynx. - FM
  2515.      */
  2516.     if (present[HTML_META_HTTP_EQUIV] &&
  2517.     value[HTML_META_HTTP_EQUIV] && *value[HTML_META_HTTP_EQUIV]) {
  2518.     StrAllocCopy(http_equiv, value[HTML_META_HTTP_EQUIV]);
  2519.     convert_to_spaces(http_equiv, TRUE);
  2520.     LYUCFullyTranslateString(&http_equiv, me->tag_charset, me->tag_charset,
  2521.                  NO, NO, YES, st_other);
  2522.     LYTrimHead(http_equiv);
  2523.     LYTrimTail(http_equiv);
  2524.     if (*http_equiv == '\0') {
  2525.         FREE(http_equiv);
  2526.     }
  2527.     }
  2528.     if (present[HTML_META_NAME] &&
  2529.     value[HTML_META_NAME] && *value[HTML_META_NAME]) {
  2530.     StrAllocCopy(name, value[HTML_META_NAME]);
  2531.     convert_to_spaces(name, TRUE);
  2532.     LYUCFullyTranslateString(&name, me->tag_charset, me->tag_charset,
  2533.                  NO, NO, YES, st_other);
  2534.     LYTrimHead(name);
  2535.     LYTrimTail(name);
  2536.     if (*name == '\0') {
  2537.         FREE(name);
  2538.     }
  2539.     }
  2540.     if (present[HTML_META_CONTENT] &&
  2541.     value[HTML_META_CONTENT] && *value[HTML_META_CONTENT]) {
  2542.     /*
  2543.      *  Technically, we should be creating a comma-separated
  2544.      *  list, but META tags come one at a time, and we'll
  2545.      *  handle (or ignore) them as each is received.  Also,
  2546.      *  at this point, we only trim leading and trailing
  2547.      *  blanks from the CONTENT value, without translating
  2548.      *  any named entities or numeric character references,
  2549.      *  because how we should do that depends on what type
  2550.      *  of information it contains, and whether or not any
  2551.      *  of it might be sent to the screen. - FM
  2552.      */
  2553.     StrAllocCopy(content, value[HTML_META_CONTENT]);
  2554.     convert_to_spaces(content, FALSE);
  2555.     LYTrimHead(content);
  2556.     LYTrimTail(content);
  2557.     if (*content == '\0') {
  2558.         FREE(content);
  2559.     }
  2560.     }
  2561.     CTRACE(tfp, "LYHandleMETA: HTTP-EQUIV=\"%s\" NAME=\"%s\" CONTENT=\"%s\"\n",
  2562.         (http_equiv ? http_equiv : "NULL"),
  2563.         (name ? name : "NULL"),
  2564.         (content ? content : "NULL"));
  2565.  
  2566.     /*
  2567.      *    Make sure we have META name/value pairs to handle. - FM
  2568.      */
  2569.     if (!(http_equiv || name) || !content)
  2570.     goto free_META_copies;
  2571.  
  2572.     /*
  2573.      * Check for a no-cache Pragma
  2574.      * or Cache-Control directive. - FM
  2575.      */
  2576.     if (!strcasecomp((http_equiv ? http_equiv : ""), "Pragma") ||
  2577.     !strcasecomp((http_equiv ? http_equiv : ""), "Cache-Control")) {
  2578.     LYUCFullyTranslateString(&content, me->tag_charset, me->tag_charset,
  2579.                  NO, NO, YES, st_other);
  2580.     LYTrimHead(content);
  2581.     LYTrimTail(content);
  2582.     if (!strcasecomp(content, "no-cache")) {
  2583.         me->node_anchor->no_cache = TRUE;
  2584.         HText_setNoCache(me->text);
  2585.     }
  2586.  
  2587.     /*
  2588.      *  If we didn't get a Cache-Control MIME header,
  2589.      *  and the META has one, convert to lowercase,
  2590.      *  store it in the anchor element, and if we
  2591.      *  haven't yet set no_cache, check whether we
  2592.      *  should. - FM
  2593.      */
  2594.     if ((!me->node_anchor->cache_control) &&
  2595.         !strcasecomp((http_equiv ? http_equiv : ""), "Cache-Control")) {
  2596.         LYLowerCase(content);
  2597.         StrAllocCopy(me->node_anchor->cache_control, content);
  2598.         if (me->node_anchor->no_cache == FALSE) {
  2599.         cp0 = content;
  2600.         while ((cp = strstr(cp0, "no-cache")) != NULL) {
  2601.             cp += 8;
  2602.             while (*cp != '\0' && WHITE(*cp))
  2603.             cp++;
  2604.             if (*cp == '\0' || *cp == ';') {
  2605.             me->node_anchor->no_cache = TRUE;
  2606.             HText_setNoCache(me->text);
  2607.             break;
  2608.             }
  2609.             cp0 = cp;
  2610.         }
  2611.         if (me->node_anchor->no_cache == TRUE)
  2612.             goto free_META_copies;
  2613.         cp0 = content;
  2614.         while ((cp = strstr(cp0, "max-age")) != NULL) {
  2615.             cp += 7;
  2616.             while (*cp != '\0' && WHITE(*cp))
  2617.             cp++;
  2618.             if (*cp == '=') {
  2619.             cp++;
  2620.             while (*cp != '\0' && WHITE(*cp))
  2621.                 cp++;
  2622.             if (isdigit((unsigned char)*cp)) {
  2623.                 cp0 = cp;
  2624.                 while (isdigit((unsigned char)*cp))
  2625.                 cp++;
  2626.                 if (*cp0 == '0' && cp == (cp0 + 1)) {
  2627.                 me->node_anchor->no_cache = TRUE;
  2628.                 HText_setNoCache(me->text);
  2629.                 break;
  2630.                 }
  2631.             }
  2632.             }
  2633.             cp0 = cp;
  2634.         }
  2635.         }
  2636.     }
  2637.  
  2638.     /*
  2639.      * Check for an Expires directive. - FM
  2640.      */
  2641.     } else if (!strcasecomp((http_equiv ? http_equiv : ""), "Expires")) {
  2642.     /*
  2643.      *  If we didn't get an Expires MIME header,
  2644.      *  store it in the anchor element, and if we
  2645.      *  haven't yet set no_cache, check whether we
  2646.      *  should.  Note that we don't accept a Date
  2647.      *  header via META tags, because it's likely
  2648.      *  to be untrustworthy, but do check for a
  2649.      *  Date header from a server when making the
  2650.      *  comparison. - FM
  2651.      */
  2652.     LYUCFullyTranslateString(&content, me->tag_charset, me->tag_charset,
  2653.                  NO, NO, YES, st_other);
  2654.     LYTrimHead(content);
  2655.     LYTrimTail(content);
  2656.     StrAllocCopy(me->node_anchor->expires, content);
  2657.     if (me->node_anchor->no_cache == FALSE) {
  2658.         if (!strcmp(content, "0")) {
  2659.         /*
  2660.          *  The value is zero, which we treat as
  2661.          *  an absolute no-cache directive. - FM
  2662.          */
  2663.         me->node_anchor->no_cache = TRUE;
  2664.         HText_setNoCache(me->text);
  2665.         } else if (me->node_anchor->date != NULL) {
  2666.         /*
  2667.          *  We have a Date header, so check if
  2668.          *  the value is less than or equal to
  2669.          *  that. - FM
  2670.          */
  2671.         if (LYmktime(content, TRUE) <=
  2672.             LYmktime(me->node_anchor->date, TRUE)) {
  2673.             me->node_anchor->no_cache = TRUE;
  2674.             HText_setNoCache(me->text);
  2675.         }
  2676.         } else if (LYmktime(content, FALSE) <= 0) {
  2677.         /*
  2678.          *  We don't have a Date header, and
  2679.          *  the value is in past for us. - FM
  2680.          */
  2681.         me->node_anchor->no_cache = TRUE;
  2682.         HText_setNoCache(me->text);
  2683.         }
  2684.     }
  2685.  
  2686.     /*
  2687.      *    Check for a text/html Content-Type with a
  2688.      *    charset directive, if we didn't already set
  2689.      *    the charset via a server's header. - AAC & FM
  2690.      */
  2691.     } else if (!(me->node_anchor->charset && *me->node_anchor->charset) &&
  2692.            !strcasecomp((http_equiv ? http_equiv : ""), "Content-Type")) {
  2693.     LYUCcharset * p_in = NULL;
  2694.     LYUCFullyTranslateString(&content, me->tag_charset, me->tag_charset,
  2695.                  NO, NO, YES, st_other);
  2696.     LYTrimHead(content);
  2697.     LYTrimTail(content);
  2698.     LYLowerCase(content);
  2699.  
  2700.     if ((cp = strstr(content, "text/html;")) != NULL &&
  2701.         (cp1 = strstr(content, "charset")) != NULL &&
  2702.         cp1 > cp) {
  2703.         BOOL chartrans_ok = NO;
  2704.         char *cp3 = NULL, *cp4;
  2705.         int chndl;
  2706.  
  2707.         cp1 += 7;
  2708.         while (*cp1 == ' ' || *cp1 == '=' || *cp1 == '"')
  2709.         cp1++;
  2710.  
  2711.         StrAllocCopy(cp3, cp1); /* copy to mutilate more */
  2712.         for (cp4 = cp3; (*cp4 != '\0' && *cp4 != '"' &&
  2713.                  *cp4 != ';'  && *cp4 != ':' &&
  2714.                  !WHITE(*cp4)); cp4++) {
  2715.         ; /* do nothing */
  2716.         }
  2717.         *cp4 = '\0';
  2718.         cp4 = cp3;
  2719.         chndl = UCGetLYhndl_byMIME(cp3);
  2720.         if (UCCanTranslateFromTo(chndl, current_char_set)) {
  2721.         chartrans_ok = YES;
  2722.         StrAllocCopy(me->node_anchor->charset, cp4);
  2723.         HTAnchor_setUCInfoStage(me->node_anchor, chndl,
  2724.                     UCT_STAGE_PARSER,
  2725.                     UCT_SETBY_STRUCTURED);
  2726.         } else if (chndl < 0) {
  2727.         /*
  2728.          *  Got something but we don't recognize it.
  2729.          */
  2730.         chndl = UCLYhndl_for_unrec;
  2731.         if (UCCanTranslateFromTo(chndl, current_char_set)) {
  2732.             chartrans_ok = YES;
  2733.             HTAnchor_setUCInfoStage(me->node_anchor, chndl,
  2734.                         UCT_STAGE_PARSER,
  2735.                         UCT_SETBY_STRUCTURED);
  2736.         }
  2737.         }
  2738.         if (chartrans_ok) {
  2739.         LYUCcharset * p_out =
  2740.                 HTAnchor_setUCInfoStage(me->node_anchor,
  2741.                             current_char_set,
  2742.                             UCT_STAGE_HTEXT,
  2743.                             UCT_SETBY_DEFAULT);
  2744.         p_in = HTAnchor_getUCInfoStage(me->node_anchor,
  2745.                            UCT_STAGE_PARSER);
  2746.         if (!p_out) {
  2747.             /*
  2748.              *    Try again.
  2749.              */
  2750.             p_out = HTAnchor_getUCInfoStage(me->node_anchor,
  2751.                             UCT_STAGE_HTEXT);
  2752.         }
  2753.         if (!strcmp(p_in->MIMEname, "x-transparent")) {
  2754.             HTPassEightBitRaw = TRUE;
  2755.             HTAnchor_setUCInfoStage(me->node_anchor,
  2756.                 HTAnchor_getUCLYhndl(me->node_anchor,
  2757.                              UCT_STAGE_HTEXT),
  2758.                              UCT_STAGE_PARSER,
  2759.                              UCT_SETBY_DEFAULT);
  2760.         }
  2761.         if (!strcmp(p_out->MIMEname, "x-transparent")) {
  2762.             HTPassEightBitRaw = TRUE;
  2763.             HTAnchor_setUCInfoStage(me->node_anchor,
  2764.                 HTAnchor_getUCLYhndl(me->node_anchor,
  2765.                              UCT_STAGE_PARSER),
  2766.                         UCT_STAGE_HTEXT,
  2767.                         UCT_SETBY_DEFAULT);
  2768.         }
  2769.         if (p_in->enc != UCT_ENC_CJK) {
  2770.             HTCJK = NOCJK;
  2771.             if (!(p_in->codepoints &
  2772.               UCT_CP_SUBSETOF_LAT1) &&
  2773.             chndl == current_char_set) {
  2774.             HTPassEightBitRaw = TRUE;
  2775.             }
  2776.         } else if (p_out->enc == UCT_ENC_CJK) {
  2777.             if (LYRawMode) {
  2778.             if ((!strcmp(p_in->MIMEname, "euc-jp") ||
  2779.                  !strcmp(p_in->MIMEname, "shift_jis")) &&
  2780.                 (!strcmp(p_out->MIMEname, "euc-jp") ||
  2781.                  !strcmp(p_out->MIMEname, "shift_jis"))) {
  2782.                 HTCJK = JAPANESE;
  2783.             } else if (!strcmp(p_in->MIMEname, "euc-cn") &&
  2784.                    !strcmp(p_out->MIMEname, "euc-cn")) {
  2785.                 HTCJK = CHINESE;
  2786.             } else if (!strcmp(p_in->MIMEname, "big-5") &&
  2787.                    !strcmp(p_out->MIMEname, "big-5")) {
  2788.                 HTCJK = TAIPEI;
  2789.             } else if (!strcmp(p_in->MIMEname, "euc-kr") &&
  2790.                    !strcmp(p_out->MIMEname, "euc-kr")) {
  2791.                 HTCJK = KOREAN;
  2792.             } else {
  2793.                 HTCJK = NOCJK;
  2794.             }
  2795.             } else {
  2796.             HTCJK = NOCJK;
  2797.             }
  2798.         }
  2799.         LYGetChartransInfo(me);
  2800.         /*
  2801.          *    Fall through to old behavior.
  2802.          */
  2803.         } else if (!strncmp(cp1, "us-ascii", 8) ||
  2804.                !strncmp(cp1, "iso-8859-1", 10)) {
  2805.         StrAllocCopy(me->node_anchor->charset, "iso-8859-1");
  2806.         HTCJK = NOCJK;
  2807.  
  2808.         /*
  2809.          *  Hope it's a match, for now. - FM
  2810.          */
  2811.         cp1 = &cp4[10];
  2812.         while (*cp1 &&
  2813.                isdigit((unsigned char)(*cp1)))
  2814.             cp1++;
  2815.         *cp1 = '\0';
  2816.         StrAllocCopy(me->node_anchor->charset, cp4);
  2817.         HTPassEightBitRaw = TRUE;
  2818.         HTAlert(me->node_anchor->charset);
  2819.  
  2820.         } else if (!strncmp(cp1, "euc-jp", 6) && HTCJK == JAPANESE) {
  2821.         StrAllocCopy(me->node_anchor->charset, "euc-jp");
  2822.  
  2823.         } else if (!strncmp(cp1, "shift_jis", 9) && HTCJK == JAPANESE) {
  2824.         StrAllocCopy(me->node_anchor->charset, "shift_jis");
  2825.  
  2826.         } else if (!strncmp(cp1, "iso-2022-jp", 11) &&
  2827.                 HTCJK == JAPANESE) {
  2828.         StrAllocCopy(me->node_anchor->charset, "iso-2022-jp");
  2829.  
  2830.         } else if (!strncmp(cp1, "iso-2022-jp-2", 13) &&
  2831.                 HTCJK == JAPANESE) {
  2832.         StrAllocCopy(me->node_anchor->charset, "iso-2022-jp-2");
  2833.  
  2834.         } else if (!strncmp(cp1, "euc-kr", 6) && HTCJK == KOREAN) {
  2835.         StrAllocCopy(me->node_anchor->charset, "euc-kr");
  2836.  
  2837.         } else if (!strncmp(cp1, "iso-2022-kr", 11) && HTCJK == KOREAN) {
  2838.         StrAllocCopy(me->node_anchor->charset, "iso-2022-kr");
  2839.  
  2840.         } else if ((!strncmp(cp1, "big5", 4) ||
  2841.             !strncmp(cp1, "cn-big5", 7)) &&
  2842.                HTCJK == TAIPEI) {
  2843.         StrAllocCopy(me->node_anchor->charset, "big5");
  2844.  
  2845.         } else if (!strncmp(cp1, "euc-cn", 6) && HTCJK == CHINESE) {
  2846.         StrAllocCopy(me->node_anchor->charset, "euc-cn");
  2847.  
  2848.         } else if ((!strncmp(cp1, "gb2312", 6) ||
  2849.             !strncmp(cp1, "cn-gb", 5)) &&
  2850.                HTCJK == CHINESE) {
  2851.         StrAllocCopy(me->node_anchor->charset, "gb2312");
  2852.  
  2853.         } else if (!strncmp(cp1, "iso-2022-cn", 11) && HTCJK == CHINESE) {
  2854.         StrAllocCopy(me->node_anchor->charset, "iso-2022-cn");
  2855.         }
  2856.         FREE(cp3);
  2857.  
  2858.         if (me->node_anchor->charset) {
  2859.         CTRACE(tfp,
  2860.             "LYHandleMETA: New charset: %s\n",
  2861.             me->node_anchor->charset);
  2862.         }
  2863.     }
  2864.     /*
  2865.      *  Set the kcode element based on the charset. - FM
  2866.      */
  2867.     HText_setKcode(me->text, me->node_anchor->charset, p_in);
  2868.  
  2869.     /*
  2870.      *    Check for a Refresh directive. - FM
  2871.      */
  2872.     } else if (!strcasecomp((http_equiv ? http_equiv : ""), "Refresh")) {
  2873.     char *Seconds = NULL;
  2874.  
  2875.     /*
  2876.      *  Look for the Seconds field. - FM
  2877.      */
  2878.     cp = LYSkipBlanks(content);
  2879.     if (*cp && isdigit(*cp)) {
  2880.         cp1 = cp;
  2881.         while (*cp1 && isdigit(*cp1))
  2882.         cp1++;
  2883.         if (*cp1)
  2884.         *cp1++ = '\0';
  2885.         StrAllocCopy(Seconds, cp);
  2886.     }
  2887.     if (Seconds) {
  2888.         /*
  2889.          *    We have the seconds field.
  2890.          *    Now look for a URL field - FM
  2891.          */
  2892.         while (*cp1) {
  2893.         if (!strncasecomp(cp1, "URL", 3)) {
  2894.             cp = (cp1 + 3);
  2895.             while (*cp && (*cp == '=' || isspace((unsigned char)*cp)))
  2896.             cp++;
  2897.             cp1 = cp;
  2898.             while (*cp1 && !isspace((unsigned char)*cp1))
  2899.             cp1++;
  2900.             *cp1 = '\0';
  2901.             if (*cp)
  2902.             StrAllocCopy(href, cp);
  2903.             break;
  2904.         }
  2905.         cp1++;
  2906.         }
  2907.         if (href) {
  2908.         /*
  2909.          *  We found a URL field, so check it out. - FM
  2910.          */
  2911.         if (!(url_type = LYLegitimizeHREF(me, (char**)&href,
  2912.                           TRUE, FALSE))) {
  2913.             /*
  2914.              *    The specs require a complete URL,
  2915.              *    but this is a Netscapism, so don't
  2916.              *    expect the author to know that. - FM
  2917.              */
  2918.             HTAlert(REFRESH_URL_NOT_ABSOLUTE);
  2919.             /*
  2920.              *    Use the document's address
  2921.              *    as the base. - FM
  2922.              */
  2923.             if (*href != '\0') {
  2924.             temp = HTParse(href,
  2925.                        me->node_anchor->address, PARSE_ALL);
  2926.             StrAllocCopy(href, temp);
  2927.             FREE(temp);
  2928.             } else {
  2929.             StrAllocCopy(href, me->node_anchor->address);
  2930.             HText_setNoCache(me->text);
  2931.             }
  2932.         }
  2933.         /*
  2934.          *  Check whether to fill in localhost. - FM
  2935.          */
  2936.         LYFillLocalFileURL((char **)&href,
  2937.                    (me->inBASE ?
  2938.                  me->base_href : me->node_anchor->address));
  2939.         /*
  2940.          *  Set the no_cache flag if the Refresh URL
  2941.          *  is the same as the document's address. - FM
  2942.          */
  2943.         if (!strcmp(href, me->node_anchor->address)) {
  2944.             HText_setNoCache(me->text);
  2945.         }
  2946.         } else {
  2947.         /*
  2948.          *  We didn't find a URL field, so use
  2949.          *  the document's own address and set
  2950.          *  the no_cache flag. - FM
  2951.          */
  2952.         StrAllocCopy(href, me->node_anchor->address);
  2953.         HText_setNoCache(me->text);
  2954.         }
  2955.         /*
  2956.          *    Check for an anchor in http or https URLs. - FM
  2957.          */
  2958.         if ((strncmp(href, "http", 4) == 0) &&
  2959.         (cp = strrchr(href, '#')) != NULL) {
  2960.         StrAllocCopy(id_string, cp);
  2961.         *cp = '\0';
  2962.         }
  2963.         if (me->inA) {
  2964.         /*
  2965.          *  Ugh!  The META tag, which is a HEAD element,
  2966.          *  is in an Anchor, which is BODY element.  All
  2967.          *  we can do is close the Anchor and cross our
  2968.          *  fingers. - FM
  2969.          */
  2970.         if (me->inBoldA == TRUE && me->inBoldH == FALSE)
  2971.             HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
  2972.         me->inBoldA = FALSE;
  2973.         HText_endAnchor(me->text, me->CurrentANum);
  2974.         me->inA = FALSE;
  2975.         me->CurrentANum = 0;
  2976.         }
  2977.         me->CurrentA = HTAnchor_findChildAndLink(
  2978.                 me->node_anchor,    /* Parent */
  2979.                 id_string,        /* Tag */
  2980.                 href,            /* Addresss */
  2981.                 (void *)0);        /* Type */
  2982.         if (id_string)
  2983.         *cp = '#';
  2984.         FREE(id_string);
  2985.         LYEnsureSingleSpace(me);
  2986.         if (me->inUnderline == FALSE)
  2987.         HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
  2988.         HTML_put_string(me, "REFRESH(");
  2989.         HTML_put_string(me, Seconds);
  2990.         HTML_put_string(me, " sec):");
  2991.         FREE(Seconds);
  2992.         if (me->inUnderline == FALSE)
  2993.         HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
  2994.         HTML_put_character(me, ' ');
  2995.         me->in_word = NO;
  2996.         HText_beginAnchor(me->text, me->inUnderline, me->CurrentA);
  2997.         if (me->inBoldH == FALSE)
  2998.         HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
  2999.         HTML_put_string(me, href);
  3000.         FREE(href);
  3001.         if (me->inBoldH == FALSE)
  3002.         HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
  3003.         HText_endAnchor(me->text, 0);
  3004.         LYEnsureSingleSpace(me);
  3005.     }
  3006.  
  3007.     /*
  3008.      *    Check for a suggested filename via a Content-Disposition with
  3009.      *    a filename=name.suffix in it, if we don't already have it
  3010.      *    via a server header. - FM
  3011.      */
  3012.     } else if (!(me->node_anchor->SugFname && *me->node_anchor->SugFname) &&
  3013.            !strcasecomp((http_equiv ?
  3014.                  http_equiv : ""), "Content-Disposition")) {
  3015.     cp = content;
  3016.     while (*cp != '\0' && strncasecomp(cp, "filename", 8))
  3017.         cp++;
  3018.     if (*cp != '\0') {
  3019.         cp += 8;
  3020.         while ((*cp != '\0') && (WHITE(*cp) || *cp == '='))
  3021.         cp++;
  3022.         while (*cp != '\0' && WHITE(*cp))
  3023.         cp++;
  3024.         if (*cp != '\0') {
  3025.         StrAllocCopy(me->node_anchor->SugFname, cp);
  3026.         if (*me->node_anchor->SugFname == '\"') {
  3027.             if ((cp = strchr((me->node_anchor->SugFname + 1),
  3028.                      '\"')) != NULL) {
  3029.             *(cp + 1) = '\0';
  3030.             HTMIME_TrimDoubleQuotes(me->node_anchor->SugFname);
  3031.             } else {
  3032.             FREE(me->node_anchor->SugFname);
  3033.             }
  3034.             if (me->node_anchor->SugFname != NULL &&
  3035.             *me->node_anchor->SugFname == '\0') {
  3036.             FREE(me->node_anchor->SugFname);
  3037.             }
  3038.         }
  3039.         if ((cp = me->node_anchor->SugFname) != NULL) {
  3040.             while (*cp != '\0' && !WHITE(*cp))
  3041.             cp++;
  3042.             *cp = '\0';
  3043.             if (*me->node_anchor->SugFname == '\0')
  3044.             FREE(me->node_anchor->SugFname);
  3045.         }
  3046.         }
  3047.     }
  3048.     /*
  3049.      *    Check for a Set-Cookie directive. - AK
  3050.      */
  3051.     } else if (!strcasecomp((http_equiv ? http_equiv : ""), "Set-Cookie")) {
  3052.     /*
  3053.      *  This will need to be updated when Set-Cookie/Set-Cookie2
  3054.      *  handling is finalized.  For now, we'll still assume
  3055.      *  "historical" cookies in META directives. - FM
  3056.      */
  3057.     url_type = is_url(me->inBASE ?
  3058.                me->base_href : me->node_anchor->address);
  3059.     if (url_type == HTTP_URL_TYPE || url_type == HTTPS_URL_TYPE) {
  3060.         LYSetCookie(content,
  3061.             NULL,
  3062.             (me->inBASE ?
  3063.               me->base_href : me->node_anchor->address));
  3064.     }
  3065.     }
  3066.  
  3067.     /*
  3068.      *    Free the copies. - FM
  3069.      */
  3070. free_META_copies:
  3071.     FREE(http_equiv);
  3072.     FREE(name);
  3073.     FREE(content);
  3074. }
  3075.  
  3076. /*
  3077. **  This function handles P elements in HTML streams.
  3078. **  If start is TRUE it handles a start tag, and if
  3079. **  FALSE, an end tag.    We presently handle start
  3080. **  and end tags identically, but this can lead to
  3081. **  a different number of blank lines between the
  3082. **  current paragraph and subsequent text when a P
  3083. **  end tag is present or not in the markup. - FM
  3084. */
  3085. PUBLIC void LYHandleP ARGS5(
  3086.     HTStructured *,     me,
  3087.     CONST BOOL*,        present,
  3088.     CONST char **,        value,
  3089.     char **,        include,
  3090.     BOOL,            start)
  3091. {
  3092.     if (TRUE) {
  3093.     /*
  3094.      *  FIG content should be a true block, which like P inherits
  3095.      *  the current style.    APPLET is like character elements or
  3096.      *  an ALT attribute, unless it content contains a block element.
  3097.      *  If we encounter a P in either's content, we set flags to treat
  3098.      *  the content as a block.  - FM
  3099.      */
  3100.     if (start) {
  3101.         if (me->inFIG)
  3102.         me->inFIGwithP = TRUE;
  3103.  
  3104.         if (me->inAPPLET)
  3105.         me->inAPPLETwithP = TRUE;
  3106.     }
  3107.  
  3108.     UPDATE_STYLE;
  3109.     if (me->List_Nesting_Level >= 0) {
  3110.         /*
  3111.          *    We're in a list.  Treat P as an instruction to
  3112.          *    create one blank line, if not already present,
  3113.          *    then fall through to handle attributes, with
  3114.          *    the "second line" margins. - FM
  3115.          */
  3116.         if (me->inP) {
  3117.         if (me->inFIG || me->inAPPLET ||
  3118.             me->inCAPTION || me->inCREDIT ||
  3119.             me->sp->style->spaceAfter > 0 ||
  3120.             (start && me->sp->style->spaceBefore > 0)) {
  3121.             LYEnsureDoubleSpace(me);
  3122.         } else {
  3123.             LYEnsureSingleSpace(me);
  3124.         }
  3125.         }
  3126.     } else if (me->sp[0].tag_number == HTML_ADDRESS) {
  3127.         /*
  3128.          *    We're in an ADDRESS. Treat P as an instruction
  3129.          *    to start a newline, if needed, then fall through
  3130.          *    to handle attributes. - FM
  3131.          */
  3132.         if (HText_LastLineSize(me->text, FALSE)) {
  3133.         HText_setLastChar(me->text, ' ');  /* absorb white space */
  3134.         HText_appendCharacter(me->text, '\r');
  3135.         }
  3136.     } else {
  3137.         if (start) {
  3138.         if (!(me->inLABEL && !me->inP)) {
  3139.             HText_appendParagraph(me->text);
  3140.         }
  3141.         } else if (me->sp->style->spaceAfter > 0) {
  3142.         LYEnsureDoubleSpace(me);
  3143.         } else {
  3144.         LYEnsureSingleSpace(me);
  3145.         }
  3146.         me->inLABEL = FALSE;
  3147.     }
  3148.     me->in_word = NO;
  3149.  
  3150.     if (LYoverride_default_alignment(me)) {
  3151.         me->sp->style->alignment = styles[me->sp[0].tag_number]->alignment;
  3152.     } else if (me->List_Nesting_Level >= 0 ||
  3153.            ((me->Division_Level < 0) &&
  3154.             (!strcmp(me->sp->style->name, "Normal") ||
  3155.              !strcmp(me->sp->style->name, "Preformatted")))) {
  3156.         me->sp->style->alignment = HT_LEFT;
  3157.     } else {
  3158.         me->sp->style->alignment = me->current_default_alignment;
  3159.     }
  3160.  
  3161.     if (start) {
  3162.         if (present && present[HTML_P_ALIGN] && value[HTML_P_ALIGN]) {
  3163.         if (!strcasecomp(value[HTML_P_ALIGN], "center") &&
  3164.             !(me->List_Nesting_Level >= 0 && !me->inP))
  3165.             me->sp->style->alignment = HT_CENTER;
  3166.         else if (!strcasecomp(value[HTML_P_ALIGN], "right") &&
  3167.             !(me->List_Nesting_Level >= 0 && !me->inP))
  3168.             me->sp->style->alignment = HT_RIGHT;
  3169.         else if (!strcasecomp(value[HTML_P_ALIGN], "left") ||
  3170.              !strcasecomp(value[HTML_P_ALIGN], "justify"))
  3171.             me->sp->style->alignment = HT_LEFT;
  3172.         }
  3173.  
  3174.         CHECK_ID(HTML_P_ID);
  3175.     }
  3176.  
  3177.     /*
  3178.      *  Mark that we are starting a new paragraph
  3179.      *  and don't have any of it's text yet. - FM
  3180.      *
  3181.      */
  3182.     me->inP = FALSE;
  3183.     }
  3184.  
  3185.     return;
  3186. }
  3187.  
  3188. /*
  3189. **  This function handles SELECT elements in HTML streams.
  3190. **  If start is TRUE it handles a start tag, and if FALSE,
  3191. **  an end tag. - FM
  3192. */
  3193. PUBLIC void LYHandleSELECT ARGS5(
  3194.     HTStructured *,     me,
  3195.     CONST BOOL*,        present,
  3196.     CONST char **,        value,
  3197.     char **,        include,
  3198.     BOOL,            start)
  3199. {
  3200.     int i;
  3201.  
  3202.     if (start == TRUE) {
  3203.     char *name = NULL;
  3204.     BOOLEAN multiple = NO;
  3205.     char *size = NULL;
  3206.  
  3207.     /*
  3208.      *  Initialize the disable attribute.
  3209.      */
  3210.     me->select_disabled = FALSE;
  3211.  
  3212.     /*
  3213.      *  Make sure we're in a form.
  3214.      */
  3215.     if (!me->inFORM) {
  3216.         if (TRACE) {
  3217.         fprintf(tfp,
  3218.             "Bad HTML: SELECT start tag not within FORM tag\n");
  3219.         } else if (!me->inBadHTML) {
  3220.         _statusline(BAD_HTML_USE_TRACE);
  3221.         me->inBadHTML = TRUE;
  3222.         sleep(MessageSecs);
  3223.         }
  3224.  
  3225.         /*
  3226.          *    We should have covered all crash possibilities with the
  3227.          *    current TagSoup parser, so we'll allow it because some
  3228.          *    people with other browsers use SELECT for "information"
  3229.          *    popups, outside of FORM blocks, though no Lynx user
  3230.          *    would do anything that awful, right? - FM
  3231.          *//***
  3232.         return;
  3233.         ***/
  3234.     }
  3235.  
  3236.     /*
  3237.      *  Check for unclosed TEXTAREA.
  3238.      */
  3239.     if (me->inTEXTAREA) {
  3240.         if (TRACE) {
  3241.         fprintf(tfp, "Bad HTML: Missing TEXTAREA end tag\n");
  3242.         } else if (!me->inBadHTML) {
  3243.         _statusline(BAD_HTML_USE_TRACE);
  3244.         me->inBadHTML = TRUE;
  3245.         sleep(MessageSecs);
  3246.         }
  3247.     }
  3248.  
  3249.     /*
  3250.      *  Set to know we are in a select tag.
  3251.      */
  3252.     me->inSELECT = TRUE;
  3253.  
  3254.     if (!(present && present[HTML_SELECT_NAME] &&
  3255.           value[HTML_SELECT_NAME]  && *value[HTML_SELECT_NAME])) {
  3256.         StrAllocCopy(name, "");
  3257.     } else if (strchr(value[HTML_SELECT_NAME], '&') == NULL) {
  3258.         StrAllocCopy(name, value[HTML_SELECT_NAME]);
  3259.     } else {
  3260.         StrAllocCopy(name, value[HTML_SELECT_NAME]);
  3261.         UNESCAPE_FIELDNAME_TO_STD(&name);
  3262.     }
  3263.     if (present && present[HTML_SELECT_MULTIPLE])
  3264.         multiple=YES;
  3265.     if (present && present[HTML_SELECT_DISABLED])
  3266.         me->select_disabled = TRUE;
  3267.     if (present && present[HTML_SELECT_SIZE] &&
  3268.         value[HTML_SELECT_SIZE] && *value[HTML_SELECT_SIZE]) {
  3269. #ifdef NOTDEFINED
  3270.         StrAllocCopy(size, value[HTML_SELECT_SIZE]);
  3271. #else
  3272.         /*
  3273.          *    Let the size be determined by the number of OPTIONs. - FM
  3274.          */
  3275.         CTRACE(tfp, "LYHandleSELECT: Ignoring SIZE=\"%s\" for SELECT.\n",
  3276.             value[HTML_SELECT_SIZE]);
  3277. #endif /* NOTDEFINED */
  3278.     }
  3279.  
  3280.     if (me->inBoldH == TRUE &&
  3281.         (multiple == NO || LYSelectPopups == FALSE)) {
  3282.         HText_appendCharacter(me->text, LY_BOLD_END_CHAR);
  3283.         me->inBoldH = FALSE;
  3284.         me->needBoldH = TRUE;
  3285.     }
  3286.     if (me->inUnderline == TRUE &&
  3287.         (multiple == NO || LYSelectPopups == FALSE)) {
  3288.         HText_appendCharacter(me->text, LY_UNDERLINE_END_CHAR);
  3289.         me->inUnderline = FALSE;
  3290.     }
  3291.  
  3292.     if ((multiple == NO && LYSelectPopups == TRUE) &&
  3293.         (me->sp[0].tag_number == HTML_PRE || me->inPRE == TRUE ||
  3294.          !me->sp->style->freeFormat) &&
  3295.         HText_LastLineSize(me->text, FALSE) > (LYcols - 8)) {
  3296.         /*
  3297.          *    Force a newline when we're using a popup in
  3298.          *    a PRE block and are within 7 columns from the
  3299.          *    right margin.  This will allow for the '['
  3300.          *    popup designator and help avoid a wrap in the
  3301.          *    underscore placeholder for the retracted popup
  3302.          *    entry in the HText structure. - FM
  3303.          */
  3304.         HTML_put_character(me, '\n');
  3305.         me->in_word = NO;
  3306.     }
  3307.  
  3308.     LYCheckForID(me, present, value, (int)HTML_SELECT_ID);
  3309.  
  3310.     HText_beginSelect(name, ATTR_CS_IN, multiple, size);
  3311.     FREE(name);
  3312.     FREE(size);
  3313.  
  3314.     me->first_option = TRUE;
  3315.     } else {
  3316.     /*
  3317.      *  Handle end tag.
  3318.      */
  3319.     char *ptr;
  3320.  
  3321.     /*
  3322.      *  Make sure we had a select start tag.
  3323.      */
  3324.     if (!me->inSELECT) {
  3325.         if (TRACE) {
  3326.         fprintf(tfp, "Bad HTML: Unmatched SELECT end tag\n");
  3327.         } else if (!me->inBadHTML) {
  3328.         _statusline(BAD_HTML_USE_TRACE);
  3329.         me->inBadHTML = TRUE;
  3330.         sleep(MessageSecs);
  3331.         }
  3332.         return;
  3333.     }
  3334.  
  3335.     /*
  3336.      *  Set to know that we are no longer in a select tag.
  3337.      */
  3338.     me->inSELECT = FALSE;
  3339.  
  3340.     /*
  3341.      *  Clear the disable attribute.
  3342.      */
  3343.     me->select_disabled = FALSE;
  3344.  
  3345.     /*
  3346.      *  Finish the data off.
  3347.      */
  3348.     HTChunkTerminate(&me->option);
  3349.     /*
  3350.      *  Finish the previous option.
  3351.      */
  3352.     ptr = HText_setLastOptionValue(me->text,
  3353.                        me->option.data,
  3354.                        me->LastOptionValue,
  3355.                        LAST_ORDER,
  3356.                        me->LastOptionChecked,
  3357.                        me->UCLYhndl,
  3358.                        ATTR_CS_IN);
  3359.     FREE(me->LastOptionValue);
  3360.  
  3361.     me->LastOptionChecked = FALSE;
  3362.  
  3363.     if (HTCurSelectGroupType == F_CHECKBOX_TYPE ||
  3364.         LYSelectPopups == FALSE) {
  3365.         /*
  3366.          *    Start a newline after the last checkbox/button option.
  3367.          */
  3368.         LYEnsureSingleSpace(me);
  3369.     } else {
  3370.         /*
  3371.          *    Output popup box with the default option to screen,
  3372.          *    but use non-breaking spaces for output.
  3373.          */
  3374.         if (ptr &&
  3375.         me->sp[0].tag_number == HTML_PRE && strlen(ptr) > 6) {
  3376.         /*
  3377.          *  The code inadequately handles OPTION fields in PRE tags.
  3378.          *  We'll put up a minimum of 6 characters, and if any
  3379.          *  more would exceed the wrap column, we'll ignore them.
  3380.          */
  3381.         for (i = 0; i < 6; i++) {
  3382.             if (*ptr == ' ')
  3383.             HText_appendCharacter(me->text, HT_NON_BREAK_SPACE);
  3384.             else
  3385.             HText_appendCharacter(me->text, *ptr);
  3386.             ptr++;
  3387.         }
  3388.         HText_setIgnoreExcess(me->text, TRUE);
  3389.         }
  3390.         for (; ptr && *ptr != '\0'; ptr++) {
  3391.         if (*ptr == ' ')
  3392.             HText_appendCharacter(me->text, HT_NON_BREAK_SPACE);
  3393.         else
  3394.             HText_appendCharacter(me->text, *ptr);
  3395.         }
  3396.         /*
  3397.          *    Add end option character.
  3398.          */
  3399.         if (!me->first_option) {
  3400.         HText_appendCharacter(me->text, ']');
  3401.         HText_setLastChar(me->text, ']');
  3402.         me->in_word = YES;
  3403.         }
  3404.         HText_setIgnoreExcess(me->text, FALSE);
  3405.     }
  3406.     HTChunkClear(&me->option);
  3407.  
  3408.     if (me->Underline_Level > 0 && me->inUnderline == FALSE) {
  3409.         HText_appendCharacter(me->text, LY_UNDERLINE_START_CHAR);
  3410.         me->inUnderline = TRUE;
  3411.     }
  3412.     if (me->needBoldH == TRUE && me->inBoldH == FALSE) {
  3413.         HText_appendCharacter(me->text, LY_BOLD_START_CHAR);
  3414.         me->inBoldH = TRUE;
  3415.         me->needBoldH = FALSE;
  3416.     }
  3417.     }
  3418. }
  3419.  
  3420. /*
  3421. **  This function strips white characters and
  3422. **  generally fixes up attribute values that
  3423. **  were received from the SGML parser and
  3424. **  are to be treated as partial or absolute
  3425. **  URLs. - FM
  3426. */
  3427. PUBLIC int LYLegitimizeHREF ARGS4(
  3428.     HTStructured *,     me,
  3429.     char **,        href,
  3430.     BOOL,            force_slash,
  3431.     BOOL,            strip_dots)
  3432. {
  3433.     int url_type = 0;
  3434.     char *pound = NULL;
  3435.     char *fragment = NULL;
  3436.  
  3437.     if (!me || !href || *href == NULL || *(*href) == '\0')
  3438.     return(url_type);
  3439.  
  3440.     LYTrimHead(*href);
  3441.     if (!strncasecomp(*href, "lynxexec:", 9) ||
  3442.     !strncasecomp(*href, "lynxprog:", 9)) {
  3443.     /*
  3444.      *  The original implementations of these schemes expected
  3445.      *  white space without hex escaping, and did not check
  3446.      *  for hex escaping, so we'll continue to support that,
  3447.      *  until that code is redone in conformance with SGML
  3448.      *  principles.  - FM
  3449.      */
  3450.     HTUnEscapeSome(*href, " \r\n\t");
  3451.     convert_to_spaces(*href, TRUE);
  3452.     } else {
  3453.     /*
  3454.      *  Collapse spaces in the actual URL, but just
  3455.      *  protect against tabs or newlines in the
  3456.      *  fragment, if present.  This seeks to cope
  3457.      *  with atrocities inflicted on the Web by
  3458.      *  authoring tools such as Frontpage. - FM
  3459.      */
  3460.     if ((pound = strchr(*href, '#')) != NULL) {
  3461.         StrAllocCopy(fragment, pound);
  3462.         *pound = '\0';
  3463.         convert_to_spaces(fragment, FALSE);
  3464.     }
  3465.     LYRemoveBlanks(*href);
  3466.     if (fragment != NULL) {
  3467.         StrAllocCat(*href, fragment);
  3468.         FREE(fragment);
  3469.     }
  3470.     }
  3471.     if (*(*href) == '\0')
  3472.     return(url_type);
  3473.     LYUCFullyTranslateString(href, me->tag_charset, me->tag_charset,
  3474.                  NO, NO, YES, st_URL);
  3475.     url_type = is_url(*href);
  3476.     if (!url_type && force_slash &&
  3477.     (!strcmp(*href, ".") || !strcmp(*href, "..")) &&
  3478.      strncmp((me->inBASE ?
  3479.            me->base_href : me->node_anchor->address),
  3480.          "file:", 5)) {
  3481.     /*
  3482.      *  The Fielding RFC/ID for resolving partial HREFs says
  3483.      *  that a slash should be on the end of the preceding
  3484.      *  symbolic element for "." and "..", but all tested
  3485.      *  browsers only do that for an explicit "./" or "../",
  3486.      *  so we'll respect the RFC/ID only if force_slash was
  3487.      *  TRUE and it's not a file URL. - FM
  3488.      */
  3489.     StrAllocCat(*href, "/");
  3490.     }
  3491.     if ((!url_type && LYStripDotDotURLs && strip_dots && *(*href) == '.') &&
  3492.      !strncasecomp((me->inBASE ?
  3493.              me->base_href : me->node_anchor->address),
  3494.                "http", 4)) {
  3495.     /*
  3496.      *  We will be resolving a partial reference versus an http
  3497.      *  or https URL, and it has lead dots, which may be retained
  3498.      *  when resolving via HTParse(), but the request would fail
  3499.      *  if the first element of the resultant path is two dots,
  3500.      *  because no http or https server accepts such paths, and
  3501.      *  the current URL draft, likely to become an RFC, says that
  3502.      *  it's optional for the UA to strip them as a form of error
  3503.      *  recovery.  So we will, recursively, for http/https URLs,
  3504.      *  like the "major market browsers" which made this problem
  3505.      *  so common on the Web, but we'll also issue a message about
  3506.      *  it, such that the bad partial reference might get corrected
  3507.      *  by the document provider. - FM
  3508.      */
  3509.     char *temp = NULL, *path = NULL, *str = "", *cp;
  3510.  
  3511.     if (((temp = HTParse(*href,
  3512.                  (me->inBASE ?
  3513.                me->base_href : me->node_anchor->address),
  3514.                  PARSE_ALL)) != NULL && temp[0] != '\0') &&
  3515.         (path = HTParse(temp, "",
  3516.                 PARSE_PATH+PARSE_PUNCTUATION)) != NULL &&
  3517.         !strncmp(path, "/..", 3)) {
  3518.         cp = (path + 3);
  3519.         if (*cp == '/' || *cp == '\0') {
  3520.         if ((me->inBASE ?
  3521.            me->base_href[4] : me->node_anchor->address[4]) == 's') {
  3522.             str = "s";
  3523.         }
  3524.         if (TRACE) {
  3525.             fprintf(tfp,
  3526.              "LYLegitimizeHREF: Bad value '%s' for http%s URL.\n",
  3527.                *href, str);
  3528.             fprintf(tfp,
  3529.              "                  Stripping lead dots.\n");
  3530.         } else if (!me->inBadHREF) {
  3531.             _statusline(BAD_PARTIAL_REFERENCE);
  3532.             me->inBadHREF = TRUE;
  3533.             sleep(AlertSecs);
  3534.         }
  3535.         }
  3536.         if (*cp == '\0') {
  3537.         StrAllocCopy(*href, "/");
  3538.         } else if (*cp == '/') {
  3539.         while (!strncmp(cp, "/..", 3)) {
  3540.             if (*(cp + 3) == '/') {
  3541.             cp += 3;
  3542.             continue;
  3543.             } else if (*(cp + 3) == '\0') {
  3544.             *(cp + 1) = '\0';
  3545.             *(cp + 2) = '\0';
  3546.             }
  3547.             break;
  3548.         }
  3549.         StrAllocCopy(*href, cp);
  3550.         }
  3551.     }
  3552.     FREE(temp);
  3553.     FREE(path);
  3554.     }
  3555.     return(url_type);
  3556. }
  3557.  
  3558. /*
  3559. **  This function checks for a Content-Base header,
  3560. **  and if not present, a Content-Location header
  3561. **  which is an absolute URL, and sets the BASE
  3562. **  accordingly.  If set, it will be replaced by
  3563. **  any BASE tag in the HTML stream, itself. - FM
  3564. */
  3565. PUBLIC void LYCheckForContentBase ARGS1(
  3566.     HTStructured *,     me)
  3567. {
  3568.     char *cp = NULL;
  3569.     BOOL present[HTML_BASE_ATTRIBUTES];
  3570.     CONST char *value[HTML_BASE_ATTRIBUTES];
  3571.     int i;
  3572.  
  3573.     if (!(me && me->node_anchor))
  3574.     return;
  3575.  
  3576.     if (me->node_anchor->content_base != NULL) {
  3577.     /*
  3578.      *  We have a Content-Base value.  Use it
  3579.      *  if it's non-zero length. - FM
  3580.      */
  3581.     if (*me->node_anchor->content_base == '\0')
  3582.         return;
  3583.     StrAllocCopy(cp, me->node_anchor->content_base);
  3584.     LYRemoveBlanks(cp);
  3585.     } else if (me->node_anchor->content_location != NULL) {
  3586.     /*
  3587.      *  We didn't have a Content-Base value, but do
  3588.      *  have a Content-Location value.  Use it if
  3589.      *  it's an absolute URL. - FM
  3590.      */
  3591.     if (*me->node_anchor->content_location == '\0')
  3592.         return;
  3593.     StrAllocCopy(cp, me->node_anchor->content_location);
  3594.     LYRemoveBlanks(cp);
  3595.     if (!is_url(cp)) {
  3596.         FREE(cp);
  3597.         return;
  3598.     }
  3599.     } else {
  3600.     /*
  3601.      *  We had neither a Content-Base nor
  3602.      *  Content-Location value. - FM
  3603.      */
  3604.     return;
  3605.     }
  3606.  
  3607.     /*
  3608.      *    If we collapsed to a zero-length value,
  3609.      *    ignore it. - FM
  3610.      */
  3611.     if (*cp == '\0') {
  3612.     FREE(cp);
  3613.     return;
  3614.     }
  3615.  
  3616.     /*
  3617.      *    Pass the value to HTML_start_element as
  3618.      *    the HREF of a BASE tag. - FM
  3619.      */
  3620.     for (i = 0; i < HTML_BASE_ATTRIBUTES; i++)
  3621.      present[i] = NO;
  3622.     present[HTML_BASE_HREF] = YES;
  3623.     value[HTML_BASE_HREF] = (CONST char *)cp;
  3624.     (*me->isa->start_element)(me, HTML_BASE, present, value,
  3625.                   0, 0);
  3626.     FREE(cp);
  3627. }
  3628.  
  3629. /*
  3630. **  This function creates NAMEd Anchors if a non-zero-length NAME
  3631. **  or ID attribute was present in the tag. - FM
  3632. */
  3633. PUBLIC void LYCheckForID ARGS4(
  3634.     HTStructured *,     me,
  3635.     CONST BOOL *,        present,
  3636.     CONST char **,        value,
  3637.     int,            attribute)
  3638. {
  3639.     HTChildAnchor *ID_A = NULL;
  3640.     char *temp = NULL;
  3641.  
  3642.     if (!(me && me->text))
  3643.     return;
  3644.  
  3645.     if (present && present[attribute]
  3646.     && value[attribute] && *value[attribute]) {
  3647.     /*
  3648.      *  Translate any named or numeric character references. - FM
  3649.      */
  3650.     StrAllocCopy(temp, value[attribute]);
  3651.     LYUCFullyTranslateString(&temp, me->tag_charset, me->tag_charset,
  3652.                  NO, NO, YES, st_URL);
  3653.  
  3654.     /*
  3655.      *  Create the link if we still have a non-zero-length string. - FM
  3656.      */
  3657.     if ((temp[0] != '\0') &&
  3658.         (ID_A = HTAnchor_findChildAndLink(
  3659.                 me->node_anchor,    /* Parent */
  3660.                 temp,            /* Tag */
  3661.                 NULL,            /* Addresss */
  3662.                 (void *)0))) {        /* Type */
  3663.         HText_beginAnchor(me->text, me->inUnderline, ID_A);
  3664.         HText_endAnchor(me->text, 0);
  3665.     }
  3666.     FREE(temp);
  3667.     }
  3668. }
  3669.  
  3670. /*
  3671. **  This function creates a NAMEd Anchor for the ID string
  3672. **  passed to it directly as an argument.  It assumes the
  3673. **  does not need checking for character references. - FM
  3674. */
  3675. PUBLIC void LYHandleID ARGS2(
  3676.     HTStructured *,     me,
  3677.     char *,         id)
  3678. {
  3679.     HTChildAnchor *ID_A = NULL;
  3680.  
  3681.     if (!(me && me->text) ||
  3682.     !(id && *id))
  3683.     return;
  3684.  
  3685.     /*
  3686.      *    Create the link if we still have a non-zero-length string. - FM
  3687.      */
  3688.     if ((ID_A = HTAnchor_findChildAndLink(
  3689.                 me->node_anchor,    /* Parent */
  3690.                 id,            /* Tag */
  3691.                 NULL,            /* Addresss */
  3692.                 (void *)0)) != NULL) {    /* Type */
  3693.     HText_beginAnchor(me->text, me->inUnderline, ID_A);
  3694.     HText_endAnchor(me->text, 0);
  3695.     }
  3696. }
  3697.  
  3698. /*
  3699. **  This function checks whether we want to override
  3700. **  the current default alignment for paragraphs and
  3701. **  instead use that specified in the element's style
  3702. **  sheet. - FM
  3703. */
  3704. PUBLIC BOOLEAN LYoverride_default_alignment ARGS1(
  3705.     HTStructured *, me)
  3706. {
  3707.     if (!me)
  3708.     return NO;
  3709.  
  3710.     switch(me->sp[0].tag_number) {
  3711.     case HTML_BLOCKQUOTE:
  3712.     case HTML_BQ:
  3713.     case HTML_NOTE:
  3714.     case HTML_FN:
  3715.     case HTML_ADDRESS:
  3716.         me->sp->style->alignment = HT_LEFT;
  3717.         return YES;
  3718.         break;
  3719.  
  3720.     default:
  3721.         break;
  3722.     }
  3723.     return NO;
  3724. }
  3725.  
  3726. /*
  3727. **  This function inserts newlines if needed to create double spacing,
  3728. **  and sets the left margin for subsequent text to the second line
  3729. **  indentation of the current style. - FM
  3730. */
  3731. PUBLIC void LYEnsureDoubleSpace ARGS1(
  3732.     HTStructured *, me)
  3733. {
  3734.     if (!me || !me->text)
  3735.     return;
  3736.  
  3737.     if (HText_LastLineSize(me->text, FALSE)) {
  3738.     HText_setLastChar(me->text, ' ');  /* absorb white space */
  3739.     HText_appendCharacter(me->text, '\r');
  3740.     HText_appendCharacter(me->text, '\r');
  3741.     } else if (HText_PreviousLineSize(me->text, FALSE)) {
  3742.     HText_setLastChar(me->text, ' ');  /* absorb white space */
  3743.     HText_appendCharacter(me->text, '\r');
  3744.     } else if (me->List_Nesting_Level >= 0) {
  3745.     HText_NegateLineOne(me->text);
  3746.     }
  3747.     me->in_word = NO;
  3748.     return;
  3749. }
  3750.  
  3751. /*
  3752. **  This function inserts a newline if needed to create single spacing,
  3753. **  and sets the left margin for subsequent text to the second line
  3754. **  indentation of the current style. - FM
  3755. */
  3756. PUBLIC void LYEnsureSingleSpace ARGS1(
  3757.     HTStructured *, me)
  3758. {
  3759.     if (!me || !me->text)
  3760.     return;
  3761.  
  3762.     if (HText_LastLineSize(me->text, FALSE)) {
  3763.     HText_setLastChar(me->text, ' ');  /* absorb white space */
  3764.     HText_appendCharacter(me->text, '\r');
  3765.     } else if (me->List_Nesting_Level >= 0) {
  3766.     HText_NegateLineOne(me->text);
  3767.     }
  3768.     me->in_word = NO;
  3769.     return;
  3770. }
  3771.  
  3772. /*
  3773. **  This function resets paragraph alignments for block
  3774. **  elements which do not have a defined style sheet. - FM
  3775. */
  3776. PUBLIC void LYResetParagraphAlignment ARGS1(
  3777.     HTStructured *, me)
  3778. {
  3779.     if (!me)
  3780.     return;
  3781.  
  3782.     if (me->List_Nesting_Level >= 0 ||
  3783.     ((me->Division_Level < 0) &&
  3784.      (!strcmp(me->sp->style->name, "Normal") ||
  3785.       !strcmp(me->sp->style->name, "Preformatted")))) {
  3786.     me->sp->style->alignment = HT_LEFT;
  3787.     } else {
  3788.     me->sp->style->alignment = me->current_default_alignment;
  3789.     }
  3790.     return;
  3791. }
  3792.  
  3793. /*
  3794. **  This example function checks whether the given anchor has
  3795. **  an address with a file scheme, and if so, loads it into the
  3796. **  the SGML parser's context->url element, which was passed as
  3797. **  the second argument.  The handle_comment() calling function in
  3798. **  SGML.c then calls LYDoCSI() in LYUtils.c to insert HTML markup
  3799. **  into the corresponding stream, homologously to an SSI by an
  3800. **  HTTP server. - FM
  3801. **
  3802. **  For functions similar to this but which depend on details of
  3803. **  the HTML handler's internal data, the calling interface should
  3804. **  be changed, and functions in SGML.c would have to make sure not
  3805. **  to call such functions inappropriately (e.g. calling a function
  3806. **  specific to the Lynx_HTML_Handler when SGML.c output goes to
  3807. **  some other HTStructured object like in HTMLGen.c), or the new
  3808. **  functions could be added to the SGML.h interface.
  3809. */
  3810. PUBLIC BOOLEAN LYCheckForCSI ARGS2(
  3811.     HTParentAnchor *,    anchor,
  3812.     char **,        url)
  3813. {
  3814.     if (!(anchor && anchor->address))
  3815.     return FALSE;
  3816.  
  3817.     if (strncasecomp(anchor->address, "file:", 5))
  3818.     return FALSE;
  3819.  
  3820.     if (!LYisLocalHost(anchor->address))
  3821.     return FALSE;
  3822.  
  3823.     StrAllocCopy(*url, anchor->address);
  3824.     return TRUE;
  3825. }
  3826.