#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DJGPP_KEYHANDLER #include #endif /* DJGPP_KEYHANDLER */ #ifdef VMS #include #include #include #endif /* VMS */ #if HAVE_UTMP #include #ifdef UTMPX_FOR_UTMP #include #define utmp utmpx #ifdef UTMP_FILE #undef UTMP_FILE #endif /* UTMP_FILE */ #ifdef UTMPX_FILE #define UTMP_FILE UTMPX_FILE #else #define UTMP_FILE __UTMPX_FILE /* at least in OS/390 S/390 -- gil -- 2100 */ #endif /* UTMPX_FILE */ #else #include #endif /* UTMPX_FOR_UTMP */ #endif /* HAVE_UTMP */ #if NEED_PTEM_H /* they neglected to define struct winsize in termios.h -- it's only in * termio.h and ptem.h (the former conflicts with other definitions). */ #include #include #endif #include #ifdef USE_COLOR_STYLE #include #include #include #endif #ifdef SVR4_BSDSELECT extern int BSDselect PARAMS((int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval * timeout)); #ifdef select #undef select #endif /* select */ #define select BSDselect #ifdef SOCKS #ifdef Rselect #undef Rselect #endif /* Rselect */ #define Rselect BSDselect #endif /* SOCKS */ #endif /* SVR4_BSDSELECT */ #ifndef FD_SETSIZE #define FD_SETSIZE 256 #endif /* !FD_SETSIZE */ #ifndef UTMP_FILE #if defined(__FreeBSD__) || defined(__bsdi__) #define UTMP_FILE _PATH_UTMP #else #define UTMP_FILE "/etc/utmp" #endif /* __FreeBSD__ || __bsdi__ */ #endif /* !UTMP_FILE */ #define COPY_COMMAND "%s %s %s" extern HTkcode kanji_code; extern BOOLEAN LYHaveCJKCharacterSet; extern HTCJKlang HTCJK; PRIVATE HTList * localhost_aliases = NULL; /* Hosts to treat as local */ PRIVATE char *HomeDir = NULL; /* HOME directory */ PUBLIC HTList * sug_filenames = NULL; /* Suggested filenames */ /* * Highlight (or unhighlight) a given link. */ PUBLIC void highlight ARGS3( int, flag, int, cur, char *, target) { char buffer[200]; int i; char tmp[7]; #if defined(FANCY_CURSES) || defined(USE_SLANG) char *cp; char *theData = NULL; char *Data = NULL; int Offset, HitOffset, tLen; int LenNeeded; BOOL TargetEmphasisON = FALSE; #endif BOOL utf_flag = (LYCharSet_UC[current_char_set].enc == UCT_ENC_UTF8); #if defined(USE_COLOR_STYLE) && !defined(NO_HILIT_FIX) BOOL hl2_drawn=FALSE; /* whether links[cur].hightext2 is already drawn properly */ #endif tmp[0] = tmp[1] = tmp[2] = '\0'; /* * Bugs in the history code might cause -1 to be sent for cur, which * yields a crash when LYstrncpy() is called with a nonsense pointer. * As far as I know, such bugs have been squashed, but if they should * reappear, this works around them. - FM */ if (cur < 0) cur = 0; if (nlinks > 0) { #if defined(USE_COLOR_STYLE) && !defined(NO_HILIT_FIX) if (flag == ON || links[cur].type == WWW_FORM_LINK_TYPE) #endif { #ifdef USE_COLOR_STYLE #define LXP (links[cur].lx) #define LYP (links[cur].ly) #endif move(links[cur].ly, links[cur].lx); #ifndef USE_COLOR_STYLE lynx_start_link_color (flag == ON, links[cur].inUnderline); #else if (flag == ON) { LynxChangeStyle(s_alink, STACK_ON, 0); } else { int s, x; /* * This is where we try to restore the original style when * a link is unhighlighted. The purpose of cached_styles[][] * is to save the original style just for this case. * If it doesn't have a color change saved at just the right * position, we look at preceding positions in the same line * until we find one. */ if (LYP >= 0 && LYP < CACHEH && LXP >= 0 && LXP < CACHEW) { s = cached_styles[LYP][LXP]; if (s == 0) { for (x = LXP-1; x >= 0; x--) { if (cached_styles[LYP][x]) { if (cached_styles[LYP][x] > 0) { s = cached_styles[LYP][x]; cached_styles[LYP][LXP] = s; } break; } } if (s == 0) s = s_a; } } else { s = s_a; } LynxChangeStyle(s, STACK_ON, 0); } #endif } if (links[cur].type == WWW_FORM_LINK_TYPE) { int len; int avail_space = (LYcols - links[cur].lx) - 1; LYstrncpy(buffer, (links[cur].hightext ? links[cur].hightext : ""), (avail_space > links[cur].form->size ? links[cur].form->size : avail_space)); addstr(buffer); len = strlen(buffer); for (; len < links[cur].form->size && len < avail_space; len++) addch('_'); } else { #if defined(USE_COLOR_STYLE) && !defined(NO_HILIT_FIX) if (flag == OFF) { hl2_drawn = TRUE; redraw_lines_of_link(cur); } else #endif { /* * Copy into the buffer only what will fit * within the width of the screen. */ LYmbcsstrncpy(buffer, (links[cur].hightext ? links[cur].hightext : ""), (sizeof(buffer) - 1), ((LYcols - 1) - links[cur].lx), utf_flag); addstr(buffer); } } /* * Display a second line as well. */ if ( links[cur].hightext2 && links[cur].ly < display_lines #if defined(USE_COLOR_STYLE) && !defined(NO_HILIT_FIX) && hl2_drawn == FALSE #endif ) { lynx_stop_link_color (flag == ON, links[cur].inUnderline); move((links[cur].ly + 1), links[cur].hightext2_offset); #ifndef USE_COLOR_STYLE lynx_start_link_color (flag == ON, links[cur].inUnderline); #else LynxChangeStyle(flag == ON ? s_alink : s_a, ABS_ON, 0); #endif for (i = 0; (tmp[0] = links[cur].hightext2[i]) != '\0' && i+links[cur].hightext2_offset < LYcols; i++) { if (!IsSpecialAttrChar(links[cur].hightext2[i])) { /* * For CJK strings, by Masanobu Kimura. */ if (HTCJK != NOCJK && !isascii(tmp[0])) { tmp[1] = links[cur].hightext2[++i]; addstr(tmp); tmp[1] = '\0'; } else { addstr(tmp); } } } } #if defined(USE_COLOR_STYLE) && !defined(NO_HILIT_FIX) if ( hl2_drawn == FALSE ) #endif lynx_stop_link_color (flag == ON, links[cur].inUnderline); #if defined(FANCY_CURSES) || defined(USE_SLANG) /* * If we have an emphasized WHEREIS hit in the highlighted * text, restore the emphasis. Note that we never emphasize * the first and last characters of the highlighted text when * we are making the link current, so the link attributes for * the current link will persist at the beginning and end, * providing an indication to the user that it has been made * current. Also note that we use HText_getFirstTargetInLine() * to determine if there's a hit in the HText structure line * containing the link, and if so, get back a copy of the line * starting at that first hit (which might be before or after * our link), and with all IsSpecial characters stripped, so we * don't need to deal with them here. - FM */ if (target && *target && (links[cur].type & WWW_LINK_TYPE) && links[cur].hightext && *links[cur].hightext && HText_getFirstTargetInLine(HTMainText, links[cur].anchor_line_num, utf_flag, (int *)&Offset, (int *)&tLen, (char **)&theData, target)) { int itmp, written, len, y, offset; char *data; int tlen = strlen(target); int hlen, hLen; int hLine = links[cur].ly, hoffset = links[cur].lx; size_t utf_extra = 0; /* * Copy into the buffer only what will fit * up to the right border of the screen. - FM */ LYmbcsstrncpy(buffer, (links[cur].hightext ? links[cur].hightext : ""), (sizeof(buffer) - 1), ((LYcols - 1) - links[cur].lx), utf_flag); hlen = strlen(buffer); hLen = ((HTCJK != NOCJK || utf_flag) ? LYmbcsstrlen(buffer, utf_flag) : hlen); /* * Break out if the first hit in the line * starts after this link. - FM */ if (Offset >= (hoffset + hLen)) { goto highlight_search_hightext2; } /* * Recursively skip hits that end before this link, and * break out if there is no hit beyond those. - FM */ Data = theData; while ((Offset < hoffset) && ((Offset + tLen) <= hoffset)) { data = (Data + tlen); offset = (Offset + tLen); if ((case_sensitive ? (cp = LYno_attr_mbcs_strstr(data, target, utf_flag, &HitOffset, &LenNeeded)) != NULL : (cp = LYno_attr_mbcs_case_strstr(data, target, utf_flag, &HitOffset, &LenNeeded)) != NULL) && (offset + LenNeeded) < LYcols) { Data = cp; Offset = (offset + HitOffset); } else { goto highlight_search_hightext2; } } data = buffer; offset = hoffset; /* * If the hit starts before the hightext, and ends * in or beyond the hightext, restore the emphasis, * skipping the first and last characters of the * hightext if we're making the link current. - FM */ if ((Offset < offset) && ((Offset + tLen) > offset)) { itmp = 0; written = 0; len = (tlen - (offset - Offset)); /* * Go to the start of the hightext and * handle its first character. - FM */ move(hLine, offset); tmp[0] = data[itmp]; if (utf_flag && !isascii(tmp[0])) { if ((*tmp & 0xe0) == 0xc0) { utf_extra = 1; } else if ((*tmp & 0xf0) == 0xe0) { utf_extra = 2; } else if ((*tmp & 0xf8) == 0xf0) { utf_extra = 3; } else if ((*tmp & 0xfc) == 0xf8) { utf_extra = 4; } else if ((*tmp & 0xfe) == 0xfc) { utf_extra = 5; } else { /* * Garbage. */ utf_extra = 0; } if (strlen(&data[itmp+1]) < utf_extra) { /* * Shouldn't happen. */ utf_extra = 0; } } if (utf_extra) { LYstrncpy(&tmp[1], &data[itmp+1], utf_extra); itmp += utf_extra; /* * Start emphasis immediately if we are * making the link non-current. - FM */ if (flag != ON) { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; addstr(tmp); } else { move(hLine, (offset + 1)); } tmp[1] = '\0'; written += (utf_extra + 1); utf_extra = 0; } else if (HTCJK != NOCJK && !isascii(tmp[0])) { /* * For CJK strings, by Masanobu Kimura. */ tmp[1] = data[++itmp]; /* * Start emphasis immediately if we are * making the link non-current. - FM */ if (flag != ON) { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; addstr(tmp); } else { move(hLine, (offset + 1)); } tmp[1] = '\0'; written += 2; } else { /* * Start emphasis immediately if we are making * the link non-current. - FM */ if (flag != ON) { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; addstr(tmp); } else { move(hLine, (offset + 1)); } written++; } itmp++; /* * Start emphasis after the first character * if we are making the link current and this * is not the last character. - FM */ if (!TargetEmphasisON && data[itmp] != '\0') { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; } /* * Handle the remaining characters. - FM */ for (; written < len && (tmp[0] = data[itmp]) != '\0'; itmp++) { /* * Print all the other target chars, except * the last character if it is also the last * character of hightext and we are making * the link current. - FM */ if (utf_flag && !isascii(tmp[0])) { if ((*tmp & 0xe0) == 0xc0) { utf_extra = 1; } else if ((*tmp & 0xf0) == 0xe0) { utf_extra = 2; } else if ((*tmp & 0xf8) == 0xf0) { utf_extra = 3; } else if ((*tmp & 0xfc) == 0xf8) { utf_extra = 4; } else if ((*tmp & 0xfe) == 0xfc) { utf_extra = 5; } else { /* * Garbage. */ utf_extra = 0; } if (strlen(&data[itmp+1]) < utf_extra) { /* * Shouldn't happen. */ utf_extra = 0; } } if (utf_extra) { LYstrncpy(&tmp[1], &data[itmp+1], utf_extra); itmp += utf_extra; /* * Make sure we don't restore emphasis to * the last character of hightext if we * are making the link current. - FM */ if (flag == ON && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; LYGetYX(y, offset); move(hLine, (offset + 1)); } else { addstr(tmp); } tmp[1] = '\0'; written += (utf_extra + 1); utf_extra = 0; } else if (HTCJK != NOCJK && !isascii(tmp[0])) { /* * For CJK strings, by Masanobu Kimura. */ tmp[1] = data[++itmp]; /* * Make sure we don't restore emphasis to * the last character of hightext if we * are making the link current. - FM */ if (flag == ON && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; LYGetYX(y, offset); move(hLine, (offset + 1)); } else { addstr(tmp); } tmp[1] = '\0'; written += 2; } else { /* * Make sure we don't restore emphasis to * the last character of hightext if we * are making the link current. - FM */ if (flag == ON && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; LYGetYX(y, offset); move(hLine, (offset + 1)); } else { addstr(tmp); } written++; } } /* * Stop the emphasis if we haven't already, then * reset the offset to our current position in * the line, and if that is beyond the link, or * or we are making the link current and it is * the last character of the hightext, we are * done. - FM */ if (TargetEmphasisON) { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; } LYGetYX(y, offset); if (offset >= (hoffset + (flag == ON ? (hLen - 1) : hLen))) { goto highlight_search_hightext2; } /* * See if we have another hit that starts * within the hightext. - FM */ data = (Data + (offset - Offset)); if (!utf_flag) { data = Data + (offset - Offset); } else { data = LYmbcs_skip_glyphs(Data, (offset - Offset), utf_flag); } if ((case_sensitive ? (cp = LYno_attr_mbcs_strstr(data, target, utf_flag, &HitOffset, &LenNeeded)) != NULL : (cp = LYno_attr_mbcs_case_strstr(data, target, utf_flag, &HitOffset, &LenNeeded)) != NULL) && (offset + LenNeeded) < LYcols) { /* * If the hit starts after the end of the hightext, * or we are making the link current and the hit * starts at its last character, we are done. - FM */ if ((HitOffset + offset) >= (hoffset + (flag == ON ? (hLen - 1) : hLen))) { goto highlight_search_hightext2; } /* * Set up the data and offset for the hit, and let * the code for within hightext hits handle it. - FM */ Data = cp; Offset = (offset + HitOffset); data = buffer; offset = hoffset; goto highlight_hit_within_hightext; } goto highlight_search_hightext2; } highlight_hit_within_hightext: /* * If we get to here, the hit starts within the * hightext. If we are making the link current * and it's the last character in the hightext, * we are done. Otherwise, move there and start * restoring the emphasis. - FM */ if ((Offset - offset) > (flag == ON ? (hLen - 1) : hLen)) { goto highlight_search_hightext2; } if (!utf_flag) { data += (Offset - offset); } else { refresh(); data = LYmbcs_skip_glyphs(data, (Offset - offset), utf_flag); } offset = Offset; itmp = 0; written = 0; len = tlen; /* * Go to the start of the hit and * handle its first character. - FM */ move(hLine, offset); tmp[0] = data[itmp]; if (utf_flag && !isascii(tmp[0])) { if ((*tmp & 0xe0) == 0xc0) { utf_extra = 1; } else if ((*tmp & 0xf0) == 0xe0) { utf_extra = 2; } else if ((*tmp & 0xf8) == 0xf0) { utf_extra = 3; } else if ((*tmp & 0xfc) == 0xf8) { utf_extra = 4; } else if ((*tmp & 0xfe) == 0xfc) { utf_extra = 5; } else { /* * Garbage. */ utf_extra = 0; } if (strlen(&data[itmp+1]) < utf_extra) { /* * Shouldn't happen. */ utf_extra = 0; } } if (utf_extra) { LYstrncpy(&tmp[1], &data[itmp+1], utf_extra); itmp += utf_extra; /* * Start emphasis immediately if we are making * the link non-current, or we are making it * current but this is not the first or last * character of the hightext. - FM */ if (flag != ON || (offset > hoffset && data[itmp+1] != '\0')) { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; addstr(tmp); } else { move(hLine, (offset + 1)); } tmp[1] = '\0'; written += (utf_extra + 1); utf_extra = 0; } else if (HTCJK != NOCJK && !isascii(tmp[0])) { /* * For CJK strings, by Masanobu Kimura. */ tmp[1] = data[++itmp]; /* * Start emphasis immediately if we are making * the link non-current, or we are making it * current but this is not the first or last * character of the hightext. - FM */ if (flag != ON || (offset > hoffset && data[itmp+1] != '\0')) { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; addstr(tmp); } else { move(hLine, (offset + 1)); } tmp[1] = '\0'; written += 2; } else { /* * Start emphasis immediately if we are making * the link non-current, or we are making it * current but this is not the first or last * character of the hightext. - FM */ if (flag != ON || (offset > hoffset && data[itmp+1] != '\0')) { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; addstr(tmp); } else { move(hLine, (offset + 1)); } written++; } itmp++; /* * Start emphasis after the first character * if we are making the link current and this * is not the last character. - FM */ if (!TargetEmphasisON && data[itmp] != '\0') { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; } for (; written < len && (tmp[0] = data[itmp]) != '\0'; itmp++) { /* * Print all the other target chars, except * the last character if it is also the last * character of hightext and we are making * the link current. - FM */ if (utf_flag && !isascii(tmp[0])) { if ((*tmp & 0xe0) == 0xc0) { utf_extra = 1; } else if ((*tmp & 0xf0) == 0xe0) { utf_extra = 2; } else if ((*tmp & 0xf8) == 0xf0) { utf_extra = 3; } else if ((*tmp & 0xfc) == 0xf8) { utf_extra = 4; } else if ((*tmp & 0xfe) == 0xfc) { utf_extra = 5; } else { /* * Garbage. */ utf_extra = 0; } if (strlen(&data[itmp+1]) < utf_extra) { /* * Shouldn't happen. */ utf_extra = 0; } } if (utf_extra) { LYstrncpy(&tmp[1], &data[itmp+1], utf_extra); itmp += utf_extra; /* * Make sure we don't restore emphasis to * the last character of hightext if we * are making the link current. - FM */ if (flag == ON && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; LYGetYX(y, offset); move(hLine, (offset + 1)); } else { addstr(tmp); } tmp[1] = '\0'; written += (utf_extra + 1); utf_extra = 0; } else if (HTCJK != NOCJK && !isascii(tmp[0])) { /* * For CJK strings, by Masanobu Kimura. */ tmp[1] = data[++itmp]; /* * Make sure we don't restore emphasis to * the last character of hightext if we * are making the link current. - FM */ if (flag == ON && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; LYGetYX(y, offset); move(hLine, (offset + 1)); } else { addstr(tmp); } tmp[1] = '\0'; written += 2; } else { /* * Make sure we don't restore emphasis to * the last character of hightext if we * are making the link current. - FM */ if (flag == ON && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; LYGetYX(y, offset); move(hLine, (offset + 1)); } else { addstr(tmp); } written++; } } /* * Stop the emphasis if we haven't already, then reset * the offset to our current position in the line, and * if that is beyond the link, or we are making the link * current and it is the last character in the hightext, * we are done. - FM */ if (TargetEmphasisON) { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; } LYGetYX(y, offset); if (offset >= (hoffset + (flag == ON ? (hLen - 1) : hLen))) { goto highlight_search_hightext2; } /* * See if we have another hit that starts * within the hightext. - FM */ if (!utf_flag) { data = Data + (offset - Offset); } else { data = LYmbcs_skip_glyphs(Data, (offset - Offset), utf_flag); } if ((case_sensitive ? (cp = LYno_attr_mbcs_strstr(data, target, utf_flag, &HitOffset, &LenNeeded)) != NULL : (cp = LYno_attr_mbcs_case_strstr(data, target, utf_flag, &HitOffset, &LenNeeded)) != NULL) && (offset + LenNeeded) < LYcols) { /* * If the hit starts after the end of the hightext, * or we are making the link current and the hit * starts at its last character, we are done. - FM */ if ((HitOffset + offset) >= (hoffset + (flag == ON ? (hLen - 1) : hLen))) { goto highlight_search_hightext2; } /* * If the target extends beyond our buffer, emphasize * everything in the hightext starting at this hit. * Otherwise, set up the data and offsets, and loop * back. - FM */ if ((HitOffset + (offset + tLen)) >= (hoffset + hLen)) { offset = (HitOffset + offset); if (!utf_flag) { data = buffer + (offset - hoffset); } else { refresh(); data = LYmbcs_skip_glyphs(buffer, (offset - hoffset), utf_flag); } move(hLine, offset); itmp = 0; written = 0; len = strlen(data); /* * Turn the emphasis back on. - FM */ LYstartTargetEmphasis(); TargetEmphasisON = TRUE; for (; written < len && (tmp[0] = data[itmp]) != '\0'; itmp++) { /* * Print all the other target chars, except * the last character if it is also the last * character of hightext and we are making * the link current. - FM */ if (utf_flag && !isascii(tmp[0])) { if ((*tmp & 0xe0) == 0xc0) { utf_extra = 1; } else if ((*tmp & 0xf0) == 0xe0) { utf_extra = 2; } else if ((*tmp & 0xf8) == 0xf0) { utf_extra = 3; } else if ((*tmp & 0xfc) == 0xf8) { utf_extra = 4; } else if ((*tmp & 0xfe) == 0xfc) { utf_extra = 5; } else { /* * Garbage. */ utf_extra = 0; } if (strlen(&data[itmp+1]) < utf_extra) { /* * Shouldn't happen. */ utf_extra = 0; } } if (utf_extra) { LYstrncpy(&tmp[1], &data[itmp+1], utf_extra); itmp += utf_extra; /* * Make sure we don't restore emphasis to * the last character of hightext if we * are making the link current. - FM */ if (flag == ON && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; LYGetYX(y, offset); move(hLine, (offset + 1)); } else { addstr(tmp); } tmp[1] = '\0'; written += (utf_extra + 1); utf_extra = 0; } else if (HTCJK != NOCJK && !isascii(tmp[0])) { /* * For CJK strings, by Masanobu Kimura. */ tmp[1] = data[++itmp]; /* * Make sure we don't restore emphasis to * the last character of hightext if we * are making the link current. - FM */ if (flag == ON && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; } else { addstr(tmp); } tmp[1] = '\0'; written += 2; } else { /* * Make sure we don't restore emphasis to * the last character of hightext if we * are making the link current. - FM */ if (flag == ON && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; } else { addstr(tmp); } written++; } } /* * Turn off the emphasis if we haven't already, * and then we're done. - FM */ if (TargetEmphasisON) { LYstopTargetEmphasis(); } goto highlight_search_hightext2; } else { Data = cp; Offset = (offset + HitOffset); data = buffer; offset = hoffset; goto highlight_hit_within_hightext; } } goto highlight_search_hightext2; } highlight_search_hightext2: if (target && *target && (links[cur].type & WWW_LINK_TYPE) && links[cur].hightext2 && *links[cur].hightext2 && links[cur].ly < display_lines && HText_getFirstTargetInLine(HTMainText, (links[cur].anchor_line_num + 1), utf_flag, (int *)&Offset, (int *)&tLen, (char **)&theData, target)) { int itmp, written, len, y, offset; char *data; int tlen = strlen(target); int hlen, hLen; int hLine = (links[cur].ly + 1); int hoffset = links[cur].hightext2_offset; size_t utf_extra = 0; /* * Copy into the buffer only what will fit * up to the right border of the screen. - FM */ LYmbcsstrncpy(buffer, (links[cur].hightext2 ? links[cur].hightext2 : ""), (sizeof(buffer) - 1), ((LYcols - 1) - links[cur].hightext2_offset), utf_flag); hlen = strlen(buffer); hLen = ((HTCJK != NOCJK || utf_flag) ? LYmbcsstrlen(buffer, utf_flag) : hlen); /* * Break out if the first hit in the line * starts after this link. - FM */ if (Offset >= (hoffset + hLen)) { goto highlight_search_done; } /* * Recursively skip hits that end before this link, and * break out if there is no hit beyond those. - FM */ Data = theData; while ((Offset < hoffset) && ((Offset + tLen) <= hoffset)) { data = (Data + tlen); offset = (Offset + tLen); if ((case_sensitive ? (cp = LYno_attr_mbcs_strstr(data, target, utf_flag, &HitOffset, &LenNeeded)) != NULL : (cp = LYno_attr_mbcs_case_strstr(data, target, utf_flag, &HitOffset, &LenNeeded)) != NULL) && (offset + LenNeeded) < LYcols) { Data = cp; Offset = (offset + HitOffset); } else { goto highlight_search_done; } } data = buffer; offset = hoffset; /* * If the hit starts before the hightext2, and ends * in or beyond the hightext2, restore the emphasis, * skipping the first and last characters of the * hightext2 if we're making the link current. - FM */ if ((Offset < offset) && ((Offset + tLen) > offset)) { itmp = 0; written = 0; len = (tlen - (offset - Offset)); /* * Go to the start of the hightext2 and * handle its first character. - FM */ move(hLine, offset); tmp[0] = data[itmp]; if (utf_flag && !isascii(tmp[0])) { if ((*tmp & 0xe0) == 0xc0) { utf_extra = 1; } else if ((*tmp & 0xf0) == 0xe0) { utf_extra = 2; } else if ((*tmp & 0xf8) == 0xf0) { utf_extra = 3; } else if ((*tmp & 0xfc) == 0xf8) { utf_extra = 4; } else if ((*tmp & 0xfe) == 0xfc) { utf_extra = 5; } else { /* * Garbage. */ utf_extra = 0; } if (strlen(&data[itmp+1]) < utf_extra) { /* * Shouldn't happen. */ utf_extra = 0; } } if (utf_extra) { LYstrncpy(&tmp[1], &data[itmp+1], utf_extra); itmp += utf_extra; /* * Start emphasis immediately if we are * making the link non-current. - FM */ if (flag != ON) { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; addstr(tmp); } else { move(hLine, (offset + 1)); } tmp[1] = '\0'; written += (utf_extra + 1); utf_extra = 0; } else if (HTCJK != NOCJK && !isascii(tmp[0])) { /* * For CJK strings, by Masanobu Kimura. */ tmp[1] = data[++itmp]; /* * Start emphasis immediately if we are * making the link non-current. - FM */ if (flag != ON) { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; addstr(tmp); } else { move(hLine, (offset + 1)); } tmp[1] = '\0'; written += 2; } else { /* * Start emphasis immediately if we are making * the link non-current. - FM */ if (flag != ON) { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; addstr(tmp); } else { move(hLine, (offset + 1)); } written++; } itmp++; /* * Start emphasis after the first character * if we are making the link current and this * is not the last character. - FM */ if (!TargetEmphasisON && data[itmp] != '\0') { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; } /* * Handle the remaining characters. - FM */ for (; written < len && (tmp[0] = data[itmp]) != '\0'; itmp++) { /* * Print all the other target chars, except * the last character if it is also the last * character of hightext2 and we are making * the link current. - FM */ if (utf_flag && !isascii(tmp[0])) { if ((*tmp & 0xe0) == 0xc0) { utf_extra = 1; } else if ((*tmp & 0xf0) == 0xe0) { utf_extra = 2; } else if ((*tmp & 0xf8) == 0xf0) { utf_extra = 3; } else if ((*tmp & 0xfc) == 0xf8) { utf_extra = 4; } else if ((*tmp & 0xfe) == 0xfc) { utf_extra = 5; } else { /* * Garbage. */ utf_extra = 0; } if (strlen(&data[itmp+1]) < utf_extra) { /* * Shouldn't happen. */ utf_extra = 0; } } if (utf_extra) { LYstrncpy(&tmp[1], &data[itmp+1], utf_extra); itmp += utf_extra; /* * Make sure we don't restore emphasis to * the last character of hightext2 if we * are making the link current. - FM */ if (flag == ON && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; LYGetYX(y, offset); move(hLine, (offset + 1)); } else { addstr(tmp); } tmp[1] = '\0'; written += (utf_extra + 1); utf_extra = 0; } else if (HTCJK != NOCJK && !isascii(tmp[0])) { /* * For CJK strings, by Masanobu Kimura. */ tmp[1] = data[++itmp]; /* * Make sure we don't restore emphasis to * the last character of hightext2 if we * are making the link current. - FM */ if (flag == ON && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; LYGetYX(y, offset); move(hLine, (offset + 1)); } else { addstr(tmp); } tmp[1] = '\0'; written += 2; } else { /* * Make sure we don't restore emphasis to * the last character of hightext2 if we * are making the link current. - FM */ if (flag == ON && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; LYGetYX(y, offset); move(hLine, (offset + 1)); } else { addstr(tmp); } written++; } } /* * Stop the emphasis if we haven't already, then * reset the offset to our current position in * the line, and if that is beyond the link, or * or we are making the link current and it is * the last character of the hightext2, we are * done. - FM */ if (TargetEmphasisON) { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; } LYGetYX(y, offset); if (offset >= (hoffset + (flag == ON ? (hLen - 1) : hLen))) { goto highlight_search_done; } /* * See if we have another hit that starts * within the hightext2. - FM */ if (!utf_flag) { data = Data + (offset - Offset); } else { data = LYmbcs_skip_glyphs(Data, (offset - Offset), utf_flag); } if ((case_sensitive ? (cp = LYno_attr_mbcs_strstr(data, target, utf_flag, &HitOffset, &LenNeeded)) != NULL : (cp = LYno_attr_mbcs_case_strstr(data, target, utf_flag, &HitOffset, &LenNeeded)) != NULL) && (offset + LenNeeded) < LYcols) { /* * If the hit starts after the end of the hightext2, * or we are making the link current and the hit * starts at its last character, we are done. - FM */ if ((HitOffset + offset) >= (hoffset + (flag == ON ? (hLen - 1) : hLen))) { goto highlight_search_done; } /* * Set up the data and offset for the hit, and let * the code for within hightext2 hits handle it. - FM */ Data = cp; Offset = (offset + HitOffset); data = buffer; offset = hoffset; goto highlight_hit_within_hightext2; } goto highlight_search_done; } highlight_hit_within_hightext2: /* * If we get to here, the hit starts within the * hightext2. If we are making the link current * and it's the last character in the hightext2, * we are done. Otherwise, move there and start * restoring the emphasis. - FM */ if ((Offset - offset) > (flag == ON ? (hLen - 1) : hLen)) { goto highlight_search_done; } if (!utf_flag) { data += (Offset - offset); } else { refresh(); data = LYmbcs_skip_glyphs(data, (Offset - offset), utf_flag); } offset = Offset; itmp = 0; written = 0; len = tlen; /* * Go to the start of the hit and * handle its first character. - FM */ move(hLine, offset); tmp[0] = data[itmp]; if (utf_flag && !isascii(tmp[0])) { if ((*tmp & 0xe0) == 0xc0) { utf_extra = 1; } else if ((*tmp & 0xf0) == 0xe0) { utf_extra = 2; } else if ((*tmp & 0xf8) == 0xf0) { utf_extra = 3; } else if ((*tmp & 0xfc) == 0xf8) { utf_extra = 4; } else if ((*tmp & 0xfe) == 0xfc) { utf_extra = 5; } else { /* * Garbage. */ utf_extra = 0; } if (strlen(&data[itmp+1]) < utf_extra) { /* * Shouldn't happen. */ utf_extra = 0; } } if (utf_extra) { LYstrncpy(&tmp[1], &data[itmp+1], utf_extra); itmp += utf_extra; /* * Start emphasis immediately if we are making * the link non-current, or we are making it * current but this is not the first or last * character of the hightext2. - FM */ if (flag != ON || (offset > hoffset && data[itmp+1] != '\0')) { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; addstr(tmp); } else { move(hLine, (offset + 1)); } tmp[1] = '\0'; written += (utf_extra + 1); utf_extra = 0; } else if (HTCJK != NOCJK && !isascii(tmp[0])) { /* * For CJK strings, by Masanobu Kimura. */ tmp[1] = data[++itmp]; /* * Start emphasis immediately if we are making * the link non-current, or we are making it * current but this is not the first or last * character of the hightext2. - FM */ if (flag != ON || (offset > hoffset && data[itmp+1] != '\0')) { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; addstr(tmp); } else { move(hLine, (offset + 1)); } tmp[1] = '\0'; written += 2; } else { /* * Start emphasis immediately if we are making * the link non-current, or we are making it * current but this is not the first or last * character of the hightext2. - FM */ if (flag != ON || (offset > hoffset && data[itmp+1] != '\0')) { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; addstr(tmp); } else { move(hLine, (offset + 1)); } written++; } itmp++; /* * Start emphasis after the first character * if we are making the link current and this * is not the last character. - FM */ if (!TargetEmphasisON && data[itmp] != '\0') { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; } for (; written < len && (tmp[0] = data[itmp]) != '\0'; itmp++) { /* * Print all the other target chars, except * the last character if it is also the last * character of hightext2 and we are making * the link current. - FM */ if (utf_flag && !isascii(tmp[0])) { if ((*tmp & 0xe0) == 0xc0) { utf_extra = 1; } else if ((*tmp & 0xf0) == 0xe0) { utf_extra = 2; } else if ((*tmp & 0xf8) == 0xf0) { utf_extra = 3; } else if ((*tmp & 0xfc) == 0xf8) { utf_extra = 4; } else if ((*tmp & 0xfe) == 0xfc) { utf_extra = 5; } else { /* * Garbage. */ utf_extra = 0; } if (strlen(&data[itmp+1]) < utf_extra) { /* * Shouldn't happen. */ utf_extra = 0; } } if (utf_extra) { LYstrncpy(&tmp[1], &data[itmp+1], utf_extra); itmp += utf_extra; /* * Make sure we don't restore emphasis to * the last character of hightext2 if we * are making the link current. - FM */ if (flag == ON && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; LYGetYX(y, offset); move(hLine, (offset + 1)); } else { addstr(tmp); } tmp[1] = '\0'; written += (utf_extra + 1); utf_extra = 0; } else if (HTCJK != NOCJK && !isascii(tmp[0])) { /* * For CJK strings, by Masanobu Kimura. */ tmp[1] = data[++itmp]; /* * Make sure we don't restore emphasis to * the last character of hightext2 if we * are making the link current. - FM */ if (flag == ON && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; LYGetYX(y, offset); move(hLine, (offset + 1)); } else { addstr(tmp); } tmp[1] = '\0'; written += 2; } else { /* * Make sure we don't restore emphasis to * the last character of hightext2 if we * are making the link current. - FM */ if (flag == ON && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; LYGetYX(y, offset); move(hLine, (offset + 1)); } else { addstr(tmp); } written++; } } /* * Stop the emphasis if we haven't already, then reset * the offset to our current position in the line, and * if that is beyond the link, or we are making the link * current and it is the last character in the hightext2, * we are done. - FM */ if (TargetEmphasisON) { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; } LYGetYX(y, offset); if (offset >= (hoffset + (flag == ON ? (hLen - 1) : hLen))) { goto highlight_search_done; } /* * See if we have another hit that starts * within the hightext2. - FM */ if (!utf_flag) { data = (Data + (offset - Offset)); } else { data = LYmbcs_skip_glyphs(Data, (offset - Offset), utf_flag); } if ((case_sensitive ? (cp = LYno_attr_mbcs_strstr(data, target, utf_flag, &HitOffset, &LenNeeded)) != NULL : (cp = LYno_attr_mbcs_case_strstr(data, target, utf_flag, &HitOffset, &LenNeeded)) != NULL) && (offset + LenNeeded) < LYcols) { /* * If the hit starts after the end of the hightext2, * or we are making the link current and the hit * starts at its last character, we are done. - FM */ if ((HitOffset + offset) >= (hoffset + (flag == ON ? (hLen - 1) : hLen))) { goto highlight_search_done; } /* * If the target extends beyond our buffer, emphasize * everything in the hightext2 starting at this hit. * Otherwise, set up the data and offsets, and loop * back. - FM */ if ((HitOffset + (offset + tLen)) >= (hoffset + hLen)) { offset = (HitOffset + offset); if (!utf_flag) { data = buffer + (offset - hoffset); } else { refresh(); data = LYmbcs_skip_glyphs(buffer, (offset - hoffset), utf_flag); } move(hLine, offset); itmp = 0; written = 0; len = strlen(data); /* * Turn the emphasis back on. - FM */ LYstartTargetEmphasis(); TargetEmphasisON = TRUE; for (; written < len && (tmp[0] = data[itmp]) != '\0'; itmp++) { /* * Print all the other target chars, except * the last character if it is also the last * character of hightext2 and we are making * the link current. - FM */ if (utf_flag && !isascii(tmp[0])) { if ((*tmp & 0xe0) == 0xc0) { utf_extra = 1; } else if ((*tmp & 0xf0) == 0xe0) { utf_extra = 2; } else if ((*tmp & 0xf8) == 0xf0) { utf_extra = 3; } else if ((*tmp & 0xfc) == 0xf8) { utf_extra = 4; } else if ((*tmp & 0xfe) == 0xfc) { utf_extra = 5; } else { /* * Garbage. */ utf_extra = 0; } if (strlen(&data[itmp+1]) < utf_extra) { /* * Shouldn't happen. */ utf_extra = 0; } } if (utf_extra) { LYstrncpy(&tmp[1], &data[itmp+1], utf_extra); itmp += utf_extra; /* * Make sure we don't restore emphasis to * the last character of hightext2 if we * are making the link current. - FM */ if (flag == ON && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; LYGetYX(y, offset); move(hLine, (offset + 1)); } else { addstr(tmp); } tmp[1] = '\0'; written += (utf_extra + 1); utf_extra = 0; } else if (HTCJK != NOCJK && !isascii(tmp[0])) { /* * For CJK strings, by Masanobu Kimura. */ tmp[1] = data[++itmp]; /* * Make sure we don't restore emphasis to * the last character of hightext2 if we * are making the link current. - FM */ if (flag == ON && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; } else { addstr(tmp); } tmp[1] = '\0'; written += 2; } else { /* * Make sure we don't restore emphasis to * the last character of hightext2 if we * are making the link current. - FM */ if (flag == ON && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; } else { addstr(tmp); } written++; } } /* * Turn off the emphasis if we haven't already, * and then we're done. - FM */ if (TargetEmphasisON) { LYstopTargetEmphasis(); } goto highlight_search_done; } else { Data = cp; Offset = (offset + HitOffset); data = buffer; offset = hoffset; goto highlight_hit_within_hightext2; } } goto highlight_search_done; } highlight_search_done: FREE(theData); if (!LYShowCursor) /* * Get cursor out of the way. */ move((LYlines - 1), (LYcols - 1)); else #endif /* FANCY CURSES || USE_SLANG */ /* * Never hide the cursor if there's no FANCY CURSES or SLANG. */ move(links[cur].ly, ((links[cur].lx > 0) ? (links[cur].lx - 1) : 0)); if (flag) refresh(); } return; } /* * free_and_clear will free a pointer if it * is non-zero and then set it to zero. */ PUBLIC void free_and_clear ARGS1( char **, pointer) { if (*pointer) { FREE(*pointer); *pointer = 0; } return; } /* * Convert single or serial newlines to single spaces throughout a string * (ignore newlines if the preceding character is a space) and convert * tabs to single spaces. Don't ignore any explicit tabs or spaces if * the condense argument is FALSE, otherwise, condense any serial spaces * or tabs to one space. - FM */ PUBLIC void convert_to_spaces ARGS2( char *, string, BOOL, condense) { char *s = string; char *ns = string; BOOL last_is_space = FALSE; if (!string) return; while (*s) { switch (*s) { case ' ': case '\t': if (!(condense && last_is_space)) *(ns++) = ' '; last_is_space = TRUE; break; case '\r': case '\n': if (!last_is_space) { *(ns++) = ' '; last_is_space = TRUE; } break; default: *(ns++) = *s; last_is_space = FALSE; break; } s++; } *ns = '\0'; return; } /* * Strip trailing slashes from directory paths. */ PUBLIC char * strip_trailing_slash ARGS1( char *, dirname) { int i; i = strlen(dirname) - 1; while (i >= 0 && dirname[i] == '/') dirname[i--] = '\0'; return(dirname); } /* * Display (or hide) the status line. */ BOOLEAN mustshow = FALSE; PUBLIC void statusline ARGS1( CONST char *, text) { char buffer[256]; unsigned char *temp = NULL; int max_length, len, i, j; unsigned char k; if (text == NULL) return; /* * Don't print statusline messages if dumping to stdout. */ if (dump_output_immediately) return; /* * Don't print statusline message if turned off. */ if (mustshow != TRUE) { if (no_statusline == TRUE) { return; } } mustshow = FALSE; /* * Deal with any CJK escape sequences and Kanji if we have a CJK * character set selected, otherwise, strip any escapes. Also, * make sure text is not longer than the statusline window. - FM */ max_length = ((LYcols - 2) < (int)sizeof(buffer)) ? (LYcols - 2) : (int)sizeof(buffer)-1; if ((text[0] != '\0') && (LYHaveCJKCharacterSet)) { /* * Translate or filter any escape sequences. - FM */ if ((temp = (unsigned char *)calloc(1, strlen(text) + 1)) == NULL) outofmem(__FILE__, "statusline"); if (kanji_code == EUC) { TO_EUC((CONST unsigned char *)text, temp); } else if (kanji_code == SJIS) { TO_SJIS((CONST unsigned char *)text, temp); } else { for (i = 0, j = 0; text[i]; i++) { if (text[i] != CH_ESC) { /* S/390 -- gil -- 2119 */ temp[j++] = text[i]; } } temp[j] = '\0'; } /* * Deal with any newlines or tabs in the string. - FM */ convert_to_spaces((char *)temp, FALSE); /* * Handle the Kanji, making sure the text is not * longer than the statusline window. - FM */ for (i = 0, j = 0, len = 0, k = '\0'; temp[i] != '\0' && len < max_length; i++) { if (k != '\0') { buffer[j++] = k; buffer[j++] = temp[i]; k = '\0'; len += 2; } else if ((temp[i] & 0200) != 0) { k = temp[i]; } else { buffer[j++] = temp[i]; len++; } } buffer[j] = '\0'; FREE(temp); } else { /* * Strip any escapes, and shorten text if necessary. Note * that we don't deal with the possibility of UTF-8 characters * in the string. This is unlikely, but if strings with such * characters are used in LYMessages_en.h, a compilation * symbol of HAVE_UTF8_STATUSLINES could be added there, and * code added here for determining the displayed string length, * as we do above for CJK. - FM */ for (i = 0, len = 0; text[i] != '\0' && len < max_length; i++) { if (text[i] != CH_ESC) { /* S/390 -- gil -- 2136 */ buffer[len++] = text[i]; } } buffer[len] = '\0'; /* * Deal with any newlines or tabs in the string. - FM */ convert_to_spaces(buffer, FALSE); } /* * Move to the desired statusline window and * output the text highlighted. - FM */ if (LYStatusLine >= 0) { if (LYStatusLine < LYlines-1) { move(LYStatusLine, 0); } else { move(LYlines-1, 0); } } else if (user_mode == NOVICE_MODE) { move(LYlines-3, 0); } else { move(LYlines-1, 0); } clrtoeol(); if (text != NULL && text[0] != '\0') { #ifdef HAVE_UTF8_STATUSLINES if (LYCharSet_UC[current_char_set].enc == UCT_ENC_UTF8) { refresh(); } #endif /* HAVE_UTF8_STATUSLINES */ #ifndef USE_COLOR_STYLE lynx_start_status_color (); addstr (buffer); lynx_stop_status_color (); #else /* draw the status bar in the STATUS style */ { int a=(strncmp(buffer, ALERT_FORMAT, ALERT_PREFIX_LEN) || !hashStyles[s_alert].name) ? s_status : s_alert; LynxChangeStyle (a, STACK_ON, 1); addstr(buffer); wbkgdset(stdscr, ((lynx_has_color && LYShowColor >= SHOW_COLOR_ON) ? hashStyles[a].color :A_NORMAL) | ' '); clrtoeol(); if (!(lynx_has_color && LYShowColor >= SHOW_COLOR_ON)) wbkgdset(stdscr, A_NORMAL | ' '); else if (s_normal != NOSTYLE) wbkgdset(stdscr, hashStyles[s_normal].color | ' '); else wbkgdset(stdscr, displayStyles[DSTYLE_NORMAL].color | ' '); LynxChangeStyle (a, STACK_OFF, 0); } #endif } refresh(); return; } PRIVATE char *novice_lines ARGS1( int, lineno) { switch (lineno) { case 0: return NOVICE_LINE_TWO_A; case 1: return NOVICE_LINE_TWO_B; case 2: return NOVICE_LINE_TWO_C; default: return ""; } } static int lineno = 0; PUBLIC void toggle_novice_line NOARGS { lineno++; if (*novice_lines(lineno) == '\0') lineno = 0; return; } PUBLIC void noviceline ARGS1( int, more_flag GCC_UNUSED) { if (dump_output_immediately) return; move(LYlines-2,0); /* stop_reverse(); */ clrtoeol(); addstr(NOVICE_LINE_ONE); clrtoeol(); #if defined(DIRED_SUPPORT ) && defined(OK_OVERRIDE) if (lynx_edit_mode && !no_dired_support) addstr(DIRED_NOVICELINE); else #endif /* DIRED_SUPPORT && OK_OVERRIDE */ if (LYUseNoviceLineTwo) addstr(NOVICE_LINE_TWO); else addstr((char *)novice_lines(lineno)); refresh(); return; } #ifdef NSL_FORK /* * Returns the file descriptor from which keyboard input is expected, * or INVSOC (-1) if not available. * If need_selectable is true, returns non-INVSOC fd only if select() * is possible - actually, currently only checks if fd is connected * to a tty. - kw */ PUBLIC int LYConsoleInputFD ARGS1( BOOLEAN, need_selectable) { int fd = INVSOC; #ifdef USE_SLANG if (!LYCursesON) fd = fileno(stdin); #if SLANG_VERSION >= 9919 /* SLang_TT_Read_FD introduced in slang 0.99.19, from its changelog: * SLang_TT_Read_FD variable is now available for unix. This is the file * descriptor used by SLang_getkey. */ else fd = SLang_TT_Read_FD; #endif /* SLANG_VERSION >= 9919 */ #else /* !USE_SLANG */ fd = fileno(stdin); #endif /* !USE_SLANG */ if (need_selectable && fd != INVSOC) { if (isatty(fd)) { return fd; } else { return INVSOC; } } return fd; } #endif /* NSL_FORK */ PRIVATE int fake_zap = 0; PUBLIC void LYFakeZap ARGS1( BOOL, set) { if (set && fake_zap < 1) { CTRACE(tfp, "\r *** Set simulated 'Z'"); if (fake_zap) CTRACE(tfp, ", %d pending", fake_zap); CTRACE(tfp, " ***\n"); fake_zap++; } else if (!set && fake_zap) { CTRACE(tfp, "\r *** Unset simulated 'Z'"); CTRACE(tfp, ", %d pending", fake_zap); CTRACE(tfp, " ***\n"); fake_zap = 0; } } PRIVATE int DontCheck NOARGS { static long last; long next; /** Curses or slang setup was not invoked **/ if (dump_output_immediately) return(TRUE); /* * Avoid checking interrupts more than one per second, since it is a slow * and expensive operation - TD */ #if HAVE_GETTIMEOFDAY { struct timeval tv; gettimeofday(&tv, (struct timezone *)0); next = tv.tv_usec / 100000L; /* 0.1 seconds is a compromise */ } #else next = time((time_t*)0); #endif if (next == last) return (TRUE); last = next; return FALSE; } PUBLIC int HTCheckForInterrupt NOARGS { int c; #ifndef VMS /* UNIX stuff: */ #ifndef USE_SLANG struct timeval socket_timeout; int ret = 0; fd_set readfds; #endif /* !USE_SLANG */ if (fake_zap > 0) { fake_zap--; CTRACE(tfp, "\r *** Got simulated 'Z' ***\n"); CTRACE_FLUSH(tfp); CTRACE_SLEEP(AlertSecs); return((int)TRUE); } /** Curses or slang setup was not invoked **/ if (DontCheck()) return((int)FALSE); #ifdef USE_SLANG /** No keystroke was entered Note that this isn't taking possible SOCKSification and the socks_flag into account, and may fail on the slang library's select() when SOCKSified. - FM **/ #ifdef DJGPP_KEYHANDLER if (0 == _bios_keybrd(_NKEYBRD_READY)) #else if (0 == SLang_input_pending(0)) #endif /* DJGPP_KEYHANDLER */ return(FALSE); #else /* Unix curses: */ socket_timeout.tv_sec = 0; socket_timeout.tv_usec = 100; FD_ZERO(&readfds); FD_SET(0, &readfds); #ifdef SOCKS if (socks_flag) ret = Rselect(FD_SETSIZE, (void *)&readfds, NULL, NULL, &socket_timeout); else #endif /* SOCKS */ ret = select(FD_SETSIZE, (void *)&readfds, NULL, NULL, &socket_timeout); /** Suspended? **/ if ((ret == -1) && (errno == EINTR)) return((int)FALSE); /** No keystroke was entered? **/ if (!FD_ISSET(0,&readfds)) return((int)FALSE); #endif /* USE_SLANG */ #if defined (DOSPATH) && defined (NCURSES) nodelay(stdscr,TRUE); #endif /* DOSPATH */ /* * 'c' contains whatever character we're able to read from keyboard */ c = LYgetch(); #if defined (DOSPATH) && defined (NCURSES) nodelay(stdscr,FALSE); #endif /* DOSPATH */ #else /* VMS: */ extern int typeahead(); if (fake_zap > 0) { fake_zap--; CTRACE(tfp, "\r *** Got simulated 'Z' ***\n"); CTRACE_FLUSH(tfp); CTRACE_SLEEP(AlertSecs); return((int)TRUE); } /** Curses or slang setup was not invoked **/ if (DontCheck()) return((int)FALSE); /** Control-C or Control-Y and a 'N'o reply to exit query **/ if (HadVMSInterrupt) { HadVMSInterrupt = FALSE; return((int)TRUE); } /* * 'c' contains whatever character we're able to read from keyboard */ c = typeahead(); #endif /* !VMS */ /* * 'c' contains whatever character we're able to read from keyboard */ /** Keyboard 'Z' or 'z', or Control-G or Control-C **/ if (TOUPPER(c) == 'Z' || c == 7 || c == 3) return((int)TRUE); /* There is a subset of mainloop() actions available at this stage: ** no new getfile() cyrcle possible until the previous finished. ** Currently we have scrolling in partial mode and toggling of trace log. */ switch (keymap[c+1]) { case LYK_TRACE_TOGGLE : /* Toggle TRACE mode. */ WWW_TraceFlag = ! WWW_TraceFlag; if (LYOpenTraceLog()) HTUserMsg(WWW_TraceFlag ? TRACE_ON : TRACE_OFF); break ; default : #ifdef DISP_PARTIAL if (display_partial && (NumOfLines_partial > 2)) /* OK, we got several lines from new document and want to scroll... */ { int res; switch (keymap[c+1]) { case LYK_FASTBACKW_LINK : if (Newline_partial <= (display_lines)+1) { Newline_partial -= display_lines ; } else if ((res = HTGetLinkOrFieldStart(-1, &Newline_partial, NULL, -1, TRUE)) == LINK_LINE_FOUND) { Newline_partial++; } else if (res == LINK_DO_ARROWUP) { Newline_partial -= display_lines ; } break; case LYK_FASTFORW_LINK : if (HText_canScrollDown()) { /* This is not an exact science... - kw */ if ((res = HTGetLinkOrFieldStart(HText_LinksInLines(HTMainText, Newline_partial, display_lines) - 1, &Newline_partial, NULL, 1, TRUE)) == LINK_LINE_FOUND) { Newline_partial++; } } break; case LYK_PREV_PAGE : if (Newline_partial > 1) Newline_partial -= display_lines ; break ; case LYK_NEXT_PAGE : if (HText_canScrollDown()) Newline_partial += display_lines ; break ; case LYK_UP_HALF : if (Newline_partial > 1) Newline_partial -= (display_lines/2) ; break ; case LYK_DOWN_HALF : if (HText_canScrollDown()) Newline_partial += (display_lines/2) ; break ; case LYK_UP_TWO : if (Newline_partial > 1) Newline_partial -= 2 ; break ; case LYK_DOWN_TWO : if (HText_canScrollDown()) Newline_partial += 2 ; break ; case LYK_HOME: if (Newline_partial > 1) Newline_partial = 1; break; case LYK_END: if (HText_canScrollDown()) Newline_partial = HText_getNumOfLines() - display_lines + 1; /* calculate for "current" bottom value */ break; case LYK_REFRESH : break ; default : /** Other or no keystrokes **/ return ((int)FALSE) ; } /* end switch */ if (Newline_partial < 1) Newline_partial = 1; NumOfLines_partial = HText_getNumOfLines(); HText_pageDisplay(Newline_partial, ""); } #endif /* DISP_PARTIAL */ } /* end switch */ /** Other or no keystrokes **/ return((int)FALSE); } /* * A file URL for a remote host is an obsolete ftp URL. * Return YES only if we're certain it's a local file. - FM */ PUBLIC BOOLEAN LYisLocalFile ARGS1( char *, filename) { char *host = NULL; char *acc_method = NULL; char *cp; if (!filename) return NO; if (!(host = HTParse(filename, "", PARSE_HOST))) return NO; if (!*host) { FREE(host); return NO; } if ((cp=strchr(host, ':')) != NULL) *cp = '\0'; if ((acc_method = HTParse(filename, "", PARSE_ACCESS))) { if (0==strcmp("file", acc_method) && (0==strcmp(host, "localhost") || #ifdef VMS 0==strcasecomp(host, HTHostName()))) #else 0==strcmp(host, HTHostName()))) #endif /* VMS */ { FREE(host); FREE(acc_method); return YES; } } FREE(host); FREE(acc_method); return NO; } /* * Utility for checking URLs with a host field. * Return YES only if we're certain it's the local host. - FM */ PUBLIC BOOLEAN LYisLocalHost ARGS1( char *, filename) { char *host = NULL; char *cp; if (!filename) return NO; if (!(host = HTParse(filename, "", PARSE_HOST))) return NO; if (!*host) { FREE(host); return NO; } if ((cp = strchr(host, ':')) != NULL) *cp = '\0'; #ifdef VMS if ((0==strcasecomp(host, "localhost") || 0==strcasecomp(host, LYHostName) || 0==strcasecomp(host, HTHostName()))) #else if ((0==strcmp(host, "localhost") || 0==strcmp(host, LYHostName) || 0==strcmp(host, HTHostName()))) #endif /* VMS */ { FREE(host); return YES; } FREE(host); return NO; } /* * Utility for freeing the list of local host aliases. - FM */ PUBLIC void LYLocalhostAliases_free NOARGS { char *alias; HTList *cur = localhost_aliases; if (!cur) return; while (NULL != (alias = (char *)HTList_nextObject(cur))) { FREE(alias); } HTList_delete(localhost_aliases); localhost_aliases = NULL; return; } /* * Utility for listing hosts to be treated as local aliases. - FM */ PUBLIC void LYAddLocalhostAlias ARGS1( char *, alias) { char *LocalAlias; if (!(alias && *alias)) return; if (!localhost_aliases) { localhost_aliases = HTList_new(); atexit(LYLocalhostAliases_free); } if ((LocalAlias = (char *)calloc(1, (strlen(alias) + 1))) == NULL) outofmem(__FILE__, "HTAddLocalhosAlias"); strcpy(LocalAlias, alias); HTList_addObject(localhost_aliases, LocalAlias); return; } /* * Utility for checking URLs with a host field. * Return YES only if we've listed the host as a local alias. - FM */ PUBLIC BOOLEAN LYisLocalAlias ARGS1( char *, filename) { char *host = NULL; char *alias; char *cp; HTList *cur = localhost_aliases; if (!cur || !filename) return NO; if (!(host = HTParse(filename, "", PARSE_HOST))) return NO; if (!(*host)) { FREE(host); return NO; } if ((cp = strchr(host, ':')) != NULL) *cp = '\0'; while (NULL != (alias = (char *)HTList_nextObject(cur))) { #ifdef VMS if (0==strcasecomp(host, alias)) #else if (0==strcmp(host, alias)) #endif /* VMS */ { FREE(host); return YES; } } FREE(host); return NO; } /* ** This function checks for a URL with an unknown scheme, ** but for which proxying has been set up, and if so, ** returns PROXY_URL_TYPE. - FM ** ** If a colon is present but the string segment which ** precedes it is not being proxied, and we can be sure ** that what follows the colon is not a port field, ** it returns UNKNOWN_URL_TYPE. Otherwise, it returns ** 0 (not a URL). - FM */ PUBLIC int LYCheckForProxyURL ARGS1( char *, filename) { char *cp = filename; char *cp1; char *cp2 = NULL; /* * Don't crash on an empty argument. */ if (cp == NULL || *cp == '\0') return(0); /* kill beginning spaces */ cp = LYSkipBlanks(cp); /* * Check for a colon, and if present, * see if we have proxying set up. */ if ((cp1 = strchr((cp+1), ':')) != NULL) { *cp1 = '\0'; StrAllocCopy(cp2, cp); *cp1 = ':'; StrAllocCat(cp2, "_proxy"); if (getenv(cp2) != NULL) { FREE(cp2); return(PROXY_URL_TYPE); } FREE(cp2); #if defined (DOSPATH) if (cp[1] == ':') return(0); /* could be drive letter? - kw */ #endif cp1++; if (!*cp) { return(0); } else if (isdigit((unsigned char)*cp1)) { while (*cp1 && isdigit((unsigned char)*cp1)) cp1++; if (*cp1 && !LYIsHtmlSep(*cp1)) return(UNKNOWN_URL_TYPE); } else { return(UNKNOWN_URL_TYPE); } } return(0); } /* * Compare a "type:" string, replacing it by the comparison-string if it * matches (and return true in that case). */ static BOOLEAN compare_type ARGS3( char *, tst, CONST char *, cmp, size_t, len) { if (!strncasecomp(tst, cmp, len)) { if (strncmp(tst, cmp, len)) { size_t i; for (i = 0; i < len; i++) tst[i] = cmp[i]; } return TRUE; } return FALSE; } /* ** Must recognize a URL and return the type. ** If recognized, based on a case-insensitive ** analysis of the scheme field, ensures that ** the scheme field has the expected case. ** ** Returns 0 (not a URL) for a NULL argument, ** one which lacks a colon. ** ** Chains to LYCheckForProxyURL() if a colon ** is present but the type is not recognized. */ PUBLIC int is_url ARGS1( char *, filename) { char *cp = filename; char *cp1; /* * Don't crash on an empty argument. */ if (cp == NULL || *cp == '\0') return(0); /* * Can't be a URL if it lacks a colon. */ if (NULL == strchr(cp, ':')) return(0); /* * Kill beginning spaces. */ cp = LYSkipBlanks(cp); /* * Can't be a URL if it starts with a slash. * So return immediately for this common case, * also to avoid false positives if there was * a colon later in the string. Also can't be * a URL if it starts with a colon. - KW */ if (*cp == ':' || LYIsHtmlSep(*cp)) return(0); if (compare_type(cp, "news:", 5)) { return(NEWS_URL_TYPE); } else if (compare_type(cp, "nntp:", 5)) { return(NNTP_URL_TYPE); } else if (compare_type(cp, "snews:", 6)) { return(SNEWS_URL_TYPE); } else if (compare_type(cp, "newspost:", 9)) { /* * Special Lynx type to handle news posts. */ return(NEWSPOST_URL_TYPE); } else if (compare_type(cp, "newsreply:", 10)) { /* * Special Lynx type to handle news replies (followups). */ return(NEWSREPLY_URL_TYPE); } else if (compare_type(cp, "snewspost:", 10)) { /* * Special Lynx type to handle snews posts. */ return(NEWSPOST_URL_TYPE); } else if (compare_type(cp, "snewsreply:", 11)) { /* * Special Lynx type to handle snews replies (followups). */ return(NEWSREPLY_URL_TYPE); } else if (compare_type(cp, "mailto:", 7)) { return(MAILTO_URL_TYPE); } else if (compare_type(cp, "file:", 5)) { if (LYisLocalFile(cp)) { return(FILE_URL_TYPE); } else if (LYIsHtmlSep(cp[5]) && LYIsHtmlSep(cp[6])) { return(FTP_URL_TYPE); } else { return(0); } } else if (compare_type(cp, "data:", 5)) { return(DATA_URL_TYPE); } else if (compare_type(cp, "lynxexec:", 9)) { /* * Special External Lynx type to handle execution * of commands or scripts which require a pause to * read the screen upon completion. */ return(LYNXEXEC_URL_TYPE); } else if (compare_type(cp, "lynxprog:", 9)) { /* * Special External Lynx type to handle execution * of commands, scripts or programs with do not * require a pause to read screen upon completion. */ return(LYNXPROG_URL_TYPE); } else if (compare_type(cp, "lynxcgi:", 8)) { /* * Special External Lynx type to handle cgi scripts. */ return(LYNXCGI_URL_TYPE); } else if (compare_type(cp, "LYNXPRINT:", 10)) { /* * Special Internal Lynx type. */ return(LYNXPRINT_URL_TYPE); } else if (compare_type(cp, "LYNXOPTIONS:", 12)) { /* * Special Internal Lynx type. */ return(LYNXOPTIONS_URL_TYPE); } else if (compare_type(cp, "LYNXCFG:", 8)) { /* * Special Internal Lynx type. */ return(LYNXCFG_URL_TYPE); } else if (compare_type(cp, "LYNXCOMPILEOPTS:", 16)) { /* * Special Internal Lynx type. */ return(LYNXCOMPILE_OPTS_URL_TYPE); } else if (compare_type(cp, "LYNXDOWNLOAD:", 13)) { /* * Special Internal Lynx type. */ return(LYNXDOWNLOAD_URL_TYPE); } else if (compare_type(cp, "LYNXDIRED:", 10)) { /* * Special Internal Lynx type. */ return(LYNXDIRED_URL_TYPE); } else if (compare_type(cp, "LYNXHIST:", 9)) { /* * Special Internal Lynx type. */ return(LYNXHIST_URL_TYPE); } else if (compare_type(cp, "LYNXKEYMAP:", 11)) { /* * Special Internal Lynx type. */ return(LYNXKEYMAP_URL_TYPE); } else if (compare_type(cp, "LYNXIMGMAP:", 11)) { /* * Special Internal Lynx type. */ (void)is_url(&cp[11]); return(LYNXIMGMAP_URL_TYPE); } else if (compare_type(cp, "LYNXCOOKIE:", 11)) { /* * Special Internal Lynx type. */ return(LYNXCOOKIE_URL_TYPE); } else if (strstr((cp+3), "://") == NULL) { /* * If it doesn't contain "://", and it's not one of the * the above, it can't be a URL with a scheme we know, * so check if it's an unknown scheme for which proxying * has been set up. - FM */ return(LYCheckForProxyURL(filename)); } else if (compare_type(cp, "http:", 5)) { return(HTTP_URL_TYPE); } else if (compare_type(cp, "https:", 6)) { return(HTTPS_URL_TYPE); } else if (compare_type(cp, "gopher:", 7)) { if ((cp1 = strchr(cp+11,'/')) != NULL) { if (TOUPPER(*(cp1+1)) == 'H' || *(cp1+1) == 'w') /* if this is a gopher html type */ return(HTML_GOPHER_URL_TYPE); else if (*(cp1+1) == 'T' || *(cp1+1) == '8') return(TELNET_GOPHER_URL_TYPE); else if (*(cp1+1) == '7') return(INDEX_GOPHER_URL_TYPE); else return(GOPHER_URL_TYPE); } else { return(GOPHER_URL_TYPE); } } else if (compare_type(cp, "ftp:", 4)) { return(FTP_URL_TYPE); } else if (compare_type(cp, "wais:", 5)) { return(WAIS_URL_TYPE); } else if (compare_type(cp, "telnet:", 7)) { return(TELNET_URL_TYPE); } else if (compare_type(cp, "tn3270:", 7)) { return(TN3270_URL_TYPE); } else if (compare_type(cp, "rlogin:", 7)) { return(RLOGIN_URL_TYPE); } else if (compare_type(cp, "cso:", 4)) { return(CSO_URL_TYPE); } else if (compare_type(cp, "finger:", 7)) { return(FINGER_URL_TYPE); } else if (compare_type(cp, "afs:", 4)) { return(AFS_URL_TYPE); } else if (compare_type(cp, "prospero:", 9)) { return(PROSPERO_URL_TYPE); } else { /* * Check if it's an unknown scheme for which * proxying has been set up. - FM */ return(LYCheckForProxyURL(filename)); } } /* * Determine whether we allow HEAD and related flags for a URL. - kw */ PUBLIC BOOLEAN LYCanDoHEAD ARGS1( CONST char *, address ) { char *temp0 = NULL; int isurl; if (!(address && *address)) return FALSE; if (!strncmp(address, "http", 4)) return TRUE; /* Make copy for is_url() since caller may not care for case changes */ StrAllocCopy(temp0, address); isurl = is_url(temp0); FREE(temp0); if (!isurl) return FALSE; if (isurl == LYNXCGI_URL_TYPE) { #if defined(LYNXCGI_LINKS) && !defined(VMS) return TRUE; #else return FALSE; #endif } if (isurl == NEWS_URL_TYPE || isurl == NNTP_URL_TYPE) { char *temp = HTParse(address, "", PARSE_PATH); char *cp = strrchr(temp, '/'); if (strchr((cp ? cp : temp), '@') != NULL) { FREE(temp); return TRUE; } if (cp && isdigit(cp[1]) && strchr(cp, '-') == NULL) { FREE(temp); return TRUE; } FREE(temp); } return FALSE; } /* * Remove backslashes from any string. */ PUBLIC void remove_backslashes ARGS1( char *, buf) { char *cp; for (cp = buf; *cp != '\0' ; cp++) { if (*cp != '\\') { /* don't print slashes */ *buf = *cp; buf++; } else if (*cp == '\\' && /* print one slash if there */ *(cp+1) == '\\') { /* are two in a row */ *buf = *cp; buf++; } } *buf = '\0'; return; } #if HAVE_UTMP extern char *ttyname PARAMS((int fd)); #endif /* * Checks to see if the current process is attached * via a terminal in the local domain. * */ PUBLIC BOOLEAN inlocaldomain NOARGS { #if ! HAVE_UTMP return(TRUE); #else int n; FILE *fp; struct utmp me; char *cp, *mytty = NULL; if ((cp=ttyname(0))) mytty = strrchr(cp, '/'); if (mytty && (fp=fopen(UTMP_FILE, "r")) != NULL) { mytty++; do { n = fread((char *) &me, sizeof(struct utmp), 1, fp); } while (n>0 && !STREQ(me.ut_line,mytty)); (void) fclose(fp); if (n > 0 && strlen(me.ut_host) > strlen(LYLocalDomain) && STREQ(LYLocalDomain, me.ut_host+strlen(me.ut_host)-strlen(LYLocalDomain)) ) return(TRUE); #ifdef LINUX /* Linux fix to check for local user. J.Cullen 11Jul94 */ if ((n > 0) && (strlen(me.ut_host) == 0)) return(TRUE); #endif /* LINUX */ } else { CTRACE(tfp,"Could not get ttyname or open UTMP file %s\n", UTMP_FILE); } return(FALSE); #endif /* !HAVE_UTMP */ } /************** ** This bit of code catches window size change signals **/ #ifdef HAVE_SYS_IOCTL_H #include #endif /* For systems that have both, but both can't be included, duh (or neither) */ /* FIXME: this whole chunk may be redundant */ #ifdef TERMIO_AND_CURSES # ifdef TERMIO_AND_TERMIOS # include # else # ifdef HAVE_TERMIOS_H # include # else # ifdef HAVE_TERMIO_H # include # endif /* HAVE_TERMIO_H */ # endif /* HAVE_TERMIOS_H */ # endif /* TERMIO_AND_TERMIOS */ #endif /* TERMIO_AND_CURSES */ PUBLIC void size_change ARGS1( int, sig GCC_UNUSED) { int old_lines = LYlines; int old_cols = LYcols; #ifdef USE_SLANG #if defined(VMS) || defined(UNIX) SLtt_get_screen_size(); #endif /* VMS || UNIX */ LYlines = SLtt_Screen_Rows; LYcols = SLtt_Screen_Cols; #ifdef SLANG_MBCS_HACK PHYSICAL_SLtt_Screen_Cols = LYcols; SLtt_Screen_Cols = (LYcols * 6); #endif /* SLANG_MBCS_HACK */ if (sig == 0) /* * Called from start_curses(). */ return; #else /* Curses: */ #if HAVE_SIZECHANGE #ifdef TIOCGSIZE struct ttysize win; #else #ifdef TIOCGWINSZ struct winsize win; #endif /* TIOCGWINSZ */ #endif /* TIOCGSIZE */ #ifdef TIOCGSIZE if (ioctl(0, TIOCGSIZE, &win) == 0) { if (win.ts_lines != 0) { LYlines = win.ts_lines; } if (win.ts_cols != 0) { LYcols = win.ts_cols; } } #else #ifdef TIOCGWINSZ if (ioctl(0, TIOCGWINSZ, &win) == 0) { if (win.ws_row != 0) { LYlines = win.ws_row; } if (win.ws_col != 0) { LYcols = win.ws_col; } } #endif /* TIOCGWINSZ */ #endif /* TIOCGSIZE */ #endif /* HAVE_SIZECHANGE */ if (LYlines <= 0) LYlines = 24; if (LYcols <= 0) LYcols = 80; #endif /* USE_SLANG */ /* * Check if the screen size has actually changed. - AJL */ if (LYlines != old_lines || LYcols != old_cols) { recent_sizechange = TRUE; CTRACE(tfp, "Window size changed from (%d,%d) to (%d,%d)\n", old_lines, old_cols, LYlines, LYcols); } #ifdef SIGWINCH (void)signal (SIGWINCH, size_change); #endif /* SIGWINCH */ return; } /* * Utility for freeing the list of previous suggested filenames. - FM */ PUBLIC void HTSugFilenames_free NOARGS { char *fname; HTList *cur = sug_filenames; if (!cur) return; while (NULL != (fname = (char *)HTList_nextObject(cur))) { FREE(fname); } HTList_delete(sug_filenames); sug_filenames = NULL; return; } /* * Utility for listing suggested filenames, making any * repeated filenames the most current in the list. - FM */ PUBLIC void HTAddSugFilename ARGS1( char *, fname) { char *new; char *old; HTList *cur; if (!(fname && *fname)) return; if ((new = (char *)calloc(1, (strlen(fname) + 1))) == NULL) outofmem(__FILE__, "HTAddSugFilename"); strcpy(new, fname); if (!sug_filenames) { sug_filenames = HTList_new(); atexit(HTSugFilenames_free); HTList_addObject(sug_filenames, new); return; } cur = sug_filenames; while (NULL != (old = (char *)HTList_nextObject(cur))) { if (!strcmp(old, new)) { HTList_removeObject(sug_filenames, old); FREE(old); break; } } HTList_addObject(sug_filenames, new); return; } /* * CHANGE_SUG_FILENAME -- Foteos Macrides 29-Dec-1993 * Upgraded for use with Lynx2.2 - FM 17-Jan-1994 */ PUBLIC void change_sug_filename ARGS1( char *, fname) { char *temp, *cp, *cp1, *end; #ifdef VMS char *dot; int j, k; #endif /* VMS */ /* * Establish the current end of fname. */ end = fname + strlen(fname); /* * Unescape fname. */ HTUnEscape(fname); /* * Rename any temporary files. */ temp = (char *)calloc(1, (strlen(lynx_temp_space) + 60)); if (temp == NULL) outofmem(__FILE__, "change_sug_filename"); cp = wwwName(lynx_temp_space); if (LYIsHtmlSep(*cp)) { sprintf(temp, "file://localhost%s%d", cp, (int)getpid()); } else { sprintf(temp, "file://localhost/%s%d", cp, (int)getpid()); } if (!strncmp(fname, temp, strlen(temp))) { cp = strrchr(fname, '.'); if (strlen(cp) > (strlen(temp) - 4)) cp = NULL; strcpy(temp, (cp ? cp : "")); strcpy(fname, "temp"); strcat(fname, temp); } FREE(temp); if (fname[strlen(fname) - 1] == '/') /* * Hmm... we have a directory name. * It is annoying to see a scheme+host+path name as a suggested one, * let's remove the last_slash and go ahead like we have a file name. - LP */ fname[strlen(fname) - 1] = '\0'; /* * Remove everything up the the last_slash if there is one. */ if ((cp = strrchr(fname,'/')) != NULL && strlen(cp) > 1) { cp1 = fname; /* * Go past the slash. */ cp++; for (; *cp != '\0'; cp++, cp1++) { *cp1 = *cp; } *cp1 = '\0'; } /* * Trim off date-size suffix, if present. */ if ((*(end - 1) == ']') && ((cp = strrchr(fname, '[')) != NULL) && (cp > fname) && *(--cp) == ' ') { while (*cp == ' ') { *(cp--) = '\0'; } } /* * Trim off VMS device and/or directory specs, if present. */ if ((cp = strchr(fname,'[')) != NULL && (cp1 = strrchr(cp,']')) != NULL && strlen(cp1) > 1) { cp1++; for (cp=fname; *cp1 != '\0'; cp1++) { *(cp++) = *cp1; } *cp = '\0'; } #ifdef VMS /* * Replace illegal or problem characters. */ dot = fname + strlen(fname); for (cp = fname; cp < dot; cp++) { /* * Replace with underscores. */ if (*cp == ' ' || *cp == '/' || *cp == ':' || *cp == '[' || *cp == ']' || *cp == '&') { *cp = '_'; /* * Replace with dashes. */ } else if (*cp == '!' || *cp == '?' || *cp == '\'' || *cp == ',' || *cp == ':' || *cp == '\"' || *cp == '+' || *cp == '@' || *cp == '\\' || *cp == '(' || *cp == ')' || *cp == '=' || *cp == '<' || *cp == '>' || *cp == '#' || *cp == '%' || *cp == '*' || *cp == '`' || *cp == '~' || *cp == '^' || *cp == '|' || *cp < ' ' || ((unsigned char)*cp) > 126) { *cp = '-'; } } /* * Collapse any serial underscores. */ cp = fname + 1; j = 0; while (cp < dot) { if (fname[j] == '_' && *cp == '_') { cp++; } else { fname[++j] = *cp++; } } fname[++j] = '\0'; /* * Collapse any serial dashes. */ dot = fname + (strlen(fname)); cp = fname + 1; j = 0; while (cp < dot) { if (fname[j] == '-' && *cp == '-') { cp++; } else { fname[++j] = *cp++; } } fname[++j] = '\0'; /* * Trim any trailing or leading * underscores or dashes. */ cp = fname + (strlen(fname)) - 1; while (*cp == '_' || *cp == '-') { *cp-- = '\0'; } if (fname[0] == '_' || fname[0] == '-') { dot = fname + (strlen(fname)); cp = fname; while ((*cp == '_' || *cp == '-') && cp < dot) { cp++; } j = 0; while (cp < dot) { fname[j++] = *cp++; } fname[j] = '\0'; } /* * Replace all but the last period with _'s, or second * to last if last is followed by a terminal Z or z, * or GZ or gz, * e.g., convert foo.tar.Z to * foo.tar_Z * or, convert foo.tar.gz to * foo.tar-gz */ j = strlen(fname) - 1; if ((dot = strrchr(fname, '.')) != NULL) { if (TOUPPER(fname[j]) == 'Z') { if ((fname[j-1] == '.') && (((cp = strchr(fname, '.')) != NULL) && cp < dot)) { *dot = '_'; dot = strrchr(fname, '.'); } else if (((TOUPPER(fname[j-1]) == 'G') && fname[j-2] == '.') && (((cp = strchr(fname, '.')) != NULL) && cp < dot)) { *dot = '-'; dot = strrchr(fname, '.'); } } cp = fname; while ((cp = strchr(cp, '.')) != NULL && cp < dot) { *cp = '_'; } /* * But if the root is > 39 characters, move * the period appropriately to the left. */ while (dot - fname > 39) { *dot = '\0'; if ((cp = strrchr(fname, '_')) != NULL) { *cp = '.'; *dot = '_'; } else if ((cp = strrchr(fname, '-')) != NULL) { *cp = '.'; *dot = '_'; } else if (*(dot + 1) == '\0') { j = strlen(fname); while (j > 39) { fname[j] = fname[j-1]; j--; } fname[j] = '.'; } else { *dot = '.'; j = 39; k = 0; while (dot[k] != '\0') { fname[j++] = dot[k++]; } fname[j] = '\0'; } dot = strrchr(fname, '.'); } /* * Make sure the extension is < 40 characters. */ if ((fname + strlen(fname) - dot) > 39) { *(dot + 40) = '\0'; } /* * Trim trailing dashes or underscores. */ j = (strlen(fname) - 1); while (fname[j] == '_' || fname[j] == '-') { fname[j--] = '\0'; } } else { /* * No period, so put one on the end, or after * the 39th character, trimming trailing dashes * or underscores. */ if (strlen(fname) > 39) { fname[39] = '\0'; } j = (strlen(fname) - 1); while ((fname[j] == '_') || (fname[j] == '-')) { j--; } fname[++j] = '.'; fname[++j] = '\0'; } #else /* Not VMS (UNIX): */ /* * Replace problem characters. */ for (cp = fname; *cp != '\0'; cp++) { switch (*cp) { case '\'': case '\"': case '/': case ' ': *cp = '-'; } } #endif /* VMS (UNIX) */ /* * Make sure the rest of the original string in nulled. */ cp = fname + strlen(fname); while (cp < end) { *cp++ = '\0'; } return; } /* * Construct a temporary-filename. Assumes result is LY_MAXPATH chars long. */ PRIVATE int fmt_tempname ARGS3( char *, result, CONST char *, prefix, CONST char *, suffix) { static unsigned counter; char leaf[LY_MAXPATH]; int code; if (prefix == 0) prefix = ""; if (suffix == 0) suffix = ""; counter++; #ifdef FNAMES_8_3 /* * The 'lynx_temp_space' string ends with a '/' or '\\', so we only have to * limit the length of the leaf. As received (e.g., from HTCompressed), * the suffix may contain more than a ".htm", e.g., "-txt.gz", so we trim * off from the filename portion to make room. */ sprintf(leaf, "%u%u", counter, (unsigned)getpid()); if (strlen(leaf) > 8) leaf[8] = 0; if (strlen(suffix) > 4 || *suffix != '.') { CONST char *tail = strchr(suffix, '.'); if (tail == 0) tail = suffix + strlen(suffix); leaf[8 - (tail - suffix)] = 0; } strcat(leaf, suffix); #else sprintf(leaf, "L%u-%uTMP%s", (unsigned)getpid(), counter, suffix); #endif /* * Someone could have configured the temporary pathname to be too long. */ if ((strlen(prefix) + strlen(leaf)) < LY_MAXPATH) { sprintf(result, "%s%s", prefix, leaf); code = TRUE; } else { sprintf(result, "%.*s", LY_MAXPATH-1, leaf); code = FALSE; } CTRACE(tfp, "-> '%s'\n", result); return (code); } /* * Convert 4, 6, 2, 8 to left, right, down, up, etc. */ PUBLIC int number2arrows ARGS1( int, number) { switch(number) { case '1': number=END_KEY; break; case '2': number=DNARROW; break; case '3': number=PGDOWN; break; case '4': number=LTARROW; break; case '5': number=DO_NOTHING; break; case '6': number=RTARROW; break; case '7': number=HOME; break; case '8': number=UPARROW; break; case '9': number=PGUP; break; } return(number); } /* * parse_restrictions takes a string of comma-separated restrictions * and sets the corresponding flags to restrict the facilities available. */ PRIVATE CONST char *restrict_name[] = { "inside_telnet" , "outside_telnet", "telnet_port" , "inside_ftp" , "outside_ftp" , "inside_rlogin" , "outside_rlogin", "suspend" , "editor" , "shell" , "bookmark" , "multibook" , "bookmark_exec" , "option_save" , "print" , "download" , "disk_save" , "exec" , "lynxcgi" , "exec_frozen" , "goto" , "jump" , "file_url" , #ifndef DISABLE_NEWS "news_post" , "inside_news" , "outside_news" , #endif "mail" , "dotfiles" , "useragent" , #ifdef DIRED_SUPPORT "dired_support" , #ifdef OK_PERMIT "change_exec_perms", #endif /* OK_PERMIT */ #endif /* DIRED_SUPPORT */ #ifdef USE_EXTERNALS "externals" , #endif (char *) 0 }; /* restrict_name and restrict_flag structure order * must be maintained exactly! */ PRIVATE BOOLEAN *restrict_flag[] = { &no_inside_telnet, &no_outside_telnet, &no_telnet_port, &no_inside_ftp, &no_outside_ftp, &no_inside_rlogin, &no_outside_rlogin, &no_suspend , &no_editor , &no_shell , &no_bookmark , &no_multibook , &no_bookmark_exec, &no_option_save, &no_print , &no_download , &no_disk_save, &no_exec , &no_lynxcgi , &exec_frozen , &no_goto , &no_jump , &no_file_url , #ifndef DISABLE_NEWS &no_newspost , &no_inside_news, &no_outside_news, #endif &no_mail , &no_dotfiles , &no_useragent , #ifdef DIRED_SUPPORT &no_dired_support, #ifdef OK_PERMIT &no_change_exec_perms, #endif /* OK_PERMIT */ #endif /* DIRED_SUPPORT */ #ifdef USE_EXTERNALS &no_externals , #endif (BOOLEAN *) 0 }; PUBLIC void parse_restrictions ARGS1( CONST char *, s) { CONST char *p; CONST char *word; int i; if (STREQ("all", s)) { /* set all restrictions */ for (i=0; restrict_flag[i]; i++) *restrict_flag[i] = TRUE; return; } if (STREQ("default", s)) { /* set all restrictions */ for (i=0; restrict_flag[i]; i++) *restrict_flag[i] = TRUE; /* reset these to defaults */ no_inside_telnet = !(CAN_ANONYMOUS_INSIDE_DOMAIN_TELNET); no_outside_telnet = !(CAN_ANONYMOUS_OUTSIDE_DOMAIN_TELNET); #ifndef DISABLE_NEWS no_inside_news = !(CAN_ANONYMOUS_INSIDE_DOMAIN_READ_NEWS); no_outside_news = !(CAN_ANONYMOUS_OUTSIDE_DOMAIN_READ_NEWS); #endif no_inside_ftp = !(CAN_ANONYMOUS_INSIDE_DOMAIN_FTP); no_outside_ftp = !(CAN_ANONYMOUS_OUTSIDE_DOMAIN_FTP); no_inside_rlogin = !(CAN_ANONYMOUS_INSIDE_DOMAIN_RLOGIN); no_outside_rlogin = !(CAN_ANONYMOUS_OUTSIDE_DOMAIN_RLOGIN); no_goto = !(CAN_ANONYMOUS_GOTO); no_goto_cso = !(CAN_ANONYMOUS_GOTO_CSO); no_goto_file = !(CAN_ANONYMOUS_GOTO_FILE); #ifndef DISABLE_FINGER no_goto_finger = !(CAN_ANONYMOUS_GOTO_FINGER); #endif no_goto_ftp = !(CAN_ANONYMOUS_GOTO_FTP); #ifndef DISABLE_GOPHER no_goto_gopher = !(CAN_ANONYMOUS_GOTO_GOPHER); #endif no_goto_http = !(CAN_ANONYMOUS_GOTO_HTTP); no_goto_https = !(CAN_ANONYMOUS_GOTO_HTTPS); no_goto_lynxcgi = !(CAN_ANONYMOUS_GOTO_LYNXCGI); no_goto_lynxexec = !(CAN_ANONYMOUS_GOTO_LYNXEXEC); no_goto_lynxprog = !(CAN_ANONYMOUS_GOTO_LYNXPROG); no_goto_mailto = !(CAN_ANONYMOUS_GOTO_MAILTO); #ifndef DISABLE_NEWS no_goto_news = !(CAN_ANONYMOUS_GOTO_NEWS); no_goto_nntp = !(CAN_ANONYMOUS_GOTO_NNTP); #endif no_goto_rlogin = !(CAN_ANONYMOUS_GOTO_RLOGIN); #ifndef DISABLE_NEWS no_goto_snews = !(CAN_ANONYMOUS_GOTO_SNEWS); #endif no_goto_telnet = !(CAN_ANONYMOUS_GOTO_TELNET); no_goto_tn3270 = !(CAN_ANONYMOUS_GOTO_TN3270); no_goto_wais = !(CAN_ANONYMOUS_GOTO_WAIS); no_telnet_port = !(CAN_ANONYMOUS_GOTO_TELNET_PORT); no_jump = !(CAN_ANONYMOUS_JUMP); no_mail = !(CAN_ANONYMOUS_MAIL); no_print = !(CAN_ANONYMOUS_PRINT); #if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS) no_exec = LOCAL_EXECUTION_LINKS_ALWAYS_OFF_FOR_ANONYMOUS; #endif /* EXEC_LINKS || EXEC_SCRIPTS */ return; } p = s; while (*p) { p = LYSkipCBlanks(p); if (*p == '\0') break; word = p; while (*p != ',' && *p != '\0') p++; for (i=0; restrict_name[i]; i++) { if (STRNEQ(word, restrict_name[i], p-word)) { *restrict_flag[i] = TRUE; break; } } if (*p) p++; } return; } #ifdef VMS #include #include #include typedef struct _VMSMailItemList { short buffer_length; short item_code; void *buffer_address; long *return_length_address; } VMSMailItemList; PUBLIC void LYCheckMail NOARGS { static BOOL firsttime = TRUE, failure = FALSE; static char user[13], dir[252]; static long userlen = 0, dirlen; static time_t lastcheck = 0; time_t now; static short new, lastcount; long ucontext = 0, status; short flags = MAIL$M_NEWMSG; VMSMailItemList null_list[] = {{0,0,0,0}}, jpi_list[] = {{sizeof(user) - 1,JPI$_USERNAME,(void *)user,&userlen}, {0,0,0,0}}, uilist[] = {{0,MAIL$_USER_USERNAME,0,0}, {0,0,0,0}}, uolist[] = {{sizeof(new),MAIL$_USER_NEW_MESSAGES,&new,0}, {sizeof(dir),MAIL$_USER_FULL_DIRECTORY,dir,&dirlen}, {0,0,0,0}}; extern long mail$user_begin(); extern long mail$user_get_info(); extern long mail$user_end(); if (failure) return; if (firsttime) { firsttime = FALSE; /* Get the username. */ status = sys$getjpiw(0,0,0,jpi_list,0,0,0); if (!(status & 1)) { failure = TRUE; return; } user[userlen] = '\0'; LYTrimTrailing(user); } /* Minimum report interval is 60 sec. */ time(&now); if (now - lastcheck < 60) return; lastcheck = now; /* Get the current newmail count. */ status = mail$user_begin(&ucontext,null_list,null_list); if (!(status & 1)) { failure = TRUE; return; } uilist[0].buffer_length = strlen(user); uilist[0].buffer_address = user; status = mail$user_get_info(&ucontext,uilist,uolist); if (!(status & 1)) { failure = TRUE; return; } /* Should we report anything to the user? */ if (new > 0) { if (lastcount == 0) /* Have newmail at startup of Lynx. */ HTUserMsg(HAVE_UNREAD_MAIL_MSG); else if (new > lastcount) /* Have additional mail since last report. */ HTUserMsg(HAVE_NEW_MAIL_MSG); lastcount = new; return; } lastcount = new; /* Clear the context */ mail$user_end((long *)&ucontext,null_list,null_list); return; } #else PUBLIC void LYCheckMail NOARGS { static BOOL firsttime = TRUE; static char *mf; static time_t lastcheck; static time_t lasttime; static long lastsize; time_t now; struct stat st; if (firsttime) { mf = getenv("MAIL"); firsttime = FALSE; time(&lasttime); } if (mf == NULL) return; time(&now); if (now - lastcheck < 60) return; lastcheck = now; if ((stat(mf,&st) < 0) || !S_ISREG(st.st_mode)) { mf = NULL; return; } if (st.st_size > 0) { if (((lasttime != st.st_mtime) && (st.st_mtime > st.st_atime)) || ((lastsize != 0) && (st.st_size > lastsize))) HTUserMsg(HAVE_NEW_MAIL_MSG); else if (lastsize == 0) HTUserMsg(HAVE_MAIL_MSG); } lastsize = st.st_size; lasttime = st.st_mtime; return; } #endif /* VMS */ /* ** This function ensures that an href will be ** converted to a fully resolved, absolute URL, ** with guessing of the host or expansions of ** lead tildes via LYConvertToURL() if needed, ** and tweaking/simplifying via HTParse(). It ** is used for LynxHome, startfile, homepage, ** an 'g'oto entries, after they have been ** passed to LYFillLocalFileURL(). - FM */ PUBLIC void LYEnsureAbsoluteURL ARGS2( char **, href, CONST char *, name) { char *temp = NULL; if (!(*href && *(*href))) return; /* * If it is not a URL then make it one. */ if (!strcasecomp(*href, "news:")) { StrAllocCat(*href, "*"); } else if (!strcasecomp(*href, "nntp:") || !strcasecomp(*href, "snews:")) { StrAllocCat(*href, "/*"); } if (!is_url(*href)) { CTRACE(tfp, "%s%s'%s' is not a URL\n", (name ? name : ""), (name ? " " : ""), *href); LYConvertToURL(href); } if ((temp = HTParse(*href, "", PARSE_ALL)) != NULL && *temp != '\0') StrAllocCopy(*href, temp); FREE(temp); } /* * Rewrite and reallocate a previously allocated string * as a file URL if the string resolves to a file or * directory on the local system, otherwise as an * http URL. - FM */ PUBLIC void LYConvertToURL ARGS1( char **, AllocatedString) { char *old_string = *AllocatedString; char *temp = NULL; char *cp = NULL; #ifndef VMS struct stat st; FILE *fptemp = NULL; #endif /* !VMS */ if (!old_string || *old_string == '\0') return; #if defined(DOSPATH) || defined(__EMX__) { char *cp_url = *AllocatedString; for(; *cp_url != '\0'; cp_url++) if(*cp_url == '\\') *cp_url = '/'; cp_url--; if(*cp_url == ':') StrAllocCat(*AllocatedString,"/"); } #endif /* DOSPATH */ *AllocatedString = NULL; /* so StrAllocCopy doesn't free it */ StrAllocCopy(*AllocatedString,"file://localhost"); if (*old_string != '/') { char *fragment = NULL; #if defined(DOSPATH) || defined(__EMX__) StrAllocCat(*AllocatedString,"/"); #endif /* DOSPATH */ #ifdef VMS /* * Not a SHELL pathspec. Get the full VMS spec and convert it. */ char *cur_dir = NULL; static char url_file[256], file_name[256], dir_name[256]; unsigned long context = 0; $DESCRIPTOR(url_file_dsc, url_file); $DESCRIPTOR(file_name_dsc, file_name); if (*old_string == '~') { /* * On VMS, we'll accept '~' on the command line as * Home_Dir(), and assume the rest of the path, if * any, has SHELL syntax. */ StrAllocCat(*AllocatedString, HTVMS_wwwName((char *)Home_Dir())); if ((cp = strchr(old_string, '/')) != NULL) { /* * Append rest of path, if present, skipping "user" if * "~user" was entered, simplifying, and eliminating * any residual relative elements. - FM */ StrAllocCopy(temp, cp); LYTrimRelFromAbsPath(temp); StrAllocCat(*AllocatedString, temp); FREE(temp); } goto have_VMS_URL; } else { if ((fragment = strchr(old_string, '#')) != NULL) *fragment = '\0'; strcpy(url_file, old_string); } url_file_dsc.dsc$w_length = (short) strlen(url_file); if (1&lib$find_file(&url_file_dsc, &file_name_dsc, &context, 0, 0, 0, 0)) { /* * We found the file. Convert to a URL pathspec. */ if ((cp = strchr(file_name, ';')) != NULL) { *cp = '\0'; } LYLowerCase(file_name); StrAllocCat(*AllocatedString, HTVMS_wwwName(file_name)); if ((cp = strchr(old_string, ';')) != NULL) { StrAllocCat(*AllocatedString, cp); } if (fragment != NULL) { *fragment = '#'; StrAllocCat(*AllocatedString, fragment); fragment = NULL; } } else if ((NULL != getcwd(dir_name, sizeof(dir_name)-1, 0)) && 0 == chdir(old_string)) { /* * Probably a directory. Try converting that. */ StrAllocCopy(cur_dir, dir_name); if (fragment != NULL) { *fragment = '#'; } if (NULL != getcwd(dir_name, sizeof(dir_name)-1, 0)) { /* * Yup, we got it! */ LYLowerCase(dir_name); StrAllocCat(*AllocatedString, dir_name); if (fragment != NULL) { StrAllocCat(*AllocatedString, fragment); fragment = NULL; } } else { /* * Nope. Assume it's an http URL with * the "http://" defaulted, if we can't * rule out a bad VMS path. */ fragment = NULL; if (strchr(old_string, '[') || ((cp = strchr(old_string, ':')) != NULL && !isdigit((unsigned char)cp[1])) || !LYExpandHostForURL((char **)&old_string, URLDomainPrefixes, URLDomainSuffixes)) { /* * Probably a bad VMS path (but can't be * sure). Use original pathspec for the * error message that will result. */ strcpy(url_file, "/"); strcat(url_file, old_string); CTRACE(tfp, "Can't find '%s' Will assume it's a bad path.\n", old_string); StrAllocCat(*AllocatedString, url_file); } else { /* * Assume a URL is wanted, so guess the * scheme with "http://" as the default. - FM */ if (!LYAddSchemeForURL((char **)&old_string, "http://")) { StrAllocCopy(*AllocatedString, "http://"); StrAllocCat(*AllocatedString, old_string); } else { StrAllocCopy(*AllocatedString, old_string); } } } } else { /* * Nothing found. Assume it's an http URL * with the "http://" defaulted, if we can't * rule out a bad VMS path. */ if (fragment != NULL) { *fragment = '#'; fragment = NULL; } if (strchr(old_string, '[') || ((cp = strchr(old_string, ':')) != NULL && !isdigit((unsigned char)cp[1])) || !LYExpandHostForURL((char **)&old_string, URLDomainPrefixes, URLDomainSuffixes)) { /* * Probably a bad VMS path (but can't be * sure). Use original pathspec for the * error message that will result. */ strcpy(url_file, "/"); strcat(url_file, old_string); CTRACE(tfp, "Can't find '%s' Will assume it's a bad path.\n", old_string); StrAllocCat(*AllocatedString, url_file); } else { /* * Assume a URL is wanted, so guess the * scheme with "http://" as the default. - FM */ if (!LYAddSchemeForURL((char **)&old_string, "http://")) { StrAllocCopy(*AllocatedString, "http://"); StrAllocCat(*AllocatedString, old_string); } else { StrAllocCopy(*AllocatedString, old_string); } } } lib$find_file_end(&context); FREE(cur_dir); have_VMS_URL: CTRACE(tfp, "Trying: '%s'\n", *AllocatedString); #else /* Unix: */ #ifdef DOSPATH if (strlen(old_string) == 1 && *old_string == '.') { /* * They want . */ char curdir[LY_MAXPATH]; getcwd (curdir, sizeof(curdir)); StrAllocCopy(temp, wwwName(curdir)); StrAllocCat(*AllocatedString, temp); FREE(temp); CTRACE(tfp, "Converted '%s' to '%s'\n", old_string, *AllocatedString); } else #endif /* DOSPATH */ if (*old_string == '~') { /* * On Unix, convert '~' to Home_Dir(). */ StrAllocCat(*AllocatedString, Home_Dir()); if ((cp = strchr(old_string, '/')) != NULL) { /* * Append rest of path, if present, skipping "user" if * "~user" was entered, simplifying, and eliminating * any residual relative elements. - FM */ StrAllocCopy(temp, cp); LYTrimRelFromAbsPath(temp); StrAllocCat(*AllocatedString, temp); FREE(temp); } CTRACE(tfp, "Converted '%s' to '%s'\n", old_string, *AllocatedString); } else { /* * Create a full path to the current default directory. */ char curdir[LY_MAXPATH]; char *temp2 = NULL; BOOL is_local = FALSE; #if HAVE_GETCWD getcwd (curdir, sizeof(curdir)); #else getwd (curdir); #endif /* NO_GETCWD */ /* * Concatenate and simplify, trimming any * residual relative elements. - FM */ #if defined (DOSPATH) || defined (__EMX__) if (old_string[1] != ':' && old_string[1] != '|') { StrAllocCopy(temp, wwwName(curdir)); LYAddHtmlSep(&temp); LYstrncpy(curdir, temp, (sizeof(curdir) - 1)); StrAllocCat(temp, old_string); } else { curdir[0] = '\0'; StrAllocCopy(temp, old_string); } #else StrAllocCopy(temp, curdir); StrAllocCat(temp, "/"); StrAllocCat(temp, old_string); #endif /* DOSPATH */ LYTrimRelFromAbsPath(temp); CTRACE(tfp, "Converted '%s' to '%s'\n", old_string, temp); if ((stat(temp, &st) > -1) || (fptemp = fopen(temp, "r")) != NULL) { /* * It is a subdirectory or file on the local system. */ #if defined (DOSPATH) || defined (__EMX__) /* Don't want to see DOS local paths like c: escaped */ /* especially when we really have file://localhost/ */ /* at the beginning. To avoid any confusion we allow */ /* escaping the path if URL specials % or # present. */ if (strchr(temp, '#') == NULL && strchr(temp, '%') == NULL) StrAllocCopy(cp, temp); else #endif /* DOSPATH */ cp = HTEscape(temp, URL_PATH); StrAllocCat(*AllocatedString, cp); FREE(cp); CTRACE(tfp, "Converted '%s' to '%s'\n", old_string, *AllocatedString); is_local = TRUE; } else { char *cp2 = NULL; StrAllocCopy(temp2, curdir); LYAddPathSep(&temp2); StrAllocCopy(cp, old_string); if ((fragment = strchr(cp, '#')) != NULL) *fragment = '\0'; /* keep as pointer into cp string */ HTUnEscape(cp); /* unescape given path without fragment */ StrAllocCat(temp2, cp); /* append to current dir */ StrAllocCopy(cp2, temp2); /* keep a copy in cp2 */ LYTrimRelFromAbsPath(temp2); if (strcmp(temp2, temp) != 0 && ((stat(temp2, &st) > -1) || (fptemp = fopen(temp2, "r")) != NULL)) { /* * It is a subdirectory or file on the local system * with escaped characters and/or a fragment to be * appended to the URL. - FM */ FREE(temp); if (strcmp(cp2, temp2) == 0) { /* * LYTrimRelFromAbsPath did nothing, use * old_string as given. - kw */ temp = HTEscape(curdir, URL_PATH); LYAddHtmlSep(&temp); StrAllocCat(temp, old_string); } else { temp = HTEscape(temp2, URL_PATH); if (fragment != NULL) { *fragment = '#'; StrAllocCat(temp, fragment); } } StrAllocCat(*AllocatedString, temp); CTRACE(tfp, "Converted '%s' to '%s'\n", old_string, *AllocatedString); is_local = TRUE; } else if (strchr(curdir, '#') != NULL || strchr(curdir, '%') != NULL) { /* * If PWD has some unusual characters, construct a * filename in temp where those are escaped. This * is mostly to prevent this function from returning * with some weird URL if the LYExpandHostForURL tests * further down fail. - kw */ FREE(temp); if (strcmp(cp2, temp2) == 0) { /* * LYTrimRelFromAbsPath did nothing, use * old_string as given. - kw */ temp = HTEscape(curdir, URL_PATH); LYAddHtmlSep(&temp); StrAllocCat(temp, old_string); } else { temp = HTEscape(temp2, URL_PATH); if (fragment != NULL) { *fragment = '#'; StrAllocCat(temp, fragment); } } } FREE(cp); FREE(cp2); } if (is_local == FALSE) { /* * It's not an accessible subdirectory or file on the * local system, so assume it's a URL request and guess * the scheme with "http://" as the default. */ CTRACE(tfp, "Can't stat() or fopen() '%s'\n", temp2 ? temp2 : temp); if (LYExpandHostForURL((char **)&old_string, URLDomainPrefixes, URLDomainSuffixes)) { if (!LYAddSchemeForURL((char **)&old_string, "http://")) { StrAllocCopy(*AllocatedString, "http://"); StrAllocCat(*AllocatedString, old_string); } else { StrAllocCopy(*AllocatedString, old_string); } } else { /* RW 1998Mar16 Restore AllocatedString to 'old_string' */ StrAllocCopy(*AllocatedString, old_string); } CTRACE(tfp, "Trying: '%s'\n", *AllocatedString); } FREE(temp); FREE(temp2); if (fptemp) { fclose(fptemp); fptemp = NULL; } } #endif /* VMS */ } else { /* * Path begins with a slash. Simplify and use it. */ if (old_string[1] == '\0') { /* * Request for root. Respect it on Unix, but * on VMS we treat that as a listing of the * login directory. - FM */ #ifdef VMS StrAllocCat(*AllocatedString, HTVMS_wwwName((char *)Home_Dir())); #else StrAllocCat(*AllocatedString, "/"); } else if ((stat(old_string, &st) > -1) || (fptemp = fopen(old_string, "r")) != NULL) { /* * It is an absolute directory or file * on the local system. - KW */ StrAllocCopy(temp, old_string); LYTrimRelFromAbsPath(temp); CTRACE(tfp, "Converted '%s' to '%s'\n", old_string, temp); cp = HTEscape(temp, URL_PATH); StrAllocCat(*AllocatedString, cp); FREE(cp); FREE(temp); if (fptemp) { fclose(fptemp); fptemp = NULL; } CTRACE(tfp, "Converted '%s' to '%s'\n", old_string, *AllocatedString); #endif /* VMS */ } else if (old_string[1] == '~') { /* * Has a Home_Dir() reference. Handle it * as if there weren't a lead slash. - FM */ #ifdef VMS StrAllocCat(*AllocatedString, HTVMS_wwwName((char *)Home_Dir())); #else StrAllocCat(*AllocatedString, Home_Dir()); #endif /* VMS */ if ((cp = strchr((old_string + 1), '/')) != NULL) { /* * Append rest of path, if present, skipping "user" if * "~user" was entered, simplifying, and eliminating * any residual relative elements. - FM */ StrAllocCopy(temp, cp); LYTrimRelFromAbsPath(temp); StrAllocCat(*AllocatedString, temp); FREE(temp); } } else { /* * Normal absolute path. Simplify, trim any * residual relative elements, and append it. - FM */ StrAllocCopy(temp, old_string); LYTrimRelFromAbsPath(temp); StrAllocCat(*AllocatedString, temp); FREE(temp); } CTRACE(tfp, "Converted '%s' to '%s'\n", old_string, *AllocatedString); } FREE(old_string); /* Pause so we can read the messages before invoking curses */ CTRACE_SLEEP(AlertSecs); } /* * This function rewrites and reallocates a previously allocated * string so that the first element is a confirmed Internet host, * and returns TRUE, otherwise it does not modify the string and * returns FALSE. It first tries the element as is, then, if the * element does not end with a dot, it adds prefixes from the * (comma separated) prefix list argument, and, if the element * does not begin with a dot, suffixes from the (comma separated) * suffix list arguments (e.g., www.host.com, then www.host,edu, * then www.host.net, then www.host.org). The remaining path, if * one is present, will be appended to the expanded host. It also * takes into account whether a colon is in the element or suffix, * and includes that and what follows as a port field for the * expanded host field (e.g, wfbr:8002/dir/lynx should yield * www.wfbr.edu:8002/dir/lynx). The calling function should * prepend the scheme field (e.g., http://), or pass the string * to LYAddSchemeForURL(), if this function returns TRUE. - FM */ PUBLIC BOOLEAN LYExpandHostForURL ARGS3( char **, AllocatedString, char *, prefix_list, char *, suffix_list) { char DomainPrefix[80], *StartP, *EndP; char DomainSuffix[80], *StartS, *EndS; char *Str = NULL, *StrColon = NULL, *MsgStr = NULL; char *Host = NULL, *HostColon = NULL, *host = NULL; char *Path = NULL; char *Fragment = NULL; BOOLEAN GotHost = FALSE; BOOLEAN Startup = (helpfilepath == NULL); /* * If it's a NULL or zero-length string, * or if it begins with a slash or hash, * don't continue pointlessly. - FM */ if (!(*AllocatedString) || *AllocatedString[0] == '\0' || *AllocatedString[0] == '/' || *AllocatedString[0] == '#') { return GotHost; } /* * If it's a partial or relative path, * don't continue pointlessly. - FM */ if (!strncmp(*AllocatedString, "..", 2) || !strncmp(*AllocatedString, "./", 2)) { return GotHost; } /* * Make a clean copy of the string, and trim off the * path if one is present, but save the information * so we can restore the path after filling in the * Host[:port] field. - FM */ StrAllocCopy(Str, *AllocatedString); if ((Path = strchr(Str, '/')) != NULL) { /* * Have a path. Any fragment should * already be included in Path. - FM */ *Path = '\0'; } else if ((Fragment = strchr(Str, '#')) != NULL) { /* * No path, so check for a fragment and * trim that, to be restored after filling * in the Host[:port] field. - FM */ *Fragment = '\0'; } /* * If the potential host string has a colon, assume it * begins a port field, and trim it off, but save the * information so we can restore the port field after * filling in the host field. - FM */ if ((StrColon = strrchr(Str, ':')) != NULL && isdigit((unsigned char)StrColon[1])) { if (StrColon == Str) { FREE(Str); return GotHost; } *StrColon = '\0'; } /* * Do a DNS test on the potential host field * as presently trimmed. - FM */ StrAllocCopy(host, Str); HTUnEscape(host); if (LYCursesON) { StrAllocCopy(MsgStr, WWW_FIND_MESSAGE); StrAllocCat(MsgStr, host); StrAllocCat(MsgStr, FIRST_SEGMENT); HTProgress(MsgStr); } else if (Startup && !dump_output_immediately) { fprintf(stdout, "%s '%s'%s\n", WWW_FIND_MESSAGE, host, FIRST_SEGMENT); } #ifndef DJGPP if (LYGetHostByName(host) != NULL) #else if (resolve(host) != 0) #endif /* DJGPP */ { /* * Clear any residual interrupt. - FM */ if (LYCursesON && HTCheckForInterrupt()) { CTRACE(tfp, "LYExpandHostForURL: Ignoring interrupt because '%s' resolved.\n", host); } /* * Return success. - FM */ GotHost = TRUE; FREE(host); FREE(Str); FREE(MsgStr); return GotHost; #ifndef DJGPP } else if (LYCursesON && (lynx_nsl_status == HT_INTERRUPTED)) { #else /* DJGPP */ } else if (LYCursesON && HTCheckForInterrupt()) { #endif /* DJGPP */ /* * Give the user chance to interrupt lookup cycles. - KW & FM */ CTRACE(tfp, "LYExpandHostForURL: Interrupted while '%s' failed to resolve.\n", host); /* * Return failure. - FM */ FREE(host); FREE(Str); FREE(MsgStr); return FALSE; } /* * Set the first prefix, making it a zero-length string * if the list is NULL or if the potential host field * ends with a dot. - FM */ StartP = ((prefix_list && Str[strlen(Str)-1] != '.') ? prefix_list : ""); /* * If we have a prefix, but the allocated string is * one of the common host prefixes, make our prefix * a zero-length string. - FM */ if (*StartP && *StartP != '.') { if (!strncasecomp(*AllocatedString, "www.", 4) || !strncasecomp(*AllocatedString, "ftp.", 4) || !strncasecomp(*AllocatedString, "gopher.", 7) || !strncasecomp(*AllocatedString, "wais.", 5) || !strncasecomp(*AllocatedString, "cso.", 4) || !strncasecomp(*AllocatedString, "ns.", 3) || !strncasecomp(*AllocatedString, "ph.", 3) || !strncasecomp(*AllocatedString, "finger.", 7) || !strncasecomp(*AllocatedString, "news.", 5) || !strncasecomp(*AllocatedString, "nntp.", 5)) { StartP = ""; } } while ((*StartP) && (WHITE(*StartP) || *StartP == ',')) { StartP++; /* Skip whitespace and separators */ } EndP = StartP; while (*EndP && !WHITE(*EndP) && *EndP != ',') { EndP++; /* Find separator */ } LYstrncpy(DomainPrefix, StartP, (EndP - StartP)); /* * Test each prefix with each suffix. - FM */ do { /* * Set the first suffix, making it a zero-length string * if the list is NULL or if the potential host field * begins with a dot. - FM */ StartS = ((suffix_list && *Str != '.') ? suffix_list : ""); while ((*StartS) && (WHITE(*StartS) || *StartS == ',')) { StartS++; /* Skip whitespace and separators */ } EndS = StartS; while (*EndS && !WHITE(*EndS) && *EndS != ',') { EndS++; /* Find separator */ } LYstrncpy(DomainSuffix, StartS, (EndS - StartS)); /* * Create domain names and do DNS tests. - FM */ do { StrAllocCopy(Host, DomainPrefix); StrAllocCat(Host, ((*Str == '.') ? (Str + 1) : Str)); if (Host[strlen(Host)-1] == '.') { Host[strlen(Host)-1] = '\0'; } StrAllocCat(Host, DomainSuffix); if ((HostColon = strrchr(Host, ':')) != NULL && isdigit((unsigned char)HostColon[1])) { *HostColon = '\0'; } StrAllocCopy(host, Host); HTUnEscape(host); if (LYCursesON) { StrAllocCopy(MsgStr, WWW_FIND_MESSAGE); StrAllocCat(MsgStr, host); StrAllocCat(MsgStr, GUESSING_SEGMENT); HTProgress(MsgStr); } else if (Startup && !dump_output_immediately) { fprintf(stdout, "%s '%s'%s\n", WWW_FIND_MESSAGE, host, GUESSING_SEGMENT); } #ifndef DJGPP GotHost = (LYGetHostByName(host) != NULL); #else GotHost = (resolve(host) != 0); #endif /* DJGPP */ if (HostColon != NULL) { *HostColon = ':'; } if (GotHost == FALSE) { /* * Give the user chance to interrupt lookup cycles. - KW */ #ifndef DJGPP if (LYCursesON && (lynx_nsl_status == HT_INTERRUPTED)) #else /* DJGPP */ if (LYCursesON && HTCheckForInterrupt()) #endif /* DJGPP */ { CTRACE(tfp, "LYExpandHostForURL: Interrupted while '%s' failed to resolve.\n", host); FREE(Str); FREE(MsgStr); FREE(Host); FREE(host); return FALSE; /* We didn't find a valid name. */ } /* * Advance to the next suffix, or end of suffix list. - FM */ StartS = ((*EndS == '\0') ? EndS : (EndS + 1)); while ((*StartS) && (WHITE(*StartS) || *StartS == ',')) { StartS++; /* Skip whitespace and separators */ } EndS = StartS; while (*EndS && !WHITE(*EndS) && *EndS != ',') { EndS++; /* Find separator */ } LYstrncpy(DomainSuffix, StartS, (EndS - StartS)); } } while ((GotHost == FALSE) && (*DomainSuffix != '\0')); if (GotHost == FALSE) { /* * Advance to the next prefix, or end of prefix list. - FM */ StartP = ((*EndP == '\0') ? EndP : (EndP + 1)); while ((*StartP) && (WHITE(*StartP) || *StartP == ',')) { StartP++; /* Skip whitespace and separators */ } EndP = StartP; while (*EndP && !WHITE(*EndP) && *EndP != ',') { EndP++; /* Find separator */ } LYstrncpy(DomainPrefix, StartP, (EndP - StartP)); } } while ((GotHost == FALSE) && (*DomainPrefix != '\0')); /* * If a test passed, restore the port field if we had one * and there is no colon in the expanded host, and the path * if we had one, and reallocate the original string with * the expanded Host[:port] field included. - FM */ if (GotHost) { if (StrColon && strchr(Host, ':') == NULL) { *StrColon = ':'; StrAllocCat(Host, StrColon); } if (Path) { *Path = '/'; StrAllocCat(Host, Path); } else if (Fragment) { StrAllocCat(Host, "/"); *Fragment = '#'; StrAllocCat(Host, Fragment); } StrAllocCopy(*AllocatedString, Host); } /* * Clear any residual interrupt. - FM */ if (LYCursesON && HTCheckForInterrupt()) { CTRACE(tfp, "LYExpandHostForURL: Ignoring interrupt because '%s' %s.\n", host, (GotHost ? "resolved" : "timed out")); } /* * Clean up and return the last test result. - FM */ FREE(Str); FREE(MsgStr); FREE(Host); FREE(host); return GotHost; } /* * This function rewrites and reallocates a previously allocated * string that begins with an Internet host name so that the string * begins with its guess of the scheme based on the first field of * the host name, or the default scheme if no guess was made, and * returns TRUE, otherwise it does not modify the string and returns * FALSE. It also returns FALSE without modifying the string if the * default_scheme argument was NULL or zero-length and no guess was * made. - FM */ PUBLIC BOOLEAN LYAddSchemeForURL ARGS2( char **, AllocatedString, char *, default_scheme) { char *Str = NULL; BOOLEAN GotScheme = FALSE; /* * If we were passed a NULL or zero-length string, * don't continue pointlessly. - FM */ if (!(*AllocatedString) || *AllocatedString[0] == '\0') { return GotScheme; } /* * Try to guess the appropriate scheme. - FM */ if (0 == strncasecomp(*AllocatedString, "www", 3)) { /* * This could be either http or https, so check * the default and otherwise use "http". - FM */ if (default_scheme != NULL && NULL != strstr(default_scheme, "http")) { StrAllocCopy(Str, default_scheme); } else { StrAllocCopy(Str, "http://"); } GotScheme = TRUE; } else if (0 == strncasecomp(*AllocatedString, "ftp", 3)) { StrAllocCopy(Str, "ftp://"); GotScheme = TRUE; } else if (0 == strncasecomp(*AllocatedString, "gopher", 6)) { StrAllocCopy(Str, "gopher://"); GotScheme = TRUE; } else if (0 == strncasecomp(*AllocatedString, "wais", 4)) { StrAllocCopy(Str, "wais://"); GotScheme = TRUE; } else if (0 == strncasecomp(*AllocatedString, "cso", 3) || 0 == strncasecomp(*AllocatedString, "ns.", 3) || 0 == strncasecomp(*AllocatedString, "ph.", 3)) { StrAllocCopy(Str, "cso://"); GotScheme = TRUE; } else if (0 == strncasecomp(*AllocatedString, "finger", 6)) { StrAllocCopy(Str, "finger://"); GotScheme = TRUE; } else if (0 == strncasecomp(*AllocatedString, "news", 4)) { /* * This could be either news, snews, or nntp, so * check the default, and otherwise use news. - FM */ if ((default_scheme != NULL) && (NULL != strstr(default_scheme, "news") || NULL != strstr(default_scheme, "nntp"))) { StrAllocCopy(Str, default_scheme); } else { StrAllocCopy(Str, "news://"); } GotScheme = TRUE; } else if (0 == strncasecomp(*AllocatedString, "nntp", 4)) { StrAllocCopy(Str, "nntp://"); GotScheme = TRUE; } /* * If we've make a guess, use it. Otherwise, if we * were passed a default scheme prefix, use that. - FM */ if (GotScheme == TRUE) { StrAllocCat(Str, *AllocatedString); StrAllocCopy(*AllocatedString, Str); FREE(Str); return GotScheme; } else if (default_scheme != NULL && *default_scheme != '\0') { StrAllocCopy(Str, default_scheme); GotScheme = TRUE; StrAllocCat(Str, *AllocatedString); StrAllocCopy(*AllocatedString, Str); FREE(Str); return GotScheme; } return GotScheme; } /* * This function expects an absolute Unix or VMS SHELL path * spec as an allocated string, simplifies it, and trims out * any residual relative elements. It also checks whether * the path had a terminal slash, and if it didn't, makes * sure that the simplified path doesn't either. If it's * a directory, our convention is to exclude "Up to parent" * links when a terminal slash is present. - FM */ PUBLIC void LYTrimRelFromAbsPath ARGS1( char *, path) { char *cp; int i; BOOL TerminalSlash; /* * Make sure we have a pointer to an absolute path. - FM */ if (path == NULL || !LYIsPathSep(*path)) return; /* * Check whether the path has a terminal slash. - FM */ TerminalSlash = LYIsPathSep(path[(strlen(path) - 1)]); /* * Simplify the path and then do any necessary trimming. - FM */ HTSimplify(path); cp = path; while (cp[1] == '.') { if (cp[2] == '\0') { /* * Eliminate trailing dot. - FM */ cp[1] = '\0'; } else if (LYIsPathSep(cp[2])) { /* * Skip over the "/." of a "/./". - FM */ cp += 2; } else if (cp[2] == '.' && cp[3] == '\0') { /* * Eliminate trailing dotdot. - FM */ cp[1] = '\0'; } else if (cp[2] == '.' && cp[3] == '/') { /* * Skip over the "/.." of a "/../". - FM */ cp += 3; } else { /* * Done trimming. - FM */ break; } } /* * Load any shifts into path, and eliminate any * terminal slash created by HTSimplify() or our * walk, but not present originally. - FM */ if (cp > path) { for (i = 0; cp[i] != '\0'; i++) path[i] = cp[i]; path[i] = '\0'; } if (TerminalSlash == FALSE) { LYTrimPathSep(path); } } /* * Example Client-Side Include interface. * * This is called from SGML.c and simply returns markup for reporting * the URL of the document being loaded if a comment begins with * "