/* * $LynxId: LYUtils.c,v 1.292 2018/05/15 21:20:52 tom Exp $ */ #include <HTUtils.h> #include <HTTCP.h> #include <HTParse.h> #include <HTAccess.h> #include <HTCJK.h> #include <HTAlert.h> #if defined(__MINGW32__) extern int kbhit(void); /* FIXME: use conio.h */ #undef UNIX #elif defined(_WINDOWS) #ifdef DONT_USE_GETTEXT #undef gettext #elif defined(HAVE_GETTEXT) #undef gettext #define gettext conio_gettext #else #undef gettext #endif #include <conio.h> #ifdef DONT_USE_GETTEXT #define gettext(s) s #elif defined(HAVE_GETTEXT) #undef gettext #ifdef _INTL_REDIRECT_MACROS #define gettext libintl_gettext /* restore definition from libintl.h */ #endif #else #undef gettext #define gettext(s) s #endif #if !defined(kbhit) && defined(_WCONIO_DEFINED) #define kbhit() _kbhit() /* reasonably recent conio.h */ #endif #elif defined(__minix) #include <termios.h> /* for struct winsize */ #endif /* __MINGW32__ */ #include <LYCurses.h> #include <LYHistory.h> #include <LYStrings.h> #include <LYGlobalDefs.h> #include <LYUtils.h> #include <LYSignal.h> #include <GridText.h> #include <LYClean.h> #include <LYCharSets.h> #include <LYCharUtils.h> #include <LYMainLoop.h> #include <LYKeymap.h> #ifdef __DJGPP__ #include <go32.h> #include <sys/exceptn.h> #endif /* __DJGPP__ */ #ifndef NO_GROUPS #include <HTFile.h> #endif #ifdef _WINDOWS /* 1998/04/30 (Thu) 19:04:25 */ #define GETPID() (unsigned) (getpid() & 0xffff) #else #define GETPID() (unsigned) getpid() #endif /* _WINDOWS */ #ifdef FNAMES_8_3 #define PID_FMT "%04x" #else #define PID_FMT "%u" #endif #ifdef DJGPP_KEYHANDLER #include <bios.h> #endif /* DJGPP_KEYHANDLER */ #ifdef __EMX__ # define BOOLEAN OS2_BOOLEAN /* Conflicts, but is used */ # undef HT_ERROR /* Conflicts too */ # define INCL_PM /* I want some PM functions.. */ # define INCL_DOSPROCESS /* TIB PIB. */ # include <os2.h> # undef BOOLEAN #endif #ifdef VMS #include <descrip.h> #include <libclidef.h> #include <lib$routines.h> #endif /* VMS */ #ifdef HAVE_UTMP #include <pwd.h> #ifdef UTMPX_FOR_UTMP #include <utmpx.h> #define utmp utmpx #ifdef UTMPX_FILE #ifdef UTMP_FILE #undef UTMP_FILE #endif /* UTMP_FILE */ #define UTMP_FILE UTMPX_FILE #else #ifdef __UTMPX_FILE #define UTMP_FILE __UTMPX_FILE /* at least in OS/390 S/390 -- gil -- 2100 */ #else #ifndef UTMP_FILE #define UTMP_FILE "/var/adm/utmpx" /* Digital Unix 4.0 */ #endif #endif #endif /* UTMPX_FILE */ #else #include <utmp.h> #endif /* UTMPX_FOR_UTMP */ #endif /* HAVE_UTMP */ #ifdef NEED_PTEM_H /* they neglected to define struct winsize in termios.h -- it's only in * termio.h and ptem.h (the former conflicts with other definitions). */ #include <sys/stream.h> #include <sys/ptem.h> #endif #include <LYLeaks.h> #ifdef USE_COLOR_STYLE #include <AttrList.h> #include <LYHash.h> #include <LYStyle.h> #endif #ifdef SVR4_BSDSELECT extern int BSDselect(int nfds, fd_set * readfds, fd_set * writefds, fd_set * exceptfds, struct timeval *timeout); #ifdef select #undef select #endif /* select */ #define select BSDselect #ifdef SOCKS #ifdef Rselect #undef Rselect #endif /* Rselect */ #define Rselect BSDselect #endif /* SOCKS */ #endif /* SVR4_BSDSELECT */ #ifdef __DJGPP__ #undef select /* defined to select_s in www_tcp.h */ #endif #ifndef UTMP_FILE #if defined(__FreeBSD__) || defined(__bsdi__) #define UTMP_FILE _PATH_UTMP #else #define UTMP_FILE "/etc/utmp" #endif /* __FreeBSD__ || __bsdi__ */ #endif /* !UTMP_FILE */ /* * experimental - make temporary filenames random to make the scheme less * obvious. However, as noted by KW, there are instances (such as the * 'O'ption page, for which Lynx will store a temporary filename even when * it no longer applies, since it will reuse that filename at a later time. */ #ifdef USE_RAND_TEMPNAME #if defined(LYNX_RAND_MAX) #define HAVE_RAND_TEMPNAME 1 #define MAX_TEMPNAME 10000 #ifndef BITS_PER_CHAR #define BITS_PER_CHAR 8 #endif #endif #endif #define COPY_COMMAND "%s %s %s" static HTList *localhost_aliases = NULL; /* Hosts to treat as local */ static char *HomeDir = NULL; /* HOME directory */ HTList *sug_filenames = NULL; /* Suggested filenames */ /* * Maintain a list of all of the temp-files we create so that we can remove * them during the cleanup. */ typedef struct _LYTemp { struct _LYTemp *next; char *name; BOOLEAN outs; FILE *file; } LY_TEMP; static LY_TEMP *ly_temp; static LY_TEMP *FindTempfileByName(const char *name) { LY_TEMP *p; for (p = ly_temp; p != 0; p = p->next) { if (!strcmp(p->name, name)) { break; } } return p; } static LY_TEMP *FindTempfileByFP(FILE *fp) { LY_TEMP *p; for (p = ly_temp; p != 0; p = p->next) { if (p->file == fp) { break; } } return p; } #if defined(_WIN32) /* * Use RegQueryValueExA() rather than RegQueryValueEx() for compatibility * with non-Unicode winvile */ static int w32_get_reg_sz(HKEY hkey, const char *name, char *value, unsigned length) { int result; DWORD dwSzBuffer = length; CTRACE((tfp, "w32_get_reg_sz(%s)\n", name)); result = RegQueryValueExA(hkey, name, NULL, NULL, (LPBYTE) value, &dwSzBuffer); if (result == ERROR_SUCCESS) { value[dwSzBuffer] = 0; CTRACE((tfp, "->%s\n", value)); } return result; } static char *w32_get_shell_folder(const char *name) { static HKEY rootkey = HKEY_CURRENT_USER; char *result = 0; HKEY hkey; char buffer[LY_MAXPATH]; if (RegOpenKeyEx(rootkey, W32_STRING("Software" "\\Microsoft" "\\Windows" "\\CurrentVersion" "\\Explorer" "\\Shell Folders"), 0, KEY_READ, &hkey) == ERROR_SUCCESS) { if (w32_get_reg_sz(hkey, name, buffer, sizeof(buffer)) == ERROR_SUCCESS) { result = strdup(buffer); (void) RegCloseKey(hkey); } (void) RegCloseKey(hkey); } return non_empty(result) ? result : 0; } #endif /* * Get an environment variable, rejecting empty strings */ char *LYGetEnv(const char *name) { char *result = getenv(name); #if defined(_WIN32) if (result == 0) { static HKEY rootkeys[] = {HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE}; int j; HKEY hkey; char buffer[256]; for (j = 0; j < (int) TABLESIZE(rootkeys); ++j) { if (RegOpenKeyEx(rootkeys[j], LYNX_SUBKEY W32_STRING("\\Environment"), 0, KEY_READ, &hkey) == ERROR_SUCCESS) { if (w32_get_reg_sz(hkey, name, buffer, sizeof(buffer)) == ERROR_SUCCESS) { result = strdup(buffer); (void) RegCloseKey(hkey); break; } (void) RegCloseKey(hkey); } } } #endif return non_empty(result) ? result : 0; } /* * ascii versions of locale sensitive functions needed because in * Turkish locales tolower("I") is not "i". That's fatal for case * sensitive operations with charset names, HTML tags etc. */ #ifdef USE_ASCII_CTYPES int ascii_tolower(int i) { if (91 > i && i > 64) return (i + 32); else return i; } int ascii_toupper(int i) { if (123 > i && i > 96) return (i - 32); else return i; } int ascii_isupper(int i) { if (91 > i && i > 64) return 1; else return 0; } #endif /* USE_ASCII_CTYPES */ /* * Check for UTF-8 data, returning the length past the first character. * Return zero if we found an ordinary character rather than UTF-8. */ size_t utf8_length(int utf_flag, const char *data) { size_t utf_extra = 0; if (utf_flag && is8bits(*data)) { if ((*data & 0xe0) == 0xc0) { utf_extra = 1; } else if ((*data & 0xf0) == 0xe0) { utf_extra = 2; } else if ((*data & 0xf8) == 0xf0) { utf_extra = 3; } else if ((*data & 0xfc) == 0xf8) { utf_extra = 4; } else if ((*data & 0xfe) == 0xfc) { utf_extra = 5; } else { /* * Garbage. */ utf_extra = 0; } if (strlen(data + 1) < utf_extra) { /* * Shouldn't happen. */ utf_extra = 0; } } return utf_extra; } /* * Free storage used for the link-highlighting. */ void LYFreeHilites(int first, int last) { int i; for (i = first; i < last; i++) { LYSetHilite(i, NULL); FREE(links[i].lname); } } #define LXP (links[cur].lx) #define LYP (links[cur].ly) /* * Set the initial highlight information for a given link. */ void LYSetHilite(int cur, const char *text) { links[cur].list.hl_base.hl_text = DeConst(text); links[cur].list.hl_len = (short) ((text != NULL) ? 1 : 0); FREE(links[cur].list.hl_info); } /* * Add highlight information for the next line of a link. */ void LYAddHilite(int cur, char *text, int x) { HiliteList *list = &(links[cur].list); HiliteInfo *have = list->hl_info; size_t need = (unsigned) (list->hl_len - 1); size_t want; list->hl_len = (short) (list->hl_len + 1); want = (size_t) list->hl_len; if (have != NULL) { have = typeRealloc(HiliteInfo, have, want); } else { have = typeMallocn(HiliteInfo, want); } list->hl_info = have; have[need].hl_text = text; have[need].hl_x = (short) x; } /* * Get the highlight text, counting from zero. */ const char *LYGetHiliteStr(int cur, int count) { const char *result; if (count >= links[cur].list.hl_len) result = NULL; else if (count > 0) result = links[cur].list.hl_info[count - 1].hl_text; else result = links[cur].list.hl_base.hl_text; return result; } /* * Get the X-ordinate at which to draw the corresponding highlight-text */ int LYGetHilitePos(int cur, int count) { int result; if (count >= links[cur].list.hl_len) result = -1; else if (count > 0) result = links[cur].list.hl_info[count - 1].hl_x; else result = LXP; return result; } #ifdef SHOW_WHEREIS_TARGETS #define SKIP_GLYPHS(theFlag, theData, theOffset) \ (theFlag \ ? LYmbcs_skip_glyphs(theData, (theOffset), theFlag) \ : (theData + (theOffset))) /* * If we have an emphasized WHEREIS hit in the highlighted text, restore the * emphasis. Note that we never emphasize the first and last characters of the * highlighted text when we are making the link current, so the link attributes * for the current link will persist at the beginning and end, providing an * indication to the user that it has been made current. Also note that we use * HText_getFirstTargetInLine() to determine if there's a hit in the HText * structure line containing the link, and if so, get back a copy of the line * starting at that first hit (which might be before or after our link), and * with all IsSpecial characters stripped, so we don't need to deal with them * here. -FM */ static BOOL show_whereis_targets(int flag, int cur, int count, const char *target, int TargetEmphasisON, int utf_flag) { const char *mydata = NULL; const char *cp; char *theData = NULL; char buffer[MAX_LINE]; char tmp[7]; int HitOffset; int LenNeeded; int Offset; int tLen; tmp[0] = tmp[1] = tmp[2] = '\0'; if (non_empty(target) && (links[cur].type & WWW_LINK_TYPE) && non_empty(LYGetHiliteStr(cur, count)) && LYP + count < display_lines && HText_getFirstTargetInLine(HTMainText, links[cur].anchor_line_num + count, utf_flag, &Offset, &tLen, &theData, target)) { int itmp, written, len, y, offset; const char *data; int tlen = (int) strlen(target); int hlen, hLen; int hLine = LYP + count; int hoffset = LYGetHilitePos(cur, count); size_t utf_extra = 0; /* * Copy into the buffer only what will fit up to the right border of * the screen. -FM */ LYmbcsstrncpy(buffer, NonNull(LYGetHiliteStr(cur, count)), (int) (sizeof(buffer) - 1), (LYcolLimit - LYGetHilitePos(cur, count)), utf_flag); hlen = (int) strlen(buffer); hLen = ((IS_CJK_TTY || utf_flag) ? LYmbcsstrlen(buffer, utf_flag, YES) : hlen); /* * Break out if the first hit in the line starts after this link. -FM */ if (Offset < (hoffset + hLen)) { /* * Recursively skip hits that end before this link, and break out * if there is no hit beyond those. -FM */ mydata = theData; while ((Offset < hoffset) && ((Offset + tLen) <= hoffset)) { data = (mydata + tlen); offset = (Offset + tLen); if (((cp = LYno_attr_mb_strstr(data, target, utf_flag, YES, &HitOffset, &LenNeeded)) != NULL) && (offset + LenNeeded) < LYcols) { mydata = cp; Offset = (offset + HitOffset); } else { goto highlight_search_done; } } data = buffer; offset = hoffset; /* * If the hit starts before the hightext, and ends in or beyond the * hightext, restore the emphasis, skipping the first and last * characters of the hightext if we're making the link current. * -FM */ if (offset >= 0 && (Offset < offset) && ((Offset + tLen) > offset)) { itmp = 0; written = 0; len = (tlen - (offset - Offset)); /* * Go to the start of the hightext and handle its first * character. -FM */ LYmove(hLine, offset); tmp[0] = data[itmp]; utf_extra = utf8_length(utf_flag, data + itmp); if (utf_extra) { LYStrNCpy(&tmp[1], &data[itmp + 1], utf_extra); itmp += (int) utf_extra; /* * Start emphasis immediately if we are making the link * non-current. -FM */ if (flag != TRUE) { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; LYaddstr(tmp); } else { LYmove(hLine, (offset + 1)); } tmp[1] = '\0'; written += (int) (utf_extra + 1); } else if (IS_CJK_TTY && is8bits(tmp[0])) { /* * For CJK strings, by Masanobu Kimura. */ tmp[1] = data[++itmp]; /* * Start emphasis immediately if we are making the link * non-current. -FM */ if (flag != TRUE) { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; LYaddstr(tmp); } else { LYmove(hLine, (offset + 1)); } tmp[1] = '\0'; written += 2; } else { /* * Start emphasis immediately if we are making the link * non-current. -FM */ if (flag != TRUE) { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; LYaddstr(tmp); } else { LYmove(hLine, (offset + 1)); } written++; } itmp++; /* * Start emphasis after the first character if we are making * the link current and this is not the last character. -FM */ if (!TargetEmphasisON && data[itmp] != '\0') { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; } /* * Handle the remaining characters. -FM */ for (; written < len && (tmp[0] = data[itmp]) != '\0'; itmp++) { /* * Print all the other target chars, except the last * character if it is also the last character of hightext * and we are making the link current. -FM */ utf_extra = utf8_length(utf_flag, data + itmp); if (utf_extra) { LYStrNCpy(&tmp[1], &data[itmp + 1], utf_extra); itmp += (int) utf_extra; /* * Make sure we don't restore emphasis to the last * character of hightext if we are making the link * current. -FM */ if (flag == TRUE && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; LYGetYX(y, offset); (void) y; LYmove(hLine, (offset + 1)); } else { LYaddstr(tmp); } tmp[1] = '\0'; written += (int) (utf_extra + 1); } else if (IS_CJK_TTY && is8bits(tmp[0])) { /* * For CJK strings, by Masanobu Kimura. */ tmp[1] = data[++itmp]; /* * Make sure we don't restore emphasis to the last * character of hightext if we are making the link * current. -FM */ if (flag == TRUE && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; LYGetYX(y, offset); LYmove(hLine, (offset + 1)); } else { LYaddstr(tmp); } tmp[1] = '\0'; written += 2; } else { /* * Make sure we don't restore emphasis to the last * character of hightext if we are making the link * current. -FM */ if (flag == TRUE && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; LYGetYX(y, offset); LYmove(hLine, (offset + 1)); } else { LYaddstr(tmp); } written++; } } /* * Stop the emphasis if we haven't already, then reset the * offset to our current position in the line, and if that is * beyond the link, or or we are making the link current and it * is the last character of the hightext, we are done. -FM */ if (TargetEmphasisON) { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; } LYGetYX(y, offset); if (offset < (hoffset + (flag == TRUE ? (hLen - 1) : hLen)) /* * See if we have another hit that starts within the * hightext. -FM */ && ((cp = LYno_attr_mb_strstr(data = SKIP_GLYPHS(utf_flag, mydata, offset - Offset), target, utf_flag, YES, &HitOffset, &LenNeeded)) != NULL) && (offset + LenNeeded) < LYcols /* * If the hit starts after the end of the hightext, or we * are making the link current and the hit starts at its * last character, we are done. -FM */ && (HitOffset + offset) < (hoffset + (flag == TRUE ? (hLen - 1) : hLen))) { /* * Set up the data and offset for the hit, and let the code * for within hightext hits handle it. -FM */ mydata = cp; Offset = (offset + HitOffset); data = buffer; offset = hoffset; goto highlight_hit_within_hightext; } goto highlight_search_done; } highlight_hit_within_hightext: /* * If we get to here, the hit starts within the hightext. If we * are making the link current and it's the last character in the * hightext, we are done. Otherwise, move there and start * restoring the emphasis. -FM */ if ((Offset - offset) <= (flag == TRUE ? (hLen - 1) : hLen)) { data = SKIP_GLYPHS(utf_flag, data, Offset - offset); if (utf_flag) { LYrefresh(); } offset = Offset; itmp = 0; written = 0; len = tlen; /* * Go to the start of the hit and handle its first character. * -FM */ LYmove(hLine, offset); tmp[0] = data[itmp]; utf_extra = utf8_length(utf_flag, data + itmp); if (utf_extra) { LYStrNCpy(&tmp[1], &data[itmp + 1], utf_extra); itmp += (int) utf_extra; /* * Start emphasis immediately if we are making the link * non-current, or we are making it current but this is not * the first or last character of the hightext. -FM */ if (flag != TRUE || (offset > hoffset && data[itmp + 1] != '\0')) { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; LYaddstr(tmp); } else { LYmove(hLine, (offset + 1)); } tmp[1] = '\0'; written += (int) (utf_extra + 1); } else if (IS_CJK_TTY && is8bits(tmp[0])) { /* * For CJK strings, by Masanobu Kimura. */ tmp[1] = data[++itmp]; /* * Start emphasis immediately if we are making the link * non-current, or we are making it current but this is not * the first or last character of the hightext. -FM */ if (flag != TRUE || (offset > hoffset && data[itmp + 1] != '\0')) { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; LYaddstr(tmp); } else { LYmove(hLine, (offset + 2)); } tmp[1] = '\0'; written += 2; } else { /* * Start emphasis immediately if we are making the link * non-current, or we are making it current but this is not * the first or last character of the hightext. -FM */ if (flag != TRUE || (offset > hoffset && data[itmp + 1] != '\0')) { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; LYaddstr(tmp); } else { LYmove(hLine, (offset + 1)); } written++; } itmp++; /* * Start emphasis after the first character if we are making * the link current and this is not the last character. -FM */ if (!TargetEmphasisON && data[itmp] != '\0') { LYstartTargetEmphasis(); TargetEmphasisON = TRUE; } for (; written < len && (tmp[0] = data[itmp]) != '\0'; itmp++) { /* * Print all the other target chars, except the last * character if it is also the last character of hightext * and we are making the link current. -FM */ utf_extra = utf8_length(utf_flag, data + itmp); if (utf_extra) { LYStrNCpy(&tmp[1], &data[itmp + 1], utf_extra); itmp += (int) utf_extra; /* * Make sure we don't restore emphasis to the last * character of hightext if we are making the link * current. -FM */ if (flag == TRUE && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; LYGetYX(y, offset); LYmove(hLine, (offset + 1)); } else { LYaddstr(tmp); } tmp[1] = '\0'; written += (int) (utf_extra + 1); } else if (IS_CJK_TTY && is8bits(tmp[0])) { /* * For CJK strings, by Masanobu Kimura. */ tmp[1] = data[++itmp]; /* * Make sure we don't restore emphasis to the last * character of hightext if we are making the link * current. -FM */ if (flag == TRUE && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; LYGetYX(y, offset); LYmove(hLine, (offset + 1)); } else { LYaddstr(tmp); } tmp[1] = '\0'; written += 2; } else { /* * Make sure we don't restore emphasis to the last * character of hightext if we are making the link * current. -FM */ if (flag == TRUE && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; LYGetYX(y, offset); LYmove(hLine, (offset + 1)); } else { LYaddstr(tmp); } written++; } } /* * Stop the emphasis if we haven't already, then reset the * offset to our current position in the line, and if that is * beyond the link, or we are making the link current and it is * the last character in the hightext, we are done. -FM */ if (TargetEmphasisON) { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; } LYGetYX(y, offset); if (offset < (hoffset + (flag == TRUE ? (hLen - 1) : hLen)) /* * See if we have another hit that starts within the * hightext. -FM */ && ((cp = LYno_attr_mb_strstr(data = SKIP_GLYPHS(utf_flag, mydata, offset - Offset), target, utf_flag, YES, &HitOffset, &LenNeeded)) != NULL) && (offset + LenNeeded) < LYcols /* * If the hit starts after the end of the hightext, or we * are making the link current and the hit starts at its * last character, we are done. -FM */ && (HitOffset + offset) < (hoffset + (flag == TRUE ? (hLen - 1) : hLen))) { /* * If the target extends beyond our buffer, emphasize * everything in the hightext starting at this hit. * Otherwise, set up the data and offsets, and loop back. * -FM */ if ((HitOffset + (offset + tLen)) >= (hoffset + hLen)) { offset = (HitOffset + offset); data = SKIP_GLYPHS(utf_flag, mydata, offset - hoffset); if (utf_flag) { LYrefresh(); } LYmove(hLine, offset); itmp = 0; written = 0; len = (int) strlen(data); /* * Turn the emphasis back on. -FM */ LYstartTargetEmphasis(); TargetEmphasisON = TRUE; for (; written < len && (tmp[0] = data[itmp]) != '\0'; itmp++) { /* * Print all the other target chars, except the * last character if it is also the last character * of hightext and we are making the link current. * -FM */ utf_extra = utf8_length(utf_flag, data + itmp); if (utf_extra) { LYStrNCpy(&tmp[1], &data[itmp + 1], utf_extra); itmp += (int) utf_extra; /* * Make sure we don't restore emphasis to the * last character of hightext if we are making * the link current. -FM */ if (flag == TRUE && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; LYGetYX(y, offset); LYmove(hLine, (offset + 1)); } else { LYaddstr(tmp); } tmp[1] = '\0'; written += (int) (utf_extra + 1); } else if (IS_CJK_TTY && is8bits(tmp[0])) { /* * For CJK strings, by Masanobu Kimura. */ tmp[1] = data[++itmp]; /* * Make sure we don't restore emphasis to the * last character of hightext if we are making * the link current. -FM */ if (flag == TRUE && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; } else { LYaddstr(tmp); } tmp[1] = '\0'; written += 2; } else { /* * Make sure we don't restore emphasis to the * last character of hightext if we are making * the link current. -FM */ if (flag == TRUE && data[(itmp + 1)] == '\0') { LYstopTargetEmphasis(); TargetEmphasisON = FALSE; } else { LYaddstr(tmp); } written++; } } /* * Turn off the emphasis if we haven't already, and * then we're done. -FM */ if (TargetEmphasisON) { LYstopTargetEmphasis(); } } else { mydata = cp; Offset = (offset + HitOffset); data = buffer; offset = hoffset; goto highlight_hit_within_hightext; } } } } } highlight_search_done: FREE(theData); return (BOOLEAN) TargetEmphasisON; } #endif /* SHOW_WHEREIS_TARGETS */ #ifdef USE_COLOR_STYLE static int find_cached_style(int cur, int flag) { int s = s_alink; #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION if (textfields_need_activation && links[cur].type == WWW_FORM_LINK_TYPE && F_TEXTLIKE(links[cur].l_form->type)) s = s_curedit; #endif if (flag != TRUE) { int x; /* * This is where we try to restore the original style when a link is * unhighlighted. The cached styles array saves the original style * just for this case. If it doesn't have a color change saved at just * the right position, we look at preceding positions in the same line * until we find one. */ if (ValidCachedStyle(LYP, LXP)) { CTRACE2(TRACE_STYLE, (tfp, "STYLE.highlight.off: cached style @(%d,%d): ", LYP, LXP)); s = (int) GetCachedStyle(LYP, LXP); if (s == 0) { for (x = LXP - 1; x >= 0; x--) { s = (int) GetCachedStyle(LYP, x); if (s != 0) { SetCachedStyle(LYP, LXP, (unsigned) s); CTRACE2(TRACE_STYLE, (tfp, "found %d, x_offset=%d.\n", s, x - LXP)); break; } } if (s == 0) { CTRACE2(TRACE_STYLE, (tfp, "not found, assume <a>.\n")); s = s_a; } } else { CTRACE2(TRACE_STYLE, (tfp, "found %d.\n", s)); } } else { CTRACE2(TRACE_STYLE, (tfp, "STYLE.highlight.off: can't use cache.\n")); s = s_a; } } else { CTRACE2(TRACE_STYLE, (tfp, "STYLE.highlight.on: @(%d,%d).\n", LYP, LXP)); } return s; } #endif /* USE_COLOR_STYLE */ /* * Highlight (or unhighlight) a given link. */ void LYhighlight(int flag, int cur, const char *target) { char buffer[MAX_LINE]; int i; int hi_count; int hi_offset; int title_adjust = (no_title ? -TITLE_LINES : 0); char tmp[7]; const char *hi_string; #ifdef SHOW_WHEREIS_TARGETS BOOL TargetEmphasisON = FALSE; BOOL target1_drawn = NO; #endif BOOL utf_flag = (BOOL) IS_UTF8_TTY; BOOL hl1_drawn = NO; tmp[0] = tmp[1] = tmp[2] = '\0'; /* * Bugs in the history code might cause -1 to be sent for cur, which yields * a crash when LYStrNCpy() is called with a nonsense pointer. As far as I * know, such bugs have been squashed, but if they should reappear, this * works around them. -FM */ if (cur < 0) { CTRACE((tfp, "LYhighlight cur %d (bug workaround)\n", cur)); cur = 0; } CTRACE((tfp, "LYhighlight at(%2d,%2d) %s %d [%d]:%s\n", links[cur].ly, links[cur].lx, (flag ? "on" : "off"), cur, links[cur].anchor_number, NONNULL(target))); #if defined(TEXTFIELDS_MAY_NEED_ACTIVATION) && defined(INACTIVE_INPUT_STYLE_VH) if (flag == FALSE) textinput_redrawn = FALSE; #endif if (nlinks > 0) { #ifdef USE_COLOR_STYLE if (flag == TRUE || links[cur].type == WWW_FORM_LINK_TYPE) { LYmove(LYP + title_adjust, LXP); LynxChangeStyle(find_cached_style(cur, flag), STACK_ON); } #else if (links[cur].type == WWW_FORM_LINK_TYPE || LYGetHiliteStr(cur, 0) == NULL) { LYMoveToLink(cur, target, NULL, flag, links[cur].inUnderline, utf_flag); lynx_start_link_color(flag == TRUE, links[cur].inUnderline); } else { LYMoveToLink(cur, target, LYGetHiliteStr(cur, 0), flag, links[cur].inUnderline, utf_flag); hl1_drawn = YES; #ifdef SHOW_WHEREIS_TARGETS target1_drawn = YES; #endif } #endif if (links[cur].type == WWW_FORM_LINK_TYPE) { int len; int avail_space = (LYcolLimit - LXP) + (LYcolLimit * (LYlines - LYP)); const char *text = LYGetHiliteStr(cur, 0); if (text == 0) text = ""; if (avail_space > links[cur].l_form->size) avail_space = links[cur].l_form->size; len = (int) (LYmbcs_skip_cells(text, avail_space, utf_flag) - text); LYwaddnstr(LYwin, text, (size_t) len); while (len++ < avail_space) LYaddch('_'); #ifdef USE_COLOR_STYLE } else if (flag == FALSE) { redraw_lines_of_link(cur); CTRACE2(TRACE_STYLE, (tfp, "STYLE.highlight.off: NOFIX branch @(%d,%d).\n", LYP, LXP)); #endif } else if (!hl1_drawn) { /* * Copy into the buffer only what will fit within the width of the * screen. */ LYmbcsstrncpy(buffer, NonNull(LYGetHiliteStr(cur, 0)), (int) (sizeof(buffer) - 1), (LYcolLimit - LXP), utf_flag); LYaddstr(buffer); } /* * Display a second line as well. */ for (hi_count = 1; (hi_string = LYGetHiliteStr(cur, hi_count)) != NULL && LYP + hi_count <= display_lines; ++hi_count) { int row = LYP + hi_count + title_adjust; hi_offset = LYGetHilitePos(cur, hi_count); if (hi_offset < 0) continue; lynx_stop_link_color(flag == TRUE, links[cur].inUnderline); LYmove(row, hi_offset); #ifdef USE_COLOR_STYLE CTRACE2(TRACE_STYLE, (tfp, "STYLE.highlight.line2: @(%d,%d), style=%d.\n", row, hi_offset, flag == TRUE ? s_alink : s_a)); LynxChangeStyle(flag == TRUE ? s_alink : s_a, ABS_ON); #else lynx_start_link_color(flag == TRUE, links[cur].inUnderline); #endif for (i = 0; (tmp[0] = hi_string[i]) != '\0' && (i + hi_offset) < LYcols; i++) { if (!IsSpecialAttrChar(hi_string[i])) { /* * For CJK strings, by Masanobu Kimura. */ if (IS_CJK_TTY && is8bits(tmp[0])) { tmp[1] = hi_string[++i]; LYaddstr(tmp); tmp[1] = '\0'; } else { LYaddstr(tmp); } } } } lynx_stop_link_color(flag == TRUE, links[cur].inUnderline); #ifdef SHOW_WHEREIS_TARGETS for (hi_count = target1_drawn ? 1 : 0; LYGetHiliteStr(cur, hi_count) != NULL; hi_count++) { TargetEmphasisON = show_whereis_targets(flag, cur, hi_count, target, TargetEmphasisON, utf_flag); } if (!LYShowCursor) /* * Get cursor out of the way. */ LYHideCursor(); else #endif /* SHOW_WHEREIS_TARGETS */ /* * Never hide the cursor if there's no FANCY CURSES or SLANG. */ LYmove(LYP + title_adjust, ((LXP > 0) ? (LXP - 1) : 0)); if (flag) LYrefresh(); } return; } /* * free_and_clear will free a pointer if it is non-zero and then set it to * zero. */ void free_and_clear(char **pointer) { if (*pointer) { FREE(*pointer); *pointer = 0; } return; } /* * Convert single or serial newlines to single spaces throughout a string * (ignore newlines if the preceding character is a space) and convert tabs to * single spaces. Don't ignore any explicit tabs or spaces if the condense * argument is FALSE, otherwise, condense any serial spaces or tabs to one * space. - FM */ void convert_to_spaces(char *string, int condense) { char *s = string; char *ns; BOOL last_is_space = FALSE; if (!s) return; s = LYSkipNonBlanks(s); ns = s; while (*s) { switch (*s) { case ' ': case '\t': if (!(condense && last_is_space)) *(ns++) = ' '; last_is_space = TRUE; break; case '\r': case '\n': if (!last_is_space) { *(ns++) = ' '; last_is_space = TRUE; } break; default: *(ns++) = *s; last_is_space = FALSE; break; } s++; } *ns = '\0'; return; } /* * Strip trailing slashes from directory paths. */ char *strip_trailing_slash(char *dirname) { int i; i = (int) strlen(dirname) - 1; while (i >= 0 && dirname[i] == '/') dirname[i--] = '\0'; return (dirname); } /* * Remove most blanks, but restore one trailing blank to make prompts nicer. */ static void remove_most_blanks(char *buffer) { int length = (int) strlen(buffer); BOOL trailing = (BOOL) ((length != 0) && (buffer[length - 1] == ' ')); LYReduceBlanks(buffer); if (trailing) strcat(buffer, " "); } /* * Display (or hide) the status line. */ BOOLEAN mustshow = FALSE; void statusline(const char *text) { char buffer[MAX_LINE]; unsigned char *temp = NULL; int max_length, len, i, j; int at_lineno; unsigned char k; char *p; char text_buff[MAX_LINE]; if (text == NULL) return; /* * Don't print statusline messages if dumping to stdout. */ if (dump_output_immediately) return; /* * Don't print statusline message if turned off. */ if (mustshow != TRUE) { if (no_statusline == TRUE) { return; } } mustshow = FALSE; /* "LYNXDOWNLOAD://Method=-1/File=%s/SugFile=%s%s\">Save to disk</a>\n" */ LYStrNCpy(text_buff, text, sizeof(text_buff) - 1); p = StrChr(text_buff, '\n'); if (p) *p = '\0'; /* * Deal with any CJK escape sequences and Kanji if we have a CJK character * set selected, otherwise, strip any escapes. Also, make sure text is not * longer than the statusline window. - FM */ max_length = (((LYcolLimit - 1) < (int) sizeof(buffer)) ? (LYcolLimit - 1) : (int) sizeof(buffer) - 1); if ((text_buff[0] != '\0') && (LYHaveCJKCharacterSet)) { /* * Translate or filter any escape sequences. - FM */ if ((temp = typecallocn(unsigned char, strlen(text_buff) + 1)) == NULL) outofmem(__FILE__, "statusline"); if (kanji_code == EUC) { TO_EUC((const unsigned char *) text_buff, temp); } else if (kanji_code == SJIS) { #ifdef KANJI_CODE_OVERRIDE if (!LYRawMode || last_kcode == SJIS) strcpy(temp, text_buff); else TO_SJIS((const unsigned char *) text_buff, temp); #else strcpy((char *) temp, text_buff); #endif } else { for (i = 0, j = 0; text_buff[i]; i++) { if (text_buff[i] != CH_ESC) { /* S/390 -- gil -- 2119 */ temp[j++] = UCH(text_buff[i]); } } temp[j] = '\0'; } /* * Deal with any newlines or tabs in the string. - FM */ remove_most_blanks((char *) temp); /* * Handle the Kanji, making sure the text is not longer than the * statusline window. - FM */ for (i = 0, j = 0, len = 0, k = '\0'; temp[i] != '\0' && len < max_length; i++) { if (k != '\0') { buffer[j++] = (char) k; buffer[j++] = (char) temp[i]; k = '\0'; len += 2; } else if ((temp[i] & 0200) != 0) { k = temp[i]; } else { buffer[j++] = (char) temp[i]; len++; } } buffer[j] = '\0'; FREE(temp); } else { /* * Deal with any newlines or tabs in the string. - FM */ remove_most_blanks(text_buff); #ifdef WIDEC_CURSES len = (int) strlen(text_buff); if (len >= (int) (sizeof(buffer) - 1)) len = (int) (sizeof(buffer) - 1); LYStrNCpy(buffer, text_buff, len); /* FIXME: a binary search might be faster */ while (len > 0 && LYstrExtent(buffer, len, len) > max_length) buffer[--len] = '\0'; #else /* * Strip any escapes, and shorten text if necessary. Note that we * don't deal with the possibility of UTF-8 characters in the string. * This is unlikely, but if strings with such characters are used in * LYMessages_en.h, a compilation symbol of HAVE_UTF8_STATUSLINES could * be added there, and code added here for determining the displayed * string length, as we do above for CJK. - FM */ for (i = 0, len = 0; text_buff[i] != '\0' && len < max_length; i++) { if (text_buff[i] != CH_ESC) { /* S/390 -- gil -- 2119 */ buffer[len++] = text_buff[i]; } } buffer[len] = '\0'; #endif } /* * Move to the desired statusline window and output the text highlighted. * - FM */ if (LYStatusLine >= 0) { if (LYStatusLine < LYlines - 1) { at_lineno = LYStatusLine; } else { at_lineno = LYlines - 1; } } else if (user_mode == NOVICE_MODE) { at_lineno = LYlines - 3; } else { at_lineno = LYlines - 1; } LYmove(at_lineno, 0); LYclrtoeol(); if (buffer[0] != '\0') { BOOLEAN has_CJK = FALSE; if (IS_CJK_TTY) { for (i = 0; buffer[i] != '\0'; i++) { if (buffer[i] & 0x80) { has_CJK = TRUE; break; } } } if (has_CJK #ifdef HAVE_UTF8_STATUSLINES || IS_UTF8_TTY #endif ) { LYrefresh(); } #ifndef USE_COLOR_STYLE lynx_start_status_color(); LYaddstr(buffer); lynx_stop_status_color(); #else /* draw the status bar in the STATUS style */ { int y, x; int a = ((StrNCmp(buffer, ALERT_FORMAT, ALERT_PREFIX_LEN) || !hashStyles[s_alert].used) ? s_status : s_alert); LynxChangeStyle(a, STACK_ON); LYaddstr(buffer); wbkgdset(LYwin, ((lynx_has_color && LYShowColor >= SHOW_COLOR_ON) ? (chtype) hashStyles[a].color : A_NORMAL) | ' '); LYGetYX(y, x); (void) x; if (y == at_lineno) { LYclrtoeol(); } if (!(lynx_has_color && LYShowColor >= SHOW_COLOR_ON)) wbkgdset(LYwin, A_NORMAL | ' '); else if (s_normal != NOSTYLE) wbkgdset(LYwin, (chtype) (hashStyles[s_normal].color | ' ')); else wbkgdset(LYwin, (chtype) (displayStyles[DSTYLE_NORMAL].color | ' ')); LynxChangeStyle(a, STACK_OFF); } #endif } LYrefresh(); return; } static const char *novice_lines(int lineno) { switch (lineno) { case 0: return NOVICE_LINE_TWO_A; case 1: return NOVICE_LINE_TWO_B; case 2: return NOVICE_LINE_TWO_C; default: return ""; } } static int lineno = 0; void toggle_novice_line(void) { lineno++; if (*novice_lines(lineno) == '\0') lineno = 0; return; } void noviceline(int more_flag GCC_UNUSED) { if (dump_output_immediately) return; LYmove(LYlines - 2, 0); LYclrtoeol(); LYaddstr(NOVICE_LINE_ONE); LYParkCursor(); #if defined(DIRED_SUPPORT ) && defined(OK_OVERRIDE) if (lynx_edit_mode && !no_dired_support) LYaddstr(DIRED_NOVICELINE); else #endif /* DIRED_SUPPORT && OK_OVERRIDE */ if (LYUseNoviceLineTwo) LYaddstr(NOVICE_LINE_TWO); else LYaddstr(novice_lines(lineno)); LYrefresh(); return; } /* * If the standard input is not a tty, and Lynx is really reading from the * standard input, attempt to reopen it, pointing to a real tty. Normally * this would happen if the user pipes data to Lynx and wants to run * interactively after that. * * Returns: * 1 if successfully reopened * -1 if we cannot reopen * 0 if we do not have to reopen */ int LYReopenInput(void) { int result = 0; int fd; if ((fd = fileno(stdin)) == 0 && !isatty(fd) && LYConsoleInputFD(FALSE) == fd) { const char *term_name = NULL; int new_fd = -1; #ifdef HAVE_TTYNAME if (isatty(fileno(stdout)) && (term_name = ttyname(fileno(stdout))) != NULL) new_fd = open(term_name, O_RDONLY); if (new_fd == -1 && isatty(fileno(stderr)) && (term_name = ttyname(fileno(stderr))) != NULL) new_fd = open(term_name, O_RDONLY); #endif #ifdef HAVE_CTERMID if (new_fd == -1 && (term_name = ctermid(NULL)) != NULL) new_fd = open(term_name, O_RDONLY); #endif #ifdef TTY_DEVICE if (new_fd == -1) new_fd = open(term_name = TTY_DEVICE, O_RDONLY); #endif CTRACE((tfp, "LYReopenInput open(%s) returned %d.\n", term_name, new_fd)); if (new_fd >= 0) { FILE *frp; close(new_fd); frp = freopen(term_name, "r", stdin); CTRACE((tfp, "LYReopenInput freopen(%s,\"r\",stdin) returned %p, stdin is now %p with fd %d.\n", term_name, (void *) frp, (void *) stdin, fileno(stdin))); result = 1; } else { result = -1; } } return result; } /* * Returns the file descriptor from which keyboard input is expected, or INVSOC * (-1) if not available. If need_selectable is true, returns non-INVSOC fd * only if select() is possible - actually, currently only checks if fd is * connected to a tty. - kw */ int LYConsoleInputFD(int need_selectable) { int fd = INVSOC; #ifdef USE_SLANG if (!LYCursesON) fd = fileno(stdin); #if ((SLANG_VERSION >= 9919) && defined(REAL_UNIX_SYSTEM) && !defined(__CYGWIN__)) /* SLang_TT_Read_FD introduced in slang 0.99.19, from its changelog: * SLang_TT_Read_FD variable is now available for unix. This is the file * descriptor used by SLang_getkey. */ else fd = SLang_TT_Read_FD; #endif /* SLANG_VERSION >= 9919 */ #else /* !USE_SLANG */ fd = fileno(stdin); #endif /* !USE_SLANG */ if (need_selectable && fd != INVSOC) { if (isatty(fd)) { return fd; } else { return INVSOC; } } return fd; } static int fake_zap = 0; void LYFakeZap(int set) { if (set && fake_zap < 1) { CTRACE((tfp, "\r *** Set simulated 'Z'")); if (fake_zap) CTRACE((tfp, ", %d pending", fake_zap)); CTRACE((tfp, " ***\n")); fake_zap++; } else if (!set && fake_zap) { CTRACE((tfp, "\r *** Unset simulated 'Z'")); CTRACE((tfp, ", %d pending", fake_zap)); CTRACE((tfp, " ***\n")); fake_zap = 0; } } static int DontCheck(void) { static time_t last; time_t next; /** Curses or slang setup was not invoked **/ if (dump_output_immediately) return (TRUE); if (LYHaveCmdScript()) /* we may be running from a script */ return (TRUE); if (LYNoZapKey) return (TRUE); /* * Avoid checking interrupts more than one per second, since it is a slow * and expensive operation - TD */ #ifdef HAVE_GETTIMEOFDAY #undef timezone /* U/Win defines a conflicting macro */ { struct timeval tv; gettimeofday(&tv, (struct timezone *) 0); next = (tv.tv_sec * 10); next += (tv.tv_usec / 100000L); /* 0.1 seconds is a compromise */ } #else next = time((time_t *) 0); #endif if (next == last) return (TRUE); last = next; return FALSE; } int HTCheckForInterrupt(void) { int c; int cmd; if (fake_zap > 0) { fake_zap--; CTRACE((tfp, "\r *** Got simulated 'Z' ***\n")); CTRACE_FLUSH(tfp); CTRACE_SLEEP(AlertSecs); return ((int) TRUE); } /** Curses or slang setup was not invoked **/ if (DontCheck()) return ((int) FALSE); #ifndef VMS /* UNIX stuff: */ #if !defined(_WINDOWS) || defined(__MINGW32__) /* * First, check if there is a character. */ #ifdef USE_SLANG /** No keystroke was entered Note that this isn't taking possible SOCKSification and the socks_flag into account, and may fail on the slang library's select() when SOCKSified. - FM **/ #ifdef DJGPP_KEYHANDLER if (0 == _bios_keybrd(_NKEYBRD_READY)) return (FALSE); #else if (0 == SLang_input_pending(0)) return (FALSE); #endif /* DJGPP_KEYHANDLER */ #else /* Unix curses: */ { struct timeval socket_timeout; int ret = 0; fd_set readfds; socket_timeout.tv_sec = 0; socket_timeout.tv_usec = 0; FD_ZERO(&readfds); FD_SET(0, &readfds); #ifdef SOCKS if (socks_flag) ret = Rselect(1, &readfds, NULL, NULL, &socket_timeout); else #endif /* SOCKS */ ret = select(1, &readfds, NULL, NULL, &socket_timeout); /** Suspended? **/ if ((ret == -1) && (SOCKET_ERRNO == EINTR)) return ((int) FALSE); /** No keystroke was entered? **/ if (!FD_ISSET(0, &readfds)) return ((int) FALSE); } #endif /* USE_SLANG */ #endif /* !_WINDOWS */ /* * Now, read the character. */ #if defined(USE_CURSES_NODELAY) nodelay(LYwin, TRUE); c = LYgetch(); nodelay(LYwin, FALSE); #elif defined(USE_SLANG) && defined(_WINDOWS) if (!SLang_input_pending(0)) return ((int) FALSE); c = LYgetch(); #else c = LYgetch(); #endif #else /* VMS: */ extern int typeahead(void); /** Control-C or Control-Y and a 'N'o reply to exit query **/ if (HadVMSInterrupt) { HadVMSInterrupt = FALSE; return ((int) TRUE); } c = typeahead(); #endif /* !VMS */ /* * 'c' contains whatever character we're able to read from keyboard */ /** Keyboard 'Z' or 'z', or Control-G or Control-C **/ if (LYCharIsINTERRUPT(c)) return ((int) TRUE); /* There is a subset of mainloop() actions available at this stage: no new * getfile() cycle is possible until the previous finished. Currently we * have scrolling in partial mode, toggling of trace log, and pasting. * User search now in progress... */ cmd = (LKC_TO_LAC(keymap, c)); switch (cmd) { case LYK_TRACE_TOGGLE: /* Toggle TRACE mode. */ handle_LYK_TRACE_TOGGLE(); break; #ifdef CAN_CUT_AND_PASTE case LYK_TO_CLIPBOARD:{ /* ^S */ const char *s = LYDownLoadAddress(); if (!s || !*s || put_clip(s)) HTInfoMsg(gettext("Copy to clipboard failed.")); else HTInfoMsg(gettext("Download document URL put to clipboard.")); break; } #endif /* defined CAN_CUT_AND_PASTE */ default: #ifdef DISP_PARTIAL /* OK, we got several lines from new document and want to scroll... */ if (display_partial && (NumOfLines_partial > 2)) { BOOLEAN do_refresh; int res; int Newline_partial = LYGetNewline(); switch (cmd) { case LYK_WHEREIS: /* search within the document */ case LYK_NEXT: /* search for the next occurrence in the document */ case LYK_PREV: /* search for the previous occurrence in the document */ handle_LYK_WHEREIS(cmd, &do_refresh); if (www_search_result != -1) { Newline_partial = www_search_result; www_search_result = -1; /* reset */ } break; case LYK_FASTBACKW_LINK: if (Newline_partial <= (display_lines) + 1) { Newline_partial -= display_lines; } else if ((res = HTGetLinkOrFieldStart(-1, &Newline_partial, NULL, -1, TRUE)) == LINK_LINE_FOUND) { Newline_partial++; } else if (res == LINK_DO_ARROWUP) { Newline_partial -= display_lines; } break; case LYK_FASTFORW_LINK: if (HText_canScrollDown()) { /* This is not an exact science... - kw */ if (HTGetLinkOrFieldStart(HText_LinksInLines(HTMainText, Newline_partial, display_lines) - 1, &Newline_partial, NULL, 1, TRUE) == LINK_LINE_FOUND) { Newline_partial++; } } break; case LYK_PREV_PAGE: if (Newline_partial > 1) Newline_partial -= display_lines; break; case LYK_NEXT_PAGE: if (HText_canScrollDown()) Newline_partial += display_lines; break; case LYK_UP_HALF: if (Newline_partial > 1) Newline_partial -= (display_lines / 2); break; case LYK_DOWN_HALF: if (HText_canScrollDown()) Newline_partial += (display_lines / 2); break; case LYK_UP_TWO: if (Newline_partial > 1) Newline_partial -= 2; break; case LYK_DOWN_TWO: if (HText_canScrollDown()) Newline_partial += 2; break; case LYK_HOME: if (Newline_partial > 1) Newline_partial = 1; break; case LYK_END: if (HText_canScrollDown()) Newline_partial = HText_getNumOfLines() - display_lines + 1; /* calculate for "current" bottom value */ break; case LYK_REFRESH: break; default: /** Other or no keystrokes **/ return ((int) FALSE); } /* end switch */ if (Newline_partial < 1) Newline_partial = 1; if (LYMainLoop_pageDisplay(Newline_partial)) NumOfLines_partial = HText_getNumOfLines(); } #endif /* DISP_PARTIAL */ break; } /* end switch */ /** Other or no keystrokes **/ return ((int) FALSE); } /* * Check if the given filename looks like it's an absolute pathname, i.e., * references a directory. */ BOOLEAN LYisAbsPath(const char *path) { BOOLEAN result = FALSE; if (non_empty(path)) { #ifdef VMS result = TRUE; #else #if defined(USE_DOS_DRIVES) result = (BOOLEAN) (LYIsPathSep(path[0]) || (LYIsDosDrive(path) && LYIsPathSep(path[2]))); #else result = (BOOLEAN) (LYIsPathSep(path[0])); #endif /* USE_DOS_DRIVES */ #endif } return result; } /* * Check if the given filename is the root path, e.g., "/" on Unix. */ BOOLEAN LYisRootPath(const char *path) { #if defined(USE_DOS_DRIVES) if (strlen(path) == 3 && LYIsDosDrive(path) && LYIsPathSep(path[2])) return TRUE; #endif return (BOOL) ((strlen(path) == 1) && LYIsPathSep(path[0])); } /* * A file URL for a remote host is an obsolete ftp URL. * Return YES only if we're certain it's a local file. - FM */ BOOLEAN LYisLocalFile(const char *filename) { char *host = NULL; char *acc_method = NULL; char *cp; if (!filename) return NO; if (!(host = HTParse(filename, "", PARSE_HOST))) return NO; if (!*host) { FREE(host); return NO; } if ((cp = StrChr(host, ':')) != NULL) *cp = '\0'; if ((acc_method = HTParse(filename, "", PARSE_ACCESS))) { if (0 == strcmp("file", acc_method) && (0 == strcmp(host, "localhost") || LYSameFilename(host, HTHostName()))) { FREE(host); FREE(acc_method); return YES; } } FREE(host); FREE(acc_method); return NO; } /* * Utility for checking URLs with a host field. Return YES only if we're * certain it's the local host. - FM */ BOOLEAN LYisLocalHost(const char *filename) { char *host = NULL; char *cp; if (!filename) return NO; if (!(host = HTParse(filename, "", PARSE_HOST))) return NO; if (!*host) { FREE(host); return NO; } if ((cp = StrChr(host, ':')) != NULL) *cp = '\0'; if ((LYSameFilename(host, "localhost") || LYSameFilename(host, LYHostName) || LYSameFilename(host, HTHostName()))) { FREE(host); return YES; } FREE(host); return NO; } /* * Free an HTList that contains strings. */ void LYFreeStringList(HTList *list) { if (list != NULL) { char *argument; HTList *cur = list; while (NULL != (argument = (char *) HTList_nextObject(cur))) { FREE(argument); } HTList_delete(list); } } /* * Utility for freeing the list of local host aliases. - FM */ void LYLocalhostAliases_free(void) { LYFreeStringList(localhost_aliases); localhost_aliases = NULL; } /* * Utility for listing hosts to be treated as local aliases. - FM */ void LYAddLocalhostAlias(char *alias) { char *LocalAlias = NULL; if (!non_empty(alias)) return; if (!localhost_aliases) { localhost_aliases = HTList_new(); #ifdef LY_FIND_LEAKS atexit(LYLocalhostAliases_free); #endif } StrAllocCopy(LocalAlias, alias); HTList_addObject(localhost_aliases, LocalAlias); return; } /* * Utility for checking URLs with a host field. Return YES only if we've * listed the host as a local alias. - FM */ BOOLEAN LYisLocalAlias(const char *filename) { char *host = NULL; char *alias; char *cp; HTList *cur = localhost_aliases; if (!cur || !filename) return NO; if (!(host = HTParse(filename, "", PARSE_HOST))) return NO; if (!(*host)) { FREE(host); return NO; } if ((cp = StrChr(host, ':')) != NULL) *cp = '\0'; while (NULL != (alias = (char *) HTList_nextObject(cur))) { if (LYSameFilename(host, alias)) { FREE(host); return YES; } } FREE(host); return NO; } /* * This function checks for a URL with an unknown scheme, * but for which proxying has been set up, and if so, * returns PROXY_URL_TYPE. - FM * * If a colon is present but the string segment which * precedes it is not being proxied, and we can be sure * that what follows the colon is not a port field, * it returns UNKNOWN_URL_TYPE. Otherwise, it returns * 0 (not a URL). - FM */ UrlTypes LYCheckForProxyURL(char *filename) { char *cp = filename; char *cp1; char *cp2 = NULL; /* * Don't crash on an empty argument. */ if (isEmpty(cp)) return (NOT_A_URL_TYPE); /* kill beginning spaces */ cp = LYSkipBlanks(cp); /* * Check for a colon, and if present, * see if we have proxying set up. */ if ((cp1 = StrChr((cp + 1), ':')) != NULL) { if ((cp2 = StrChr((cp + 1), '/')) != NULL && cp2 < cp1) return (NOT_A_URL_TYPE); *cp1 = '\0'; cp2 = NULL; StrAllocCopy(cp2, cp); *cp1 = ':'; StrAllocCat(cp2, "_proxy"); if (LYGetEnv(cp2) != NULL) { FREE(cp2); return (PROXY_URL_TYPE); } FREE(cp2); #if defined (USE_DOS_DRIVES) if (LYIsDosDrive(cp)) return (NOT_A_URL_TYPE); #endif cp1++; if (!*cp) { return (NOT_A_URL_TYPE); } else if (isdigit(UCH(*cp1))) { while (*cp1 && isdigit(UCH(*cp1))) cp1++; if (*cp1 && !LYIsHtmlSep(*cp1)) return (UNKNOWN_URL_TYPE); } else { return (UNKNOWN_URL_TYPE); } } return (NOT_A_URL_TYPE); } /* * Compare a "type:" string, replacing it by the comparison-string if it * matches (and return true in that case). */ static BOOLEAN compare_type(char *tst, const char *cmp, size_t len) { if (!strncasecomp(tst, cmp, (int) len)) { if (StrNCmp(tst, cmp, len)) { size_t i; for (i = 0; i < len; i++) tst[i] = cmp[i]; } return TRUE; } return FALSE; } #define CompareType(tst,cmp,len) compare_type((tst),(cmp),(size_t)(len)) #define DoubleHtmlSep(s) (LYIsHtmlSep((s)[0]) && LYIsHtmlSep((s)[1])) #define compare_two(tst,cmp,len,limit) \ ((len + 2) <= limit \ && DoubleHtmlSep(tst + len) \ && CompareType(tst, cmp, len)) /* * Must recognize a URL and return the type. If recognized, based on a * case-insensitive analysis of the scheme field, ensures that the scheme field * has the expected case. * * Returns 0 (not a URL) for a NULL argument, one which lacks a colon. * * Chains to LYCheckForProxyURL() if a colon is present but the type is not * recognized. */ UrlTypes is_url(char *filename) { char *cp = filename; char *cp1; UrlTypes result = NOT_A_URL_TYPE; int limit; /* * Don't crash on an empty argument. */ if (isEmpty(cp)) return (result); /* * Can't be a URL if it lacks a colon and if it starts with '[' it's * probably IPv6 adress. */ if (NULL == StrChr(cp, ':') || cp[0] == '[') return (result); /* * Kill beginning spaces. */ cp = LYSkipBlanks(cp); /* * Can't be a URL if it starts with a slash. So return immediately for * this common case, also to avoid false positives if there was a colon * later in the string. Also can't be a URL if it starts with a colon. - * KW */ if (*cp == ':' || LYIsHtmlSep(*cp)) { result = NOT_A_URL_TYPE; } else { limit = (int) strlen(cp); switch (*cp) { case 'L': case 'l': /* * Lynx internal pages ("LYNXfoo:" or "lynxfoo:") start with 'l' or * 'L', other URLs aren't. */ if (CompareType(cp, STR_LYNXEXEC, LEN_LYNXEXEC)) { /* * Special External Lynx type to handle execution of commands * or scripts which require a pause to read the screen upon * completion. */ result = LYNXEXEC_URL_TYPE; } else if (CompareType(cp, STR_LYNXPROG, LEN_LYNXPROG)) { /* * Special External Lynx type to handle execution of commands, * scripts or programs which do not require a pause to read * screen upon completion. */ result = LYNXPROG_URL_TYPE; } else if (CompareType(cp, STR_LYNXCGI, LEN_LYNXCGI)) { result = LYNXCGI_URL_TYPE; } else if (CompareType(cp, STR_LYNXPRINT, LEN_LYNXPRINT)) { result = LYNXPRINT_URL_TYPE; } else if (CompareType(cp, STR_LYNXOPTIONS, LEN_LYNXOPTIONS)) { result = LYNXOPTIONS_URL_TYPE; } else if (CompareType(cp, STR_LYNXCFG, LEN_LYNXCFG)) { result = LYNXCFG_URL_TYPE; } else if (CompareType(cp, STR_LYNXMESSAGES, LEN_LYNXMESSAGES)) { result = LYNXMESSAGES_URL_TYPE; } else if (CompareType(cp, STR_LYNXCFLAGS, LEN_LYNXCFLAGS)) { result = LYNXCOMPILE_OPTS_URL_TYPE; } else if (CompareType(cp, STR_LYNXDOWNLOAD, LEN_LYNXDOWNLOAD)) { result = LYNXDOWNLOAD_URL_TYPE; } else if (CompareType(cp, STR_LYNXDIRED, LEN_LYNXDIRED)) { result = LYNXDIRED_URL_TYPE; } else if (CompareType(cp, STR_LYNXEDITMAP, LEN_LYNXEDITMAP)) { result = LYNXEDITMAP_URL_TYPE; } else if (CompareType(cp, STR_LYNXHIST, LEN_LYNXHIST)) { result = LYNXHIST_URL_TYPE; #ifdef USE_CACHEJAR } else if (CompareType(cp, STR_LYNXCACHE, LEN_LYNXCACHE)) { result = LYNXCACHE_URL_TYPE; #endif } else if (CompareType(cp, STR_LYNXKEYMAP, LEN_LYNXKEYMAP)) { result = LYNXKEYMAP_URL_TYPE; } else if (CompareType(cp, STR_LYNXIMGMAP, LEN_LYNXIMGMAP)) { /* force lower/uppercase of next part */ (void) is_url(&cp[LEN_LYNXIMGMAP]); result = LYNXIMGMAP_URL_TYPE; } else if (CompareType(cp, STR_LYNXCOOKIE, LEN_LYNXCOOKIE)) { result = LYNXCOOKIE_URL_TYPE; } break; #ifndef DISABLE_NEWS /* * NEWSfoo: schemes - */ case 'N': case 'n': if (CompareType(cp, STR_NEWS_URL, LEN_NEWS_URL)) { result = NEWS_URL_TYPE; } else if (CompareType(cp, STR_NNTP_URL, LEN_NNTP_URL)) { result = NNTP_URL_TYPE; } else if (CompareType(cp, "newspost:", 9)) { /* * Special Lynx type to handle news posts. */ result = NEWSPOST_URL_TYPE; } else if (CompareType(cp, "newsreply:", 10)) { /* * Special Lynx type to handle news replies (followups). */ result = NEWSREPLY_URL_TYPE; } break; /* * SNEWSfoo: schemes - */ case 'S': case 's': if (CompareType(cp, STR_SNEWS_URL, LEN_SNEWS_URL)) { result = SNEWS_URL_TYPE; } else if (CompareType(cp, "snewspost:", 10)) { /* * Special Lynx type to handle snews posts. */ result = NEWSPOST_URL_TYPE; } else if (CompareType(cp, "snewsreply:", 11)) { /* * Special Lynx type to handle snews replies (followups). */ result = NEWSREPLY_URL_TYPE; } break; #endif case 'M': case 'm': if (CompareType(cp, STR_MAILTO_URL, LEN_MAILTO_URL)) { result = MAILTO_URL_TYPE; } break; case 'F': case 'f': if (CompareType(cp, STR_FILE_URL, LEN_FILE_URL)) { if (LYisLocalFile(cp)) { result = FILE_URL_TYPE; } else if (DoubleHtmlSep(cp + LEN_FILE_URL)) { result = FTP_URL_TYPE; } } #ifndef DISABLE_FTP else if (compare_two(cp, STR_FTP_URL, LEN_FTP_URL, limit)) { result = FTP_URL_TYPE; } #endif #ifndef DISABLE_FINGER else if (compare_two(cp, STR_FINGER_URL, LEN_FINGER_URL, limit)) { result = FINGER_URL_TYPE; } #endif break; case 'B': case 'b': #ifndef DISABLE_BIBP if (CompareType(cp, STR_BIBP_URL, LEN_BIBP_URL)) { result = BIBP_URL_TYPE; } #endif break; case 'D': case 'd': if (CompareType(cp, "data:", 5)) { result = DATA_URL_TYPE; } break; default: if (limit >= 3 && ((cp1 = StrChr(cp + 3, ':')) == NULL || !DoubleHtmlSep(cp1 + 1))) { /* * If it doesn't contain "://", and it's not one of the the * above, it can't be a URL with a scheme we know, so check if * it's an unknown scheme for which proxying has been set up. * - FM */ if (cp1 != NULL && (cp1 - cp) > 1 /* exclude DOS-style device:/path */ && LYisAbsPath(cp1 + 1)) { result = NCFTP_URL_TYPE; } } else { switch (*cp) { case 'H': case 'h': if (CompareType(cp, STR_HTTP_URL, LEN_HTTP_URL)) { result = HTTP_URL_TYPE; } else if (CompareType(cp, STR_HTTPS_URL, LEN_HTTPS_URL)) { result = HTTPS_URL_TYPE; } break; #ifndef DISABLE_GOPHER case 'G': case 'g': if (CompareType(cp, STR_GOPHER_URL, LEN_GOPHER_URL)) { if (strlen(cp) >= 11 && (cp1 = StrChr(cp + 11, '/')) != NULL) { if (TOUPPER(*(cp1 + 1)) == 'H' || *(cp1 + 1) == 'w') /* if this is a gopher html type */ result = HTML_GOPHER_URL_TYPE; else if (*(cp1 + 1) == 'T' || *(cp1 + 1) == '8') result = TELNET_GOPHER_URL_TYPE; else if (*(cp1 + 1) == '7') result = INDEX_GOPHER_URL_TYPE; else result = GOPHER_URL_TYPE; } else { result = GOPHER_URL_TYPE; } } break; #endif case 'W': case 'w': if (CompareType(cp, STR_WAIS_URL, LEN_WAIS_URL)) { result = WAIS_URL_TYPE; } break; case 'T': case 't': if (CompareType(cp, STR_TELNET_URL, LEN_TELNET_URL)) { result = TELNET_URL_TYPE; } else if (CompareType(cp, STR_TN3270_URL, LEN_TN3270_URL)) { result = TN3270_URL_TYPE; } break; case 'R': case 'r': if (CompareType(cp, STR_RLOGIN_URL, LEN_RLOGIN_URL)) { result = RLOGIN_URL_TYPE; } break; case 'C': case 'c': if (CompareType(cp, STR_CSO_URL, LEN_CSO_URL)) { result = CSO_URL_TYPE; } break; case 'A': case 'a': if (CompareType(cp, "afs:", 4)) { result = AFS_URL_TYPE; } break; case 'P': case 'p': if (CompareType(cp, "prospero:", 9)) { result = PROSPERO_URL_TYPE; } break; } } } /* * Check if it is an unknown scheme for which proxying has been set up. */ if (result == NOT_A_URL_TYPE) result = LYCheckForProxyURL(filename); } return result; } /* * Sometimes it is just expected that curses is on when an alert or other * statusline message needs to be shown and we are not just dumping * immediately. Calling this will 'fix' it, but may not always be appropriate. * - kw */ void LYFixCursesOn(const char *reason) { if (dump_output_immediately || LYCursesON) return; if (reason) { CTRACE((tfp, "Forcing curses on to %s\n", reason)); } start_curses(); } /* * Most protocol modules called through HTLoad* expect that curses is on unless * dump_output_immediately is set, so that statusline messages can be shown. * Some protocols expect the opposite, namely telnet and friends. This * function should be called after the 'physical' URL for accessing addr has * been established. It does the right thing to the degree that curses is * turned on for known problem cases. In any normal circumstances this should * never apply, but proxying or rule substitution is not prevented for * telnet-like URLs, and this 'fix' avoids some crashes that can otherwise * occur. - kw */ BOOLEAN LYFixCursesOnForAccess(const char *addr, const char *physical) { /* * If curses is off when maybe it shouldn't... */ if (!dump_output_immediately && !LYCursesON && physical) { char *cp1; /* * If requested resource wants to be accessed with curses off, and * getfile() would indeed have turned curses off for it... */ if (strstr(addr, "://") != NULL && (isTELNET_URL(addr) || isRLOGIN_URL(addr) || isTN3270_URL(addr) || (!isGOPHER_URL(addr) && (cp1 = StrChr(addr + 11, '/')) != NULL && (*(cp1 + 1) == 'T' || *(cp1 + 1) == '8')))) { /* * If actual access that will be done is ok with curses off, then * do nothing special, else force curses on. - kw */ if (!isTELNET_URL(physical) && !isRLOGIN_URL(physical) && !isTN3270_URL(physical)) { start_curses(); HTAlert(gettext("Unexpected access protocol for this URL scheme.")); return TRUE; } } } return FALSE; } /* * Determine whether we allow HEAD and related flags for a URL. - kw */ BOOLEAN LYCanDoHEAD(const char *address) { char *temp0 = NULL; int isurl; if (!non_empty(address)) return FALSE; if (!StrNCmp(address, "http", 4)) return TRUE; /* Make copy for is_url() since caller may not care for case changes */ StrAllocCopy(temp0, address); isurl = is_url(temp0); if (!isurl) { FREE(temp0); return FALSE; } if (isurl == LYNXCGI_URL_TYPE) { FREE(temp0); #if defined(LYNXCGI_LINKS) && !defined(VMS) return TRUE; #else return FALSE; #endif } /* * The idea of the following is to allow HEAD for news URLs that identify * single articles, not those that identify ranges of articles or groups or * a list of groups. - kw */ if (isurl == NEWS_URL_TYPE || isurl == NNTP_URL_TYPE) { char *temp = HTParse(address, "", PARSE_PATH); char *cp = strrchr(temp, '/'); if (StrChr((cp ? cp : temp), '@') != NULL) { FREE(temp0); FREE(temp); return TRUE; } if (cp && isdigit(UCH(cp[1])) && StrChr(cp, '-') == NULL) { FREE(temp0); FREE(temp); return TRUE; } FREE(temp); } #define ALLOW_PROXY_HEAD /* If defined, also allow head requests for URLs proxied through the "http" or * "lynxcgi" protocols, which understand HEAD. Only the proxy environment * variables are checked, not the HTRules system. - kw */ #ifdef ALLOW_PROXY_HEAD if (isurl != FILE_URL_TYPE) { char *acc_method = HTParse(temp0, "", PARSE_ACCESS); if (non_empty(acc_method)) { char *proxy; StrAllocCat(acc_method, "_proxy"); proxy = LYGetEnv(acc_method); if (proxy && (isHTTP_URL(proxy) || isLYNXCGI(proxy)) && !override_proxy(temp0)) { FREE(temp0); FREE(acc_method); return TRUE; } } FREE(acc_method); } #endif /* ALLOW_PROXY_HEAD */ FREE(temp0); return FALSE; } /* * Close an input file. */ BOOLEAN LYCloseInput(FILE *fp) { int result = FALSE; if (fp != 0) { int err = ferror(fp); LY_TEMP *p = FindTempfileByFP(fp); fclose(fp); if (p != 0) { p->file = 0; } if (!err) { result = TRUE; } } return (BOOLEAN) result; } /* * Close an output file, reporting any problems with writing to it. */ BOOLEAN LYCloseOutput(FILE *fp) { int result = FALSE; if (fp != 0) { int err = ferror(fp); LY_TEMP *p = FindTempfileByFP(fp); fclose(fp); if (p != 0) { p->file = 0; } if (!err) { result = TRUE; } } if (!result) { HTAlert(CANNOT_WRITE_TO_FILE); } return (BOOLEAN) result; } /* * Test if we'll be able to write a file. If not, warn the user. */ BOOLEAN LYCanWriteFile(const char *filename) { BOOLEAN result = FALSE; if (LYCloseOutput(fopen(filename, "w"))) { if (remove(filename) == 0) { result = TRUE; } } else { _statusline(NEW_FILENAME_PROMPT); } return result; } /* * Test if we'll be able to read a file. */ BOOLEAN LYCanReadFile(const char *filename) { FILE *fp; BOOLEAN result = FALSE; if (non_empty(filename)) { if ((fp = fopen(filename, "r")) != 0) { result = LYCloseInput(fp); } } return result; } char *LYFindConfigFile(const char *nominal, const char *dftfile) { char *result = 0; char *path = 0; char *head = 0; char *leaf; char *item; if (non_empty(nominal)) { StrAllocCopy(result, nominal); /* * Look for it in as-is - first expanding any tilde. */ LYTildeExpand(&result, TRUE); if (!LYCanReadFile(result)) { const char *cfg_path; char *list = 0; BOOLEAN found = FALSE; /* * Now try in the config-path. */ if ((cfg_path = LYGetEnv("LYNX_CFG_PATH")) == NULL) cfg_path = LYNX_CFG_PATH; StrAllocCopy(list, cfg_path); path = list; while ((item = LYstrsep(&path, PATH_SEPARATOR)) != 0) { if (isEmpty(item)) continue; FREE(result); HTSprintf0(&result, "%s%s%s", item, FILE_SEPARATOR, nominal); LYTildeExpand(&result, TRUE); if (LYCanReadFile(result)) { found = TRUE; break; } } FREE(list); if (!found) { /* * If not found, try finding it in the same directory as the * compiled-in location of the default file. */ StrAllocCopy(head, dftfile); if (strcmp(nominal, dftfile) && (leaf = LYPathLeaf(head)) != head) { head[leaf - head] = '\0'; StrAllocCopy(result, head); StrAllocCat(result, nominal); if (!LYCanReadFile(result)) { FREE(result); } } #ifdef USE_PROGRAM_DIR else { /* * Finally, try in the same directory as the executable. */ StrAllocCopy(result, program_dir); LYAddPathSep(&result); StrAllocCat(result, nominal); LYTildeExpand(&result, TRUE); if (!LYCanReadFile(result)) { FREE(result); } } #endif } } } FREE(head); return result; } /* * Remove backslashes from any string. */ void remove_backslashes(char *buf) { char *cp; for (cp = buf; *cp != '\0'; cp++) { if (*cp != '\\') { /* don't print slashes */ *buf = *cp; buf++; } else if (*cp == '\\' && /* print one slash if there */ *(cp + 1) == '\\') { /* are two in a row */ *buf = *cp; buf++; } } *buf = '\0'; return; } /* * Checks to see if the current process is attached via a terminal in the local * domain. */ BOOLEAN inlocaldomain(void) { BOOLEAN result = TRUE; #ifdef HAVE_UTMP int n; FILE *fp; struct utmp me; char *cp, *mytty = NULL; if ((cp = ttyname(0))) { mytty = cp; if (!strncmp(mytty, "/dev/", 5)) { mytty += 5; /* pty's can be like "pts/0" in utmp */ } else { if ((mytty = LYLastPathSep(cp)) != 0) ++mytty; } } result = FALSE; if (mytty && (fp = fopen(UTMP_FILE, "r")) != NULL) { size_t ulen = strlen(mytty); if (ulen > sizeof(me.ut_line)) ulen = sizeof(me.ut_line); do { n = (int) fread((char *) &me, sizeof(struct utmp), (size_t) 1, fp); if (n <= 0) break; } while (memcmp(me.ut_line, mytty, ulen)); (void) LYCloseInput(fp); if (n > 0) { for (ulen = 0; ulen < sizeof(me.ut_host); ++ulen) { if (me.ut_host[ulen] == '\0') break; } if (ulen > strlen(LYLocalDomain) && STREQ(LYLocalDomain, me.ut_host + ulen - strlen(LYLocalDomain))) { result = TRUE; } #ifdef LINUX /* Linux fix to check for local user. J.Cullen 11Jul94 */ else if (strlen(me.ut_host) == 0) { result = TRUE; } #endif /* LINUX */ } } else { CTRACE((tfp, "Could not get ttyname (returned %s) or open UTMP file %s\n", NONNULL(cp), UTMP_FILE)); } #else CTRACE((tfp, "LYUtils: inlocaldomain() not supported.\n")); #endif /* HAVE_UTMP */ return (result); } #ifdef HAVE_SIGACTION /* * An extended alternative for calling signal(), sets some flags for signal * handler as we want them if that functionality is available. (We don't * return anything from this function since the return value would currently be * ignored anyway.) - kw */ void LYExtSignal(int sig, LYSigHandlerFunc_t *handler) { #ifdef SIGWINCH /* add more cases to if(condition) if required... */ if (sig == SIGWINCH && LYNonRestartingSIGWINCH) { struct sigaction act; act.sa_handler = handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(sig, &act, NULL); } else #endif /* defined(SIGWINCH) */ signal(sig, handler); } #endif /* HAVE_SIGACTION */ #if defined(SIGTSTP) && !defined(USE_SLANG) #ifdef HAVE_SIGACTION /* * For switching a signal's handling between SIG_DFL and something (possibly) * different that may have been set up by lynx code or e.g. by curses library. * Uses sigaction to preserve / restore as much state as possible. * * Second arg is where to save or restore from. * * Third arg to_dfl specifies what to do: * 1 Save current state in where, set handling to SIG_DFL * 0 Restore current state to previously saved one in where * * Currently only used for SIGTSTP without SLANG, to prevent (n)curses signal * handler from running while lynx is waiting in system() for an interactive * command like an editor. - kw */ static BOOLEAN LYToggleSigDfl(int sig, struct sigaction *where, int to_dfl) { int rv = -1; struct sigaction oact; if (to_dfl == 1) { rv = sigaction(sig, NULL, &oact); if (rv == 0) { if (oact.sa_handler != SIG_DFL) { oact.sa_handler = SIG_DFL; rv = sigaction(sig, &oact, where); } else if (where) { memcpy(where, &oact, sizeof(oact)); rv = 0; } } } else { rv = sigaction(sig, where, NULL); } if (rv != 0) { CTRACE((tfp, "Error in LYToggleSigDfl: %s\n", LYStrerror(errno))); return FALSE; } else return TRUE; } #endif /* HAVE_SIGACTION */ #endif /* SIGTSTP && !USE_SLANG */ /************** * This bit of code catches window size change signals */ #ifdef HAVE_SYS_IOCTL_H #include <sys/ioctl.h> #endif /* For systems that have both, but both can't be included, duh (or neither) */ /* FIXME: this whole chunk may be redundant */ #ifdef TERMIO_AND_CURSES # ifdef TERMIO_AND_TERMIOS # include <termio.h> # else # ifdef HAVE_TERMIOS_H # include <termios.h> # else # ifdef HAVE_TERMIO_H # include <termio.h> # endif /* HAVE_TERMIO_H */ # endif /* HAVE_TERMIOS_H */ # endif /* TERMIO_AND_TERMIOS */ #endif /* TERMIO_AND_CURSES */ void size_change(int sig GCC_UNUSED) { int old_lines = LYlines; int old_cols = LYcols; #ifdef USE_SLANG #if defined(VMS) || defined(UNIX) SLtt_get_screen_size(); #endif /* VMS || UNIX */ LYlines = SLtt_Screen_Rows; LYcols = SLtt_Screen_Cols; if (sig == 0) /* * Called from start_curses(). */ return; #else /* Curses: */ #ifdef 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, (long) 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 */ #ifdef __EMX__ { int scrsize[2]; _scrsize(scrsize); LYcols = scrsize[0]; LYlines = scrsize[1]; } #endif if (LYlines <= 0) LYlines = DFT_ROWS; if (LYcols <= 0) LYcols = DFT_COLS; #endif /* USE_SLANG */ /* * Check if the screen size has actually changed. - AJL */ if (LYlines != old_lines || LYcols != old_cols) { recent_sizechange = TRUE; CTRACE((tfp, "Window size changed from (%d,%d) to (%d,%d)\n", old_lines, old_cols, LYlines, LYcols)); #if defined(CAN_SWITCH_DISPLAY_CHARSET) && defined(CAN_AUTODETECT_DISPLAY_CHARSET) /* May need to reload the font due to different char-box size */ if (current_char_set != auto_display_charset) Switch_Display_Charset(current_char_set, SWITCH_DISPLAY_CHARSET_RESIZE); #endif } #ifdef SIGWINCH LYExtSignal(SIGWINCH, size_change); #endif /* SIGWINCH */ return; } /* * Utility for freeing the list of previous suggested filenames. - FM */ void HTSugFilenames_free(void) { LYFreeStringList(sug_filenames); sug_filenames = NULL; } /* * Utility for listing suggested filenames, making any repeated filenames the * most current in the list. - FM */ void HTAddSugFilename(char *fname) { char *tmp = NULL; char *old; HTList *cur; if (!non_empty(fname)) return; StrAllocCopy(tmp, fname); if (!sug_filenames) { sug_filenames = HTList_new(); #ifdef LY_FIND_LEAKS atexit(HTSugFilenames_free); #endif HTList_addObject(sug_filenames, tmp); return; } cur = sug_filenames; while (NULL != (old = (char *) HTList_nextObject(cur))) { if (!strcmp(old, tmp)) { HTList_removeObject(sug_filenames, old); FREE(old); break; } } HTList_addObject(sug_filenames, tmp); return; } /* * CHANGE_SUG_FILENAME -- Foteos Macrides 29-Dec-1993 Upgraded for use with * Lynx2.2 - FM 17-Jan-1994 */ void change_sug_filename(char *fname) { const char *cp2; char *temp = 0, *cp, *cp1, *end; #ifdef VMS char *dot; int j, k; #endif /* VMS */ /* * Establish the current end of fname. */ end = fname + strlen(fname); /* * Unescape fname. */ HTUnEscape(fname); /* * Rename any temporary files. */ cp2 = wwwName(lynx_temp_space); if (LYIsHtmlSep(*cp2)) { HTSprintf0(&temp, "file://localhost%s" PID_FMT, cp2, GETPID()); } else { HTSprintf0(&temp, "file://localhost/%s" PID_FMT, cp2, GETPID()); } if (!StrNCmp(fname, temp, strlen(temp))) { if ((cp = strrchr(fname, '.')) != 0) { if (strlen(cp) > (strlen(temp) - 4)) cp = NULL; } StrAllocCopy(temp, NonNull(cp)); sprintf(fname, "temp%.*s", LY_MAXPATH - 10, temp); } FREE(temp); if (fname[strlen(fname) - 1] == '/') /* * Hmm... we have a directory name. It is annoying to see a * scheme+host+path name as a suggested one, let's remove the * last_slash and go ahead like we have a file name. - LP */ fname[strlen(fname) - 1] = '\0'; /* * Remove everything up the the last_slash if there is one. */ if ((cp = strrchr(fname, '/')) != NULL && strlen(cp) > 1) { cp1 = fname; /* * Go past the slash. */ cp++; for (; *cp != '\0'; cp++, cp1++) { *cp1 = *cp; } *cp1 = '\0'; } #ifdef _WINDOWS /* 1998/05/05 (Tue) 10:08:05 */ if ((cp = strrchr(fname, '=')) != NULL && strlen(cp) > 1) { cp1 = fname; /* * Go past the '='. */ cp++; for (; *cp != '\0'; cp++, cp1++) { *cp1 = *cp; } *cp1 = '\0'; } #endif /* * Trim off date-size suffix, if present. */ if ((*(end - 1) == ']') && ((cp = strrchr(fname, '[')) != NULL) && (cp > fname) && *(--cp) == ' ') { while (*cp == ' ') { *(cp--) = '\0'; } } #ifdef VMS /* * Trim off VMS device and/or directory specs, if present. */ if ((cp = StrChr(fname, '[')) != NULL && (cp1 = strrchr(cp, ']')) != NULL && strlen(cp1) > 1) { cp1++; for (cp = fname; *cp1 != '\0'; cp1++) { *(cp++) = *cp1; } *cp = '\0'; } /* * Replace illegal or problem characters. */ dot = fname + strlen(fname); for (cp = fname; cp < dot; cp++) { /* * Replace with underscores. */ if (*cp == ' ' || *cp == '/' || *cp == ':' || *cp == '[' || *cp == ']' || *cp == '&') { *cp = '_'; /* * Replace with dashes. */ } else if (*cp == '!' || *cp == '?' || *cp == '\'' || *cp == ',' || *cp == ':' || *cp == '"' || *cp == '+' || *cp == '@' || *cp == '\\' || *cp == '(' || *cp == ')' || *cp == '=' || *cp == '<' || *cp == '>' || *cp == '#' || *cp == '%' || *cp == '*' || *cp == '`' || *cp == '~' || *cp == '^' || *cp == '|' || *cp < ' ' || (UCH(*cp)) > 126) { *cp = '-'; } } /* * Collapse any serial underscores. */ cp = fname + 1; j = 0; while (cp < dot) { if (fname[j] == '_' && *cp == '_') { cp++; } else { fname[++j] = *cp++; } } fname[++j] = '\0'; /* * Collapse any serial dashes. */ dot = fname + (strlen(fname)); cp = fname + 1; j = 0; while (cp < dot) { if (fname[j] == '-' && *cp == '-') { cp++; } else { fname[++j] = *cp++; } } fname[++j] = '\0'; /* * Trim any trailing or leading underscores or dashes. */ cp = fname + (strlen(fname)) - 1; while (*cp == '_' || *cp == '-') { *cp-- = '\0'; } if (fname[0] == '_' || fname[0] == '-') { dot = fname + (strlen(fname)); cp = fname; while ((*cp == '_' || *cp == '-') && cp < dot) { cp++; } j = 0; while (cp < dot) { fname[j++] = *cp++; } fname[j] = '\0'; } /* * Replace all but the last period with _'s, or second to last if last is * followed by a terminal Z or z, or GZ or gz, * e.g., convert foo.tar.Z to foo.tar_Z * or, convert foo.tar.gz to foo.tar-gz */ j = strlen(fname) - 1; if ((dot = strrchr(fname, '.')) != NULL) { if (TOUPPER(fname[j]) == 'Z') { if ((fname[j - 1] == '.') && (((cp = StrChr(fname, '.')) != NULL) && cp < dot)) { *dot = '_'; dot = strrchr(fname, '.'); } else if (((TOUPPER(fname[j - 1]) == 'G') && fname[j - 2] == '.') && (((cp = StrChr(fname, '.')) != NULL) && cp < dot)) { *dot = '-'; dot = strrchr(fname, '.'); } } cp = fname; while ((cp = StrChr(cp, '.')) != NULL && cp < dot) { *cp = '_'; } /* * But if the root is > 39 characters, move the period appropriately to * the left. */ while (dot - fname > 39) { *dot = '\0'; if ((cp = strrchr(fname, '_')) != NULL) { *cp = '.'; *dot = '_'; } else if ((cp = strrchr(fname, '-')) != NULL) { *cp = '.'; *dot = '_'; } else if (*(dot + 1) == '\0') { j = strlen(fname); while (j > 39) { fname[j] = fname[j - 1]; j--; } fname[j] = '.'; } else { *dot = '.'; j = 39; k = 0; while (dot[k] != '\0') { fname[j++] = dot[k++]; } fname[j] = '\0'; } dot = strrchr(fname, '.'); } /* * Make sure the extension is < 40 characters. */ if ((fname + strlen(fname) - dot) > 39) { *(dot + 40) = '\0'; } /* * Trim trailing dashes or underscores. */ j = (strlen(fname) - 1); while (fname[j] == '_' || fname[j] == '-') { fname[j--] = '\0'; } } else { /* * No period, so put one on the end, or after the 39th character, * trimming trailing dashes or underscores. */ if (strlen(fname) > 39) { fname[39] = '\0'; } j = (strlen(fname) - 1); while ((fname[j] == '_') || (fname[j] == '-')) { j--; } fname[++j] = '.'; fname[++j] = '\0'; } #else /* Not VMS (UNIX): */ /* * Replace problem characters. */ for (cp = fname; *cp != '\0'; cp++) { switch (*cp) { case '\'': case '"': case '/': case ' ': *cp = '-'; } } #endif /* VMS (UNIX) */ /* * Make sure the rest of the original string in nulled. */ cp = fname + strlen(fname); while (cp < end) { *cp++ = '\0'; } return; } /* * Construct a temporary-filename. Assumes result is LY_MAXPATH chars long. */ static int fmt_tempname(char *result, const char *prefix, const char *suffix) { int code; #ifdef HAVE_RAND_TEMPNAME #define SIZE_TEMPNAME ((MAX_TEMPNAME / BITS_PER_CHAR) + 1) static BOOL first = TRUE; static int names_used = 0; static unsigned char used_tempname[SIZE_TEMPNAME]; unsigned offset, mask; #endif static unsigned counter; char leaf[LY_MAXPATH]; if (prefix == 0) prefix = ""; if (suffix == 0) suffix = ""; /* * Prefer a random value rather than a counter. */ #ifdef HAVE_RAND_TEMPNAME if (first) { lynx_srand((unsigned) ((long) time((time_t *) NULL) + (long) result)); first = FALSE; } /* We don't really need all of the bits from rand(). The high-order bits * are the more-random portion in any case, but limiting the width of the * generated name is done partly to avoid problems on systems that may not * support long filenames. */ counter = MAX_TEMPNAME; if (names_used < MAX_TEMPNAME) { long get_rand = lynx_rand(); long max_rand = LYNX_RAND_MAX; counter = (unsigned) (((float) MAX_TEMPNAME * (float) get_rand) / (float) max_rand + 1); /* * Avoid reusing a temporary name, since there are places in the code * which can refer to a temporary filename even after it has been * closed and removed from the filesystem. */ do { counter %= MAX_TEMPNAME; offset = counter / BITS_PER_CHAR; mask = (unsigned) (1 << (counter % BITS_PER_CHAR)); if ((used_tempname[offset] & mask) == 0) { names_used++; used_tempname[offset] |= UCH(mask); break; } } while ((used_tempname[offset] & mask) == 0); } if (names_used >= MAX_TEMPNAME) HTAlert(gettext("Too many tempfiles")); #else counter++; #endif #ifdef FNAMES_8_3 /* * The 'lynx_temp_space' string ends with a '/' or '\\', so we only have to * limit the length of the leaf. As received (e.g., from HTCompressed), * the suffix may contain more than a ".htm", e.g., "-txt.gz", so we trim * off from the filename portion to make room. */ sprintf(leaf, PID_FMT PID_FMT, counter, GETPID()); if (strlen(leaf) > 8) leaf[8] = 0; if (strlen(suffix) > 4 || *suffix != '.') { const char *tail = StrChr(suffix, '.'); if (tail == 0) tail = suffix + strlen(suffix); if (8 - (tail - suffix) >= 0) leaf[8 - (tail - suffix)] = 0; } strcat(leaf, suffix); #else sprintf(leaf, "L" PID_FMT "-%uTMP%s", GETPID(), counter, suffix); #endif /* * Someone could have configured the temporary pathname to be too long. */ if ((strlen(prefix) + strlen(leaf)) < LY_MAXPATH) { sprintf(result, "%s%s", prefix, leaf); code = TRUE; } else { sprintf(result, "%.*s", LY_MAXPATH - 1, leaf); code = FALSE; } CTRACE((tfp, "-> '%s'\n", result)); return (code); } /* * Convert 4, 6, 2, 8 to left, right, down, up, etc. */ int number2arrows(int number) { switch (number) { case '1': number = END_KEY; break; case '2': number = DNARROW_KEY; break; case '3': number = PGDOWN_KEY; break; case '4': number = LTARROW_KEY; break; case '5': number = DO_NOTHING; break; case '6': number = RTARROW_KEY; break; case '7': number = HOME_KEY; break; case '8': number = UPARROW_KEY; break; case '9': number = PGUP_KEY; break; } return (number); } /* * parse_restrictions takes a string of comma-separated restrictions and sets * the corresponding flags to restrict the facilities available. */ /* The first two are special: we want to record whether "default" or "all" * restrictions were applied, in addition to the detailed effects of those * options. - kw */ /* skip the special flags when processing "all" and "default": */ #define N_SPECIAL_RESTRICT_OPTIONS 2 /* *INDENT-OFF* */ static const struct { const char *name; BOOLEAN *flag; BOOLEAN can; } restrictions[] = { { "default", &had_restrictions_default, TRUE }, { "all", &had_restrictions_all, TRUE }, { "inside_telnet", &no_inside_telnet, CAN_ANONYMOUS_INSIDE_DOMAIN_TELNET }, { "outside_telnet", &no_outside_telnet, CAN_ANONYMOUS_OUTSIDE_DOMAIN_TELNET }, { "telnet_port", &no_telnet_port, CAN_ANONYMOUS_GOTO_TELNET_PORT }, { "inside_ftp", &no_inside_ftp, CAN_ANONYMOUS_INSIDE_DOMAIN_FTP }, { "outside_ftp", &no_outside_ftp, CAN_ANONYMOUS_OUTSIDE_DOMAIN_FTP }, { "inside_rlogin", &no_inside_rlogin, CAN_ANONYMOUS_INSIDE_DOMAIN_RLOGIN }, { "outside_rlogin", &no_outside_rlogin, CAN_ANONYMOUS_OUTSIDE_DOMAIN_RLOGIN }, { "suspend", &no_suspend, FALSE }, { "editor", &no_editor, FALSE }, { "shell", &no_shell, FALSE }, { "bookmark", &no_bookmark, FALSE }, { "multibook", &no_multibook, FALSE }, { "bookmark_exec", &no_bookmark_exec, FALSE }, { "option_save", &no_option_save, FALSE }, { "print", &no_print, CAN_ANONYMOUS_PRINT }, { "download", &no_download, FALSE }, { "disk_save", &no_disk_save, FALSE }, #if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS) { "exec", &no_exec, LOCAL_EXECUTION_LINKS_ALWAYS_OFF_FOR_ANONYMOUS }, #endif { "lynxcgi", &no_lynxcgi, FALSE }, { "exec_frozen", &exec_frozen, FALSE }, { "goto", &no_goto, CAN_ANONYMOUS_GOTO }, { "jump", &no_jump, CAN_ANONYMOUS_JUMP }, { "file_url", &no_file_url, FALSE }, #ifndef DISABLE_NEWS { "news_post", &no_newspost, FALSE }, { "inside_news", &no_inside_news, CAN_ANONYMOUS_INSIDE_DOMAIN_READ_NEWS }, { "outside_news", &no_outside_news, CAN_ANONYMOUS_OUTSIDE_DOMAIN_READ_NEWS }, #endif { "mail", &no_mail, CAN_ANONYMOUS_MAIL }, { "dotfiles", &no_dotfiles, FALSE }, { "useragent", &no_useragent, FALSE }, #ifdef SUPPORT_CHDIR { "chdir", &no_chdir, FALSE }, #endif #ifdef DIRED_SUPPORT { "dired_support", &no_dired_support, FALSE }, #ifdef OK_PERMIT { "change_exec_perms", &no_change_exec_perms, FALSE }, #endif /* OK_PERMIT */ #endif /* DIRED_SUPPORT */ #ifdef USE_EXTERNALS { "externals", &no_externals, FALSE }, #endif { "lynxcfg_info", &no_lynxcfg_info, CAN_ANONYMOUS_VIEW_LYNXCFG_INFO }, #ifndef NO_CONFIG_INFO { "lynxcfg_xinfo", &no_lynxcfg_xinfo, CAN_ANONYMOUS_VIEW_LYNXCFG_EXTENDED_INFO }, #ifdef HAVE_CONFIG_H { "compileopts_info", &no_compileopts_info, CAN_ANONYMOUS_VIEW_COMPILEOPTS_INFO }, #endif #endif /* put "goto" restrictions on the end, since they are a refinement */ #ifndef DISABLE_BIBP { "goto_bibp", &no_goto_bibp, CAN_ANONYMOUS_GOTO_BIBP }, #endif #ifdef HAVE_CONFIG_H #ifndef NO_CONFIG_INFO { "goto_configinfo", &no_goto_configinfo, CAN_ANONYMOUS_GOTO_CONFIGINFO }, #endif #endif { "goto_cso", &no_goto_cso, CAN_ANONYMOUS_GOTO_CSO }, { "goto_file", &no_goto_file, CAN_ANONYMOUS_GOTO_FILE }, #ifndef DISABLE_FINGER { "goto_finger", &no_goto_finger, CAN_ANONYMOUS_GOTO_FINGER }, #endif { "goto_ftp", &no_goto_ftp, CAN_ANONYMOUS_GOTO_FTP }, #ifndef DISABLE_GOPHER { "goto_gopher", &no_goto_gopher, CAN_ANONYMOUS_GOTO_GOPHER }, #endif { "goto_http", &no_goto_http, CAN_ANONYMOUS_GOTO_HTTP }, { "goto_https", &no_goto_https, CAN_ANONYMOUS_GOTO_HTTPS }, { "goto_lynxcgi", &no_goto_lynxcgi, CAN_ANONYMOUS_GOTO_LYNXCGI }, { "goto_lynxexec", &no_goto_lynxexec, CAN_ANONYMOUS_GOTO_LYNXEXEC }, { "goto_lynxprog", &no_goto_lynxprog, CAN_ANONYMOUS_GOTO_LYNXPROG }, { "goto_mailto", &no_goto_mailto, CAN_ANONYMOUS_GOTO_MAILTO }, #ifndef DISABLE_NEWS { "goto_news", &no_goto_news, CAN_ANONYMOUS_GOTO_NEWS }, { "goto_nntp", &no_goto_nntp, CAN_ANONYMOUS_GOTO_NNTP }, #endif { "goto_rlogin", &no_goto_rlogin, CAN_ANONYMOUS_GOTO_RLOGIN }, #ifndef DISABLE_NEWS { "goto_snews", &no_goto_snews, CAN_ANONYMOUS_GOTO_SNEWS }, #endif { "goto_telnet", &no_goto_telnet, CAN_ANONYMOUS_GOTO_TELNET }, { "goto_tn3270", &no_goto_tn3270, CAN_ANONYMOUS_GOTO_TN3270 }, { "goto_wais", &no_goto_wais, CAN_ANONYMOUS_GOTO_WAIS }, }; /* *INDENT-ON* */ /* This will make no difference between '-' and '_'. It does only in/equality * compare. It assumes that p2 can't contain dashes, but p1 can. This * function is also used (if macro OPTNAME_ALLOW_DASHES doesn't have value of * zero) for compare of commandline options -VH */ BOOL strn_dash_equ(const char *p1, const char *p2, int len) { while (len--) { if (!*p2) return 0; /* canonical name is shorter */ switch (*p1) { case 0: return 0; case '-': case '_': if (*p2 != '_') return 0; else break; default: if (*p1 != *p2) return 0; } ++p1; ++p2; } return 1; } /* Uncomment following lines to allow only exact string matching */ /* #define RESTRICT_NM_ALLOW_DASHES 0 */ #ifndef RESTRICT_NM_ALLOW_DASHES # define RESTRICT_NM_ALLOW_DASHES 1 #endif #if RESTRICT_NM_ALLOW_DASHES # define RESTRICT_NM_EQU(a,b,len) strn_dash_equ(a,b,len) #else # define RESTRICT_NM_EQU(a,b,len) STRNEQ(a,b,len) #endif /* * Returns the inx'th name from the restrictions table, or null if inx is * out of range. */ const char *index_to_restriction(unsigned inx) { if (inx < TABLESIZE(restrictions)) return restrictions[inx].name; return NULL; } /* * Returns the value TRUE/FALSE of a given restriction, or -1 if it is not * one that we recognize. */ int find_restriction(const char *name, int len) { unsigned i; if (len < 0) len = (int) strlen(name); for (i = 0; i < TABLESIZE(restrictions); i++) { if (RESTRICT_NM_EQU(name, restrictions[i].name, len)) { return (*restrictions[i].flag); } } return -1; } void parse_restrictions(const char *s) { const char *p; const char *word; unsigned i; BOOLEAN found; p = s; while (*p) { p = LYSkipCBlanks(p); if (*p == '\0') break; word = p; while (*p != ',' && *p != '\0') p++; found = FALSE; if (RESTRICT_NM_EQU(word, "all", (int) (p - word))) { found = TRUE; for (i = N_SPECIAL_RESTRICT_OPTIONS; i < TABLESIZE(restrictions); i++) *(restrictions[i].flag) = TRUE; } else if (RESTRICT_NM_EQU(word, "default", (int) (p - word))) { found = TRUE; for (i = N_SPECIAL_RESTRICT_OPTIONS; i < TABLESIZE(restrictions); i++) *(restrictions[i].flag) = (BOOLEAN) !restrictions[i].can; } else { for (i = 0; i < TABLESIZE(restrictions); i++) { if (RESTRICT_NM_EQU(word, restrictions[i].name, (int) (p - word))) { *(restrictions[i].flag) = TRUE; found = TRUE; break; } } } if (!found) { printf("%s: %.*s\n", gettext("unknown restriction"), (int) (p - word), word); exit_immediately(EXIT_FAILURE); } if (*p) p++; } /* * If shell is restricted, set restrictions on related topics. */ if (no_shell) { no_goto_lynxexec = TRUE; no_goto_lynxprog = TRUE; no_goto_lynxcgi = TRUE; #ifdef EXEC_LINKS local_exec_on_local_files = TRUE; #endif } } void print_restrictions_to_fd(FILE *fp) { unsigned i, count = 0; for (i = 0; i < TABLESIZE(restrictions); i++) { if (*(restrictions[i].flag) == TRUE) { count++; } } if (!count) { fprintf(fp, gettext("No restrictions set.\n")); return; } fprintf(fp, gettext("Restrictions set:\n")); for (i = 0; i < TABLESIZE(restrictions); i++) { if (*(restrictions[i].flag) == TRUE) { /* if "goto" is restricted, don't bother tell about its * refinements */ if (StrNCmp(restrictions[i].name, "goto_", 5) || !no_goto) fprintf(fp, " %s\n", restrictions[i].name); } } } #ifdef VMS #include <jpidef.h> #include <maildef.h> #include <starlet.h> typedef struct _VMSMailItemList { short buffer_length; short item_code; void *buffer_address; long *return_length_address; } VMSMailItemList; void LYCheckMail(void) { static BOOL firsttime = TRUE, failure = FALSE; static char user[13], dir[252]; static long userlen = 0, dirlen; static time_t lastcheck = 0; time_t now; static short new, lastcount; long ucontext = 0, status; short flags = MAIL$M_NEWMSG; /* *INDENT-OFF* */ VMSMailItemList null_list[] = {{0,0,0,0}}, jpi_list[] = {{sizeof(user) - 1,JPI$_USERNAME,(void *)user,&userlen}, {0,0,0,0}}, uilist[] = {{0,MAIL$_USER_USERNAME,0,0}, {0,0,0,0}}, uolist[] = {{sizeof(new),MAIL$_USER_NEW_MESSAGES,&new,0}, {sizeof(dir),MAIL$_USER_FULL_DIRECTORY,dir,&dirlen}, {0,0,0,0}}; /* *INDENT-ON* */ extern long mail$user_begin(); extern long mail$user_get_info(); extern long mail$user_end(); if (failure) return; if (firsttime) { firsttime = FALSE; /* Get the username. */ status = sys$getjpiw(0, 0, 0, jpi_list, 0, 0, 0); if (!(status & 1)) { failure = TRUE; return; } user[userlen] = '\0'; LYTrimTrailing(user); } /* Minimum report interval is 60 sec. */ time(&now); if (now - lastcheck < 60) return; lastcheck = now; /* Get the current newmail count. */ status = mail$user_begin(&ucontext, null_list, null_list); if (!(status & 1)) { failure = TRUE; return; } uilist[0].buffer_length = strlen(user); uilist[0].buffer_address = user; status = mail$user_get_info(&ucontext, uilist, uolist); if (!(status & 1)) { failure = TRUE; return; } /* Should we report anything to the user? */ if (new > 0) { if (lastcount == 0) /* Have newmail at startup of Lynx. */ HTUserMsg(HAVE_UNREAD_MAIL_MSG); else if (new > lastcount) /* Have additional mail since last report. */ HTUserMsg(HAVE_NEW_MAIL_MSG); lastcount = new; return; } lastcount = new; /* Clear the context */ mail$user_end((long *) &ucontext, null_list, null_list); return; } #else void LYCheckMail(void) { static BOOL firsttime = TRUE; static char *mf; static time_t lastcheck; static time_t lasttime; static long lastsize; time_t now; struct stat st; if (firsttime) { mf = LYGetEnv("MAIL"); firsttime = FALSE; time(&lasttime); } if (mf == NULL) return; time(&now); if (now - lastcheck < 60) return; lastcheck = now; if ((stat(mf, &st) < 0) || !S_ISREG(st.st_mode)) { mf = NULL; return; } if (st.st_size > 0) { if (((lasttime != st.st_mtime) && (st.st_mtime > st.st_atime)) || ((lastsize != 0) && (st.st_size > lastsize))) HTUserMsg(HAVE_NEW_MAIL_MSG); else if (lastsize == 0) HTUserMsg(HAVE_MAIL_MSG); } lastsize = (long) st.st_size; lasttime = st.st_mtime; return; } #endif /* VMS */ /* * This function ensures that an href will be * converted to a fully resolved, absolute URL, * with guessing of the host or expansions of * lead tildes via LYConvertToURL() if needed, * and tweaking/simplifying via HTParse(). It * is used for LynxHome, startfile, homepage, * and 'g'oto entries, after they have been * passed to LYFillLocalFileURL(). - FM * Such URLs have no `base' reference to which they * could be resolved. LYLegitimizeHREF could not be used. */ void LYEnsureAbsoluteURL(char **href, const char *name, int fixit) { char *temp = NULL; if (isEmpty(*href)) return; /* * Check whether to fill in localhost. - FM */ LYFillLocalFileURL(href, "file://localhost"); /* * If it is not a URL then make it one. */ if (!strcasecomp(*href, STR_NEWS_URL)) { StrAllocCat(*href, "*"); } else if (!strcasecomp(*href, STR_SNEWS_URL)) { StrAllocCat(*href, "/*"); } if (!is_url(*href)) { CTRACE((tfp, "%s%s'%s' is not a URL\n", NonNull(name), (name ? " " : ""), *href)); LYConvertToURL(href, fixit); } temp = HTParse(*href, "", PARSE_ALL); if (non_empty(temp)) StrAllocCopy(*href, temp); FREE(temp); } static const char *default_scheme = "http://"; static const char *guess_scheme(const char *url) { const char *scheme = NULL; if (LYGuessScheme && non_empty(url)) { if (0 == strncasecomp(url, "www.", 4)) { scheme = "http://"; } else if (0 == strncasecomp(url, "ftp.", 4)) { scheme = "ftp://"; } else if (0 == strncasecomp(url, "gopher.", 7)) { scheme = "gopher://"; } else if (0 == strncasecomp(url, "wais.", 5)) { scheme = "wais://"; } else if (0 == strncasecomp(url, "cso.", 4) || 0 == strncasecomp(url, "ns.", 3) || 0 == strncasecomp(url, "ph.", 3)) { scheme = "cso://"; } else if (0 == strncasecomp(url, "finger.", 7)) { scheme = "finger://"; } else if (0 == strncasecomp(url, "news.", 5)) { scheme = "news://"; } else if (0 == strncasecomp(url, "nntp.", 5)) { scheme = "nntp://"; } } CTRACE((tfp, "guess_scheme(%s) -> '%s'\n", NonNull(url), NonNull(scheme))); return scheme; } /* * This function rewrites and reallocates a previously allocated string that * begins with an Internet host name so that the string begins with its guess * of the scheme based on the first field of the host name, or the default * scheme if no guess was made. */ static void LYAddSchemeForURL(char **AllocatedString) { if (non_empty(*AllocatedString)) { char *Str = NULL; const char *scheme = guess_scheme(*AllocatedString); if (scheme == NULL) scheme = default_scheme; StrAllocCopy(Str, scheme); StrAllocCat(Str, *AllocatedString); StrAllocCopy(*AllocatedString, Str); FREE(Str); } } /* * This function rewrites and reallocates a previously allocated string so that * the first element is a confirmed Internet host, and returns TRUE, otherwise * it does not modify the string and returns FALSE. * * It first tries the element as is, then, if the element does not end with a * dot, it adds prefixes from the (comma separated) prefix list argument, and, * if the element does not begin with a dot, suffixes from the (comma * separated) suffix list arguments (e.g., www.host.com, then www.host,edu, * then www.host.net, then www.host.org). * * The remaining path, if one is present, will be appended to the expanded * host. * * It also takes into account whether a colon is in the element or suffix, and * includes that and what follows as a port field for the expanded host field * (e.g, wfbr:8002/dir/lynx should yield www.wfbr.edu:8002/dir/lynx). */ static BOOLEAN LYExpandHostForURL(char **AllocatedString, char *prefix_list, char *suffix_list) { char *DomainPrefix = NULL; const char *StartP, *EndP; char *DomainSuffix = NULL; const char *StartS, *EndS; char *Str = NULL, *StrColon = NULL, *MsgStr = NULL; char *Host = NULL, *HostColon = NULL, *host = NULL; char *Path = NULL; char *Fragment = NULL; BOOLEAN GotHost = FALSE; BOOLEAN Startup = (BOOL) (helpfilepath == NULL); /* * If it's a NULL or zero-length string, or if it begins with a slash or * hash, don't continue pointlessly. - FM */ if (isEmpty(*AllocatedString) || *AllocatedString[0] == '/' || *AllocatedString[0] == '#') { return GotHost; } /* * If it's a partial or relative path, don't continue pointlessly. - FM */ if (!StrNCmp(*AllocatedString, "..", 2) || !StrNCmp(*AllocatedString, "./", 2)) { return GotHost; } /* * Make a clean copy of the string, and trim off the path if one is * present, but save the information so we can restore the path after * filling in the Host[:port] field. - FM */ StrAllocCopy(Str, *AllocatedString); if ((Path = StrChr(Str, '/')) != NULL) { /* * Have a path. Any fragment should already be included in Path. - FM */ *Path = '\0'; } else { /* * No path, so check for a fragment and trim that, to be restored after * filling in the Host[:port] field. - FM */ Fragment = trimPoundSelector(Str); } /* * If the potential host string has a colon, assume it begins a port field, * and trim it off, but save the information so we can restore the port * field after filling in the host field. - FM */ if ((StrColon = strrchr(Str, ':')) != NULL && isdigit(UCH(StrColon[1])) && StrChr(StrColon, ']') == NULL) { if (StrColon == Str) { goto cleanup; } *StrColon = '\0'; } /* * Do a DNS test on the potential host field as presently trimmed. - FM */ StrAllocCopy(host, Str); strip_userid(host, FALSE); HTUnEscape(host); if (LYCursesON) { StrAllocCopy(MsgStr, WWW_FIND_MESSAGE); StrAllocCat(MsgStr, host); StrAllocCat(MsgStr, FIRST_SEGMENT); HTProgress(MsgStr); } else if (Startup && !dump_output_immediately) { fprintf(stdout, "%s '%s'%s\r\n", WWW_FIND_MESSAGE, host, FIRST_SEGMENT); } #ifdef INET6 if (HTCheckAddrInfo(host, 80)) #else if (LYCheckHostByName(host)) #endif /* INET6 */ { /* * Clear any residual interrupt. - FM */ if (LYCursesON && HTCheckForInterrupt()) { CTRACE((tfp, "LYExpandHostForURL: Ignoring interrupt because '%s' resolved.\n", host)); } /* * Return success. - FM */ GotHost = TRUE; goto cleanup; } else if (LYCursesON && (lynx_nsl_status == HT_INTERRUPTED)) { /* * Give the user chance to interrupt lookup cycles. - KW & FM */ CTRACE((tfp, "LYExpandHostForURL: Interrupted while '%s' failed to resolve.\n", host)); /* * Return failure. - FM */ goto cleanup; } /* * Set the first prefix, making it a zero-length string if the list is NULL * or if the potential host field ends with a dot. - FM */ StartP = ((prefix_list && Str[strlen(Str) - 1] != '.') ? prefix_list : ""); /* * If we have a prefix, but the allocated string is one of the common host * prefixes, make our prefix a zero-length string. - FM */ if (*StartP && *StartP != '.') { if (guess_scheme(*AllocatedString) != NULL) { StartP = ""; } } while ((*StartP) && (WHITE(*StartP) || *StartP == ',')) { StartP++; /* Skip whitespace and separators */ } EndP = StartP; while (*EndP && !WHITE(*EndP) && *EndP != ',') { EndP++; /* Find separator */ } StrAllocCopy(DomainPrefix, StartP); DomainPrefix[EndP - StartP] = '\0'; /* * Test each prefix with each suffix. - FM */ do { /* * Set the first suffix, making it a zero-length string if the list is * NULL or if the potential host field begins with a dot. - FM */ StartS = ((suffix_list && *Str != '.') ? suffix_list : ""); while ((*StartS) && (WHITE(*StartS) || *StartS == ',')) { StartS++; /* Skip whitespace and separators */ } EndS = StartS; while (*EndS && !WHITE(*EndS) && *EndS != ',') { EndS++; /* Find separator */ } StrAllocCopy(DomainSuffix, StartS); DomainSuffix[EndS - StartS] = '\0'; /* * Create domain names and do DNS tests. - FM */ do { StrAllocCopy(Host, DomainPrefix); StrAllocCat(Host, ((*Str == '.') ? (Str + 1) : Str)); if (Host[strlen(Host) - 1] == '.') { Host[strlen(Host) - 1] = '\0'; } StrAllocCat(Host, DomainSuffix); if ((HostColon = strrchr(Host, ':')) != NULL && isdigit(UCH(HostColon[1]))) { *HostColon = '\0'; } StrAllocCopy(host, Host); HTUnEscape(host); if (LYCursesON) { StrAllocCopy(MsgStr, WWW_FIND_MESSAGE); StrAllocCat(MsgStr, host); StrAllocCat(MsgStr, GUESSING_SEGMENT); HTProgress(MsgStr); } else if (Startup && !dump_output_immediately) { fprintf(stdout, "%s '%s'%s\n", WWW_FIND_MESSAGE, host, GUESSING_SEGMENT); } GotHost = LYCheckHostByName(host); if (HostColon != NULL) { *HostColon = ':'; } if (GotHost == FALSE) { /* * Give the user chance to interrupt lookup cycles. - KW */ if (LYCursesON && (lynx_nsl_status == HT_INTERRUPTED)) { CTRACE((tfp, "LYExpandHostForURL: Interrupted while '%s' failed to resolve.\n", host)); goto cleanup; /* We didn't find a valid name. */ } /* * Advance to the next suffix, or end of suffix list. - FM */ StartS = ((*EndS == '\0') ? EndS : (EndS + 1)); while ((*StartS) && (WHITE(*StartS) || *StartS == ',')) { StartS++; /* Skip whitespace and separators */ } EndS = StartS; while (*EndS && !WHITE(*EndS) && *EndS != ',') { EndS++; /* Find separator */ } LYStrNCpy(DomainSuffix, StartS, (EndS - StartS)); } } while ((GotHost == FALSE) && (*DomainSuffix != '\0')); if (GotHost == FALSE) { /* * Advance to the next prefix, or end of prefix list. - FM */ StartP = ((*EndP == '\0') ? EndP : (EndP + 1)); while ((*StartP) && (WHITE(*StartP) || *StartP == ',')) { StartP++; /* Skip whitespace and separators */ } EndP = StartP; while (*EndP && !WHITE(*EndP) && *EndP != ',') { EndP++; /* Find separator */ } LYStrNCpy(DomainPrefix, StartP, (EndP - StartP)); } } while ((GotHost == FALSE) && (*DomainPrefix != '\0')); /* * If a test passed, restore the port field if we had one and there is no * colon in the expanded host, and the path if we had one, and reallocate * the original string with the expanded Host[:port] field included. - FM */ if (GotHost) { if (StrColon && StrChr(Host, ':') == NULL) { *StrColon = ':'; StrAllocCat(Host, StrColon); } if (Path) { *Path = '/'; StrAllocCat(Host, Path); } else if (Fragment) { StrAllocCat(Host, "/"); restorePoundSelector(Fragment); StrAllocCat(Host, Fragment); } StrAllocCopy(*AllocatedString, Host); } /* * Clear any residual interrupt. - FM */ if (LYCursesON && HTCheckForInterrupt()) { CTRACE((tfp, "LYExpandHostForURL: Ignoring interrupt because '%s' %s.\n", host, (GotHost ? "resolved" : "timed out"))); } /* * Clean up and return the last test result. - FM */ cleanup: FREE(DomainPrefix); FREE(DomainSuffix); FREE(Str); FREE(MsgStr); FREE(Host); FREE(host); return GotHost; } /* * Rewrite and reallocate a previously allocated string as a file URL if the * string resolves to a file or directory on the local system, otherwise as an * http URL. - FM */ void LYConvertToURL(char **AllocatedString, int fixit) { char *old_string = *AllocatedString; char *temp = NULL; char *cp = NULL; #ifndef VMS struct stat st; #endif /* !VMS */ if (isEmpty(old_string)) return; #if defined(USE_DOS_DRIVES) { char *cp_url = *AllocatedString; for (; *cp_url != '\0'; cp_url++) if (*cp_url == '\\') *cp_url = '/'; cp_url--; if (LYIsDosDrive(*AllocatedString) && *cp_url == ':') LYAddPathSep(AllocatedString); } #endif /* USE_DOS_DRIVES */ *AllocatedString = NULL; /* so StrAllocCopy doesn't free it */ StrAllocCopy(*AllocatedString, "file://localhost"); if (*old_string != '/') { char *fragment = NULL; #if defined(USE_DOS_DRIVES) StrAllocCat(*AllocatedString, "/"); #endif /* USE_DOS_DRIVES */ #ifdef VMS /* * Not a SHELL pathspec. Get the full VMS spec and convert it. */ char *cur_dir = NULL; static char url_file[LY_MAXPATH], file_name[LY_MAXPATH], dir_name[LY_MAXPATH]; unsigned long context = 0; $DESCRIPTOR(url_file_dsc, url_file); $DESCRIPTOR(file_name_dsc, file_name); if (LYIsTilde(*old_string)) { /* * On VMS, we'll accept '~' on the command line as Home_Dir(), and * assume the rest of the path, if any, has SHELL syntax. */ StrAllocCat(*AllocatedString, HTVMS_wwwName(Home_Dir())); if ((cp = StrChr(old_string, '/')) != NULL) { /* * Append rest of path, if present, skipping "user" if "~user" * was entered, simplifying, and eliminating any residual * relative elements. - FM */ StrAllocCopy(temp, cp); LYTrimRelFromAbsPath(temp); StrAllocCat(*AllocatedString, temp); FREE(temp); } goto have_VMS_URL; } else { fragment = trimPoundSelector(old_string); LYStrNCpy(url_file, old_string, sizeof(url_file) - 1); } url_file_dsc.dsc$w_length = (short) strlen(url_file); if (1 & lib$find_file(&url_file_dsc, &file_name_dsc, &context, 0, 0, 0, 0)) { /* * We found the file. Convert to a URL pathspec. */ if ((cp = StrChr(file_name, ';')) != NULL) { *cp = '\0'; } LYLowerCase(file_name); StrAllocCat(*AllocatedString, HTVMS_wwwName(file_name)); if ((cp = StrChr(old_string, ';')) != NULL) { StrAllocCat(*AllocatedString, cp); } if (fragment != NULL) { restorePoundSelector(fragment); StrAllocCat(*AllocatedString, fragment); fragment = NULL; } } else if ((NULL != getcwd(dir_name, sizeof(dir_name) - 1, 0)) && 0 == chdir(old_string)) { /* * Probably a directory. Try converting that. */ StrAllocCopy(cur_dir, dir_name); restorePoundSelector(fragment); if (NULL != getcwd(dir_name, sizeof(dir_name) - 1, 0)) { /* * Yup, we got it! */ LYLowerCase(dir_name); StrAllocCat(*AllocatedString, dir_name); if (fragment != NULL) { StrAllocCat(*AllocatedString, fragment); fragment = NULL; } } else { /* * Nope. Assume it's an http URL with the "http://" defaulted, * if we can't rule out a bad VMS path. */ fragment = NULL; if (StrChr(old_string, '[') || ((cp = StrChr(old_string, ':')) != NULL && !isdigit(UCH(cp[1]))) || !LYExpandHostForURL(&old_string, URLDomainPrefixes, URLDomainSuffixes)) { /* * Probably a bad VMS path (but can't be sure). Use * original pathspec for the error message that will * result. */ sprintf(url_file, "/%.*s", sizeof(url_file) - 2, old_string); CTRACE((tfp, "Can't find '%s' Will assume it's a bad path.\n", old_string)); StrAllocCat(*AllocatedString, url_file); } else { LYAddSchemeForURL(&old_string); StrAllocCopy(*AllocatedString, old_string); } } } else { /* * Nothing found. Assume it's an http URL with the "http://" * defaulted, if we can't rule out a bad VMS path. */ restorePoundSelector(fragment); fragment = NULL; if (StrChr(old_string, '[') || ((cp = StrChr(old_string, ':')) != NULL && !isdigit(UCH(cp[1]))) || !LYExpandHostForURL(&old_string, URLDomainPrefixes, URLDomainSuffixes)) { /* * Probably a bad VMS path (but can't be sure). Use original * pathspec for the error message that will result. */ sprintf(url_file, "/%.*s", sizeof(url_file) - 2, old_string); CTRACE((tfp, "Can't find '%s' Will assume it's a bad path.\n", old_string)); StrAllocCat(*AllocatedString, url_file); } else { LYAddSchemeForURL(&old_string); StrAllocCopy(*AllocatedString, old_string); } } lib$find_file_end(&context); FREE(cur_dir); have_VMS_URL: CTRACE((tfp, "Trying: '%s'\n", *AllocatedString)); #else /* not VMS: */ #if defined(USE_DOS_DRIVES) #ifdef _WINDOWS if (*old_string == '.') { char fullpath[MAX_PATH + 1]; char *filepart = NULL; DWORD chk; chk = GetFullPathNameA(old_string, MAX_PATH + 1, fullpath, &filepart); if (chk != 0) { StrAllocCopy(temp, wwwName(fullpath)); StrAllocCat(*AllocatedString, temp); FREE(temp); CTRACE((tfp, "Converted '%s' to '%s'\n", old_string, *AllocatedString)); } else { StrAllocCat(*AllocatedString, old_string); } } #else if (strlen(old_string) == 1 && *old_string == '.') { /* * They want . */ char curdir[LY_MAXPATH]; StrAllocCopy(temp, wwwName(Current_Dir(curdir))); StrAllocCat(*AllocatedString, temp); FREE(temp); CTRACE((tfp, "Converted '%s' to '%s'\n", old_string, *AllocatedString)); } #endif else #endif /* USE_DOS_DRIVES */ if (LYIsTilde(*old_string)) { char *his_home = NULL; StrAllocCopy(his_home, old_string); LYTildeExpand(&his_home, FALSE); StrAllocCat(*AllocatedString, his_home); FREE(his_home); CTRACE((tfp, "Converted '%s' to '%s'\n", old_string, *AllocatedString)); } else { /* * Create a full path to the current default directory. */ char curdir[LY_MAXPATH]; char *temp2 = NULL; BOOL is_local = FALSE; Current_Dir(curdir); /* * Concatenate and simplify, trimming any residual relative * elements. - FM */ #if defined (USE_DOS_DRIVES) if (old_string[1] != ':' && old_string[1] != '|') { StrAllocCopy(temp, wwwName(curdir)); LYAddHtmlSep(&temp); LYStrNCpy(curdir, temp, (sizeof(curdir) - 1)); StrAllocCat(temp, old_string); } else { curdir[0] = '\0'; /* 1998/01/13 (Tue) 12:24:33 */ if (old_string[1] == '|') old_string[1] = ':'; StrAllocCopy(temp, old_string); if (strlen(temp) == 2 && LYIsDosDrive(temp)) LYAddPathSep(&temp); } #else StrAllocCopy(temp, curdir); StrAllocCat(temp, "/"); StrAllocCat(temp, old_string); #endif /* USE_DOS_DRIVES */ LYTrimRelFromAbsPath(temp); CTRACE((tfp, "Converted '%s' to '%s'\n", old_string, temp)); if ((stat(temp, &st) > -1) || LYCanReadFile(temp)) { /* * It is a subdirectory or file on the local system. */ #if defined (USE_DOS_DRIVES) /* Don't want to see DOS local paths like c: escaped */ /* especially when we really have file://localhost/ */ /* at the beginning. To avoid any confusion we allow */ /* escaping the path if URL specials % or # present. */ if (StrChr(temp, '#') == NULL && StrChr(temp, '%') == NULL) StrAllocCopy(cp, temp); else cp = HTEscape(temp, URL_PATH); #else cp = HTEscape(temp, URL_PATH); #endif /* USE_DOS_DRIVES */ StrAllocCat(*AllocatedString, cp); FREE(cp); CTRACE((tfp, "Converted '%s' to '%s'\n", old_string, *AllocatedString)); is_local = TRUE; } else { char *cp2 = NULL; StrAllocCopy(temp2, curdir); LYAddPathSep(&temp2); StrAllocCopy(cp, old_string); fragment = trimPoundSelector(cp); HTUnEscape(cp); /* unescape given path without fragment */ StrAllocCat(temp2, cp); /* append to current dir */ StrAllocCopy(cp2, temp2); /* keep a copy in cp2 */ LYTrimRelFromAbsPath(temp2); #ifdef WIN_EX /* 1998/07/31 (Fri) 09:09:03 */ HTUnEscape(temp2); /* for LFN */ #endif if (strcmp(temp2, temp) != 0 && ((stat(temp2, &st) > -1) || LYCanReadFile(temp2))) { /* * It is a subdirectory or file on the local system with * escaped characters and/or a fragment to be appended to * the URL. - FM */ FREE(temp); if (strcmp(cp2, temp2) == 0) { /* * LYTrimRelFromAbsPath did nothing, use old_string as * given. - kw */ temp = HTEscape(curdir, URL_PATH); LYAddHtmlSep(&temp); StrAllocCat(temp, old_string); } else { temp = HTEscape(temp2, URL_PATH); if (fragment != NULL) { restorePoundSelector(fragment); StrAllocCat(temp, fragment); } } StrAllocCat(*AllocatedString, temp); CTRACE((tfp, "Converted '%s' to '%s'\n", old_string, *AllocatedString)); is_local = TRUE; } else if (StrChr(curdir, '#') != NULL || StrChr(curdir, '%') != NULL) { /* * If PWD has some unusual characters, construct a filename * in temp where those are escaped. This is mostly to * prevent this function from returning with some weird URL * if the LYExpandHostForURL tests further down fail. - kw */ FREE(temp); if (strcmp(cp2, temp2) == 0) { /* * LYTrimRelFromAbsPath did nothing, use old_string as * given. - kw */ temp = HTEscape(curdir, URL_PATH); LYAddHtmlSep(&temp); StrAllocCat(temp, old_string); } else { temp = HTEscape(temp2, URL_PATH); if (fragment != NULL) { restorePoundSelector(fragment); StrAllocCat(temp, fragment); } } } FREE(cp); FREE(cp2); } if (is_local == FALSE) { /* * It's not an accessible subdirectory or file on the local * system, so assume it's a URL request and guess the scheme * with "http://" as the default. */ CTRACE((tfp, "Can't stat() or fopen() '%s'\n", temp2 ? temp2 : temp)); #ifdef WIN_EX /* 1998/01/13 (Tue) 09:07:37 */ { const char *p, *q; char buff[LY_MAXPATH + 128]; p = Home_Dir(); q = temp2 ? temp2 : temp; if (strlen(q) == 3 && LYIsDosDrive(q)) { sprintf(buff, "'%s' not exist, Goto LynxHome '%s'.", q, p); _statusline(buff); LYSleepAlert(); FREE(temp); StrAllocCat(*AllocatedString, p); goto Retry; } } #endif if (LYExpandHostForURL(&old_string, URLDomainPrefixes, URLDomainSuffixes)) { LYAddSchemeForURL(&old_string); StrAllocCopy(*AllocatedString, old_string); } else if (fixit) { StrAllocCopy(*AllocatedString, old_string); } else { /* Return file URL for the file that does not exist */ StrAllocCat(*AllocatedString, temp); } #ifdef WIN_EX Retry: #endif CTRACE((tfp, "Trying: '%s'\n", *AllocatedString)); } FREE(temp); FREE(temp2); } #endif /* VMS */ } else { /* * Path begins with a slash. Simplify and use it. */ if (old_string[1] == '\0') { /* * Request for root. Respect it on Unix, but on VMS we treat that * as a listing of the login directory. - FM */ #ifdef VMS StrAllocCat(*AllocatedString, HTVMS_wwwName(Home_Dir())); #else StrAllocCat(*AllocatedString, "/"); } else if ((stat(old_string, &st) > -1) || LYCanReadFile(old_string)) { /* * It is an absolute directory or file on the local system. - KW */ StrAllocCopy(temp, old_string); LYTrimRelFromAbsPath(temp); CTRACE((tfp, "Converted '%s' to '%s'\n", old_string, temp)); cp = HTEscape(temp, URL_PATH); StrAllocCat(*AllocatedString, cp); FREE(cp); FREE(temp); CTRACE((tfp, "Converted '%s' to '%s'\n", old_string, *AllocatedString)); #endif /* VMS */ } else if (LYIsTilde(old_string[1])) { /* * Has a Home_Dir() reference. Handle it as if there weren't a * lead slash. - FM */ StrAllocCat(*AllocatedString, wwwName(Home_Dir())); if ((cp = StrChr((old_string + 1), '/')) != NULL) { /* * Append rest of path, if present, skipping "user" if "~user" * was entered, simplifying, and eliminating any residual * relative elements. - FM */ StrAllocCopy(temp, cp); LYTrimRelFromAbsPath(temp); StrAllocCat(*AllocatedString, temp); FREE(temp); } } else { /* * Normal absolute path. Simplify, trim any residual relative * elements, and append it. - FM */ StrAllocCopy(temp, old_string); LYTrimRelFromAbsPath(temp); StrAllocCat(*AllocatedString, temp); FREE(temp); } CTRACE((tfp, "Converted '%s' to '%s'\n", old_string, *AllocatedString)); } FREE(old_string); /* Pause so we can read the messages before invoking curses */ CTRACE_SLEEP(AlertSecs); } #if defined(_WINDOWS) /* 1998/06/23 (Tue) 16:45:20 */ int win32_check_interrupt(void) { int c; if (kbhit()) { c = LYgetch(); /** Keyboard 'Z' or 'z', or Control-G or Control-C **/ if (LYCharIsINTERRUPT(c) || c == 0x1b) { return TRUE; } } return FALSE; } #if (!defined(__MINGW32__) && !defined(sleep)) || (defined(__MINGW32__) && !defined(HAVE_SLEEP)) void sleep(unsigned sec) { unsigned int i, j; for (j = 0; j < sec; j++) { for (i = 0; i < 10; i++) { Sleep(100); if (kbhit()) { (void) LYgetch(); return; } } } } #endif /* !__MINGW32__ */ #endif /* _WINDOWS */ /* * This function expects an absolute Unix or VMS SHELL path spec as an * allocated string, simplifies it, and trims out any residual relative * elements. It also checks whether the path had a terminal slash, and if it * didn't, makes sure that the simplified path doesn't either. If it's a * directory, our convention is to exclude "Up to parent" links when a terminal * slash is present. - FM */ void LYTrimRelFromAbsPath(char *path) { char *cp; int i; BOOL TerminalSlash; /* * Make sure we have a pointer to an absolute path. - FM */ if (path == NULL || !LYIsPathSep(*path)) return; /* * Check whether the path has a terminal slash. - FM */ TerminalSlash = (BOOL) (LYIsPathSep(path[(strlen(path) - 1)])); /* * Simplify the path and then do any necessary trimming. - FM */ HTSimplify(path); cp = path; while (cp[1] == '.') { if (cp[2] == '\0') { /* * Eliminate trailing dot. - FM */ cp[1] = '\0'; } else if (LYIsPathSep(cp[2])) { /* * Skip over the "/." of a "/./". - FM */ cp += 2; } else if (cp[2] == '.' && cp[3] == '\0') { /* * Eliminate trailing dotdot. - FM */ cp[1] = '\0'; } else if (cp[2] == '.' && cp[3] == '/') { /* * Skip over the "/.." of a "/../". - FM */ cp += 3; } else { /* * Done trimming. - FM */ break; } } /* * Load any shifts into path, and eliminate any terminal slash created by * HTSimplify() or our walk, but not present originally. - FM */ if (cp > path) { for (i = 0; cp[i] != '\0'; i++) path[i] = cp[i]; path[i] = '\0'; } if (TerminalSlash == FALSE) { LYTrimPathSep(path); } } /* * Example Client-Side Include interface. * * This is called from SGML.c and simply returns markup for reporting the URL * of the document being loaded if a comment begins with "<!--#lynxCSI". The * markup will be included as if it were in the document. Move this function * to a separate module for doing this kind of thing seriously, someday. - FM */ void LYDoCSI(char *url, const char *comment, char **csi) { const char *cp = comment; if (cp == NULL) return; if (StrNCmp(cp, "!--#", 4)) return; cp += 4; if (!strncasecomp(cp, "lynxCSI", 7)) { StrAllocCat(*csi, "\n<p align=\"center\">URL: "); StrAllocCat(*csi, url); StrAllocCat(*csi, "</p>\n\n"); } return; } #ifdef VMS /* * Define_VMSLogical -- Fote Macrides 04-Apr-1995 * Define VMS logicals in the process table. */ void Define_VMSLogical(char *LogicalName, char *LogicalValue) { $DESCRIPTOR(lname, ""); $DESCRIPTOR(lvalue, ""); $DESCRIPTOR(ltable, "LNM$PROCESS"); if (isEmpty(LogicalName)) return; lname.dsc$w_length = strlen(LogicalName); lname.dsc$a_pointer = LogicalName; if (isEmpty(LogicalValue)) { lib$delete_logical(&lname, <able); return; } lvalue.dsc$w_length = strlen(LogicalValue); lvalue.dsc$a_pointer = LogicalValue; lib$set_logical(&lname, &lvalue, <able, 0, 0); return; } #endif /* VMS */ #ifdef LY_FIND_LEAKS static void LYHomeDir_free(void) { FREE(HomeDir); } #endif /* LY_FIND_LEAKS */ char *Current_Dir(char *pathname) { char *result; #ifdef HAVE_GETCWD result = getcwd(pathname, (size_t) LY_MAXPATH); #else result = getwd(pathname); #endif /* NO_GETCWD */ if (result == 0) strcpy(pathname, "."); return pathname; } /* * Verify that the given path refers to an existing directory, returning the * string if the directory exists. If not, return null. */ static char *CheckDir(char *path) { struct stat stat_info; if (!LYisAbsPath(path) || (HTStat(path, &stat_info) < 0 || !S_ISDIR(stat_info.st_mode))) { path = NULL; } CTRACE((tfp, "CheckDir(%s) %s\n", path, path ? "OK" : "ERR")); return path; } /* * Lookup various possibilities for $HOME, and check that the directory exists. */ static char *HomeEnv(void) { char *result = CheckDir(LYGetEnv("HOME")); #if defined (USE_DOS_DRIVES) && defined(_WIN32) if (result == 0) { char *head; char *leaf; static char *temp = NULL; result = w32_get_shell_folder("Personal"); if (result == 0) { /* Windows Vista/7 */ if ((head = LYGetEnv("USERPROFILE")) != 0) { HTSprintf0(&temp, "%s%sDocuments", head, FILE_SEPARATOR); result = CheckDir(temp); if (result == 0) { /* Windows 2000 */ HTSprintf0(&temp, "%s%sMy Documents", head, FILE_SEPARATOR); result = CheckDir(temp); } } } /* NT4 */ if (result == 0) { if ((head = LYGetEnv("HOMEDRIVE")) != 0) { if ((leaf = LYGetEnv("HOMEPATH")) != 0) { HTSprintf0(&temp, "%s%s%s", head, FILE_SEPARATOR, leaf); result = CheckDir(temp); } } } /* General M$ */ #ifdef USE_PROGRAM_DIR if (result == 0) result = CheckDir(program_dir); #endif if (result == 0) result = CheckDir(LYGetEnv("TEMP")); if (result == 0) result = CheckDir(LYGetEnv("TMP")); if (result == 0) { if ((head = LYGetEnv("SystemDrive")) != 0) { HTSprintf0(&temp, "%s%s", head, FILE_SEPARATOR); result = CheckDir(temp); } } if (result == 0) result = CheckDir("C:" FILE_SEPARATOR); } #endif return result; } const char *Home_Dir(void) { static const char *homedir = NULL; char *cp = NULL; if (homedir == NULL) { if ((cp = HomeEnv()) == NULL) { #ifdef VMS if ((cp = LYGetEnv("SYS$LOGIN")) == NULL && (cp = LYGetEnv("SYS$SCRATCH")) == NULL) { cp = "sys$scratch:"; } StrAllocCopy(HomeDir, cp); #else #ifdef UNIX #ifdef HAVE_UTMP /* * One could use getlogin() and getpwnam() here instead. */ struct passwd *pw = getpwuid(geteuid()); if (pw && pw->pw_dir) { StrAllocCopy(HomeDir, pw->pw_dir); } else #endif { /* * Use /tmp; it should be writable. */ StrAllocCopy(HomeDir, "/tmp"); } #endif #endif /* VMS */ } else { StrAllocCopy(HomeDir, cp); } homedir = (const char *) HomeDir; #ifdef LY_FIND_LEAKS atexit(LYHomeDir_free); #endif } if (homedir == NULL) { printf("%s\n", gettext("Cannot find HOME directory")); exit_immediately(EXIT_FAILURE); } return homedir; } /* * Return a pointer to the final leaf of the given pathname, If no pathname * separators are found, returns the original pathname. The leaf may be * empty. */ char *LYPathLeaf(char *pathname) { char *leaf; #ifdef UNIX if ((leaf = strrchr(pathname, '/')) != 0) { leaf++; } #else #ifdef VMS if ((leaf = strrchr(pathname, ']')) == 0) leaf = strrchr(pathname, ':'); if (leaf != 0) leaf++; #else int n; for (leaf = 0, n = (int) strlen(pathname) - 1; n >= 0; n--) { if (StrChr("\\/:", pathname[n]) != 0) { leaf = pathname + n + 1; break; } } #endif #endif return (leaf != 0) ? leaf : pathname; } /* * This function checks the acceptability of file paths that are intended to be * off the home directory. The file path should be passed in fbuffer, together * with the size of the buffer. The function simplifies the file path, and if * it is acceptable, loads it into fbuffer and returns TRUE. Otherwise, it * does not modify fbuffer and returns FALSE. If a subdirectory is present and * the path does not begin with "./", that is prefixed to make the situation * clear. - FM */ BOOLEAN LYPathOffHomeOK(char *fbuffer, size_t fbuffer_size) { char *file = NULL; char *cp, *cp1; /* * Make sure we have an fbuffer and a string in it. - FM */ if (fbuffer_size < 2 || isEmpty(fbuffer)) { return (FALSE); } StrAllocCopy(file, fbuffer); cp = file; /* * Check for an inappropriate reference to the home directory, and correct * it if we can. - FM */ #ifdef VMS if (!strncasecomp(cp, "sys$login", 9)) { if (*(cp + 9) == '\0') { /* * Reject "sys$login". - FM */ FREE(file); return (FALSE); } if (*(cp + 9) == ':') { cp += 10; if (*cp == '\0') { /* * Reject "sys$login:". Otherwise, we have converted * "sys$login:file" to "file", or have left a strange path for * VMS as it was originally. - FM */ FREE(file); return (FALSE); } } } #endif /* VMS */ if (LYIsTilde(cp[0])) { if (LYIsPathSep(cp[1])) { if (cp[2] != '\0') { if (StrChr((cp + 2), '/') != NULL) { /* * Convert "~/subdir(s)/file" to "./subdir(s)/file". - FM */ *cp = '.'; } else { /* * Convert "~/file" to "file". - FM */ cp += 2; } } else { /* * Reject "~/". - FM */ FREE(file); return (FALSE); } } else if ((*(cp + 1) != '\0') && (cp1 = StrChr((cp + 1), '/')) != NULL) { cp = (cp1 - 1); if (*(cp + 2) != '\0') { if (StrChr((cp + 2), '/') != NULL) { /* * Convert "~user/subdir(s)/file" to "./subdir(s)/file". * If user is someone else, we covered a spoof. Otherwise, * we simplified. - FM */ *cp = '.'; } else { /* * Convert "~user/file" to "file". - FM */ cp += 2; } } else { /* * Reject "~user/". - FM */ FREE(file); return (FALSE); } } else { /* * Reject "~user". - FM */ FREE(file); return (FALSE); } } #ifdef VMS /* * Check for VMS path specs, and reject if still present. - FM */ if (StrChr(cp, ':') != NULL || StrChr(cp, ']') != NULL) { FREE(file); return (FALSE); } #endif /* VMS */ /* * Check for a URL or absolute path, and reject if present. - FM */ if (is_url(cp) || LYIsPathSep(*cp)) { FREE(file); return (FALSE); } /* * Simplify it. - FM */ HTSimplify(cp); /* * Check if it has a pointless "./". - FM */ if (!StrNCmp(cp, "./", 2)) { if (StrChr((cp + 2), '/') == NULL) { cp += 2; } } /* * Check for spoofing. - FM */ if (*cp == '\0' || LYIsPathSep(*cp) || LYIsPathSep(cp[(strlen(cp) - 1)]) || strstr(cp, "..") != NULL || !strcmp(cp, ".")) { FREE(file); return (FALSE); } /* * Load what we have at this point into fbuffer, trimming if too long, and * claim it's OK. - FM */ if (fbuffer_size > 3 && StrNCmp(cp, "./", 2) && StrChr(cp, '/')) { /* * We have a subdirectory and no lead "./", so prefix it to make the * situation clear. - FM */ strcpy(fbuffer, "./"); if (strlen(cp) > (fbuffer_size - 3)) cp[(fbuffer_size - 3)] = '\0'; strcat(fbuffer, cp); } else { if (strlen(cp) > (fbuffer_size - 1)) cp[(fbuffer_size - 1)] = '\0'; strcpy(fbuffer, cp); } FREE(file); return (TRUE); } /* * Search for a leading tilde, optionally embedded. If found, return a pointer * to the tilde. If not found, return the original parameter. */ static char *FindLeadingTilde(char *pathname, int embedded) { char *result = pathname; if (pathname != NULL) { if (embedded) { while (pathname[0] != '\0') { if (LYIsPathSep(pathname[0])) { if (LYIsTilde(pathname[1])) { ++pathname; break; } } ++pathname; } } if (LYIsTilde(*pathname)) result = pathname; } return result; } /* * Convert a non-absolute path to one which is off the home directory. Expand * tildes as a side-effect. Return a pointer to the converted result. */ char *LYAbsOrHomePath(char **fname) { if (*fname && !LYisAbsPath(*fname)) { if (LYIsTilde((*fname)[0])) { LYTildeExpand(fname, FALSE); } else { char temp[LY_MAXPATH]; LYAddPathToHome(temp, sizeof(temp), *fname); StrAllocCopy(*fname, temp); } } return *fname; } /* * Expand a "leading" tilde into the user's home directory in WWW format. If * "embedded" is true, allow that "leading" tilde to follow a path separator. */ char *LYTildeExpand(char **pathname, int embedded) { char *temp = FindLeadingTilde(*pathname, embedded); if (LYIsTilde(temp[0])) { CTRACE((tfp, "LYTildeExpand %s\n", *pathname)); if (LYIsPathSep(temp[1])) { char *first = NULL; char *second = NULL; StrAllocCopy(first, *pathname); first[temp - *pathname] = '\0'; StrAllocCopy(second, temp + 2); StrAllocCopy(*pathname, first); StrAllocCat(*pathname, wwwName(Home_Dir())); LYAddPathSep(pathname); StrAllocCat(*pathname, second); FREE(first); FREE(second); } else if (temp[1] == '\0') { StrAllocCopy(*pathname, wwwName(Home_Dir())); #ifndef NOUSERS } else { char *save; char saved = '\0'; struct passwd *pw; for (save = temp; *save != '\0'; ++save) { if (LYIsPathSep(*save)) { saved = *save; *save = '\0'; break; } } pw = getpwnam(temp + 1); *save = saved; if (pw != 0 && non_empty(pw->pw_dir)) { temp = NULL; StrAllocCopy(temp, save); StrAllocCopy(*pathname, pw->pw_dir); StrAllocCat(*pathname, temp); FREE(temp); } #endif } CTRACE((tfp, "expanded path %s\n", *pathname)); } return *pathname; } /* * This function appends fname to the home path and returns the full path and * filename. The fname string can be just a filename (e.g., * "lynx_bookmarks.html"), or include a subdirectory off the home directory, in * which case fname should begin with "./" (e.g., ./BM/lynx_bookmarks.html) Use * LYPathOffHomeOK() to check and/or fix up fname before calling this function. * On VMS, the resultant full path and filename are converted to VMS syntax. - * FM */ void LYAddPathToHome(char *fbuffer, size_t fbuffer_size, const char *fname) { char *home = NULL; const char *file = fname; int len; /* * Make sure we have a buffer. - FM */ if (!fbuffer) return; if (fbuffer_size < 2) { fbuffer[0] = '\0'; return; } fbuffer[(fbuffer_size - 1)] = '\0'; /* * Make sure we have a file name. - FM */ if (!file) file = ""; /* * Set up home string and length. - FM */ StrAllocCopy(home, Home_Dir()); #ifdef VMS #define NO_HOMEPATH "Error:" #else #define NO_HOMEPATH "/error" #endif /* VMS */ if (!non_empty(home)) /* * Home_Dir() has a bug if this ever happens. - FM */ StrAllocCopy(home, NO_HOMEPATH); len = (int) fbuffer_size - ((int) strlen(home) + 1); if (len <= 0) { /* * Buffer is smaller than or only big enough for the home path. Load * what fits of the home path and return. This will fail, but we need * something in the buffer. - FM */ LYStrNCpy(fbuffer, home, (fbuffer_size - 1)); FREE(home); return; } #ifdef VMS /* * Check whether we have a subdirectory path or just a filename. - FM */ if (!StrNCmp(file, "./", 2)) { /* * We have a subdirectory path. - FM */ if (home[strlen(home) - 1] == ']') { /* * We got the home directory, so convert it to SHELL syntax and * append subdirectory path, then convert that to VMS syntax. - FM */ char *temp = NULL; HTSprintf0(&temp, "%s%s", HTVMS_wwwName(home), (file + 1)); sprintf(fbuffer, "%.*s", (fbuffer_size - 1), HTVMS_name("", temp)); FREE(temp); } else { /* * This will fail, but we need something in the buffer. - FM */ sprintf(fbuffer, "%s%.*s", home, len, file); } } else { /* * We have a file in the home directory. - FM */ sprintf(fbuffer, "%s%.*s", home, len, file); } #else /* * Check whether we have a subdirectory path or just a filename. - FM */ sprintf(fbuffer, "%s/%.*s", home, len, (StrNCmp(file, "./", 2) ? file : (file + 2))); #endif /* VMS */ FREE(home); } /* * Given a filename, concatenate it to the save-space pathname, unless it is * an absolute pathname. If there is no save-space defined, use the home * directory. Return a new string with the result. */ char *LYAddPathToSave(char *fname) { char *result = NULL; if (LYisAbsPath(fname)) { StrAllocCopy(result, fname); } else { if (non_empty(lynx_save_space)) { StrAllocCopy(result, lynx_save_space); } else { char temp[LY_MAXPATH]; LYAddPathToHome(temp, sizeof(temp), fname); StrAllocCopy(result, temp); } } return result; } #if !defined(HAVE_PUTENV) && !defined(_WINDOWS) /* * No putenv on the NeXT so we use this code instead! */ /* Copyright (C) 1991 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #if defined(STDC_HEADERS) || defined(USG) #include <string.h> #else /* Not (STDC_HEADERS or USG): */ #include <strings.h> #endif /* STDC_HEADERS or USG */ #ifndef NULL #define NULL 0 #endif /* !NULL */ extern char **environ; /* * Put STRING, which is of the form "NAME=VALUE", in the environment. */ int putenv(const char *string) { char *name_end = StrChr(string, '='); register size_t size; register char **ep; if (name_end == NULL) { /* Remove the variable from the environment. */ size = strlen(string); for (ep = environ; *ep != NULL; ++ep) if (!StrNCmp(*ep, string, size) && (*ep)[size] == '=') { while (ep[1] != NULL) { ep[0] = ep[1]; ++ep; } *ep = NULL; return 0; } } size = 0; for (ep = environ; *ep != NULL; ++ep) if (!StrNCmp(*ep, string, name_end - string) && (*ep)[name_end - string] == '=') break; else ++size; if (*ep == NULL) { static char **last_environ = NULL; char **new_environ = (char **) malloc((size + 2) * sizeof(char *)); if (new_environ == NULL) return -1; (void) memcpy((char *) new_environ, (char *) environ, size * sizeof(char *)); new_environ[size] = (char *) string; new_environ[size + 1] = NULL; if (last_environ != NULL) FREE(last_environ); last_environ = new_environ; environ = new_environ; } else *ep = (char *) string; return 0; } #endif /* !HAVE_PUTENV */ #ifdef NEED_REMOVE int remove(char *name) { return unlink(name); } #endif #if defined(MULTI_USER_UNIX) #if defined(HAVE_LSTAT) && defined(S_IFLNK) /* * If IsOurFile() is checking a symbolic link, ensure that the target * points to the user's file as well. */ static BOOL IsOurSymlink(const char *name) { BOOL result = FALSE; size_t size = LY_MAXPATH; size_t used; char *buffer = typeMallocn(char, (unsigned) size); char *check; if (buffer != 0) { while ((used = (size_t) readlink(name, buffer, (size - 1))) == size - 1) { check = typeRealloc(char, buffer, (unsigned) (size *= 2)); if (check == 0) break; buffer = check; } if (buffer != 0) { if ((int) used > 0) { buffer[used] = '\0'; } else { FREE(buffer); } } } if (buffer != 0) { if (!LYisAbsPath(buffer)) { char *cutoff = LYLastPathSep(name); char *clone = NULL; if (cutoff != 0) { HTSprintf0(&clone, "%.*s%s%s", (int) (cutoff - name), name, FILE_SEPARATOR, buffer); FREE(buffer); buffer = clone; } } CTRACE2(TRACE_CFG, (tfp, "IsOurSymlink(%s -> %s)\n", name, buffer)); result = IsOurFile(buffer); FREE(buffer); } return result; } #endif /* * Verify if this is really a file which is not accessed by a symbolic link, * except for the special case of its directory being pointed to by a link from * a directory owned by root and not writable by other users. */ BOOL IsOurFile(const char *name) { BOOL result = FALSE; struct stat data; if (!LYIsTilde(name[0]) && lstat(name, &data) == 0 && ((S_ISREG(data.st_mode) && (data.st_mode & (S_IWOTH | S_IWGRP)) == 0 && data.st_uid == getuid()) #if defined(HAVE_LSTAT) && defined(S_IFLNK) || (S_ISLNK(data.st_mode) && IsOurSymlink(name)) #endif )) { int linked = FALSE; /* * ( If this is not a single-user system, the other user is presumed by * some people busy trying to use a symlink attack on our files ;-) */ #if defined(HAVE_LSTAT) char *path = 0; char *leaf; StrAllocCopy(path, name); do { if ((leaf = LYPathLeaf(path)) != path) *--leaf = '\0'; /* write a null on the '/' */ if (lstat(*path ? path : "/", &data) != 0) { break; } /* * If we find a symbolic link, it has to be in a directory that's * protected. Otherwise someone could have switched it to point * to one of the real user's files. */ if (S_ISLNK(data.st_mode)) { linked = TRUE; /* could be link-to-link; doesn't matter */ } else if (S_ISDIR(data.st_mode)) { if (linked) { linked = FALSE; /* * We assume that a properly-configured system has the * unwritable directories owned by root. This is not * necessarily so (bin, news, etc., may), but the only * uid we can count on is 0. It would be nice to add a * check for the gid also, but that wouldn't be * portable. * * Likewise, the t-bit would be nice to rely upon. However * it is marked as an extension in POSIX, rather than * required. */ if (data.st_uid != 0 || (data.st_mode & S_IWOTH) != 0) { linked = TRUE; /* force an error-return */ break; } } } else if (linked) { break; } } while (leaf != path); FREE(path); #endif result = (BOOLEAN) !linked; } CTRACE2(TRACE_CFG, (tfp, "IsOurFile(%s) %d\n", name, result)); return result; } /* * Open a file that we don't want other users to see. */ static FILE *OpenHiddenFile(const char *name, const char *mode) { FILE *fp = 0; struct stat data; BOOLEAN binary = (BOOLEAN) (StrChr(mode, 'b') != 0); #if defined(O_CREAT) && defined(O_EXCL) /* we have fcntl.h or kindred? */ /* * This is the preferred method for creating new files, since it ensures * that no one has an existing file or link that they happen to own. */ if (*mode == 'w') { int fd = open(name, O_CREAT | O_EXCL | O_WRONLY, HIDE_CHMOD); if (fd < 0 && errno == EEXIST && IsOurFile(name)) { if (remove(name) == 0) { /* FIXME: there's a race at this point if directory is open */ fd = open(name, O_CREAT | O_EXCL | O_WRONLY, HIDE_CHMOD); } } if (fd >= 0) { #if defined(O_BINARY) && defined(__CYGWIN__) if (binary) setmode(fd, O_BINARY); #endif fp = fdopen(fd, mode); } } else #endif if (*mode == 'a') { if (IsOurFile(name) && chmod(name, HIDE_CHMOD) == 0) fp = fopen(name, mode); else if (lstat(name, &data) != 0) fp = OpenHiddenFile(name, binary ? BIN_W : TXT_W); /* * This is less stringent, but reasonably portable. For new files, the * umask will suffice; however if the file already exists we'll change * permissions first, before opening it. If the chmod fails because of * some reason other than a non-existent file, there's no point in trying * to open it. * * This won't work properly if the user is root, since the chmod succeeds. */ } else if (*mode != 'a') { mode_t save = umask(HIDE_UMASK); if (chmod(name, HIDE_CHMOD) == 0 || errno == ENOENT) fp = fopen(name, mode); (void) umask(save); } return fp; } #else #define OpenHiddenFile(name, mode) fopen(name, mode) #endif /* MULTI_USER_UNIX */ FILE *LYNewBinFile(const char *name) { #ifdef VMS FILE *fp = fopen(name, BIN_W, "mbc=32"); (void) chmod(name, HIDE_CHMOD); #else FILE *fp = OpenHiddenFile(name, BIN_W); #endif return fp; } FILE *LYNewTxtFile(const char *name) { FILE *fp; #ifdef VMS fp = fopen(name, TXT_W, "shr=get"); (void) chmod(name, HIDE_CHMOD); #else SetDefaultMode(O_TEXT); fp = OpenHiddenFile(name, TXT_W); SetDefaultMode(O_BINARY); #endif return fp; } FILE *LYAppendToTxtFile(const char *name) { FILE *fp; #ifdef VMS fp = fopen(name, TXT_A, "shr=get"); (void) chmod(name, HIDE_CHMOD); #else SetDefaultMode(O_TEXT); fp = OpenHiddenFile(name, TXT_A); SetDefaultMode(O_BINARY); #endif return fp; } #if defined(MULTI_USER_UNIX) /* * Restore normal permissions to a copy of a file that we have created with * temp file restricted permissions. The normal umask should apply for user * files. - kw */ void LYRelaxFilePermissions(const char *name) { mode_t mode; struct stat stat_buf; if (stat(name, &stat_buf) == 0 && S_ISREG(stat_buf.st_mode) && (mode = (stat_buf.st_mode & 0777)) == HIDE_CHMOD) { /* * It looks plausible that this is a file we created with temp file * paranoid permissions (and the umask wasn't even more restrictive * when it was copied). - kw */ mode_t save = umask(HIDE_UMASK); mode = (mode_t) (((mode & 0700) | 0066) & ~save); (void) umask(save); (void) chmod(name, mode); } } #endif /* * Check if the given anchor has an associated file-cache. */ BOOLEAN LYCachedTemp(char *target, char **cached) { BOOLEAN result = FALSE; if (*cached) { LYStrNCpy(target, *cached, LY_MAXPATH); FREE(*cached); if (LYCanReadFile(target)) { if (remove(target) != 0) { CTRACE((tfp, "cannot remove %s\n", target)); } } result = TRUE; } return result; } #ifndef HAVE_MKDTEMP #define mkdtemp(path) ((mktemp(path) != 0) && (mkdir(path, 0700) == 0)) #endif /* * Open a temp-file, ensuring that it is unique, and not readable by other * users. * * The mode can be one of: "w", "a", "wb". */ FILE *LYOpenTemp(char *result, const char *suffix, const char *mode) { FILE *fp = 0; BOOL txt = TRUE; char wrt = 'r'; LY_TEMP *p; CTRACE((tfp, "LYOpenTemp(,%s,%s)\n", suffix, mode)); if (result == 0) return 0; while (*mode != '\0') { switch (*mode++) { case 'w': wrt = 'w'; break; case 'a': wrt = 'a'; break; case 'b': txt = FALSE; break; default: CTRACE((tfp, "%s @%d: BUG\n", __FILE__, __LINE__)); return 0; } } /* * Verify if the given space looks secure enough. Otherwise, make a * secure subdirectory of that. */ #if defined(MULTI_USER_UNIX) && (defined(HAVE_MKTEMP) || defined(HAVE_MKDTEMP)) if (lynx_temp_subspace == 0) { BOOL make_it = FALSE; struct stat sb; if (lstat(lynx_temp_space, &sb) == 0 && S_ISDIR(sb.st_mode)) { if (sb.st_uid != getuid() || (sb.st_mode & (S_IWOTH | S_IWGRP)) != 0) { make_it = TRUE; CTRACE((tfp, "lynx_temp_space is not our directory %s owner %d mode %03o\n", lynx_temp_space, (int) sb.st_uid, (int) sb.st_mode & 0777)); } } else { make_it = TRUE; CTRACE((tfp, "lynx_temp_space is not a directory %s\n", lynx_temp_space)); } if (make_it) { mode_t old_mask = umask(HIDE_UMASK); StrAllocCat(lynx_temp_space, "lynxXXXXXXXXXX"); if (mkdtemp(lynx_temp_space) == 0) { printf("%s: %s\n", lynx_temp_space, LYStrerror(errno)); exit_immediately(EXIT_FAILURE); } (void) umask(old_mask); lynx_temp_subspace = 1; StrAllocCat(lynx_temp_space, "/"); CTRACE((tfp, "made subdirectory %s\n", lynx_temp_space)); } else { lynx_temp_subspace = -1; } } #endif do { if (!fmt_tempname(result, lynx_temp_space, suffix)) return 0; if (txt) { switch (wrt) { case 'w': fp = LYNewTxtFile(result); break; case 'a': fp = LYAppendToTxtFile(result); break; } } else { fp = LYNewBinFile(result); } /* * If we get a failure to make a temporary file, don't bother to try a * different name unless the failure was because the file already * exists. */ #ifdef EEXIST /* FIXME (need a better test) in fcntl.h or unistd.h */ if ((fp == 0) && (errno != EEXIST)) { CTRACE((tfp, "... LYOpenTemp(%s) failed: %s\n", result, LYStrerror(errno))); return 0; } #endif } while (fp == 0); if ((p = typecalloc(LY_TEMP)) != 0) { p->next = ly_temp; StrAllocCopy((p->name), result); p->file = fp; p->outs = (BOOLEAN) (wrt != 'r'); ly_temp = p; } else { outofmem(__FILE__, "LYOpenTemp"); } CTRACE((tfp, "... LYOpenTemp(%s)\n", result)); return fp; } /* * Reopen a temporary file */ FILE *LYReopenTemp(char *name) { LY_TEMP *p; FILE *fp = 0; LYCloseTemp(name); if ((p = FindTempfileByName(name)) != 0) { fp = p->file = LYAppendToTxtFile(name); } return fp; } /* * Open a temp-file for writing, possibly re-using a previously used * name and file. * If a non-empty fname is given, it is reused if it indicates a file * previously registered as a temp file and, in case the file still * exists, if it looks like we can write to it safely. Otherwise a * new temp file (with new name) will be generated and returned in fname. * * File permissions are set so that the file is not readable by unprivileged * other users. * * Suffix is only used if fname is not being reused. * The mode should be "w", others are possible (they may be passed on) * but probably don't make sense. - kw */ FILE *LYOpenTempRewrite(char *fname, const char *suffix, const char *mode) { FILE *fp = 0; BOOL txt = TRUE; char wrt = 'r'; BOOL registered = NO; BOOL writable_exists = NO; BOOL is_ours = NO; BOOL still_open = NO; LY_TEMP *p; struct stat stat_buf; CTRACE((tfp, "LYOpenTempRewrite(%s,%s,%s)\n", fname, suffix, mode)); if (*fname == '\0') /* first time, no filename yet */ return (LYOpenTemp(fname, suffix, mode)); if ((p = FindTempfileByName(fname)) != 0) { registered = YES; if (p->file != 0) still_open = YES; CTRACE((tfp, "...used before%s\n", still_open ? ", still open!" : ".")); } if (registered) { #ifndef NO_GROUPS writable_exists = HTEditable(fname); /* existing, can write */ #define CTRACE_EXISTS "exists and is writable, " #else writable_exists = (BOOL) (stat(fname, &stat_buf) == 0); /* existing, assume can write */ #define CTRACE_EXISTS "exists, " #endif if (writable_exists) { is_ours = IsOurFile(fname); } CTRACE((tfp, "...%s%s\n", writable_exists ? CTRACE_EXISTS : "", is_ours ? "is our file." : "is NOT our file.")); } /* * Note that in cases where LYOpenTemp is called as fallback below, we * don't call LYRemoveTemp first. That may be appropriate in some cases, * but not trying to remove a weird existing file seems safer and could * help diagnose an unusual situation. (They may be removed anyway later.) */ if (still_open) { /* * This should probably not happen. Make a new one. */ return (LYOpenTemp(fname, suffix, mode)); } else if (!registered) { /* * Not registered. It should have been registered at one point though, * otherwise we wouldn't be called like this. */ return (LYOpenTemp(fname, suffix, mode)); } else if (writable_exists && !is_ours) { /* * File exists, writable if we checked, but something is wrong with it. */ return (LYOpenTemp(fname, suffix, mode)); #ifndef NO_GROUPS } else if (!is_ours && (lstat(fname, &stat_buf) == 0)) { /* * Exists but not writable, and something is wrong with it. */ return (LYOpenTemp(fname, suffix, mode)); #endif } while (*mode != '\0') { switch (*mode++) { case 'w': wrt = 'w'; break; case 'a': wrt = 'a'; break; case 'b': txt = FALSE; break; default: CTRACE((tfp, "%s @%d: BUG\n", __FILE__, __LINE__)); return fp; } } if (is_ours) { /* * Yes, it exists, is writable if we checked, and everything looks ok * so far. This should be the most regular case. - kw */ #ifdef HAVE_TRUNCATE if (txt == TRUE) { /* limitation of LYReopenTemp. shrug */ /* * We truncate and then append, this avoids having a small window * in which the file doesn't exist. - kw */ if (truncate(fname, (off_t) 0) != 0) { CTRACE((tfp, "... truncate(%s,0) failed: %s\n", fname, LYStrerror(errno))); return (LYOpenTemp(fname, suffix, mode)); } else { return (LYReopenTemp(fname)); } } #endif remove(fname); } /* We come here in two cases: either the file existed and was ours and we * just got rid of it. Or the file did and does not exist, but is * registered as a temp file. It must have been removed by some means * other than LYRemoveTemp. In both cases, reuse the name! - kw */ if (txt) { switch (wrt) { case 'w': fp = LYNewTxtFile(fname); break; case 'a': fp = LYAppendToTxtFile(fname); break; } } else { fp = LYNewBinFile(fname); } p->file = fp; CTRACE((tfp, "... LYOpenTempRewrite(%s), %s\n", fname, (fp) ? "ok" : "failed")); /* * We could fall back to trying LYOpenTemp() here in case of failure. * After all the checks already done above a filure here should be pretty * unusual though, so maybe it's better to let the user notice that * something went wrong, and not try to fix it up. - kw */ return fp; } /* * Special case of LYOpenTemp, used for manipulating bookmark file, i.e., with * renaming. */ FILE *LYOpenScratch(char *result, const char *prefix) { FILE *fp; LY_TEMP *p; if (!fmt_tempname(result, prefix, HTML_SUFFIX)) return 0; if ((fp = LYNewTxtFile(result)) != 0) { if ((p = typecalloc(LY_TEMP)) != 0) { p->next = ly_temp; StrAllocCopy((p->name), result); p->file = fp; ly_temp = p; } else { outofmem(__FILE__, "LYOpenScratch"); } } CTRACE((tfp, "LYOpenScratch(%s)\n", result)); return fp; } static void LY_close_temp(LY_TEMP * p) { if (p->file != 0) { if (p->outs) { LYCloseOutput(p->file); } else { LYCloseInput(p->file); } p->file = 0; } } /* * Close a temp-file, given its name */ void LYCloseTemp(char *name) { LY_TEMP *p; CTRACE((tfp, "LYCloseTemp(%s)\n", name)); if ((p = FindTempfileByName(name)) != 0) { CTRACE((tfp, "...LYCloseTemp(%s)%s\n", name, (p->file != 0) ? ", closed" : "")); LY_close_temp(p); } } /* * Close a temp-file, given its file-pointer */ void LYCloseTempFP(FILE *fp) { LY_TEMP *p; CTRACE((tfp, "LYCloseTempFP\n")); if ((p = FindTempfileByFP(fp)) != 0) { LY_close_temp(p); CTRACE((tfp, "...LYCloseTempFP(%s)\n", p->name)); } } /* * Close a temp-file, removing it. */ int LYRemoveTemp(char *name) { LY_TEMP *p, *q; int code = -1; if (non_empty(name)) { CTRACE((tfp, "LYRemoveTemp(%s)\n", name)); for (p = ly_temp, q = 0; p != 0; q = p, p = p->next) { if (!strcmp(name, p->name)) { if (q != 0) { q->next = p->next; } else { ly_temp = p->next; } LY_close_temp(p); code = HTSYS_remove(name); CTRACE((tfp, "...LYRemoveTemp done(%d)%s\n", code, (p->file != 0) ? ", closed" : "")); CTRACE_FLUSH(tfp); FREE(p->name); FREE(p); break; } } } return code; } /* * Remove all of the temp-files. Note that this assumes that they are closed, * since some systems will not allow us to remove a file which is open. */ void LYCleanupTemp(void) { while (ly_temp != 0) { (void) LYRemoveTemp(ly_temp->name); } #if defined(MULTI_USER_UNIX) if (lynx_temp_subspace > 0) { char result[LY_MAXPATH]; LYStrNCpy(result, lynx_temp_space, sizeof(result) - 1); LYTrimPathSep(result); CTRACE((tfp, "LYCleanupTemp removing %s\n", result)); rmdir(result); lynx_temp_subspace = -1; } #endif } /* * We renamed a temporary file. Keep track so we can remove it on exit. */ void LYRenamedTemp(char *oldname, char *newname) { LY_TEMP *p; CTRACE((tfp, "LYRenamedTemp(old=%s, new=%s)\n", oldname, newname)); if ((p = FindTempfileByName(oldname)) != 0) { StrAllocCopy((p->name), newname); } } #ifndef DISABLE_BIBP /* * Check that bibhost defines the BibP icon. */ void LYCheckBibHost(void) { DocAddress bibhostIcon; BOOLEAN saveFlag; bibhostIcon.address = NULL; StrAllocCopy(bibhostIcon.address, BibP_bibhost); StrAllocCat(bibhostIcon.address, "bibp1.0/bibpicon.jpg"); bibhostIcon.post_data = NULL; bibhostIcon.post_content_type = NULL; bibhostIcon.bookmark = FALSE; bibhostIcon.isHEAD = FALSE; bibhostIcon.safe = FALSE; saveFlag = traversal; traversal = TRUE; /* Hack to force error response. */ BibP_bibhost_available = (BOOLEAN) (HTLoadAbsolute(&bibhostIcon) == YES); traversal = saveFlag; BibP_bibhost_checked = TRUE; } #endif /* !DISABLE_BIBP */ /* * Management of User Interface Pages. - kw * * These are mostly temp files. Pages which can be recognized by their special * URL (after having been loaded) need not be tracked here. * * First some private stuff: */ typedef struct uipage_entry { UIP_t type; unsigned flags; char *url; HTList *alturls; char *file; } uip_entry; #define UIP_F_MULTI 0x0001 /* flag: track multiple instances */ #define UIP_F_LIMIT 0x0002 /* flag: limit size of alturls list */ #define UIP_F_LMULTI (UIP_F_MULTI | UIP_F_LIMIT) /* *INDENT-OFF* */ static uip_entry ly_uip[] = { { UIP_HISTORY , UIP_F_LMULTI, NULL, NULL, NULL } , { UIP_DOWNLOAD_OPTIONS , 0 , NULL, NULL, NULL } , { UIP_PRINT_OPTIONS , 0 , NULL, NULL, NULL } , { UIP_SHOWINFO , UIP_F_LMULTI, NULL, NULL, NULL } , { UIP_LIST_PAGE , UIP_F_LMULTI, NULL, NULL, NULL } , { UIP_VLINKS , UIP_F_LMULTI, NULL, NULL, NULL } #if !defined(NO_OPTION_FORMS) , { UIP_OPTIONS_MENU , UIP_F_LMULTI, NULL, NULL, NULL } #endif #ifdef DIRED_SUPPORT , { UIP_DIRED_MENU , 0 , NULL, NULL, NULL } , { UIP_PERMIT_OPTIONS , 0 , NULL, NULL, NULL } , { UIP_UPLOAD_OPTIONS , UIP_F_LMULTI, NULL, NULL, NULL } #endif #ifdef USE_ADDRLIST_PAGE , { UIP_ADDRLIST_PAGE , UIP_F_LMULTI, NULL, NULL, NULL } #endif , { UIP_LYNXCFG , UIP_F_LMULTI, NULL, NULL, NULL } #if !defined(NO_CONFIG_INFO) , { UIP_CONFIG_DEF , UIP_F_LMULTI, NULL, NULL, NULL } #endif /* The following are not generated tempfiles: */ , { UIP_TRACELOG , 0 , NULL, NULL, NULL } #if defined(DIRED_SUPPORT) && defined(OK_INSTALL) , { UIP_INSTALL , 0 , NULL, NULL, NULL } #endif }; /* *INDENT-ON* */ /* Public entry points for User Interface Page management: */ BOOL LYIsUIPage3(const char *url, UIP_t type, int flagparam) { unsigned int i; size_t l; BOOL result = NO; if (url) { for (i = 0; i < TABLESIZE(ly_uip); i++) { if (ly_uip[i].type == type) { if (!ly_uip[i].url) { break; } else if ((flagparam & UIP_P_FRAG) ? (!StrNCmp(ly_uip[i].url, url, (l = strlen(ly_uip[i].url))) && (url[l] == '\0' || url[l] == '#')) : !strcmp(ly_uip[i].url, url)) { result = YES; } else if (ly_uip[i].flags & UIP_F_MULTI) { char *p; HTList *l0 = ly_uip[i].alturls; while ((p = (char *) HTList_nextObject(l0)) != NULL) { if ((flagparam & UIP_P_FRAG) ? (!StrNCmp(p, url, (l = strlen(p))) && (url[l] == '\0' || url[l] == '#')) : !strcmp(p, url)) { result = YES; break; } } } break; } } } return result; } void LYRegisterUIPage(const char *url, UIP_t type) { unsigned int i; for (i = 0; i < TABLESIZE(ly_uip); i++) { if (ly_uip[i].type == type) { if (ly_uip[i].url && url && !strcmp(ly_uip[i].url, url)) { } else if (!ly_uip[i].url || !url || !(ly_uip[i].flags & UIP_F_MULTI)) { StrAllocCopy(ly_uip[i].url, url); } else { char *p; int n = 0; HTList *l0 = ly_uip[i].alturls; while ((p = (char *) HTList_nextObject(l0)) != NULL) { if (!strcmp(p, url)) return; if (!strcmp(p, ly_uip[i].url)) { StrAllocCopy(ly_uip[i].url, url); return; } n++; } if (!ly_uip[i].alturls) ly_uip[i].alturls = HTList_new(); if (n >= HTCacheSize && (ly_uip[i].flags & UIP_F_LIMIT)) HTList_removeFirstObject(ly_uip[i].alturls); HTList_addObject(ly_uip[i].alturls, ly_uip[i].url); ly_uip[i].url = NULL; StrAllocCopy(ly_uip[i].url, url); } return; } } } void LYUIPages_free(void) { unsigned int i; for (i = 0; i < TABLESIZE(ly_uip); i++) { FREE(ly_uip[i].url); FREE(ly_uip[i].file); LYFreeStringList(ly_uip[i].alturls); ly_uip[i].alturls = NULL; } } /* * Convert local pathname to www name * (do not bother about file://localhost prefix at this point). */ const char *wwwName(const char *pathname) { const char *cp = NULL; #if defined(USE_DOS_DRIVES) cp = HTDOS_wwwName(pathname); #else #ifdef VMS cp = HTVMS_wwwName(pathname); #else cp = pathname; #endif /* VMS */ #endif return cp; } /* * Given a user-specified filename, e.g., for download or print, validate and * expand it. Expand home-directory expressions in the given string. Only * allow pipes if the user can spawn shell commands. */ BOOLEAN LYValidateFilename(bstring **result, bstring **given) { BOOLEAN code = TRUE; char *cp = NULL; /* * Cancel if the user entered "/dev/null" on Unix, or an "nl:" path on VMS. * - FM */ if (LYIsNullDevice((*given)->str)) { /* just ignore it */ code = FALSE; #ifdef HAVE_POPEN } else if (LYIsPipeCommand((*given)->str)) { if (no_shell) { HTUserMsg(SPAWNING_DISABLED); code = FALSE; } else { BStrCopy(*result, (*given)); } #endif } else { if (FindLeadingTilde((*given)->str, TRUE) != 0) { char *cp1 = NULL; StrAllocCopy(cp1, (*given)->str); LYTildeExpand(&cp1, TRUE); BStrCopy0(*result, cp1); BStrCopy0(*given, cp1); FREE(cp1); } #ifdef VMS if (StrChr((*given)->str, '/') != NULL) { BStrCopy0(*result, HTVMS_name("", (*given)->str)); BStrCopy(*given, *result); } if ((*given)->str[0] != '/' && StrChr((*given)->str, ':') == NULL) { BStrCopy0(*result, "sys$disk:"); if (StrChr((*given)->str, ']') == NULL) BStrCat0(*result, "[]"); BStrCat(*result, (*given)); } else { BStrCopy(*result, (*given)); } #else #ifndef __EMX__ if (!LYisAbsPath((*given)->str)) { #if defined(__DJGPP__) || defined(_WINDOWS) if (StrChr((*result)->str, ':') != NULL) cp = NULL; else #endif /* __DJGPP__ || _WINDOWS */ { #ifdef SUPPORT_CHDIR static char buf[LY_MAXPATH]; cp = Current_Dir(buf); #else cp = original_dir; #endif } } else #endif /* __EMX__ */ cp = NULL; if (cp) { LYTrimPathSep(cp); BStrCopy0(*result, cp); BStrCat0(*result, "/"); } else { BStrCopy0(*result, ""); } if (code) { cp = HTSYS_name((*given)->str); BStrCat0(*result, cp); } #endif /* VMS */ } return code; } /* * Given a valid filename, check if it exists. If so, we'll have to worry * about overwriting it. * * Returns: * 'Y' (yes/success) * 'N' (no/retry) * 3 (cancel) */ int LYValidateOutput(char *filename) { int c; CTRACE((tfp, "LYValidateOutput '%s'\n", filename)); /* * Assume we can write to a pipe */ #ifdef HAVE_POPEN if (LYIsPipeCommand(filename)) return 'Y'; #endif if (no_dotfiles || !show_dotfiles) { if (*LYPathLeaf(filename) == '.') { HTAlert(FILENAME_CANNOT_BE_DOT); return 'N'; } } /* * See if it already exists. */ if (LYCanReadFile(filename)) { #ifdef VMS c = HTConfirm(FILE_EXISTS_HPROMPT); #else c = HTConfirm(FILE_EXISTS_OPROMPT); #endif /* VMS */ if (HTLastConfirmCancelled()) { HTInfoMsg(SAVE_REQUEST_CANCELLED); return 3; } else if (c == NO) { return 'N'; } } else if (!LYCanWriteFile(filename)) { return 'N'; } return 'Y'; } /* * Convert a local filename to a URL */ void LYLocalFileToURL(char **target, const char *source) { const char *leaf; StrAllocCopy(*target, "file://localhost"); leaf = wwwName(source); if (!LYisAbsPath(source)) { char temp[LY_MAXPATH]; Current_Dir(temp); if (!LYIsHtmlSep(*temp)) LYAddHtmlSep(target); StrAllocCat(*target, temp); } if (leaf && !LYIsHtmlSep(*leaf)) LYAddHtmlSep(target); StrAllocCat(*target, leaf); } #define MY_DOCTYPE "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n" #define PUT_STRING(buf) (*(target)->isa->put_string)(target, buf) /* * Like WriteInternalTitle, used for writing title on pages constructed via * streams. */ void WriteStreamTitle(HTStream *target, const char *Title) { char *buf = 0; PUT_STRING(MY_DOCTYPE); PUT_STRING("<html>\n<head>\n"); LYAddMETAcharsetToStream(target, -1); HTSprintf0(&buf, "<title>%s</title>\n</head>\n<body>\n", Title); PUT_STRING(buf); FREE(buf); } /* * Open a temporary file for internal-pages, optionally reusing an existing * filename. */ FILE *InternalPageFP(char *filename, int reuse_flag) { FILE *fp; if (LYReuseTempfiles && reuse_flag) { fp = LYOpenTempRewrite(filename, HTML_SUFFIX, BIN_W); } else { (void) LYRemoveTemp(filename); fp = LYOpenTemp(filename, HTML_SUFFIX, BIN_W); } if (fp == NULL) { HTAlert(CANNOT_OPEN_TEMP); } return fp; } /* * This part is shared by all internal pages. */ void WriteInternalTitle(FILE *fp0, const char *Title) { fprintf(fp0, MY_DOCTYPE); fprintf(fp0, "<html>\n<head>\n"); LYAddMETAcharsetToFD(fp0, -1); if (LYIsListpageTitle(Title)) { if (StrChr(HTLoadedDocumentURL(), '"') == NULL) { char *Address = NULL; /* * Insert a BASE tag so there is some way to relate the List Page * file to its underlying document after we are done. It won't be * actually used for resolving relative URLs. - kw */ StrAllocCopy(Address, HTLoadedDocumentURL()); LYEntify(&Address, FALSE); fprintf(fp0, "<base href=\"%s\">\n", Address); FREE(Address); } } fprintf(fp0, "<title>%s</title>\n</head>\n<body>\n", Title); } /* * This is used to start most internal pages, except for special cases where * the embedded HREF's in the title differ. */ void BeginInternalPage(FILE *fp0, const char *Title, const char *HelpURL) { WriteInternalTitle(fp0, Title); if ((user_mode == NOVICE_MODE) && LYwouldPush(Title, NULL) && (HelpURL != 0)) { fprintf(fp0, "<h1>%s (%s%s%s), <a href=\"%s%s\">help</a></h1>\n", Title, LYNX_NAME, VERSION_SEGMENT, LYNX_VERSION, helpfilepath, HelpURL); } else { fprintf(fp0, "<h1>%s (%s%s%s)</h1>\n", Title, LYNX_NAME, VERSION_SEGMENT, LYNX_VERSION); } } void EndInternalPage(FILE *fp0) { fprintf(fp0, "</body>\n</html>"); } char *trimPoundSelector(char *address) { char *pound = findPoundSelector(address); if (pound != 0) *pound = '\0'; return pound; } /* * Trim a trailing path-separator to avoid confusing other programs when we concatenate * to it. This only applies to local filesystems. */ void LYTrimPathSep(char *path) { size_t len; if (path != 0 && (len = strlen(path)) != 0 && LYIsPathSep(path[len - 1])) path[len - 1] = 0; } /* * Add a trailing path-separator to avoid confusing other programs when we concatenate * to it. This only applies to local filesystems. */ void LYAddPathSep(char **path) { size_t len; char *temp; if ((path != 0) && ((temp = *path) != 0) && (len = strlen(temp)) != 0 && !LYIsPathSep(temp[len - 1])) { StrAllocCat(*path, FILE_SEPARATOR); } } /* * Add a trailing path-separator to avoid confusing other programs when we concatenate * to it. This only applies to local filesystems. */ void LYAddPathSep0(char *path) { size_t len; if ((path != 0) && (len = strlen(path)) != 0 && (len < LY_MAXPATH - 2) && !LYIsPathSep(path[len - 1])) { strcat(path, FILE_SEPARATOR); } } /* * Check if a given string contains a path separator */ char *LYLastPathSep(const char *path) { char *result; #if defined(USE_DOS_DRIVES) if ((result = strrchr(path, '\\')) == 0) result = strrchr(path, '/'); #else result = strrchr(path, '/'); #endif return result; } /* * Trim a trailing path-separator to avoid confusing other programs when we concatenate * to it. This only applies to HTML paths. */ void LYTrimHtmlSep(char *path) { size_t len; if (path != 0 && (len = strlen(path)) != 0 && LYIsHtmlSep(path[len - 1])) path[len - 1] = 0; } /* * Add a trailing path-separator to avoid confusing other programs when we concatenate * to it. This only applies to HTML paths. */ void LYAddHtmlSep(char **path) { size_t len; char *temp; if ((path != 0) && ((temp = *path) != 0) && (len = strlen(temp)) != 0 && !LYIsHtmlSep(temp[len - 1])) { StrAllocCat(*path, "/"); } } /* * Add a trailing path-separator to avoid confusing other programs when we concatenate * to it. This only applies to HTML paths. */ void LYAddHtmlSep0(char *path) { size_t len; if ((path != 0) && (len = strlen(path)) != 0 && (len < LY_MAXPATH - 2) && !LYIsHtmlSep(path[len - 1])) { strcat(path, "/"); } } /* * Rename a file */ int LYRenameFile(char *src, char *dst) { #ifdef _WINDOWS /* * If dest_file exists prior to calling rename(), rename() will fail on Windows platforms. * https://www.securecoding.cert.org/confluence/display/c/FIO10-C.+Take+care+when+using+the+rename%28%29+function */ struct stat st; if (stat(dst, &st) == 0) { unlink(dst); } #endif return rename(src, dst); } /* * Copy a file */ int LYCopyFile(char *src, char *dst) { int code; const char *program; if ((program = HTGetProgramPath(ppCOPY)) != NULL) { char *the_command = 0; HTAddParam(&the_command, COPY_COMMAND, 1, program); HTAddParam(&the_command, COPY_COMMAND, 2, src); HTAddParam(&the_command, COPY_COMMAND, 3, dst); HTEndParam(&the_command, COPY_COMMAND, 3); CTRACE((tfp, "command: %s\n", the_command)); stop_curses(); code = LYSystem(the_command); start_curses(); FREE(the_command); } else { FILE *fin, *fout; unsigned char buff[BUFSIZ]; size_t len; code = EOF; if ((fin = fopen(src, BIN_R)) != 0) { if ((fout = fopen(dst, BIN_W)) != 0) { code = 0; while ((len = fread(buff, (size_t) 1, sizeof(buff), fin)) != 0) { if (fwrite(buff, (size_t) 1, len, fout) < len || ferror(fout)) { code = EOF; break; } } LYCloseOutput(fout); } LYCloseInput(fin); } CTRACE((tfp, "builtin copy ->%d\n\tsource=%s\n\ttarget=%s\n", code, src, dst)); } if (code) { HTAlert(CANNOT_WRITE_TO_FILE); } return code; } #ifdef __DJGPP__ static char *escape_backslashes(char *source) { char *result = 0; int count = 0; int n; for (n = 0; source[n] != '\0'; ++n) { if (source[n] == '\\') ++count; } if (count != 0) { result = malloc(count + n + 1); if (result != 0) { int ch; char *target = result; while ((ch = *source++) != '\0') { if (ch == '\\') *target++ = ch; *target++ = ch; } *target = '\0'; } } return result; } #endif /* __DJGPP__ */ /* * Invoke a shell command, return nonzero on error. */ int LYSystem(char *command) { int code; int do_free = 0; #if defined(HAVE_SIGACTION) && defined(SIGTSTP) && !defined(USE_SLANG) struct sigaction saved_sigtstp_act; BOOLEAN sigtstp_saved = FALSE; #endif int saved_errno = 0; #ifdef __EMX__ int scrsize[4]; #endif fflush(stdout); fflush(stderr); CTRACE((tfp, "LYSystem(%s)\n", command)); CTRACE_FLUSH(tfp); #ifdef __DJGPP__ __djgpp_set_ctrl_c(0); _go32_want_ctrl_break(1); #endif /* __DJGPP__ */ #ifdef VMS code = DCLsystem(command); #else # ifdef __EMX__ /* FIXME: Should be LY_CONVERT_SLASH? */ /* Configure writes commands which contain direct slashes. Native command-(non)-shell will not tolerate this. */ { char *space = command, *slash = command; _scrsize(scrsize); while (*space && *space != ' ' && *space != '\t') space++; while (slash < space && *slash != '/') slash++; if (slash != space) { char *old = command; command = NULL; StrAllocCopy(command, old); do_free = 1; slash = (slash - old) + command - 1; space = (space - old) + command; while (++slash < space) if (*slash == '/') *slash = '\\'; } } # endif /* * This chunk of code does not work, for two reasons: * a) the Cygwin system() function edits out the backslashes * b) it does not account for more than one parameter, e.g., +number */ #if defined(__CYGWIN__) && defined(DOSPATH) /* 1999/02/26 (Fri) */ { char cmd[LY_MAXPATH]; char win32_name[LY_MAXPATH]; char new_cmd[LY_MAXPATH]; char new_command[LY_MAXPATH * 2 + 10]; char *p, *q; p = command; q = cmd; while (*p) { if (*p == ' ') break; else *q = *p; p++; q++; } *q = '\0'; if (cmd[0] == '/') cygwin_conv_to_full_posix_path(cmd, new_cmd); else strcpy(new_cmd, cmd); while (*p == ' ') p++; if (StrChr(p, '\\') == NULL) { /* for Windows Application */ cygwin_conv_to_full_win32_path(p, win32_name); sprintf(new_command, "%.*s \"%.*s\"", LY_MAXPATH, new_cmd, LY_MAXPATH, win32_name); } else { /* for DOS like editor */ q = win32_name; while (*p) { if (*p == '\\') { if (*(p + 1) == '\\') p++; } *q = *p; q++, p++; } *q = '\0'; sprintf(new_command, "%.*s %.*s", LY_MAXPATH, new_cmd, LY_MAXPATH, win32_name); } command = new_command; } #endif #ifdef __DJGPP__ if (dj_is_bash) { char *new_command = escape_backslashes(command); if (new_command != 0) { if (do_free) free(command); command = new_command; } } #endif /* __DJGPP__ */ #ifdef _WIN_CC code = exec_command(command, TRUE); /* Wait exec */ #else /* !_WIN_CC */ #ifdef SIGPIPE if (restore_sigpipe_for_children) signal(SIGPIPE, SIG_DFL); /* Some commands expect the default */ #endif #if defined(HAVE_SIGACTION) && defined(SIGTSTP) && !defined(USE_SLANG) if (!dump_output_immediately && !LYCursesON && !no_suspend) sigtstp_saved = LYToggleSigDfl(SIGTSTP, &saved_sigtstp_act, 1); #endif code = system(command); saved_errno = errno; #if defined(HAVE_SIGACTION) && defined(SIGTSTP) && !defined(USE_SLANG) if (sigtstp_saved) LYToggleSigDfl(SIGTSTP, &saved_sigtstp_act, 0); #endif #ifdef SIGPIPE if (restore_sigpipe_for_children) signal(SIGPIPE, SIG_IGN); /* Ignore it again - kw */ #endif #endif #endif #ifdef __DJGPP__ __djgpp_set_ctrl_c(1); _go32_want_ctrl_break(0); #endif /* __DJGPP__ */ fflush(stdout); fflush(stderr); if (do_free) FREE(command); #if !defined(UCX) || !defined(VAXC) /* errno not modifiable ?? */ set_errno(saved_errno); /* may have been clobbered */ #endif #ifdef __EMX__ /* Check whether the screen size changed */ size_change(0); #endif return code; } /* * Return a string which can be used in LYSystem() for spawning a subshell */ #if defined(__CYGWIN__) /* 1999/02/26 (Fri) */ int Cygwin_Shell(void) { char *shell; int code; STARTUPINFO startUpInfo; PROCESS_INFORMATION procInfo; SECURITY_ATTRIBUTES sa; /* Set up security attributes to allow inheritance of the file handle */ sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = 0; sa.bInheritHandle = TRUE; /* Init a startup structure */ GetStartupInfo(&startUpInfo); shell = LYGetEnv("COMSPEC"); /* Create the child process, specifying inherited handles. Pass the value of the handle as a command line parameter */ code = 0; if (shell) { code = CreateProcess(0, shell, 0, 0, TRUE, 0, 0, 0, &startUpInfo, &procInfo); if (!code) { printf("shell = [%s], code = %ld\n", shell, (long) GetLastError()); } /* wait for the child to return (this is not a requirement since the child is its own independent process) */ WaitForSingleObject(procInfo.hProcess, INFINITE); } return code; } #endif #ifdef WIN_EX /* * Quote the path to make it safe for shell command processing. * We always quote it not only includes spaces in it. * At least we should quote paths which include "&". */ char *quote_pathname(char *pathname) { char *result = NULL; HTSprintf0(&result, "\"%s\"", pathname); return result; } #endif const char *LYSysShell(void) { const char *shell = 0; #ifdef DOSPATH #ifdef WIN_EX shell = LYGetEnv("SHELL"); if (shell) { if (access(shell, 0) != 0) shell = LYGetEnv("COMSPEC"); } else { shell = LYGetEnv("COMSPEC"); } if (shell == NULL) { if (system_is_NT) shell = "cmd.exe"; else shell = "command.com"; } #else shell = LYGetEnv("SHELL"); if (shell == NULL) { shell = LYGetEnv("COMSPEC"); } if (shell == NULL) { shell = "command.com"; } #endif /* WIN_EX */ #else #ifdef __EMX__ if (LYGetEnv("SHELL") != NULL) { shell = LYGetEnv("SHELL"); } else { shell = (LYGetEnv("COMSPEC") == NULL) ? "cmd.exe" : LYGetEnv("COMSPEC"); } #else #ifdef VMS shell = ""; #else shell = "exec $SHELL"; #endif /* __EMX__ */ #endif /* VMS */ #endif /* DOSPATH */ return shell; } #ifdef VMS #define DISPLAY "DECW$DISPLAY" #else #define DISPLAY "DISPLAY" #endif /* VMS */ /* * Return the X-Window $DISPLAY string if it is nonnull/nonempty */ char *LYgetXDisplay(void) { return LYGetEnv(DISPLAY); } /* * Set the value of the X-Window $DISPLAY variable (yes it leaks memory, but * that is putenv's fault). */ void LYsetXDisplay(char *new_display) { if (new_display != 0) { #ifdef VMS LYUpperCase(new_display); Define_VMSLogical(DISPLAY, new_display); #else static char *display_putenv_command; display_putenv_command = NULL; /* yes, this is a leak - cannot fix */ HTSprintf0(&display_putenv_command, "DISPLAY=%s", new_display); putenv(display_putenv_command); #endif /* VMS */ if ((new_display = LYgetXDisplay()) != 0) { StrAllocCopy(x_display, new_display); } } } #ifdef CAN_CUT_AND_PASTE #ifdef __EMX__ static int proc_type = -1; static PPIB pib; static HAB hab; static HMQ hmq; static void morph_PM(void) { PTIB tib; int first = 0; if (proc_type == -1) { DosGetInfoBlocks(&tib, &pib); proc_type = pib->pib_ultype; first = 1; } if (pib->pib_ultype != 3) /* 2 is VIO */ pib->pib_ultype = 3; /* 3 is PM */ if (first) hab = WinInitialize(0); /* 64 messages if before OS/2 3.0, ignored otherwise */ hmq = WinCreateMsgQueue(hab, 64); WinCancelShutdown(hmq, 1); /* Do not inform us on shutdown */ } static void unmorph_PM(void) { WinDestroyMsgQueue(hmq); pib->pib_ultype = proc_type; } int size_clip(void) { return 8192; } /* Code partially stolen from FED editor. */ int put_clip(const char *s) { int sz = strlen(s) + 1; int ret = EOF, nl = 0; char *pByte = 0, *s1 = s, c, *t; while ((c = *s1++)) { if (c == '\r' && *s1 == '\n') s1++; else if (c == '\n') nl++; } if (DosAllocSharedMem((PPVOID) & pByte, 0, sz + nl, PAG_WRITE | PAG_COMMIT | OBJ_GIVEABLE | OBJ_GETTABLE)) return ret; if (!nl) memcpy(pByte, s, sz); else { t = pByte; while ((c = *t++ = *s++)) if (c == '\n' && (t == pByte + 1 || t[-2] != '\r')) t[-1] = '\r', *t++ = '\n'; } morph_PM(); if (!hab) goto fail; WinOpenClipbrd(hab); WinEmptyClipbrd(hab); if (WinSetClipbrdData(hab, (ULONG) pByte, CF_TEXT, CFI_POINTER)) ret = 0; WinCloseClipbrd(hab); unmorph_PM(); if (ret == 0) return 0; fail: DosFreeMem((PPVOID) & pByte); return EOF; } static int clip_open; /* get_clip_grab() returns a pointer to the string in the system area. get_clip_release() should be called ASAP after this. */ char *get_clip_grab(void) { char *ClipData; ULONG ulFormat; int sz; morph_PM(); if (!hab) return 0; if (clip_open) get_clip_release(); WinQueryClipbrdFmtInfo(hab, CF_TEXT, &ulFormat); if (ulFormat != CFI_POINTER) { unmorph_PM(); return 0; } WinOpenClipbrd(hab); clip_open = 1; ClipData = (char *) WinQueryClipbrdData(hab, CF_TEXT); sz = strlen(ClipData); if (!ClipData || !sz) { get_clip_release(); return 0; } return ClipData; } void get_clip_release(void) { if (!clip_open) return; WinCloseClipbrd(hab); clip_open = 0; unmorph_PM(); } #else /* !( defined __EMX__ ) */ # if !defined(WIN_EX) && defined(HAVE_POPEN) static FILE *paste_handle = 0; static char *paste_buf = NULL; void get_clip_release(void) { if (paste_handle != 0) pclose(paste_handle); if (paste_buf) FREE(paste_buf); } static int clip_grab(void) { char *cmd = LYGetEnv("RL_PASTE_CMD"); if (paste_handle) pclose(paste_handle); if (!cmd) return 0; paste_handle = popen(cmd, TXT_R); if (!paste_handle) return 0; return 1; } #define PASTE_BUFFER 1008 #define CF_TEXT 0 /* Not used */ char *get_clip_grab(void) { int len; unsigned size = PASTE_BUFFER; int off = 0; if (!clip_grab()) return NULL; if (!paste_handle) return NULL; if (paste_buf) FREE(paste_buf); paste_buf = typeMallocn(char, PASTE_BUFFER); while (1) { len = (int) fread(paste_buf + off, (size_t) 1, (size_t) PASTE_BUFFER - 1, paste_handle); paste_buf[off + len] = '\0'; if (len < PASTE_BUFFER - 1) break; if (StrChr(paste_buf + off, '\r') || StrChr(paste_buf + off, '\n')) break; paste_buf = typeRealloc(char, paste_buf, size += PASTE_BUFFER - 1); off += len; } return paste_buf; } int put_clip(const char *s) { char *cmd = LYGetEnv("RL_CLCOPY_CMD"); FILE *fh; size_t l = strlen(s), res; if (!cmd) return -1; fh = popen(cmd, TXT_W); if (!fh) return -1; res = fwrite(s, (size_t) 1, l, fh); if (pclose(fh) != 0 || res != l) return -1; return 0; } # endif /* !defined(WIN_EX) && defined(HAVE_POPEN) */ #endif /* __EMX__ */ /* * Sleep for a number of milli-sec. */ void LYmsec_delay(unsigned msec) { #if defined(_WINDOWS) Sleep(msec); #elif defined(HAVE_NAPMS) napms((int) msec); #elif defined(DJGPP) || defined(HAVE_USLEEP) usleep(1000 * msec); #else struct timeval tv; unsigned long usec = 1000UL * msec; tv.tv_sec = usec / 1000000UL; tv.tv_usec = usec % 1000000UL; select(0, NULL, NULL, NULL, &tv); #endif } #if defined(WIN_EX) /* 1997/10/16 (Thu) 20:13:28 */ int put_clip(const char *szBuffer) { HANDLE hWnd; HANDLE m_hLogData; LPTSTR pLogData; HANDLE hClip; int len; if (szBuffer == NULL) return EOF; len = (int) strlen(szBuffer); if (len == 0) return EOF; else len++; m_hLogData = GlobalAlloc(GHND, len); if (m_hLogData == NULL) { return EOF; } hWnd = NULL; if (!OpenClipboard(hWnd)) { return EOF; } /* Remove the current Clipboard contents */ if (!EmptyClipboard()) { GlobalFree(m_hLogData); return EOF; } /* Lock the global memory while we write to it. */ pLogData = (LPTSTR) GlobalLock(m_hLogData); lstrcpy((LPTSTR) pLogData, szBuffer); GlobalUnlock(m_hLogData); /* If there were any lines at all then copy them to clipboard. */ hClip = SetClipboardData(CF_TEXT, m_hLogData); if (!hClip) { /* If we couldn't clip the data then free the global handle. */ GlobalFree(m_hLogData); } CloseClipboard(); return 0; } static HANDLE m_hLogData; static int m_locked; /* get_clip_grab() returns a pointer to the string in the system area. get_clip_release() should be called ASAP after this. */ char *get_clip_grab() { HANDLE hWnd; LPTSTR pLogData; hWnd = NULL; if (!OpenClipboard(hWnd)) { return 0; } m_hLogData = GetClipboardData(CF_TEXT); if (m_hLogData == NULL) { CloseClipboard(); m_locked = 0; return 0; } pLogData = (LPTSTR) GlobalLock(m_hLogData); m_locked = 1; return pLogData; } void get_clip_release() { if (!m_locked) return; GlobalUnlock(m_hLogData); CloseClipboard(); m_locked = 0; } #endif /* WIN_EX */ #endif /* CAN_CUT_AND_PASTE */ #if defined(WIN_EX) #ifndef WSABASEERR #define WSABASEERR 10000 #endif #ifdef ENABLE_IPV6 #define WSOCK_NAME "ws2_32" #else #define WSOCK_NAME "wsock32" #endif /* * Description: the windows32 version of perror() * * Returns: a pointer to a static error * * Notes/Dependencies: I got this from * comp.os.ms-windows.programmer.win32 */ char *w32_strerror(DWORD ercode) { /* __declspec(thread) necessary if you will use multiple threads */ #if defined(__CYGWIN__) || defined(__MINGW32__) static char msg_buff[256]; #else __declspec(thread) static char msg_buff[256]; #endif HMODULE hModule; int i, msg_type; unsigned char *p, *q, tmp_buff[256]; DWORD rc; hModule = NULL; msg_type = FORMAT_MESSAGE_FROM_SYSTEM; /* Fill message buffer with a default message in * case FormatMessage fails */ wsprintf(msg_buff, "Error %ld", ercode); /* * Special code for winsock error handling. */ if (ercode > WSABASEERR) { hModule = GetModuleHandle(WSOCK_NAME); if (hModule) msg_type = FORMAT_MESSAGE_FROM_HMODULE; } /* * message handling. If not found in module, retry from system. */ rc = FormatMessage(msg_type, hModule, ercode, LANG_NEUTRAL, msg_buff, sizeof(msg_buff), NULL); if (rc == 0 && msg_type == FORMAT_MESSAGE_FROM_HMODULE) { FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, ercode, LANG_NEUTRAL, msg_buff, sizeof(msg_buff), NULL); } strcpy((char *) tmp_buff, msg_buff); p = q = tmp_buff; i = 0; while (*p) { if (!(*p == '\n' || *p == '\r')) msg_buff[i++] = *p; p++; } msg_buff[i] = '\0'; return msg_buff; } #endif #if defined(SYSLOG_REQUESTED_URLS) /* * syslog() interface */ void LYOpenlog(const char *banner) { if (syslog_requested_urls) { CTRACE((tfp, "LYOpenlog(%s)\n", NONNULL(banner))); #if defined(DJGPP) openlog("lynx", LOG_PID | LOG_NDELAY, LOG_LOCAL5); #else openlog("lynx", LOG_PID, LOG_LOCAL5); #endif if (banner) { syslog(LOG_INFO, "Session start:%s", banner); } else { syslog(LOG_INFO, "Session start"); } } } static BOOLEAN looks_like_password(char *first, char *last) { BOOLEAN result = FALSE; while (first <= last) { if (*first == '/' || *first == ':') { result = FALSE; break; } result = TRUE; first++; } return result; } void LYSyslog(char *arg) { char *colon1; char *colon2; char *atsign; if (syslog_requested_urls) { CTRACE((tfp, "LYSyslog %s\n", arg)); if (is_url(arg)) { /* proto://user:password@host/path:port */ /* ^this colon */ if ((colon1 = StrChr(arg, ':')) != 0 && !StrNCmp(colon1, "://", 3) && (colon2 = StrChr(colon1 + 3, ':')) != 0 && (atsign = StrChr(colon1, '@')) != 0 && (colon2 < atsign) && looks_like_password(colon2 + 1, atsign - 1)) { char *buf = NULL; StrAllocCopy(buf, arg); buf[colon2 - arg + 1] = 0; StrAllocCat(buf, "******"); StrAllocCat(buf, atsign); syslog(LOG_INFO | LOG_LOCAL5, "%s", buf); CTRACE((tfp, "...alter %s\n", buf)); FREE(buf); return; } } syslog(LOG_INFO | LOG_LOCAL5, "%s", NONNULL(arg)); } } void LYCloselog(void) { if (syslog_requested_urls) { syslog(LOG_INFO, "Session over"); closelog(); } } #endif /* SYSLOG_REQUESTED_URLS */ #if defined(WIN_EX) || defined(__CYGWIN__) /* 2000/03/07 (Tue) 17:17:46 */ #define IS_SEP(p) ((p == '\\') || (p == '/') || (p == ':')) static char *black_list[] = { "con", "prn", "clock$", "config$", NULL }; static int is_device(char *fname) { HANDLE fileHandle; DWORD val; int i; i = 0; while (black_list[i] != NULL) { if (strcasecomp(fname, black_list[i]) == 0) { return 1; /* device file */ } i++; } fileHandle = CreateFile(fname, 0, 0, 0, OPEN_EXISTING, 0, 0); if (fileHandle == INVALID_HANDLE_VALUE) { return 0; /* normal file */ } else { val = GetFileType(fileHandle); switch (val) { case 1: val = 0; break; case 2: val = 1; /* device file */ break; default: val = 0; break; } CloseHandle(fileHandle); } return val; } static char *device_list[] = { "con", "nul", "aux", "prn", NULL }; int unsafe_filename(const char *fname) { int i, len, sum; char *cp; char *save; i = 0; while (device_list[i] != NULL) { if (strcasecomp(fname, device_list[i]) == 0) { return 0; /* device file (open OK) */ } i++; } save = cp = strdup(fname); while (*cp) { if (IS_SJIS_HI1(UCH(*cp)) || IS_SJIS_HI2(UCH(*cp))) cp += 2; /* KANJI skip */ if (IS_SEP(*cp)) { *cp = '\0'; } cp++; } sum = 0; cp = save; len = (int) strlen(fname); while (cp < (save + len)) { if (*cp == '\0') { cp++; } else { char *q; q = StrChr(cp, '.'); if (q) *q = '\0'; if (is_device(cp)) { sum++; break; } if (q) cp = q + 1; while (*cp) cp++; } } free(save); return (sum != 0); } FILE *safe_fopen(const char *fname, const char *mode) { if (unsafe_filename(fname)) { return (FILE *) NULL; } else { return fopen(fname, mode); } } #endif /* defined(WIN_EX) || defined(__CYGWIN__) */