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 / LYUtils.c < prev    next >
C/C++ Source or Header  |  1998-05-10  |  137KB  |  5,522 lines

  1. #include <HTUtils.h>
  2. #include <tcp.h>
  3. #include <ctype.h>
  4. #include <HTParse.h>
  5. #include <HTAccess.h>
  6. #include <HTCJK.h>
  7. #include <HTAlert.h>
  8. #include <LYCurses.h>
  9. #include <LYUtils.h>
  10. #include <LYStrings.h>
  11. #include <LYGlobalDefs.h>
  12. #include <LYSignal.h>
  13. #include <GridText.h>
  14. #include <LYCharSets.h>
  15.  
  16. #ifdef DOSPATH
  17. #include <HTDOS.h>
  18. #endif
  19. #ifdef DISP_PARTIAL
  20. #include <LYKeymap.h>
  21. #endif /* DISP_PARTIAL */
  22. #ifdef VMS
  23. #include <descrip.h>
  24. #include <libclidef.h>
  25. #include <lib$routines.h>
  26. #include <HTVMSUtils.h>
  27. #endif /* VMS */
  28.  
  29. #if HAVE_UTMP
  30. #include <pwd.h>
  31. #ifdef UTMPX_FOR_UTMP
  32. #include <utmpx.h>
  33. #define utmp utmpx
  34. #ifdef UTMP_FILE
  35. #undef UTMP_FILE
  36. #endif /* UTMP_FILE */
  37. #define UTMP_FILE UTMPX_FILE
  38. #else
  39. #include <utmp.h>
  40. #endif /* UTMPX_FOR_UTMP */
  41. #endif /* HAVE_UTMP */
  42.  
  43. #if NEED_PTEM_H
  44. /* they neglected to define struct winsize in termios.h -- it's only in
  45.  * termio.h and ptem.h (the former conflicts with other definitions).
  46.  */
  47. #include    <sys/stream.h>
  48. #include    <sys/ptem.h>
  49. #endif
  50.  
  51. #include <LYLeaks.h>
  52.  
  53. #ifdef USE_COLOR_STYLE
  54. #include <AttrList.h>
  55. #include <LYHash.h>
  56. #include <LYStyle.h>
  57. #endif
  58.  
  59. #undef hline   /* FIXME: this is a curses feature used as a variable here */
  60.  
  61. #ifdef SVR4_BSDSELECT
  62. extern int BSDselect PARAMS((int nfds, fd_set * readfds, fd_set * writefds,
  63.                  fd_set * exceptfds, struct timeval * timeout));
  64. #ifdef select
  65. #undef select
  66. #endif /* select */
  67. #define select BSDselect
  68. #ifdef SOCKS
  69. #ifdef Rselect
  70. #undef Rselect
  71. #endif /* Rselect */
  72. #define Rselect BSDselect
  73. #endif /* SOCKS */
  74. #endif /* SVR4_BSDSELECT */
  75.  
  76. #ifndef FD_SETSIZE
  77. #define FD_SETSIZE 256
  78. #endif /* !FD_SETSIZE */
  79.  
  80. #ifndef UTMP_FILE
  81. #if defined(__FreeBSD__) || defined(__bsdi__)
  82. #define UTMP_FILE _PATH_UTMP
  83. #else
  84. #define UTMP_FILE "/etc/utmp"
  85. #endif /* __FreeBSD__ || __bsdi__ */
  86. #endif /* !UTMP_FILE */
  87.  
  88. #define FREE(x) if (x) {free(x); x = NULL;}
  89.  
  90. extern HTkcode kanji_code;
  91. extern BOOLEAN LYHaveCJKCharacterSet;
  92. extern HTCJKlang HTCJK;
  93.  
  94. PRIVATE HTList * localhost_aliases = NULL;    /* Hosts to treat as local */
  95. PRIVATE char *HomeDir = NULL;            /* HOME directory */
  96. PUBLIC    HTList * sug_filenames = NULL;        /* Suggested filenames     */
  97.  
  98. /*
  99.  *  Highlight (or unhighlight) a given link.
  100.  */
  101. PUBLIC void highlight ARGS3(
  102.     int,        flag,
  103.     int,        cur,
  104.     char *,     target)
  105. {
  106.     char buffer[200];
  107.     int i;
  108.     char tmp[7], *cp;
  109.     char *theData = NULL;
  110.     char *Data = NULL;
  111.     int Offset, HitOffset, tLen;
  112.     int LenNeeded;
  113.     BOOL TargetEmphasisON = FALSE;
  114.     BOOL utf_flag = (LYCharSet_UC[current_char_set].enc == UCT_ENC_UTF8);
  115.  
  116.     tmp[0] = tmp[1] = tmp[2] = '\0';
  117.  
  118.     /*
  119.      *    Bugs in the history code might cause -1 to be sent for cur, which
  120.      *    yields a crash when LYstrncpy() is called with a nonsense pointer.
  121.      *    As far as I know, such bugs have been squashed, but if they should
  122.      *    reappear, this works around them. - FM
  123.      */
  124.     if (cur < 0)
  125.     cur = 0;
  126.  
  127.     if (nlinks > 0) {
  128. #ifdef USE_COLOR_STYLE
  129. #define LXP (links[cur].lx)
  130. #define LYP (links[cur].ly)
  131. #endif
  132.     move(links[cur].ly, links[cur].lx);
  133. #ifndef USE_COLOR_STYLE
  134.     lynx_start_link_color (flag == ON, links[cur].inUnderline);
  135. #else
  136.     if (flag == ON) {
  137.         LynxChangeStyle(s_alink, ABS_ON, 0);
  138.     } else {
  139.         /* the logic is flawed here - no provision is made for links that
  140.         ** aren't coloured as [s_a] by default - rjp
  141.         */
  142.         if (LYP >= 0 && LYP < CACHEH && LXP >= 0 && LXP < CACHEW &&
  143.         cached_styles[LYP][LXP]) {
  144.         LynxChangeStyle(cached_styles[LYP][LXP], ABS_ON, 0);
  145.         }
  146.         else {
  147.         LynxChangeStyle(s_a, ABS_ON, 0);
  148.         }
  149.     }
  150. #endif
  151.  
  152.     if (links[cur].type == WWW_FORM_LINK_TYPE) {
  153.         int len;
  154.         int avail_space = (LYcols - links[cur].lx) - 1;
  155.  
  156.         LYstrncpy(buffer,
  157.               (links[cur].hightext ?
  158.                links[cur].hightext : ""),
  159.               (avail_space > links[cur].form->size ?
  160.                       links[cur].form->size : avail_space));
  161.         addstr(buffer);
  162.  
  163.         len = strlen(buffer);
  164.         for (; len < links[cur].form->size && len < avail_space; len++)
  165.         addch('_');
  166.  
  167.     } else {
  168.         /*
  169.          *    Copy into the buffer only what will fit
  170.          *    within the width of the screen.
  171.          */
  172.         LYmbcsstrncpy(buffer,
  173.               (links[cur].hightext ?
  174.                links[cur].hightext : ""),
  175.               (sizeof(buffer) - 1),
  176.               ((LYcols - 1) - links[cur].lx),
  177.               utf_flag);
  178.         addstr(buffer);
  179.     }
  180.  
  181.     /*
  182.      *  Display a second line as well.
  183.      */
  184.     if (links[cur].hightext2 && links[cur].ly < display_lines) {
  185.         lynx_stop_link_color (flag == ON, links[cur].inUnderline);
  186.         move((links[cur].ly + 1), links[cur].hightext2_offset);
  187. #ifndef USE_COLOR_STYLE
  188.         lynx_start_link_color (flag == ON, links[cur].inUnderline);
  189. #else
  190.         LynxChangeStyle(flag == ON ? s_alink : s_a, ABS_ON, 0);
  191. #endif
  192.  
  193.         for (i = 0; (tmp[0] = links[cur].hightext2[i]) != '\0' &&
  194.             i+links[cur].hightext2_offset < LYcols; i++) {
  195.         if (!IsSpecialAttrChar(links[cur].hightext2[i])) {
  196.             /*
  197.              *    For CJK strings, by Masanobu Kimura.
  198.              */
  199.             if (HTCJK != NOCJK && !isascii(tmp[0])) {
  200.             tmp[1] = links[cur].hightext2[++i];
  201.             addstr(tmp);
  202.             tmp[1] = '\0';
  203.             } else {
  204.             addstr(tmp);
  205.             }
  206.          }
  207.         }
  208.     }
  209.     lynx_stop_link_color (flag == ON, links[cur].inUnderline);
  210.  
  211. #if defined(FANCY_CURSES) || defined(USE_SLANG)
  212.     /*
  213.      *  If we have an emphasized WHEREIS hit in the highlighted
  214.      *  text, restore the emphasis.  Note that we never emphasize
  215.      *  the first and last characters of the highlighted text when
  216.      *  we are making the link current, so the link attributes for
  217.      *  the current link will persist at the beginning and end,
  218.      *  providing an indication to the user that it has been made
  219.      *  current.   Also note that we use HText_getFirstTargetInLine()
  220.      *  to determine if there's a hit in the HText structure line
  221.      *  containing the link, and if so, get back a copy of the line
  222.      *  starting at that first hit (which might be before or after
  223.      *  our link), and with all IsSpecial characters stripped, so we
  224.      *  don't need to deal with them here. - FM
  225.      */
  226.     if (target && *target && (links[cur].type & WWW_LINK_TYPE) &&
  227.         links[cur].hightext && *links[cur].hightext &&
  228.         HText_getFirstTargetInLine(HTMainText,
  229.                        links[cur].anchor_line_num,
  230.                        utf_flag,
  231.                        (int *)&Offset,
  232.                        (int *)&tLen,
  233.                        (char **)&theData,
  234.                        target)) {
  235.         int itmp, written, len, y, offset;
  236.         char *data;
  237.         int tlen = strlen(target);
  238.         int hlen, hLen;
  239.         int hLine = links[cur].ly, hoffset = links[cur].lx;
  240.         size_t utf_extra = 0;
  241.  
  242.         /*
  243.          *    Copy into the buffer only what will fit
  244.          *    up to the right border of the screen. - FM
  245.          */
  246.         LYmbcsstrncpy(buffer,
  247.               (links[cur].hightext ?
  248.                links[cur].hightext : ""),
  249.               (sizeof(buffer) - 1),
  250.               ((LYcols - 1) - links[cur].lx),
  251.               utf_flag);
  252.         hlen = strlen(buffer);
  253.         hLen = ((HTCJK != NOCJK || utf_flag) ?
  254.           LYmbcsstrlen(buffer, utf_flag) : hlen);
  255.  
  256.         /*
  257.          *    Break out if the first hit in the line
  258.          *    starts after this link. - FM
  259.          */
  260.         if (Offset >= (hoffset + hLen)) {
  261.         goto highlight_search_hightext2;
  262.         }
  263.  
  264.         /*
  265.          *    Recursively skip hits that end before this link, and
  266.          *    break out if there is no hit beyond those. - FM
  267.          */
  268.         Data = theData;
  269.         while ((Offset < hoffset) &&
  270.            ((Offset + tLen) <= hoffset)) {
  271.         data = (Data + tlen);
  272.         offset = (Offset + tLen);
  273.         if ((case_sensitive ?
  274.              (cp = LYno_attr_mbcs_strstr(data,
  275.                          target,
  276.                          utf_flag,
  277.                          &HitOffset,
  278.                          &LenNeeded)) != NULL :
  279.              (cp = LYno_attr_mbcs_case_strstr(data,
  280.                          target,
  281.                          utf_flag,
  282.                          &HitOffset,
  283.                          &LenNeeded)) != NULL) &&
  284.             (offset + LenNeeded) < LYcols) {
  285.             Data = cp;
  286.             Offset = (offset + HitOffset);
  287.         } else {
  288.             goto highlight_search_hightext2;
  289.         }
  290.         }
  291.         data = buffer;
  292.         offset = hoffset;
  293.  
  294.         /*
  295.          *    If the hit starts before the hightext, and ends
  296.          *    in or beyond the hightext, restore the emphasis,
  297.          *    skipping the first and last characters of the
  298.          *    hightext if we're making the link current. - FM
  299.          */
  300.         if ((Offset < offset) &&
  301.         ((Offset + tLen) > offset)) {
  302.         itmp = 0;
  303.         written = 0;
  304.         len = (tlen - (offset - Offset));
  305.  
  306.         /*
  307.          *  Go to the start of the hightext and
  308.          *  handle its first character. - FM
  309.          */
  310.         move(hLine, offset);
  311.         tmp[0] = data[itmp];
  312.         if (utf_flag && !isascii(tmp[0])) {
  313.             if ((*tmp & 0xe0) == 0xc0) {
  314.             utf_extra = 1;
  315.             } else if ((*tmp & 0xf0) == 0xe0) {
  316.             utf_extra = 2;
  317.             } else if ((*tmp & 0xf8) == 0xf0) {
  318.             utf_extra = 3;
  319.             } else if ((*tmp & 0xfc) == 0xf8) {
  320.             utf_extra = 4;
  321.             } else if ((*tmp & 0xfe) == 0xfc) {
  322.             utf_extra = 5;
  323.             } else {
  324.             /*
  325.              *  Garbage.
  326.              */
  327.             utf_extra = 0;
  328.             }
  329.             if (strlen(&data[itmp+1]) < utf_extra) {
  330.             /*
  331.              *  Shouldn't happen.
  332.              */
  333.             utf_extra = 0;
  334.             }
  335.         }
  336.         if (utf_extra) {
  337.             LYstrncpy(&tmp[1], &data[itmp+1], utf_extra);
  338.             itmp += utf_extra;
  339.             /*
  340.              *    Start emphasis immediately if we are
  341.              *    making the link non-current. - FM
  342.              */
  343.             if (flag != ON) {
  344.             LYstartTargetEmphasis();
  345.             TargetEmphasisON = TRUE;
  346.             addstr(tmp);
  347.             } else {
  348.             move(hLine, (offset + 1));
  349.             }
  350.             tmp[1] = '\0';
  351.             written += (utf_extra + 1);
  352.             utf_extra = 0;
  353.         } else if (HTCJK != NOCJK && !isascii(tmp[0])) {
  354.             /*
  355.              *    For CJK strings, by Masanobu Kimura.
  356.              */
  357.             tmp[1] = data[++itmp];
  358.             /*
  359.              *    Start emphasis immediately if we are
  360.              *    making the link non-current. - FM
  361.              */
  362.             if (flag != ON) {
  363.             LYstartTargetEmphasis();
  364.             TargetEmphasisON = TRUE;
  365.             addstr(tmp);
  366.             } else {
  367.             move(hLine, (offset + 1));
  368.             }
  369.             tmp[1] = '\0';
  370.             written += 2;
  371.         } else {
  372.             /*
  373.              *    Start emphasis immediately if we are making
  374.              *    the link non-current. - FM
  375.              */
  376.             if (flag != ON) {
  377.             LYstartTargetEmphasis();
  378.             TargetEmphasisON = TRUE;
  379.             addstr(tmp);
  380.             } else {
  381.             move(hLine, (offset + 1));
  382.             }
  383.             written++;
  384.         }
  385.         itmp++;
  386.         /*
  387.          *  Start emphasis after the first character
  388.          *  if we are making the link current and this
  389.          *  is not the last character. - FM
  390.          */
  391.         if (!TargetEmphasisON &&
  392.             data[itmp] != '\0') {
  393.             LYstartTargetEmphasis();
  394.             TargetEmphasisON = TRUE;
  395.         }
  396.  
  397.         /*
  398.          *  Handle the remaining characters. - FM
  399.          */
  400.         for (;
  401.              written < len && (tmp[0] = data[itmp]) != '\0';
  402.              itmp++)  {
  403.             /*
  404.              *    Print all the other target chars, except
  405.              *    the last character if it is also the last
  406.              *    character of hightext and we are making
  407.              *    the link current. - FM
  408.              */
  409.             if (utf_flag && !isascii(tmp[0])) {
  410.             if ((*tmp & 0xe0) == 0xc0) {
  411.                 utf_extra = 1;
  412.             } else if ((*tmp & 0xf0) == 0xe0) {
  413.                 utf_extra = 2;
  414.             } else if ((*tmp & 0xf8) == 0xf0) {
  415.                 utf_extra = 3;
  416.             } else if ((*tmp & 0xfc) == 0xf8) {
  417.                 utf_extra = 4;
  418.             } else if ((*tmp & 0xfe) == 0xfc) {
  419.                 utf_extra = 5;
  420.             } else {
  421.                 /*
  422.                  *    Garbage.
  423.                  */
  424.                 utf_extra = 0;
  425.             }
  426.             if (strlen(&data[itmp+1]) < utf_extra) {
  427.                 /*
  428.                  *    Shouldn't happen.
  429.                  */
  430.                 utf_extra = 0;
  431.             }
  432.             }
  433.             if (utf_extra) {
  434.             LYstrncpy(&tmp[1], &data[itmp+1], utf_extra);
  435.             itmp += utf_extra;
  436.             /*
  437.              *  Make sure we don't restore emphasis to
  438.              *  the last character of hightext if we
  439.              *  are making the link current. - FM
  440.              */
  441.             if (flag == ON && data[(itmp + 1)] == '\0') {
  442.                 LYstopTargetEmphasis();
  443.                 TargetEmphasisON = FALSE;
  444.                 LYGetYX(y, offset);
  445.                 move(hLine, (offset + 1));
  446.             } else {
  447.                 addstr(tmp);
  448.             }
  449.             tmp[1] = '\0';
  450.             written += (utf_extra + 1);
  451.             utf_extra = 0;
  452.             } else if (HTCJK != NOCJK && !isascii(tmp[0])) {
  453.             /*
  454.              *  For CJK strings, by Masanobu Kimura.
  455.              */
  456.             tmp[1] = data[++itmp];
  457.             /*
  458.              *  Make sure we don't restore emphasis to
  459.              *  the last character of hightext if we
  460.              *  are making the link current. - FM
  461.              */
  462.             if (flag == ON && data[(itmp + 1)] == '\0') {
  463.                 LYstopTargetEmphasis();
  464.                 TargetEmphasisON = FALSE;
  465.                 LYGetYX(y, offset);
  466.                 move(hLine, (offset + 1));
  467.             } else {
  468.                 addstr(tmp);
  469.             }
  470.             tmp[1] = '\0';
  471.             written += 2;
  472.             } else {
  473.             /*
  474.              *  Make sure we don't restore emphasis to
  475.              *  the last character of hightext if we
  476.              *  are making the link current. - FM
  477.              */
  478.             if (flag == ON && data[(itmp + 1)] == '\0') {
  479.                 LYstopTargetEmphasis();
  480.                 TargetEmphasisON = FALSE;
  481.                 LYGetYX(y, offset);
  482.                 move(hLine, (offset + 1));
  483.             } else {
  484.                 addstr(tmp);
  485.             }
  486.             written++;
  487.             }
  488.         }
  489.  
  490.         /*
  491.          *  Stop the emphasis if we haven't already, then
  492.          *  reset the offset to our current position in
  493.          *  the line, and if that is beyond the link, or
  494.          *  or we are making the link current and it is
  495.          *  the last character of the hightext, we are
  496.          *  done. - FM
  497.          */
  498.         if (TargetEmphasisON) {
  499.             LYstopTargetEmphasis();
  500.             TargetEmphasisON = FALSE;
  501.         }
  502.         LYGetYX(y, offset);
  503.         if (offset >=
  504.             (hoffset +
  505.              (flag == ON ? (hLen - 1) : hLen)))  {
  506.             goto highlight_search_hightext2;
  507.         }
  508.  
  509.         /*
  510.          *  See if we have another hit that starts
  511.          *  within the hightext. - FM
  512.          */
  513.         data = (Data + (offset - Offset));
  514.         if (!utf_flag) {
  515.             data = Data + (offset - Offset);
  516.         } else {
  517.             data = LYmbcs_skip_glyphs(Data,
  518.                           (offset - Offset),
  519.                           utf_flag);
  520.         }
  521.         if ((case_sensitive ?
  522.              (cp = LYno_attr_mbcs_strstr(data,
  523.                          target,
  524.                          utf_flag,
  525.                          &HitOffset,
  526.                          &LenNeeded)) != NULL :
  527.              (cp = LYno_attr_mbcs_case_strstr(data,
  528.                          target,
  529.                          utf_flag,
  530.                          &HitOffset,
  531.                          &LenNeeded)) != NULL) &&
  532.             (offset + LenNeeded) < LYcols) {
  533.             /*
  534.              *    If the hit starts after the end of the hightext,
  535.              *    or we are making the link current and the hit
  536.              *    starts at its last character, we are done. - FM
  537.              */
  538.             if ((HitOffset + offset) >=
  539.             (hoffset +
  540.              (flag == ON ? (hLen - 1) : hLen)))  {
  541.             goto highlight_search_hightext2;
  542.             }
  543.  
  544.             /*
  545.              *    Set up the data and offset for the hit, and let
  546.              *    the code for within hightext hits handle it. - FM
  547.              */
  548.             Data = cp;
  549.             Offset = (offset + HitOffset);
  550.             data = buffer;
  551.             offset = hoffset;
  552.             goto highlight_hit_within_hightext;
  553.         }
  554.         goto highlight_search_hightext2;
  555.         }
  556.  
  557. highlight_hit_within_hightext:
  558.         /*
  559.          *    If we get to here, the hit starts within the
  560.          *    hightext.  If we are making the link current
  561.          *    and it's the last character in the hightext,
  562.          *    we are done.  Otherwise, move there and start
  563.          *    restoring the emphasis. - FM
  564.          */
  565.         if ((Offset - offset) >
  566.         (flag == ON ? (hLen - 1) : hLen))  {
  567.         goto highlight_search_hightext2;
  568.         }
  569.         if (!utf_flag) {
  570.         data += (Offset - offset);
  571.         } else {
  572.         refresh();
  573.         data = LYmbcs_skip_glyphs(data,
  574.                       (Offset - offset),
  575.                       utf_flag);
  576.         }
  577.         offset = Offset;
  578.         itmp = 0;
  579.         written = 0;
  580.         len = tlen;
  581.  
  582.         /*
  583.          *    Go to the start of the hit and
  584.          *    handle its first character. - FM
  585.          */
  586.         move(hLine, offset);
  587.         tmp[0] = data[itmp];
  588.         if (utf_flag && !isascii(tmp[0])) {
  589.         if ((*tmp & 0xe0) == 0xc0) {
  590.             utf_extra = 1;
  591.         } else if ((*tmp & 0xf0) == 0xe0) {
  592.             utf_extra = 2;
  593.         } else if ((*tmp & 0xf8) == 0xf0) {
  594.             utf_extra = 3;
  595.         } else if ((*tmp & 0xfc) == 0xf8) {
  596.             utf_extra = 4;
  597.         } else if ((*tmp & 0xfe) == 0xfc) {
  598.             utf_extra = 5;
  599.         } else {
  600.             /*
  601.              *    Garbage.
  602.              */
  603.             utf_extra = 0;
  604.         }
  605.         if (strlen(&data[itmp+1]) < utf_extra) {
  606.             /*
  607.              *    Shouldn't happen.
  608.              */
  609.             utf_extra = 0;
  610.         }
  611.         }
  612.         if (utf_extra) {
  613.         LYstrncpy(&tmp[1], &data[itmp+1], utf_extra);
  614.         itmp += utf_extra;
  615.         /*
  616.          *  Start emphasis immediately if we are making
  617.          *  the link non-current, or we are making it
  618.          *  current but this is not the first or last
  619.          *  character of the hightext. - FM
  620.          */
  621.         if (flag != ON ||
  622.             (offset > hoffset && data[itmp+1] != '\0')) {
  623.             LYstartTargetEmphasis();
  624.             TargetEmphasisON = TRUE;
  625.             addstr(tmp);
  626.         } else {
  627.             move(hLine, (offset + 1));
  628.         }
  629.         tmp[1] = '\0';
  630.         written += (utf_extra + 1);
  631.         utf_extra = 0;
  632.         } else if (HTCJK != NOCJK && !isascii(tmp[0])) {
  633.         /*
  634.          *  For CJK strings, by Masanobu Kimura.
  635.          */
  636.         tmp[1] = data[++itmp];
  637.         /*
  638.          *  Start emphasis immediately if we are making
  639.          *  the link non-current, or we are making it
  640.          *  current but this is not the first or last
  641.          *  character of the hightext. - FM
  642.          */
  643.         if (flag != ON ||
  644.             (offset > hoffset && data[itmp+1] != '\0')) {
  645.             LYstartTargetEmphasis();
  646.             TargetEmphasisON = TRUE;
  647.             addstr(tmp);
  648.         } else {
  649.             move(hLine, (offset + 1));
  650.         }
  651.         tmp[1] = '\0';
  652.         written += 2;
  653.         } else {
  654.         /*
  655.          *  Start emphasis immediately if we are making
  656.          *  the link non-current, or we are making it
  657.          *  current but this is not the first or last
  658.          *  character of the hightext. - FM
  659.          */
  660.         if (flag != ON ||
  661.             (offset > hoffset && data[itmp+1] != '\0')) {
  662.             LYstartTargetEmphasis();
  663.             TargetEmphasisON = TRUE;
  664.             addstr(tmp);
  665.         } else {
  666.             move(hLine, (offset + 1));
  667.         }
  668.         written++;
  669.         }
  670.         itmp++;
  671.         /*
  672.          *    Start emphasis after the first character
  673.          *    if we are making the link current and this
  674.          *    is not the last character. - FM
  675.          */
  676.         if (!TargetEmphasisON &&
  677.         data[itmp] != '\0') {
  678.         LYstartTargetEmphasis();
  679.         TargetEmphasisON = TRUE;
  680.         }
  681.  
  682.         for (;
  683.          written < len && (tmp[0] = data[itmp]) != '\0';
  684.          itmp++)  {
  685.         /*
  686.          *  Print all the other target chars, except
  687.          *  the last character if it is also the last
  688.          *  character of hightext and we are making
  689.          *  the link current. - FM
  690.          */
  691.         if (utf_flag && !isascii(tmp[0])) {
  692.             if ((*tmp & 0xe0) == 0xc0) {
  693.             utf_extra = 1;
  694.             } else if ((*tmp & 0xf0) == 0xe0) {
  695.             utf_extra = 2;
  696.             } else if ((*tmp & 0xf8) == 0xf0) {
  697.             utf_extra = 3;
  698.             } else if ((*tmp & 0xfc) == 0xf8) {
  699.             utf_extra = 4;
  700.             } else if ((*tmp & 0xfe) == 0xfc) {
  701.             utf_extra = 5;
  702.             } else {
  703.             /*
  704.              *  Garbage.
  705.              */
  706.             utf_extra = 0;
  707.             }
  708.             if (strlen(&data[itmp+1]) < utf_extra) {
  709.             /*
  710.              *  Shouldn't happen.
  711.              */
  712.             utf_extra = 0;
  713.             }
  714.         }
  715.         if (utf_extra) {
  716.             LYstrncpy(&tmp[1], &data[itmp+1], utf_extra);
  717.             itmp += utf_extra;
  718.             /*
  719.              *    Make sure we don't restore emphasis to
  720.              *    the last character of hightext if we
  721.              *    are making the link current. - FM
  722.              */
  723.             if (flag == ON && data[(itmp + 1)] == '\0') {
  724.             LYstopTargetEmphasis();
  725.             TargetEmphasisON = FALSE;
  726.             LYGetYX(y, offset);
  727.             move(hLine, (offset + 1));
  728.             } else {
  729.             addstr(tmp);
  730.             }
  731.             tmp[1] = '\0';
  732.             written += (utf_extra + 1);
  733.             utf_extra = 0;
  734.         } else if (HTCJK != NOCJK && !isascii(tmp[0])) {
  735.             /*
  736.              *    For CJK strings, by Masanobu Kimura.
  737.              */
  738.             tmp[1] = data[++itmp];
  739.             /*
  740.              *    Make sure we don't restore emphasis to
  741.              *    the last character of hightext if we
  742.              *    are making the link current. - FM
  743.              */
  744.             if (flag == ON && data[(itmp + 1)] == '\0') {
  745.             LYstopTargetEmphasis();
  746.             TargetEmphasisON = FALSE;
  747.             LYGetYX(y, offset);
  748.             move(hLine, (offset + 1));
  749.             } else {
  750.             addstr(tmp);
  751.             }
  752.             tmp[1] = '\0';
  753.             written += 2;
  754.         } else {
  755.             /*
  756.              *    Make sure we don't restore emphasis to
  757.              *    the last character of hightext if we
  758.              *    are making the link current. - FM
  759.              */
  760.             if (flag == ON && data[(itmp + 1)] == '\0') {
  761.             LYstopTargetEmphasis();
  762.             TargetEmphasisON = FALSE;
  763.             LYGetYX(y, offset);
  764.             move(hLine, (offset + 1));
  765.             } else {
  766.             addstr(tmp);
  767.             }
  768.             written++;
  769.         }
  770.         }
  771.  
  772.         /*
  773.          *    Stop the emphasis if we haven't already, then reset
  774.          *    the offset to our current position in the line, and
  775.          *    if that is beyond the link, or we are making the link
  776.          *    current and it is the last character in the hightext,
  777.          *    we are done. - FM
  778.          */
  779.         if (TargetEmphasisON) {
  780.         LYstopTargetEmphasis();
  781.         TargetEmphasisON = FALSE;
  782.         }
  783.         LYGetYX(y, offset);
  784.         if (offset >=
  785.         (hoffset + (flag == ON ? (hLen - 1) : hLen))) {
  786.         goto highlight_search_hightext2;
  787.         }
  788.  
  789.         /*
  790.          *    See if we have another hit that starts
  791.          *    within the hightext. - FM
  792.          */
  793.         if (!utf_flag) {
  794.         data = Data + (offset - Offset);
  795.         } else {
  796.         data = LYmbcs_skip_glyphs(Data,
  797.                       (offset - Offset),
  798.                       utf_flag);
  799.         }
  800.         if ((case_sensitive ?
  801.          (cp = LYno_attr_mbcs_strstr(data,
  802.                          target,
  803.                          utf_flag,
  804.                          &HitOffset,
  805.                          &LenNeeded)) != NULL :
  806.          (cp = LYno_attr_mbcs_case_strstr(data,
  807.                          target,
  808.                          utf_flag,
  809.                          &HitOffset,
  810.                          &LenNeeded)) != NULL) &&
  811.         (offset + LenNeeded) < LYcols) {
  812.         /*
  813.          *  If the hit starts after the end of the hightext,
  814.          *  or we are making the link current and the hit
  815.          *  starts at its last character, we are done. - FM
  816.          */
  817.         if ((HitOffset + offset) >=
  818.             (hoffset +
  819.              (flag == ON ? (hLen - 1) : hLen)))  {
  820.             goto highlight_search_hightext2;
  821.         }
  822.  
  823.         /*
  824.          *  If the target extends beyond our buffer, emphasize
  825.          *  everything in the hightext starting at this hit.
  826.          *  Otherwise, set up the data and offsets, and loop
  827.          *  back. - FM
  828.          */
  829.         if ((HitOffset + (offset + tLen)) >=
  830.             (hoffset + hLen)) {
  831.             offset = (HitOffset + offset);
  832.             if (!utf_flag) {
  833.             data = buffer + (offset - hoffset);
  834.             } else {
  835.             refresh();
  836.             data = LYmbcs_skip_glyphs(buffer,
  837.                           (offset - hoffset),
  838.                           utf_flag);
  839.             }
  840.             move(hLine, offset);
  841.             itmp = 0;
  842.             written = 0;
  843.             len = strlen(data);
  844.  
  845.             /*
  846.              *    Turn the emphasis back on. - FM
  847.              */
  848.             LYstartTargetEmphasis();
  849.             TargetEmphasisON = TRUE;
  850.             for (;
  851.              written < len && (tmp[0] = data[itmp]) != '\0';
  852.              itmp++)  {
  853.             /*
  854.              *  Print all the other target chars, except
  855.              *  the last character if it is also the last
  856.              *  character of hightext and we are making
  857.              *  the link current. - FM
  858.              */
  859.             if (utf_flag && !isascii(tmp[0])) {
  860.                 if ((*tmp & 0xe0) == 0xc0) {
  861.                 utf_extra = 1;
  862.                 } else if ((*tmp & 0xf0) == 0xe0) {
  863.                 utf_extra = 2;
  864.                 } else if ((*tmp & 0xf8) == 0xf0) {
  865.                 utf_extra = 3;
  866.                 } else if ((*tmp & 0xfc) == 0xf8) {
  867.                 utf_extra = 4;
  868.                 } else if ((*tmp & 0xfe) == 0xfc) {
  869.                 utf_extra = 5;
  870.                 } else {
  871.                 /*
  872.                  *  Garbage.
  873.                  */
  874.                 utf_extra = 0;
  875.                 }
  876.                 if (strlen(&data[itmp+1]) < utf_extra) {
  877.                 /*
  878.                  *  Shouldn't happen.
  879.                  */
  880.                 utf_extra = 0;
  881.                 }
  882.             }
  883.             if (utf_extra) {
  884.                 LYstrncpy(&tmp[1], &data[itmp+1], utf_extra);
  885.                 itmp += utf_extra;
  886.                 /*
  887.                  *    Make sure we don't restore emphasis to
  888.                  *    the last character of hightext if we
  889.                  *    are making the link current. - FM
  890.                  */
  891.                 if (flag == ON && data[(itmp + 1)] == '\0') {
  892.                 LYstopTargetEmphasis();
  893.                 TargetEmphasisON = FALSE;
  894.                 LYGetYX(y, offset);
  895.                 move(hLine, (offset + 1));
  896.                 } else {
  897.                 addstr(tmp);
  898.                 }
  899.                 tmp[1] = '\0';
  900.                 written += (utf_extra + 1);
  901.                 utf_extra = 0;
  902.             } else if (HTCJK != NOCJK && !isascii(tmp[0])) {
  903.                 /*
  904.                  *    For CJK strings, by Masanobu Kimura.
  905.                  */
  906.                 tmp[1] = data[++itmp];
  907.                 /*
  908.                  *    Make sure we don't restore emphasis to
  909.                  *    the last character of hightext if we
  910.                  *    are making the link current. - FM
  911.                  */
  912.                 if (flag == ON && data[(itmp + 1)] == '\0') {
  913.                 LYstopTargetEmphasis();
  914.                 TargetEmphasisON = FALSE;
  915.                 } else {
  916.                 addstr(tmp);
  917.                 }
  918.                 tmp[1] = '\0';
  919.                 written += 2;
  920.             } else {
  921.                 /*
  922.                  *    Make sure we don't restore emphasis to
  923.                  *    the last character of hightext if we
  924.                  *    are making the link current. - FM
  925.                  */
  926.                 if (flag == ON && data[(itmp + 1)] == '\0') {
  927.                 LYstopTargetEmphasis();
  928.                 TargetEmphasisON = FALSE;
  929.                 } else {
  930.                 addstr(tmp);
  931.                 }
  932.                 written++;
  933.             }
  934.             }
  935.             /*
  936.              *    Turn off the emphasis if we haven't already,
  937.              *    and then we're done. - FM
  938.              */
  939.             if (TargetEmphasisON) {
  940.             LYstopTargetEmphasis();
  941.             }
  942.             goto highlight_search_hightext2;
  943.         } else {
  944.             Data = cp;
  945.             Offset = (offset + HitOffset);
  946.             data = buffer;
  947.             offset = hoffset;
  948.             goto highlight_hit_within_hightext;
  949.         }
  950.         }
  951.         goto highlight_search_hightext2;
  952.     }
  953. highlight_search_hightext2:
  954.     if (target && *target && (links[cur].type & WWW_LINK_TYPE) &&
  955.         links[cur].hightext2 && *links[cur].hightext2 &&
  956.         links[cur].ly < display_lines &&
  957.         HText_getFirstTargetInLine(HTMainText,
  958.                        (links[cur].anchor_line_num + 1),
  959.                        utf_flag,
  960.                        (int *)&Offset,
  961.                        (int *)&tLen,
  962.                        (char **)&theData,
  963.                        target)) {
  964.         int itmp, written, len, y, offset;
  965.         char *data;
  966.         int tlen = strlen(target);
  967.         int hlen, hLen;
  968.         int hLine = (links[cur].ly + 1);
  969.         int hoffset = links[cur].hightext2_offset;
  970.         size_t utf_extra = 0;
  971.  
  972.         /*
  973.          *    Copy into the buffer only what will fit
  974.          *    up to the right border of the screen. - FM
  975.          */
  976.         LYmbcsstrncpy(buffer,
  977.               (links[cur].hightext2 ?
  978.                links[cur].hightext2 : ""),
  979.               (sizeof(buffer) - 1),
  980.               ((LYcols - 1) - links[cur].hightext2_offset),
  981.               utf_flag);
  982.         hlen = strlen(buffer);
  983.         hLen = ((HTCJK != NOCJK || utf_flag) ?
  984.           LYmbcsstrlen(buffer, utf_flag) : hlen);
  985.  
  986.         /*
  987.          *    Break out if the first hit in the line
  988.          *    starts after this link. - FM
  989.          */
  990.         if (Offset >= (hoffset + hLen)) {
  991.         goto highlight_search_done;
  992.         }
  993.  
  994.         /*
  995.          *    Recursively skip hits that end before this link, and
  996.          *    break out if there is no hit beyond those. - FM
  997.          */
  998.         Data = theData;
  999.         while ((Offset < hoffset) &&
  1000.            ((Offset + tLen) <= hoffset)) {
  1001.         data = (Data + tlen);
  1002.         offset = (Offset + tLen);
  1003.         if ((case_sensitive ?
  1004.              (cp = LYno_attr_mbcs_strstr(data,
  1005.                          target,
  1006.                          utf_flag,
  1007.                          &HitOffset,
  1008.                          &LenNeeded)) != NULL :
  1009.              (cp = LYno_attr_mbcs_case_strstr(data,
  1010.                          target,
  1011.                          utf_flag,
  1012.                          &HitOffset,
  1013.                          &LenNeeded)) != NULL) &&
  1014.             (offset + LenNeeded) < LYcols) {
  1015.             Data = cp;
  1016.             Offset = (offset + HitOffset);
  1017.         } else {
  1018.             goto highlight_search_done;
  1019.         }
  1020.         }
  1021.         data = buffer;
  1022.         offset = hoffset;
  1023.  
  1024.         /*
  1025.          *    If the hit starts before the hightext2, and ends
  1026.          *    in or beyond the hightext2, restore the emphasis,
  1027.          *    skipping the first and last characters of the
  1028.          *    hightext2 if we're making the link current. - FM
  1029.          */
  1030.         if ((Offset < offset) &&
  1031.         ((Offset + tLen) > offset)) {
  1032.         itmp = 0;
  1033.         written = 0;
  1034.         len = (tlen - (offset - Offset));
  1035.  
  1036.         /*
  1037.          *  Go to the start of the hightext2 and
  1038.          *  handle its first character. - FM
  1039.          */
  1040.         move(hLine, offset);
  1041.         tmp[0] = data[itmp];
  1042.         if (utf_flag && !isascii(tmp[0])) {
  1043.             if ((*tmp & 0xe0) == 0xc0) {
  1044.             utf_extra = 1;
  1045.             } else if ((*tmp & 0xf0) == 0xe0) {
  1046.             utf_extra = 2;
  1047.             } else if ((*tmp & 0xf8) == 0xf0) {
  1048.             utf_extra = 3;
  1049.             } else if ((*tmp & 0xfc) == 0xf8) {
  1050.             utf_extra = 4;
  1051.             } else if ((*tmp & 0xfe) == 0xfc) {
  1052.             utf_extra = 5;
  1053.             } else {
  1054.             /*
  1055.              *  Garbage.
  1056.              */
  1057.             utf_extra = 0;
  1058.             }
  1059.             if (strlen(&data[itmp+1]) < utf_extra) {
  1060.             /*
  1061.              *  Shouldn't happen.
  1062.              */
  1063.             utf_extra = 0;
  1064.             }
  1065.         }
  1066.         if (utf_extra) {
  1067.             LYstrncpy(&tmp[1], &data[itmp+1], utf_extra);
  1068.             itmp += utf_extra;
  1069.             /*
  1070.              *    Start emphasis immediately if we are
  1071.              *    making the link non-current. - FM
  1072.              */
  1073.             if (flag != ON) {
  1074.             LYstartTargetEmphasis();
  1075.             TargetEmphasisON = TRUE;
  1076.             addstr(tmp);
  1077.             } else {
  1078.             move(hLine, (offset + 1));
  1079.             }
  1080.             tmp[1] = '\0';
  1081.             written += (utf_extra + 1);
  1082.             utf_extra = 0;
  1083.         } else if (HTCJK != NOCJK && !isascii(tmp[0])) {
  1084.             /*
  1085.              *    For CJK strings, by Masanobu Kimura.
  1086.              */
  1087.             tmp[1] = data[++itmp];
  1088.             /*
  1089.              *    Start emphasis immediately if we are
  1090.              *    making the link non-current. - FM
  1091.              */
  1092.             if (flag != ON) {
  1093.             LYstartTargetEmphasis();
  1094.             TargetEmphasisON = TRUE;
  1095.             addstr(tmp);
  1096.             } else {
  1097.             move(hLine, (offset + 1));
  1098.             }
  1099.             tmp[1] = '\0';
  1100.             written += 2;
  1101.         } else {
  1102.             /*
  1103.              *    Start emphasis immediately if we are making
  1104.              *    the link non-current. - FM
  1105.              */
  1106.             if (flag != ON) {
  1107.             LYstartTargetEmphasis();
  1108.             TargetEmphasisON = TRUE;
  1109.             addstr(tmp);
  1110.             } else {
  1111.             move(hLine, (offset + 1));
  1112.             }
  1113.             written++;
  1114.         }
  1115.         itmp++;
  1116.         /*
  1117.          *  Start emphasis after the first character
  1118.          *  if we are making the link current and this
  1119.          *  is not the last character. - FM
  1120.          */
  1121.         if (!TargetEmphasisON &&
  1122.             data[itmp] != '\0') {
  1123.             LYstartTargetEmphasis();
  1124.             TargetEmphasisON = TRUE;
  1125.         }
  1126.  
  1127.         /*
  1128.          *  Handle the remaining characters. - FM
  1129.          */
  1130.         for (;
  1131.              written < len && (tmp[0] = data[itmp]) != '\0';
  1132.              itmp++)  {
  1133.             /*
  1134.              *    Print all the other target chars, except
  1135.              *    the last character if it is also the last
  1136.              *    character of hightext2 and we are making
  1137.              *    the link current. - FM
  1138.              */
  1139.             if (utf_flag && !isascii(tmp[0])) {
  1140.             if ((*tmp & 0xe0) == 0xc0) {
  1141.                 utf_extra = 1;
  1142.             } else if ((*tmp & 0xf0) == 0xe0) {
  1143.                 utf_extra = 2;
  1144.             } else if ((*tmp & 0xf8) == 0xf0) {
  1145.                 utf_extra = 3;
  1146.             } else if ((*tmp & 0xfc) == 0xf8) {
  1147.                 utf_extra = 4;
  1148.             } else if ((*tmp & 0xfe) == 0xfc) {
  1149.                 utf_extra = 5;
  1150.             } else {
  1151.                 /*
  1152.                  *    Garbage.
  1153.                  */
  1154.                 utf_extra = 0;
  1155.             }
  1156.             if (strlen(&data[itmp+1]) < utf_extra) {
  1157.                 /*
  1158.                  *    Shouldn't happen.
  1159.                  */
  1160.                 utf_extra = 0;
  1161.             }
  1162.             }
  1163.             if (utf_extra) {
  1164.             LYstrncpy(&tmp[1], &data[itmp+1], utf_extra);
  1165.             itmp += utf_extra;
  1166.             /*
  1167.              *  Make sure we don't restore emphasis to
  1168.              *  the last character of hightext2 if we
  1169.              *  are making the link current. - FM
  1170.              */
  1171.             if (flag == ON && data[(itmp + 1)] == '\0') {
  1172.                 LYstopTargetEmphasis();
  1173.                 TargetEmphasisON = FALSE;
  1174.                 LYGetYX(y, offset);
  1175.                 move(hLine, (offset + 1));
  1176.             } else {
  1177.                 addstr(tmp);
  1178.             }
  1179.             tmp[1] = '\0';
  1180.             written += (utf_extra + 1);
  1181.             utf_extra = 0;
  1182.             } else if (HTCJK != NOCJK && !isascii(tmp[0])) {
  1183.             /*
  1184.              *  For CJK strings, by Masanobu Kimura.
  1185.              */
  1186.             tmp[1] = data[++itmp];
  1187.             /*
  1188.              *  Make sure we don't restore emphasis to
  1189.              *  the last character of hightext2 if we
  1190.              *  are making the link current. - FM
  1191.              */
  1192.             if (flag == ON && data[(itmp + 1)] == '\0') {
  1193.                 LYstopTargetEmphasis();
  1194.                 TargetEmphasisON = FALSE;
  1195.                 LYGetYX(y, offset);
  1196.                 move(hLine, (offset + 1));
  1197.             } else {
  1198.                 addstr(tmp);
  1199.             }
  1200.             tmp[1] = '\0';
  1201.             written += 2;
  1202.             } else {
  1203.             /*
  1204.              *  Make sure we don't restore emphasis to
  1205.              *  the last character of hightext2 if we
  1206.              *  are making the link current. - FM
  1207.              */
  1208.             if (flag == ON && data[(itmp + 1)] == '\0') {
  1209.                 LYstopTargetEmphasis();
  1210.                 TargetEmphasisON = FALSE;
  1211.                 LYGetYX(y, offset);
  1212.                 move(hLine, (offset + 1));
  1213.             } else {
  1214.                 addstr(tmp);
  1215.             }
  1216.             written++;
  1217.             }
  1218.         }
  1219.  
  1220.         /*
  1221.          *  Stop the emphasis if we haven't already, then
  1222.          *  reset the offset to our current position in
  1223.          *  the line, and if that is beyond the link, or
  1224.          *  or we are making the link current and it is
  1225.          *  the last character of the hightext2, we are
  1226.          *  done. - FM
  1227.          */
  1228.         if (TargetEmphasisON) {
  1229.             LYstopTargetEmphasis();
  1230.             TargetEmphasisON = FALSE;
  1231.         }
  1232.         LYGetYX(y, offset);
  1233.         if (offset >=
  1234.             (hoffset +
  1235.              (flag == ON ? (hLen - 1) : hLen)))  {
  1236.             goto highlight_search_done;
  1237.         }
  1238.  
  1239.         /*
  1240.          *  See if we have another hit that starts
  1241.          *  within the hightext2. - FM
  1242.          */
  1243.         if (!utf_flag) {
  1244.             data = Data + (offset - Offset);
  1245.         } else {
  1246.             data = LYmbcs_skip_glyphs(Data,
  1247.                           (offset - Offset),
  1248.                           utf_flag);
  1249.         }
  1250.         if ((case_sensitive ?
  1251.              (cp = LYno_attr_mbcs_strstr(data,
  1252.                          target,
  1253.                          utf_flag,
  1254.                          &HitOffset,
  1255.                          &LenNeeded)) != NULL :
  1256.              (cp = LYno_attr_mbcs_case_strstr(data,
  1257.                          target,
  1258.                          utf_flag,
  1259.                          &HitOffset,
  1260.                          &LenNeeded)) != NULL) &&
  1261.             (offset + LenNeeded) < LYcols) {
  1262.             /*
  1263.              *    If the hit starts after the end of the hightext2,
  1264.              *    or we are making the link current and the hit
  1265.              *    starts at its last character, we are done. - FM
  1266.              */
  1267.             if ((HitOffset + offset) >=
  1268.             (hoffset +
  1269.              (flag == ON ? (hLen - 1) : hLen)))  {
  1270.             goto highlight_search_done;
  1271.             }
  1272.  
  1273.             /*
  1274.              *    Set up the data and offset for the hit, and let
  1275.              *    the code for within hightext2 hits handle it. - FM
  1276.              */
  1277.             Data = cp;
  1278.             Offset = (offset + HitOffset);
  1279.             data = buffer;
  1280.             offset = hoffset;
  1281.             goto highlight_hit_within_hightext2;
  1282.         }
  1283.         goto highlight_search_done;
  1284.         }
  1285.  
  1286. highlight_hit_within_hightext2:
  1287.         /*
  1288.          *    If we get to here, the hit starts within the
  1289.          *    hightext2.  If we are making the link current
  1290.          *    and it's the last character in the hightext2,
  1291.          *    we are done.  Otherwise, move there and start
  1292.          *    restoring the emphasis. - FM
  1293.          */
  1294.         if ((Offset - offset) >
  1295.         (flag == ON ? (hLen - 1) : hLen))  {
  1296.         goto highlight_search_done;
  1297.         }
  1298.         if (!utf_flag) {
  1299.         data += (Offset - offset);
  1300.         } else {
  1301.         refresh();
  1302.         data = LYmbcs_skip_glyphs(data,
  1303.                       (Offset - offset),
  1304.                       utf_flag);
  1305.         }
  1306.         offset = Offset;
  1307.         itmp = 0;
  1308.         written = 0;
  1309.         len = tlen;
  1310.  
  1311.         /*
  1312.          *    Go to the start of the hit and
  1313.          *    handle its first character. - FM
  1314.          */
  1315.         move(hLine, offset);
  1316.         tmp[0] = data[itmp];
  1317.         if (utf_flag && !isascii(tmp[0])) {
  1318.         if ((*tmp & 0xe0) == 0xc0) {
  1319.             utf_extra = 1;
  1320.         } else if ((*tmp & 0xf0) == 0xe0) {
  1321.             utf_extra = 2;
  1322.         } else if ((*tmp & 0xf8) == 0xf0) {
  1323.             utf_extra = 3;
  1324.         } else if ((*tmp & 0xfc) == 0xf8) {
  1325.             utf_extra = 4;
  1326.         } else if ((*tmp & 0xfe) == 0xfc) {
  1327.             utf_extra = 5;
  1328.         } else {
  1329.             /*
  1330.              *    Garbage.
  1331.              */
  1332.             utf_extra = 0;
  1333.         }
  1334.         if (strlen(&data[itmp+1]) < utf_extra) {
  1335.             /*
  1336.              *    Shouldn't happen.
  1337.              */
  1338.             utf_extra = 0;
  1339.         }
  1340.         }
  1341.         if (utf_extra) {
  1342.         LYstrncpy(&tmp[1], &data[itmp+1], utf_extra);
  1343.         itmp += utf_extra;
  1344.         /*
  1345.          *  Start emphasis immediately if we are making
  1346.          *  the link non-current, or we are making it
  1347.          *  current but this is not the first or last
  1348.          *  character of the hightext2. - FM
  1349.          */
  1350.         if (flag != ON ||
  1351.             (offset > hoffset && data[itmp+1] != '\0')) {
  1352.             LYstartTargetEmphasis();
  1353.             TargetEmphasisON = TRUE;
  1354.             addstr(tmp);
  1355.         } else {
  1356.             move(hLine, (offset + 1));
  1357.         }
  1358.         tmp[1] = '\0';
  1359.         written += (utf_extra + 1);
  1360.         utf_extra = 0;
  1361.         } else if (HTCJK != NOCJK && !isascii(tmp[0])) {
  1362.         /*
  1363.          *  For CJK strings, by Masanobu Kimura.
  1364.          */
  1365.         tmp[1] = data[++itmp];
  1366.         /*
  1367.          *  Start emphasis immediately if we are making
  1368.          *  the link non-current, or we are making it
  1369.          *  current but this is not the first or last
  1370.          *  character of the hightext2. - FM
  1371.          */
  1372.         if (flag != ON ||
  1373.             (offset > hoffset && data[itmp+1] != '\0')) {
  1374.             LYstartTargetEmphasis();
  1375.             TargetEmphasisON = TRUE;
  1376.             addstr(tmp);
  1377.         } else {
  1378.             move(hLine, (offset + 1));
  1379.         }
  1380.         tmp[1] = '\0';
  1381.         written += 2;
  1382.         } else {
  1383.         /*
  1384.          *  Start emphasis immediately if we are making
  1385.          *  the link non-current, or we are making it
  1386.          *  current but this is not the first or last
  1387.          *  character of the hightext2. - FM
  1388.          */
  1389.         if (flag != ON ||
  1390.             (offset > hoffset && data[itmp+1] != '\0')) {
  1391.             LYstartTargetEmphasis();
  1392.             TargetEmphasisON = TRUE;
  1393.             addstr(tmp);
  1394.         } else {
  1395.             move(hLine, (offset + 1));
  1396.         }
  1397.         written++;
  1398.         }
  1399.         itmp++;
  1400.         /*
  1401.          *    Start emphasis after the first character
  1402.          *    if we are making the link current and this
  1403.          *    is not the last character. - FM
  1404.          */
  1405.         if (!TargetEmphasisON &&
  1406.         data[itmp] != '\0') {
  1407.         LYstartTargetEmphasis();
  1408.         TargetEmphasisON = TRUE;
  1409.         }
  1410.  
  1411.         for (;
  1412.          written < len && (tmp[0] = data[itmp]) != '\0';
  1413.          itmp++)  {
  1414.         /*
  1415.          *  Print all the other target chars, except
  1416.          *  the last character if it is also the last
  1417.          *  character of hightext2 and we are making
  1418.          *  the link current. - FM
  1419.          */
  1420.         if (utf_flag && !isascii(tmp[0])) {
  1421.             if ((*tmp & 0xe0) == 0xc0) {
  1422.             utf_extra = 1;
  1423.             } else if ((*tmp & 0xf0) == 0xe0) {
  1424.             utf_extra = 2;
  1425.             } else if ((*tmp & 0xf8) == 0xf0) {
  1426.             utf_extra = 3;
  1427.             } else if ((*tmp & 0xfc) == 0xf8) {
  1428.             utf_extra = 4;
  1429.             } else if ((*tmp & 0xfe) == 0xfc) {
  1430.             utf_extra = 5;
  1431.             } else {
  1432.             /*
  1433.              *  Garbage.
  1434.              */
  1435.             utf_extra = 0;
  1436.             }
  1437.             if (strlen(&data[itmp+1]) < utf_extra) {
  1438.             /*
  1439.              *  Shouldn't happen.
  1440.              */
  1441.             utf_extra = 0;
  1442.             }
  1443.         }
  1444.         if (utf_extra) {
  1445.             LYstrncpy(&tmp[1], &data[itmp+1], utf_extra);
  1446.             itmp += utf_extra;
  1447.             /*
  1448.              *    Make sure we don't restore emphasis to
  1449.              *    the last character of hightext2 if we
  1450.              *    are making the link current. - FM
  1451.              */
  1452.             if (flag == ON && data[(itmp + 1)] == '\0') {
  1453.             LYstopTargetEmphasis();
  1454.             TargetEmphasisON = FALSE;
  1455.             LYGetYX(y, offset);
  1456.             move(hLine, (offset + 1));
  1457.             } else {
  1458.             addstr(tmp);
  1459.             }
  1460.             tmp[1] = '\0';
  1461.             written += (utf_extra + 1);
  1462.             utf_extra = 0;
  1463.         } else if (HTCJK != NOCJK && !isascii(tmp[0])) {
  1464.             /*
  1465.              *    For CJK strings, by Masanobu Kimura.
  1466.              */
  1467.             tmp[1] = data[++itmp];
  1468.             /*
  1469.              *    Make sure we don't restore emphasis to
  1470.              *    the last character of hightext2 if we
  1471.              *    are making the link current. - FM
  1472.              */
  1473.             if (flag == ON && data[(itmp + 1)] == '\0') {
  1474.             LYstopTargetEmphasis();
  1475.             TargetEmphasisON = FALSE;
  1476.             LYGetYX(y, offset);
  1477.             move(hLine, (offset + 1));
  1478.             } else {
  1479.             addstr(tmp);
  1480.             }
  1481.             tmp[1] = '\0';
  1482.             written += 2;
  1483.         } else {
  1484.             /*
  1485.              *    Make sure we don't restore emphasis to
  1486.              *    the last character of hightext2 if we
  1487.              *    are making the link current. - FM
  1488.              */
  1489.             if (flag == ON && data[(itmp + 1)] == '\0') {
  1490.             LYstopTargetEmphasis();
  1491.             TargetEmphasisON = FALSE;
  1492.             LYGetYX(y, offset);
  1493.             move(hLine, (offset + 1));
  1494.             } else {
  1495.             addstr(tmp);
  1496.             }
  1497.             written++;
  1498.         }
  1499.         }
  1500.  
  1501.         /*
  1502.          *    Stop the emphasis if we haven't already, then reset
  1503.          *    the offset to our current position in the line, and
  1504.          *    if that is beyond the link, or we are making the link
  1505.          *    current and it is the last character in the hightext2,
  1506.          *    we are done. - FM
  1507.          */
  1508.         if (TargetEmphasisON) {
  1509.         LYstopTargetEmphasis();
  1510.         TargetEmphasisON = FALSE;
  1511.         }
  1512.         LYGetYX(y, offset);
  1513.         if (offset >=
  1514.         (hoffset + (flag == ON ? (hLen - 1) : hLen))) {
  1515.         goto highlight_search_done;
  1516.         }
  1517.  
  1518.         /*
  1519.          *    See if we have another hit that starts
  1520.          *    within the hightext2. - FM
  1521.          */
  1522.         if (!utf_flag) {
  1523.         data = (Data + (offset - Offset));
  1524.         } else {
  1525.         data = LYmbcs_skip_glyphs(Data,
  1526.                       (offset - Offset),
  1527.                       utf_flag);
  1528.         }
  1529.         if ((case_sensitive ?
  1530.          (cp = LYno_attr_mbcs_strstr(data,
  1531.                          target,
  1532.                          utf_flag,
  1533.                          &HitOffset,
  1534.                          &LenNeeded)) != NULL :
  1535.          (cp = LYno_attr_mbcs_case_strstr(data,
  1536.                          target,
  1537.                          utf_flag,
  1538.                          &HitOffset,
  1539.                          &LenNeeded)) != NULL) &&
  1540.         (offset + LenNeeded) < LYcols) {
  1541.         /*
  1542.          *  If the hit starts after the end of the hightext2,
  1543.          *  or we are making the link current and the hit
  1544.          *  starts at its last character, we are done. - FM
  1545.          */
  1546.         if ((HitOffset + offset) >=
  1547.             (hoffset +
  1548.              (flag == ON ? (hLen - 1) : hLen)))  {
  1549.             goto highlight_search_done;
  1550.         }
  1551.  
  1552.         /*
  1553.          *  If the target extends beyond our buffer, emphasize
  1554.          *  everything in the hightext2 starting at this hit.
  1555.          *  Otherwise, set up the data and offsets, and loop
  1556.          *  back. - FM
  1557.          */
  1558.         if ((HitOffset + (offset + tLen)) >=
  1559.             (hoffset + hLen)) {
  1560.             offset = (HitOffset + offset);
  1561.             if (!utf_flag) {
  1562.             data = buffer + (offset - hoffset);
  1563.             } else {
  1564.             refresh();
  1565.             data = LYmbcs_skip_glyphs(buffer,
  1566.                           (offset - hoffset),
  1567.                           utf_flag);
  1568.             }
  1569.             move(hLine, offset);
  1570.             itmp = 0;
  1571.             written = 0;
  1572.             len = strlen(data);
  1573.  
  1574.             /*
  1575.              *    Turn the emphasis back on. - FM
  1576.              */
  1577.             LYstartTargetEmphasis();
  1578.             TargetEmphasisON = TRUE;
  1579.             for (;
  1580.              written < len && (tmp[0] = data[itmp]) != '\0';
  1581.              itmp++)  {
  1582.             /*
  1583.              *  Print all the other target chars, except
  1584.              *  the last character if it is also the last
  1585.              *  character of hightext2 and we are making
  1586.              *  the link current. - FM
  1587.              */
  1588.             if (utf_flag && !isascii(tmp[0])) {
  1589.                 if ((*tmp & 0xe0) == 0xc0) {
  1590.                 utf_extra = 1;
  1591.                 } else if ((*tmp & 0xf0) == 0xe0) {
  1592.                 utf_extra = 2;
  1593.                 } else if ((*tmp & 0xf8) == 0xf0) {
  1594.                 utf_extra = 3;
  1595.                 } else if ((*tmp & 0xfc) == 0xf8) {
  1596.                 utf_extra = 4;
  1597.                 } else if ((*tmp & 0xfe) == 0xfc) {
  1598.                 utf_extra = 5;
  1599.                 } else {
  1600.                 /*
  1601.                  *  Garbage.
  1602.                  */
  1603.                 utf_extra = 0;
  1604.                 }
  1605.                 if (strlen(&data[itmp+1]) < utf_extra) {
  1606.                 /*
  1607.                  *  Shouldn't happen.
  1608.                  */
  1609.                 utf_extra = 0;
  1610.                 }
  1611.             }
  1612.             if (utf_extra) {
  1613.                 LYstrncpy(&tmp[1], &data[itmp+1], utf_extra);
  1614.                 itmp += utf_extra;
  1615.                 /*
  1616.                  *    Make sure we don't restore emphasis to
  1617.                  *    the last character of hightext2 if we
  1618.                  *    are making the link current. - FM
  1619.                  */
  1620.                 if (flag == ON && data[(itmp + 1)] == '\0') {
  1621.                 LYstopTargetEmphasis();
  1622.                 TargetEmphasisON = FALSE;
  1623.                 LYGetYX(y, offset);
  1624.                 move(hLine, (offset + 1));
  1625.                 } else {
  1626.                 addstr(tmp);
  1627.                 }
  1628.                 tmp[1] = '\0';
  1629.                 written += (utf_extra + 1);
  1630.                 utf_extra = 0;
  1631.             } else if (HTCJK != NOCJK && !isascii(tmp[0])) {
  1632.                 /*
  1633.                  *    For CJK strings, by Masanobu Kimura.
  1634.                  */
  1635.                 tmp[1] = data[++itmp];
  1636.                 /*
  1637.                  *    Make sure we don't restore emphasis to
  1638.                  *    the last character of hightext2 if we
  1639.                  *    are making the link current. - FM
  1640.                  */
  1641.                 if (flag == ON && data[(itmp + 1)] == '\0') {
  1642.                 LYstopTargetEmphasis();
  1643.                 TargetEmphasisON = FALSE;
  1644.                 } else {
  1645.                 addstr(tmp);
  1646.                 }
  1647.                 tmp[1] = '\0';
  1648.                 written += 2;
  1649.             } else {
  1650.                 /*
  1651.                  *    Make sure we don't restore emphasis to
  1652.                  *    the last character of hightext2 if we
  1653.                  *    are making the link current. - FM
  1654.                  */
  1655.                 if (flag == ON && data[(itmp + 1)] == '\0') {
  1656.                 LYstopTargetEmphasis();
  1657.                 TargetEmphasisON = FALSE;
  1658.                 } else {
  1659.                 addstr(tmp);
  1660.                 }
  1661.                 written++;
  1662.             }
  1663.             }
  1664.             /*
  1665.              *    Turn off the emphasis if we haven't already,
  1666.              *    and then we're done. - FM
  1667.              */
  1668.             if (TargetEmphasisON) {
  1669.             LYstopTargetEmphasis();
  1670.             }
  1671.             goto highlight_search_done;
  1672.         } else {
  1673.             Data = cp;
  1674.             Offset = (offset + HitOffset);
  1675.             data = buffer;
  1676.             offset = hoffset;
  1677.             goto highlight_hit_within_hightext2;
  1678.         }
  1679.         }
  1680.         goto highlight_search_done;
  1681.     }
  1682. highlight_search_done:
  1683.     FREE(theData);
  1684.  
  1685.     if (!LYShowCursor)
  1686.         /*
  1687.          *    Get cursor out of the way.
  1688.          */
  1689.         move((LYlines - 1), (LYcols - 1));
  1690.     else
  1691. #endif /* FANCY CURSES || USE_SLANG */
  1692.         /*
  1693.          *    Never hide the cursor if there's no FANCY CURSES or SLANG.
  1694.          */
  1695.         move(links[cur].ly,
  1696.          ((links[cur].lx > 0) ? (links[cur].lx - 1) : 0));
  1697.  
  1698.     if (flag)
  1699.         refresh();
  1700.     }
  1701.     return;
  1702. }
  1703.  
  1704. /*
  1705.  *  free_and_clear will free a pointer if it
  1706.  *  is non-zero and then set it to zero.
  1707.  */
  1708. PUBLIC void free_and_clear ARGS1(
  1709.     char **,    pointer)
  1710. {
  1711.     if (*pointer) {
  1712.     free(*pointer);
  1713.     *pointer = 0;
  1714.     }
  1715.     return;
  1716. }
  1717.  
  1718. /*
  1719.  *  Convert single or serial newlines to single spaces throughout a string
  1720.  *  (ignore newlines if the preceding character is a space) and convert
  1721.  *  tabs to single spaces.  Don't ignore any explicit tabs or spaces if
  1722.  *  the condense argument is FALSE, otherwise, condense any serial spaces
  1723.  *  or tabs to one space. - FM
  1724.  */
  1725. PUBLIC void convert_to_spaces ARGS2(
  1726.     char *,     string,
  1727.     BOOL,        condense)
  1728. {
  1729.     char *s = string;
  1730.     char *ns = string;
  1731.     BOOL last_is_space = FALSE;
  1732.  
  1733.     if (!string)
  1734.     return;
  1735.  
  1736.     while (*s) {
  1737.     switch (*s) {
  1738.         case ' ':
  1739.         case '\t':
  1740.         if (!(condense && last_is_space))
  1741.             *(ns++) = ' ';
  1742.         last_is_space = TRUE;
  1743.         break;
  1744.  
  1745.         case '\r':
  1746.         case '\n':
  1747.         if (!last_is_space) {
  1748.             *(ns++) = ' ';
  1749.             last_is_space = TRUE;
  1750.         }
  1751.         break;
  1752.  
  1753.         default:
  1754.         *(ns++) = *s;
  1755.         last_is_space = FALSE;
  1756.         break;
  1757.     }
  1758.     s++;
  1759.     }
  1760.     *ns = '\0';
  1761.     return;
  1762. }
  1763.  
  1764. /*
  1765.  *  Strip trailing slashes from directory paths.
  1766.  */
  1767. PUBLIC char * strip_trailing_slash ARGS1(
  1768.     char *,     dirname)
  1769. {
  1770.     int i;
  1771.  
  1772.     i = strlen(dirname) - 1;
  1773.     while (i >= 0 && dirname[i] == '/')
  1774.     dirname[i--] = '\0';
  1775.     return(dirname);
  1776. }
  1777.  
  1778. /*
  1779.  *  Display (or hide) the status line.
  1780.  */
  1781. BOOLEAN mustshow = FALSE;
  1782.  
  1783. PUBLIC void statusline ARGS1(
  1784.     CONST char *,    text)
  1785. {
  1786.     char buffer[256];
  1787.     unsigned char *temp = NULL;
  1788.     int max_length, len, i, j;
  1789.     unsigned char k;
  1790.  
  1791.     if (text == NULL)
  1792.     return;
  1793.  
  1794.     /*
  1795.      *    Don't print statusline messages if dumping to stdout.
  1796.      */
  1797.     if (dump_output_immediately)
  1798.     return;
  1799.  
  1800.     /*
  1801.      *    Don't print statusline message if turned off.
  1802.      */
  1803.     if (mustshow != TRUE) {
  1804.     if (no_statusline == TRUE) {
  1805.         return;
  1806.     }
  1807.     }
  1808.     mustshow = FALSE;
  1809.  
  1810.     /*
  1811.      *    Deal with any CJK escape sequences and Kanji if we have a CJK
  1812.      *    character set selected, otherwise, strip any escapes.  Also,
  1813.      *    make sure text is not longer than the statusline window. - FM
  1814.      */
  1815.     max_length = ((LYcols - 2) < 256) ? (LYcols - 2) : 255;
  1816.     if ((text[0] != '\0') &&
  1817.     (LYHaveCJKCharacterSet)) {
  1818.     /*
  1819.      *  Translate or filter any escape sequences. - FM
  1820.      */
  1821.     if ((temp = (unsigned char *)calloc(1, strlen(text) + 1)) == NULL)
  1822.         outofmem(__FILE__, "statusline");
  1823.     if (kanji_code == EUC) {
  1824.         TO_EUC((CONST unsigned char *)text, temp);
  1825.     } else if (kanji_code == SJIS) {
  1826.         TO_SJIS((CONST unsigned char *)text, temp);
  1827.     } else {
  1828.         for (i = 0, j = 0; text[i]; i++) {
  1829.         if (text[i] != '\033') {
  1830.             temp[j++] = text[i];
  1831.         }
  1832.         }
  1833.         temp[j] = '\0';
  1834.     }
  1835.  
  1836.     /*
  1837.      *  Deal with any newlines or tabs in the string. - FM
  1838.      */
  1839.     convert_to_spaces((char *)temp, FALSE);
  1840.  
  1841.     /*
  1842.      *  Handle the Kanji, making sure the text is not
  1843.      *  longer than the statusline window. - FM
  1844.      */
  1845.     for (i = 0, j = 0, len = 0, k = '\0';
  1846.          temp[i] != '\0' && len < max_length; i++) {
  1847.         if (k != '\0') {
  1848.         buffer[j++] = k;
  1849.         buffer[j++] = temp[i];
  1850.         k = '\0';
  1851.         len += 2;
  1852.         } else if ((temp[i] & 0200) != 0) {
  1853.         k = temp[i];
  1854.         } else {
  1855.         buffer[j++] = temp[i];
  1856.         len++;
  1857.         }
  1858.     }
  1859.     buffer[j] = '\0';
  1860.     FREE(temp);
  1861.     } else {
  1862.     /*
  1863.      *  Strip any escapes, and shorten text if necessary.  Note
  1864.      *  that we don't deal with the possibility of UTF-8 characters
  1865.      *  in the string.  This is unlikely, but if strings with such
  1866.      *  characters are used in LYMessages_en.h, a compilation
  1867.      *  symbol of HAVE_UTF8_STATUSLINES could be added there, and
  1868.      *  code added here for determining the displayed string length,
  1869.      *  as we do above for CJK. - FM
  1870.      */
  1871.     for (i = 0, len = 0; text[i] != '\0' && len < max_length; i++) {
  1872.         if (text[i] != '\033') {
  1873.         buffer[len++] = text[i];
  1874.         }
  1875.     }
  1876.     buffer[len] = '\0';
  1877.     /*
  1878.      *  Deal with any newlines or tabs in the string. - FM
  1879.      */
  1880.     convert_to_spaces(buffer, FALSE);
  1881.     }
  1882.  
  1883.     /*
  1884.      *    Move to the desired statusline window and
  1885.      *    output the text highlighted. - FM
  1886.      */
  1887.     if (LYStatusLine >= 0) {
  1888.     if (LYStatusLine < LYlines-1) {
  1889.         move(LYStatusLine, 0);
  1890.     } else {
  1891.         move(LYlines-1, 0);
  1892.     }
  1893.     } else if (user_mode == NOVICE_MODE) {
  1894.     move(LYlines-3, 0);
  1895.     } else {
  1896.     move(LYlines-1, 0);
  1897.     }
  1898.     clrtoeol();
  1899.     if (text != NULL && text[0] != '\0') {
  1900. #ifdef HAVE_UTF8_STATUSLINES
  1901.     if (LYCharSet_UC[current_char_set].enc == UCT_ENC_UTF8) {
  1902.         refresh();
  1903.     }
  1904. #endif /* HAVE_UTF8_STATUSLINES */
  1905. #ifndef USE_COLOR_STYLE
  1906.     lynx_start_status_color ();
  1907.     addstr (buffer);
  1908.     lynx_stop_status_color ();
  1909. #else
  1910.     /* draw the status bar in the STATUS style */
  1911.     {
  1912.         int a=(strncmp(buffer, "Alert", 5) || !hashStyles[s_alert].name ? s_status : s_alert);
  1913.         LynxChangeStyle (a, ABS_ON, 1);
  1914.         addstr(buffer);
  1915.         wbkgdset(stdscr,
  1916.              ((lynx_has_color && LYShowColor >= SHOW_COLOR_ON)
  1917.               ? hashStyles[a].color
  1918.               :A_NORMAL) | ' ');
  1919.         clrtoeol();
  1920.         if (s_normal != NOSTYLE)
  1921.             wbkgdset(stdscr, hashStyles[s_normal].color | ' ');
  1922.         else
  1923.             wbkgdset(stdscr,
  1924.                  ((lynx_has_color && LYShowColor >= SHOW_COLOR_ON)
  1925.                   ? displayStyles[DSTYLE_NORMAL].color
  1926.                   : A_NORMAL) | ' ');
  1927.         LynxChangeStyle (a, ABS_OFF, 0);
  1928.     }
  1929. #endif
  1930.     }
  1931.     refresh();
  1932.  
  1933.     return;
  1934. }
  1935.  
  1936. static char *novice_lines[] = {
  1937. #ifndef NOVICE_LINE_TWO_A
  1938. #define NOVICE_LINE_TWO_A    NOVICE_LINE_TWO
  1939. #define NOVICE_LINE_TWO_B    ""
  1940. #define NOVICE_LINE_TWO_C    ""
  1941. #endif /* !NOVICE_LINE_TWO_A */
  1942.   NOVICE_LINE_TWO_A,
  1943.   NOVICE_LINE_TWO_B,
  1944.   NOVICE_LINE_TWO_C,
  1945.   ""
  1946. };
  1947. static int lineno = 0;
  1948.  
  1949. PUBLIC void toggle_novice_line NOARGS
  1950. {
  1951.     lineno++;
  1952.     if (*novice_lines[lineno] == '\0')
  1953.         lineno = 0;
  1954.     return;
  1955. }
  1956.  
  1957. PUBLIC void noviceline ARGS1(
  1958.     int,        more_flag GCC_UNUSED)
  1959. {
  1960.  
  1961.     if (dump_output_immediately)
  1962.     return;
  1963.  
  1964.     move(LYlines-2,0);
  1965.     /* stop_reverse(); */
  1966.     clrtoeol();
  1967.     addstr(NOVICE_LINE_ONE);
  1968.     clrtoeol();
  1969.  
  1970. #if defined(DIRED_SUPPORT ) && defined(OK_OVERRIDE)
  1971.     if (lynx_edit_mode && !no_dired_support)
  1972.        addstr(DIRED_NOVICELINE);
  1973.     else
  1974. #endif /* DIRED_SUPPORT && OK_OVERRIDE */
  1975.  
  1976.     if (LYUseNoviceLineTwo)
  1977.     addstr(NOVICE_LINE_TWO);
  1978.     else
  1979.     addstr(novice_lines[lineno]);
  1980.  
  1981. #ifdef NOTDEFINED
  1982.     if (is_www_index && more_flag) {
  1983.     addstr("This is a searchable index.  Use ");
  1984.     addstr(key_for_func(LYK_INDEX_SEARCH));
  1985.     addstr(" to search:");
  1986.     stop_reverse();
  1987.     addstr("                ");
  1988.     start_reverse();
  1989.     addstr("space for more");
  1990.  
  1991.     } else if (is_www_index) {
  1992.     addstr("This is a searchable index.  Use ");
  1993.     addstr(key_for_func(LYK_INDEX_SEARCH));
  1994.     addstr(" to search:");
  1995.     } else {
  1996.     addstr("Type a command or ? for help:");
  1997.  
  1998.     if (more_flag) {
  1999.         stop_reverse();
  2000.         addstr("                       ");
  2001.         start_reverse();
  2002.         addstr("Press space for next page");
  2003.     }
  2004.     }
  2005.  
  2006. #endif /* NOTDEFINED */
  2007.  
  2008.     refresh();
  2009.     return;
  2010. }
  2011.  
  2012. PRIVATE int fake_zap = 0;
  2013.  
  2014. PUBLIC void LYFakeZap ARGS1(
  2015.     BOOL,    set)
  2016. {
  2017.     if (set && fake_zap < 1) {
  2018.     CTRACE(tfp, "\r *** Set simulated 'Z'");
  2019.     if (fake_zap)
  2020.         CTRACE(tfp, ", %d pending", fake_zap);
  2021.     CTRACE(tfp, " ***\n");
  2022.     fake_zap++;
  2023.     } else if (!set && fake_zap) {
  2024.     CTRACE(tfp, "\r *** Unset simulated 'Z'");
  2025.     CTRACE(tfp, ", %d pending", fake_zap);
  2026.     CTRACE(tfp, " ***\n");
  2027.     fake_zap = 0;
  2028.     }
  2029.  
  2030. }
  2031.  
  2032. PUBLIC int HTCheckForInterrupt NOARGS
  2033. {
  2034. #ifndef VMS /* UNIX stuff: */
  2035.     int c;
  2036. #ifndef USE_SLANG
  2037.     struct timeval socket_timeout;
  2038.     int ret = 0;
  2039.     fd_set readfds;
  2040. #endif /* !USE_SLANG */
  2041.  
  2042.     if (fake_zap > 0) {
  2043.     fake_zap--;
  2044.     if (TRACE) {
  2045.         fprintf(tfp, "\r *** Got simulated 'Z' ***\n");
  2046.         fflush(tfp);
  2047.         if (!LYTraceLogFP)
  2048.         sleep(AlertSecs);
  2049.     }
  2050.     return((int)TRUE);
  2051.     }
  2052.  
  2053.     /** Curses or slang setup was not invoked **/
  2054.     if (dump_output_immediately)
  2055.     return((int)FALSE);
  2056.  
  2057. #ifdef USE_SLANG
  2058.     /** No keystroke was entered
  2059.     Note that this isn't taking possible SOCKSification
  2060.     and the socks_flag into account, and may fail on the
  2061.     slang library's select() when SOCKSified. - FM **/
  2062.     if (0 == SLang_input_pending(0))
  2063.     return(FALSE);
  2064.  
  2065. #else /* Unix curses: */
  2066.  
  2067.     socket_timeout.tv_sec = 0;
  2068.     socket_timeout.tv_usec = 100;
  2069.     FD_ZERO(&readfds);
  2070.     FD_SET(0, &readfds);
  2071. #ifdef SOCKS
  2072.     if (socks_flag)
  2073.     ret = Rselect(FD_SETSIZE, (void *)&readfds, NULL, NULL,
  2074.               &socket_timeout);
  2075.     else
  2076. #endif /* SOCKS */
  2077.     ret = select(FD_SETSIZE, (void *)&readfds, NULL, NULL,
  2078.              &socket_timeout);
  2079.  
  2080.     /** Suspended? **/
  2081.     if ((ret == -1) && (errno == EINTR))
  2082.      return((int)FALSE);
  2083.  
  2084.     /** No keystroke was entered? **/
  2085.     if (!FD_ISSET(0,&readfds))
  2086.      return((int)FALSE);
  2087. #endif /* USE_SLANG */
  2088.  
  2089.     /** Keyboard 'Z' or 'z', or Control-G or Control-C **/
  2090. #if defined (DOSPATH) && defined (NCURSES)
  2091.     nodelay(stdscr,TRUE);
  2092. #endif /* DOSPATH */
  2093.     c = LYgetch();
  2094. #if defined (DOSPATH) && defined (NCURSES)
  2095.     nodelay(stdscr,FALSE);
  2096. #endif /* DOSPATH */
  2097.     if (TOUPPER(c) == 'Z' || c == 7 || c == 3)
  2098.     return((int)TRUE);
  2099. #ifdef DISP_PARTIAL
  2100.     else if (display_partial)
  2101.     {
  2102.     switch (keymap[c+1])
  2103.     {
  2104.     case LYK_PREV_PAGE :
  2105.         if (Newline > 1)
  2106.         Newline -= display_lines ;
  2107.         break ;
  2108.     case LYK_NEXT_PAGE :
  2109.         if (HText_canScrollDown())
  2110.         Newline += display_lines ;
  2111.         break ;
  2112.     case LYK_UP_TWO :
  2113.         if (Newline > 1)
  2114.         Newline -= 2 ;
  2115.         break ;
  2116.     case LYK_DOWN_TWO :
  2117.         if (HText_canScrollDown())
  2118.         Newline += 2 ;
  2119.         break ;
  2120.     case LYK_HOME:
  2121.         if (Newline > 1)
  2122.         Newline = 1;
  2123.         break;
  2124.     case LYK_END:
  2125.         if (HText_canScrollDown())
  2126.         Newline = MAXINT;
  2127.         break;
  2128.     case LYK_REFRESH :
  2129.         break ;
  2130.     default :
  2131.         return ((int)FALSE) ;
  2132.     }
  2133.     HText_pageDisplay(Newline, "");
  2134.     }
  2135. #endif /* DISP_PARTIAL */
  2136.  
  2137.     /** Other keystrokes **/
  2138.     return((int)FALSE);
  2139.  
  2140. #else /* VMS: */
  2141.  
  2142.     int c;
  2143.     extern BOOLEAN HadVMSInterrupt;
  2144.     extern int typeahead();
  2145.  
  2146.     if (fake_zap > 0) {
  2147.     fake_zap--;
  2148.     if (TRACE) {
  2149.         fprintf(tfp, "\r *** Got simulated 'Z' ***\n");
  2150.         fflush(tfp);
  2151.         if (!LYTraceLogFP)
  2152.         sleep(AlertSecs);
  2153.     }
  2154.     return((int)TRUE);
  2155.     }
  2156.  
  2157.     /** Curses or slang setup was not invoked **/
  2158.     if (dump_output_immediately)
  2159.       return((int)FALSE);
  2160.  
  2161.     /** Control-C or Control-Y and a 'N'o reply to exit query **/
  2162.     if (HadVMSInterrupt) {
  2163.     HadVMSInterrupt = FALSE;
  2164.     return((int)TRUE);
  2165.     }
  2166.  
  2167.     /** Keyboard 'Z' or 'z', or Control-G or Control-C **/
  2168.     c = typeahead();
  2169.     if (TOUPPER(c) == 'Z' || c == 7 || c == 3)
  2170.     return((int)TRUE);
  2171.  
  2172.     /** Other or no keystrokes **/
  2173.     return((int)FALSE);
  2174. #endif /* !VMS */
  2175. }
  2176.  
  2177. /*
  2178.  *  A file URL for a remote host is an obsolete ftp URL.
  2179.  *  Return YES only if we're certain it's a local file. - FM
  2180.  */
  2181. PUBLIC BOOLEAN LYisLocalFile ARGS1(
  2182.     char *,     filename)
  2183. {
  2184.     char *host = NULL;
  2185.     char *acc_method = NULL;
  2186.     char *cp;
  2187.  
  2188.     if (!filename)
  2189.     return NO;
  2190.     if (!(host = HTParse(filename, "", PARSE_HOST)))
  2191.     return NO;
  2192.     if (!*host) {
  2193.     FREE(host);
  2194.     return NO;
  2195.     }
  2196.  
  2197.     if ((cp=strchr(host, ':')) != NULL)
  2198.     *cp = '\0';
  2199.  
  2200.     if ((acc_method = HTParse(filename, "", PARSE_ACCESS))) {
  2201.     if (0==strcmp("file", acc_method) &&
  2202.         (0==strcmp(host, "localhost") ||
  2203. #ifdef VMS
  2204.          0==strcasecomp(host, HTHostName())))
  2205. #else
  2206.          0==strcmp(host, HTHostName())))
  2207. #endif /* VMS */
  2208.     {
  2209.         FREE(host);
  2210.         FREE(acc_method);
  2211.         return YES;
  2212.     }
  2213.     }
  2214.  
  2215.     FREE(host);
  2216.     FREE(acc_method);
  2217.     return NO;
  2218. }
  2219.  
  2220. /*
  2221.  *  Utility for checking URLs with a host field.
  2222.  *  Return YES only if we're certain it's the local host. - FM
  2223.  */
  2224. PUBLIC BOOLEAN LYisLocalHost ARGS1(
  2225.     char *,     filename)
  2226. {
  2227.     char *host = NULL;
  2228.     char *cp;
  2229.  
  2230.     if (!filename)
  2231.     return NO;
  2232.     if (!(host = HTParse(filename, "", PARSE_HOST)))
  2233.     return NO;
  2234.     if (!*host) {
  2235.     FREE(host);
  2236.     return NO;
  2237.     }
  2238.  
  2239.     if ((cp = strchr(host, ':')) != NULL)
  2240.     *cp = '\0';
  2241.  
  2242. #ifdef VMS
  2243.     if ((0==strcasecomp(host, "localhost") ||
  2244.      0==strcasecomp(host, LYHostName) ||
  2245.      0==strcasecomp(host, HTHostName()))) {
  2246. #else
  2247.     if ((0==strcmp(host, "localhost") ||
  2248.      0==strcmp(host, LYHostName) ||
  2249.      0==strcmp(host, HTHostName()))) {
  2250. #endif /* VMS */
  2251.         FREE(host);
  2252.         return YES;
  2253.     }
  2254.  
  2255.     FREE(host);
  2256.     return NO;
  2257. }
  2258.  
  2259. /*
  2260.  *  Utility for freeing the list of local host aliases. - FM
  2261.  */
  2262. PUBLIC void LYLocalhostAliases_free NOARGS
  2263. {
  2264.     char *alias;
  2265.     HTList *cur = localhost_aliases;
  2266.  
  2267.     if (!cur)
  2268.     return;
  2269.  
  2270.     while (NULL != (alias = (char *)HTList_nextObject(cur))) {
  2271.     FREE(alias);
  2272.     }
  2273.     HTList_delete(localhost_aliases);
  2274.     localhost_aliases = NULL;
  2275.     return;
  2276. }
  2277.  
  2278. /*
  2279.  *  Utility for listing hosts to be treated as local aliases. - FM
  2280.  */
  2281. PUBLIC void LYAddLocalhostAlias ARGS1(
  2282.     char *,     alias)
  2283. {
  2284.     char *LocalAlias;
  2285.  
  2286.     if (!(alias && *alias))
  2287.     return;
  2288.  
  2289.     if (!localhost_aliases) {
  2290.     localhost_aliases = HTList_new();
  2291.     atexit(LYLocalhostAliases_free);
  2292.     }
  2293.  
  2294.     if ((LocalAlias = (char *)calloc(1, (strlen(alias) + 1))) == NULL)
  2295.     outofmem(__FILE__, "HTAddLocalhosAlias");
  2296.     strcpy(LocalAlias, alias);
  2297.     HTList_addObject(localhost_aliases, LocalAlias);
  2298.  
  2299.     return;
  2300. }
  2301.  
  2302. /*
  2303.  *  Utility for checking URLs with a host field.
  2304.  *  Return YES only if we've listed the host as a local alias. - FM
  2305.  */
  2306. PUBLIC BOOLEAN LYisLocalAlias ARGS1(
  2307.     char *,     filename)
  2308. {
  2309.     char *host = NULL;
  2310.     char *alias;
  2311.     char *cp;
  2312.     HTList *cur = localhost_aliases;
  2313.  
  2314.     if (!cur || !filename)
  2315.     return NO;
  2316.     if (!(host = HTParse(filename, "", PARSE_HOST)))
  2317.     return NO;
  2318.     if (!(*host)) {
  2319.     FREE(host);
  2320.     return NO;
  2321.     }
  2322.  
  2323.     if ((cp = strchr(host, ':')) != NULL)
  2324.     *cp = '\0';
  2325.  
  2326.     while (NULL != (alias = (char *)HTList_nextObject(cur))) {
  2327. #ifdef VMS
  2328.     if (0==strcasecomp(host, alias)) {
  2329. #else
  2330.     if (0==strcmp(host, alias)) {
  2331. #endif /* VMS */
  2332.         FREE(host);
  2333.         return YES;
  2334.     }
  2335.     }
  2336.  
  2337.     FREE(host);
  2338.     return NO;
  2339. }
  2340.  
  2341. /*
  2342. **  This function checks for a URL with an unknown scheme,
  2343. **  but for which proxying has been set up, and if so,
  2344. **  returns PROXY_URL_TYPE. - FM
  2345. **
  2346. **  If a colon is present but the string segment which
  2347. **  precedes it is not being proxied, and we can rule
  2348. **  out that what follows the colon is not a port field,
  2349. **  it returns UNKNOWN_URL_TYPE.  Otherwise, it returns
  2350. **  0 (not a URL). - FM
  2351. */
  2352. PUBLIC int LYCheckForProxyURL ARGS1(
  2353.     char *,     filename)
  2354. {
  2355.     char *cp = filename;
  2356.     char *cp1;
  2357.     char *cp2 = NULL;
  2358.  
  2359.     /*
  2360.      *    Don't crash on an empty argument.
  2361.      */
  2362.     if (cp == NULL || *cp == '\0')
  2363.     return(0);
  2364.  
  2365.     /* kill beginning spaces */
  2366.     cp = LYSkipBlanks(cp);
  2367.  
  2368.     /*
  2369.      * Check for a colon, and if present,
  2370.      * see if we have proxying set up.
  2371.      */
  2372.     if ((cp1 = strchr((cp+1), ':')) != NULL) {
  2373.     *cp1 = '\0';
  2374.     StrAllocCopy(cp2, cp);
  2375.     *cp1 = ':';
  2376.     StrAllocCat(cp2, "_proxy");
  2377.     if (getenv(cp2) != NULL) {
  2378.         FREE(cp2);
  2379.         return(PROXY_URL_TYPE);
  2380.     }
  2381.     FREE(cp2);
  2382.     cp1++;
  2383.     if (isdigit((unsigned char)*cp1)) {
  2384.         while (*cp1 && isdigit((unsigned char)*cp1))
  2385.         cp1++;
  2386.         if (*cp1 && *cp1 != '/')
  2387.         return(UNKNOWN_URL_TYPE);
  2388.     }
  2389.     }
  2390.  
  2391.     return(0);
  2392. }
  2393.  
  2394. /*
  2395.  * Compare a "type:" string, replacing it by the comparison-string if it
  2396.  * matches (and return true in that case).
  2397.  */
  2398. static BOOLEAN compare_type ARGS3(
  2399.     char *,        tst,
  2400.     char *,        cmp,
  2401.     size_t,        len)
  2402. {
  2403.     if (!strncasecomp(tst, cmp, len)) {
  2404.     if (strncmp(tst, cmp, len)) {
  2405.         size_t i;
  2406.         for (i = 0; i < len; i++)
  2407.         tst[i] = cmp[i];
  2408.     }
  2409.     return TRUE;
  2410.     }
  2411.     return FALSE;
  2412. }
  2413.  
  2414. /*
  2415. **  Must recognize a URL and return the type.
  2416. **  If recognized, based on a case-insensitive
  2417. **  analyis of the scheme field, ensures that
  2418. **  the scheme field has the expected case.
  2419. **
  2420. **  Returns 0 (not a URL) for a NULL argument,
  2421. **  one which lacks a colon.
  2422. **
  2423. **  Chains to LYCheckForProxyURL() if a colon
  2424. **  is present but the type is not recognized.
  2425. */
  2426. PUBLIC int is_url ARGS1(
  2427.     char *,     filename)
  2428. {
  2429.     char *cp = filename;
  2430.     char *cp1;
  2431.  
  2432.     /*
  2433.      *    Don't crash on an empty argument.
  2434.      */
  2435.     if (cp == NULL || *cp == '\0')
  2436.     return(0);
  2437.  
  2438.     /*
  2439.      *    Can't be a URL if it lacks a colon.
  2440.      */
  2441.     if (NULL == strchr(cp, ':'))
  2442.     return(0);
  2443.  
  2444.     /*
  2445.      *    Kill beginning spaces.
  2446.      */
  2447.     cp = LYSkipBlanks(cp);
  2448.  
  2449.     /*
  2450.      *    Can't be a URL if it starts with a slash.
  2451.      *    So return immediately for this common case,
  2452.      *    also to avoid false positives if there was
  2453.      *    a colon later in the string. - KW
  2454.      */
  2455.     if (*cp == '/')
  2456.     return(0);
  2457.  
  2458. #if defined (DOSPATH) || defined (__EMX__) /* sorry! */
  2459.     if (strncmp(cp, "file:///", 8) && strlen(cp) == 19 &&
  2460.         cp[strlen(cp)-1] == ':')
  2461.         StrAllocCat(cp,"/");
  2462. #endif
  2463.  
  2464.     if (compare_type(cp, "news:", 5)) {
  2465.     return(NEWS_URL_TYPE);
  2466.  
  2467.     } else if (compare_type(cp, "nntp:", 5)) {
  2468.     return(NNTP_URL_TYPE);
  2469.  
  2470.     } else if (compare_type(cp, "snews:", 6)) {
  2471.     return(SNEWS_URL_TYPE);
  2472.  
  2473.     } else if (compare_type(cp, "newspost:", 9)) {
  2474.     /*
  2475.      *  Special Lynx type to handle news posts.
  2476.      */
  2477.     return(NEWSPOST_URL_TYPE);
  2478.  
  2479.     } else if (compare_type(cp, "newsreply:", 10)) {
  2480.     /*
  2481.      *  Special Lynx type to handle news replies (followups).
  2482.      */
  2483.     return(NEWSREPLY_URL_TYPE);
  2484.  
  2485.     } else if (compare_type(cp, "snewspost:", 10)) {
  2486.     /*
  2487.      *  Special Lynx type to handle snews posts.
  2488.      */
  2489.     return(NEWSPOST_URL_TYPE);
  2490.  
  2491.     } else if (compare_type(cp, "snewsreply:", 11)) {
  2492.     /*
  2493.      *  Special Lynx type to handle snews replies (followups).
  2494.      */
  2495.     return(NEWSREPLY_URL_TYPE);
  2496.  
  2497.     } else if (compare_type(cp, "mailto:", 7)) {
  2498.     return(MAILTO_URL_TYPE);
  2499.  
  2500.     } else if (compare_type(cp, "file:", 5)) {
  2501.     if (LYisLocalFile(cp)) {
  2502.         return(FILE_URL_TYPE);
  2503.     } else if (cp[5] == '/' && cp[6] == '/') {
  2504.         return(FTP_URL_TYPE);
  2505.     } else {
  2506.         return(0);
  2507.     }
  2508.  
  2509.     } else if (compare_type(cp, "data:", 5)) {
  2510.     return(DATA_URL_TYPE);
  2511.  
  2512.     } else if (compare_type(cp, "lynxexec:", 9)) {
  2513.     /*
  2514.      *  Special External Lynx type to handle execution
  2515.      *  of commands or scripts which require a pause to
  2516.      *  read the screen upon completion.
  2517.      */
  2518.     return(LYNXEXEC_URL_TYPE);
  2519.  
  2520.     } else if (compare_type(cp, "lynxprog:", 9)) {
  2521.     /*
  2522.      *  Special External Lynx type to handle execution
  2523.      *  of commans, sriptis or programs with do not
  2524.      *  require a pause to read screen upon completion.
  2525.      */
  2526.     return(LYNXPROG_URL_TYPE);
  2527.  
  2528.     } else if (compare_type(cp, "lynxcgi:", 8)) {
  2529.     /*
  2530.      *  Special External Lynx type to handle cgi scripts.
  2531.      */
  2532.     return(LYNXCGI_URL_TYPE);
  2533.  
  2534.     } else if (compare_type(cp, "LYNXPRINT:", 10)) {
  2535.     /*
  2536.      *  Special Internal Lynx type.
  2537.      */
  2538.     return(LYNXPRINT_URL_TYPE);
  2539.  
  2540.     } else if (compare_type(cp, "LYNXDOWNLOAD:", 13)) {
  2541.     /*
  2542.      *  Special Internal Lynx type.
  2543.      */
  2544.     return(LYNXDOWNLOAD_URL_TYPE);
  2545.  
  2546.     } else if (compare_type(cp, "LYNXDIRED:", 10)) {
  2547.     /*
  2548.      *  Special Internal Lynx type.
  2549.      */
  2550.     return(LYNXDIRED_URL_TYPE);
  2551.  
  2552.     } else if (compare_type(cp, "LYNXHIST:", 9)) {
  2553.     /*
  2554.      *  Special Internal Lynx type.
  2555.      */
  2556.     return(LYNXHIST_URL_TYPE);
  2557.  
  2558.     } else if (compare_type(cp, "LYNXKEYMAP:", 11)) {
  2559.     /*
  2560.      *  Special Internal Lynx type.
  2561.      */
  2562.     return(LYNXKEYMAP_URL_TYPE);
  2563.  
  2564.     } else if (compare_type(cp, "LYNXIMGMAP:", 11)) {
  2565.     /*
  2566.      *  Special Internal Lynx type.
  2567.      */
  2568.     (void)is_url(&cp[11]);
  2569.     return(LYNXIMGMAP_URL_TYPE);
  2570.  
  2571.     } else if (compare_type(cp, "LYNXCOOKIE:", 11)) {
  2572.     /*
  2573.      *  Special Internal Lynx type.
  2574.      */
  2575.     return(LYNXCOOKIE_URL_TYPE);
  2576.  
  2577.     } else if (strstr((cp+3), "://") == NULL) {
  2578.     /*
  2579.      *  If it doesn't contain "://", and it's not one of the
  2580.      *  the above, it can't be a URL with a scheme we know,
  2581.      *  so check if it's an unknown scheme for which proxying
  2582.      *  has been set up. - FM
  2583.      */
  2584.     return(LYCheckForProxyURL(filename));
  2585.  
  2586.     } else if (compare_type(cp, "http:", 5)) {
  2587.     return(HTTP_URL_TYPE);
  2588.  
  2589.     } else if (compare_type(cp, "https:", 6)) {
  2590.     return(HTTPS_URL_TYPE);
  2591.  
  2592.     } else if (compare_type(cp, "gopher:", 7)) {
  2593.     if ((cp1 = strchr(cp+11,'/')) != NULL) {
  2594.  
  2595.         if (TOUPPER(*(cp1+1)) == 'H' || *(cp1+1) == 'w')
  2596.         /* if this is a gopher html type */
  2597.         return(HTML_GOPHER_URL_TYPE);
  2598.         else if (*(cp1+1) == 'T' || *(cp1+1) == '8')
  2599.         return(TELNET_GOPHER_URL_TYPE);
  2600.         else if (*(cp1+1) == '7')
  2601.         return(INDEX_GOPHER_URL_TYPE);
  2602.         else
  2603.         return(GOPHER_URL_TYPE);
  2604.     } else {
  2605.         return(GOPHER_URL_TYPE);
  2606.     }
  2607.  
  2608.     } else if (compare_type(cp, "ftp:", 4)) {
  2609.     return(FTP_URL_TYPE);
  2610.  
  2611.     } else if (compare_type(cp, "wais:", 5)) {
  2612.     return(WAIS_URL_TYPE);
  2613.  
  2614.     } else if (compare_type(cp, "telnet:", 7)) {
  2615.     return(TELNET_URL_TYPE);
  2616.  
  2617.     } else if (compare_type(cp, "tn3270:", 7)) {
  2618.     return(TN3270_URL_TYPE);
  2619.  
  2620.     } else if (compare_type(cp, "rlogin:", 7)) {
  2621.     return(RLOGIN_URL_TYPE);
  2622.  
  2623.     } else if (compare_type(cp, "cso:", 4)) {
  2624.     return(CSO_URL_TYPE);
  2625.  
  2626.     } else if (compare_type(cp, "finger:", 7)) {
  2627.     return(FINGER_URL_TYPE);
  2628.  
  2629.     } else if (compare_type(cp, "afs:", 4)) {
  2630.     return(AFS_URL_TYPE);
  2631.  
  2632.     } else if (compare_type(cp, "prospero:", 9)) {
  2633.     return(PROSPERO_URL_TYPE);
  2634.  
  2635.     } else {
  2636.     /*
  2637.      *  Check if it's an unknown scheme for which
  2638.      *  proxying has been set up. - FM
  2639.      */
  2640.     return(LYCheckForProxyURL(filename));
  2641.     }
  2642. }
  2643.  
  2644. /*
  2645.  *  Determine whether we allow HEAD and related flags for a URL. - kw
  2646.  */
  2647. PUBLIC BOOLEAN LYCanDoHEAD ARGS1(
  2648.     CONST char *,    address
  2649.     )
  2650. {
  2651.     char *temp0 = NULL;
  2652.     int isurl;
  2653.     if (!(address && *address))
  2654.     return FALSE;
  2655.     if (!strncmp(address, "http", 4))
  2656.     return TRUE;
  2657.     /* Make copy for is_url() since caller may not care for case changes */
  2658.     StrAllocCopy(temp0, address);
  2659.     isurl = is_url(temp0);
  2660.     FREE(temp0);
  2661.     if (!isurl)
  2662.     return FALSE;
  2663.     if (isurl == LYNXCGI_URL_TYPE) {
  2664. #if defined(LYNXCGI_LINKS) && !defined(VMS)
  2665.     return TRUE;
  2666. #else
  2667.     return FALSE;
  2668. #endif
  2669.     }
  2670.     if (isurl == NEWS_URL_TYPE || isurl == NNTP_URL_TYPE) {
  2671.     char *temp = HTParse(address, "", PARSE_PATH);
  2672.     char *cp = strrchr(temp, '/');
  2673.     if (strchr((cp ? cp : temp), '@') != NULL) {
  2674.         FREE(temp);
  2675.         return TRUE;
  2676.     }
  2677.     if (cp && isdigit(cp[1]) && strchr(cp, '-') == NULL) {
  2678.         FREE(temp);
  2679.         return TRUE;
  2680.     }
  2681.     FREE(temp);
  2682.     }
  2683.     return FALSE;
  2684. }
  2685.  
  2686. /*
  2687.  *  Remove backslashes from any string.
  2688.  */
  2689. PUBLIC void remove_backslashes ARGS1(
  2690.     char *,     buf)
  2691. {
  2692.     char *cp;
  2693.  
  2694.     for (cp = buf; *cp != '\0' ; cp++) {
  2695.  
  2696.     if (*cp != '\\') { /* don't print slashes */
  2697.         *buf = *cp;
  2698.         buf++;
  2699.     } else if (*cp == '\\' &&    /* print one slash if there */
  2700.            *(cp+1) == '\\') {    /* are two in a row        */
  2701.         *buf = *cp;
  2702.         buf++;
  2703.     }
  2704.     }
  2705.     *buf = '\0';
  2706.     return;
  2707. }
  2708.  
  2709. /*
  2710.  *  Quote the path to make it safe for shell command processing.
  2711.  *
  2712.  *  We use a simple technique which involves quoting the entire
  2713.  *  string using single quotes, escaping the real single quotes
  2714.  *  with double quotes. This may be gross but it seems to work.
  2715.  */
  2716. PUBLIC char * quote_pathname ARGS1(
  2717.     char *,     pathname)
  2718. {
  2719.     size_t i, n = 0;
  2720.     char * result;
  2721.  
  2722.     for (i=0; i < strlen(pathname); ++i)
  2723.     if (pathname[i] == '\'') ++n;
  2724.  
  2725.     result = (char *)malloc(strlen(pathname) + 5*n + 3);
  2726.     if (result == NULL)
  2727.     outofmem(__FILE__, "quote_pathname");
  2728.  
  2729.     result[0] = '\'';
  2730.     for (i = 0, n = 1; i < strlen(pathname); i++)
  2731.     if (pathname[i] == '\'') {
  2732.         result[n++] = '\'';
  2733.         result[n++] = '"';
  2734.         result[n++] = '\'';
  2735.         result[n++] = '"';
  2736.         result[n++] = '\'';
  2737.     } else {
  2738.         result[n++] = pathname[i];
  2739.     }
  2740.     result[n++] = '\'';
  2741.     result[n] = '\0';
  2742.     return result;
  2743. }
  2744.  
  2745. #if HAVE_UTMP
  2746. extern char *ttyname PARAMS((int fd));
  2747. #endif
  2748.  
  2749. /*
  2750.  *  Checks to see if the current process is attached
  2751.  *  via a terminal in the local domain.
  2752.  *
  2753.  */
  2754. PUBLIC BOOLEAN inlocaldomain NOARGS
  2755. {
  2756. #if ! HAVE_UTMP
  2757.     return(TRUE);
  2758. #else
  2759.     int n;
  2760.     FILE *fp;
  2761.     struct utmp me;
  2762.     char *cp, *mytty = NULL;
  2763.  
  2764.     if ((cp=ttyname(0)))
  2765.     mytty = strrchr(cp, '/');
  2766.  
  2767.     if (mytty && (fp=fopen(UTMP_FILE, "r")) != NULL) {
  2768.         mytty++;
  2769.         do {
  2770.         n = fread((char *) &me, sizeof(struct utmp), 1, fp);
  2771.         } while (n>0 && !STREQ(me.ut_line,mytty));
  2772.         (void) fclose(fp);
  2773.  
  2774.         if (n > 0 &&
  2775.         strlen(me.ut_host) > strlen(LYLocalDomain) &&
  2776.         STREQ(LYLocalDomain,
  2777.           me.ut_host+strlen(me.ut_host)-strlen(LYLocalDomain)) )
  2778.         return(TRUE);
  2779. #ifdef LINUX
  2780. /* Linux fix to check for local user. J.Cullen 11Jul94        */
  2781.         if ((n > 0) && (strlen(me.ut_host) == 0))
  2782.             return(TRUE);
  2783. #endif /* LINUX */
  2784.  
  2785.     } else {
  2786.     CTRACE(tfp,"Could not get ttyname or open UTMP file");
  2787.     }
  2788.  
  2789.     return(FALSE);
  2790. #endif /* !HAVE_UTMP */
  2791. }
  2792.  
  2793. /**************
  2794. ** This bit of code catches window size change signals
  2795. **/
  2796.  
  2797. #ifdef HAVE_SYS_IOCTL_H
  2798. #include <sys/ioctl.h>
  2799. #endif
  2800.  
  2801. /* For systems that have both, but both can't be included, duh */
  2802. #ifdef TERMIO_AND_TERMIOS
  2803. # include <termio.h>
  2804. #else
  2805. # ifdef HAVE_TERMIOS_H
  2806. #  include <termios.h>
  2807. # else
  2808. #  ifdef HAVE_TERMIO_H
  2809. #   include <termio.h>
  2810. #  endif /* HAVE_TERMIO_H */
  2811. # endif /* HAVE_TERMIOS_H */
  2812. #endif    /* TERMIO_AND_TERMIOS */
  2813.  
  2814. PUBLIC void size_change ARGS1(
  2815.     int,        sig GCC_UNUSED)
  2816. {
  2817.     int old_lines = LYlines;
  2818.     int old_cols = LYcols;
  2819.  
  2820. #ifdef USE_SLANG
  2821. #if defined(VMS) || defined(UNIX) 
  2822.     SLtt_get_screen_size();
  2823. #endif /* VMS || UNIX */ 
  2824.     LYlines = SLtt_Screen_Rows;
  2825.     LYcols  = SLtt_Screen_Cols;
  2826. #ifdef SLANG_MBCS_HACK
  2827.     PHYSICAL_SLtt_Screen_Cols = LYcols;
  2828.     SLtt_Screen_Cols = (LYcols * 6);
  2829. #endif /* SLANG_MBCS_HACK */
  2830.     if (sig == 0)
  2831.     /*
  2832.      *  Called from start_curses().
  2833.      */
  2834.     return;
  2835. #else /* Curses: */
  2836. #if HAVE_SIZECHANGE
  2837. #ifdef TIOCGSIZE
  2838.     struct ttysize win;
  2839. #else
  2840. #ifdef TIOCGWINSZ
  2841.     struct winsize win;
  2842. #endif /* TIOCGWINSZ */
  2843. #endif /* TIOCGSIZE */
  2844.  
  2845. #ifdef TIOCGSIZE
  2846.     if (ioctl(0, TIOCGSIZE, &win) == 0) {
  2847.     if (win.ts_lines != 0) {
  2848.         LYlines = win.ts_lines;
  2849.     }
  2850.     if (win.ts_cols != 0) {
  2851.         LYcols = win.ts_cols;
  2852.     }
  2853.     }
  2854. #else
  2855. #ifdef TIOCGWINSZ
  2856.     if (ioctl(0, TIOCGWINSZ, &win) == 0) {
  2857.     if (win.ws_row != 0) {
  2858.         LYlines = win.ws_row;
  2859.     }
  2860.     if (win.ws_col != 0) {
  2861.         LYcols = win.ws_col;
  2862.     }
  2863.     }
  2864. #endif /* TIOCGWINSZ */
  2865. #endif /* TIOCGSIZE */
  2866. #endif /* HAVE_SIZECHANGE */
  2867.  
  2868.     if (LYlines <= 0)
  2869.     LYlines = 24;
  2870.     if (LYcols <= 0)
  2871.     LYcols = 80;
  2872. #endif /* USE_SLANG */
  2873.  
  2874.     /*
  2875.      *    Check if the screen size has actually changed. - AJL
  2876.      */
  2877.     if (LYlines != old_lines || LYcols != old_cols) {
  2878.     recent_sizechange = TRUE;
  2879.     }
  2880.     CTRACE(tfp, "Window size changed from (%d,%d) to (%d,%d)\n",
  2881.         old_lines, old_cols, LYlines, LYcols);
  2882. #ifdef SIGWINCH
  2883.     (void)signal (SIGWINCH, size_change);
  2884. #endif /* SIGWINCH */
  2885.  
  2886.     return;
  2887. }
  2888.  
  2889. /*
  2890.  *  Utility for freeing the list of previous suggested filenames. - FM
  2891.  */
  2892. PUBLIC void HTSugFilenames_free NOARGS
  2893. {
  2894.     char *fname;
  2895.     HTList *cur = sug_filenames;
  2896.  
  2897.     if (!cur)
  2898.     return;
  2899.  
  2900.     while (NULL != (fname = (char *)HTList_nextObject(cur))) {
  2901.     FREE(fname);
  2902.     }
  2903.     HTList_delete(sug_filenames);
  2904.     sug_filenames = NULL;
  2905.     return;
  2906. }
  2907.  
  2908. /*
  2909.  *  Utility for listing suggested filenames, making any
  2910.  *  repeated filenanmes the most current in the list. - FM
  2911.  */
  2912. PUBLIC void HTAddSugFilename ARGS1(
  2913.     char *,     fname)
  2914. {
  2915.     char *new;
  2916.     char *old;
  2917.     HTList *cur;
  2918.  
  2919.     if (!(fname && *fname))
  2920.     return;
  2921.  
  2922.     if ((new = (char *)calloc(1, (strlen(fname) + 1))) == NULL)
  2923.     outofmem(__FILE__, "HTAddSugFilename");
  2924.     strcpy(new, fname);
  2925.  
  2926.     if (!sug_filenames) {
  2927.     sug_filenames = HTList_new();
  2928.     atexit(HTSugFilenames_free);
  2929.     HTList_addObject(sug_filenames, new);
  2930.     return;
  2931.     }
  2932.  
  2933.     cur = sug_filenames;
  2934.     while (NULL != (old = (char *)HTList_nextObject(cur))) {
  2935.     if (!strcmp(old, new)) {
  2936.         HTList_removeObject(sug_filenames, old);
  2937.         FREE(old);
  2938.         break;
  2939.     }
  2940.     }
  2941.     HTList_addObject(sug_filenames, new);
  2942.  
  2943.     return;
  2944. }
  2945.  
  2946. /*
  2947.  *  CHANGE_SUG_FILENAME -- Foteos Macrides 29-Dec-1993
  2948.  *    Upgraded for use with Lynx2.2 - FM 17-Jan-1994
  2949.  */
  2950. PUBLIC void change_sug_filename ARGS1(
  2951.     char *,     fname)
  2952. {
  2953.     char *temp, *cp, *cp1, *end;
  2954. #ifdef VMS
  2955.     char *dot;
  2956.     int j, k;
  2957. #endif /* VMS */
  2958.  
  2959.     /*
  2960.      *    Establish the current end of fname.
  2961.      */
  2962.     end = fname + strlen(fname);
  2963.  
  2964.     /*
  2965.      *    Unescape fname.
  2966.      */
  2967.     HTUnEscape(fname);
  2968.  
  2969.     /*
  2970.      *    Rename any temporary files.
  2971.      */
  2972.     temp = (char *)calloc(1, (strlen(lynx_temp_space) + 60));
  2973. #if defined(FNAMES_8_3) && defined(DOSPATH)
  2974.     cp = HTDOS_wwwName(lynx_temp_space);
  2975. #else
  2976.     cp = lynx_temp_space;
  2977. #endif
  2978.     if (*cp == '/') {
  2979.     sprintf(temp, "file://localhost%s%d", cp, (int)getpid());
  2980.     } else {
  2981.     sprintf(temp, "file://localhost/%s%d", cp, (int)getpid());
  2982.     }
  2983.     if (!strncmp(fname, temp, strlen(temp))) {
  2984.     cp = strrchr(fname, '.');
  2985.     if (strlen(cp) > (strlen(temp) - 4))
  2986.         cp = NULL;
  2987.     strcpy(temp, (cp ? cp : ""));
  2988.     strcpy(fname, "temp");
  2989.     strcat(fname, temp);
  2990.     }
  2991.     FREE(temp);
  2992.  
  2993.     /*
  2994.      *    Remove everything up the the last_slash if there is one.
  2995.      */
  2996.     if ((cp = strrchr(fname,'/')) != NULL && strlen(cp) > 1) {
  2997.     cp1 = fname;
  2998.     /*
  2999.      *  Go past the slash.
  3000.      */
  3001.     cp++;
  3002.     for (; *cp != '\0'; cp++, cp1++) {
  3003.         *cp1 = *cp;
  3004.     }
  3005.     *cp1 = '\0';
  3006.     }
  3007.  
  3008.     /*
  3009.      *    Trim off date-size suffix, if present.
  3010.      */
  3011.     if ((*(end - 1) == ']') && ((cp = strrchr(fname, '[')) != NULL) &&
  3012.     (cp > fname) && *(--cp) == ' ') {
  3013.     while (*cp == ' ') {
  3014.         *(cp--) = '\0';
  3015.     }
  3016.     }
  3017.  
  3018.     /*
  3019.      *    Trim off VMS device and/or directory specs, if present.
  3020.      */
  3021.     if ((cp = strchr(fname,'[')) != NULL &&
  3022.     (cp1 = strrchr(cp,']')) != NULL && strlen(cp1) > 1) {
  3023.     cp1++;
  3024.     for (cp=fname; *cp1 != '\0'; cp1++) {
  3025.         *(cp++) = *cp1;
  3026.     }
  3027.     *cp = '\0';
  3028.     }
  3029.  
  3030. #ifdef VMS
  3031.     /*
  3032.      *    Replace illegal or problem characters.
  3033.      */
  3034.     dot = fname + strlen(fname);
  3035.     for (cp = fname; cp < dot; cp++) {
  3036.     /*
  3037.      *  Replace with underscores.
  3038.      */
  3039.     if (*cp == ' ' || *cp == '/' || *cp == ':' ||
  3040.         *cp == '[' || *cp == ']' || *cp == '&') {
  3041.         *cp = '_';
  3042.     /*
  3043.      *  Replace with dashes.
  3044.      */
  3045.     } else if (*cp == '!' || *cp == '?' || *cp == '\'' ||
  3046.            *cp == ',' || *cp == ':' || *cp == '\"' ||
  3047.            *cp == '+' || *cp == '@' || *cp == '\\' ||
  3048.            *cp == '(' || *cp == ')' || *cp == '=' ||
  3049.            *cp == '<' || *cp == '>' || *cp == '#' ||
  3050.            *cp == '%' || *cp == '*' || *cp == '`' ||
  3051.            *cp == '~' || *cp == '^' || *cp == '|' ||
  3052.            *cp <  ' ' || ((unsigned char)*cp) > 126) {
  3053.         *cp = '-';
  3054.     }
  3055.     }
  3056.  
  3057.     /*
  3058.      *    Collapse any serial underscores.
  3059.      */
  3060.     cp = fname + 1;
  3061.     j = 0;
  3062.     while (cp < dot) {
  3063.     if (fname[j] == '_' && *cp == '_') {
  3064.         cp++;
  3065.     } else {
  3066.         fname[++j] = *cp++;
  3067.     }
  3068.     }
  3069.     fname[++j] = '\0';
  3070.  
  3071.     /*
  3072.      *    Collapse any serial dashes.
  3073.      */
  3074.     dot = fname + (strlen(fname));
  3075.     cp = fname + 1;
  3076.     j = 0;
  3077.     while (cp < dot) {
  3078.     if (fname[j] == '-' && *cp == '-') {
  3079.         cp++;
  3080.     }  else {
  3081.         fname[++j] = *cp++;
  3082.     }
  3083.     }
  3084.     fname[++j] = '\0';
  3085.  
  3086.     /*
  3087.      *    Trim any trailing or leading
  3088.      *    underscrores or dashes.
  3089.      */
  3090.     cp = fname + (strlen(fname)) - 1;
  3091.     while (*cp == '_' || *cp == '-') {
  3092.     *cp-- = '\0';
  3093.     }
  3094.     if (fname[0] == '_' || fname[0] == '-') {
  3095.     dot = fname + (strlen(fname));
  3096.     cp = fname;
  3097.     while ((*cp == '_' || *cp == '-') && cp < dot) {
  3098.         cp++;
  3099.     }
  3100.     j = 0;
  3101.     while (cp < dot) {
  3102.         fname[j++] = *cp++;
  3103.     }
  3104.     fname[j] = '\0';
  3105.     }
  3106.  
  3107.     /*
  3108.      *    Replace all but the last period with _'s, or second
  3109.      *    to last if last is followed by a terminal Z or z,
  3110.      *    or GZ or gz,
  3111.      *    e.g., convert foo.tar.Z to
  3112.      *              foo.tar_Z
  3113.      *      or, convert foo.tar.gz to
  3114.      *              foo.tar-gz
  3115.      */
  3116.     j = strlen(fname) - 1;
  3117.     if ((dot = strrchr(fname, '.')) != NULL) {
  3118.     if (TOUPPER(fname[j]) == 'Z') {
  3119.         if ((fname[j-1] == '.') &&
  3120.         (((cp = strchr(fname, '.')) != NULL) && cp < dot)) {
  3121.         *dot = '_';
  3122.         dot = strrchr(fname, '.');
  3123.         } else if (((TOUPPER(fname[j-1]) == 'G') &&
  3124.             fname[j-2] == '.') &&
  3125.                (((cp = strchr(fname, '.')) != NULL) && cp < dot)) {
  3126.         *dot = '-';
  3127.         dot = strrchr(fname, '.');
  3128.         }
  3129.     }
  3130.     cp = fname;
  3131.     while ((cp = strchr(cp, '.')) != NULL && cp < dot) {
  3132.         *cp = '_';
  3133.     }
  3134.  
  3135.     /*
  3136.      *  But if the root is > 39 characters, move
  3137.      *  the period appropriately to the left.
  3138.      */
  3139.     while (dot - fname > 39) {
  3140.         *dot = '\0';
  3141.         if ((cp = strrchr(fname, '_')) != NULL) {
  3142.         *cp  = '.';
  3143.         *dot = '_';
  3144.         } else if ((cp = strrchr(fname, '-')) != NULL) {
  3145.         *cp  = '.';
  3146.         *dot = '_';
  3147.         } else if (*(dot + 1) == '\0') {
  3148.         j = strlen(fname);
  3149.         while (j > 39) {
  3150.             fname[j] = fname[j-1];
  3151.             j--;
  3152.         }
  3153.         fname[j] = '.';
  3154.         } else {
  3155.         *dot = '.';
  3156.         j = 39;
  3157.         k = 0;
  3158.         while (dot[k] != '\0') {
  3159.             fname[j++] = dot[k++];
  3160.         }
  3161.         fname[j] = '\0';
  3162.         }
  3163.         dot = strrchr(fname, '.');
  3164.     }
  3165.  
  3166.     /*
  3167.      *  Make sure the extension is < 40 characters.
  3168.      */
  3169.     if ((fname + strlen(fname) - dot) > 39) {
  3170.         *(dot + 40) = '\0';
  3171.     }
  3172.  
  3173.     /*
  3174.      *  Trim trailing dashes or underscores.
  3175.      */
  3176.     j = (strlen(fname) - 1);
  3177.     while (fname[j] == '_' || fname[j] == '-') {
  3178.         fname[j--] = '\0';
  3179.     }
  3180.     } else {
  3181.     /*
  3182.      *  No period, so put one on the end, or after
  3183.      *  the 39th character, trimming trailing dashes
  3184.      *  or underscrores.
  3185.      */
  3186.     if (strlen(fname) > 39) {
  3187.         fname[39] = '\0';
  3188.     }
  3189.     j = (strlen(fname) - 1);
  3190.     while ((fname[j] == '_') || (fname[j] == '-')) {
  3191.         j--;
  3192.     }
  3193.     fname[++j] = '.';
  3194.     fname[++j] = '\0';
  3195.     }
  3196.  
  3197. #else /* Not VMS (UNIX): */
  3198.  
  3199.     /*
  3200.      *    Replace problem characters.
  3201.      */
  3202.     for (cp = fname; *cp != '\0'; cp++) {
  3203.     switch (*cp) {
  3204.         case '\'':
  3205.         case '\"':
  3206.         case '/':
  3207.         case ' ':
  3208.         *cp = '-';
  3209.     }
  3210.     }
  3211. #endif /* VMS (UNIX) */
  3212.  
  3213.     /*
  3214.      *    Make sure the rest of the original string in nulled.
  3215.      */
  3216.     cp = fname + strlen(fname);
  3217.     while (cp < end) {
  3218.     *cp++ = '\0';
  3219.     }
  3220.  
  3221.     return;
  3222. }
  3223.  
  3224. /*
  3225.  * Construct a name for 'tempname()'
  3226.  */
  3227. PRIVATE char *fmt_tempname ARGS3(
  3228.     char *,        result,
  3229.     unsigned,    counter,
  3230.     char *,        suffix)
  3231. {
  3232.     sprintf(result,
  3233. #ifdef FNAMES_8_3
  3234.     "%s%u%u%s",
  3235. #else
  3236.     "%sL%u-%uTMP%s",
  3237. #endif
  3238.     lynx_temp_space, (unsigned)getpid(), counter, suffix);
  3239.     CTRACE(tfp, "-> '%s'\n", result);
  3240.     return result;
  3241. }
  3242.  
  3243. /*
  3244.  * 'tempname()' requires that the resulting file does not exist.  Test this
  3245.  * by trying to open it.
  3246.  */
  3247. PRIVATE BOOLEAN bad_tempname ARGS3(
  3248.     char *,        result,
  3249.     unsigned,    counter,
  3250.     char *,        suffix)
  3251. {
  3252.     FILE *fp;
  3253.  
  3254.     if ((fp = fopen(fmt_tempname(result, counter, suffix), "r")) != 0) {
  3255.     fclose(fp);
  3256.     CTRACE(tfp, "tempname: file '%s' already exists!\n", result);
  3257.     return TRUE;
  3258.     }
  3259.  
  3260.     return FALSE;
  3261. }
  3262.  
  3263. /*
  3264.  *  To create standard temporary file names.
  3265.  */
  3266. PUBLIC void tempname ARGS2(
  3267.     char *,     namebuffer,
  3268.     int,        action)
  3269. {
  3270.     static unsigned counter = 0;
  3271. #ifdef FNAMES_8_3
  3272.     unsigned LYMaxTempCount = 1000; /* Arbitrary limit.  Make it configurable? */
  3273. #else
  3274.     unsigned LYMaxTempCount = 10000; /* Arbitrary limit.  Make it configurable? */
  3275. #endif /* FNAMES_8_3 */
  3276.  
  3277.     if (action == REMOVE_FILES) {
  3278.     /*
  3279.      *  Remove all temporary files with .txt or .html suffixes. - FM
  3280.      */
  3281.     CTRACE(tfp, "tempname REMOVE_FILES (%d)\n", counter);
  3282.     for (; counter != 0; counter--) {
  3283.         remove(fmt_tempname(namebuffer, counter-1, ".txt"));
  3284.         remove(fmt_tempname(namebuffer, counter-1, HTML_SUFFIX));
  3285.     }
  3286.     } else {
  3287.     /*
  3288.      *  Load a tentative temporary file name into namebuffer. - FM
  3289.      */
  3290.     CTRACE(tfp, "tempname NEW_FILE (%d)\n", counter);
  3291.     while (counter < LYMaxTempCount) {
  3292.         /*
  3293.          *    Create names with .txt, then .bin, then
  3294.          *    .html suffixes, and check for their prior
  3295.          *    existence.  If any already exist, someone
  3296.          *    might be trying to spoof us, so increment
  3297.          *    the count and try again.  Otherwise, return
  3298.          *    with the name which has the .html suffix
  3299.          *    loaded in namebuffer. - FM
  3300.          *
  3301.          *    Some systems may use .htm instead of .html.  This
  3302.          *    should be done consistently by always using HTML_SUFFIX
  3303.          *    where filenames are generated for new local files. - kw
  3304.          */
  3305.         if (bad_tempname(namebuffer, counter, ".txt")
  3306.          || bad_tempname(namebuffer, counter, ".bin")
  3307.          || bad_tempname(namebuffer, counter, HTML_SUFFIX)) {
  3308.         continue;
  3309.         }
  3310.         /*
  3311.          *    Return to the calling function, with the tentative
  3312.          *    temporary file name loaded in namebuffer.  Note that
  3313.          *    if the calling function will use a suffix other than
  3314.          *    .txt, .bin, or .html, it similarly should do tests for
  3315.          *    a spoof.  The file name can be reused if it is written
  3316.          *    to on receipt of this name, and thereafter accessed
  3317.          *    for reading.  Note that if writing to a file is to
  3318.          *    be followed by reading it, as is the usual case for
  3319.          *    Lynx, the spoof attempt will be apparent, and the user
  3320.          *    can take appropriate action. - FM
  3321.          */
  3322.         counter++;
  3323.         return;
  3324.     }
  3325.     /*
  3326.      *  The tempfile maximum count has been reached.
  3327.      *  Issue a message and exit. - FM
  3328.      */
  3329.     _statusline(MAX_TEMPCOUNT_REACHED);
  3330.     sleep(AlertSecs);
  3331.     exit(-1);
  3332.     }
  3333.  
  3334.     /*
  3335.      *    We were called for a clean up, and have done it. - FM
  3336.      */
  3337.     return;
  3338. }
  3339.  
  3340. /*
  3341.  *  Convert 4, 6, 2, 8 to left, right, down, up, etc.
  3342.  */
  3343. PUBLIC int number2arrows ARGS1(
  3344.     int,        number)
  3345. {
  3346.     switch(number) {
  3347.     case '1':
  3348.         number=END_KEY;
  3349.         break;
  3350.     case '2':
  3351.         number=DNARROW;
  3352.         break;
  3353.     case '3':
  3354.         number=PGDOWN;
  3355.         break;
  3356.     case '4':
  3357.         number=LTARROW;
  3358.         break;
  3359.     case '5':
  3360.         number=DO_NOTHING;
  3361.         break;
  3362.     case '6':
  3363.         number=RTARROW;
  3364.         break;
  3365.     case '7':
  3366.         number=HOME;
  3367.         break;
  3368.     case '8':
  3369.         number=UPARROW;
  3370.         break;
  3371.     case '9':
  3372.         number=PGUP;
  3373.         break;
  3374.     }
  3375.  
  3376.     return(number);
  3377. }
  3378.  
  3379. /*
  3380.  *  parse_restrictions takes a string of comma-separated restrictions
  3381.  *  and sets the corresponding flags to restrict the facilities available.
  3382.  */
  3383. PRIVATE char *restrict_name[] = {
  3384.        "inside_telnet" ,
  3385.        "outside_telnet",
  3386.        "telnet_port"   ,
  3387.        "inside_ftp"    ,
  3388.        "outside_ftp"   ,
  3389.        "inside_rlogin" ,
  3390.        "outside_rlogin",
  3391.        "suspend"       ,
  3392.        "editor"        ,
  3393.        "shell"           ,
  3394.        "bookmark"      ,
  3395.        "multibook"     ,
  3396.        "bookmark_exec" ,
  3397.        "option_save"   ,
  3398.        "print"           ,
  3399.        "download"      ,
  3400.        "disk_save"     ,
  3401.        "exec"           ,
  3402.        "lynxcgi"       ,
  3403.        "exec_frozen"   ,
  3404.        "goto"           ,
  3405.        "jump"           ,
  3406.        "file_url"      ,
  3407.        "news_post"     ,
  3408.        "inside_news"   ,
  3409.        "outside_news"  ,
  3410.        "mail"           ,
  3411.        "dotfiles"      ,
  3412.        "useragent"     ,
  3413. #ifdef DIRED_SUPPORT
  3414.        "dired_support" ,
  3415. #ifdef OK_PERMIT
  3416.        "change_exec_perms",
  3417. #endif /* OK_PERMIT */
  3418. #endif /* DIRED_SUPPORT */
  3419. #ifdef USE_EXTERNALS
  3420.        "externals" ,
  3421. #endif
  3422.        (char *) 0     };
  3423.  
  3424.     /* restrict_name and restrict_flag structure order
  3425.      * must be maintained exactly!
  3426.      */
  3427.  
  3428. PRIVATE BOOLEAN *restrict_flag[] = {
  3429.        &no_inside_telnet,
  3430.        &no_outside_telnet,
  3431.        &no_telnet_port,
  3432.        &no_inside_ftp,
  3433.        &no_outside_ftp,
  3434.        &no_inside_rlogin,
  3435.        &no_outside_rlogin,
  3436.        &no_suspend  ,
  3437.        &no_editor   ,
  3438.        &no_shell    ,
  3439.        &no_bookmark ,
  3440.        &no_multibook ,
  3441.        &no_bookmark_exec,
  3442.        &no_option_save,
  3443.        &no_print    ,
  3444.        &no_download ,
  3445.        &no_disk_save,
  3446.        &no_exec     ,
  3447.        &no_lynxcgi  ,
  3448.        &exec_frozen ,
  3449.        &no_goto     ,
  3450.        &no_jump     ,
  3451.        &no_file_url ,
  3452.        &no_newspost ,
  3453.        &no_inside_news,
  3454.        &no_outside_news,
  3455.        &no_mail     ,
  3456.        &no_dotfiles ,
  3457.        &no_useragent ,
  3458. #ifdef DIRED_SUPPORT
  3459.        &no_dired_support,
  3460. #ifdef OK_PERMIT
  3461.        &no_change_exec_perms,
  3462. #endif /* OK_PERMIT */
  3463. #endif /* DIRED_SUPPORT */
  3464. #ifdef USE_EXTERNALS
  3465.        &no_externals ,
  3466. #endif
  3467.        (BOOLEAN *) 0  };
  3468.  
  3469. PUBLIC void parse_restrictions ARGS1(
  3470.     char *,     s)
  3471. {
  3472.       char *p;
  3473.       char *word;
  3474.       int i;
  3475.  
  3476.       if (STREQ("all", s)) {
  3477.        /* set all restrictions */
  3478.       for (i=0; restrict_flag[i]; i++)
  3479.           *restrict_flag[i] = TRUE;
  3480.       return;
  3481.       }
  3482.  
  3483.       if (STREQ("default", s)) {
  3484.        /* set all restrictions */
  3485.       for (i=0; restrict_flag[i]; i++)
  3486.           *restrict_flag[i] = TRUE;
  3487.  
  3488.          /* reset these to defaults */
  3489.          no_inside_telnet = !(CAN_ANONYMOUS_INSIDE_DOMAIN_TELNET);
  3490.         no_outside_telnet = !(CAN_ANONYMOUS_OUTSIDE_DOMAIN_TELNET);
  3491.            no_inside_news = !(CAN_ANONYMOUS_INSIDE_DOMAIN_READ_NEWS);
  3492.           no_outside_news = !(CAN_ANONYMOUS_OUTSIDE_DOMAIN_READ_NEWS);
  3493.         no_inside_ftp = !(CAN_ANONYMOUS_INSIDE_DOMAIN_FTP);
  3494.            no_outside_ftp = !(CAN_ANONYMOUS_OUTSIDE_DOMAIN_FTP);
  3495.          no_inside_rlogin = !(CAN_ANONYMOUS_INSIDE_DOMAIN_RLOGIN);
  3496.         no_outside_rlogin = !(CAN_ANONYMOUS_OUTSIDE_DOMAIN_RLOGIN);
  3497.               no_goto = !(CAN_ANONYMOUS_GOTO);
  3498.           no_goto_cso = !(CAN_ANONYMOUS_GOTO_CSO);
  3499.          no_goto_file = !(CAN_ANONYMOUS_GOTO_FILE);
  3500.            no_goto_finger = !(CAN_ANONYMOUS_GOTO_FINGER);
  3501.           no_goto_ftp = !(CAN_ANONYMOUS_GOTO_FTP);
  3502.            no_goto_gopher = !(CAN_ANONYMOUS_GOTO_GOPHER);
  3503.          no_goto_http = !(CAN_ANONYMOUS_GOTO_HTTP);
  3504.         no_goto_https = !(CAN_ANONYMOUS_GOTO_HTTPS);
  3505.           no_goto_lynxcgi = !(CAN_ANONYMOUS_GOTO_LYNXCGI);
  3506.          no_goto_lynxexec = !(CAN_ANONYMOUS_GOTO_LYNXEXEC);
  3507.          no_goto_lynxprog = !(CAN_ANONYMOUS_GOTO_LYNXPROG);
  3508.            no_goto_mailto = !(CAN_ANONYMOUS_GOTO_MAILTO);
  3509.          no_goto_news = !(CAN_ANONYMOUS_GOTO_NEWS);
  3510.          no_goto_nntp = !(CAN_ANONYMOUS_GOTO_NNTP);
  3511.            no_goto_rlogin = !(CAN_ANONYMOUS_GOTO_RLOGIN);
  3512.         no_goto_snews = !(CAN_ANONYMOUS_GOTO_SNEWS);
  3513.            no_goto_telnet = !(CAN_ANONYMOUS_GOTO_TELNET);
  3514.            no_goto_tn3270 = !(CAN_ANONYMOUS_GOTO_TN3270);
  3515.          no_goto_wais = !(CAN_ANONYMOUS_GOTO_WAIS);
  3516.            no_telnet_port = !(CAN_ANONYMOUS_GOTO_TELNET_PORT);
  3517.               no_jump = !(CAN_ANONYMOUS_JUMP);
  3518.               no_mail = !(CAN_ANONYMOUS_MAIL);
  3519.              no_print = !(CAN_ANONYMOUS_PRINT);
  3520. #if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)
  3521.               no_exec = LOCAL_EXECUTION_LINKS_ALWAYS_OFF_FOR_ANONYMOUS;
  3522. #endif /* EXEC_LINKS || EXEC_SCRIPTS */
  3523.       return;
  3524.       }
  3525.  
  3526.       p = s;
  3527.       while (*p) {
  3528.       p = LYSkipBlanks(p);
  3529.       if (*p == '\0')
  3530.           break;
  3531.       word = p;
  3532.       while (*p != ',' && *p != '\0')
  3533.           p++;
  3534.       if (*p)
  3535.           *p++ = '\0';
  3536.  
  3537.       for (i=0; restrict_name[i]; i++)
  3538.          if (STREQ(word, restrict_name[i])) {
  3539.          *restrict_flag[i] = TRUE;
  3540.          break;
  3541.          }
  3542.       }
  3543.       return;
  3544. }
  3545.  
  3546. #ifdef VMS
  3547. #include <jpidef.h>
  3548. #include <maildef.h>
  3549. #include <starlet.h>
  3550.  
  3551. typedef struct _VMSMailItemList
  3552. {
  3553.   short buffer_length;
  3554.   short item_code;
  3555.   void *buffer_address;
  3556.   long *return_length_address;
  3557. } VMSMailItemList;
  3558.  
  3559. PUBLIC int LYCheckMail NOARGS
  3560. {
  3561.     static BOOL firsttime = TRUE, failure = FALSE;
  3562.     static char user[13], dir[252];
  3563.     static long userlen = 0, dirlen;
  3564.     static time_t lastcheck = 0;
  3565.     time_t now;
  3566.     static short new, lastcount;
  3567.     long ucontext = 0, status;
  3568.     short flags = MAIL$M_NEWMSG;
  3569.     VMSMailItemList
  3570.       null_list[] = {{0,0,0,0}},
  3571.       jpi_list[]  = {{sizeof(user) - 1,JPI$_USERNAME,(void *)user,&userlen},
  3572.              {0,0,0,0}},
  3573.       uilist[]      = {{0,MAIL$_USER_USERNAME,0,0},
  3574.              {0,0,0,0}},
  3575.       uolist[]      = {{sizeof(new),MAIL$_USER_NEW_MESSAGES,&new,0},
  3576.              {sizeof(dir),MAIL$_USER_FULL_DIRECTORY,dir,&dirlen},
  3577.              {0,0,0,0}};
  3578.     extern long mail$user_begin();
  3579.     extern long mail$user_get_info();
  3580.     extern long mail$user_end();
  3581.  
  3582.     if (failure)
  3583.     return 0;
  3584.  
  3585.     if (firsttime) {
  3586.     firsttime = FALSE;
  3587.     /* Get the username. */
  3588.     status = sys$getjpiw(0,0,0,jpi_list,0,0,0);
  3589.     if (!(status & 1)) {
  3590.         failure = TRUE;
  3591.         return 0;
  3592.     }
  3593.     user[userlen] = '\0';
  3594.     LYTrimTrailing(user);
  3595.     }
  3596.  
  3597.     /* Minimum report interval is 60 sec. */
  3598.     time(&now);
  3599.     if (now - lastcheck < 60)
  3600.     return 0;
  3601.     lastcheck = now;
  3602.  
  3603.     /* Get the current newmail count. */
  3604.     status = mail$user_begin(&ucontext,null_list,null_list);
  3605.     if (!(status & 1)) {
  3606.     failure = TRUE;
  3607.     return 0;
  3608.     }
  3609.     uilist[0].buffer_length = strlen(user);
  3610.     uilist[0].buffer_address = user;
  3611.     status = mail$user_get_info(&ucontext,uilist,uolist);
  3612.     if (!(status & 1)) {
  3613.     failure = TRUE;
  3614.     return 0;
  3615.     }
  3616.  
  3617.     /* Should we report anything to the user? */
  3618.     if (new > 0) {
  3619.     if (lastcount == 0)
  3620.         /* Have newmail at startup of Lynx. */
  3621.         _statusline(HAVE_UNREAD_MAIL_MSG);
  3622.     else if (new > lastcount)
  3623.         /* Have additional mail since last report. */
  3624.         _statusline(HAVE_NEW_MAIL_MSG);
  3625.     lastcount = new;
  3626.     return 1;
  3627.     }
  3628.     lastcount = new;
  3629.  
  3630.     /* Clear the context */
  3631.     mail$user_end((long *)&ucontext,null_list,null_list);
  3632.     return 0;
  3633. }
  3634. #else
  3635. PUBLIC int LYCheckMail NOARGS
  3636. {
  3637.     static BOOL firsttime = TRUE;
  3638.     static char *mf;
  3639.     static time_t lastcheck;
  3640.     static long lastsize;
  3641.     time_t now;
  3642.     struct stat st;
  3643.  
  3644.     if (firsttime) {
  3645.     mf = getenv("MAIL");
  3646.     firsttime = FALSE;
  3647.     }
  3648.  
  3649.     if (mf == NULL)
  3650.     return 0;
  3651.  
  3652.     time(&now);
  3653.     if (now - lastcheck < 60)
  3654.     return 0;
  3655.     lastcheck = now;
  3656.  
  3657.     if (stat(mf,&st) < 0) {
  3658.     mf = NULL;
  3659.     return 0;
  3660.     }
  3661.  
  3662.     if (st.st_size > 0) {
  3663.     if (st.st_mtime > st.st_atime ||
  3664.         (lastsize && st.st_size > lastsize))
  3665.         _statusline(HAVE_NEW_MAIL_MSG);
  3666.     else if (lastsize == 0)
  3667.         _statusline(HAVE_MAIL_MSG);
  3668.     lastsize = st.st_size;
  3669.     return 1;
  3670.     }
  3671.     lastsize = st.st_size;
  3672.     return 0;
  3673. }
  3674. #endif /* VMS */
  3675.  
  3676. /*
  3677. **  This function ensures that an href will be
  3678. **  converted to a fully resolved, absolute URL,
  3679. **  with guessing of the host or expansions of
  3680. **  lead tildes via LYConvertToURL() if needed,
  3681. **  and tweaking/simplifying via HTParse().  It
  3682. **  is used for LynxHome, startfile, homepage,
  3683. **  an 'g'oto entries, after they have been
  3684. **  passed to LYFillLocalFileURL(). - FM
  3685. */
  3686. PUBLIC void LYEnsureAbsoluteURL ARGS2(
  3687.     char **,    href,
  3688.     char *,     name)
  3689. {
  3690.     char *temp = NULL;
  3691.  
  3692.     if (!(*href && *(*href)))
  3693.     return;
  3694.  
  3695.     /*
  3696.      *    If it is not a URL then make it one.
  3697.      */
  3698.     if (!strcasecomp(*href, "news:")) {
  3699.     StrAllocCat(*href, "*");
  3700.     } else if (!strcasecomp(*href, "nntp:") ||
  3701.            !strcasecomp(*href, "snews:")) {
  3702.     StrAllocCat(*href, "/*");
  3703.     }
  3704.     if (!is_url(*href)) {
  3705.     CTRACE(tfp, "%s%s'%s' is not a URL\n",
  3706.             (name ? name : ""), (name ? " " : ""), *href);
  3707.     LYConvertToURL(href);
  3708.     }
  3709.     if ((temp = HTParse(*href, "", PARSE_ALL)) != NULL && *temp != '\0')
  3710.     StrAllocCopy(*href, temp);
  3711.     FREE(temp);
  3712. }
  3713.  
  3714. /*
  3715.  *  Rewrite and reallocate a previously allocated string
  3716.  *  as a file URL if the string resolves to a file or
  3717.  *  directory on the local system, otherwise as an
  3718.  *  http URL. - FM
  3719.  */
  3720. PUBLIC void LYConvertToURL ARGS1(
  3721.     char **,    AllocatedString)
  3722. {
  3723.     char *old_string = *AllocatedString;
  3724.     char *temp = NULL;
  3725.     char *cp = NULL;
  3726. #ifndef VMS
  3727.     struct stat st;
  3728.     FILE *fptemp = NULL;
  3729. #endif /* !VMS */
  3730.  
  3731.     if (!old_string || *old_string == '\0')
  3732.     return;
  3733.  
  3734. #if defined(DOSPATH) || defined(__EMX__)
  3735.     {
  3736.      char *cp_url = *AllocatedString;
  3737.      for(; *cp_url != '\0'; cp_url++)
  3738.         if(*cp_url == '\\') *cp_url = '/';
  3739.      cp_url--;
  3740.      if(*cp_url == ':')
  3741.          StrAllocCat(*AllocatedString,"/");
  3742. #ifdef NOTDEFINED
  3743.      if(strlen(old_string) > 3 && *cp_url == '/')
  3744.         *cp_url = '\0';
  3745. #endif
  3746.     }
  3747. #endif /* DOSPATH */
  3748.  
  3749.     *AllocatedString = NULL;  /* so StrAllocCopy doesn't free it */
  3750.     StrAllocCopy(*AllocatedString,"file://localhost");
  3751.  
  3752.     if (*old_string != '/') {
  3753.     char *fragment = NULL;
  3754. #if defined(DOSPATH) || defined(__EMX__)
  3755.     StrAllocCat(*AllocatedString,"/");
  3756. #endif /* DOSPATH */
  3757. #ifdef VMS
  3758.     /*
  3759.      *  Not a SHELL pathspec.  Get the full VMS spec and convert it.
  3760.      */
  3761.     char *cur_dir = NULL;
  3762.     static char url_file[256], file_name[256], dir_name[256];
  3763.     unsigned long context = 0;
  3764.     $DESCRIPTOR(url_file_dsc, url_file);
  3765.     $DESCRIPTOR(file_name_dsc, file_name);
  3766.     if (*old_string == '~') {
  3767.         /*
  3768.          *    On VMS, we'll accept '~' on the command line as
  3769.          *    Home_Dir(), and assume the rest of the path, if
  3770.          *    any, has SHELL syntax.
  3771.          */
  3772.         StrAllocCat(*AllocatedString, HTVMS_wwwName((char *)Home_Dir()));
  3773.         if ((cp = strchr(old_string, '/')) != NULL) {
  3774.         /*
  3775.          *  Append rest of path, if present, skipping "user" if
  3776.          *  "~user" was entered, simplifying, and eliminating
  3777.          *  any residual relative elements. - FM
  3778.          */
  3779.         StrAllocCopy(temp, cp);
  3780.         LYTrimRelFromAbsPath(temp);
  3781.         StrAllocCat(*AllocatedString, temp);
  3782.         FREE(temp);
  3783.         }
  3784.         goto have_VMS_URL;
  3785.     } else {
  3786.         if ((fragment = strchr(old_string, '#')) != NULL)
  3787.         *fragment = '\0';
  3788.         strcpy(url_file, old_string);
  3789.     }
  3790.     url_file_dsc.dsc$w_length = (short) strlen(url_file);
  3791.     if (1&lib$find_file(&url_file_dsc, &file_name_dsc, &context,
  3792.                 0, 0, 0, 0)) {
  3793.         /*
  3794.          *    We found the file.  Convert to a URL pathspec.
  3795.          */
  3796.         if ((cp = strchr(file_name, ';')) != NULL) {
  3797.         *cp = '\0';
  3798.         }
  3799.         LYLowerCase(file_name);
  3800.         StrAllocCat(*AllocatedString, HTVMS_wwwName(file_name));
  3801.         if ((cp = strchr(old_string, ';')) != NULL) {
  3802.         StrAllocCat(*AllocatedString, cp);
  3803.         }
  3804.         if (fragment != NULL) {
  3805.         *fragment = '#';
  3806.         StrAllocCat(*AllocatedString, fragment);
  3807.         fragment = NULL;
  3808.         }
  3809.     } else if ((NULL != getcwd(dir_name, 255, 0)) &&
  3810.            0 == chdir(old_string)) {
  3811.         /*
  3812.          * Probably a directory.  Try converting that.
  3813.          */
  3814.         StrAllocCopy(cur_dir, dir_name);
  3815.         if (fragment != NULL) {
  3816.         *fragment = '#';
  3817.         }
  3818.         if (NULL != getcwd(dir_name, 255, 0)) {
  3819.         /*
  3820.          * Yup, we got it!
  3821.          */
  3822.         LYLowerCase(dir_name);
  3823.         StrAllocCat(*AllocatedString, dir_name);
  3824.         if (fragment != NULL) {
  3825.             StrAllocCat(*AllocatedString, fragment);
  3826.             fragment = NULL;
  3827.         }
  3828.         } else {
  3829.         /*
  3830.          *  Nope.  Assume it's an http URL with
  3831.          *  the "http://" defaulted, if we can't
  3832.          *  rule out a bad VMS path.
  3833.          */
  3834.         fragment = NULL;
  3835.         if (strchr(old_string, '[') ||
  3836.             ((cp = strchr(old_string, ':')) != NULL &&
  3837.              !isdigit((unsigned char)cp[1])) ||
  3838.             !LYExpandHostForURL((char **)&old_string,
  3839.                     URLDomainPrefixes,
  3840.                     URLDomainSuffixes)) {
  3841.             /*
  3842.              *    Probably a bad VMS path (but can't be
  3843.              *    sure).    Use original pathspec for the
  3844.              *    error message that will result.
  3845.              */
  3846.             strcpy(url_file, "/");
  3847.             strcat(url_file, old_string);
  3848.             CTRACE(tfp, "Can't find '%s'  Will assume it's a bad path.\n",
  3849.                 old_string);
  3850.             }
  3851.             StrAllocCat(*AllocatedString, url_file);
  3852.         } else {
  3853.             /*
  3854.              *    Assume a URL is wanted, so guess the
  3855.              *    scheme with "http://" as the default. - FM
  3856.              */
  3857.             if (!LYAddSchemeForURL((char **)&old_string, "http://")) {
  3858.             StrAllocCopy(*AllocatedString, "http://");
  3859.             StrAllocCat(*AllocatedString, old_string);
  3860.             } else {
  3861.             StrAllocCopy(*AllocatedString, old_string);
  3862.             }
  3863.         }
  3864.         }
  3865.     } else {
  3866.         /*
  3867.          *    Nothing found.    Assume it's an http URL
  3868.          *    with the "http://" defaulted, if we can't
  3869.          *    rule out a bad VMS path.
  3870.          */
  3871.         if (fragment != NULL) {
  3872.         *fragment = '#';
  3873.         fragment = NULL;
  3874.         }
  3875.         if (strchr(old_string, '[') ||
  3876.         ((cp = strchr(old_string, ':')) != NULL &&
  3877.          !isdigit((unsigned char)cp[1])) ||
  3878.         !LYExpandHostForURL((char **)&old_string,
  3879.                     URLDomainPrefixes,
  3880.                     URLDomainSuffixes)) {
  3881.         /*
  3882.          *  Probably a bad VMS path (but can't be
  3883.          *  sure).  Use original pathspec for the
  3884.          *  error message that will result.
  3885.          */
  3886.         strcpy(url_file, "/");
  3887.         strcat(url_file, old_string);
  3888.         CTRACE(tfp, "Can't find '%s'  Will assume it's a bad path.\n",
  3889.                 old_string);
  3890.         StrAllocCat(*AllocatedString, url_file);
  3891.         } else {
  3892.         /*
  3893.          *  Assume a URL is wanted, so guess the
  3894.          *  scheme with "http://" as the default. - FM
  3895.          */
  3896.         if (!LYAddSchemeForURL((char **)&old_string, "http://")) {
  3897.             StrAllocCopy(*AllocatedString, "http://");
  3898.             StrAllocCat(*AllocatedString, old_string);
  3899.         } else {
  3900.             StrAllocCopy(*AllocatedString, old_string);
  3901.         }
  3902.         }
  3903.     }
  3904.     lib$find_file_end(&context);
  3905.     FREE(cur_dir);
  3906. have_VMS_URL:
  3907.     CTRACE(tfp, "Trying: '%s'\n", *AllocatedString);
  3908. #else /* Unix: */
  3909. #ifdef DOSPATH
  3910.     if (strlen(old_string) == 1 && *old_string == '.') {
  3911.         /*
  3912.          *    They want .
  3913.          */
  3914.         char curdir[DIRNAMESIZE];
  3915.         getcwd (curdir, DIRNAMESIZE);
  3916.         StrAllocCopy(temp, HTDOS_wwwName(curdir));
  3917.         StrAllocCat(*AllocatedString, temp);
  3918.         FREE(temp);
  3919.         CTRACE(tfp, "Converted '%s' to '%s'\n",
  3920.             old_string, *AllocatedString);
  3921.     } else
  3922. #endif /* DOSPATH */
  3923.     if (*old_string == '~') {
  3924.         /*
  3925.          *    On Unix, convert '~' to Home_Dir().
  3926.          */
  3927.         StrAllocCat(*AllocatedString, Home_Dir());
  3928.         if ((cp = strchr(old_string, '/')) != NULL) {
  3929.         /*
  3930.          *  Append rest of path, if present, skipping "user" if
  3931.          *  "~user" was entered, simplifying, and eliminating
  3932.          *  any residual relative elements. - FM
  3933.          */
  3934.         StrAllocCopy(temp, cp);
  3935.         LYTrimRelFromAbsPath(temp);
  3936.         StrAllocCat(*AllocatedString, temp);
  3937.         FREE(temp);
  3938.         }
  3939.         CTRACE(tfp, "Converted '%s' to '%s'\n",
  3940.             old_string, *AllocatedString);
  3941.     } else {
  3942.         /*
  3943.          *    Create a full path to the current default directory.
  3944.          */
  3945.         char curdir[DIRNAMESIZE];
  3946.         char *temp2 = NULL;
  3947.         BOOL is_local = FALSE;
  3948. #if HAVE_GETCWD
  3949.         getcwd (curdir, DIRNAMESIZE);
  3950. #else
  3951.         getwd (curdir);
  3952. #endif /* NO_GETCWD */
  3953.         /*
  3954.          *    Concatenate and simplify, trimming any
  3955.          *    residual relative elements. - FM
  3956.          */
  3957. #if defined (DOSPATH) || defined (__EMX__)
  3958.         if (old_string[1] != ':' && old_string[1] != '|') {
  3959. #ifdef DOSPATH
  3960.         StrAllocCopy(temp, HTDOS_wwwName(curdir));
  3961. #else
  3962.         StrAllocCopy(temp, curdir);
  3963. #endif
  3964.         if(curdir[strlen(curdir)-1] != '/')
  3965.             StrAllocCat(temp, "/");
  3966.         LYstrncpy(curdir, temp, (DIRNAMESIZE - 1));
  3967.         StrAllocCat(temp, old_string);
  3968.         } else {
  3969.         curdir[0] = '\0';
  3970.         StrAllocCopy(temp, old_string);
  3971.         }
  3972. #else
  3973.         StrAllocCopy(temp, curdir);
  3974.         StrAllocCat(temp, "/");
  3975.         StrAllocCat(temp, old_string);
  3976. #endif /* DOSPATH */
  3977.         LYTrimRelFromAbsPath(temp);
  3978.         CTRACE(tfp, "Converted '%s' to '%s'\n", old_string, temp);
  3979.         if ((stat(temp, &st) > -1) ||
  3980.         (fptemp = fopen(temp, "r")) != NULL) {
  3981.         /*
  3982.          *  It is a subdirectory or file on the local system.
  3983.          */
  3984. #if defined (DOSPATH) || defined (__EMX__)
  3985.         /* Don't want to see DOS local paths like c: escaped */
  3986.         /* especially when we really have file://localhost/  */
  3987.         /* at the beginning. To avoid any confusion we allow */
  3988.         /* escaping the path if URL specials % or # present. */
  3989.         if (strchr(temp, '#') == NULL &&
  3990.                strchr(temp, '%') == NULL)
  3991.         StrAllocCopy(cp, temp);
  3992.         else
  3993. #endif /* DOSPATH */
  3994.         cp = HTEscape(temp, URL_PATH);
  3995.         StrAllocCat(*AllocatedString, cp);
  3996.         FREE(cp);
  3997.         CTRACE(tfp, "Converted '%s' to '%s'\n",
  3998.                 old_string, *AllocatedString);
  3999.         is_local = TRUE;
  4000.         } else {
  4001.         char *cp2 = NULL;
  4002.         StrAllocCopy(temp2, curdir);
  4003.         if (curdir[0] != '\0' && curdir[strlen(curdir)-1] != '/')
  4004.             StrAllocCat(temp2, "/");
  4005.         StrAllocCopy(cp, old_string);
  4006.         if ((fragment = strchr(cp, '#')) != NULL)
  4007.             *fragment = '\0';    /* keep as pointer into cp string */
  4008.         HTUnEscape(cp);   /* unescape given path without fragment */
  4009.         StrAllocCat(temp2, cp);     /* append to current dir  */
  4010.         StrAllocCopy(cp2, temp2);    /* keep a copy in cp2      */
  4011.         LYTrimRelFromAbsPath(temp2);
  4012.  
  4013.         if (strcmp(temp2, temp) != 0 &&
  4014.             ((stat(temp2, &st) > -1) ||
  4015.              (fptemp = fopen(temp2, "r")) != NULL)) {
  4016.             /*
  4017.              *    It is a subdirectory or file on the local system
  4018.              *    with escaped characters and/or a fragment to be
  4019.              *    appended to the URL. - FM
  4020.              */
  4021.  
  4022.             FREE(temp);
  4023.             if (strcmp(cp2, temp2) == 0) {
  4024.             /*
  4025.              *  LYTrimRelFromAbsPath did nothing, use
  4026.              *  old_string as given. - kw
  4027.              */
  4028.             temp = HTEscape(curdir, URL_PATH);
  4029.             if (curdir[0] != '\0' && curdir[strlen(curdir)-1] != '/')
  4030.                 StrAllocCat(temp, "/");
  4031.             StrAllocCat(temp, old_string);
  4032.             } else {
  4033.             temp = HTEscape(temp2, URL_PATH);
  4034.             if (fragment != NULL) {
  4035.                 *fragment = '#';
  4036.                 StrAllocCat(temp, fragment);
  4037.             }
  4038.             }
  4039.             StrAllocCat(*AllocatedString, temp);
  4040.             CTRACE(tfp, "Converted '%s' to '%s'\n",
  4041.                 old_string, *AllocatedString);
  4042.             is_local = TRUE;
  4043.  
  4044.         } else if (strchr(curdir, '#') != NULL ||
  4045.                strchr(curdir, '%') != NULL) {
  4046.             /*
  4047.              *    If PWD has some unusual characters, construct a
  4048.              *    filename in temp where those are escaped.  This
  4049.              *    is mostly to prevent this function from returning
  4050.              *    with some weird URL if the LYExpandHostForURL tests
  4051.              *    further down fail. - kw
  4052.              */
  4053.             FREE(temp);
  4054.             if (strcmp(cp2, temp2) == 0) {
  4055.             /*
  4056.              *  LYTrimRelFromAbsPath did nothing, use
  4057.              *  old_string as given. - kw
  4058.              */
  4059.             temp = HTEscape(curdir, URL_PATH);
  4060.             if (curdir[0] != '\0' && curdir[strlen(curdir)-1] != '/')
  4061.                 StrAllocCat(temp, "/");
  4062.             StrAllocCat(temp, old_string);
  4063.             } else {
  4064.             temp = HTEscape(temp2, URL_PATH);
  4065.             if (fragment != NULL) {
  4066.                 *fragment = '#';
  4067.                 StrAllocCat(temp, fragment);
  4068.             }
  4069.             }
  4070.         }
  4071.         FREE(cp);
  4072.         FREE(cp2);
  4073.         }
  4074.         if (is_local == FALSE) {
  4075.         /*
  4076.          *  It's not an accessible subdirectory or file on the
  4077.          *  local system, so assume it's a URL request and guess
  4078.          *  the scheme with "http://" as the default.
  4079.          */
  4080.         CTRACE(tfp, "Can't stat() or fopen() '%s'\n",
  4081.                 temp2 ? temp2 : temp);
  4082.         if (LYExpandHostForURL((char **)&old_string,
  4083.                        URLDomainPrefixes,
  4084.                        URLDomainSuffixes)) {
  4085.             if (!LYAddSchemeForURL((char **)&old_string, "http://")) {
  4086.             StrAllocCopy(*AllocatedString, "http://");
  4087.             StrAllocCat(*AllocatedString, old_string);
  4088.             } else {
  4089.             StrAllocCopy(*AllocatedString, old_string);
  4090.             }
  4091.         } else {
  4092.           /* RW 1998Mar16  Restore AllocatedString to 'old_string' */
  4093.             StrAllocCopy(*AllocatedString, old_string);
  4094.         }
  4095.         CTRACE(tfp, "Trying: '%s'\n", *AllocatedString);
  4096.         }
  4097.         FREE(temp);
  4098.         FREE(temp2);
  4099.         if (fptemp) {
  4100.         fclose(fptemp);
  4101.         fptemp = NULL;
  4102.         }
  4103.     }
  4104. #endif /* VMS */
  4105.     } else {
  4106.     /*
  4107.      *  Path begins with a slash.  Simplify and use it.
  4108.      */
  4109.     if (old_string[1] == '\0') {
  4110.         /*
  4111.          *    Request for root.  Respect it on Unix, but
  4112.          *    on VMS we treat that as a listing of the
  4113.          *    login directory. - FM
  4114.          */
  4115. #ifdef VMS
  4116.         StrAllocCat(*AllocatedString, HTVMS_wwwName((char *)Home_Dir()));
  4117. #else
  4118.         StrAllocCat(*AllocatedString, "/");
  4119.     } else if ((stat(old_string, &st) > -1) ||
  4120.            (fptemp = fopen(old_string, "r")) != NULL) {
  4121.         /*
  4122.          *    It is an absolute directory or file
  4123.          *    on the local system. - KW
  4124.          */
  4125.         StrAllocCopy(temp, old_string);
  4126.         LYTrimRelFromAbsPath(temp);
  4127.         CTRACE(tfp, "Converted '%s' to '%s'\n", old_string, temp);
  4128.         cp = HTEscape(temp, URL_PATH);
  4129.         StrAllocCat(*AllocatedString, cp);
  4130.         FREE(cp);
  4131.         FREE(temp);
  4132.         if (fptemp) {
  4133.         fclose(fptemp);
  4134.         fptemp = NULL;
  4135.         }
  4136.         CTRACE(tfp, "Converted '%s' to '%s'\n",
  4137.             old_string, *AllocatedString);
  4138. #endif /* VMS */
  4139.     } else if (old_string[1] == '~') {
  4140.         /*
  4141.          *    Has a Home_Dir() reference.  Handle it
  4142.          *    as if there weren't a lead slash. - FM
  4143.          */
  4144. #ifdef VMS
  4145.         StrAllocCat(*AllocatedString, HTVMS_wwwName((char *)Home_Dir()));
  4146. #else
  4147.         StrAllocCat(*AllocatedString, Home_Dir());
  4148. #endif /* VMS */
  4149.         if ((cp = strchr((old_string + 1), '/')) != NULL) {
  4150.         /*
  4151.          *  Append rest of path, if present, skipping "user" if
  4152.          *  "~user" was entered, simplifying, and eliminating
  4153.          *  any residual relative elements. - FM
  4154.          */
  4155.         StrAllocCopy(temp, cp);
  4156.         LYTrimRelFromAbsPath(temp);
  4157.         StrAllocCat(*AllocatedString, temp);
  4158.         FREE(temp);
  4159.         }
  4160.     } else {
  4161.         /*
  4162.          *    Normal absolute path.  Simplify, trim any
  4163.          *    residual relative elements, and append it. - FM
  4164.          */
  4165.         StrAllocCopy(temp, old_string);
  4166.         LYTrimRelFromAbsPath(temp);
  4167.         StrAllocCat(*AllocatedString, temp);
  4168.         FREE(temp);
  4169.     }
  4170.     CTRACE(tfp, "Converted '%s' to '%s'\n",
  4171.             old_string, *AllocatedString);
  4172.     }
  4173.     FREE(old_string);
  4174.     if (TRACE) {
  4175.     /* Pause so we can read the messages before invoking curses */
  4176.     if (!LYTraceLogFP)
  4177.         sleep(AlertSecs);
  4178.     }
  4179. }
  4180.  
  4181. /*
  4182.  *  This function rewrites and reallocates a previously allocated
  4183.  *  string so that the first element is a confirmed Internet host,
  4184.  *  and returns TRUE, otherwise it does not modify the string and
  4185.  *  returns FALSE.  It first tries the element as is, then, if the
  4186.  *  element does not end with a dot, it adds prefixes from the
  4187.  *  (comma separated) prefix list arguement, and, if the element
  4188.  *  does not begin with a dot, suffixes from the (comma separated)
  4189.  *  suffix list arguments (e.g., www.host.com, then www.host,edu,
  4190.  *  then www.host.net, then www.host.org).  The remaining path, if
  4191.  *  one is present, will be appended to the expanded host.  It also
  4192.  *  takes into account whether a colon is in the element or suffix,
  4193.  *  and includes that and what follows as a port field for the
  4194.  *  expanded host field (e.g, wfbr:8002/dir/lynx should yield
  4195.  *  www.wfbr.edu:8002/dir/lynx).  The calling function should
  4196.  *  prepend the scheme field (e.g., http://), or pass the string
  4197.  *  to LYAddSchemeForURL(), if this function returns TRUE. - FM
  4198.  */
  4199. PUBLIC BOOLEAN LYExpandHostForURL ARGS3(
  4200.     char **,    AllocatedString,
  4201.     char *,     prefix_list,
  4202.     char *,     suffix_list)
  4203. {
  4204.     char DomainPrefix[80], *StartP, *EndP;
  4205.     char DomainSuffix[80], *StartS, *EndS;
  4206.     char *Str = NULL, *StrColon = NULL, *MsgStr = NULL;
  4207.     char *Host = NULL, *HostColon = NULL, *host = NULL;
  4208.     char *Path = NULL;
  4209.     char *Fragment = NULL;
  4210.     struct hostent  *phost;
  4211.     BOOLEAN GotHost = FALSE;
  4212.     BOOLEAN Startup = (helpfilepath == NULL);
  4213.  
  4214.     /*
  4215.      *    If it's a NULL or zero-length string,
  4216.      *    or if it begins with a slash or hash,
  4217.      *    don't continue pointlessly. - FM
  4218.      */
  4219.     if (!(*AllocatedString) || *AllocatedString[0] == '\0' ||
  4220.     *AllocatedString[0] == '/' || *AllocatedString[0] == '#') {
  4221.     return GotHost;
  4222.     }
  4223.  
  4224.     /*
  4225.      *    If it's a partial or relative path,
  4226.      *    don't continue pointlessly. - FM
  4227.      */
  4228.     if (!strncmp(*AllocatedString, "..", 2) ||
  4229.     !strncmp(*AllocatedString, "./", 2)) {
  4230.     return GotHost;
  4231.     }
  4232.  
  4233.     /*
  4234.      *    Make a clean copy of the string, and trim off the
  4235.      *    path if one is present, but save the information
  4236.      *    so we can restore the path after filling in the
  4237.      *    Host[:port] field. - FM
  4238.      */
  4239.     StrAllocCopy(Str, *AllocatedString);
  4240.     if ((Path = strchr(Str, '/')) != NULL) {
  4241.     /*
  4242.      *  Have a path.  Any fragment should
  4243.      *  already be included in Path. - FM
  4244.      */
  4245.     *Path = '\0';
  4246.     } else if ((Fragment = strchr(Str, '#')) != NULL) {
  4247.     /*
  4248.      *  No path, so check for a fragment and
  4249.      *  trim that, to be restored after filling
  4250.      *  in the Host[:port] field. - FM
  4251.      */
  4252.     *Fragment = '\0';
  4253.     }
  4254.  
  4255.     /*
  4256.      *    If the potential host string has a colon, assume it
  4257.      *    begins a port field, and trim it off, but save the
  4258.      *    information so we can restore the port field after
  4259.      *    filling in the host field. - FM
  4260.      */
  4261.     if ((StrColon = strrchr(Str, ':')) != NULL &&
  4262.     isdigit((unsigned char)StrColon[1])) {
  4263.     if (StrColon == Str) {
  4264.         FREE(Str);
  4265.         return GotHost;
  4266.     }
  4267.     *StrColon = '\0';
  4268.     }
  4269.  
  4270.     /*
  4271.      *    Do a DNS test on the potential host field
  4272.      *    as presently trimmed. - FM
  4273.      */
  4274.     StrAllocCopy(host, Str);
  4275.     HTUnEscape(host);
  4276.     if (LYCursesON) {
  4277.     StrAllocCopy(MsgStr, "Looking up ");
  4278.     StrAllocCat(MsgStr, host);
  4279.     StrAllocCat(MsgStr, " first.");
  4280.     HTProgress(MsgStr);
  4281.     } else if (Startup && !dump_output_immediately) {
  4282.     fprintf(stdout, "Looking up '%s' first.\n", host);
  4283.     }
  4284. #ifndef DJGPP
  4285.     if ((phost = gethostbyname(host)) != NULL)
  4286. #else
  4287.     if (resolve(host) != 0)
  4288. #endif /* DJGPP */
  4289.     {
  4290.     /*
  4291.      *  Clear any residual interrupt. - FM
  4292.      */
  4293.     if (LYCursesON && HTCheckForInterrupt()) {
  4294.         CTRACE(tfp, "LYExpandHostForURL: Ignoring interrupt because '%s' resolved.\n",
  4295.             host);
  4296.     }
  4297.  
  4298.     /*
  4299.      *  Return success. - FM
  4300.      */
  4301.     GotHost = TRUE;
  4302.     FREE(host);
  4303.     FREE(Str);
  4304.     FREE(MsgStr);
  4305.     return GotHost;
  4306.     } else if (LYCursesON && HTCheckForInterrupt()) {
  4307.     /*
  4308.      *  Give the user chance to interrupt lookup cycles. - KW & FM
  4309.      */
  4310.     CTRACE(tfp, "LYExpandHostForURL: Interrupted while '%s' failed to resolve.\n",
  4311.             host);
  4312.  
  4313.     /*
  4314.      *  Return failure. - FM
  4315.      */
  4316.     FREE(host);
  4317.     FREE(Str);
  4318.     FREE(MsgStr);
  4319.     return FALSE;
  4320.     }
  4321.  
  4322.     /*
  4323.      *    Set the first prefix, making it a zero-length string
  4324.      *    if the list is NULL or if the potential host field
  4325.      *    ends with a dot. - FM
  4326.      */
  4327.     StartP = ((prefix_list && Str[strlen(Str)-1] != '.') ?
  4328.                          prefix_list : "");
  4329.     /*
  4330.      *    If we have a prefix, but the allocated string is
  4331.      *    one of the common host prefixes, make our prefix
  4332.      *    a zero-length string. - FM
  4333.      */
  4334.     if (*StartP && *StartP != '.') {
  4335.     if (!strncasecomp(*AllocatedString, "www.", 4) ||
  4336.         !strncasecomp(*AllocatedString, "ftp.", 4) ||
  4337.         !strncasecomp(*AllocatedString, "gopher.", 7) ||
  4338.         !strncasecomp(*AllocatedString, "wais.", 5) ||
  4339.         !strncasecomp(*AllocatedString, "cso.", 4) ||
  4340.         !strncasecomp(*AllocatedString, "ns.", 3) ||
  4341.         !strncasecomp(*AllocatedString, "ph.", 3) ||
  4342.         !strncasecomp(*AllocatedString, "finger.", 7) ||
  4343.         !strncasecomp(*AllocatedString, "news.", 5) ||
  4344.         !strncasecomp(*AllocatedString, "nntp.", 5)) {
  4345.         StartP = "";
  4346.     }
  4347.     }
  4348.     while ((*StartP) && (WHITE(*StartP) || *StartP == ',')) {
  4349.     StartP++;    /* Skip whitespace and separators */
  4350.     }
  4351.     EndP = StartP;
  4352.     while (*EndP && !WHITE(*EndP) && *EndP != ',') {
  4353.     EndP++;     /* Find separator */
  4354.     }
  4355.     LYstrncpy(DomainPrefix, StartP, (EndP - StartP));
  4356.  
  4357.     /*
  4358.      *    Test each prefix with each suffix. - FM
  4359.      */
  4360.     do {
  4361.     /*
  4362.      *  Set the first suffix, making it a zero-length string
  4363.      *  if the list is NULL or if the potential host field
  4364.      *  begins with a dot. - FM
  4365.      */
  4366.     StartS = ((suffix_list && *Str != '.') ?
  4367.                    suffix_list : "");
  4368.     while ((*StartS) && (WHITE(*StartS) || *StartS == ',')) {
  4369.         StartS++;    /* Skip whitespace and separators */
  4370.     }
  4371.     EndS = StartS;
  4372.     while (*EndS && !WHITE(*EndS) && *EndS != ',') {
  4373.         EndS++;    /* Find separator */
  4374.     }
  4375.     LYstrncpy(DomainSuffix, StartS, (EndS - StartS));
  4376.  
  4377.     /*
  4378.      *  Create domain names and do DNS tests. - FM
  4379.      */
  4380.     do {
  4381.         StrAllocCopy(Host, DomainPrefix);
  4382.         StrAllocCat(Host, ((*Str == '.') ? (Str + 1) : Str));
  4383.         if (Host[strlen(Host)-1] == '.') {
  4384.         Host[strlen(Host)-1] = '\0';
  4385.         }
  4386.         StrAllocCat(Host, DomainSuffix);
  4387.         if ((HostColon = strrchr(Host, ':')) != NULL &&
  4388.         isdigit((unsigned char)HostColon[1])) {
  4389.         *HostColon = '\0';
  4390.         }
  4391.         StrAllocCopy(host, Host);
  4392.         HTUnEscape(host);
  4393.         if (LYCursesON) {
  4394.         StrAllocCopy(MsgStr, "Looking up ");
  4395.         StrAllocCat(MsgStr, host);
  4396.         StrAllocCat(MsgStr, ", guessing...");
  4397.         HTProgress(MsgStr);
  4398.         } else if (Startup && !dump_output_immediately) {
  4399.         fprintf(stdout, "Looking up '%s', guessing...\n", host);
  4400.         }
  4401. #ifndef DJGPP
  4402.         GotHost = ((phost = gethostbyname(host)) != NULL);
  4403. #else
  4404.         GotHost = (resolve(host) != 0);
  4405. #endif /* DJGPP */
  4406.         if (HostColon != NULL) {
  4407.         *HostColon = ':';
  4408.         }
  4409.         if (GotHost == FALSE) {
  4410.         /*
  4411.          *  Give the user chance to interrupt lookup cycles. - KW
  4412.          */
  4413.         if (LYCursesON && HTCheckForInterrupt()) {
  4414.             CTRACE(tfp, "LYExpandHostForURL: Interrupted while '%s' failed to resolve.\n",
  4415.                 host);
  4416.             FREE(Str);
  4417.             FREE(MsgStr);
  4418.             FREE(Host);
  4419.             FREE(host);
  4420.             return FALSE; /* We didn't find a valid name. */
  4421.         }
  4422.  
  4423.         /*
  4424.          *  Advance to the next suffix, or end of suffix list. - FM
  4425.          */
  4426.         StartS = ((*EndS == '\0') ? EndS : (EndS + 1));
  4427.         while ((*StartS) && (WHITE(*StartS) || *StartS == ',')) {
  4428.             StartS++;    /* Skip whitespace and separators */
  4429.         }
  4430.         EndS = StartS;
  4431.         while (*EndS && !WHITE(*EndS) && *EndS != ',') {
  4432.             EndS++;    /* Find separator */
  4433.         }
  4434.         LYstrncpy(DomainSuffix, StartS, (EndS - StartS));
  4435.         }
  4436.     }  while ((GotHost == FALSE) && (*DomainSuffix != '\0'));
  4437.  
  4438.     if (GotHost == FALSE) {
  4439.        /*
  4440.         *  Advance to the next prefix, or end of prefix list. - FM
  4441.         */
  4442.        StartP = ((*EndP == '\0') ? EndP : (EndP + 1));
  4443.        while ((*StartP) && (WHITE(*StartP) || *StartP == ',')) {
  4444.            StartP++;    /* Skip whitespace and separators */
  4445.        }
  4446.        EndP = StartP;
  4447.        while (*EndP && !WHITE(*EndP) && *EndP != ',') {
  4448.            EndP++;        /* Find separator */
  4449.        }
  4450.        LYstrncpy(DomainPrefix, StartP, (EndP - StartP));
  4451.     }
  4452.     } while ((GotHost == FALSE) && (*DomainPrefix != '\0'));
  4453.  
  4454.     /*
  4455.      *    If a test passed, restore the port field if we had one
  4456.      *    and there is no colon in the expanded host, and the path
  4457.      *    if we had one, and reallocate the original string with
  4458.      *    the expanded Host[:port] field included. - FM
  4459.      */
  4460.     if (GotHost) {
  4461.     if (StrColon && strchr(Host, ':') == NULL) {
  4462.         *StrColon = ':';
  4463.         StrAllocCat(Host, StrColon);
  4464.     }
  4465.     if (Path) {
  4466.         *Path = '/';
  4467.         StrAllocCat(Host, Path);
  4468.     } else if (Fragment) {
  4469.         StrAllocCat(Host, "/");
  4470.         *Fragment = '#';
  4471.         StrAllocCat(Host, Fragment);
  4472.     }
  4473.     StrAllocCopy(*AllocatedString, Host);
  4474.     }
  4475.  
  4476.     /*
  4477.      *    Clear any residual interrupt. - FM
  4478.      */
  4479.     if (LYCursesON && HTCheckForInterrupt()) {
  4480.     CTRACE(tfp, "LYExpandHostForURL: Ignoring interrupt because '%s' %s.\n",
  4481.             host,
  4482.             (GotHost ? "resolved" : "timed out"));
  4483.     }
  4484.  
  4485.     /*
  4486.      *    Clean up and return the last test result. - FM
  4487.      */
  4488.     FREE(Str);
  4489.     FREE(MsgStr);
  4490.     FREE(Host);
  4491.     FREE(host);
  4492.     return GotHost;
  4493. }
  4494.  
  4495. /*
  4496.  *  This function rewrites and reallocates a previously allocated
  4497.  *  string that begins with an Internet host name so that the string
  4498.  *  begins with its guess of the scheme based on the first field of
  4499.  *  the host name, or the default scheme if no guess was made, and
  4500.  *  returns TRUE, otherwise it does not modify the string and returns
  4501.  *  FALSE.  It also returns FALSE without modifying the string if the
  4502.  *  default_scheme argument was NULL or zero-length and no guess was
  4503.  *  made. - FM
  4504.   */
  4505. PUBLIC BOOLEAN LYAddSchemeForURL ARGS2(
  4506.     char **,    AllocatedString,
  4507.     char *,     default_scheme)
  4508. {
  4509.     char *Str = NULL;
  4510.     BOOLEAN GotScheme = FALSE;
  4511.  
  4512.     /*
  4513.      *    If we were passed a NULL or zero-length string,
  4514.      *    don't continue pointlessly. - FM
  4515.      */
  4516.     if (!(*AllocatedString) || *AllocatedString[0] == '\0') {
  4517.     return GotScheme;
  4518.     }
  4519.  
  4520.     /*
  4521.      * Try to guess the appropriate scheme. - FM
  4522.      */
  4523.     if (0 == strncasecomp(*AllocatedString, "www", 3)) {
  4524.     /*
  4525.      *  This could be either http or https, so check
  4526.      *  the default and otherwise use "http". - FM
  4527.      */
  4528.     if (default_scheme != NULL &&
  4529.         NULL != strstr(default_scheme, "http")) {
  4530.         StrAllocCopy(Str, default_scheme);
  4531.     } else {
  4532.         StrAllocCopy(Str, "http://");
  4533.     }
  4534.     GotScheme = TRUE;
  4535.  
  4536.     } else if (0 == strncasecomp(*AllocatedString, "ftp", 3)) {
  4537.     StrAllocCopy(Str, "ftp://");
  4538.     GotScheme = TRUE;
  4539.  
  4540.     } else if (0 == strncasecomp(*AllocatedString, "gopher", 6)) {
  4541.     StrAllocCopy(Str, "gopher://");
  4542.     GotScheme = TRUE;
  4543.  
  4544.     } else if (0 == strncasecomp(*AllocatedString, "wais", 4)) {
  4545.     StrAllocCopy(Str, "wais://");
  4546.     GotScheme = TRUE;
  4547.  
  4548.     } else if (0 == strncasecomp(*AllocatedString, "cso", 3) ||
  4549.            0 == strncasecomp(*AllocatedString, "ns.", 3) ||
  4550.            0 == strncasecomp(*AllocatedString, "ph.", 3)) {
  4551.     StrAllocCopy(Str, "cso://");
  4552.     GotScheme = TRUE;
  4553.  
  4554.     } else if (0 == strncasecomp(*AllocatedString, "finger", 6)) {
  4555.     StrAllocCopy(Str, "finger://");
  4556.     GotScheme = TRUE;
  4557.  
  4558.     } else if (0 == strncasecomp(*AllocatedString, "news", 4)) {
  4559.     /*
  4560.      *  This could be either news, snews, or nntp, so
  4561.      *  check the default, and otherwise use news. - FM
  4562.      */
  4563.     if ((default_scheme != NULL) &&
  4564.         (NULL != strstr(default_scheme, "news") ||
  4565.          NULL != strstr(default_scheme, "nntp"))) {
  4566.         StrAllocCopy(Str, default_scheme);
  4567.     } else {
  4568.         StrAllocCopy(Str, "news://");
  4569.     }
  4570.     GotScheme = TRUE;
  4571.  
  4572.     } else if (0 == strncasecomp(*AllocatedString, "nntp", 4)) {
  4573.     StrAllocCopy(Str, "nntp://");
  4574.     GotScheme = TRUE;
  4575.  
  4576.     }
  4577.  
  4578.     /*
  4579.      *    If we've make a guess, use it.    Otherwise, if we
  4580.      *    were passed a default scheme prefix, use that. - FM
  4581.      */
  4582.     if (GotScheme == TRUE) {
  4583.     StrAllocCat(Str, *AllocatedString);
  4584.     StrAllocCopy(*AllocatedString, Str);
  4585.     FREE(Str);
  4586.     return GotScheme;
  4587.  
  4588.     } else if (default_scheme != NULL && *default_scheme != '\0') {
  4589.     StrAllocCopy(Str, default_scheme);
  4590.     GotScheme = TRUE;
  4591.     StrAllocCat(Str, *AllocatedString);
  4592.     StrAllocCopy(*AllocatedString, Str);
  4593.     FREE(Str);
  4594.     return GotScheme;
  4595.     }
  4596.  
  4597.     return GotScheme;
  4598. }
  4599.  
  4600. /*
  4601.  *  This function expects an absolute Unix or VMS SHELL path
  4602.  *  spec as an allocated string, simplifies it, and trims out
  4603.  *  any residual relative elements.  It also checks whether
  4604.  *  the path had a terminal slash, and if it didn't, makes
  4605.  *  sure that the simplified path doesn't either.  If it's
  4606.  *  a directory, our convention is to exclude "Up to parent"
  4607.  *  links when a terminal slash is present. - FM
  4608.  */
  4609. PUBLIC void LYTrimRelFromAbsPath ARGS1(
  4610.     char *,     path)
  4611. {
  4612.     char *cp;
  4613.     int i;
  4614.     BOOL TerminalSlash;
  4615.  
  4616.     /*
  4617.      *    Make sure we have a pointer to an absolute path. - FM
  4618.      */
  4619.     if (path == NULL || *path != '/')
  4620.     return;
  4621.  
  4622.     /*
  4623.      *    Check whether the path has a terminal slash. - FM
  4624.      */
  4625.     TerminalSlash = (path[(strlen(path) - 1)] == '/');
  4626.  
  4627.     /*
  4628.      *    Simplify the path and then do any necessary trimming. - FM
  4629.      */
  4630.     HTSimplify(path);
  4631.     cp = path;
  4632.     while (cp[1] == '.') {
  4633.     if (cp[2] == '\0') {
  4634.         /*
  4635.          *    Eliminate trailing dot. - FM
  4636.          */
  4637.         cp[1] = '\0';
  4638.     } else if (cp[2] == '/') {
  4639.         /*
  4640.          *    Skip over the "/." of a "/./". - FM
  4641.          */
  4642.         cp += 2;
  4643.     } else if (cp[2] == '.' && cp[3] == '\0') {
  4644.         /*
  4645.          *    Eliminate trailing dotdot. - FM
  4646.          */
  4647.         cp[1] = '\0';
  4648.     } else if (cp[2] == '.' && cp[3] == '/') {
  4649.         /*
  4650.          *    Skip over the "/.." of a "/../". - FM
  4651.          */
  4652.         cp += 3;
  4653.     } else {
  4654.         /*
  4655.          *    Done trimming. - FM
  4656.          */
  4657.         break;
  4658.     }
  4659.     }
  4660.  
  4661.     /*
  4662.      *    Load any shifts into path, and eliminate any
  4663.      *    terminal slash created by HTSimplify() or our
  4664.      *    walk, but not present originally. - FM
  4665.      */
  4666.     if (cp > path) {
  4667.     for (i = 0; cp[i] != '\0'; i++)
  4668.         path[i] = cp[i];
  4669.     path[i] = '\0';
  4670.     }
  4671.     if (TerminalSlash == FALSE &&
  4672.     path[(strlen(path) - 1)] == '/') {
  4673.     path[(strlen(path) - 1)] = '\0';
  4674.     }
  4675. }
  4676.  
  4677. /*
  4678.  *  Example Client-Side Include interface.
  4679.  *
  4680.  *  This is called from SGML.c and simply returns markup for reporting
  4681.  *  the URL of the document being loaded if a comment begins with
  4682.  *  "<!--#lynxCSI".  The markup will be included as if it were in the
  4683.  *  document.  Move this function to a separate module for doing this
  4684.  *  kind of thing seriously, someday. - FM
  4685.  */
  4686. PUBLIC void LYDoCSI ARGS3(
  4687.     char *,     url,
  4688.     CONST char *,    comment,
  4689.     char **,    csi)
  4690. {
  4691.     CONST char *cp = comment;
  4692.  
  4693.     if (cp == NULL)
  4694.     return;
  4695.  
  4696.     if (strncmp(cp, "!--#", 4))
  4697.     return;
  4698.  
  4699.     cp += 4;
  4700.     if (!strncasecomp(cp, "lynxCSI", 7)) {
  4701.     StrAllocCat(*csi, "\n<p align=\"center\">URL: ");
  4702.     StrAllocCat(*csi, url);
  4703.     StrAllocCat(*csi, "</p>\n\n");
  4704.     }
  4705.  
  4706.     return;
  4707. }
  4708.  
  4709. #ifdef VMS
  4710. /*
  4711.  *  Define_VMSLogical -- Fote Macrides 04-Apr-1995
  4712.  *    Define VMS logicals in the process table.
  4713.  */
  4714. PUBLIC void Define_VMSLogical ARGS2(
  4715.     char *,     LogicalName,
  4716.     char *,     LogicalValue)
  4717. {
  4718.     $DESCRIPTOR(lname, "");
  4719.     $DESCRIPTOR(lvalue, "");
  4720.     $DESCRIPTOR(ltable, "LNM$PROCESS");
  4721.  
  4722.     if (!LogicalName || *LogicalName == '\0')
  4723.     return;
  4724.  
  4725.     lname.dsc$w_length = strlen(LogicalName);
  4726.     lname.dsc$a_pointer = LogicalName;
  4727.  
  4728.     if (!LogicalValue || *LogicalValue == '\0') {
  4729.     lib$delete_logical(&lname, <able);
  4730.     return;
  4731.     }
  4732.  
  4733.     lvalue.dsc$w_length = strlen(LogicalValue);
  4734.     lvalue.dsc$a_pointer = LogicalValue;
  4735.     lib$set_logical(&lname, &lvalue, <able, 0, 0);
  4736.     return;
  4737. }
  4738. #endif /* VMS */
  4739.  
  4740. PRIVATE void LYHomeDir_free NOARGS
  4741. {
  4742.     FREE(HomeDir);
  4743. }
  4744.  
  4745. PUBLIC CONST char * Home_Dir NOARGS
  4746. {
  4747.     static CONST char *homedir = NULL;
  4748.     char *cp = NULL;
  4749.  
  4750.     if (homedir == NULL) {
  4751.     if ((cp = getenv("HOME")) == NULL || *cp == '\0') {
  4752. #if defined (DOSPATH) || defined (__EMX__) /* BAD!    WSB */
  4753.         if ((cp = getenv("TEMP")) == NULL || *cp == '\0') {
  4754.         if ((cp = getenv("TMP")) == NULL || *cp == '\0') {
  4755.             StrAllocCopy(HomeDir, "C:\\");
  4756.         } else {
  4757.             StrAllocCopy(HomeDir, cp);
  4758.         }
  4759.         } else {
  4760.         StrAllocCopy(HomeDir, cp);
  4761.         }
  4762. #else
  4763. #ifdef VMS
  4764.         if ((cp = getenv("SYS$LOGIN")) == NULL || *cp == '\0') {
  4765.         if ((cp = getenv("SYS$SCRATCH")) == NULL || *cp == '\0') {
  4766.             StrAllocCopy(HomeDir, "sys$scratch:");
  4767.         } else {
  4768.             StrAllocCopy(HomeDir, cp);
  4769.         }
  4770.         } else {
  4771.         StrAllocCopy(HomeDir, cp);
  4772.         }
  4773. #else
  4774. #if HAVE_UTMP
  4775.         /*
  4776.          *    One could use getlogin() and getpwnam() here instead.
  4777.          */
  4778.         struct passwd *pw = getpwuid(geteuid());
  4779.  
  4780.         if (pw && pw->pw_dir) {
  4781.         StrAllocCopy(HomeDir, pw->pw_dir);
  4782.         } else
  4783. #endif
  4784.         {
  4785.         /*
  4786.          *  Use /tmp; it should be writable.
  4787.          */
  4788.         StrAllocCopy(HomeDir, "/tmp");
  4789.         }
  4790. #endif /* VMS */
  4791. #endif /* DOSPATH */
  4792.     } else {
  4793.         StrAllocCopy(HomeDir, cp);
  4794.     }
  4795.     homedir = (CONST char *)HomeDir;
  4796.     atexit(LYHomeDir_free);
  4797.     }
  4798.     return homedir;
  4799. }
  4800.  
  4801. /*
  4802.  *  This function checks the acceptability of file paths that
  4803.  *  are intended to be off the home directory.    The file path
  4804.  *  should be passed in fbuffer, together with the size of the
  4805.  *  buffer.  The function simplifies the file path, and if it
  4806.  *  is acceptible, loads it into fbuffer and returns TRUE.
  4807.  *  Otherwise, it does not modify fbuffer and returns FALSE.
  4808.  *  If a subdirectory is present and the path does not begin
  4809.  *  with "./", that is prefixed to make the situation clear. - FM
  4810.  */
  4811. PUBLIC BOOLEAN LYPathOffHomeOK ARGS2(
  4812.     char *,     fbuffer,
  4813.     size_t,     fbuffer_size)
  4814. {
  4815.     char *file = NULL;
  4816.     char *cp, *cp1;
  4817.  
  4818.     /*
  4819.      *    Make sure we have an fbuffer and a string in it. - FM
  4820.      */
  4821.     if (!fbuffer || fbuffer_size < 2 || fbuffer[0] == '\0') {
  4822.     return(FALSE);
  4823.     }
  4824.     StrAllocCopy(file, fbuffer);
  4825.     cp = file;
  4826.  
  4827.     /*
  4828.      *    Check for an inappropriate reference to the
  4829.      *    home directory, and correct it if we can. - FM
  4830.      */
  4831. #ifdef VMS
  4832.     if (!strncasecomp(cp, "sys$login", 9)) {
  4833.     if (*(cp + 9) == '\0') {
  4834.         /*
  4835.          *    Reject "sys$login". - FM
  4836.          */
  4837.         FREE(file);
  4838.         return(FALSE);
  4839.     }
  4840.     if (*(cp + 9) == ':') {
  4841.         cp += 10;
  4842.         if (*cp == '\0') {
  4843.         /*
  4844.          *  Reject "sys$login:".  Otherwise, we have
  4845.          *  converted "sys$login:file" to "file", or
  4846.          *  have left a strange path for VMS as it
  4847.          *  was originally. - FM
  4848.          */
  4849.         FREE(file);
  4850.         return(FALSE);
  4851.         }
  4852.     }
  4853.     }
  4854. #endif /* VMS */
  4855.     if (*cp == '~') {
  4856.     if (*(cp + 1) == '/') {
  4857.         if (*(cp + 2) != '\0') {
  4858.         if ((cp1 = strchr((cp + 2), '/')) != NULL) {
  4859.             /*
  4860.              *    Convert "~/subdir(s)/file"
  4861.              *    to "./subdir(s)/file". - FM
  4862.              */
  4863.             *cp = '.';
  4864.         } else {
  4865.             /*
  4866.              *    Convert "~/file" to "file". - FM
  4867.              */
  4868.             cp += 2;
  4869.         }
  4870.         } else {
  4871.         /*
  4872.          *  Reject "~/". - FM
  4873.          */
  4874.         FREE(file);
  4875.         return(FALSE);
  4876.         }
  4877.     } else if ((*(cp + 1) != '\0') &&
  4878.            (cp1 = strchr((cp + 1), '/')) != NULL) {
  4879.         cp = (cp1 - 1) ;
  4880.         if (*(cp + 2) != '\0') {
  4881.         if ((cp1 = strchr((cp + 2), '/')) != NULL) {
  4882.             /*
  4883.              *    Convert "~user/subdir(s)/file" to
  4884.              *    "./subdir(s)/file".  If user is someone
  4885.              *    else, we covered a spoof.  Otherwise,
  4886.              *    we simplified. - FM
  4887.              */
  4888.             *cp = '.';
  4889.         } else {
  4890.             /*
  4891.              *    Convert "~user/file" to "file". - FM
  4892.              */
  4893.             cp += 2;
  4894.         }
  4895.         } else {
  4896.         /*
  4897.          *  Reject "~user/". - FM
  4898.          */
  4899.         FREE(file);
  4900.         return(FALSE);
  4901.         }
  4902.     } else {
  4903.         /*
  4904.          *    Reject "~user". - FM
  4905.          */
  4906.         FREE(file);
  4907.         return(FALSE);
  4908.     }
  4909.     }
  4910.  
  4911. #ifdef VMS
  4912.     /*
  4913.      *    Check for VMS path specs, and reject if still present. - FM
  4914.      */
  4915.     if (strchr(cp, ':') != NULL || strchr(cp, ']') != NULL) {
  4916.     FREE(file);
  4917.     return(FALSE);
  4918.     }
  4919. #endif /* VMS */
  4920.  
  4921.     /*
  4922.      *    Check for a URL or absolute path, and reject if present. - FM
  4923.      */
  4924.     if (is_url(cp) || *cp == '/') {
  4925.     FREE(file);
  4926.     return(FALSE);
  4927.     }
  4928.  
  4929.     /*
  4930.      *    Simplify it. - FM
  4931.      */
  4932.     HTSimplify(cp);
  4933.  
  4934.     /*
  4935.      *    Check if it has a pointless "./". - FM
  4936.      */
  4937.     if (!strncmp(cp, "./", 2)) {
  4938.     if ((cp1 = strchr((cp + 2), '/')) == NULL) {
  4939.         cp += 2;
  4940.     }
  4941.     }
  4942.  
  4943.     /*
  4944.      *    Check for spoofing. - FM
  4945.      */
  4946.     if (*cp == '\0' || *cp == '/' || cp[(strlen(cp) - 1)] == '/' ||
  4947.     strstr(cp, "..") != NULL || !strcmp(cp, ".")) {
  4948.     FREE(file);
  4949.     return(FALSE);
  4950.     }
  4951.  
  4952.     /*
  4953.      *    Load what we have at this point into fbuffer,
  4954.      *    trimming if too long, and claim it's OK. - FM
  4955.      */
  4956.     if (fbuffer_size > 3 && strncmp(cp, "./", 2) && strchr(cp, '/')) {
  4957.     /*
  4958.      *  We have a subdirectory and no lead "./", so
  4959.      *  prefix it to make the situation clear. - FM
  4960.      */
  4961.     strcpy(fbuffer, "./");
  4962.     if (strlen(cp) > (fbuffer_size - 3))
  4963.         cp[(fbuffer_size - 3)] = '\0';
  4964.     strcat(fbuffer, cp);
  4965.     } else {
  4966.     if (strlen(cp) > (fbuffer_size - 1))
  4967.         cp[(fbuffer_size - 1)] = '\0';
  4968.     strcpy(fbuffer, cp);
  4969.     }
  4970.     FREE(file);
  4971.     return(TRUE);
  4972. }
  4973.  
  4974. /*
  4975.  *  This function appends fname to the home path and returns
  4976.  *  the full path and filename.  The fname string can be just
  4977.  *  a filename (e.g., "lynx_bookmarks.html"), or include a
  4978.  *  subdirectory off the home directory, in which case fname
  4979.  *  should begin with "./" (e.g., ./BM/lynx_bookmarks.html)
  4980.  *  Use LYPathOffHomeOK() to check and/or fix up fname before
  4981.  *  calling this function.  On VMS, the resultant full path
  4982.  *  and filename are converted to VMS syntax. - FM
  4983.  */
  4984. PUBLIC void LYAddPathToHome ARGS3(
  4985.     char *,     fbuffer,
  4986.     size_t,     fbuffer_size,
  4987.     char *,     fname)
  4988. {
  4989.     char *home = NULL;
  4990.     char *file = fname;
  4991.     int len;
  4992.  
  4993.     /*
  4994.      *    Make sure we have a buffer. - FM
  4995.      */
  4996.     if (!fbuffer)
  4997.     return;
  4998.     if (fbuffer_size < 2) {
  4999.     fbuffer[0] = '\0';
  5000.     return;
  5001.     }
  5002.     fbuffer[(fbuffer_size - 1)] = '\0';
  5003.  
  5004.     /*
  5005.      *    Make sure we have a file name. - FM
  5006.      */
  5007.     if (!file)
  5008.     file = "";
  5009.  
  5010.     /*
  5011.      *    Set up home string and length. - FM
  5012.      */
  5013.     StrAllocCopy(home, Home_Dir());
  5014.     if (!(home && *home))
  5015.     /*
  5016.      *  Home_Dir() has a bug if this ever happens. - FM
  5017.      */
  5018. #ifdef VMS
  5019.     StrAllocCopy(home, "Error:");
  5020. #else
  5021.     StrAllocCopy(home, "/error");
  5022. #endif /* VMS */
  5023.     len = fbuffer_size - (strlen(home) + 1);
  5024.     if (len <= 0) {
  5025.     /*
  5026.      *  Buffer is smaller than or only big enough for the home path.
  5027.      *  Load what fits of the home path and return.  This will fail,
  5028.      *  but we need something in the buffer. - FM
  5029.      */
  5030.     LYstrncpy(fbuffer, home, (fbuffer_size - 1));
  5031.     FREE(home);
  5032.     return;
  5033.     }
  5034.  
  5035. #ifdef VMS
  5036.     /*
  5037.      *    Check whether we have a subdirectory path or just a filename. - FM
  5038.      */
  5039.     if (!strncmp(file, "./", 2)) {
  5040.     /*
  5041.      *  We have a subdirectory path. - FM
  5042.      */
  5043.     if (home[strlen(home)-1] == ']') {
  5044.         /*
  5045.          *    We got the home directory, so convert it to
  5046.          *    SHELL syntax and append subdirectory path,
  5047.          *    then convert that to VMS syntax. - FM
  5048.          */
  5049.         char *temp = (char *)calloc(1,
  5050.                     (strlen(home) + strlen(file) + 10));
  5051.         sprintf(temp, "%s%s", HTVMS_wwwName(home), (file + 1));
  5052.         sprintf(fbuffer, "%.*s",
  5053.             (fbuffer_size - 1), HTVMS_name("", temp));
  5054.         FREE(temp);
  5055.     } else {
  5056.         /*
  5057.          *    This will fail, but we need something in the buffer. - FM
  5058.          */
  5059.         sprintf(fbuffer, "%s%.*s", home, len, file);
  5060.     }
  5061.     } else {
  5062.     /*
  5063.      *  We have a file in the home directory. - FM
  5064.      */
  5065.     sprintf(fbuffer, "%s%.*s", home, len, file);
  5066.     }
  5067. #else
  5068.     /*
  5069.      *    Check whether we have a subdirectory path or just a filename. - FM
  5070.      */
  5071.     sprintf(fbuffer, "%s/%.*s", home, len,
  5072.              (strncmp(file, "./", 2) ? file : (file + 2)));
  5073. #endif /* VMS */
  5074.     FREE(home);
  5075. }
  5076.  
  5077. /*
  5078.  *  This function takes a string in the format
  5079.  *    "Mon, 01-Jan-96 13:45:35 GMT" or
  5080.  *    "Mon,  1 Jan 1996 13:45:35 GMT"" or
  5081.  *    "dd-mm-yyyy"
  5082.  *  as an argument, and returns its conversion to clock format
  5083.  *  (seconds since 00:00:00 Jan 1 1970), or 0 if the string
  5084.  *  doesn't match the expected pattern.  It also returns 0 if
  5085.  *  the time is in the past and the "absolute" argument is FALSE.
  5086.  *  It is intended for handling 'expires' strings in Version 0
  5087.  *  cookies homologously to 'max-age' strings in Version 1 cookies,
  5088.  *  for which 0 is the minimum, and greater values are handled as
  5089.  *  '[max-age seconds] + time(NULL)'.    If "absolute" if TRUE, we
  5090.  *  return the clock format value itself, but if anything goes wrong
  5091.  *  when parsing the expected patterns, we still return 0. - FM
  5092.  */
  5093. PUBLIC time_t LYmktime ARGS2(
  5094.     char *,     string,
  5095.     BOOL,        absolute)
  5096. {
  5097.     char *s;
  5098.     time_t now, clock2;
  5099.     int day, month, year, hour, minutes, seconds;
  5100.     char *start;
  5101.     char temp[8];
  5102.  
  5103.     /*
  5104.      *    Make sure we have a string to parse. - FM
  5105.      */
  5106.     if (!(string && *string))
  5107.     return(0);
  5108.     s = string;
  5109.     CTRACE(tfp, "LYmktime: Parsing '%s'\n", s);
  5110.  
  5111.     /*
  5112.      *    Skip any lead alphabetic "Day, " field and
  5113.      *    seek a numberic day field. - FM
  5114.      */
  5115.     while (*s != '\0' && !isdigit((unsigned char)*s))
  5116.     s++;
  5117.     if (*s == '\0')
  5118.     return(0);
  5119.  
  5120.     /*
  5121.      *    Get the numeric day and convert to an integer. - FM
  5122.      */
  5123.     start = s;
  5124.     while (*s != '\0' && isdigit((unsigned char)*s))
  5125.        s++;
  5126.     if (*s == '\0' || (s - start) > 2)
  5127.     return(0);
  5128.     LYstrncpy(temp, start, (int)(s - start));
  5129.     day = atoi(temp);
  5130.     if (day < 1 || day > 31)
  5131.     return(0);
  5132.  
  5133.     /*
  5134.      *    Get the month string and convert to an integer. - FM
  5135.      */
  5136.     while (*s != '\0' && !isalnum((unsigned char)*s))
  5137.     s++;
  5138.     if (*s == '\0')
  5139.     return(0);
  5140.     start = s;
  5141.     while (*s != '\0' && isalnum((unsigned char)*s))
  5142.     s++;
  5143.     if ((*s == '\0') ||
  5144.     (s - start) < (isdigit((unsigned char)*(s - 1)) ? 2 : 3) ||
  5145.     (s - start) > (isdigit((unsigned char)*(s - 1)) ? 2 : 9))
  5146.     return(0);
  5147.     LYstrncpy(temp, start, (isdigit((unsigned char)*(s - 1)) ? 2 : 3));
  5148.     switch (TOUPPER(temp[0])) {
  5149.     case '0':
  5150.     case '1':
  5151.         month = atoi(temp);
  5152.         if (month < 1 || month > 12) {
  5153.         return(0);
  5154.         }
  5155.         break;
  5156.     case 'A':
  5157.         if (!strcasecomp(temp, "Apr")) {
  5158.         month = 4;
  5159.         } else if (!strcasecomp(temp, "Aug")) {
  5160.         month = 8;
  5161.         } else {
  5162.         return(0);
  5163.         }
  5164.         break;
  5165.     case 'D':
  5166.         if (!strcasecomp(temp, "Dec")) {
  5167.         month = 12;
  5168.         } else {
  5169.         return(0);
  5170.         }
  5171.         break;
  5172.     case 'F':
  5173.         if (!strcasecomp(temp, "Feb")) {
  5174.         month = 2;
  5175.         } else {
  5176.         return(0);
  5177.         }
  5178.         break;
  5179.     case 'J':
  5180.         if (!strcasecomp(temp, "Jan")) {
  5181.         month = 1;
  5182.         } else if (!strcasecomp(temp, "Jun")) {
  5183.         month = 6;
  5184.         } else if (!strcasecomp(temp, "Jul")) {
  5185.         month = 7;
  5186.         } else {
  5187.         return(0);
  5188.         }
  5189.         break;
  5190.     case 'M':
  5191.         if (!strcasecomp(temp, "Mar")) {
  5192.         month = 3;
  5193.         } else if (!strcasecomp(temp, "May")) {
  5194.         month = 5;
  5195.         } else {
  5196.         return(0);
  5197.         }
  5198.         break;
  5199.     case 'N':
  5200.         if (!strcasecomp(temp, "Nov")) {
  5201.         month = 11;
  5202.         } else {
  5203.         return(0);
  5204.         }
  5205.         break;
  5206.     case 'O':
  5207.         if (!strcasecomp(temp, "Oct")) {
  5208.         month = 10;
  5209.         } else {
  5210.         return(0);
  5211.         }
  5212.         break;
  5213.     case 'S':
  5214.         if (!strcasecomp(temp, "Sep")) {
  5215.         month = 9;
  5216.         } else {
  5217.         return(0);
  5218.         }
  5219.         break;
  5220.     default:
  5221.         return(0);
  5222.     }
  5223.  
  5224.     /*
  5225.      *    Get the numeric year string and convert to an integer. - FM
  5226.      */
  5227.     while (*s != '\0' && !isdigit((unsigned char)*s))
  5228.     s++;
  5229.     if (*s == '\0')
  5230.     return(0);
  5231.     start = s;
  5232.     while (*s != '\0' && isdigit((unsigned char)*s))
  5233.     s++;
  5234.     if ((s - start) == 4) {
  5235.     LYstrncpy(temp, start, 4);
  5236.     } else if ((s - start) == 2) {
  5237.     now = time(NULL);
  5238.     /*
  5239.      * Assume that received 2-digit dates >= 70 are 19xx; others
  5240.      * are 20xx.  Only matters when dealing with broken software
  5241.      * (HTTP server or web page) which is not Y2K compliant.  The
  5242.      * line is drawn on a best-guess basis; it is impossible for
  5243.      * this to be completely accurate because it depends on what
  5244.      * the broken sender software intends.    (This totally breaks
  5245.      * in 2100 -- setting up the next crisis...) - BL
  5246.      */
  5247.     if (atoi(start) >= 70)
  5248.         LYstrncpy(temp, "19", 2);
  5249.     else
  5250.         LYstrncpy(temp, "20", 2);
  5251.     strncat(temp, start, 2);
  5252.     temp[4] = '\0';
  5253.     } else {
  5254.     return(0);
  5255.     }
  5256.     year = atoi(temp);
  5257.  
  5258.     /*
  5259.      *    Get the numeric hour string and convert to an integer. - FM
  5260.      */
  5261.     while (*s != '\0' && !isdigit((unsigned char)*s))
  5262.     s++;
  5263.     if (*s == '\0') {
  5264.     hour = 0;
  5265.     minutes = 0;
  5266.     seconds = 0;
  5267.     } else {
  5268.     start = s;
  5269.     while (*s != '\0' && isdigit((unsigned char)*s))
  5270.         s++;
  5271.     if (*s != ':' || (s - start) > 2)
  5272.         return(0);
  5273.     LYstrncpy(temp, start, (int)(s - start));
  5274.     hour = atoi(temp);
  5275.  
  5276.     /*
  5277.      *  Get the numeric minutes string and convert to an integer. - FM
  5278.      */
  5279.     while (*s != '\0' && !isdigit((unsigned char)*s))
  5280.         s++;
  5281.     if (*s == '\0')
  5282.         return(0);
  5283.     start = s;
  5284.     while (*s != '\0' && isdigit((unsigned char)*s))
  5285.         s++;
  5286.     if (*s != ':' || (s - start) > 2)
  5287.         return(0);
  5288.     LYstrncpy(temp, start, (int)(s - start));
  5289.     minutes = atoi(temp);
  5290.  
  5291.     /*
  5292.      *  Get the numeric seconds string and convert to an integer. - FM
  5293.      */
  5294.     while (*s != '\0' && !isdigit((unsigned char)*s))
  5295.         s++;
  5296.     if (*s == '\0')
  5297.         return(0);
  5298.     start = s;
  5299.     while (*s != '\0' && isdigit((unsigned char)*s))
  5300.         s++;
  5301.     if (*s == '\0' || (s - start) > 2)
  5302.         return(0);
  5303.     LYstrncpy(temp, start, (int)(s - start));
  5304.     seconds = atoi(temp);
  5305.     }
  5306.  
  5307.     /*
  5308.      *    Convert to clock format (seconds since 00:00:00 Jan 1 1970),
  5309.      *    but then zero it if it's in the past and "absolute" is not
  5310.      *    TRUE.  - FM
  5311.      */
  5312.     month -= 3;
  5313.     if (month < 0) {
  5314.      month += 12;
  5315.      year--;
  5316.     }
  5317.     day += (year - 1968)*1461/4;
  5318.     day += ((((month*153) + 2)/5) - 672);
  5319.     clock2 = (time_t)((day * 60 * 60 * 24) +
  5320.              (hour * 60 * 60) +
  5321.              (minutes * 60) +
  5322.              seconds);
  5323.     if (absolute == FALSE && clock2 <= time(NULL))
  5324.     clock2 = (time_t)0;
  5325.     if (clock2 > 0)
  5326.     CTRACE(tfp, "LYmktime: clock=%ld, ctime=%s",
  5327.             (long) clock2,
  5328.             ctime(&clock2));
  5329.  
  5330.     return(clock2);
  5331. }
  5332.  
  5333. #if ! HAVE_PUTENV
  5334. /*
  5335.  *  No putenv on the next so we use this code instead!
  5336.  */
  5337.  
  5338. /* Copyright (C) 1991 Free Software Foundation, Inc.
  5339. This file is part of the GNU C Library.
  5340.  
  5341. The GNU C Library is free software; you can  redistribute it and/or
  5342. modify it under the terms of the GNU Library General  Public License as
  5343. published by the Free Software Foundation; either  version 2 of the
  5344. License, or (at your option) any later version.
  5345.  
  5346. The GNU C Library is distributed in the hope that it  will be useful,
  5347. but WITHOUT ANY WARRANTY; without even the implied  warranty of
  5348. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.   See the GNU
  5349. Library General Public License for more details.
  5350.  
  5351. You should have received a copy of the GNU Library  General Public
  5352. License along with the GNU C Library; see the file  COPYING.LIB.  If
  5353. not, write to the Free Software Foundation, Inc., 675  Mass Ave,
  5354. Cambridge, MA 02139, USA.  */
  5355.  
  5356. #include <sys/types.h>
  5357. #include <errno.h>
  5358. #ifdef STDC_HEADERS
  5359. #include <stdlib.h>
  5360. #else
  5361. extern int errno;
  5362. #endif /* STDC_HEADERS */
  5363.  
  5364. #if defined(STDC_HEADERS) || defined(USG)
  5365. #include <string.h>
  5366. #else /* Not (STDC_HEADERS or USG): */
  5367. #include <strings.h>
  5368. #endif /* STDC_HEADERS or USG */
  5369.  
  5370. #ifndef NULL
  5371. #define NULL 0
  5372. #endif /* !NULL */
  5373.  
  5374. #if !__STDC__
  5375. #define const
  5376. #endif /* !__STDC__ */
  5377.  
  5378. extern char **environ;
  5379.  
  5380. /*
  5381.  *  Put STRING, which is of the form "NAME=VALUE", in  the environment.
  5382.  */
  5383. PUBLIC int putenv ARGS1(
  5384.     CONST char *,    string)
  5385. {
  5386.   char *name_end = strchr(string, '=');
  5387.   register size_t size;
  5388.   register char **ep;
  5389.  
  5390.   if (name_end == NULL)
  5391.     {
  5392.       /* Remove the variable from the environment.  */
  5393.       size = strlen (string);
  5394.       for (ep = environ; *ep != NULL; ++ep)
  5395.     if (!strncmp (*ep, string, size) && (*ep)[size]  == '=')
  5396.       {
  5397.         while (ep[1] != NULL)
  5398.           {
  5399.         ep[0] = ep[1];
  5400.         ++ep;
  5401.           }
  5402.         *ep = NULL;
  5403.         return 0;
  5404.       }
  5405.     }
  5406.  
  5407.   size = 0;
  5408.   for (ep = environ; *ep != NULL; ++ep)
  5409.     if (!strncmp (*ep, string, name_end - string) && (*ep)[name_end - string] == '=')
  5410.       break;
  5411.     else
  5412.       ++size;
  5413.  
  5414.   if (*ep == NULL)
  5415.     {
  5416.       static char **last_environ = NULL;
  5417.       char **new_environ = (char **) malloc ((size + 2) * sizeof (char *));
  5418.       if (new_environ == NULL)
  5419.     return -1;
  5420.       (void) memcpy((char *)new_environ, (char *)environ, size * sizeof(char *));
  5421.       new_environ[size] = (char *) string;
  5422.       new_environ[size + 1] = NULL;
  5423.       if (last_environ != NULL)
  5424.     free ((char *) last_environ);
  5425.       last_environ = new_environ;
  5426.       environ = new_environ;
  5427.     }
  5428.   else
  5429.     *ep = (char *) string;
  5430.  
  5431.   return 0;
  5432. }
  5433. #endif /* !HAVE_PUTENV */
  5434.  
  5435. #ifdef NEED_REMOVE
  5436. int remove ARGS1(char *, name)
  5437. {
  5438.     return unlink(name);
  5439. }
  5440. #endif
  5441.  
  5442. #ifdef UNIX
  5443. /*
  5444.  * Open a file that we don't want other users to see.  For new files, the umask
  5445.  * will suffice; however if the file already exists we'll change permissions
  5446.  * first, before opening it.  If the chmod fails because of some reason other
  5447.  * than a non-existent file, there's no point in trying to open it.
  5448.  */
  5449. PRIVATE FILE *OpenHiddenFile ARGS2(char *, name, char *, mode)
  5450. {
  5451.     int save = umask(HIDE_UMASK);
  5452.     FILE *fp = 0;
  5453.     if (chmod(name, HIDE_CHMOD) == 0 || errno == ENOENT)
  5454.     fp = fopen(name, mode);
  5455.     umask(save);
  5456.     return fp;
  5457. }
  5458. #else
  5459. # ifndef VMS
  5460. #  define OpenHiddenFile(name, mode) fopen(name, mode)
  5461. # endif
  5462. #endif
  5463.  
  5464. PUBLIC FILE *LYNewBinFile ARGS1(char *, name)
  5465. {
  5466. #ifdef VMS
  5467.     FILE *fp = fopen (name, "wb", "mbc=32");
  5468.     chmod(name, HIDE_CHMOD);
  5469. #else
  5470.     FILE *fp = OpenHiddenFile(name, "wb");
  5471. #endif
  5472.     return fp;
  5473. }
  5474.  
  5475. PUBLIC FILE *LYNewTxtFile ARGS1(char *, name)
  5476. {
  5477. #ifdef VMS
  5478.     FILE *fp = fopen (name, "w", "shr=get");
  5479.     chmod(name, HIDE_CHMOD);
  5480. #else
  5481.     FILE *fp = OpenHiddenFile(name, "w");
  5482. #endif
  5483.     return fp;
  5484. }
  5485.  
  5486. PUBLIC FILE *LYAppendToTxtFile ARGS1(char *, name)
  5487. {
  5488. #ifdef VMS
  5489.     FILE *fp = fopen (name, "a+", "shr=get");
  5490.     chmod(name, HIDE_CHMOD);
  5491. #else
  5492.     FILE *fp = OpenHiddenFile(name, "a+");
  5493. #endif
  5494.     return fp;
  5495. }
  5496.  
  5497. #ifdef UNIX
  5498. /*
  5499.  *  Restore normal permisions to a copy of a file that we have created
  5500.  *  with temp file restricted permissions.  The normal umask should
  5501.  *  apply for user files. - kw
  5502.  */
  5503. PUBLIC void LYRelaxFilePermissions ARGS1(CONST char *, name)
  5504. {
  5505.     int mode;
  5506.     struct stat stat_buf;
  5507.     if (stat(name, &stat_buf) == 0 &&
  5508.     S_ISREG(stat_buf.st_mode) &&
  5509.     (mode = (stat_buf.st_mode & 0777)) == HIDE_CHMOD) {
  5510.     /*
  5511.      *  It looks plausible that this is a file we created with
  5512.      *  temp file paranoid permissions (and the umask wasn't even
  5513.      *  more restrictive when it was copied). - kw
  5514.      */
  5515.     int save = umask(HIDE_UMASK);
  5516.     mode = ((mode & 0700) | 0066) & ~save;
  5517.     umask(save);
  5518.     chmod(name, mode);
  5519.     }
  5520. }
  5521. #endif
  5522.