1 /*
2 * $LynxId: LYUtils.c,v 1.242 2013/07/29 00:33:32 tom Exp $
3 */
4 #include <HTUtils.h>
5 #include <HTTCP.h>
6 #include <HTParse.h>
7 #include <HTAccess.h>
8 #include <HTCJK.h>
9 #include <HTAlert.h>
10
11 #if defined(__MINGW32__)
12
13 extern int kbhit(void); /* FIXME: use conio.h */
14
15 #undef UNIX
16
17 #elif defined(_WINDOWS)
18
19 #ifdef DONT_USE_GETTEXT
20 #undef gettext
21 #elif defined(HAVE_GETTEXT)
22 #undef gettext
23 #define gettext conio_gettext
24 #else
25 #undef gettext
26 #endif
27
28 #include <conio.h>
29
30 #ifdef DONT_USE_GETTEXT
31 #define gettext(s) s
32 #elif defined(HAVE_GETTEXT)
33 #undef gettext
34 #ifdef _INTL_REDIRECT_MACROS
35 #define gettext libintl_gettext /* restore definition from libintl.h */
36 #endif
37 #else
38 #undef gettext
39 #define gettext(s) s
40 #endif
41
42 #if !defined(kbhit) && defined(_WCONIO_DEFINED)
43 #define kbhit() _kbhit() /* reasonably recent conio.h */
44 #endif
45
46 #endif /* __MINGW32__ */
47
48 #include <LYCurses.h>
49 #include <LYHistory.h>
50 #include <LYStrings.h>
51 #include <LYGlobalDefs.h>
52 #include <LYUtils.h>
53 #include <LYSignal.h>
54 #include <GridText.h>
55 #include <LYClean.h>
56 #include <LYCharSets.h>
57 #include <LYCharUtils.h>
58
59 #include <LYMainLoop.h>
60 #include <LYKeymap.h>
61
62 #ifdef __DJGPP__
63 #include <go32.h>
64 #include <sys/exceptn.h>
65 #endif /* __DJGPP__ */
66
67 #ifndef NO_GROUPS
68 #include <HTFile.h>
69 #endif
70
71 #ifdef _WINDOWS /* 1998/04/30 (Thu) 19:04:25 */
72 #define GETPID() (unsigned) (getpid() & 0xffff)
73 #else
74 #define GETPID() (unsigned) getpid()
75 #endif /* _WINDOWS */
76
77 #ifdef FNAMES_8_3
78 #define PID_FMT "%04x"
79 #else
80 #define PID_FMT "%u"
81 #endif
82
83 #ifdef DJGPP_KEYHANDLER
84 #include <bios.h>
85 #endif /* DJGPP_KEYHANDLER */
86
87 #ifdef __EMX__
88 # define BOOLEAN OS2_BOOLEAN /* Conflicts, but is used */
89 # undef HT_ERROR /* Conflicts too */
90 # define INCL_PM /* I want some PM functions.. */
91 # define INCL_DOSPROCESS /* TIB PIB. */
92 # include <os2.h>
93 # undef BOOLEAN
94 #endif
95
96 #ifdef VMS
97 #include <descrip.h>
98 #include <libclidef.h>
99 #include <lib$routines.h>
100 #endif /* VMS */
101
102 #ifdef HAVE_UTMP
103 #include <pwd.h>
104 #ifdef UTMPX_FOR_UTMP
105 #include <utmpx.h>
106 #define utmp utmpx
107 #ifdef UTMPX_FILE
108 #ifdef UTMP_FILE
109 #undef UTMP_FILE
110 #endif /* UTMP_FILE */
111 #define UTMP_FILE UTMPX_FILE
112 #else
113 #ifdef __UTMPX_FILE
114 #define UTMP_FILE __UTMPX_FILE /* at least in OS/390 S/390 -- gil -- 2100 */
115 #else
116 #ifndef UTMP_FILE
117 #define UTMP_FILE "/var/adm/utmpx" /* Digital Unix 4.0 */
118 #endif
119 #endif
120 #endif /* UTMPX_FILE */
121 #else
122 #include <utmp.h>
123 #endif /* UTMPX_FOR_UTMP */
124 #endif /* HAVE_UTMP */
125
126 #ifdef NEED_PTEM_H
127 /* they neglected to define struct winsize in termios.h -- it's only in
128 * termio.h and ptem.h (the former conflicts with other definitions).
129 */
130 #include <sys/stream.h>
131 #include <sys/ptem.h>
132 #endif
133
134 #include <LYLeaks.h>
135
136 #ifdef USE_COLOR_STYLE
137 #include <AttrList.h>
138 #include <LYHash.h>
139 #include <LYStyle.h>
140 #endif
141
142 #ifdef SVR4_BSDSELECT
143 extern int BSDselect(int nfds, fd_set * readfds, fd_set * writefds,
144 fd_set * exceptfds, struct timeval *timeout);
145
146 #ifdef select
147 #undef select
148 #endif /* select */
149 #define select BSDselect
150 #ifdef SOCKS
151 #ifdef Rselect
152 #undef Rselect
153 #endif /* Rselect */
154 #define Rselect BSDselect
155 #endif /* SOCKS */
156 #endif /* SVR4_BSDSELECT */
157
158 #ifdef __DJGPP__
159 #undef select /* defined to select_s in www_tcp.h */
160 #endif
161
162 #ifndef UTMP_FILE
163 #if defined(__FreeBSD__) || defined(__bsdi__)
164 #define UTMP_FILE _PATH_UTMP
165 #else
166 #define UTMP_FILE "/etc/utmp"
167 #endif /* __FreeBSD__ || __bsdi__ */
168 #endif /* !UTMP_FILE */
169
170 /*
171 * experimental - make temporary filenames random to make the scheme less
172 * obvious. However, as noted by KW, there are instances (such as the
173 * 'O'ption page, for which Lynx will store a temporary filename even when
174 * it no longer applies, since it will reuse that filename at a later time.
175 */
176 #ifdef USE_RAND_TEMPNAME
177 #if defined(LYNX_RAND_MAX)
178 #define HAVE_RAND_TEMPNAME 1
179 #define MAX_TEMPNAME 10000
180 #ifndef BITS_PER_CHAR
181 #define BITS_PER_CHAR 8
182 #endif
183 #endif
184 #endif
185
186 #define COPY_COMMAND "%s %s %s"
187
188 static HTList *localhost_aliases = NULL; /* Hosts to treat as local */
189 static char *HomeDir = NULL; /* HOME directory */
190
191 HTList *sug_filenames = NULL; /* Suggested filenames */
192
193 /*
194 * Maintain a list of all of the temp-files we create so that we can remove
195 * them during the cleanup.
196 */
197 typedef struct _LYTemp {
198 struct _LYTemp *next;
199 char *name;
200 BOOLEAN outs;
201 FILE *file;
202 } LY_TEMP;
203
204 static LY_TEMP *ly_temp;
205
FindTempfileByName(const char * name)206 static LY_TEMP *FindTempfileByName(const char *name)
207 {
208 LY_TEMP *p;
209
210 for (p = ly_temp; p != 0; p = p->next) {
211 if (!strcmp(p->name, name)) {
212 break;
213 }
214 }
215 return p;
216 }
217
FindTempfileByFP(FILE * fp)218 static LY_TEMP *FindTempfileByFP(FILE *fp)
219 {
220 LY_TEMP *p;
221
222 for (p = ly_temp; p != 0; p = p->next) {
223 if (p->file == fp) {
224 break;
225 }
226 }
227 return p;
228 }
229
230 #if defined(_WIN32)
231 /*
232 * Use RegQueryValueExA() rather than RegQueryValueEx() for compatibility
233 * with non-Unicode winvile
234 */
w32_get_reg_sz(HKEY hkey,const char * name,char * value,unsigned length)235 static int w32_get_reg_sz(HKEY hkey, const char *name, char *value, unsigned length)
236 {
237 int result;
238 DWORD dwSzBuffer = length;
239
240 CTRACE((tfp, "w32_get_reg_sz(%s)\n", name));
241 result = RegQueryValueExA(hkey,
242 name,
243 NULL,
244 NULL,
245 (LPBYTE) value,
246 &dwSzBuffer);
247 if (result == ERROR_SUCCESS) {
248 value[dwSzBuffer] = 0;
249 CTRACE((tfp, "->%s\n", value));
250 }
251 return result;
252 }
253 #endif
254
255 /*
256 * Get an environment variable, rejecting empty strings
257 */
LYGetEnv(const char * name)258 char *LYGetEnv(const char *name)
259 {
260 char *result = getenv(name);
261
262 #if defined(_WIN32)
263 if (result == 0) {
264 static HKEY rootkeys[] =
265 {HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE};
266
267 int j;
268 HKEY hkey;
269 char buffer[256];
270
271 for (j = 0; j < (int) TABLESIZE(rootkeys); ++j) {
272 if (RegOpenKeyEx(rootkeys[j],
273 LYNX_SUBKEY W32_STRING("\\Environment"),
274 0,
275 KEY_READ,
276 &hkey) == ERROR_SUCCESS) {
277 if (w32_get_reg_sz(hkey, name, buffer, sizeof(buffer)) == ERROR_SUCCESS) {
278
279 result = strdup(buffer);
280 (void) RegCloseKey(hkey);
281 break;
282 }
283
284 (void) RegCloseKey(hkey);
285 }
286 }
287 }
288 #endif
289 return non_empty(result) ? result : 0;
290 }
291
292 /*
293 * ascii versions of locale sensitive functions needed because in
294 * Turkish locales tolower("I") is not "i". That's fatal for case
295 * sensitive operations with charset names, HTML tags etc.
296 */
297 #ifdef USE_ASCII_CTYPES
ascii_tolower(int i)298 int ascii_tolower(int i)
299 {
300 if (91 > i && i > 64)
301 return (i + 32);
302 else
303 return i;
304 }
305
ascii_toupper(int i)306 int ascii_toupper(int i)
307 {
308 if (123 > i && i > 96)
309 return (i - 32);
310 else
311 return i;
312 }
313
ascii_isupper(int i)314 int ascii_isupper(int i)
315 {
316 if (91 > i && i > 64)
317 return 1;
318 else
319 return 0;
320 }
321 #endif /* USE_ASCII_CTYPES */
322
323 /*
324 * Check for UTF-8 data, returning the length past the first character.
325 * Return zero if we found an ordinary character rather than UTF-8.
326 */
utf8_length(int utf_flag,const char * data)327 size_t utf8_length(int utf_flag,
328 const char *data)
329 {
330 size_t utf_extra = 0;
331
332 if (utf_flag && is8bits(*data)) {
333 if ((*data & 0xe0) == 0xc0) {
334 utf_extra = 1;
335 } else if ((*data & 0xf0) == 0xe0) {
336 utf_extra = 2;
337 } else if ((*data & 0xf8) == 0xf0) {
338 utf_extra = 3;
339 } else if ((*data & 0xfc) == 0xf8) {
340 utf_extra = 4;
341 } else if ((*data & 0xfe) == 0xfc) {
342 utf_extra = 5;
343 } else {
344 /*
345 * Garbage.
346 */
347 utf_extra = 0;
348 }
349 if (strlen(data + 1) < utf_extra) {
350 /*
351 * Shouldn't happen.
352 */
353 utf_extra = 0;
354 }
355 }
356 return utf_extra;
357 }
358
359 /*
360 * Free storage used for the link-highlighting.
361 */
LYFreeHilites(int first,int last)362 void LYFreeHilites(int first, int last)
363 {
364 int i;
365
366 for (i = first; i < last; i++) {
367 LYSetHilite(i, NULL);
368 FREE(links[i].lname);
369 }
370 }
371
372 #define LXP (links[cur].lx)
373 #define LYP (links[cur].ly)
374
375 /*
376 * Set the initial highlight information for a given link.
377 */
LYSetHilite(int cur,const char * text)378 void LYSetHilite(int cur,
379 const char *text)
380 {
381 links[cur].list.hl_base.hl_text = (char *) text;
382 links[cur].list.hl_len = (short) ((text != NULL) ? 1 : 0);
383 FREE(links[cur].list.hl_info);
384 }
385
386 /*
387 * Add highlight information for the next line of a link.
388 */
LYAddHilite(int cur,char * text,int x)389 void LYAddHilite(int cur,
390 char *text,
391 int x)
392 {
393 HiliteList *list = &(links[cur].list);
394 HiliteInfo *have = list->hl_info;
395 size_t need = (unsigned) (list->hl_len - 1);
396 size_t want;
397
398 list->hl_len = (short) (list->hl_len + 1);
399 want = (size_t) list->hl_len;
400
401 if (have != NULL) {
402 have = typeRealloc(HiliteInfo, have, want);
403 } else {
404 have = typeMallocn(HiliteInfo, want);
405 }
406 list->hl_info = have;
407 have[need].hl_text = text;
408 have[need].hl_x = (short) x;
409 }
410
411 /*
412 * Get the highlight text, counting from zero.
413 */
LYGetHiliteStr(int cur,int count)414 const char *LYGetHiliteStr(int cur,
415 int count)
416 {
417 const char *result;
418
419 if (count >= links[cur].list.hl_len)
420 result = NULL;
421 else if (count > 0)
422 result = links[cur].list.hl_info[count - 1].hl_text;
423 else
424 result = links[cur].list.hl_base.hl_text;
425 return result;
426 }
427
428 /*
429 * Get the X-ordinate at which to draw the corresponding highlight-text
430 */
LYGetHilitePos(int cur,int count)431 int LYGetHilitePos(int cur,
432 int count)
433 {
434 int result;
435
436 if (count >= links[cur].list.hl_len)
437 result = -1;
438 else if (count > 0)
439 result = links[cur].list.hl_info[count - 1].hl_x;
440 else
441 result = LXP;
442 return result;
443 }
444
445 #ifdef SHOW_WHEREIS_TARGETS
446
447 #define SKIP_GLYPHS(theFlag, theData, theOffset) \
448 (theFlag \
449 ? LYmbcs_skip_glyphs(theData, (theOffset), theFlag) \
450 : (theData + (theOffset)))
451
452 /*
453 * If we have an emphasized WHEREIS hit in the highlighted text, restore the
454 * emphasis. Note that we never emphasize the first and last characters of the
455 * highlighted text when we are making the link current, so the link attributes
456 * for the current link will persist at the beginning and end, providing an
457 * indication to the user that it has been made current. Also note that we use
458 * HText_getFirstTargetInLine() to determine if there's a hit in the HText
459 * structure line containing the link, and if so, get back a copy of the line
460 * starting at that first hit (which might be before or after our link), and
461 * with all IsSpecial characters stripped, so we don't need to deal with them
462 * here. -FM
463 */
show_whereis_targets(int flag,int cur,int count,const char * target,int TargetEmphasisON,int utf_flag)464 static BOOL show_whereis_targets(int flag,
465 int cur,
466 int count,
467 const char *target,
468 int TargetEmphasisON,
469 int utf_flag)
470 {
471 const char *Data = NULL;
472 const char *cp;
473 char *theData = NULL;
474 char buffer[MAX_LINE];
475 char tmp[7];
476 int HitOffset;
477 int LenNeeded;
478 int Offset;
479 int tLen;
480
481 tmp[0] = tmp[1] = tmp[2] = '\0';
482
483 if (non_empty(target)
484 && (links[cur].type & WWW_LINK_TYPE)
485 && non_empty(LYGetHiliteStr(cur, count))
486 && LYP + count < display_lines
487 && HText_getFirstTargetInLine(HTMainText,
488 links[cur].anchor_line_num + count,
489 utf_flag,
490 &Offset,
491 &tLen,
492 &theData,
493 target)) {
494 int itmp, written, len, y, offset;
495 const char *data;
496 int tlen = (int) strlen(target);
497 int hlen, hLen;
498 int hLine = LYP + count;
499 int hoffset = LYGetHilitePos(cur, count);
500 size_t utf_extra = 0;
501
502 /*
503 * Copy into the buffer only what will fit up to the right border of
504 * the screen. -FM
505 */
506 LYmbcsstrncpy(buffer,
507 NonNull(LYGetHiliteStr(cur, count)),
508 (int) (sizeof(buffer) - 1),
509 (LYcolLimit - LYGetHilitePos(cur, count)),
510 utf_flag);
511 hlen = (int) strlen(buffer);
512 hLen = ((IS_CJK_TTY || utf_flag) ?
513 LYmbcsstrlen(buffer, utf_flag, YES) : hlen);
514
515 /*
516 * Break out if the first hit in the line starts after this link. -FM
517 */
518 if (Offset < (hoffset + hLen)) {
519 /*
520 * Recursively skip hits that end before this link, and break out
521 * if there is no hit beyond those. -FM
522 */
523 Data = theData;
524 while ((Offset < hoffset) &&
525 ((Offset + tLen) <= hoffset)) {
526 data = (Data + tlen);
527 offset = (Offset + tLen);
528 if (((cp = LYno_attr_mb_strstr(data,
529 target,
530 utf_flag, YES,
531 &HitOffset,
532 &LenNeeded)) != NULL)
533 && (offset + LenNeeded) < LYcols) {
534 Data = cp;
535 Offset = (offset + HitOffset);
536 } else {
537 goto highlight_search_done;
538 }
539 }
540 data = buffer;
541 offset = hoffset;
542
543 /*
544 * If the hit starts before the hightext, and ends in or beyond the
545 * hightext, restore the emphasis, skipping the first and last
546 * characters of the hightext if we're making the link current.
547 * -FM
548 */
549 if (offset >= 0 &&
550 (Offset < offset) &&
551 ((Offset + tLen) > offset)) {
552 itmp = 0;
553 written = 0;
554 len = (tlen - (offset - Offset));
555
556 /*
557 * Go to the start of the hightext and handle its first
558 * character. -FM
559 */
560 LYmove(hLine, offset);
561 tmp[0] = data[itmp];
562 utf_extra = utf8_length(utf_flag, data + itmp);
563 if (utf_extra) {
564 LYStrNCpy(&tmp[1], &data[itmp + 1], utf_extra);
565 itmp += (int) utf_extra;
566 /*
567 * Start emphasis immediately if we are making the link
568 * non-current. -FM
569 */
570 if (flag != TRUE) {
571 LYstartTargetEmphasis();
572 TargetEmphasisON = TRUE;
573 LYaddstr(tmp);
574 } else {
575 LYmove(hLine, (offset + 1));
576 }
577 tmp[1] = '\0';
578 written += (int) (utf_extra + 1);
579 } else if (IS_CJK_TTY && is8bits(tmp[0])) {
580 /*
581 * For CJK strings, by Masanobu Kimura.
582 */
583 tmp[1] = data[++itmp];
584 /*
585 * Start emphasis immediately if we are making the link
586 * non-current. -FM
587 */
588 if (flag != TRUE) {
589 LYstartTargetEmphasis();
590 TargetEmphasisON = TRUE;
591 LYaddstr(tmp);
592 } else {
593 LYmove(hLine, (offset + 1));
594 }
595 tmp[1] = '\0';
596 written += 2;
597 } else {
598 /*
599 * Start emphasis immediately if we are making the link
600 * non-current. -FM
601 */
602 if (flag != TRUE) {
603 LYstartTargetEmphasis();
604 TargetEmphasisON = TRUE;
605 LYaddstr(tmp);
606 } else {
607 LYmove(hLine, (offset + 1));
608 }
609 written++;
610 }
611 itmp++;
612 /*
613 * Start emphasis after the first character if we are making
614 * the link current and this is not the last character. -FM
615 */
616 if (!TargetEmphasisON &&
617 data[itmp] != '\0') {
618 LYstartTargetEmphasis();
619 TargetEmphasisON = TRUE;
620 }
621
622 /*
623 * Handle the remaining characters. -FM
624 */
625 for (;
626 written < len && (tmp[0] = data[itmp]) != '\0';
627 itmp++) {
628 /*
629 * Print all the other target chars, except the last
630 * character if it is also the last character of hightext
631 * and we are making the link current. -FM
632 */
633 utf_extra = utf8_length(utf_flag, data + itmp);
634 if (utf_extra) {
635 LYStrNCpy(&tmp[1], &data[itmp + 1], utf_extra);
636 itmp += (int) utf_extra;
637 /*
638 * Make sure we don't restore emphasis to the last
639 * character of hightext if we are making the link
640 * current. -FM
641 */
642 if (flag == TRUE && data[(itmp + 1)] == '\0') {
643 LYstopTargetEmphasis();
644 TargetEmphasisON = FALSE;
645 LYGetYX(y, offset);
646 (void) y;
647 LYmove(hLine, (offset + 1));
648 } else {
649 LYaddstr(tmp);
650 }
651 tmp[1] = '\0';
652 written += (int) (utf_extra + 1);
653 } else if (IS_CJK_TTY && is8bits(tmp[0])) {
654 /*
655 * For CJK strings, by Masanobu Kimura.
656 */
657 tmp[1] = data[++itmp];
658 /*
659 * Make sure we don't restore emphasis to the last
660 * character of hightext if we are making the link
661 * current. -FM
662 */
663 if (flag == TRUE && data[(itmp + 1)] == '\0') {
664 LYstopTargetEmphasis();
665 TargetEmphasisON = FALSE;
666 LYGetYX(y, offset);
667 LYmove(hLine, (offset + 1));
668 } else {
669 LYaddstr(tmp);
670 }
671 tmp[1] = '\0';
672 written += 2;
673 } else {
674 /*
675 * Make sure we don't restore emphasis to the last
676 * character of hightext if we are making the link
677 * current. -FM
678 */
679 if (flag == TRUE && data[(itmp + 1)] == '\0') {
680 LYstopTargetEmphasis();
681 TargetEmphasisON = FALSE;
682 LYGetYX(y, offset);
683 LYmove(hLine, (offset + 1));
684 } else {
685 LYaddstr(tmp);
686 }
687 written++;
688 }
689 }
690
691 /*
692 * Stop the emphasis if we haven't already, then reset the
693 * offset to our current position in the line, and if that is
694 * beyond the link, or or we are making the link current and it
695 * is the last character of the hightext, we are done. -FM
696 */
697 if (TargetEmphasisON) {
698 LYstopTargetEmphasis();
699 TargetEmphasisON = FALSE;
700 }
701 LYGetYX(y, offset);
702 if (offset < (hoffset + (flag == TRUE ? (hLen - 1) : hLen))
703 /*
704 * See if we have another hit that starts within the
705 * hightext. -FM
706 */
707 && ((cp =
708 LYno_attr_mb_strstr(data = SKIP_GLYPHS(utf_flag,
709 Data,
710 offset - Offset),
711 target,
712 utf_flag, YES,
713 &HitOffset,
714 &LenNeeded)) != NULL)
715 && (offset + LenNeeded) < LYcols
716 /*
717 * If the hit starts after the end of the hightext, or we
718 * are making the link current and the hit starts at its
719 * last character, we are done. -FM
720 */
721 && (HitOffset + offset) <
722 (hoffset +
723 (flag == TRUE ? (hLen - 1) : hLen))) {
724 /*
725 * Set up the data and offset for the hit, and let the code
726 * for within hightext hits handle it. -FM
727 */
728 Data = cp;
729 Offset = (offset + HitOffset);
730 data = buffer;
731 offset = hoffset;
732 goto highlight_hit_within_hightext;
733 }
734 goto highlight_search_done;
735 }
736
737 highlight_hit_within_hightext:
738 /*
739 * If we get to here, the hit starts within the hightext. If we
740 * are making the link current and it's the last character in the
741 * hightext, we are done. Otherwise, move there and start
742 * restoring the emphasis. -FM
743 */
744 if ((Offset - offset) <= (flag == TRUE ? (hLen - 1) : hLen)) {
745 data = SKIP_GLYPHS(utf_flag, data, Offset - offset);
746 if (utf_flag) {
747 LYrefresh();
748 }
749 offset = Offset;
750 itmp = 0;
751 written = 0;
752 len = tlen;
753
754 /*
755 * Go to the start of the hit and handle its first character.
756 * -FM
757 */
758 LYmove(hLine, offset);
759 tmp[0] = data[itmp];
760 utf_extra = utf8_length(utf_flag, data + itmp);
761 if (utf_extra) {
762 LYStrNCpy(&tmp[1], &data[itmp + 1], utf_extra);
763 itmp += (int) utf_extra;
764 /*
765 * Start emphasis immediately if we are making the link
766 * non-current, or we are making it current but this is not
767 * the first or last character of the hightext. -FM
768 */
769 if (flag != TRUE ||
770 (offset > hoffset && data[itmp + 1] != '\0')) {
771 LYstartTargetEmphasis();
772 TargetEmphasisON = TRUE;
773 LYaddstr(tmp);
774 } else {
775 LYmove(hLine, (offset + 1));
776 }
777 tmp[1] = '\0';
778 written += (int) (utf_extra + 1);
779 } else if (IS_CJK_TTY && is8bits(tmp[0])) {
780 /*
781 * For CJK strings, by Masanobu Kimura.
782 */
783 tmp[1] = data[++itmp];
784 /*
785 * Start emphasis immediately if we are making the link
786 * non-current, or we are making it current but this is not
787 * the first or last character of the hightext. -FM
788 */
789 if (flag != TRUE ||
790 (offset > hoffset && data[itmp + 1] != '\0')) {
791 LYstartTargetEmphasis();
792 TargetEmphasisON = TRUE;
793 LYaddstr(tmp);
794 } else {
795 LYmove(hLine, (offset + 2));
796 }
797 tmp[1] = '\0';
798 written += 2;
799 } else {
800 /*
801 * Start emphasis immediately if we are making the link
802 * non-current, or we are making it current but this is not
803 * the first or last character of the hightext. -FM
804 */
805 if (flag != TRUE ||
806 (offset > hoffset && data[itmp + 1] != '\0')) {
807 LYstartTargetEmphasis();
808 TargetEmphasisON = TRUE;
809 LYaddstr(tmp);
810 } else {
811 LYmove(hLine, (offset + 1));
812 }
813 written++;
814 }
815 itmp++;
816 /*
817 * Start emphasis after the first character if we are making
818 * the link current and this is not the last character. -FM
819 */
820 if (!TargetEmphasisON &&
821 data[itmp] != '\0') {
822 LYstartTargetEmphasis();
823 TargetEmphasisON = TRUE;
824 }
825
826 for (;
827 written < len && (tmp[0] = data[itmp]) != '\0';
828 itmp++) {
829 /*
830 * Print all the other target chars, except the last
831 * character if it is also the last character of hightext
832 * and we are making the link current. -FM
833 */
834 utf_extra = utf8_length(utf_flag, data + itmp);
835 if (utf_extra) {
836 LYStrNCpy(&tmp[1], &data[itmp + 1], utf_extra);
837 itmp += (int) utf_extra;
838 /*
839 * Make sure we don't restore emphasis to the last
840 * character of hightext if we are making the link
841 * current. -FM
842 */
843 if (flag == TRUE && data[(itmp + 1)] == '\0') {
844 LYstopTargetEmphasis();
845 TargetEmphasisON = FALSE;
846 LYGetYX(y, offset);
847 LYmove(hLine, (offset + 1));
848 } else {
849 LYaddstr(tmp);
850 }
851 tmp[1] = '\0';
852 written += (int) (utf_extra + 1);
853 } else if (IS_CJK_TTY && is8bits(tmp[0])) {
854 /*
855 * For CJK strings, by Masanobu Kimura.
856 */
857 tmp[1] = data[++itmp];
858 /*
859 * Make sure we don't restore emphasis to the last
860 * character of hightext if we are making the link
861 * current. -FM
862 */
863 if (flag == TRUE && data[(itmp + 1)] == '\0') {
864 LYstopTargetEmphasis();
865 TargetEmphasisON = FALSE;
866 LYGetYX(y, offset);
867 LYmove(hLine, (offset + 1));
868 } else {
869 LYaddstr(tmp);
870 }
871 tmp[1] = '\0';
872 written += 2;
873 } else {
874 /*
875 * Make sure we don't restore emphasis to the last
876 * character of hightext if we are making the link
877 * current. -FM
878 */
879 if (flag == TRUE && data[(itmp + 1)] == '\0') {
880 LYstopTargetEmphasis();
881 TargetEmphasisON = FALSE;
882 LYGetYX(y, offset);
883 LYmove(hLine, (offset + 1));
884 } else {
885 LYaddstr(tmp);
886 }
887 written++;
888 }
889 }
890
891 /*
892 * Stop the emphasis if we haven't already, then reset the
893 * offset to our current position in the line, and if that is
894 * beyond the link, or we are making the link current and it is
895 * the last character in the hightext, we are done. -FM
896 */
897 if (TargetEmphasisON) {
898 LYstopTargetEmphasis();
899 TargetEmphasisON = FALSE;
900 }
901 LYGetYX(y, offset);
902 if (offset < (hoffset + (flag == TRUE ? (hLen - 1) : hLen))
903 /*
904 * See if we have another hit that starts within the
905 * hightext. -FM
906 */
907 && ((cp =
908 LYno_attr_mb_strstr(data = SKIP_GLYPHS(utf_flag,
909 Data,
910 offset - Offset),
911 target,
912 utf_flag, YES,
913 &HitOffset,
914 &LenNeeded)) != NULL)
915 && (offset + LenNeeded) < LYcols
916 /*
917 * If the hit starts after the end of the hightext, or we
918 * are making the link current and the hit starts at its
919 * last character, we are done. -FM
920 */
921 && (HitOffset + offset) <
922 (hoffset + (flag == TRUE ? (hLen - 1) : hLen))) {
923 /*
924 * If the target extends beyond our buffer, emphasize
925 * everything in the hightext starting at this hit.
926 * Otherwise, set up the data and offsets, and loop back.
927 * -FM
928 */
929 if ((HitOffset + (offset + tLen)) >= (hoffset + hLen)) {
930 offset = (HitOffset + offset);
931 data = SKIP_GLYPHS(utf_flag, Data, offset - hoffset);
932 if (utf_flag) {
933 LYrefresh();
934 }
935 LYmove(hLine, offset);
936 itmp = 0;
937 written = 0;
938 len = (int) strlen(data);
939
940 /*
941 * Turn the emphasis back on. -FM
942 */
943 LYstartTargetEmphasis();
944 TargetEmphasisON = TRUE;
945 for (;
946 written < len && (tmp[0] = data[itmp]) != '\0';
947 itmp++) {
948 /*
949 * Print all the other target chars, except the
950 * last character if it is also the last character
951 * of hightext and we are making the link current.
952 * -FM
953 */
954 utf_extra = utf8_length(utf_flag, data + itmp);
955 if (utf_extra) {
956 LYStrNCpy(&tmp[1], &data[itmp + 1], utf_extra);
957 itmp += (int) utf_extra;
958 /*
959 * Make sure we don't restore emphasis to the
960 * last character of hightext if we are making
961 * the link current. -FM
962 */
963 if (flag == TRUE && data[(itmp + 1)] == '\0') {
964 LYstopTargetEmphasis();
965 TargetEmphasisON = FALSE;
966 LYGetYX(y, offset);
967 LYmove(hLine, (offset + 1));
968 } else {
969 LYaddstr(tmp);
970 }
971 tmp[1] = '\0';
972 written += (int) (utf_extra + 1);
973 } else if (IS_CJK_TTY && is8bits(tmp[0])) {
974 /*
975 * For CJK strings, by Masanobu Kimura.
976 */
977 tmp[1] = data[++itmp];
978 /*
979 * Make sure we don't restore emphasis to the
980 * last character of hightext if we are making
981 * the link current. -FM
982 */
983 if (flag == TRUE && data[(itmp + 1)] == '\0') {
984 LYstopTargetEmphasis();
985 TargetEmphasisON = FALSE;
986 } else {
987 LYaddstr(tmp);
988 }
989 tmp[1] = '\0';
990 written += 2;
991 } else {
992 /*
993 * Make sure we don't restore emphasis to the
994 * last character of hightext if we are making
995 * the link current. -FM
996 */
997 if (flag == TRUE && data[(itmp + 1)] == '\0') {
998 LYstopTargetEmphasis();
999 TargetEmphasisON = FALSE;
1000 } else {
1001 LYaddstr(tmp);
1002 }
1003 written++;
1004 }
1005 }
1006 /*
1007 * Turn off the emphasis if we haven't already, and
1008 * then we're done. -FM
1009 */
1010 if (TargetEmphasisON) {
1011 LYstopTargetEmphasis();
1012 }
1013 } else {
1014 Data = cp;
1015 Offset = (offset + HitOffset);
1016 data = buffer;
1017 offset = hoffset;
1018 goto highlight_hit_within_hightext;
1019 }
1020 }
1021 }
1022 }
1023 }
1024 highlight_search_done:
1025 FREE(theData);
1026 return (BOOLEAN) TargetEmphasisON;
1027 }
1028 #endif /* SHOW_WHEREIS_TARGETS */
1029
1030 #ifdef USE_COLOR_STYLE
find_cached_style(int cur,int flag)1031 static int find_cached_style(int cur,
1032 int flag)
1033 {
1034 int s = s_alink;
1035
1036 #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
1037 if (textfields_need_activation
1038 && links[cur].type == WWW_FORM_LINK_TYPE
1039 && F_TEXTLIKE(links[cur].l_form->type))
1040 s = s_curedit;
1041 #endif
1042
1043 if (flag != TRUE) {
1044 int x;
1045
1046 /*
1047 * This is where we try to restore the original style when a link is
1048 * unhighlighted. The cached styles array saves the original style
1049 * just for this case. If it doesn't have a color change saved at just
1050 * the right position, we look at preceding positions in the same line
1051 * until we find one.
1052 */
1053 if (ValidCachedStyle(LYP, LXP)) {
1054 CTRACE2(TRACE_STYLE,
1055 (tfp, "STYLE.highlight.off: cached style @(%d,%d): ",
1056 LYP, LXP));
1057 s = (int) GetCachedStyle(LYP, LXP);
1058 if (s == 0) {
1059 for (x = LXP - 1; x >= 0; x--) {
1060 s = (int) GetCachedStyle(LYP, x);
1061 if (s != 0) {
1062 SetCachedStyle(LYP, LXP, (unsigned) s);
1063 CTRACE2(TRACE_STYLE,
1064 (tfp, "found %d, x_offset=%d.\n", s, x - LXP));
1065 break;
1066 }
1067 }
1068 if (s == 0) {
1069 CTRACE2(TRACE_STYLE, (tfp, "not found, assume <a>.\n"));
1070 s = s_a;
1071 }
1072 } else {
1073 CTRACE2(TRACE_STYLE, (tfp, "found %d.\n", s));
1074 }
1075 } else {
1076 CTRACE2(TRACE_STYLE,
1077 (tfp, "STYLE.highlight.off: can't use cache.\n"));
1078 s = s_a;
1079 }
1080 } else {
1081 CTRACE2(TRACE_STYLE, (tfp, "STYLE.highlight.on: @(%d,%d).\n", LYP, LXP));
1082 }
1083 return s;
1084 }
1085 #endif /* USE_COLOR_STYLE */
1086
1087 /*
1088 * Highlight (or unhighlight) a given link.
1089 */
LYhighlight(int flag,int cur,const char * target)1090 void LYhighlight(int flag,
1091 int cur,
1092 const char *target)
1093 {
1094 char buffer[MAX_LINE];
1095 int i;
1096 int hi_count;
1097 int hi_offset;
1098 int title_adjust = (no_title ? -TITLE_LINES : 0);
1099 char tmp[7];
1100 const char *hi_string;
1101
1102 #ifdef SHOW_WHEREIS_TARGETS
1103 BOOL TargetEmphasisON = FALSE;
1104 BOOL target1_drawn = NO;
1105 #endif
1106 BOOL utf_flag = (BOOL) IS_UTF8_TTY;
1107 BOOL hl1_drawn = NO;
1108
1109 #ifdef USE_COLOR_STYLE
1110 BOOL hl2_drawn = FALSE; /* whether links[cur].l_hightext2 is already drawn
1111
1112 properly */
1113 #endif
1114 tmp[0] = tmp[1] = tmp[2] = '\0';
1115
1116 /*
1117 * Bugs in the history code might cause -1 to be sent for cur, which yields
1118 * a crash when LYStrNCpy() is called with a nonsense pointer. As far as I
1119 * know, such bugs have been squashed, but if they should reappear, this
1120 * works around them. -FM
1121 */
1122 if (cur < 0) {
1123 CTRACE((tfp, "LYhighlight cur %d (bug workaround)\n", cur));
1124 cur = 0;
1125 }
1126
1127 CTRACE((tfp, "LYhighlight at(%2d,%2d) %s %d [%d]:%s\n",
1128 links[cur].ly, links[cur].lx,
1129 (flag
1130 ? "on"
1131 : "off"),
1132 cur,
1133 links[cur].anchor_number,
1134 NONNULL(target)));
1135
1136 #if defined(TEXTFIELDS_MAY_NEED_ACTIVATION) && defined(INACTIVE_INPUT_STYLE_VH)
1137 if (flag == FALSE)
1138 textinput_redrawn = FALSE;
1139 #endif
1140
1141 if (nlinks > 0) {
1142 #ifdef USE_COLOR_STYLE
1143 if (flag == TRUE || links[cur].type == WWW_FORM_LINK_TYPE) {
1144 LYmove(LYP + title_adjust, LXP);
1145 LynxChangeStyle(find_cached_style(cur, flag), STACK_ON);
1146 }
1147 #else
1148 if (links[cur].type == WWW_FORM_LINK_TYPE
1149 || LYGetHiliteStr(cur, 0) == NULL) {
1150 LYMoveToLink(cur, target, NULL,
1151 flag, links[cur].inUnderline, utf_flag);
1152 lynx_start_link_color(flag == TRUE, links[cur].inUnderline);
1153 } else {
1154 LYMoveToLink(cur, target, LYGetHiliteStr(cur, 0),
1155 flag, links[cur].inUnderline, utf_flag);
1156 hl1_drawn = YES;
1157 #ifdef SHOW_WHEREIS_TARGETS
1158 target1_drawn = YES;
1159 #endif
1160 }
1161 #endif
1162
1163 if (links[cur].type == WWW_FORM_LINK_TYPE) {
1164 int len;
1165 int avail_space = (LYcolLimit - LXP) + (LYcolLimit * (LYlines - LYP));
1166 const char *text = LYGetHiliteStr(cur, 0);
1167
1168 if (text == 0)
1169 text = "";
1170
1171 if (avail_space > links[cur].l_form->size)
1172 avail_space = links[cur].l_form->size;
1173
1174 len = (int) (LYmbcs_skip_cells(text, avail_space, utf_flag) - text);
1175 LYwaddnstr(LYwin, text, (size_t) len);
1176 while (len++ < avail_space)
1177 LYaddch('_');
1178
1179 #ifdef USE_COLOR_STYLE
1180 } else if (flag == FALSE) {
1181 hl2_drawn = TRUE;
1182 redraw_lines_of_link(cur);
1183 CTRACE2(TRACE_STYLE,
1184 (tfp, "STYLE.highlight.off: NOFIX branch @(%d,%d).\n",
1185 LYP, LXP));
1186 #endif
1187 } else if (!hl1_drawn) {
1188 /*
1189 * Copy into the buffer only what will fit within the width of the
1190 * screen.
1191 */
1192 LYmbcsstrncpy(buffer,
1193 NonNull(LYGetHiliteStr(cur, 0)),
1194 (int) (sizeof(buffer) - 1),
1195 (LYcolLimit - LXP),
1196 utf_flag);
1197 LYaddstr(buffer);
1198 }
1199
1200 /*
1201 * Display a second line as well.
1202 */
1203 #ifdef USE_COLOR_STYLE
1204 if (hl2_drawn == FALSE)
1205 #endif
1206 {
1207 for (hi_count = 1;
1208 (hi_string = LYGetHiliteStr(cur, hi_count)) != NULL
1209 && LYP + hi_count <= display_lines;
1210 ++hi_count) {
1211 int row = LYP + hi_count + title_adjust;
1212
1213 hi_offset = LYGetHilitePos(cur, hi_count);
1214 if (hi_offset < 0)
1215 continue;
1216 lynx_stop_link_color(flag == TRUE, links[cur].inUnderline);
1217 LYmove(row, hi_offset);
1218
1219 #ifdef USE_COLOR_STYLE
1220 CTRACE2(TRACE_STYLE,
1221 (tfp, "STYLE.highlight.line2: @(%d,%d), style=%d.\n",
1222 row, hi_offset,
1223 flag == TRUE ? s_alink : s_a));
1224 LynxChangeStyle(flag == TRUE ? s_alink : s_a, ABS_ON);
1225 #else
1226 lynx_start_link_color(flag == TRUE, links[cur].inUnderline);
1227 #endif
1228
1229 for (i = 0; (tmp[0] = hi_string[i]) != '\0'
1230 && (i + hi_offset) < LYcols; i++) {
1231 if (!IsSpecialAttrChar(hi_string[i])) {
1232 /*
1233 * For CJK strings, by Masanobu Kimura.
1234 */
1235 if (IS_CJK_TTY && is8bits(tmp[0])) {
1236 tmp[1] = hi_string[++i];
1237 LYaddstr(tmp);
1238 tmp[1] = '\0';
1239 } else {
1240 LYaddstr(tmp);
1241 }
1242 }
1243 }
1244 }
1245 lynx_stop_link_color(flag == TRUE, links[cur].inUnderline);
1246 }
1247 #ifdef SHOW_WHEREIS_TARGETS
1248 for (hi_count = target1_drawn ? 1 : 0;
1249 LYGetHiliteStr(cur, hi_count) != NULL;
1250 hi_count++) {
1251 TargetEmphasisON = show_whereis_targets(flag,
1252 cur,
1253 hi_count,
1254 target,
1255 TargetEmphasisON,
1256 utf_flag);
1257 }
1258
1259 if (!LYShowCursor)
1260 /*
1261 * Get cursor out of the way.
1262 */
1263 LYHideCursor();
1264 else
1265 #endif /* SHOW_WHEREIS_TARGETS */
1266 /*
1267 * Never hide the cursor if there's no FANCY CURSES or SLANG.
1268 */
1269 LYmove(LYP + title_adjust, ((LXP > 0) ? (LXP - 1) : 0));
1270
1271 if (flag)
1272 LYrefresh();
1273 }
1274 return;
1275 }
1276
1277 /*
1278 * free_and_clear will free a pointer if it is non-zero and then set it to
1279 * zero.
1280 */
free_and_clear(char ** pointer)1281 void free_and_clear(char **pointer)
1282 {
1283 if (*pointer) {
1284 FREE(*pointer);
1285 *pointer = 0;
1286 }
1287 return;
1288 }
1289
1290 /*
1291 * Convert single or serial newlines to single spaces throughout a string
1292 * (ignore newlines if the preceding character is a space) and convert tabs to
1293 * single spaces. Don't ignore any explicit tabs or spaces if the condense
1294 * argument is FALSE, otherwise, condense any serial spaces or tabs to one
1295 * space. - FM
1296 */
convert_to_spaces(char * string,int condense)1297 void convert_to_spaces(char *string,
1298 int condense)
1299 {
1300 char *s = string;
1301 char *ns;
1302 BOOL last_is_space = FALSE;
1303
1304 if (!s)
1305 return;
1306
1307 s = LYSkipNonBlanks(s);
1308 ns = s;
1309
1310 while (*s) {
1311 switch (*s) {
1312 case ' ':
1313 case '\t':
1314 if (!(condense && last_is_space))
1315 *(ns++) = ' ';
1316 last_is_space = TRUE;
1317 break;
1318
1319 case '\r':
1320 case '\n':
1321 if (!last_is_space) {
1322 *(ns++) = ' ';
1323 last_is_space = TRUE;
1324 }
1325 break;
1326
1327 default:
1328 *(ns++) = *s;
1329 last_is_space = FALSE;
1330 break;
1331 }
1332 s++;
1333 }
1334 *ns = '\0';
1335 return;
1336 }
1337
1338 /*
1339 * Strip trailing slashes from directory paths.
1340 */
strip_trailing_slash(char * dirname)1341 char *strip_trailing_slash(char *dirname)
1342 {
1343 int i;
1344
1345 i = (int) strlen(dirname) - 1;
1346 while (i >= 0 && dirname[i] == '/')
1347 dirname[i--] = '\0';
1348 return (dirname);
1349 }
1350
1351 /*
1352 * Remove most blanks, but restore one trailing blank to make prompts nicer.
1353 */
remove_most_blanks(char * buffer)1354 static void remove_most_blanks(char *buffer)
1355 {
1356 int length = (int) strlen(buffer);
1357 BOOL trailing = (BOOL) ((length != 0) && (buffer[length - 1] == ' '));
1358
1359 LYReduceBlanks(buffer);
1360 if (trailing)
1361 strcat(buffer, " ");
1362 }
1363
1364 /*
1365 * Display (or hide) the status line.
1366 */
1367 BOOLEAN mustshow = FALSE;
1368
statusline(const char * text)1369 void statusline(const char *text)
1370 {
1371 char buffer[MAX_LINE];
1372 unsigned char *temp = NULL;
1373 int max_length, len, i, j;
1374 int at_lineno;
1375 unsigned char k;
1376 char *p;
1377 char text_buff[MAX_LINE];
1378
1379 if (text == NULL)
1380 return;
1381
1382 /*
1383 * Don't print statusline messages if dumping to stdout.
1384 */
1385 if (dump_output_immediately)
1386 return;
1387
1388 /*
1389 * Don't print statusline message if turned off.
1390 */
1391 if (mustshow != TRUE) {
1392 if (no_statusline == TRUE) {
1393 return;
1394 }
1395 }
1396 mustshow = FALSE;
1397
1398 /* "LYNXDOWNLOAD://Method=-1/File=%s/SugFile=%s%s\">Save to disk</a>\n" */
1399 LYStrNCpy(text_buff, text, sizeof(text_buff) - 1);
1400 p = strchr(text_buff, '\n');
1401 if (p)
1402 *p = '\0';
1403
1404 /*
1405 * Deal with any CJK escape sequences and Kanji if we have a CJK character
1406 * set selected, otherwise, strip any escapes. Also, make sure text is not
1407 * longer than the statusline window. - FM
1408 */
1409 max_length = (((LYcolLimit - 1) < (int) sizeof(buffer))
1410 ? (LYcolLimit - 1)
1411 : (int) sizeof(buffer) - 1);
1412 if ((text_buff[0] != '\0') &&
1413 (LYHaveCJKCharacterSet)) {
1414 /*
1415 * Translate or filter any escape sequences. - FM
1416 */
1417 if ((temp = typecallocn(unsigned char, strlen(text_buff) + 1)) == NULL)
1418 outofmem(__FILE__, "statusline");
1419
1420 assert(temp != NULL);
1421
1422 if (kanji_code == EUC) {
1423 TO_EUC((const unsigned char *) text_buff, temp);
1424 } else if (kanji_code == SJIS) {
1425 #ifdef KANJI_CODE_OVERRIDE
1426 if (!LYRawMode || last_kcode == SJIS)
1427 strcpy(temp, text_buff);
1428 else
1429 TO_SJIS((const unsigned char *) text_buff, temp);
1430 #else
1431 strcpy((char *) temp, text_buff);
1432 #endif
1433 } else {
1434 for (i = 0, j = 0; text_buff[i]; i++) {
1435 if (text_buff[i] != CH_ESC) { /* S/390 -- gil -- 2119 */
1436 temp[j++] = UCH(text_buff[i]);
1437 }
1438 }
1439 temp[j] = '\0';
1440 }
1441
1442 /*
1443 * Deal with any newlines or tabs in the string. - FM
1444 */
1445 remove_most_blanks((char *) temp);
1446
1447 /*
1448 * Handle the Kanji, making sure the text is not longer than the
1449 * statusline window. - FM
1450 */
1451 for (i = 0, j = 0, len = 0, k = '\0';
1452 temp[i] != '\0' && len < max_length; i++) {
1453 if (k != '\0') {
1454 buffer[j++] = (char) k;
1455 buffer[j++] = (char) temp[i];
1456 k = '\0';
1457 len += 2;
1458 } else if ((temp[i] & 0200) != 0) {
1459 k = temp[i];
1460 } else {
1461 buffer[j++] = (char) temp[i];
1462 len++;
1463 }
1464 }
1465 buffer[j] = '\0';
1466 FREE(temp);
1467 } else {
1468 /*
1469 * Deal with any newlines or tabs in the string. - FM
1470 */
1471 remove_most_blanks(text_buff);
1472 #ifdef WIDEC_CURSES
1473 len = (int) strlen(text_buff);
1474 if (len >= (int) (sizeof(buffer) - 1))
1475 len = (int) (sizeof(buffer) - 1);
1476 LYStrNCpy(buffer, text_buff, len);
1477 /* FIXME: a binary search might be faster */
1478 while (len > 0 && LYstrExtent(buffer, len, len) > max_length)
1479 buffer[--len] = '\0';
1480 #else
1481 /*
1482 * Strip any escapes, and shorten text if necessary. Note that we
1483 * don't deal with the possibility of UTF-8 characters in the string.
1484 * This is unlikely, but if strings with such characters are used in
1485 * LYMessages_en.h, a compilation symbol of HAVE_UTF8_STATUSLINES could
1486 * be added there, and code added here for determining the displayed
1487 * string length, as we do above for CJK. - FM
1488 */
1489 for (i = 0, len = 0; text_buff[i] != '\0' && len < max_length; i++) {
1490 if (text_buff[i] != CH_ESC) { /* S/390 -- gil -- 2119 */
1491 buffer[len++] = text_buff[i];
1492 }
1493 }
1494 buffer[len] = '\0';
1495 #endif
1496 }
1497
1498 /*
1499 * Move to the desired statusline window and output the text highlighted.
1500 * - FM
1501 */
1502 if (LYStatusLine >= 0) {
1503 if (LYStatusLine < LYlines - 1) {
1504 at_lineno = LYStatusLine;
1505 } else {
1506 at_lineno = LYlines - 1;
1507 }
1508 } else if (user_mode == NOVICE_MODE) {
1509 at_lineno = LYlines - 3;
1510 } else {
1511 at_lineno = LYlines - 1;
1512 }
1513 LYmove(at_lineno, 0);
1514 LYclrtoeol();
1515
1516 if (buffer[0] != '\0') {
1517 BOOLEAN has_CJK = FALSE;
1518
1519 if (IS_CJK_TTY) {
1520 for (i = 0; buffer[i] != '\0'; i++) {
1521 if (buffer[i] & 0x80) {
1522 has_CJK = TRUE;
1523 break;
1524 }
1525 }
1526 }
1527
1528 if (has_CJK
1529 #ifdef HAVE_UTF8_STATUSLINES
1530 || IS_UTF8_TTY
1531 #endif
1532 ) {
1533 LYrefresh();
1534 }
1535 #ifndef USE_COLOR_STYLE
1536 lynx_start_status_color();
1537 LYaddstr(buffer);
1538 lynx_stop_status_color();
1539 #else
1540 /* draw the status bar in the STATUS style */
1541 {
1542 int y, x;
1543 int a = ((StrNCmp(buffer, ALERT_FORMAT, ALERT_PREFIX_LEN)
1544 || !hashStyles[s_alert].name)
1545 ? s_status
1546 : s_alert);
1547
1548 LynxChangeStyle(a, STACK_ON);
1549 LYaddstr(buffer);
1550 wbkgdset(LYwin,
1551 ((lynx_has_color && LYShowColor >= SHOW_COLOR_ON)
1552 ? (chtype) hashStyles[a].color
1553 : A_NORMAL) | ' ');
1554 LYGetYX(y, x);
1555 (void) x;
1556 if (y == at_lineno) {
1557 LYclrtoeol();
1558 }
1559 if (!(lynx_has_color && LYShowColor >= SHOW_COLOR_ON))
1560 wbkgdset(LYwin, A_NORMAL | ' ');
1561 else if (s_normal != NOSTYLE)
1562 wbkgdset(LYwin, (chtype) (hashStyles[s_normal].color | ' '));
1563 else
1564 wbkgdset(LYwin, (chtype) (displayStyles[DSTYLE_NORMAL].color | ' '));
1565 LynxChangeStyle(a, STACK_OFF);
1566 }
1567 #endif
1568 }
1569 LYrefresh();
1570
1571 return;
1572 }
1573
novice_lines(int lineno)1574 static const char *novice_lines(int lineno)
1575 {
1576 switch (lineno) {
1577 case 0:
1578 return NOVICE_LINE_TWO_A;
1579 case 1:
1580 return NOVICE_LINE_TWO_B;
1581 case 2:
1582 return NOVICE_LINE_TWO_C;
1583 default:
1584 return "";
1585 }
1586 }
1587
1588 static int lineno = 0;
1589
toggle_novice_line(void)1590 void toggle_novice_line(void)
1591 {
1592 lineno++;
1593 if (*novice_lines(lineno) == '\0')
1594 lineno = 0;
1595 return;
1596 }
1597
noviceline(int more_flag GCC_UNUSED)1598 void noviceline(int more_flag GCC_UNUSED)
1599 {
1600 if (dump_output_immediately)
1601 return;
1602
1603 LYmove(LYlines - 2, 0);
1604 LYclrtoeol();
1605 LYaddstr(NOVICE_LINE_ONE);
1606
1607 LYmove(LYlines - 1, 0);
1608 LYclrtoeol();
1609 #if defined(DIRED_SUPPORT ) && defined(OK_OVERRIDE)
1610 if (lynx_edit_mode && !no_dired_support)
1611 LYaddstr(DIRED_NOVICELINE);
1612 else
1613 #endif /* DIRED_SUPPORT && OK_OVERRIDE */
1614
1615 if (LYUseNoviceLineTwo)
1616 LYaddstr(NOVICE_LINE_TWO);
1617 else
1618 LYaddstr(novice_lines(lineno));
1619
1620 LYrefresh();
1621 return;
1622 }
1623
1624 #if defined(MISC_EXP) || defined(TTY_DEVICE) || defined(HAVE_TTYNAME)
1625 /*
1626 * If the standard input is not a tty, and Lynx is really reading from the
1627 * standard input, attempt to reopen it, pointing to a real tty. Normally
1628 * this would happen if the user pipes data to Lynx and wants to run
1629 * interactively after that.
1630 *
1631 * Returns:
1632 * 1 if successfully reopened
1633 * -1 if we cannot reopen
1634 * 0 if we do not have to reopen
1635 */
LYReopenInput(void)1636 int LYReopenInput(void)
1637 {
1638 int result = 0;
1639 int fd;
1640
1641 if ((fd = fileno(stdin)) == 0
1642 && !isatty(fd)
1643 && LYConsoleInputFD(FALSE) == fd) {
1644 const char *term_name = NULL;
1645 int new_fd = -1;
1646
1647 #ifdef HAVE_TTYNAME
1648 if (isatty(fileno(stdout)) &&
1649 (term_name = ttyname(fileno(stdout))) != NULL)
1650 new_fd = open(term_name, O_RDONLY);
1651
1652 if (new_fd == -1 &&
1653 isatty(fileno(stderr)) &&
1654 (term_name = ttyname(fileno(stderr))) != NULL)
1655 new_fd = open(term_name, O_RDONLY);
1656 #endif
1657
1658 #ifdef HAVE_CTERMID
1659 if (new_fd == -1 &&
1660 (term_name = ctermid(NULL)) != NULL)
1661 new_fd = open(term_name, O_RDONLY);
1662 #endif
1663
1664 #ifdef TTY_DEVICE
1665 if (new_fd == -1)
1666 new_fd = open(term_name = TTY_DEVICE, O_RDONLY);
1667 #endif
1668
1669 CTRACE((tfp, "LYReopenInput open(%s) returned %d.\n", term_name, new_fd));
1670 if (new_fd >= 0) {
1671 FILE *frp;
1672
1673 close(new_fd);
1674 frp = freopen(term_name, "r", stdin);
1675 CTRACE((tfp,
1676 "LYReopenInput freopen(%s,\"r\",stdin) returned %p, stdin is now %p with fd %d.\n",
1677 term_name, (void *) frp, (void *) stdin, fileno(stdin)));
1678 result = 1;
1679 } else {
1680 result = -1;
1681 }
1682 }
1683 return result;
1684 }
1685 #endif
1686
1687 #if defined(NSL_FORK) || defined(MISC_EXP) || defined (TTY_DEVICE) || defined(HAVE_TTYNAME)
1688 /*
1689 * Returns the file descriptor from which keyboard input is expected, or INVSOC
1690 * (-1) if not available. If need_selectable is true, returns non-INVSOC fd
1691 * only if select() is possible - actually, currently only checks if fd is
1692 * connected to a tty. - kw
1693 */
LYConsoleInputFD(int need_selectable)1694 int LYConsoleInputFD(int need_selectable)
1695 {
1696 int fd = INVSOC;
1697
1698 #ifdef USE_SLANG
1699 if (!LYCursesON)
1700 fd = fileno(stdin);
1701 #if ((SLANG_VERSION >= 9919) && defined(REAL_UNIX_SYSTEM) && !defined(__CYGWIN__))
1702 /* SLang_TT_Read_FD introduced in slang 0.99.19, from its changelog:
1703 * SLang_TT_Read_FD variable is now available for unix. This is the file
1704 * descriptor used by SLang_getkey. */
1705 else
1706 fd = SLang_TT_Read_FD;
1707 #endif /* SLANG_VERSION >= 9919 */
1708 #else /* !USE_SLANG */
1709 fd = fileno(stdin);
1710 #endif /* !USE_SLANG */
1711
1712 if (need_selectable && fd != INVSOC) {
1713 if (isatty(fd)) {
1714 return fd;
1715 } else {
1716 return INVSOC;
1717 }
1718 }
1719 return fd;
1720 }
1721 #endif /* NSL_FORK || MISC_EXP */
1722
1723 static int fake_zap = 0;
1724
LYFakeZap(int set)1725 void LYFakeZap(int set)
1726 {
1727 if (set && fake_zap < 1) {
1728 CTRACE((tfp, "\r *** Set simulated 'Z'"));
1729 if (fake_zap)
1730 CTRACE((tfp, ", %d pending", fake_zap));
1731 CTRACE((tfp, " ***\n"));
1732 fake_zap++;
1733 } else if (!set && fake_zap) {
1734 CTRACE((tfp, "\r *** Unset simulated 'Z'"));
1735 CTRACE((tfp, ", %d pending", fake_zap));
1736 CTRACE((tfp, " ***\n"));
1737 fake_zap = 0;
1738 }
1739
1740 }
1741
DontCheck(void)1742 static int DontCheck(void)
1743 {
1744 static time_t last;
1745 time_t next;
1746
1747 /** Curses or slang setup was not invoked **/
1748 if (dump_output_immediately)
1749 return (TRUE);
1750
1751 if (LYHaveCmdScript()) /* we may be running from a script */
1752 return (TRUE);
1753
1754 #ifdef MISC_EXP
1755 if (LYNoZapKey)
1756 return (TRUE);
1757 #endif
1758 /*
1759 * Avoid checking interrupts more than one per second, since it is a slow
1760 * and expensive operation - TD
1761 */
1762 #ifdef HAVE_GETTIMEOFDAY
1763 #undef timezone /* U/Win defines a conflicting macro */
1764 {
1765 struct timeval tv;
1766
1767 gettimeofday(&tv, (struct timezone *) 0);
1768 next = tv.tv_usec / 100000L; /* 0.1 seconds is a compromise */
1769 }
1770 #else
1771 next = time((time_t *) 0);
1772 #endif
1773 if (next == last)
1774 return (TRUE);
1775
1776 last = next;
1777 return FALSE;
1778 }
1779
HTCheckForInterrupt(void)1780 int HTCheckForInterrupt(void)
1781 {
1782 int c;
1783 int cmd;
1784
1785 if (fake_zap > 0) {
1786 fake_zap--;
1787 CTRACE((tfp, "\r *** Got simulated 'Z' ***\n"));
1788 CTRACE_FLUSH(tfp);
1789 CTRACE_SLEEP(AlertSecs);
1790 return ((int) TRUE);
1791 }
1792
1793 /** Curses or slang setup was not invoked **/
1794 if (DontCheck())
1795 return ((int) FALSE);
1796
1797 #ifndef VMS /* UNIX stuff: */
1798
1799 #if !defined(_WINDOWS) || defined(__MINGW32__)
1800
1801 /*
1802 * First, check if there is a character.
1803 */
1804 #ifdef USE_SLANG
1805 /** No keystroke was entered
1806 Note that this isn't taking possible SOCKSification
1807 and the socks_flag into account, and may fail on the
1808 slang library's select() when SOCKSified. - FM **/
1809 #ifdef DJGPP_KEYHANDLER
1810 if (0 == _bios_keybrd(_NKEYBRD_READY))
1811 return (FALSE);
1812 #else
1813 if (0 == SLang_input_pending(0))
1814 return (FALSE);
1815 #endif /* DJGPP_KEYHANDLER */
1816
1817 #else /* Unix curses: */
1818 {
1819 struct timeval socket_timeout;
1820 int ret = 0;
1821 fd_set readfds;
1822
1823 socket_timeout.tv_sec = 0;
1824 socket_timeout.tv_usec = 0;
1825 FD_ZERO(&readfds);
1826 FD_SET(0, &readfds);
1827 #ifdef SOCKS
1828 if (socks_flag)
1829 ret = Rselect(1, &readfds, NULL, NULL, &socket_timeout);
1830 else
1831 #endif /* SOCKS */
1832 ret = select(1, &readfds, NULL, NULL, &socket_timeout);
1833
1834 /** Suspended? **/
1835 if ((ret == -1) && (SOCKET_ERRNO == EINTR))
1836 return ((int) FALSE);
1837
1838 /** No keystroke was entered? **/
1839 if (!FD_ISSET(0, &readfds))
1840 return ((int) FALSE);
1841 }
1842 #endif /* USE_SLANG */
1843
1844 #endif /* !_WINDOWS */
1845
1846 /*
1847 * Now, read the character.
1848 */
1849 #if defined(USE_CURSES_NODELAY)
1850 nodelay(LYwin, TRUE);
1851 c = LYgetch();
1852 nodelay(LYwin, FALSE);
1853 #elif defined(USE_SLANG) && defined(_WINDOWS)
1854 if (!SLang_input_pending(0))
1855 return ((int) FALSE);
1856 c = LYgetch();
1857 #else
1858 c = LYgetch();
1859 #endif
1860
1861 #else /* VMS: */
1862 extern int typeahead(void);
1863
1864 /** Control-C or Control-Y and a 'N'o reply to exit query **/
1865 if (HadVMSInterrupt) {
1866 HadVMSInterrupt = FALSE;
1867 return ((int) TRUE);
1868 }
1869
1870 c = typeahead();
1871
1872 #endif /* !VMS */
1873
1874 /*
1875 * 'c' contains whatever character we're able to read from keyboard
1876 */
1877
1878 /** Keyboard 'Z' or 'z', or Control-G or Control-C **/
1879 if (LYCharIsINTERRUPT(c))
1880 return ((int) TRUE);
1881
1882 /* There is a subset of mainloop() actions available at this stage: no new
1883 * getfile() cycle is possible until the previous finished. Currently we
1884 * have scrolling in partial mode, toggling of trace log, and pasting.
1885 * User search now in progress...
1886 */
1887 cmd = (LKC_TO_LAC(keymap, c));
1888 switch (cmd) {
1889 case LYK_TRACE_TOGGLE: /* Toggle TRACE mode. */
1890 handle_LYK_TRACE_TOGGLE();
1891 break;
1892 #ifdef CAN_CUT_AND_PASTE
1893 case LYK_TO_CLIPBOARD:{ /* ^S */
1894 const char *s = LYDownLoadAddress();
1895
1896 if (!s || !*s || put_clip(s))
1897 HTInfoMsg(gettext("Copy to clipboard failed."));
1898 else
1899 HTInfoMsg(gettext("Download document URL put to clipboard."));
1900 break;
1901 }
1902 #endif /* defined CAN_CUT_AND_PASTE */
1903 default:
1904 #ifdef DISP_PARTIAL
1905 /* OK, we got several lines from new document and want to scroll... */
1906 if (display_partial && (NumOfLines_partial > 2)) {
1907 BOOLEAN do_refresh;
1908 int res;
1909 int Newline_partial = LYGetNewline();
1910
1911 switch (cmd) {
1912 case LYK_WHEREIS: /* search within the document */
1913 case LYK_NEXT: /* search for the next occurrence in the document */
1914 case LYK_PREV: /* search for the previous occurrence in the document */
1915 handle_LYK_WHEREIS(cmd, &do_refresh);
1916 if (www_search_result != -1) {
1917 Newline_partial = www_search_result;
1918 www_search_result = -1; /* reset */
1919 }
1920 break;
1921
1922 case LYK_FASTBACKW_LINK:
1923 if (Newline_partial <= (display_lines) + 1) {
1924 Newline_partial -= display_lines;
1925 } else if ((res =
1926 HTGetLinkOrFieldStart(-1,
1927 &Newline_partial, NULL,
1928 -1, TRUE)) == LINK_LINE_FOUND) {
1929 Newline_partial++;
1930 } else if (res == LINK_DO_ARROWUP) {
1931 Newline_partial -= display_lines;
1932 }
1933 break;
1934 case LYK_FASTFORW_LINK:
1935 if (HText_canScrollDown()) {
1936 /* This is not an exact science... - kw */
1937 if (HTGetLinkOrFieldStart(HText_LinksInLines(HTMainText,
1938 Newline_partial,
1939 display_lines)
1940 - 1,
1941 &Newline_partial, NULL,
1942 1, TRUE) == LINK_LINE_FOUND) {
1943 Newline_partial++;
1944 }
1945 }
1946 break;
1947 case LYK_PREV_PAGE:
1948 if (Newline_partial > 1)
1949 Newline_partial -= display_lines;
1950 break;
1951 case LYK_NEXT_PAGE:
1952 if (HText_canScrollDown())
1953 Newline_partial += display_lines;
1954 break;
1955 case LYK_UP_HALF:
1956 if (Newline_partial > 1)
1957 Newline_partial -= (display_lines / 2);
1958 break;
1959 case LYK_DOWN_HALF:
1960 if (HText_canScrollDown())
1961 Newline_partial += (display_lines / 2);
1962 break;
1963 case LYK_UP_TWO:
1964 if (Newline_partial > 1)
1965 Newline_partial -= 2;
1966 break;
1967 case LYK_DOWN_TWO:
1968 if (HText_canScrollDown())
1969 Newline_partial += 2;
1970 break;
1971 case LYK_HOME:
1972 if (Newline_partial > 1)
1973 Newline_partial = 1;
1974 break;
1975 case LYK_END:
1976 if (HText_canScrollDown())
1977 Newline_partial = HText_getNumOfLines() - display_lines + 1;
1978 /* calculate for "current" bottom value */
1979 break;
1980 case LYK_REFRESH:
1981 break;
1982 default:
1983 /** Other or no keystrokes **/
1984 return ((int) FALSE);
1985 } /* end switch */
1986 if (Newline_partial < 1)
1987 Newline_partial = 1;
1988 if (LYMainLoop_pageDisplay(Newline_partial))
1989 NumOfLines_partial = HText_getNumOfLines();
1990 }
1991 #endif /* DISP_PARTIAL */
1992 break;
1993 } /* end switch */
1994 /** Other or no keystrokes **/
1995 return ((int) FALSE);
1996 }
1997
1998 /*
1999 * Check if the given filename looks like it's an absolute pathname, i.e.,
2000 * references a directory.
2001 */
LYisAbsPath(const char * path)2002 BOOLEAN LYisAbsPath(const char *path)
2003 {
2004 BOOLEAN result = FALSE;
2005
2006 if (non_empty(path)) {
2007 #ifdef VMS
2008 result = TRUE;
2009 #else
2010 #if defined(USE_DOS_DRIVES)
2011 result = (BOOLEAN) (LYIsPathSep(path[0])
2012 || (LYIsDosDrive(path)
2013 && LYIsPathSep(path[2])));
2014 #else
2015 result = (BOOLEAN) (LYIsPathSep(path[0]));
2016 #endif /* USE_DOS_DRIVES */
2017 #endif
2018 }
2019 return result;
2020 }
2021
2022 /*
2023 * Check if the given filename is the root path, e.g., "/" on Unix.
2024 */
LYisRootPath(const char * path)2025 BOOLEAN LYisRootPath(const char *path)
2026 {
2027 #if defined(USE_DOS_DRIVES)
2028 if (strlen(path) == 3
2029 && LYIsDosDrive(path)
2030 && LYIsPathSep(path[2]))
2031 return TRUE;
2032 #endif
2033 return (BOOL) ((strlen(path) == 1) && LYIsPathSep(path[0]));
2034 }
2035
2036 /*
2037 * A file URL for a remote host is an obsolete ftp URL.
2038 * Return YES only if we're certain it's a local file. - FM
2039 */
LYisLocalFile(const char * filename)2040 BOOLEAN LYisLocalFile(const char *filename)
2041 {
2042 char *host = NULL;
2043 char *acc_method = NULL;
2044 char *cp;
2045
2046 if (!filename)
2047 return NO;
2048 if (!(host = HTParse(filename, "", PARSE_HOST)))
2049 return NO;
2050 if (!*host) {
2051 FREE(host);
2052 return NO;
2053 }
2054
2055 if ((cp = strchr(host, ':')) != NULL)
2056 *cp = '\0';
2057
2058 if ((acc_method = HTParse(filename, "", PARSE_ACCESS))) {
2059 if (0 == strcmp("file", acc_method) &&
2060 (0 == strcmp(host, "localhost") ||
2061 LYSameFilename(host, HTHostName()))) {
2062 FREE(host);
2063 FREE(acc_method);
2064 return YES;
2065 }
2066 }
2067
2068 FREE(host);
2069 FREE(acc_method);
2070 return NO;
2071 }
2072
2073 /*
2074 * Utility for checking URLs with a host field. Return YES only if we're
2075 * certain it's the local host. - FM
2076 */
LYisLocalHost(const char * filename)2077 BOOLEAN LYisLocalHost(const char *filename)
2078 {
2079 char *host = NULL;
2080 char *cp;
2081
2082 if (!filename)
2083 return NO;
2084 if (!(host = HTParse(filename, "", PARSE_HOST)))
2085 return NO;
2086 if (!*host) {
2087 FREE(host);
2088 return NO;
2089 }
2090
2091 if ((cp = strchr(host, ':')) != NULL)
2092 *cp = '\0';
2093
2094 if ((LYSameFilename(host, "localhost") ||
2095 LYSameFilename(host, LYHostName) ||
2096 LYSameFilename(host, HTHostName()))) {
2097 FREE(host);
2098 return YES;
2099 }
2100
2101 FREE(host);
2102 return NO;
2103 }
2104
2105 /*
2106 * Free an HTList that contains strings.
2107 */
LYFreeStringList(HTList * list)2108 void LYFreeStringList(HTList *list)
2109 {
2110 if (list != NULL) {
2111 char *argument;
2112 HTList *cur = list;
2113
2114 while (NULL != (argument = (char *) HTList_nextObject(cur))) {
2115 FREE(argument);
2116 }
2117 HTList_delete(list);
2118 }
2119 }
2120
2121 /*
2122 * Utility for freeing the list of local host aliases. - FM
2123 */
LYLocalhostAliases_free(void)2124 void LYLocalhostAliases_free(void)
2125 {
2126 LYFreeStringList(localhost_aliases);
2127 localhost_aliases = NULL;
2128 }
2129
2130 /*
2131 * Utility for listing hosts to be treated as local aliases. - FM
2132 */
LYAddLocalhostAlias(char * alias)2133 void LYAddLocalhostAlias(char *alias)
2134 {
2135 char *LocalAlias = NULL;
2136
2137 if (!non_empty(alias))
2138 return;
2139
2140 if (!localhost_aliases) {
2141 localhost_aliases = HTList_new();
2142 #ifdef LY_FIND_LEAKS
2143 atexit(LYLocalhostAliases_free);
2144 #endif
2145 }
2146
2147 StrAllocCopy(LocalAlias, alias);
2148 HTList_addObject(localhost_aliases, LocalAlias);
2149
2150 return;
2151 }
2152
2153 /*
2154 * Utility for checking URLs with a host field. Return YES only if we've
2155 * listed the host as a local alias. - FM
2156 */
LYisLocalAlias(const char * filename)2157 BOOLEAN LYisLocalAlias(const char *filename)
2158 {
2159 char *host = NULL;
2160 char *alias;
2161 char *cp;
2162 HTList *cur = localhost_aliases;
2163
2164 if (!cur || !filename)
2165 return NO;
2166 if (!(host = HTParse(filename, "", PARSE_HOST)))
2167 return NO;
2168 if (!(*host)) {
2169 FREE(host);
2170 return NO;
2171 }
2172
2173 if ((cp = strchr(host, ':')) != NULL)
2174 *cp = '\0';
2175
2176 while (NULL != (alias = (char *) HTList_nextObject(cur))) {
2177 if (LYSameFilename(host, alias)) {
2178 FREE(host);
2179 return YES;
2180 }
2181 }
2182
2183 FREE(host);
2184 return NO;
2185 }
2186
2187 /*
2188 * This function checks for a URL with an unknown scheme,
2189 * but for which proxying has been set up, and if so,
2190 * returns PROXY_URL_TYPE. - FM
2191 *
2192 * If a colon is present but the string segment which
2193 * precedes it is not being proxied, and we can be sure
2194 * that what follows the colon is not a port field,
2195 * it returns UNKNOWN_URL_TYPE. Otherwise, it returns
2196 * 0 (not a URL). - FM
2197 */
LYCheckForProxyURL(char * filename)2198 UrlTypes LYCheckForProxyURL(char *filename)
2199 {
2200 char *cp = filename;
2201 char *cp1;
2202 char *cp2 = NULL;
2203
2204 /*
2205 * Don't crash on an empty argument.
2206 */
2207 if (isEmpty(cp))
2208 return (NOT_A_URL_TYPE);
2209
2210 /* kill beginning spaces */
2211 cp = LYSkipBlanks(cp);
2212
2213 /*
2214 * Check for a colon, and if present,
2215 * see if we have proxying set up.
2216 */
2217 if ((cp1 = strchr((cp + 1), ':')) != NULL) {
2218 if ((cp2 = strchr((cp + 1), '/')) != NULL && cp2 < cp1)
2219 return (NOT_A_URL_TYPE);
2220 *cp1 = '\0';
2221 cp2 = NULL;
2222 StrAllocCopy(cp2, cp);
2223 *cp1 = ':';
2224 StrAllocCat(cp2, "_proxy");
2225 if (LYGetEnv(cp2) != NULL) {
2226 FREE(cp2);
2227 return (PROXY_URL_TYPE);
2228 }
2229 FREE(cp2);
2230 #if defined (USE_DOS_DRIVES)
2231 if (LYIsDosDrive(cp))
2232 return (NOT_A_URL_TYPE);
2233 #endif
2234 cp1++;
2235 if (!*cp) {
2236 return (NOT_A_URL_TYPE);
2237 } else if (isdigit(UCH(*cp1))) {
2238 while (*cp1 && isdigit(UCH(*cp1)))
2239 cp1++;
2240 if (*cp1 && !LYIsHtmlSep(*cp1))
2241 return (UNKNOWN_URL_TYPE);
2242 } else {
2243 return (UNKNOWN_URL_TYPE);
2244 }
2245 }
2246
2247 return (NOT_A_URL_TYPE);
2248 }
2249
2250 /*
2251 * Compare a "type:" string, replacing it by the comparison-string if it
2252 * matches (and return true in that case).
2253 */
compare_type(char * tst,const char * cmp,size_t len)2254 static BOOLEAN compare_type(char *tst,
2255 const char *cmp,
2256 size_t len)
2257 {
2258 if (!strncasecomp(tst, cmp, (int) len)) {
2259 if (StrNCmp(tst, cmp, len)) {
2260 size_t i;
2261
2262 for (i = 0; i < len; i++)
2263 tst[i] = cmp[i];
2264 }
2265 return TRUE;
2266 }
2267 return FALSE;
2268 }
2269 #define CompareType(tst,cmp,len) compare_type((tst),(cmp),(size_t)(len))
2270
2271 #define DoubleHtmlSep(s) (LYIsHtmlSep((s)[0]) && LYIsHtmlSep((s)[1]))
2272 #define compare_two(tst,cmp,len,limit) \
2273 ((len + 2) <= limit \
2274 && DoubleHtmlSep(tst + len) \
2275 && CompareType(tst, cmp, len))
2276
2277 /*
2278 * Must recognize a URL and return the type.
2279 * If recognized, based on a case-insensitive
2280 * analysis of the scheme field, ensures that
2281 * the scheme field has the expected case.
2282 *
2283 * Returns 0 (not a URL) for a NULL argument,
2284 * one which lacks a colon.
2285 *
2286 * Chains to LYCheckForProxyURL() if a colon
2287 * is present but the type is not recognized.
2288 */
is_url(char * filename)2289 UrlTypes is_url(char *filename)
2290 {
2291 char *cp = filename;
2292 char *cp1;
2293 UrlTypes result = NOT_A_URL_TYPE;
2294 int limit;
2295
2296 /*
2297 * Don't crash on an empty argument.
2298 */
2299 if (isEmpty(cp))
2300 return (result);
2301
2302 /*
2303 * Can't be a URL if it lacks a colon and if it starts with '[' it's
2304 * probably IPv6 adress.
2305 */
2306 if (NULL == strchr(cp, ':') || cp[0] == '[')
2307 return (result);
2308
2309 /*
2310 * Kill beginning spaces.
2311 */
2312 cp = LYSkipBlanks(cp);
2313
2314 /*
2315 * Can't be a URL if it starts with a slash. So return immediately for
2316 * this common case, also to avoid false positives if there was a colon
2317 * later in the string. Also can't be a URL if it starts with a colon. -
2318 * KW
2319 */
2320 if (*cp == ':' || LYIsHtmlSep(*cp)) {
2321 result = NOT_A_URL_TYPE;
2322
2323 } else {
2324 limit = (int) strlen(cp);
2325 switch (*cp) {
2326 case 'L':
2327 case 'l':
2328 /*
2329 * Lynx internal pages ("LYNXfoo:" or "lynxfoo:") start with 'l' or
2330 * 'L', other URLs aren't.
2331 */
2332 if (CompareType(cp, STR_LYNXEXEC, LEN_LYNXEXEC)) {
2333 /*
2334 * Special External Lynx type to handle execution of commands
2335 * or scripts which require a pause to read the screen upon
2336 * completion.
2337 */
2338 result = LYNXEXEC_URL_TYPE;
2339
2340 } else if (CompareType(cp, STR_LYNXPROG, LEN_LYNXPROG)) {
2341 /*
2342 * Special External Lynx type to handle execution of commands,
2343 * scripts or programs with do not require a pause to read
2344 * screen upon completion.
2345 */
2346 result = LYNXPROG_URL_TYPE;
2347
2348 } else if (CompareType(cp, STR_LYNXCGI, LEN_LYNXCGI)) {
2349 /*
2350 * Special External Lynx type to handle cgi scripts.
2351 */
2352 result = LYNXCGI_URL_TYPE;
2353
2354 } else if (CompareType(cp, STR_LYNXPRINT, LEN_LYNXPRINT)) {
2355 /*
2356 * Special Internal Lynx type.
2357 */
2358 result = LYNXPRINT_URL_TYPE;
2359
2360 } else if (CompareType(cp, STR_LYNXOPTIONS, LEN_LYNXOPTIONS)) {
2361 /*
2362 * Special Internal Lynx type.
2363 */
2364 result = LYNXOPTIONS_URL_TYPE;
2365
2366 } else if (CompareType(cp, STR_LYNXCFG, LEN_LYNXCFG)) {
2367 /*
2368 * Special Internal Lynx type.
2369 */
2370 result = LYNXCFG_URL_TYPE;
2371
2372 } else if (CompareType(cp, STR_LYNXMESSAGES, LEN_LYNXMESSAGES)) {
2373 /*
2374 * Special Internal Lynx type.
2375 */
2376 result = LYNXMESSAGES_URL_TYPE;
2377
2378 } else if (CompareType(cp, STR_LYNXCFLAGS, LEN_LYNXCFLAGS)) {
2379 /*
2380 * Special Internal Lynx type.
2381 */
2382 result = LYNXCOMPILE_OPTS_URL_TYPE;
2383
2384 } else if (CompareType(cp, STR_LYNXDOWNLOAD, LEN_LYNXDOWNLOAD)) {
2385 /*
2386 * Special Internal Lynx type.
2387 */
2388 result = LYNXDOWNLOAD_URL_TYPE;
2389
2390 } else if (CompareType(cp, STR_LYNXDIRED, LEN_LYNXDIRED)) {
2391 /*
2392 * Special Internal Lynx type.
2393 */
2394 result = LYNXDIRED_URL_TYPE;
2395
2396 } else if (CompareType(cp, STR_LYNXHIST, LEN_LYNXHIST)) {
2397 /*
2398 * Special Internal Lynx type.
2399 */
2400 result = LYNXHIST_URL_TYPE;
2401
2402 #ifdef USE_CACHEJAR
2403 } else if (CompareType(cp, STR_LYNXCACHE, LEN_LYNXCACHE)) {
2404 /*
2405 * Special Internal Lynx type.
2406 */
2407 result = LYNXCACHE_URL_TYPE;
2408 #endif
2409
2410 } else if (CompareType(cp, STR_LYNXKEYMAP, LEN_LYNXKEYMAP)) {
2411 /*
2412 * Special Internal Lynx type.
2413 */
2414 result = LYNXKEYMAP_URL_TYPE;
2415
2416 } else if (CompareType(cp, STR_LYNXIMGMAP, LEN_LYNXIMGMAP)) {
2417 /*
2418 * Special Internal Lynx type.
2419 */
2420 /* force lower/uppercase of next part */
2421 (void) is_url(&cp[LEN_LYNXIMGMAP]);
2422 result = LYNXIMGMAP_URL_TYPE;
2423
2424 } else if (CompareType(cp, STR_LYNXCOOKIE, LEN_LYNXCOOKIE)) {
2425 /*
2426 * Special Internal Lynx type.
2427 */
2428 result = LYNXCOOKIE_URL_TYPE;
2429 }
2430 break;
2431 #ifndef DISABLE_NEWS
2432 /*
2433 * NEWSfoo: schemes -
2434 */
2435 case 'N':
2436 case 'n':
2437 if (CompareType(cp, STR_NEWS_URL, LEN_NEWS_URL)) {
2438 result = NEWS_URL_TYPE;
2439
2440 } else if (CompareType(cp, STR_NNTP_URL, LEN_NNTP_URL)) {
2441 result = NNTP_URL_TYPE;
2442
2443 } else if (CompareType(cp, "newspost:", 9)) {
2444 /*
2445 * Special Lynx type to handle news posts.
2446 */
2447 result = NEWSPOST_URL_TYPE;
2448
2449 } else if (CompareType(cp, "newsreply:", 10)) {
2450 /*
2451 * Special Lynx type to handle news replies (followups).
2452 */
2453 result = NEWSREPLY_URL_TYPE;
2454 }
2455 break;
2456
2457 /*
2458 * SNEWSfoo: schemes -
2459 */
2460 case 'S':
2461 case 's':
2462 if (CompareType(cp, STR_SNEWS_URL, LEN_SNEWS_URL)) {
2463 result = SNEWS_URL_TYPE;
2464
2465 } else if (CompareType(cp, "snewspost:", 10)) {
2466 /*
2467 * Special Lynx type to handle snews posts.
2468 */
2469 result = NEWSPOST_URL_TYPE;
2470
2471 } else if (CompareType(cp, "snewsreply:", 11)) {
2472 /*
2473 * Special Lynx type to handle snews replies (followups).
2474 */
2475 result = NEWSREPLY_URL_TYPE;
2476 }
2477 break;
2478 #endif
2479 case 'M':
2480 case 'm':
2481 if (CompareType(cp, STR_MAILTO_URL, LEN_MAILTO_URL)) {
2482 result = MAILTO_URL_TYPE;
2483 }
2484 break;
2485
2486 case 'F':
2487 case 'f':
2488 if (CompareType(cp, STR_FILE_URL, LEN_FILE_URL)) {
2489 if (LYisLocalFile(cp)) {
2490 result = FILE_URL_TYPE;
2491 } else if (DoubleHtmlSep(cp + LEN_FILE_URL)) {
2492 result = FTP_URL_TYPE;
2493 }
2494 }
2495 #ifndef DISABLE_FTP
2496 else if (compare_two(cp, STR_FTP_URL, LEN_FTP_URL, limit)) {
2497 result = FTP_URL_TYPE;
2498 }
2499 #endif
2500 #ifndef DISABLE_FINGER
2501 else if (compare_two(cp, STR_FINGER_URL, LEN_FINGER_URL, limit)) {
2502 result = FINGER_URL_TYPE;
2503 }
2504 #endif
2505 break;
2506
2507 case 'B':
2508 case 'b':
2509 #ifndef DISABLE_BIBP
2510 if (CompareType(cp, STR_BIBP_URL, LEN_BIBP_URL)) {
2511 result = BIBP_URL_TYPE;
2512 }
2513 #endif
2514 break;
2515
2516 case 'D':
2517 case 'd':
2518 if (CompareType(cp, "data:", 5)) {
2519 result = DATA_URL_TYPE;
2520 }
2521 break;
2522
2523 default:
2524 if (limit >= 3
2525 && ((cp1 = strchr(cp + 3, ':')) == NULL
2526 || !DoubleHtmlSep(cp1 + 1))) {
2527 /*
2528 * If it doesn't contain "://", and it's not one of the the
2529 * above, it can't be a URL with a scheme we know, so check if
2530 * it's an unknown scheme for which proxying has been set up.
2531 * - FM
2532 */
2533 if (cp1 != NULL
2534 && (cp1 - cp) > 1 /* exclude DOS-style device:/path */
2535 && LYisAbsPath(cp1 + 1)) {
2536 result = NCFTP_URL_TYPE;
2537 }
2538
2539 } else {
2540 switch (*cp) {
2541 case 'H':
2542 case 'h':
2543 if (CompareType(cp, STR_HTTP_URL, LEN_HTTP_URL)) {
2544 result = HTTP_URL_TYPE;
2545
2546 } else if (CompareType(cp, STR_HTTPS_URL, LEN_HTTPS_URL)) {
2547 result = HTTPS_URL_TYPE;
2548 }
2549 break;
2550
2551 #ifndef DISABLE_GOPHER
2552 case 'G':
2553 case 'g':
2554 if (CompareType(cp, STR_GOPHER_URL, LEN_GOPHER_URL)) {
2555 if (strlen(cp) >= 11
2556 && (cp1 = strchr(cp + 11, '/')) != NULL) {
2557
2558 if (TOUPPER(*(cp1 + 1)) == 'H' || *(cp1 + 1) == 'w')
2559 /* if this is a gopher html type */
2560 result = HTML_GOPHER_URL_TYPE;
2561 else if (*(cp1 + 1) == 'T' || *(cp1 + 1) == '8')
2562 result = TELNET_GOPHER_URL_TYPE;
2563 else if (*(cp1 + 1) == '7')
2564 result = INDEX_GOPHER_URL_TYPE;
2565 else
2566 result = GOPHER_URL_TYPE;
2567 } else {
2568 result = GOPHER_URL_TYPE;
2569 }
2570 }
2571 break;
2572 #endif
2573 case 'W':
2574 case 'w':
2575 if (CompareType(cp, STR_WAIS_URL, LEN_WAIS_URL)) {
2576 result = WAIS_URL_TYPE;
2577 }
2578 break;
2579
2580 case 'T':
2581 case 't':
2582 if (CompareType(cp, STR_TELNET_URL, LEN_TELNET_URL)) {
2583 result = TELNET_URL_TYPE;
2584
2585 } else if (CompareType(cp, STR_TN3270_URL, LEN_TN3270_URL)) {
2586 result = TN3270_URL_TYPE;
2587 }
2588 break;
2589
2590 case 'R':
2591 case 'r':
2592 if (CompareType(cp, STR_RLOGIN_URL, LEN_RLOGIN_URL)) {
2593 result = RLOGIN_URL_TYPE;
2594 }
2595 break;
2596
2597 case 'C':
2598 case 'c':
2599 if (CompareType(cp, STR_CSO_URL, LEN_CSO_URL)) {
2600 result = CSO_URL_TYPE;
2601 }
2602 break;
2603
2604 case 'A':
2605 case 'a':
2606 if (CompareType(cp, "afs:", 4)) {
2607 result = AFS_URL_TYPE;
2608 }
2609 break;
2610
2611 case 'P':
2612 case 'p':
2613 if (CompareType(cp, "prospero:", 9)) {
2614 result = PROSPERO_URL_TYPE;
2615 }
2616 break;
2617 }
2618 }
2619 }
2620 /*
2621 * Check if it is an unknown scheme for which proxying has been set up.
2622 */
2623 if (result == NOT_A_URL_TYPE)
2624 result = LYCheckForProxyURL(filename);
2625 }
2626 return result;
2627 }
2628
2629 /*
2630 * Sometimes it is just expected that curses is on when an alert or other
2631 * statusline message needs to be shown and we are not just dumping
2632 * immediately. Calling this will 'fix' it, but may not always be appropriate.
2633 * - kw
2634 */
LYFixCursesOn(const char * reason)2635 void LYFixCursesOn(const char *reason)
2636 {
2637 if (dump_output_immediately || LYCursesON)
2638 return;
2639 if (reason) {
2640 CTRACE((tfp, "Forcing curses on to %s\n", reason));
2641 }
2642 start_curses();
2643 }
2644
2645 /*
2646 * Most protocol modules called through HTLoad* expect that curses is on unless
2647 * dump_output_immediately is set, so that statusline messages can be shown.
2648 * Some protocols expect the opposite, namely telnet and friends. This
2649 * function should be called after the 'physical' URL for accessing addr has
2650 * been established. It does the right thing to the degree that curses is
2651 * turned on for known problem cases. In any normal circumstances this should
2652 * never apply, but proxying or rule substitution is not prevented for
2653 * telnet-like URLs, and this 'fix' avoids some crashes that can otherwise
2654 * occur. - kw
2655 */
LYFixCursesOnForAccess(const char * addr,const char * physical)2656 BOOLEAN LYFixCursesOnForAccess(const char *addr,
2657 const char *physical)
2658 {
2659 /*
2660 * If curses is off when maybe it shouldn't...
2661 */
2662 if (!dump_output_immediately && !LYCursesON && physical) {
2663 char *cp1;
2664
2665 /*
2666 * If requested resource wants to be accessed with curses off, and
2667 * getfile() would indeed have turned curses off for it...
2668 */
2669 if (strstr(addr, "://") != NULL &&
2670 (isTELNET_URL(addr) ||
2671 isRLOGIN_URL(addr) ||
2672 isTN3270_URL(addr) ||
2673 (!isGOPHER_URL(addr) &&
2674 (cp1 = strchr(addr + 11, '/')) != NULL &&
2675 (*(cp1 + 1) == 'T' || *(cp1 + 1) == '8')))) {
2676 /*
2677 * If actual access that will be done is ok with curses off, then
2678 * do nothing special, else force curses on. - kw
2679 */
2680 if (!isTELNET_URL(physical) &&
2681 !isRLOGIN_URL(physical) &&
2682 !isTN3270_URL(physical)) {
2683 start_curses();
2684 HTAlert(gettext("Unexpected access protocol for this URL scheme."));
2685 return TRUE;
2686 }
2687 }
2688 }
2689 return FALSE;
2690 }
2691
2692 /*
2693 * Determine whether we allow HEAD and related flags for a URL. - kw
2694 */
LYCanDoHEAD(const char * address)2695 BOOLEAN LYCanDoHEAD(const char *address)
2696 {
2697 char *temp0 = NULL;
2698 int isurl;
2699
2700 if (!non_empty(address))
2701 return FALSE;
2702 if (!StrNCmp(address, "http", 4))
2703 return TRUE;
2704 /* Make copy for is_url() since caller may not care for case changes */
2705 StrAllocCopy(temp0, address);
2706 isurl = is_url(temp0);
2707 if (!isurl) {
2708 FREE(temp0);
2709 return FALSE;
2710 }
2711 if (isurl == LYNXCGI_URL_TYPE) {
2712 FREE(temp0);
2713 #if defined(LYNXCGI_LINKS) && !defined(VMS)
2714 return TRUE;
2715 #else
2716 return FALSE;
2717 #endif
2718 }
2719 /*
2720 * The idea of the following is to allow HEAD for news URLs that identify
2721 * single articles, not those that identify ranges of articles or groups or
2722 * a list of groups. - kw
2723 */
2724 if (isurl == NEWS_URL_TYPE || isurl == NNTP_URL_TYPE) {
2725 char *temp = HTParse(address, "", PARSE_PATH);
2726 char *cp = strrchr(temp, '/');
2727
2728 if (strchr((cp ? cp : temp), '@') != NULL) {
2729 FREE(temp0);
2730 FREE(temp);
2731 return TRUE;
2732 }
2733 if (cp && isdigit(UCH(cp[1])) && strchr(cp, '-') == NULL) {
2734 FREE(temp0);
2735 FREE(temp);
2736 return TRUE;
2737 }
2738 FREE(temp);
2739 }
2740 #define ALLOW_PROXY_HEAD
2741 /* If defined, also allow head requests for URLs proxied through the "http" or
2742 * "lynxcgi" protocols, which understand HEAD. Only the proxy environment
2743 * variables are checked, not the HTRules system. - kw
2744 */
2745 #ifdef ALLOW_PROXY_HEAD
2746 if (isurl != FILE_URL_TYPE) {
2747 char *acc_method = HTParse(temp0, "", PARSE_ACCESS);
2748
2749 if (non_empty(acc_method)) {
2750 char *proxy;
2751
2752 StrAllocCat(acc_method, "_proxy");
2753 proxy = LYGetEnv(acc_method);
2754 if (proxy && (isHTTP_URL(proxy) ||
2755 isLYNXCGI(proxy)) &&
2756 !override_proxy(temp0)) {
2757 FREE(temp0);
2758 FREE(acc_method);
2759 return TRUE;
2760 }
2761 }
2762 FREE(acc_method);
2763 }
2764 #endif /* ALLOW_PROXY_HEAD */
2765
2766 FREE(temp0);
2767 return FALSE;
2768 }
2769
2770 /*
2771 * Close an input file.
2772 */
LYCloseInput(FILE * fp)2773 BOOLEAN LYCloseInput(FILE *fp)
2774 {
2775 int result = FALSE;
2776
2777 if (fp != 0) {
2778 int err = ferror(fp);
2779 LY_TEMP *p = FindTempfileByFP(fp);
2780
2781 fclose(fp);
2782 if (p != 0) {
2783 p->file = 0;
2784 }
2785 if (!err) {
2786 result = TRUE;
2787 }
2788 }
2789 return (BOOLEAN) result;
2790 }
2791
2792 /*
2793 * Close an output file, reporting any problems with writing to it.
2794 */
LYCloseOutput(FILE * fp)2795 BOOLEAN LYCloseOutput(FILE *fp)
2796 {
2797 int result = FALSE;
2798
2799 if (fp != 0) {
2800 int err = ferror(fp);
2801 LY_TEMP *p = FindTempfileByFP(fp);
2802
2803 fclose(fp);
2804 if (p != 0) {
2805 p->file = 0;
2806 }
2807 if (!err) {
2808 result = TRUE;
2809 }
2810 }
2811 if (!result) {
2812 HTAlert(CANNOT_WRITE_TO_FILE);
2813 }
2814 return (BOOLEAN) result;
2815 }
2816
2817 /*
2818 * Test if we'll be able to write a file. If not, warn the user.
2819 */
LYCanWriteFile(const char * filename)2820 BOOLEAN LYCanWriteFile(const char *filename)
2821 {
2822 BOOLEAN result = FALSE;
2823
2824 if (LYCloseOutput(fopen(filename, "w"))) {
2825 if (remove(filename) == 0) {
2826 result = TRUE;
2827 }
2828 } else {
2829 _statusline(NEW_FILENAME_PROMPT);
2830 }
2831 return result;
2832 }
2833
2834 /*
2835 * Test if we'll be able to read a file.
2836 */
LYCanReadFile(const char * filename)2837 BOOLEAN LYCanReadFile(const char *filename)
2838 {
2839 FILE *fp;
2840
2841 if (non_empty(filename)) {
2842 if ((fp = fopen(filename, "r")) != 0) {
2843 return LYCloseInput(fp);
2844 }
2845 }
2846 return FALSE;
2847 }
2848
2849 /*
2850 * Remove backslashes from any string.
2851 */
remove_backslashes(char * buf)2852 void remove_backslashes(char *buf)
2853 {
2854 char *cp;
2855
2856 for (cp = buf; *cp != '\0'; cp++) {
2857
2858 if (*cp != '\\') { /* don't print slashes */
2859 *buf = *cp;
2860 buf++;
2861 } else if (*cp == '\\' && /* print one slash if there */
2862 *(cp + 1) == '\\') { /* are two in a row */
2863 *buf = *cp;
2864 buf++;
2865 }
2866 }
2867 *buf = '\0';
2868 return;
2869 }
2870
2871 /*
2872 * Checks to see if the current process is attached via a terminal in the local
2873 * domain.
2874 */
inlocaldomain(void)2875 BOOLEAN inlocaldomain(void)
2876 {
2877 BOOLEAN result = TRUE;
2878
2879 #ifdef HAVE_UTMP
2880 int n;
2881 FILE *fp;
2882 struct utmp me;
2883 char *cp, *mytty = NULL;
2884
2885 if ((cp = ttyname(0)))
2886 mytty = LYLastPathSep(cp);
2887
2888 result = FALSE;
2889 if (mytty && (fp = fopen(UTMP_FILE, "r")) != NULL) {
2890 mytty++;
2891 do {
2892 n = (int) fread((char *) &me, sizeof(struct utmp), (size_t) 1, fp);
2893 } while (n > 0 && !STREQ(me.ut_line, mytty));
2894 (void) LYCloseInput(fp);
2895
2896 if (n > 0) {
2897 if (strlen(me.ut_host) > strlen(LYLocalDomain) &&
2898 STREQ(LYLocalDomain,
2899 me.ut_host + strlen(me.ut_host) - strlen(LYLocalDomain))) {
2900 result = TRUE;
2901 }
2902 #ifdef LINUX
2903 /* Linux fix to check for local user. J.Cullen 11Jul94 */
2904 else if (strlen(me.ut_host) == 0) {
2905 result = TRUE;
2906 }
2907 #endif /* LINUX */
2908 }
2909
2910 } else {
2911 CTRACE((tfp,
2912 "Could not get ttyname (returned %s) or open UTMP file %s\n",
2913 NONNULL(cp), UTMP_FILE));
2914 }
2915 #else
2916 CTRACE((tfp, "LYUtils: inlocaldomain() not supported.\n"));
2917 #endif /* HAVE_UTMP */
2918 return (result);
2919 }
2920
2921 #ifdef HAVE_SIGACTION
2922 /*
2923 * An extended alternative for calling signal(), sets some flags for signal
2924 * handler as we want them if that functionality is available. (We don't
2925 * return anything from this function since the return value would currently be
2926 * ignored anyway.) - kw
2927 */
LYExtSignal(int sig,LYSigHandlerFunc_t * handler)2928 void LYExtSignal(int sig,
2929 LYSigHandlerFunc_t *handler)
2930 {
2931 #ifdef SIGWINCH
2932 /* add more cases to if(condition) if required... */
2933 if (sig == SIGWINCH && LYNonRestartingSIGWINCH) {
2934 struct sigaction act;
2935
2936 act.sa_handler = handler;
2937 sigemptyset(&act.sa_mask);
2938 act.sa_flags = 0;
2939 sigaction(sig, &act, NULL);
2940 } else
2941 #endif /* defined(SIGWINCH) */
2942 signal(sig, handler);
2943 }
2944 #endif /* HAVE_SIGACTION */
2945
2946 #if defined(SIGTSTP) && !defined(USE_SLANG)
2947 #ifdef HAVE_SIGACTION
2948 /*
2949 * For switching a signal's handling between SIG_DFL and something (possibly)
2950 * different that may have been set up by lynx code or e.g. by curses library.
2951 * Uses sigaction to preserve / restore as much state as possible.
2952 *
2953 * Second arg is where to save or restore from.
2954 *
2955 * Third arg to_dfl specifies what to do:
2956 * 1 Save current state in where, set handling to SIG_DFL
2957 * 0 Restore current state to previously saved one in where
2958 *
2959 * Currently only used for SIGTSTP without SLANG, to prevent (n)curses signal
2960 * handler from running while lynx is waiting in system() for an interactive
2961 * command like an editor. - kw
2962 */
LYToggleSigDfl(int sig,struct sigaction * where,int to_dfl)2963 static BOOLEAN LYToggleSigDfl(int sig,
2964 struct sigaction *where,
2965 int to_dfl)
2966 {
2967 int rv = -1;
2968 struct sigaction oact;
2969
2970 if (to_dfl == 1) {
2971 rv = sigaction(sig, NULL, &oact);
2972 if (rv == 0) {
2973 if (oact.sa_handler != SIG_DFL) {
2974 oact.sa_handler = SIG_DFL;
2975 rv = sigaction(sig, &oact, where);
2976 } else if (where) {
2977 memcpy(where, &oact, sizeof(oact));
2978 rv = 0;
2979 }
2980 }
2981 } else {
2982 rv = sigaction(sig, where, NULL);
2983 }
2984 if (rv != 0) {
2985 CTRACE((tfp, "Error in LYToggleSigDfl: %s\n", LYStrerror(errno)));
2986 return FALSE;
2987 } else
2988 return TRUE;
2989 }
2990 #endif /* HAVE_SIGACTION */
2991 #endif /* SIGTSTP && !USE_SLANG */
2992
2993 /**************
2994 * This bit of code catches window size change signals
2995 */
2996
2997 #ifdef HAVE_SYS_IOCTL_H
2998 #include <sys/ioctl.h>
2999 #endif
3000
3001 /* For systems that have both, but both can't be included, duh (or neither) */
3002 /* FIXME: this whole chunk may be redundant */
3003 #ifdef TERMIO_AND_CURSES
3004 # ifdef TERMIO_AND_TERMIOS
3005 # include <termio.h>
3006 # else
3007 # ifdef HAVE_TERMIOS_H
3008 # include <termios.h>
3009 # else
3010 # ifdef HAVE_TERMIO_H
3011 # include <termio.h>
3012 # endif /* HAVE_TERMIO_H */
3013 # endif /* HAVE_TERMIOS_H */
3014 # endif /* TERMIO_AND_TERMIOS */
3015 #endif /* TERMIO_AND_CURSES */
3016
size_change(int sig GCC_UNUSED)3017 void size_change(int sig GCC_UNUSED)
3018 {
3019 int old_lines = LYlines;
3020 int old_cols = LYcols;
3021
3022 #ifdef USE_SLANG
3023 #if defined(VMS) || defined(UNIX)
3024 SLtt_get_screen_size();
3025 #endif /* VMS || UNIX */
3026 LYlines = SLtt_Screen_Rows;
3027 LYcols = SLtt_Screen_Cols;
3028 #ifdef SLANG_MBCS_HACK
3029 PHYSICAL_SLtt_Screen_Cols = LYcols;
3030 #ifdef SLANG_NO_LIMIT /* define this if slang has been fixed */
3031 SLtt_Screen_Cols = LYcolLimit * 6;
3032 #else
3033 /* Needs to be limited: fixed buffer bugs in slang can cause crash,
3034 see slang's SLtt_smart_puts - kw */
3035 SLtt_Screen_Cols = HTMIN(LYcolLimit * 6, 255);
3036 #endif
3037 #endif /* SLANG_MBCS_HACK */
3038 if (sig == 0)
3039 /*
3040 * Called from start_curses().
3041 */
3042 return;
3043 #else /* Curses: */
3044 #ifdef HAVE_SIZECHANGE
3045 #ifdef TIOCGSIZE
3046 struct ttysize win;
3047
3048 #else
3049 #ifdef TIOCGWINSZ
3050 struct winsize win;
3051 #endif /* TIOCGWINSZ */
3052 #endif /* TIOCGSIZE */
3053
3054 #ifdef TIOCGSIZE
3055 if (ioctl(0, TIOCGSIZE, &win) == 0) {
3056 if (win.ts_lines != 0) {
3057 LYlines = win.ts_lines;
3058 }
3059 if (win.ts_cols != 0) {
3060 LYcols = win.ts_cols;
3061 }
3062 }
3063 #else
3064 #ifdef TIOCGWINSZ
3065 if (ioctl(0, (long) TIOCGWINSZ, &win) == 0) {
3066 if (win.ws_row != 0) {
3067 LYlines = win.ws_row;
3068 }
3069 if (win.ws_col != 0) {
3070 LYcols = win.ws_col;
3071 }
3072 }
3073 #endif /* TIOCGWINSZ */
3074 #endif /* TIOCGSIZE */
3075 #endif /* HAVE_SIZECHANGE */
3076
3077 #ifdef __EMX__
3078 {
3079 int scrsize[2];
3080
3081 _scrsize(scrsize);
3082 LYcols = scrsize[0];
3083 LYlines = scrsize[1];
3084 }
3085 #endif
3086
3087 if (LYlines <= 0)
3088 LYlines = DFT_ROWS;
3089 if (LYcols <= 0)
3090 LYcols = DFT_COLS;
3091 #endif /* USE_SLANG */
3092
3093 /*
3094 * Check if the screen size has actually changed. - AJL
3095 */
3096 if (LYlines != old_lines || LYcols != old_cols) {
3097 recent_sizechange = TRUE;
3098 CTRACE((tfp, "Window size changed from (%d,%d) to (%d,%d)\n",
3099 old_lines, old_cols, LYlines, LYcols));
3100 #if defined(CAN_SWITCH_DISPLAY_CHARSET) && defined(CAN_AUTODETECT_DISPLAY_CHARSET)
3101 /* May need to reload the font due to different char-box size */
3102 if (current_char_set != auto_display_charset)
3103 Switch_Display_Charset(current_char_set, SWITCH_DISPLAY_CHARSET_RESIZE);
3104 #endif
3105 }
3106 #ifdef SIGWINCH
3107 LYExtSignal(SIGWINCH, size_change);
3108 #endif /* SIGWINCH */
3109
3110 return;
3111 }
3112
3113 /*
3114 * Utility for freeing the list of previous suggested filenames. - FM
3115 */
HTSugFilenames_free(void)3116 void HTSugFilenames_free(void)
3117 {
3118 LYFreeStringList(sug_filenames);
3119 sug_filenames = NULL;
3120 }
3121
3122 /*
3123 * Utility for listing suggested filenames, making any repeated filenames the
3124 * most current in the list. - FM
3125 */
HTAddSugFilename(char * fname)3126 void HTAddSugFilename(char *fname)
3127 {
3128 char *tmp = NULL;
3129 char *old;
3130 HTList *cur;
3131
3132 if (!non_empty(fname))
3133 return;
3134
3135 StrAllocCopy(tmp, fname);
3136
3137 if (!sug_filenames) {
3138 sug_filenames = HTList_new();
3139 #ifdef LY_FIND_LEAKS
3140 atexit(HTSugFilenames_free);
3141 #endif
3142 HTList_addObject(sug_filenames, tmp);
3143 return;
3144 }
3145
3146 cur = sug_filenames;
3147 while (NULL != (old = (char *) HTList_nextObject(cur))) {
3148 if (!strcmp(old, tmp)) {
3149 HTList_removeObject(sug_filenames, old);
3150 FREE(old);
3151 break;
3152 }
3153 }
3154 HTList_addObject(sug_filenames, tmp);
3155
3156 return;
3157 }
3158
3159 /*
3160 * CHANGE_SUG_FILENAME -- Foteos Macrides 29-Dec-1993 Upgraded for use with
3161 * Lynx2.2 - FM 17-Jan-1994
3162 */
change_sug_filename(char * fname)3163 void change_sug_filename(char *fname)
3164 {
3165 const char *cp2;
3166 char *temp = 0, *cp, *cp1, *end;
3167
3168 #ifdef VMS
3169 char *dot;
3170 int j, k;
3171 #endif /* VMS */
3172
3173 /*
3174 * Establish the current end of fname.
3175 */
3176 end = fname + strlen(fname);
3177
3178 /*
3179 * Unescape fname.
3180 */
3181 HTUnEscape(fname);
3182
3183 /*
3184 * Rename any temporary files.
3185 */
3186 cp2 = wwwName(lynx_temp_space);
3187 if (LYIsHtmlSep(*cp2)) {
3188 HTSprintf0(&temp, "file://localhost%s" PID_FMT, cp2, GETPID());
3189 } else {
3190 HTSprintf0(&temp, "file://localhost/%s" PID_FMT, cp2, GETPID());
3191 }
3192 if (!StrNCmp(fname, temp, strlen(temp))) {
3193 if ((cp = strrchr(fname, '.')) != 0) {
3194 if (strlen(cp) > (strlen(temp) - 4))
3195 cp = NULL;
3196 }
3197 StrAllocCopy(temp, NonNull(cp));
3198 sprintf(fname, "temp%.*s", LY_MAXPATH - 10, temp);
3199 }
3200 FREE(temp);
3201
3202 if (fname[strlen(fname) - 1] == '/')
3203 /*
3204 * Hmm... we have a directory name. It is annoying to see a
3205 * scheme+host+path name as a suggested one, let's remove the
3206 * last_slash and go ahead like we have a file name. - LP
3207 */
3208 fname[strlen(fname) - 1] = '\0';
3209
3210 /*
3211 * Remove everything up the the last_slash if there is one.
3212 */
3213 if ((cp = strrchr(fname, '/')) != NULL && strlen(cp) > 1) {
3214 cp1 = fname;
3215 /*
3216 * Go past the slash.
3217 */
3218 cp++;
3219 for (; *cp != '\0'; cp++, cp1++) {
3220 *cp1 = *cp;
3221 }
3222 *cp1 = '\0';
3223 }
3224 #ifdef _WINDOWS /* 1998/05/05 (Tue) 10:08:05 */
3225 if ((cp = strrchr(fname, '=')) != NULL && strlen(cp) > 1) {
3226 cp1 = fname;
3227 /*
3228 * Go past the '='.
3229 */
3230 cp++;
3231 for (; *cp != '\0'; cp++, cp1++) {
3232 *cp1 = *cp;
3233 }
3234 *cp1 = '\0';
3235 }
3236 #endif
3237
3238 /*
3239 * Trim off date-size suffix, if present.
3240 */
3241 if ((*(end - 1) == ']') && ((cp = strrchr(fname, '[')) != NULL) &&
3242 (cp > fname) && *(--cp) == ' ') {
3243 while (*cp == ' ') {
3244 *(cp--) = '\0';
3245 }
3246 }
3247 #ifdef VMS
3248 /*
3249 * Trim off VMS device and/or directory specs, if present.
3250 */
3251 if ((cp = strchr(fname, '[')) != NULL &&
3252 (cp1 = strrchr(cp, ']')) != NULL && strlen(cp1) > 1) {
3253 cp1++;
3254 for (cp = fname; *cp1 != '\0'; cp1++) {
3255 *(cp++) = *cp1;
3256 }
3257 *cp = '\0';
3258 }
3259 /*
3260 * Replace illegal or problem characters.
3261 */
3262 dot = fname + strlen(fname);
3263 for (cp = fname; cp < dot; cp++) {
3264 /*
3265 * Replace with underscores.
3266 */
3267 if (*cp == ' ' || *cp == '/' || *cp == ':' ||
3268 *cp == '[' || *cp == ']' || *cp == '&') {
3269 *cp = '_';
3270 /*
3271 * Replace with dashes.
3272 */
3273 } else if (*cp == '!' || *cp == '?' || *cp == '\'' ||
3274 *cp == ',' || *cp == ':' || *cp == '"' ||
3275 *cp == '+' || *cp == '@' || *cp == '\\' ||
3276 *cp == '(' || *cp == ')' || *cp == '=' ||
3277 *cp == '<' || *cp == '>' || *cp == '#' ||
3278 *cp == '%' || *cp == '*' || *cp == '`' ||
3279 *cp == '~' || *cp == '^' || *cp == '|' ||
3280 *cp < ' ' || (UCH(*cp)) > 126) {
3281 *cp = '-';
3282 }
3283 }
3284
3285 /*
3286 * Collapse any serial underscores.
3287 */
3288 cp = fname + 1;
3289 j = 0;
3290 while (cp < dot) {
3291 if (fname[j] == '_' && *cp == '_') {
3292 cp++;
3293 } else {
3294 fname[++j] = *cp++;
3295 }
3296 }
3297 fname[++j] = '\0';
3298
3299 /*
3300 * Collapse any serial dashes.
3301 */
3302 dot = fname + (strlen(fname));
3303 cp = fname + 1;
3304 j = 0;
3305 while (cp < dot) {
3306 if (fname[j] == '-' && *cp == '-') {
3307 cp++;
3308 } else {
3309 fname[++j] = *cp++;
3310 }
3311 }
3312 fname[++j] = '\0';
3313
3314 /*
3315 * Trim any trailing or leading underscores or dashes.
3316 */
3317 cp = fname + (strlen(fname)) - 1;
3318 while (*cp == '_' || *cp == '-') {
3319 *cp-- = '\0';
3320 }
3321 if (fname[0] == '_' || fname[0] == '-') {
3322 dot = fname + (strlen(fname));
3323 cp = fname;
3324 while ((*cp == '_' || *cp == '-') && cp < dot) {
3325 cp++;
3326 }
3327 j = 0;
3328 while (cp < dot) {
3329 fname[j++] = *cp++;
3330 }
3331 fname[j] = '\0';
3332 }
3333
3334 /*
3335 * Replace all but the last period with _s, or second to last if last is
3336 * followed by a terminal Z or z, or GZ or gz,
3337 * e.g., convert foo.tar.Z to foo.tar_Z
3338 * or, convert foo.tar.gz to foo.tar-gz
3339 */
3340 j = strlen(fname) - 1;
3341 if ((dot = strrchr(fname, '.')) != NULL) {
3342 if (TOUPPER(fname[j]) == 'Z') {
3343 if ((fname[j - 1] == '.') &&
3344 (((cp = strchr(fname, '.')) != NULL) && cp < dot)) {
3345 *dot = '_';
3346 dot = strrchr(fname, '.');
3347 } else if (((TOUPPER(fname[j - 1]) == 'G') &&
3348 fname[j - 2] == '.') &&
3349 (((cp = strchr(fname, '.')) != NULL) && cp < dot)) {
3350 *dot = '-';
3351 dot = strrchr(fname, '.');
3352 }
3353 }
3354 cp = fname;
3355 while ((cp = strchr(cp, '.')) != NULL && cp < dot) {
3356 *cp = '_';
3357 }
3358
3359 /*
3360 * But if the root is > 39 characters, move the period appropriately to
3361 * the left.
3362 */
3363 while (dot - fname > 39) {
3364 *dot = '\0';
3365 if ((cp = strrchr(fname, '_')) != NULL) {
3366 *cp = '.';
3367 *dot = '_';
3368 } else if ((cp = strrchr(fname, '-')) != NULL) {
3369 *cp = '.';
3370 *dot = '_';
3371 } else if (*(dot + 1) == '\0') {
3372 j = strlen(fname);
3373 while (j > 39) {
3374 fname[j] = fname[j - 1];
3375 j--;
3376 }
3377 fname[j] = '.';
3378 } else {
3379 *dot = '.';
3380 j = 39;
3381 k = 0;
3382 while (dot[k] != '\0') {
3383 fname[j++] = dot[k++];
3384 }
3385 fname[j] = '\0';
3386 }
3387 dot = strrchr(fname, '.');
3388 }
3389
3390 /*
3391 * Make sure the extension is < 40 characters.
3392 */
3393 if ((fname + strlen(fname) - dot) > 39) {
3394 *(dot + 40) = '\0';
3395 }
3396
3397 /*
3398 * Trim trailing dashes or underscores.
3399 */
3400 j = (strlen(fname) - 1);
3401 while (fname[j] == '_' || fname[j] == '-') {
3402 fname[j--] = '\0';
3403 }
3404 } else {
3405 /*
3406 * No period, so put one on the end, or after the 39th character,
3407 * trimming trailing dashes or underscores.
3408 */
3409 if (strlen(fname) > 39) {
3410 fname[39] = '\0';
3411 }
3412 j = (strlen(fname) - 1);
3413 while ((fname[j] == '_') || (fname[j] == '-')) {
3414 j--;
3415 }
3416 fname[++j] = '.';
3417 fname[++j] = '\0';
3418 }
3419
3420 #else /* Not VMS (UNIX): */
3421
3422 /*
3423 * Replace problem characters.
3424 */
3425 for (cp = fname; *cp != '\0'; cp++) {
3426 switch (*cp) {
3427 case '\'':
3428 case '"':
3429 case '/':
3430 case ' ':
3431 *cp = '-';
3432 }
3433 }
3434 #endif /* VMS (UNIX) */
3435
3436 /*
3437 * Make sure the rest of the original string in nulled.
3438 */
3439 cp = fname + strlen(fname);
3440 while (cp < end) {
3441 *cp++ = '\0';
3442 }
3443
3444 return;
3445 }
3446
3447 /*
3448 * Construct a temporary-filename. Assumes result is LY_MAXPATH chars long.
3449 */
fmt_tempname(char * result,const char * prefix,const char * suffix)3450 static int fmt_tempname(char *result,
3451 const char *prefix,
3452 const char *suffix)
3453 {
3454 int code;
3455
3456 #ifdef HAVE_RAND_TEMPNAME
3457 #define SIZE_TEMPNAME ((MAX_TEMPNAME / BITS_PER_CHAR) + 1)
3458 static BOOL first = TRUE;
3459 static int names_used = 0;
3460 static unsigned char used_tempname[SIZE_TEMPNAME];
3461 unsigned offset, mask;
3462 #endif
3463 static unsigned counter;
3464 char leaf[LY_MAXPATH];
3465
3466 if (prefix == 0)
3467 prefix = "";
3468 if (suffix == 0)
3469 suffix = "";
3470 /*
3471 * Prefer a random value rather than a counter.
3472 */
3473 #ifdef HAVE_RAND_TEMPNAME
3474 if (first) {
3475 lynx_srand((unsigned) ((long) time((time_t *) NULL) + (long) result));
3476 first = FALSE;
3477 }
3478
3479 /* We don't really need all of the bits from rand(). The high-order bits
3480 * are the more-random portion in any case, but limiting the width of the
3481 * generated name is done partly to avoid problems on systems that may not
3482 * support long filenames.
3483 */
3484 counter = MAX_TEMPNAME;
3485 if (names_used < MAX_TEMPNAME) {
3486 long get_rand = lynx_rand();
3487 long max_rand = LYNX_RAND_MAX;
3488
3489 counter = (unsigned) (((float) MAX_TEMPNAME * (float) get_rand) /
3490 (float) max_rand + 1);
3491 /*
3492 * Avoid reusing a temporary name, since there are places in the code
3493 * which can refer to a temporary filename even after it has been
3494 * closed and removed from the filesystem.
3495 */
3496 do {
3497 counter %= MAX_TEMPNAME;
3498 offset = counter / BITS_PER_CHAR;
3499 mask = (unsigned) (1 << (counter % BITS_PER_CHAR));
3500 if ((used_tempname[offset] & mask) == 0) {
3501 names_used++;
3502 used_tempname[offset] |= UCH(mask);
3503 break;
3504 }
3505 } while ((used_tempname[offset] & mask) == 0);
3506 }
3507 if (names_used >= MAX_TEMPNAME)
3508 HTAlert(gettext("Too many tempfiles"));
3509 #else
3510 counter++;
3511 #endif
3512
3513 #ifdef FNAMES_8_3
3514 /*
3515 * The 'lynx_temp_space' string ends with a '/' or '\\', so we only have to
3516 * limit the length of the leaf. As received (e.g., from HTCompressed),
3517 * the suffix may contain more than a ".htm", e.g., "-txt.gz", so we trim
3518 * off from the filename portion to make room.
3519 */
3520 sprintf(leaf, PID_FMT PID_FMT, counter, GETPID());
3521 if (strlen(leaf) > 8)
3522 leaf[8] = 0;
3523 if (strlen(suffix) > 4 || *suffix != '.') {
3524 const char *tail = strchr(suffix, '.');
3525
3526 if (tail == 0)
3527 tail = suffix + strlen(suffix);
3528 if (8 - (tail - suffix) >= 0)
3529 leaf[8 - (tail - suffix)] = 0;
3530 }
3531 strcat(leaf, suffix);
3532 #else
3533 sprintf(leaf, "L" PID_FMT "-%uTMP%s", GETPID(), counter, suffix);
3534 #endif
3535 /*
3536 * Someone could have configured the temporary pathname to be too long.
3537 */
3538 if ((strlen(prefix) + strlen(leaf)) < LY_MAXPATH) {
3539 sprintf(result, "%s%s", prefix, leaf);
3540 code = TRUE;
3541 } else {
3542 sprintf(result, "%.*s", LY_MAXPATH - 1, leaf);
3543 code = FALSE;
3544 }
3545 CTRACE((tfp, "-> '%s'\n", result));
3546 return (code);
3547 }
3548
3549 /*
3550 * Convert 4, 6, 2, 8 to left, right, down, up, etc.
3551 */
number2arrows(int number)3552 int number2arrows(int number)
3553 {
3554 switch (number) {
3555 case '1':
3556 number = END_KEY;
3557 break;
3558 case '2':
3559 number = DNARROW;
3560 break;
3561 case '3':
3562 number = PGDOWN;
3563 break;
3564 case '4':
3565 number = LTARROW;
3566 break;
3567 case '5':
3568 number = DO_NOTHING;
3569 break;
3570 case '6':
3571 number = RTARROW;
3572 break;
3573 case '7':
3574 number = HOME;
3575 break;
3576 case '8':
3577 number = UPARROW;
3578 break;
3579 case '9':
3580 number = PGUP;
3581 break;
3582 }
3583
3584 return (number);
3585 }
3586
3587 /*
3588 * parse_restrictions takes a string of comma-separated restrictions and sets
3589 * the corresponding flags to restrict the facilities available.
3590 */
3591 /* The first two are special: we want to record whether "default" or "all"
3592 * restrictions were applied, in addition to the detailed effects of those
3593 * options. - kw
3594 */
3595 /* skip the special flags when processing "all" and "default": */
3596 #define N_SPECIAL_RESTRICT_OPTIONS 2
3597 /* *INDENT-OFF* */
3598 static const struct {
3599 const char *name;
3600 BOOLEAN *flag;
3601 BOOLEAN can;
3602 } restrictions[] = {
3603 { "default", &had_restrictions_default, TRUE },
3604 { "all", &had_restrictions_all, TRUE },
3605 { "inside_telnet", &no_inside_telnet, CAN_ANONYMOUS_INSIDE_DOMAIN_TELNET },
3606 { "outside_telnet", &no_outside_telnet, CAN_ANONYMOUS_OUTSIDE_DOMAIN_TELNET },
3607 { "telnet_port", &no_telnet_port, CAN_ANONYMOUS_GOTO_TELNET_PORT },
3608 { "inside_ftp", &no_inside_ftp, CAN_ANONYMOUS_INSIDE_DOMAIN_FTP },
3609 { "outside_ftp", &no_outside_ftp, CAN_ANONYMOUS_OUTSIDE_DOMAIN_FTP },
3610 { "inside_rlogin", &no_inside_rlogin, CAN_ANONYMOUS_INSIDE_DOMAIN_RLOGIN },
3611 { "outside_rlogin", &no_outside_rlogin, CAN_ANONYMOUS_OUTSIDE_DOMAIN_RLOGIN },
3612 { "suspend", &no_suspend, FALSE },
3613 { "editor", &no_editor, FALSE },
3614 { "shell", &no_shell, FALSE },
3615 { "bookmark", &no_bookmark, FALSE },
3616 { "multibook", &no_multibook, FALSE },
3617 { "bookmark_exec", &no_bookmark_exec, FALSE },
3618 { "option_save", &no_option_save, FALSE },
3619 { "print", &no_print, CAN_ANONYMOUS_PRINT },
3620 { "download", &no_download, FALSE },
3621 { "disk_save", &no_disk_save, FALSE },
3622 #if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)
3623 { "exec", &no_exec, LOCAL_EXECUTION_LINKS_ALWAYS_OFF_FOR_ANONYMOUS },
3624 #endif
3625 { "lynxcgi", &no_lynxcgi, FALSE },
3626 { "exec_frozen", &exec_frozen, FALSE },
3627 { "goto", &no_goto, CAN_ANONYMOUS_GOTO },
3628 { "jump", &no_jump, CAN_ANONYMOUS_JUMP },
3629 { "file_url", &no_file_url, FALSE },
3630 #ifndef DISABLE_NEWS
3631 { "news_post", &no_newspost, FALSE },
3632 { "inside_news", &no_inside_news, CAN_ANONYMOUS_INSIDE_DOMAIN_READ_NEWS },
3633 { "outside_news", &no_outside_news, CAN_ANONYMOUS_OUTSIDE_DOMAIN_READ_NEWS },
3634 #endif
3635 { "mail", &no_mail, CAN_ANONYMOUS_MAIL },
3636 { "dotfiles", &no_dotfiles, FALSE },
3637 { "useragent", &no_useragent, FALSE },
3638 #ifdef SUPPORT_CHDIR
3639 { "chdir", &no_chdir, FALSE },
3640 #endif
3641 #ifdef DIRED_SUPPORT
3642 { "dired_support", &no_dired_support, FALSE },
3643 #ifdef OK_PERMIT
3644 { "change_exec_perms", &no_change_exec_perms, FALSE },
3645 #endif /* OK_PERMIT */
3646 #endif /* DIRED_SUPPORT */
3647 #ifdef USE_EXTERNALS
3648 { "externals", &no_externals, FALSE },
3649 #endif
3650 { "lynxcfg_info", &no_lynxcfg_info, CAN_ANONYMOUS_VIEW_LYNXCFG_INFO },
3651 #ifndef NO_CONFIG_INFO
3652 { "lynxcfg_xinfo", &no_lynxcfg_xinfo, CAN_ANONYMOUS_VIEW_LYNXCFG_EXTENDED_INFO },
3653 #ifdef HAVE_CONFIG_H
3654 { "compileopts_info", &no_compileopts_info, CAN_ANONYMOUS_VIEW_COMPILEOPTS_INFO },
3655 #endif
3656 #endif
3657 /* put "goto" restrictions on the end, since they are a refinement */
3658 #ifndef DISABLE_BIBP
3659 { "goto_bibp", &no_goto_bibp, CAN_ANONYMOUS_GOTO_BIBP },
3660 #endif
3661 #ifdef HAVE_CONFIG_H
3662 #ifndef NO_CONFIG_INFO
3663 { "goto_configinfo", &no_goto_configinfo, CAN_ANONYMOUS_GOTO_CONFIGINFO },
3664 #endif
3665 #endif
3666 { "goto_cso", &no_goto_cso, CAN_ANONYMOUS_GOTO_CSO },
3667 { "goto_file", &no_goto_file, CAN_ANONYMOUS_GOTO_FILE },
3668 #ifndef DISABLE_FINGER
3669 { "goto_finger", &no_goto_finger, CAN_ANONYMOUS_GOTO_FINGER },
3670 #endif
3671 { "goto_ftp", &no_goto_ftp, CAN_ANONYMOUS_GOTO_FTP },
3672 #ifndef DISABLE_GOPHER
3673 { "goto_gopher", &no_goto_gopher, CAN_ANONYMOUS_GOTO_GOPHER },
3674 #endif
3675 { "goto_http", &no_goto_http, CAN_ANONYMOUS_GOTO_HTTP },
3676 { "goto_https", &no_goto_https, CAN_ANONYMOUS_GOTO_HTTPS },
3677 { "goto_lynxcgi", &no_goto_lynxcgi, CAN_ANONYMOUS_GOTO_LYNXCGI },
3678 { "goto_lynxexec", &no_goto_lynxexec, CAN_ANONYMOUS_GOTO_LYNXEXEC },
3679 { "goto_lynxprog", &no_goto_lynxprog, CAN_ANONYMOUS_GOTO_LYNXPROG },
3680 { "goto_mailto", &no_goto_mailto, CAN_ANONYMOUS_GOTO_MAILTO },
3681 #ifndef DISABLE_NEWS
3682 { "goto_news", &no_goto_news, CAN_ANONYMOUS_GOTO_NEWS },
3683 { "goto_nntp", &no_goto_nntp, CAN_ANONYMOUS_GOTO_NNTP },
3684 #endif
3685 { "goto_rlogin", &no_goto_rlogin, CAN_ANONYMOUS_GOTO_RLOGIN },
3686 #ifndef DISABLE_NEWS
3687 { "goto_snews", &no_goto_snews, CAN_ANONYMOUS_GOTO_SNEWS },
3688 #endif
3689 { "goto_telnet", &no_goto_telnet, CAN_ANONYMOUS_GOTO_TELNET },
3690 { "goto_tn3270", &no_goto_tn3270, CAN_ANONYMOUS_GOTO_TN3270 },
3691 { "goto_wais", &no_goto_wais, CAN_ANONYMOUS_GOTO_WAIS },
3692 };
3693 /* *INDENT-ON* */
3694
3695 /* This will make no difference between '-' and '_'. It does only in/equality
3696 * compare. It assumes that p2 can't contain dashes, but p1 can. This
3697 * function is also used (if macro OPTNAME_ALLOW_DASHES doesn't have value of
3698 * zero) for compare of commandline options -VH
3699 */
strn_dash_equ(const char * p1,const char * p2,int len)3700 BOOL strn_dash_equ(const char *p1,
3701 const char *p2,
3702 int len)
3703 {
3704 while (len--) {
3705 if (!*p2)
3706 return 0; /* canonical name is shorter */
3707 switch (*p1) {
3708 case 0:
3709 return 0;
3710 case '-':
3711 case '_':
3712 if (*p2 != '_')
3713 return 0;
3714 else
3715 break;
3716 default:
3717 if (*p1 != *p2)
3718 return 0;
3719 }
3720 ++p1;
3721 ++p2;
3722 }
3723 return 1;
3724 }
3725
3726 /* Uncomment following lines to allow only exact string matching */
3727 /* #define RESTRICT_NM_ALLOW_DASHES 0 */
3728
3729 #ifndef RESTRICT_NM_ALLOW_DASHES
3730 # define RESTRICT_NM_ALLOW_DASHES 1
3731 #endif
3732
3733 #if RESTRICT_NM_ALLOW_DASHES
3734 # define RESTRICT_NM_EQU(a,b,len) strn_dash_equ(a,b,len)
3735 #else
3736 # define RESTRICT_NM_EQU(a,b,len) STRNEQ(a,b,len)
3737 #endif
3738
3739 /*
3740 * Returns the inx'th name from the restrictions table, or null if inx is
3741 * out of range.
3742 */
index_to_restriction(unsigned inx)3743 const char *index_to_restriction(unsigned inx)
3744 {
3745 if (inx < TABLESIZE(restrictions))
3746 return restrictions[inx].name;
3747 return NULL;
3748 }
3749
3750 /*
3751 * Returns the value TRUE/FALSE of a given restriction, or -1 if it is not
3752 * one that we recognize.
3753 */
find_restriction(const char * name,int len)3754 int find_restriction(const char *name,
3755 int len)
3756 {
3757 unsigned i;
3758
3759 if (len < 0)
3760 len = (int) strlen(name);
3761 for (i = 0; i < TABLESIZE(restrictions); i++) {
3762 if (RESTRICT_NM_EQU(name, restrictions[i].name, len)) {
3763 return (*restrictions[i].flag);
3764 }
3765 }
3766 return -1;
3767 }
3768
parse_restrictions(const char * s)3769 void parse_restrictions(const char *s)
3770 {
3771 const char *p;
3772 const char *word;
3773 unsigned i;
3774 BOOLEAN found;
3775
3776 p = s;
3777 while (*p) {
3778 p = LYSkipCBlanks(p);
3779 if (*p == '\0')
3780 break;
3781 word = p;
3782 while (*p != ',' && *p != '\0')
3783 p++;
3784
3785 found = FALSE;
3786 if (RESTRICT_NM_EQU(word, "all", (int) (p - word))) {
3787 found = TRUE;
3788 for (i = N_SPECIAL_RESTRICT_OPTIONS;
3789 i < TABLESIZE(restrictions);
3790 i++)
3791 *(restrictions[i].flag) = TRUE;
3792 } else if (RESTRICT_NM_EQU(word, "default", (int) (p - word))) {
3793 found = TRUE;
3794 for (i = N_SPECIAL_RESTRICT_OPTIONS;
3795 i < TABLESIZE(restrictions);
3796 i++)
3797 *(restrictions[i].flag) = (BOOLEAN) !restrictions[i].can;
3798 } else {
3799 for (i = 0; i < TABLESIZE(restrictions); i++) {
3800 if (RESTRICT_NM_EQU(word, restrictions[i].name, (int) (p - word))) {
3801 *(restrictions[i].flag) = TRUE;
3802 found = TRUE;
3803 break;
3804 }
3805 }
3806 }
3807 if (!found) {
3808 printf("%s: %.*s\n", gettext("unknown restriction"),
3809 (int) (p - word), word);
3810 exit_immediately(EXIT_FAILURE);
3811 }
3812 if (*p)
3813 p++;
3814 }
3815
3816 /*
3817 * If shell is restricted, set restrictions on related topics.
3818 */
3819 if (no_shell) {
3820 no_goto_lynxexec = TRUE;
3821 no_goto_lynxprog = TRUE;
3822 no_goto_lynxcgi = TRUE;
3823 #ifdef EXEC_LINKS
3824 local_exec_on_local_files = TRUE;
3825 #endif
3826 }
3827 }
3828
print_restrictions_to_fd(FILE * fp)3829 void print_restrictions_to_fd(FILE *fp)
3830 {
3831 unsigned i, count = 0;
3832
3833 for (i = 0; i < TABLESIZE(restrictions); i++) {
3834 if (*(restrictions[i].flag) == TRUE) {
3835 count++;
3836 }
3837 }
3838 if (!count) {
3839 fprintf(fp, gettext("No restrictions set.\n"));
3840 return;
3841 }
3842 fprintf(fp, gettext("Restrictions set:\n"));
3843 for (i = 0; i < TABLESIZE(restrictions); i++) {
3844 if (*(restrictions[i].flag) == TRUE) {
3845 /* if "goto" is restricted, don't bother tell about its
3846 * refinements
3847 */
3848 if (StrNCmp(restrictions[i].name, "goto_", 5)
3849 || !no_goto)
3850 fprintf(fp, " %s\n", restrictions[i].name);
3851 }
3852 }
3853 }
3854
3855 #ifdef VMS
3856 #include <jpidef.h>
3857 #include <maildef.h>
3858 #include <starlet.h>
3859
3860 typedef struct _VMSMailItemList {
3861 short buffer_length;
3862 short item_code;
3863 void *buffer_address;
3864 long *return_length_address;
3865 } VMSMailItemList;
3866
LYCheckMail(void)3867 void LYCheckMail(void)
3868 {
3869 static BOOL firsttime = TRUE, failure = FALSE;
3870 static char user[13], dir[252];
3871 static long userlen = 0, dirlen;
3872 static time_t lastcheck = 0;
3873 time_t now;
3874 static short new, lastcount;
3875 long ucontext = 0, status;
3876 short flags = MAIL$M_NEWMSG;
3877 /* *INDENT-OFF* */
3878 VMSMailItemList
3879 null_list[] = {{0,0,0,0}},
3880 jpi_list[] = {{sizeof(user) - 1,JPI$_USERNAME,(void *)user,&userlen},
3881 {0,0,0,0}},
3882 uilist[] = {{0,MAIL$_USER_USERNAME,0,0},
3883 {0,0,0,0}},
3884 uolist[] = {{sizeof(new),MAIL$_USER_NEW_MESSAGES,&new,0},
3885 {sizeof(dir),MAIL$_USER_FULL_DIRECTORY,dir,&dirlen},
3886 {0,0,0,0}};
3887 /* *INDENT-ON* */
3888
3889 extern long mail$user_begin();
3890 extern long mail$user_get_info();
3891 extern long mail$user_end();
3892
3893 if (failure)
3894 return;
3895
3896 if (firsttime) {
3897 firsttime = FALSE;
3898 /* Get the username. */
3899 status = sys$getjpiw(0, 0, 0, jpi_list, 0, 0, 0);
3900 if (!(status & 1)) {
3901 failure = TRUE;
3902 return;
3903 }
3904 user[userlen] = '\0';
3905 LYTrimTrailing(user);
3906 }
3907
3908 /* Minimum report interval is 60 sec. */
3909 time(&now);
3910 if (now - lastcheck < 60)
3911 return;
3912 lastcheck = now;
3913
3914 /* Get the current newmail count. */
3915 status = mail$user_begin(&ucontext, null_list, null_list);
3916 if (!(status & 1)) {
3917 failure = TRUE;
3918 return;
3919 }
3920 uilist[0].buffer_length = strlen(user);
3921 uilist[0].buffer_address = user;
3922 status = mail$user_get_info(&ucontext, uilist, uolist);
3923 if (!(status & 1)) {
3924 failure = TRUE;
3925 return;
3926 }
3927
3928 /* Should we report anything to the user? */
3929 if (new > 0) {
3930 if (lastcount == 0)
3931 /* Have newmail at startup of Lynx. */
3932 HTUserMsg(HAVE_UNREAD_MAIL_MSG);
3933 else if (new > lastcount)
3934 /* Have additional mail since last report. */
3935 HTUserMsg(HAVE_NEW_MAIL_MSG);
3936 lastcount = new;
3937 return;
3938 }
3939 lastcount = new;
3940
3941 /* Clear the context */
3942 mail$user_end((long *) &ucontext, null_list, null_list);
3943 return;
3944 }
3945 #else
LYCheckMail(void)3946 void LYCheckMail(void)
3947 {
3948 static BOOL firsttime = TRUE;
3949 static char *mf;
3950 static time_t lastcheck;
3951 static time_t lasttime;
3952 static long lastsize;
3953 time_t now;
3954 struct stat st;
3955
3956 if (firsttime) {
3957 mf = LYGetEnv("MAIL");
3958 firsttime = FALSE;
3959 time(&lasttime);
3960 }
3961
3962 if (mf == NULL)
3963 return;
3964
3965 time(&now);
3966 if (now - lastcheck < 60)
3967 return;
3968 lastcheck = now;
3969
3970 if ((stat(mf, &st) < 0)
3971 || !S_ISREG(st.st_mode)) {
3972 mf = NULL;
3973 return;
3974 }
3975
3976 if (st.st_size > 0) {
3977 if (((lasttime != st.st_mtime) && (st.st_mtime > st.st_atime))
3978 || ((lastsize != 0) && (st.st_size > lastsize)))
3979 HTUserMsg(HAVE_NEW_MAIL_MSG);
3980 else if (lastsize == 0)
3981 HTUserMsg(HAVE_MAIL_MSG);
3982 }
3983 lastsize = (long) st.st_size;
3984 lasttime = st.st_mtime;
3985 return;
3986 }
3987 #endif /* VMS */
3988
3989 /*
3990 * This function ensures that an href will be
3991 * converted to a fully resolved, absolute URL,
3992 * with guessing of the host or expansions of
3993 * lead tildes via LYConvertToURL() if needed,
3994 * and tweaking/simplifying via HTParse(). It
3995 * is used for LynxHome, startfile, homepage,
3996 * and 'g'oto entries, after they have been
3997 * passed to LYFillLocalFileURL(). - FM
3998 * Such URLs have no `base' reference to which they
3999 * could be resolved. LYLegitimizeHREF could not be used.
4000 */
LYEnsureAbsoluteURL(char ** href,const char * name,int fixit)4001 void LYEnsureAbsoluteURL(char **href,
4002 const char *name,
4003 int fixit)
4004 {
4005 char *temp = NULL;
4006
4007 if (isEmpty(*href))
4008 return;
4009
4010 /*
4011 * Check whether to fill in localhost. - FM
4012 */
4013 LYFillLocalFileURL(href, "file://localhost");
4014
4015 /*
4016 * If it is not a URL then make it one.
4017 */
4018 if (!strcasecomp(*href, STR_NEWS_URL)) {
4019 StrAllocCat(*href, "*");
4020 } else if (!strcasecomp(*href, STR_SNEWS_URL)) {
4021 StrAllocCat(*href, "/*");
4022 }
4023
4024 if (!is_url(*href)) {
4025 CTRACE((tfp, "%s%s'%s' is not a URL\n",
4026 NonNull(name), (name ? " " : ""), *href));
4027 LYConvertToURL(href, fixit);
4028 }
4029
4030 temp = HTParse(*href, "", PARSE_ALL);
4031 if (non_empty(temp))
4032 StrAllocCopy(*href, temp);
4033 FREE(temp);
4034 }
4035
4036 /*
4037 * Rewrite and reallocate a previously allocated string as a file URL if the
4038 * string resolves to a file or directory on the local system, otherwise as an
4039 * http URL. - FM
4040 */
LYConvertToURL(char ** AllocatedString,int fixit)4041 void LYConvertToURL(char **AllocatedString,
4042 int fixit)
4043 {
4044 char *old_string = *AllocatedString;
4045 char *temp = NULL;
4046 char *cp = NULL;
4047
4048 #ifndef VMS
4049 struct stat st;
4050 #endif /* !VMS */
4051
4052 if (isEmpty(old_string))
4053 return;
4054
4055 #if defined(USE_DOS_DRIVES)
4056 {
4057 char *cp_url = *AllocatedString;
4058
4059 for (; *cp_url != '\0'; cp_url++)
4060 if (*cp_url == '\\')
4061 *cp_url = '/';
4062 cp_url--;
4063 if (LYIsDosDrive(*AllocatedString) && *cp_url == ':')
4064 LYAddPathSep(AllocatedString);
4065 }
4066 #endif /* USE_DOS_DRIVES */
4067
4068 *AllocatedString = NULL; /* so StrAllocCopy doesn't free it */
4069 StrAllocCopy(*AllocatedString, "file://localhost");
4070
4071 if (*old_string != '/') {
4072 char *fragment = NULL;
4073
4074 #if defined(USE_DOS_DRIVES)
4075 StrAllocCat(*AllocatedString, "/");
4076 #endif /* USE_DOS_DRIVES */
4077 #ifdef VMS
4078 /*
4079 * Not a SHELL pathspec. Get the full VMS spec and convert it.
4080 */
4081 char *cur_dir = NULL;
4082 static char url_file[LY_MAXPATH], file_name[LY_MAXPATH], dir_name[LY_MAXPATH];
4083 unsigned long context = 0;
4084
4085 $DESCRIPTOR(url_file_dsc, url_file);
4086 $DESCRIPTOR(file_name_dsc, file_name);
4087 if (LYIsTilde(*old_string)) {
4088 /*
4089 * On VMS, we'll accept '~' on the command line as Home_Dir(), and
4090 * assume the rest of the path, if any, has SHELL syntax.
4091 */
4092 StrAllocCat(*AllocatedString, HTVMS_wwwName(Home_Dir()));
4093 if ((cp = strchr(old_string, '/')) != NULL) {
4094 /*
4095 * Append rest of path, if present, skipping "user" if "~user"
4096 * was entered, simplifying, and eliminating any residual
4097 * relative elements. - FM
4098 */
4099 StrAllocCopy(temp, cp);
4100 LYTrimRelFromAbsPath(temp);
4101 StrAllocCat(*AllocatedString, temp);
4102 FREE(temp);
4103 }
4104 goto have_VMS_URL;
4105 } else {
4106 fragment = trimPoundSelector(old_string);
4107 LYStrNCpy(url_file, old_string, sizeof(url_file) - 1);
4108 }
4109 url_file_dsc.dsc$w_length = (short) strlen(url_file);
4110 if (1 & lib$find_file(&url_file_dsc, &file_name_dsc, &context,
4111 0, 0, 0, 0)) {
4112 /*
4113 * We found the file. Convert to a URL pathspec.
4114 */
4115 if ((cp = strchr(file_name, ';')) != NULL) {
4116 *cp = '\0';
4117 }
4118 LYLowerCase(file_name);
4119 StrAllocCat(*AllocatedString, HTVMS_wwwName(file_name));
4120 if ((cp = strchr(old_string, ';')) != NULL) {
4121 StrAllocCat(*AllocatedString, cp);
4122 }
4123 if (fragment != NULL) {
4124 restorePoundSelector(fragment);
4125 StrAllocCat(*AllocatedString, fragment);
4126 fragment = NULL;
4127 }
4128 } else if ((NULL != getcwd(dir_name, sizeof(dir_name) - 1, 0)) &&
4129 0 == chdir(old_string)) {
4130 /*
4131 * Probably a directory. Try converting that.
4132 */
4133 StrAllocCopy(cur_dir, dir_name);
4134 restorePoundSelector(fragment);
4135 if (NULL != getcwd(dir_name, sizeof(dir_name) - 1, 0)) {
4136 /*
4137 * Yup, we got it!
4138 */
4139 LYLowerCase(dir_name);
4140 StrAllocCat(*AllocatedString, dir_name);
4141 if (fragment != NULL) {
4142 StrAllocCat(*AllocatedString, fragment);
4143 fragment = NULL;
4144 }
4145 } else {
4146 /*
4147 * Nope. Assume it's an http URL with the "http://" defaulted,
4148 * if we can't rule out a bad VMS path.
4149 */
4150 fragment = NULL;
4151 if (strchr(old_string, '[') ||
4152 ((cp = strchr(old_string, ':')) != NULL &&
4153 !isdigit(UCH(cp[1]))) ||
4154 !LYExpandHostForURL(&old_string,
4155 URLDomainPrefixes,
4156 URLDomainSuffixes)) {
4157 /*
4158 * Probably a bad VMS path (but can't be sure). Use
4159 * original pathspec for the error message that will
4160 * result.
4161 */
4162 sprintf(url_file, "/%.*s", sizeof(url_file) - 2, old_string);
4163 CTRACE((tfp,
4164 "Can't find '%s' Will assume it's a bad path.\n",
4165 old_string));
4166 StrAllocCat(*AllocatedString, url_file);
4167 } else {
4168 /*
4169 * Assume a URL is wanted, so guess the scheme with
4170 * "http://" as the default. - FM
4171 */
4172 if (!LYAddSchemeForURL(&old_string, "http://")) {
4173 StrAllocCopy(*AllocatedString, "http://");
4174 StrAllocCat(*AllocatedString, old_string);
4175 } else {
4176 StrAllocCopy(*AllocatedString, old_string);
4177 }
4178 }
4179 }
4180 } else {
4181 /*
4182 * Nothing found. Assume it's an http URL with the "http://"
4183 * defaulted, if we can't rule out a bad VMS path.
4184 */
4185 restorePoundSelector(fragment);
4186 fragment = NULL;
4187
4188 if (strchr(old_string, '[') ||
4189 ((cp = strchr(old_string, ':')) != NULL &&
4190 !isdigit(UCH(cp[1]))) ||
4191 !LYExpandHostForURL(&old_string,
4192 URLDomainPrefixes,
4193 URLDomainSuffixes)) {
4194 /*
4195 * Probably a bad VMS path (but can't be sure). Use original
4196 * pathspec for the error message that will result.
4197 */
4198 sprintf(url_file, "/%.*s", sizeof(url_file) - 2, old_string);
4199 CTRACE((tfp, "Can't find '%s' Will assume it's a bad path.\n",
4200 old_string));
4201 StrAllocCat(*AllocatedString, url_file);
4202 } else {
4203 /*
4204 * Assume a URL is wanted, so guess the scheme with "http://"
4205 * as the default. - FM
4206 */
4207 if (!LYAddSchemeForURL(&old_string, "http://")) {
4208 StrAllocCopy(*AllocatedString, "http://");
4209 StrAllocCat(*AllocatedString, old_string);
4210 } else {
4211 StrAllocCopy(*AllocatedString, old_string);
4212 }
4213 }
4214 }
4215 lib$find_file_end(&context);
4216 FREE(cur_dir);
4217 have_VMS_URL:
4218 CTRACE((tfp, "Trying: '%s'\n", *AllocatedString));
4219 #else /* not VMS: */
4220 #if defined(USE_DOS_DRIVES)
4221 #ifdef _WINDOWS
4222 if (*old_string == '.') {
4223 char fullpath[MAX_PATH + 1];
4224 char *filepart = NULL;
4225 DWORD chk;
4226
4227 chk = GetFullPathNameA(old_string, MAX_PATH + 1,
4228 fullpath, &filepart);
4229 if (chk != 0) {
4230 StrAllocCopy(temp, wwwName(fullpath));
4231 StrAllocCat(*AllocatedString, temp);
4232 FREE(temp);
4233 CTRACE((tfp, "Converted '%s' to '%s'\n",
4234 old_string, *AllocatedString));
4235 } else {
4236 StrAllocCat(*AllocatedString, old_string);
4237 }
4238 }
4239 #else
4240 if (strlen(old_string) == 1 && *old_string == '.') {
4241 /*
4242 * They want .
4243 */
4244 char curdir[LY_MAXPATH];
4245
4246 StrAllocCopy(temp, wwwName(Current_Dir(curdir)));
4247 StrAllocCat(*AllocatedString, temp);
4248 FREE(temp);
4249 CTRACE((tfp, "Converted '%s' to '%s'\n",
4250 old_string, *AllocatedString));
4251 }
4252 #endif
4253 else
4254 #endif /* USE_DOS_DRIVES */
4255 if (LYIsTilde(*old_string)) {
4256 char *his_home = NULL;
4257
4258 StrAllocCopy(his_home, old_string);
4259 LYTildeExpand(&his_home, FALSE);
4260 StrAllocCat(*AllocatedString, his_home);
4261 FREE(his_home);
4262
4263 CTRACE((tfp, "Converted '%s' to '%s'\n",
4264 old_string, *AllocatedString));
4265 } else {
4266 /*
4267 * Create a full path to the current default directory.
4268 */
4269 char curdir[LY_MAXPATH];
4270 char *temp2 = NULL;
4271 BOOL is_local = FALSE;
4272
4273 Current_Dir(curdir);
4274 /*
4275 * Concatenate and simplify, trimming any residual relative
4276 * elements. - FM
4277 */
4278 #if defined (USE_DOS_DRIVES)
4279 if (old_string[1] != ':' && old_string[1] != '|') {
4280 StrAllocCopy(temp, wwwName(curdir));
4281 LYAddHtmlSep(&temp);
4282 LYStrNCpy(curdir, temp, (sizeof(curdir) - 1));
4283 StrAllocCat(temp, old_string);
4284 } else {
4285 curdir[0] = '\0';
4286 /* 1998/01/13 (Tue) 12:24:33 */
4287 if (old_string[1] == '|')
4288 old_string[1] = ':';
4289 StrAllocCopy(temp, old_string);
4290
4291 if (strlen(temp) == 2 && LYIsDosDrive(temp))
4292 LYAddPathSep(&temp);
4293 }
4294 #else
4295 StrAllocCopy(temp, curdir);
4296 StrAllocCat(temp, "/");
4297 StrAllocCat(temp, old_string);
4298 #endif /* USE_DOS_DRIVES */
4299 LYTrimRelFromAbsPath(temp);
4300 CTRACE((tfp, "Converted '%s' to '%s'\n", old_string, temp));
4301 if ((stat(temp, &st) > -1) ||
4302 LYCanReadFile(temp)) {
4303 /*
4304 * It is a subdirectory or file on the local system.
4305 */
4306 #if defined (USE_DOS_DRIVES)
4307 /* Don't want to see DOS local paths like c: escaped */
4308 /* especially when we really have file://localhost/ */
4309 /* at the beginning. To avoid any confusion we allow */
4310 /* escaping the path if URL specials % or # present. */
4311 if (strchr(temp, '#') == NULL && strchr(temp, '%') == NULL)
4312 StrAllocCopy(cp, temp);
4313 else
4314 cp = HTEscape(temp, URL_PATH);
4315 #else
4316 cp = HTEscape(temp, URL_PATH);
4317 #endif /* USE_DOS_DRIVES */
4318 StrAllocCat(*AllocatedString, cp);
4319 FREE(cp);
4320 CTRACE((tfp, "Converted '%s' to '%s'\n",
4321 old_string, *AllocatedString));
4322 is_local = TRUE;
4323 } else {
4324 char *cp2 = NULL;
4325
4326 StrAllocCopy(temp2, curdir);
4327 LYAddPathSep(&temp2);
4328 StrAllocCopy(cp, old_string);
4329 fragment = trimPoundSelector(cp);
4330 HTUnEscape(cp); /* unescape given path without fragment */
4331 StrAllocCat(temp2, cp); /* append to current dir */
4332 StrAllocCopy(cp2, temp2); /* keep a copy in cp2 */
4333 LYTrimRelFromAbsPath(temp2);
4334 #ifdef WIN_EX /* 1998/07/31 (Fri) 09:09:03 */
4335 HTUnEscape(temp2); /* for LFN */
4336 #endif
4337
4338 if (strcmp(temp2, temp) != 0 &&
4339 ((stat(temp2, &st) > -1) ||
4340 LYCanReadFile(temp2))) {
4341 /*
4342 * It is a subdirectory or file on the local system with
4343 * escaped characters and/or a fragment to be appended to
4344 * the URL. - FM
4345 */
4346
4347 FREE(temp);
4348 if (strcmp(cp2, temp2) == 0) {
4349 /*
4350 * LYTrimRelFromAbsPath did nothing, use old_string as
4351 * given. - kw
4352 */
4353 temp = HTEscape(curdir, URL_PATH);
4354 LYAddHtmlSep(&temp);
4355 StrAllocCat(temp, old_string);
4356 } else {
4357 temp = HTEscape(temp2, URL_PATH);
4358 if (fragment != NULL) {
4359 restorePoundSelector(fragment);
4360 StrAllocCat(temp, fragment);
4361 }
4362 }
4363 StrAllocCat(*AllocatedString, temp);
4364 CTRACE((tfp, "Converted '%s' to '%s'\n",
4365 old_string, *AllocatedString));
4366 is_local = TRUE;
4367
4368 } else if (strchr(curdir, '#') != NULL ||
4369 strchr(curdir, '%') != NULL) {
4370 /*
4371 * If PWD has some unusual characters, construct a filename
4372 * in temp where those are escaped. This is mostly to
4373 * prevent this function from returning with some weird URL
4374 * if the LYExpandHostForURL tests further down fail. - kw
4375 */
4376 FREE(temp);
4377 if (strcmp(cp2, temp2) == 0) {
4378 /*
4379 * LYTrimRelFromAbsPath did nothing, use old_string as
4380 * given. - kw
4381 */
4382 temp = HTEscape(curdir, URL_PATH);
4383 LYAddHtmlSep(&temp);
4384 StrAllocCat(temp, old_string);
4385 } else {
4386 temp = HTEscape(temp2, URL_PATH);
4387 if (fragment != NULL) {
4388 restorePoundSelector(fragment);
4389 StrAllocCat(temp, fragment);
4390 }
4391 }
4392 }
4393 FREE(cp);
4394 FREE(cp2);
4395 }
4396 if (is_local == FALSE) {
4397 /*
4398 * It's not an accessible subdirectory or file on the local
4399 * system, so assume it's a URL request and guess the scheme
4400 * with "http://" as the default.
4401 */
4402 CTRACE((tfp, "Can't stat() or fopen() '%s'\n",
4403 temp2 ? temp2 : temp));
4404 #ifdef WIN_EX /* 1998/01/13 (Tue) 09:07:37 */
4405 {
4406 const char *p, *q;
4407 char buff[LY_MAXPATH + 128];
4408
4409 p = Home_Dir();
4410 q = temp2 ? temp2 : temp;
4411
4412 if (strlen(q) == 3 && LYIsDosDrive(q)) {
4413 sprintf(buff,
4414 "'%s' not exist, Goto LynxHome '%s'.", q, p);
4415 _statusline(buff);
4416 LYSleepAlert();
4417 FREE(temp);
4418 StrAllocCat(*AllocatedString, p);
4419 goto Retry;
4420 }
4421 }
4422 #endif
4423 if (LYExpandHostForURL(&old_string,
4424 URLDomainPrefixes,
4425 URLDomainSuffixes)) {
4426 if (!LYAddSchemeForURL(&old_string, "http://")) {
4427 StrAllocCopy(*AllocatedString, "http://");
4428 StrAllocCat(*AllocatedString, old_string);
4429 } else {
4430 StrAllocCopy(*AllocatedString, old_string);
4431 }
4432 } else if (fixit) {
4433 /* RW 1998Mar16 Restore AllocatedString to 'old_string' */
4434 StrAllocCopy(*AllocatedString, old_string);
4435 } else {
4436 /* Return file URL for the file that does not exist */
4437 StrAllocCat(*AllocatedString, temp);
4438 }
4439 #ifdef WIN_EX
4440 Retry:
4441 #endif
4442 CTRACE((tfp, "Trying: '%s'\n", *AllocatedString));
4443 }
4444 FREE(temp);
4445 FREE(temp2);
4446 }
4447 #endif /* VMS */
4448 } else {
4449 /*
4450 * Path begins with a slash. Simplify and use it.
4451 */
4452 if (old_string[1] == '\0') {
4453 /*
4454 * Request for root. Respect it on Unix, but on VMS we treat that
4455 * as a listing of the login directory. - FM
4456 */
4457 #ifdef VMS
4458 StrAllocCat(*AllocatedString, HTVMS_wwwName(Home_Dir()));
4459 #else
4460 StrAllocCat(*AllocatedString, "/");
4461 } else if ((stat(old_string, &st) > -1) ||
4462 LYCanReadFile(old_string)) {
4463 /*
4464 * It is an absolute directory or file on the local system. - KW
4465 */
4466 StrAllocCopy(temp, old_string);
4467 LYTrimRelFromAbsPath(temp);
4468 CTRACE((tfp, "Converted '%s' to '%s'\n", old_string, temp));
4469 cp = HTEscape(temp, URL_PATH);
4470 StrAllocCat(*AllocatedString, cp);
4471 FREE(cp);
4472 FREE(temp);
4473 CTRACE((tfp, "Converted '%s' to '%s'\n",
4474 old_string, *AllocatedString));
4475 #endif /* VMS */
4476 } else if (LYIsTilde(old_string[1])) {
4477 /*
4478 * Has a Home_Dir() reference. Handle it as if there weren't a
4479 * lead slash. - FM
4480 */
4481 StrAllocCat(*AllocatedString, wwwName(Home_Dir()));
4482 if ((cp = strchr((old_string + 1), '/')) != NULL) {
4483 /*
4484 * Append rest of path, if present, skipping "user" if "~user"
4485 * was entered, simplifying, and eliminating any residual
4486 * relative elements. - FM
4487 */
4488 StrAllocCopy(temp, cp);
4489 LYTrimRelFromAbsPath(temp);
4490 StrAllocCat(*AllocatedString, temp);
4491 FREE(temp);
4492 }
4493 } else {
4494 /*
4495 * Normal absolute path. Simplify, trim any residual relative
4496 * elements, and append it. - FM
4497 */
4498 StrAllocCopy(temp, old_string);
4499 LYTrimRelFromAbsPath(temp);
4500 StrAllocCat(*AllocatedString, temp);
4501 FREE(temp);
4502 }
4503 CTRACE((tfp, "Converted '%s' to '%s'\n",
4504 old_string, *AllocatedString));
4505 }
4506 FREE(old_string);
4507 /* Pause so we can read the messages before invoking curses */
4508 CTRACE_SLEEP(AlertSecs);
4509 }
4510
4511 #if defined(_WINDOWS) /* 1998/06/23 (Tue) 16:45:20 */
4512
win32_check_interrupt(void)4513 int win32_check_interrupt(void)
4514 {
4515 int c;
4516
4517 if (kbhit()) {
4518 c = LYgetch();
4519 /** Keyboard 'Z' or 'z', or Control-G or Control-C **/
4520 if (LYCharIsINTERRUPT(c) || c == 0x1b) {
4521 return TRUE;
4522 }
4523 }
4524 return FALSE;
4525 }
4526
4527 #if !defined(__MINGW32__)
sleep(unsigned sec)4528 void sleep(unsigned sec)
4529 {
4530 unsigned int i, j;
4531
4532 for (j = 0; j < sec; j++) {
4533 for (i = 0; i < 10; i++) {
4534 Sleep(100);
4535 if (kbhit()) {
4536 (void) LYgetch();
4537 return;
4538 }
4539 }
4540 }
4541 }
4542 #endif /* !__MINGW32__ */
4543 #endif /* _WINDOWS */
4544
4545 /*
4546 * This function rewrites and reallocates a previously allocated string so that
4547 * the first element is a confirmed Internet host, and returns TRUE, otherwise
4548 * it does not modify the string and returns FALSE. It first tries the element
4549 * as is, then, if the element does not end with a dot, it adds prefixes from
4550 * the (comma separated) prefix list argument, and, if the element does not
4551 * begin with a dot, suffixes from the (comma separated) suffix list arguments
4552 * (e.g., www.host.com, then www.host,edu, then www.host.net, then
4553 * www.host.org). The remaining path, if one is present, will be appended to
4554 * the expanded host. It also takes into account whether a colon is in the
4555 * element or suffix, and includes that and what follows as a port field for
4556 * the expanded host field (e.g, wfbr:8002/dir/lynx should yield
4557 * www.wfbr.edu:8002/dir/lynx). The calling function should prepend the scheme
4558 * field (e.g., http://), or pass the string to LYAddSchemeForURL(), if this
4559 * function returns TRUE. - FM
4560 */
LYExpandHostForURL(char ** AllocatedString,char * prefix_list,char * suffix_list)4561 BOOLEAN LYExpandHostForURL(char **AllocatedString,
4562 char *prefix_list,
4563 char *suffix_list)
4564 {
4565 char *DomainPrefix = NULL;
4566 const char *StartP, *EndP;
4567 char *DomainSuffix = NULL;
4568 const char *StartS, *EndS;
4569 char *Str = NULL, *StrColon = NULL, *MsgStr = NULL;
4570 char *Host = NULL, *HostColon = NULL, *host = NULL;
4571 char *Path = NULL;
4572 char *Fragment = NULL;
4573 BOOLEAN GotHost = FALSE;
4574 BOOLEAN Startup = (BOOL) (helpfilepath == NULL);
4575
4576 #ifdef INET6
4577 struct addrinfo hints, *res;
4578 int error;
4579 char *begin;
4580 char *end = NULL;
4581 #endif /* INET6 */
4582
4583 /*
4584 * If it's a NULL or zero-length string, or if it begins with a slash or
4585 * hash, don't continue pointlessly. - FM
4586 */
4587 if (!(*AllocatedString) || *AllocatedString[0] == '\0' ||
4588 *AllocatedString[0] == '/' || *AllocatedString[0] == '#') {
4589 return GotHost;
4590 }
4591
4592 /*
4593 * If it's a partial or relative path, don't continue pointlessly. - FM
4594 */
4595 if (!StrNCmp(*AllocatedString, "..", 2) ||
4596 !StrNCmp(*AllocatedString, "./", 2)) {
4597 return GotHost;
4598 }
4599
4600 /*
4601 * Make a clean copy of the string, and trim off the path if one is
4602 * present, but save the information so we can restore the path after
4603 * filling in the Host[:port] field. - FM
4604 */
4605 StrAllocCopy(Str, *AllocatedString);
4606 if ((Path = strchr(Str, '/')) != NULL) {
4607 /*
4608 * Have a path. Any fragment should already be included in Path. - FM
4609 */
4610 *Path = '\0';
4611 } else {
4612 /*
4613 * No path, so check for a fragment and trim that, to be restored after
4614 * filling in the Host[:port] field. - FM
4615 */
4616 Fragment = trimPoundSelector(Str);
4617 }
4618
4619 /*
4620 * If the potential host string has a colon, assume it begins a port field,
4621 * and trim it off, but save the information so we can restore the port
4622 * field after filling in the host field. - FM
4623 */
4624 if ((StrColon = strrchr(Str, ':')) != NULL &&
4625 isdigit(UCH(StrColon[1])) && strchr(StrColon, ']') == NULL) {
4626 if (StrColon == Str) {
4627 goto cleanup;
4628 }
4629 *StrColon = '\0';
4630 }
4631
4632 /*
4633 * Do a DNS test on the potential host field as presently trimmed. - FM
4634 */
4635 StrAllocCopy(host, Str);
4636 HTUnEscape(host);
4637 if (LYCursesON) {
4638 StrAllocCopy(MsgStr, WWW_FIND_MESSAGE);
4639 StrAllocCat(MsgStr, host);
4640 StrAllocCat(MsgStr, FIRST_SEGMENT);
4641 HTProgress(MsgStr);
4642 } else if (Startup && !dump_output_immediately) {
4643 fprintf(stdout, "%s '%s'%s\r\n", WWW_FIND_MESSAGE, host, FIRST_SEGMENT);
4644 }
4645 #ifdef INET6
4646 begin = host;
4647 if (host[0] == '[' && ((end = strrchr(host, ']')))) {
4648 /*
4649 * cut '[' and ']' from the IPv6 address, e.g. [::1]
4650 */
4651 begin = host + 1;
4652 *end = '\0';
4653 }
4654 memset(&hints, 0, sizeof(hints));
4655 hints.ai_family = PF_UNSPEC;
4656 hints.ai_socktype = SOCK_STREAM;
4657 error = getaddrinfo(begin, "80", &hints, &res);
4658 if (end)
4659 *end = ']';
4660
4661 if (!error && res)
4662 #else
4663 if (LYGetHostByName(host) != NULL)
4664 #endif /* INET6 */
4665 {
4666 /*
4667 * Clear any residual interrupt. - FM
4668 */
4669 if (LYCursesON && HTCheckForInterrupt()) {
4670 CTRACE((tfp,
4671 "LYExpandHostForURL: Ignoring interrupt because '%s' resolved.\n",
4672 host));
4673 }
4674
4675 /*
4676 * Return success. - FM
4677 */
4678 GotHost = TRUE;
4679 goto cleanup;
4680 } else if (LYCursesON && (lynx_nsl_status == HT_INTERRUPTED)) {
4681 /*
4682 * Give the user chance to interrupt lookup cycles. - KW & FM
4683 */
4684 CTRACE((tfp,
4685 "LYExpandHostForURL: Interrupted while '%s' failed to resolve.\n",
4686 host));
4687
4688 /*
4689 * Return failure. - FM
4690 */
4691 goto cleanup;
4692 }
4693
4694 /*
4695 * Set the first prefix, making it a zero-length string if the list is NULL
4696 * or if the potential host field ends with a dot. - FM
4697 */
4698 StartP = ((prefix_list && Str[strlen(Str) - 1] != '.')
4699 ? prefix_list
4700 : "");
4701 /*
4702 * If we have a prefix, but the allocated string is one of the common host
4703 * prefixes, make our prefix a zero-length string. - FM
4704 */
4705 if (*StartP && *StartP != '.') {
4706 if (!strncasecomp(*AllocatedString, "www.", 4) ||
4707 !strncasecomp(*AllocatedString, "ftp.", 4) ||
4708 !strncasecomp(*AllocatedString, "gopher.", 7) ||
4709 !strncasecomp(*AllocatedString, "wais.", 5) ||
4710 !strncasecomp(*AllocatedString, "cso.", 4) ||
4711 !strncasecomp(*AllocatedString, "ns.", 3) ||
4712 !strncasecomp(*AllocatedString, "ph.", 3) ||
4713 !strncasecomp(*AllocatedString, "finger.", 7) ||
4714 !strncasecomp(*AllocatedString, "news.", 5) ||
4715 !strncasecomp(*AllocatedString, "nntp.", 5)) {
4716 StartP = "";
4717 }
4718 }
4719 while ((*StartP) && (WHITE(*StartP) || *StartP == ',')) {
4720 StartP++; /* Skip whitespace and separators */
4721 }
4722 EndP = StartP;
4723 while (*EndP && !WHITE(*EndP) && *EndP != ',') {
4724 EndP++; /* Find separator */
4725 }
4726 StrAllocCopy(DomainPrefix, StartP);
4727 DomainPrefix[EndP - StartP] = '\0';
4728
4729 /*
4730 * Test each prefix with each suffix. - FM
4731 */
4732 do {
4733 /*
4734 * Set the first suffix, making it a zero-length string if the list is
4735 * NULL or if the potential host field begins with a dot. - FM
4736 */
4737 StartS = ((suffix_list && *Str != '.')
4738 ? suffix_list
4739 : "");
4740 while ((*StartS) && (WHITE(*StartS) || *StartS == ',')) {
4741 StartS++; /* Skip whitespace and separators */
4742 }
4743 EndS = StartS;
4744 while (*EndS && !WHITE(*EndS) && *EndS != ',') {
4745 EndS++; /* Find separator */
4746 }
4747 StrAllocCopy(DomainSuffix, StartS);
4748 DomainSuffix[EndS - StartS] = '\0';
4749
4750 /*
4751 * Create domain names and do DNS tests. - FM
4752 */
4753 do {
4754 StrAllocCopy(Host, DomainPrefix);
4755 StrAllocCat(Host, ((*Str == '.') ? (Str + 1) : Str));
4756 if (Host[strlen(Host) - 1] == '.') {
4757 Host[strlen(Host) - 1] = '\0';
4758 }
4759 StrAllocCat(Host, DomainSuffix);
4760 if ((HostColon = strrchr(Host, ':')) != NULL &&
4761 isdigit(UCH(HostColon[1]))) {
4762 *HostColon = '\0';
4763 }
4764 StrAllocCopy(host, Host);
4765 HTUnEscape(host);
4766 if (LYCursesON) {
4767 StrAllocCopy(MsgStr, WWW_FIND_MESSAGE);
4768 StrAllocCat(MsgStr, host);
4769 StrAllocCat(MsgStr, GUESSING_SEGMENT);
4770 HTProgress(MsgStr);
4771 } else if (Startup && !dump_output_immediately) {
4772 fprintf(stdout, "%s '%s'%s\n", WWW_FIND_MESSAGE, host, GUESSING_SEGMENT);
4773 }
4774 GotHost = (BOOL) (LYGetHostByName(host) != NULL);
4775 if (HostColon != NULL) {
4776 *HostColon = ':';
4777 }
4778 if (GotHost == FALSE) {
4779 /*
4780 * Give the user chance to interrupt lookup cycles. - KW
4781 */
4782 if (LYCursesON && (lynx_nsl_status == HT_INTERRUPTED)) {
4783 CTRACE((tfp,
4784 "LYExpandHostForURL: Interrupted while '%s' failed to resolve.\n",
4785 host));
4786 goto cleanup; /* We didn't find a valid name. */
4787 }
4788
4789 /*
4790 * Advance to the next suffix, or end of suffix list. - FM
4791 */
4792 StartS = ((*EndS == '\0') ? EndS : (EndS + 1));
4793 while ((*StartS) && (WHITE(*StartS) || *StartS == ',')) {
4794 StartS++; /* Skip whitespace and separators */
4795 }
4796 EndS = StartS;
4797 while (*EndS && !WHITE(*EndS) && *EndS != ',') {
4798 EndS++; /* Find separator */
4799 }
4800 LYStrNCpy(DomainSuffix, StartS, (EndS - StartS));
4801 }
4802 } while ((GotHost == FALSE) && (*DomainSuffix != '\0'));
4803
4804 if (GotHost == FALSE) {
4805 /*
4806 * Advance to the next prefix, or end of prefix list. - FM
4807 */
4808 StartP = ((*EndP == '\0') ? EndP : (EndP + 1));
4809 while ((*StartP) && (WHITE(*StartP) || *StartP == ',')) {
4810 StartP++; /* Skip whitespace and separators */
4811 }
4812 EndP = StartP;
4813 while (*EndP && !WHITE(*EndP) && *EndP != ',') {
4814 EndP++; /* Find separator */
4815 }
4816 LYStrNCpy(DomainPrefix, StartP, (EndP - StartP));
4817 }
4818 } while ((GotHost == FALSE) && (*DomainPrefix != '\0'));
4819
4820 /*
4821 * If a test passed, restore the port field if we had one and there is no
4822 * colon in the expanded host, and the path if we had one, and reallocate
4823 * the original string with the expanded Host[:port] field included. - FM
4824 */
4825 if (GotHost) {
4826 if (StrColon && strchr(Host, ':') == NULL) {
4827 *StrColon = ':';
4828 StrAllocCat(Host, StrColon);
4829 }
4830 if (Path) {
4831 *Path = '/';
4832 StrAllocCat(Host, Path);
4833 } else if (Fragment) {
4834 StrAllocCat(Host, "/");
4835 restorePoundSelector(Fragment);
4836 StrAllocCat(Host, Fragment);
4837 }
4838 StrAllocCopy(*AllocatedString, Host);
4839 }
4840
4841 /*
4842 * Clear any residual interrupt. - FM
4843 */
4844 if (LYCursesON && HTCheckForInterrupt()) {
4845 CTRACE((tfp,
4846 "LYExpandHostForURL: Ignoring interrupt because '%s' %s.\n",
4847 host,
4848 (GotHost ? "resolved" : "timed out")));
4849 }
4850
4851 /*
4852 * Clean up and return the last test result. - FM
4853 */
4854 cleanup:
4855 FREE(DomainPrefix);
4856 FREE(DomainSuffix);
4857 FREE(Str);
4858 FREE(MsgStr);
4859 FREE(Host);
4860 FREE(host);
4861 return GotHost;
4862 }
4863
4864 /*
4865 * This function rewrites and reallocates a previously allocated string that
4866 * begins with an Internet host name so that the string begins with its guess
4867 * of the scheme based on the first field of the host name, or the default
4868 * scheme if no guess was made, and returns TRUE, otherwise it does not modify
4869 * the string and returns FALSE. It also returns FALSE without modifying the
4870 * string if the default_scheme argument was NULL or zero-length and no guess
4871 * was made. - FM
4872 */
LYAddSchemeForURL(char ** AllocatedString,const char * default_scheme)4873 BOOLEAN LYAddSchemeForURL(char **AllocatedString,
4874 const char *default_scheme)
4875 {
4876 char *Str = NULL;
4877 BOOLEAN GotScheme = FALSE;
4878
4879 /*
4880 * If we were passed a NULL or zero-length string, don't continue
4881 * pointlessly. - FM
4882 */
4883 if (!(*AllocatedString) || *AllocatedString[0] == '\0') {
4884 return GotScheme;
4885 }
4886
4887 /*
4888 * Try to guess the appropriate scheme. - FM
4889 */
4890 if (0 == strncasecomp(*AllocatedString, "www", 3)) {
4891 /*
4892 * This could be either http or https, so check the default and
4893 * otherwise use "http". - FM
4894 */
4895 if (default_scheme != NULL &&
4896 NULL != strstr(default_scheme, "http")) {
4897 StrAllocCopy(Str, default_scheme);
4898 } else {
4899 StrAllocCopy(Str, "http://");
4900 }
4901 GotScheme = TRUE;
4902
4903 } else if (0 == strncasecomp(*AllocatedString, "ftp", 3)) {
4904 StrAllocCopy(Str, "ftp://");
4905 GotScheme = TRUE;
4906
4907 } else if (0 == strncasecomp(*AllocatedString, "gopher", 6)) {
4908 StrAllocCopy(Str, "gopher://");
4909 GotScheme = TRUE;
4910
4911 } else if (0 == strncasecomp(*AllocatedString, "wais", 4)) {
4912 StrAllocCopy(Str, "wais://");
4913 GotScheme = TRUE;
4914
4915 } else if (0 == strncasecomp(*AllocatedString, "cso", 3) ||
4916 0 == strncasecomp(*AllocatedString, "ns.", 3) ||
4917 0 == strncasecomp(*AllocatedString, "ph.", 3)) {
4918 StrAllocCopy(Str, "cso://");
4919 GotScheme = TRUE;
4920
4921 } else if (0 == strncasecomp(*AllocatedString, "finger", 6)) {
4922 StrAllocCopy(Str, "finger://");
4923 GotScheme = TRUE;
4924
4925 } else if (0 == strncasecomp(*AllocatedString, "news", 4)) {
4926 /*
4927 * This could be either news, snews, or nntp, so check the default, and
4928 * otherwise use news. - FM
4929 */
4930 if ((default_scheme != NULL) &&
4931 (NULL != strstr(default_scheme, "news") ||
4932 NULL != strstr(default_scheme, "nntp"))) {
4933 StrAllocCopy(Str, default_scheme);
4934 } else {
4935 StrAllocCopy(Str, "news://");
4936 }
4937 GotScheme = TRUE;
4938
4939 } else if (0 == strncasecomp(*AllocatedString, "nntp", 4)) {
4940 StrAllocCopy(Str, "nntp://");
4941 GotScheme = TRUE;
4942
4943 }
4944
4945 /*
4946 * If we've make a guess, use it. Otherwise, if we were passed a default
4947 * scheme prefix, use that. - FM
4948 */
4949 if (GotScheme == TRUE) {
4950 StrAllocCat(Str, *AllocatedString);
4951 StrAllocCopy(*AllocatedString, Str);
4952 FREE(Str);
4953 return GotScheme;
4954
4955 } else if (non_empty(default_scheme)) {
4956 StrAllocCopy(Str, default_scheme);
4957 GotScheme = TRUE;
4958 StrAllocCat(Str, *AllocatedString);
4959 StrAllocCopy(*AllocatedString, Str);
4960 FREE(Str);
4961 return GotScheme;
4962 }
4963
4964 return GotScheme;
4965 }
4966
4967 /*
4968 * This function expects an absolute Unix or VMS SHELL path spec as an
4969 * allocated string, simplifies it, and trims out any residual relative
4970 * elements. It also checks whether the path had a terminal slash, and if it
4971 * didn't, makes sure that the simplified path doesn't either. If it's a
4972 * directory, our convention is to exclude "Up to parent" links when a terminal
4973 * slash is present. - FM
4974 */
LYTrimRelFromAbsPath(char * path)4975 void LYTrimRelFromAbsPath(char *path)
4976 {
4977 char *cp;
4978 int i;
4979 BOOL TerminalSlash;
4980
4981 /*
4982 * Make sure we have a pointer to an absolute path. - FM
4983 */
4984 if (path == NULL || !LYIsPathSep(*path))
4985 return;
4986
4987 /*
4988 * Check whether the path has a terminal slash. - FM
4989 */
4990 TerminalSlash = (BOOL) (LYIsPathSep(path[(strlen(path) - 1)]));
4991
4992 /*
4993 * Simplify the path and then do any necessary trimming. - FM
4994 */
4995 HTSimplify(path);
4996 cp = path;
4997 while (cp[1] == '.') {
4998 if (cp[2] == '\0') {
4999 /*
5000 * Eliminate trailing dot. - FM
5001 */
5002 cp[1] = '\0';
5003 } else if (LYIsPathSep(cp[2])) {
5004 /*
5005 * Skip over the "/." of a "/./". - FM
5006 */
5007 cp += 2;
5008 } else if (cp[2] == '.' && cp[3] == '\0') {
5009 /*
5010 * Eliminate trailing dotdot. - FM
5011 */
5012 cp[1] = '\0';
5013 } else if (cp[2] == '.' && cp[3] == '/') {
5014 /*
5015 * Skip over the "/.." of a "/../". - FM
5016 */
5017 cp += 3;
5018 } else {
5019 /*
5020 * Done trimming. - FM
5021 */
5022 break;
5023 }
5024 }
5025
5026 /*
5027 * Load any shifts into path, and eliminate any terminal slash created by
5028 * HTSimplify() or our walk, but not present originally. - FM
5029 */
5030 if (cp > path) {
5031 for (i = 0; cp[i] != '\0'; i++)
5032 path[i] = cp[i];
5033 path[i] = '\0';
5034 }
5035 if (TerminalSlash == FALSE) {
5036 LYTrimPathSep(path);
5037 }
5038 }
5039
5040 /*
5041 * Example Client-Side Include interface.
5042 *
5043 * This is called from SGML.c and simply returns markup for reporting the URL
5044 * of the document being loaded if a comment begins with "<!--#lynxCSI". The
5045 * markup will be included as if it were in the document. Move this function
5046 * to a separate module for doing this kind of thing seriously, someday. - FM
5047 */
LYDoCSI(char * url,const char * comment,char ** csi)5048 void LYDoCSI(char *url,
5049 const char *comment,
5050 char **csi)
5051 {
5052 const char *cp = comment;
5053
5054 if (cp == NULL)
5055 return;
5056
5057 if (StrNCmp(cp, "!--#", 4))
5058 return;
5059
5060 cp += 4;
5061 if (!strncasecomp(cp, "lynxCSI", 7)) {
5062 StrAllocCat(*csi, "\n<p align=\"center\">URL: ");
5063 StrAllocCat(*csi, url);
5064 StrAllocCat(*csi, "</p>\n\n");
5065 }
5066
5067 return;
5068 }
5069
5070 #ifdef VMS
5071 /*
5072 * Define_VMSLogical -- Fote Macrides 04-Apr-1995
5073 * Define VMS logicals in the process table.
5074 */
Define_VMSLogical(char * LogicalName,char * LogicalValue)5075 void Define_VMSLogical(char *LogicalName,
5076 char *LogicalValue)
5077 {
5078 $DESCRIPTOR(lname, "");
5079 $DESCRIPTOR(lvalue, "");
5080 $DESCRIPTOR(ltable, "LNM$PROCESS");
5081
5082 if (isEmpty(LogicalName))
5083 return;
5084
5085 lname.dsc$w_length = strlen(LogicalName);
5086 lname.dsc$a_pointer = LogicalName;
5087
5088 if (isEmpty(LogicalValue)) {
5089 lib$delete_logical(&lname, <able);
5090 return;
5091 }
5092
5093 lvalue.dsc$w_length = strlen(LogicalValue);
5094 lvalue.dsc$a_pointer = LogicalValue;
5095 lib$set_logical(&lname, &lvalue, <able, 0, 0);
5096 return;
5097 }
5098 #endif /* VMS */
5099
5100 #ifdef LY_FIND_LEAKS
LYHomeDir_free(void)5101 static void LYHomeDir_free(void)
5102 {
5103 FREE(HomeDir);
5104 }
5105 #endif /* LY_FIND_LEAKS */
5106
Current_Dir(char * pathname)5107 char *Current_Dir(char *pathname)
5108 {
5109 char *result;
5110
5111 #ifdef HAVE_GETCWD
5112 result = getcwd(pathname, (size_t) LY_MAXPATH);
5113 #else
5114 result = getwd(pathname);
5115 #endif /* NO_GETCWD */
5116 if (result == 0)
5117 strcpy(pathname, ".");
5118 return pathname;
5119 }
5120
5121 /*
5122 * Verify that the given path refers to an existing directory, returning the
5123 * string if the directory exists. If not, return null.
5124 */
CheckDir(char * path)5125 static char *CheckDir(char *path)
5126 {
5127 struct stat stat_info;
5128
5129 if (!LYisAbsPath(path)
5130 || (HTStat(path, &stat_info) < 0
5131 || !S_ISDIR(stat_info.st_mode))) {
5132 path = NULL;
5133 }
5134 return path;
5135 }
5136
5137 /*
5138 * Lookup various possibilities for $HOME, and check that the directory exists.
5139 */
HomeEnv(void)5140 static char *HomeEnv(void)
5141 {
5142 char *result = CheckDir(LYGetEnv("HOME"));
5143
5144 #if defined (USE_DOS_DRIVES)
5145 if (result == 0) {
5146 char *head;
5147 char *leaf;
5148 static char *temp = NULL;
5149
5150 /* Windows 2000 */
5151 if ((result = LYGetEnv("USERPROFILE")) != 0) {
5152 HTSprintf0(&temp, "%s%sMy Documents", result, PATHSEP_STR);
5153 result = CheckDir(temp);
5154 }
5155 /* NT4 */
5156 if (result == 0) {
5157 if ((head = LYGetEnv("HOMEDRIVE")) != 0) {
5158 if ((leaf = LYGetEnv("HOMEPATH")) != 0) {
5159 HTSprintf0(&temp, "%s%s%s", head, PATHSEP_STR, leaf);
5160 result = CheckDir(temp);
5161 }
5162 }
5163 }
5164 /* General M$ */
5165 #ifdef USE_PROGRAM_DIR
5166 if (result == 0)
5167 result = CheckDir(program_dir);
5168 #endif
5169 if (result == 0)
5170 result = CheckDir(LYGetEnv("TEMP"));
5171 if (result == 0)
5172 result = CheckDir(LYGetEnv("TMP"));
5173 if (result == 0) {
5174 if ((head = LYGetEnv("SystemDrive")) != 0) {
5175 HTSprintf0(&temp, "%s%s", head, PATHSEP_STR);
5176 result = CheckDir(temp);
5177 }
5178 }
5179 if (result == 0)
5180 result = CheckDir("C:" PATHSEP_STR);
5181 }
5182 #endif
5183
5184 return result;
5185 }
5186
Home_Dir(void)5187 const char *Home_Dir(void)
5188 {
5189 static const char *homedir = NULL;
5190 char *cp = NULL;
5191
5192 if (homedir == NULL) {
5193 if ((cp = HomeEnv()) == NULL) {
5194 #ifdef VMS
5195 if ((cp = LYGetEnv("SYS$LOGIN")) == NULL
5196 && (cp = LYGetEnv("SYS$SCRATCH")) == NULL) {
5197 cp = "sys$scratch:";
5198 }
5199 StrAllocCopy(HomeDir, cp);
5200 #else
5201 #ifdef UNIX
5202 #ifdef HAVE_UTMP
5203 /*
5204 * One could use getlogin() and getpwnam() here instead.
5205 */
5206 struct passwd *pw = getpwuid(geteuid());
5207
5208 if (pw && pw->pw_dir) {
5209 StrAllocCopy(HomeDir, pw->pw_dir);
5210 } else
5211 #endif
5212 {
5213 /*
5214 * Use /tmp; it should be writable.
5215 */
5216 StrAllocCopy(HomeDir, "/tmp");
5217 }
5218 #endif
5219 #endif /* VMS */
5220 } else {
5221 StrAllocCopy(HomeDir, cp);
5222 }
5223 homedir = (const char *) HomeDir;
5224 #ifdef LY_FIND_LEAKS
5225 atexit(LYHomeDir_free);
5226 #endif
5227 }
5228 if (homedir == NULL) {
5229 printf("%s\n", gettext("Cannot find HOME directory"));
5230 exit_immediately(EXIT_FAILURE);
5231 }
5232 return homedir;
5233 }
5234
5235 /*
5236 * Return a pointer to the final leaf of the given pathname, If no pathname
5237 * separators are found, returns the original pathname. The leaf may be
5238 * empty.
5239 */
LYPathLeaf(char * pathname)5240 char *LYPathLeaf(char *pathname)
5241 {
5242 char *leaf;
5243
5244 #ifdef UNIX
5245 if ((leaf = strrchr(pathname, '/')) != 0) {
5246 leaf++;
5247 }
5248 #else
5249 #ifdef VMS
5250 if ((leaf = strrchr(pathname, ']')) == 0)
5251 leaf = strrchr(pathname, ':');
5252 if (leaf != 0)
5253 leaf++;
5254 #else
5255 int n;
5256
5257 for (leaf = 0, n = strlen(pathname) - 1; n >= 0; n--) {
5258 if (strchr("\\/:", pathname[n]) != 0) {
5259 leaf = pathname + n + 1;
5260 break;
5261 }
5262 }
5263 #endif
5264 #endif
5265 return (leaf != 0) ? leaf : pathname;
5266 }
5267
5268 /*
5269 * This function checks the acceptability of file paths that are intended to be
5270 * off the home directory. The file path should be passed in fbuffer, together
5271 * with the size of the buffer. The function simplifies the file path, and if
5272 * it is acceptable, loads it into fbuffer and returns TRUE. Otherwise, it
5273 * does not modify fbuffer and returns FALSE. If a subdirectory is present and
5274 * the path does not begin with "./", that is prefixed to make the situation
5275 * clear. - FM
5276 */
LYPathOffHomeOK(char * fbuffer,size_t fbuffer_size)5277 BOOLEAN LYPathOffHomeOK(char *fbuffer,
5278 size_t fbuffer_size)
5279 {
5280 char *file = NULL;
5281 char *cp, *cp1;
5282
5283 /*
5284 * Make sure we have an fbuffer and a string in it. - FM
5285 */
5286 if (fbuffer_size < 2 || isEmpty(fbuffer)) {
5287 return (FALSE);
5288 }
5289 StrAllocCopy(file, fbuffer);
5290 cp = file;
5291
5292 /*
5293 * Check for an inappropriate reference to the home directory, and correct
5294 * it if we can. - FM
5295 */
5296 #ifdef VMS
5297 if (!strncasecomp(cp, "sys$login", 9)) {
5298 if (*(cp + 9) == '\0') {
5299 /*
5300 * Reject "sys$login". - FM
5301 */
5302 FREE(file);
5303 return (FALSE);
5304 }
5305 if (*(cp + 9) == ':') {
5306 cp += 10;
5307 if (*cp == '\0') {
5308 /*
5309 * Reject "sys$login:". Otherwise, we have converted
5310 * "sys$login:file" to "file", or have left a strange path for
5311 * VMS as it was originally. - FM
5312 */
5313 FREE(file);
5314 return (FALSE);
5315 }
5316 }
5317 }
5318 #endif /* VMS */
5319 if (LYIsTilde(cp[0])) {
5320 if (LYIsPathSep(cp[1])) {
5321 if (cp[2] != '\0') {
5322 if (strchr((cp + 2), '/') != NULL) {
5323 /*
5324 * Convert "~/subdir(s)/file" to "./subdir(s)/file". - FM
5325 */
5326 *cp = '.';
5327 } else {
5328 /*
5329 * Convert "~/file" to "file". - FM
5330 */
5331 cp += 2;
5332 }
5333 } else {
5334 /*
5335 * Reject "~/". - FM
5336 */
5337 FREE(file);
5338 return (FALSE);
5339 }
5340 } else if ((*(cp + 1) != '\0') &&
5341 (cp1 = strchr((cp + 1), '/')) != NULL) {
5342 cp = (cp1 - 1);
5343 if (*(cp + 2) != '\0') {
5344 if (strchr((cp + 2), '/') != NULL) {
5345 /*
5346 * Convert "~user/subdir(s)/file" to "./subdir(s)/file".
5347 * If user is someone else, we covered a spoof. Otherwise,
5348 * we simplified. - FM
5349 */
5350 *cp = '.';
5351 } else {
5352 /*
5353 * Convert "~user/file" to "file". - FM
5354 */
5355 cp += 2;
5356 }
5357 } else {
5358 /*
5359 * Reject "~user/". - FM
5360 */
5361 FREE(file);
5362 return (FALSE);
5363 }
5364 } else {
5365 /*
5366 * Reject "~user". - FM
5367 */
5368 FREE(file);
5369 return (FALSE);
5370 }
5371 }
5372 #ifdef VMS
5373 /*
5374 * Check for VMS path specs, and reject if still present. - FM
5375 */
5376 if (strchr(cp, ':') != NULL || strchr(cp, ']') != NULL) {
5377 FREE(file);
5378 return (FALSE);
5379 }
5380 #endif /* VMS */
5381
5382 /*
5383 * Check for a URL or absolute path, and reject if present. - FM
5384 */
5385 if (is_url(cp) || LYIsPathSep(*cp)) {
5386 FREE(file);
5387 return (FALSE);
5388 }
5389
5390 /*
5391 * Simplify it. - FM
5392 */
5393 HTSimplify(cp);
5394
5395 /*
5396 * Check if it has a pointless "./". - FM
5397 */
5398 if (!StrNCmp(cp, "./", 2)) {
5399 if (strchr((cp + 2), '/') == NULL) {
5400 cp += 2;
5401 }
5402 }
5403
5404 /*
5405 * Check for spoofing. - FM
5406 */
5407 if (*cp == '\0'
5408 || LYIsPathSep(*cp)
5409 || LYIsPathSep(cp[(strlen(cp) - 1)])
5410 || strstr(cp, "..") != NULL
5411 || !strcmp(cp, ".")) {
5412 FREE(file);
5413 return (FALSE);
5414 }
5415
5416 /*
5417 * Load what we have at this point into fbuffer, trimming if too long, and
5418 * claim it's OK. - FM
5419 */
5420 if (fbuffer_size > 3 && StrNCmp(cp, "./", 2) && strchr(cp, '/')) {
5421 /*
5422 * We have a subdirectory and no lead "./", so prefix it to make the
5423 * situation clear. - FM
5424 */
5425 strcpy(fbuffer, "./");
5426 if (strlen(cp) > (fbuffer_size - 3))
5427 cp[(fbuffer_size - 3)] = '\0';
5428 strcat(fbuffer, cp);
5429 } else {
5430 if (strlen(cp) > (fbuffer_size - 1))
5431 cp[(fbuffer_size - 1)] = '\0';
5432 strcpy(fbuffer, cp);
5433 }
5434 FREE(file);
5435 return (TRUE);
5436 }
5437
5438 /*
5439 * Search for a leading tilde, optionally embedded. If found, return a pointer
5440 * to the tilde. If not found, return the original parameter.
5441 */
FindLeadingTilde(char * pathname,int embedded)5442 static char *FindLeadingTilde(char *pathname, int embedded)
5443 {
5444 char *result = pathname;
5445
5446 if (pathname != NULL) {
5447 if (embedded) {
5448 while (pathname[0] != '\0') {
5449 if (LYIsPathSep(pathname[0])) {
5450 if (LYIsTilde(pathname[1])) {
5451 ++pathname;
5452 break;
5453 }
5454 }
5455 ++pathname;
5456 }
5457 }
5458 if (LYIsTilde(*pathname))
5459 result = pathname;
5460 }
5461 return result;
5462 }
5463
5464 /*
5465 * Convert a non-absolute path to one which is off the home directory. Expand
5466 * tildes as a side-effect. Return a pointer to the converted result.
5467 */
LYAbsOrHomePath(char ** fname)5468 char *LYAbsOrHomePath(char **fname)
5469 {
5470 if (*fname && !LYisAbsPath(*fname)) {
5471 if (LYIsTilde((*fname)[0])) {
5472 LYTildeExpand(fname, FALSE);
5473 } else {
5474 char temp[LY_MAXPATH];
5475
5476 LYAddPathToHome(temp, sizeof(temp), *fname);
5477 StrAllocCopy(*fname, temp);
5478 }
5479 }
5480 return *fname;
5481 }
5482
5483 /*
5484 * Expand a "leading" tilde into the user's home directory in WWW format. If
5485 * "embedded" is true, allow that "leading" tilde to follow a path separator.
5486 */
LYTildeExpand(char ** pathname,int embedded)5487 char *LYTildeExpand(char **pathname,
5488 int embedded)
5489 {
5490 char *temp = FindLeadingTilde(*pathname, embedded);
5491
5492 if (LYIsTilde(temp[0])) {
5493
5494 CTRACE((tfp, "LYTildeExpand %s\n", *pathname));
5495 if (LYIsPathSep(temp[1])) {
5496 char *first = NULL;
5497 char *second = NULL;
5498
5499 StrAllocCopy(first, *pathname);
5500 first[temp - *pathname] = '\0';
5501
5502 StrAllocCopy(second, temp + 2);
5503
5504 StrAllocCopy(*pathname, first);
5505 StrAllocCat(*pathname, wwwName(Home_Dir()));
5506 LYAddPathSep(pathname);
5507 StrAllocCat(*pathname, second);
5508
5509 FREE(first);
5510 FREE(second);
5511 } else if (temp[1] == '\0') {
5512 StrAllocCopy(*pathname, wwwName(Home_Dir()));
5513 #ifndef NOUSERS
5514 } else {
5515 char *save;
5516 char saved = '\0';
5517 struct passwd *pw;
5518
5519 for (save = temp; *save != '\0'; ++save) {
5520 if (LYIsPathSep(*save)) {
5521 saved = *save;
5522 *save = '\0';
5523 break;
5524 }
5525 }
5526 pw = getpwnam(temp + 1);
5527 *save = saved;
5528 if (pw != 0 && non_empty(pw->pw_dir)) {
5529 temp = NULL;
5530 StrAllocCopy(temp, save);
5531 StrAllocCopy(*pathname, pw->pw_dir);
5532 StrAllocCat(*pathname, temp);
5533 FREE(temp);
5534 }
5535 #endif
5536 }
5537 CTRACE((tfp, "expanded path %s\n", *pathname));
5538 }
5539 return *pathname;
5540 }
5541
5542 /*
5543 * This function appends fname to the home path and returns the full path and
5544 * filename. The fname string can be just a filename (e.g.,
5545 * "lynx_bookmarks.html"), or include a subdirectory off the home directory, in
5546 * which case fname should begin with "./" (e.g., ./BM/lynx_bookmarks.html) Use
5547 * LYPathOffHomeOK() to check and/or fix up fname before calling this function.
5548 * On VMS, the resultant full path and filename are converted to VMS syntax. -
5549 * FM
5550 */
LYAddPathToHome(char * fbuffer,size_t fbuffer_size,const char * fname)5551 void LYAddPathToHome(char *fbuffer,
5552 size_t fbuffer_size,
5553 const char *fname)
5554 {
5555 char *home = NULL;
5556 const char *file = fname;
5557 int len;
5558
5559 /*
5560 * Make sure we have a buffer. - FM
5561 */
5562 if (!fbuffer)
5563 return;
5564 if (fbuffer_size < 2) {
5565 fbuffer[0] = '\0';
5566 return;
5567 }
5568 fbuffer[(fbuffer_size - 1)] = '\0';
5569
5570 /*
5571 * Make sure we have a file name. - FM
5572 */
5573 if (!file)
5574 file = "";
5575
5576 /*
5577 * Set up home string and length. - FM
5578 */
5579 StrAllocCopy(home, Home_Dir());
5580
5581 #ifdef VMS
5582 #define NO_HOMEPATH "Error:"
5583 #else
5584 #define NO_HOMEPATH "/error"
5585 #endif /* VMS */
5586 if (!non_empty(home))
5587 /*
5588 * Home_Dir() has a bug if this ever happens. - FM
5589 */
5590 StrAllocCopy(home, NO_HOMEPATH);
5591
5592 len = (int) fbuffer_size - ((int) strlen(home) + 1);
5593 if (len <= 0) {
5594 /*
5595 * Buffer is smaller than or only big enough for the home path. Load
5596 * what fits of the home path and return. This will fail, but we need
5597 * something in the buffer. - FM
5598 */
5599 LYStrNCpy(fbuffer, home, (fbuffer_size - 1));
5600 FREE(home);
5601 return;
5602 }
5603 #ifdef VMS
5604 /*
5605 * Check whether we have a subdirectory path or just a filename. - FM
5606 */
5607 if (!StrNCmp(file, "./", 2)) {
5608 /*
5609 * We have a subdirectory path. - FM
5610 */
5611 if (home[strlen(home) - 1] == ']') {
5612 /*
5613 * We got the home directory, so convert it to SHELL syntax and
5614 * append subdirectory path, then convert that to VMS syntax. - FM
5615 */
5616 char *temp = NULL;
5617
5618 HTSprintf0(&temp, "%s%s", HTVMS_wwwName(home), (file + 1));
5619 sprintf(fbuffer, "%.*s",
5620 (fbuffer_size - 1), HTVMS_name("", temp));
5621 FREE(temp);
5622 } else {
5623 /*
5624 * This will fail, but we need something in the buffer. - FM
5625 */
5626 sprintf(fbuffer, "%s%.*s", home, len, file);
5627 }
5628 } else {
5629 /*
5630 * We have a file in the home directory. - FM
5631 */
5632 sprintf(fbuffer, "%s%.*s", home, len, file);
5633 }
5634 #else
5635 /*
5636 * Check whether we have a subdirectory path or just a filename. - FM
5637 */
5638 sprintf(fbuffer, "%s/%.*s", home, len,
5639 (StrNCmp(file, "./", 2) ? file : (file + 2)));
5640 #endif /* VMS */
5641 FREE(home);
5642 }
5643
5644 /*
5645 * Given a filename, concatenate it to the save-space pathname, unless it is
5646 * an absolute pathname. If there is no save-space defined, use the home
5647 * directory. Return a new string with the result.
5648 */
LYAddPathToSave(char * fname)5649 char *LYAddPathToSave(char *fname)
5650 {
5651 char *result = NULL;
5652
5653 if (LYisAbsPath(fname)) {
5654 StrAllocCopy(result, fname);
5655 } else {
5656 if (lynx_save_space != NULL) {
5657 StrAllocCopy(result, lynx_save_space);
5658 } else {
5659 char temp[LY_MAXPATH];
5660
5661 LYAddPathToHome(temp, sizeof(temp), fname);
5662 StrAllocCopy(result, temp);
5663 }
5664 }
5665 return result;
5666 }
5667
5668 #if !defined(HAVE_PUTENV) && !defined(_WINDOWS)
5669 /*
5670 * No putenv on the NeXT so we use this code instead!
5671 */
5672
5673 /* Copyright (C) 1991 Free Software Foundation, Inc.
5674 This file is part of the GNU C Library.
5675
5676 The GNU C Library is free software; you can redistribute it and/or
5677 modify it under the terms of the GNU Library General Public License as
5678 published by the Free Software Foundation; either version 2 of the
5679 License, or (at your option) any later version.
5680
5681 The GNU C Library is distributed in the hope that it will be useful,
5682 but WITHOUT ANY WARRANTY; without even the implied warranty of
5683 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5684 Library General Public License for more details.
5685
5686 You should have received a copy of the GNU Library General Public
5687 License along with the GNU C Library; see the file COPYING.LIB. If
5688 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
5689 Cambridge, MA 02139, USA. */
5690
5691 #if defined(STDC_HEADERS) || defined(USG)
5692 #include <string.h>
5693 #else /* Not (STDC_HEADERS or USG): */
5694 #include <strings.h>
5695 #endif /* STDC_HEADERS or USG */
5696
5697 #ifndef NULL
5698 #define NULL 0
5699 #endif /* !NULL */
5700
5701 extern char **environ;
5702
5703 /*
5704 * Put STRING, which is of the form "NAME=VALUE", in the environment.
5705 */
putenv(const char * string)5706 int putenv(const char *string)
5707 {
5708 char *name_end = strchr(string, '=');
5709 register size_t size;
5710 register char **ep;
5711
5712 if (name_end == NULL) {
5713 /* Remove the variable from the environment. */
5714 size = strlen(string);
5715 for (ep = environ; *ep != NULL; ++ep)
5716 if (!StrNCmp(*ep, string, size) && (*ep)[size] == '=') {
5717 while (ep[1] != NULL) {
5718 ep[0] = ep[1];
5719 ++ep;
5720 }
5721 *ep = NULL;
5722 return 0;
5723 }
5724 }
5725
5726 size = 0;
5727 for (ep = environ; *ep != NULL; ++ep)
5728 if (!StrNCmp(*ep, string, name_end - string) &&
5729 (*ep)[name_end - string] == '=')
5730 break;
5731 else
5732 ++size;
5733
5734 if (*ep == NULL) {
5735 static char **last_environ = NULL;
5736 char **new_environ = (char **) malloc((size + 2) * sizeof(char *));
5737
5738 if (new_environ == NULL)
5739 return -1;
5740 (void) memcpy((char *) new_environ, (char *) environ, size * sizeof(char *));
5741
5742 new_environ[size] = (char *) string;
5743 new_environ[size + 1] = NULL;
5744 if (last_environ != NULL)
5745 FREE(last_environ);
5746 last_environ = new_environ;
5747 environ = new_environ;
5748 } else
5749 *ep = (char *) string;
5750
5751 return 0;
5752 }
5753 #endif /* !HAVE_PUTENV */
5754
5755 #ifdef NEED_REMOVE
remove(char * name)5756 int remove(char *name)
5757 {
5758 return unlink(name);
5759 }
5760 #endif
5761
5762 #if defined(MULTI_USER_UNIX)
5763
5764 #if defined(HAVE_LSTAT) && defined(S_IFLNK)
5765 /*
5766 * If IsOurFile() is checking a symbolic link, ensure that the target
5767 * points to the user's file as well.
5768 */
IsOurSymlink(const char * name)5769 static BOOL IsOurSymlink(const char *name)
5770 {
5771 BOOL result = FALSE;
5772 size_t size = LY_MAXPATH;
5773 size_t used;
5774 char *buffer = typeMallocn(char, (unsigned) size);
5775 char *check;
5776
5777 if (buffer != 0) {
5778 while ((used = (size_t) readlink(name, buffer, (size - 1))) == size - 1) {
5779 check = typeRealloc(char, buffer, (unsigned) (size *= 2));
5780
5781 if (check == 0)
5782 break;
5783 buffer = check;
5784 }
5785 if (buffer != 0) {
5786 if ((int) used > 0) {
5787 buffer[used] = '\0';
5788 } else {
5789 FREE(buffer);
5790 }
5791 }
5792 }
5793 if (buffer != 0) {
5794 if (!LYisAbsPath(buffer)) {
5795 char *cutoff = LYLastPathSep(name);
5796 char *clone = NULL;
5797
5798 if (cutoff != 0) {
5799 HTSprintf0(&clone, "%.*s%s%s",
5800 (int) (cutoff - name),
5801 name, PATHSEP_STR, buffer);
5802 FREE(buffer);
5803 buffer = clone;
5804 }
5805 }
5806 CTRACE2(TRACE_CFG, (tfp, "IsOurSymlink(%s -> %s)\n", name, buffer));
5807 result = IsOurFile(buffer);
5808 FREE(buffer);
5809 }
5810 return result;
5811 }
5812 #endif
5813
5814 /*
5815 * Verify if this is really a file, not accessed by a link, except for the
5816 * special case of its directory being pointed to by a link from a directory
5817 * owned by root and not writable by other users.
5818 */
IsOurFile(const char * name)5819 BOOL IsOurFile(const char *name)
5820 {
5821 BOOL result = FALSE;
5822 struct stat data;
5823
5824 if (!LYIsTilde(name[0])
5825 && lstat(name, &data) == 0
5826 && ((S_ISREG(data.st_mode)
5827 && (data.st_mode & (S_IWOTH | S_IWGRP)) == 0
5828 && data.st_nlink == 1
5829 && data.st_uid == getuid())
5830 #if defined(HAVE_LSTAT) && defined(S_IFLNK)
5831 || (S_ISLNK(data.st_mode) && IsOurSymlink(name))
5832 #endif
5833 )) {
5834 int linked = FALSE;
5835
5836 /*
5837 * ( If this is not a single-user system, the other user is presumed by
5838 * some people busy trying to use a symlink attack on our files ;-)
5839 */
5840 #if defined(HAVE_LSTAT)
5841 char *path = 0;
5842 char *leaf;
5843
5844 StrAllocCopy(path, name);
5845 do {
5846 if ((leaf = LYPathLeaf(path)) != path)
5847 *--leaf = '\0'; /* write a null on the '/' */
5848 if (lstat(*path ? path : "/", &data) != 0) {
5849 break;
5850 }
5851 /*
5852 * If we find a symbolic link, it has to be in a directory that's
5853 * protected. Otherwise someone could have switched it to point
5854 * to one of the real user's files.
5855 */
5856 if (S_ISLNK(data.st_mode)) {
5857 linked = TRUE; /* could be link-to-link; doesn't matter */
5858 } else if (S_ISDIR(data.st_mode)) {
5859 if (linked) {
5860 linked = FALSE;
5861 /*
5862 * We assume that a properly-configured system has the
5863 * unwritable directories owned by root. This is not
5864 * necessarily so (bin, news, etc., may), but the only
5865 * uid we can count on is 0. It would be nice to add a
5866 * check for the gid also, but that wouldn't be
5867 * portable.
5868 */
5869 if (data.st_uid != 0
5870 || (data.st_mode & S_IWOTH) != 0) {
5871 linked = TRUE; /* force an error-return */
5872 break;
5873 }
5874 }
5875 } else if (linked) {
5876 break;
5877 }
5878 } while (leaf != path);
5879 FREE(path);
5880 #endif
5881 result = (BOOLEAN) !linked;
5882 }
5883 CTRACE2(TRACE_CFG, (tfp, "IsOurFile(%s) %d\n", name, result));
5884 return result;
5885 }
5886
5887 /*
5888 * Open a file that we don't want other users to see.
5889 */
OpenHiddenFile(const char * name,const char * mode)5890 static FILE *OpenHiddenFile(const char *name, const char *mode)
5891 {
5892 FILE *fp = 0;
5893 struct stat data;
5894 BOOLEAN binary = (BOOLEAN) (strchr(mode, 'b') != 0);
5895
5896 #if defined(O_CREAT) && defined(O_EXCL) /* we have fcntl.h or kindred? */
5897 /*
5898 * This is the preferred method for creating new files, since it ensures
5899 * that no one has an existing file or link that they happen to own.
5900 */
5901 if (*mode == 'w') {
5902 int fd = open(name, O_CREAT | O_EXCL | O_WRONLY, HIDE_CHMOD);
5903
5904 if (fd < 0
5905 && errno == EEXIST
5906 && IsOurFile(name)) {
5907 if (remove(name) == 0) {
5908 /* FIXME: there's a race at this point if directory is open */
5909 fd = open(name, O_CREAT | O_EXCL | O_WRONLY, HIDE_CHMOD);
5910 }
5911 }
5912 if (fd >= 0) {
5913 #if defined(O_BINARY) && defined(__CYGWIN__)
5914 if (binary)
5915 setmode(fd, O_BINARY);
5916 #endif
5917 fp = fdopen(fd, mode);
5918 }
5919 } else
5920 #endif
5921 if (*mode == 'a') {
5922 if (IsOurFile(name)
5923 && chmod(name, HIDE_CHMOD) == 0)
5924 fp = fopen(name, mode);
5925 else if (lstat(name, &data) != 0)
5926 fp = OpenHiddenFile(name, binary ? BIN_W : TXT_W);
5927 /*
5928 * This is less stringent, but reasonably portable. For new files, the
5929 * umask will suffice; however if the file already exists we'll change
5930 * permissions first, before opening it. If the chmod fails because of
5931 * some reason other than a non-existent file, there's no point in trying
5932 * to open it.
5933 *
5934 * This won't work properly if the user is root, since the chmod succeeds.
5935 */
5936 } else if (*mode != 'a') {
5937 mode_t save = umask(HIDE_UMASK);
5938
5939 if (chmod(name, HIDE_CHMOD) == 0 || errno == ENOENT)
5940 fp = fopen(name, mode);
5941 (void) umask(save);
5942 }
5943 return fp;
5944 }
5945 #else
5946 #define OpenHiddenFile(name, mode) fopen(name, mode)
5947 #endif /* MULTI_USER_UNIX */
5948
LYNewBinFile(const char * name)5949 FILE *LYNewBinFile(const char *name)
5950 {
5951 #ifdef VMS
5952 FILE *fp = fopen(name, BIN_W, "mbc=32");
5953
5954 (void) chmod(name, HIDE_CHMOD);
5955 #else
5956 FILE *fp = OpenHiddenFile(name, BIN_W);
5957 #endif
5958 return fp;
5959 }
5960
LYNewTxtFile(const char * name)5961 FILE *LYNewTxtFile(const char *name)
5962 {
5963 FILE *fp;
5964
5965 #ifdef VMS
5966 fp = fopen(name, TXT_W, "shr=get");
5967 (void) chmod(name, HIDE_CHMOD);
5968 #else
5969 SetDefaultMode(O_TEXT);
5970
5971 fp = OpenHiddenFile(name, TXT_W);
5972
5973 SetDefaultMode(O_BINARY);
5974 #endif
5975
5976 return fp;
5977 }
5978
LYAppendToTxtFile(const char * name)5979 FILE *LYAppendToTxtFile(const char *name)
5980 {
5981 FILE *fp;
5982
5983 #ifdef VMS
5984 fp = fopen(name, TXT_A, "shr=get");
5985 (void) chmod(name, HIDE_CHMOD);
5986 #else
5987 SetDefaultMode(O_TEXT);
5988
5989 fp = OpenHiddenFile(name, TXT_A);
5990
5991 SetDefaultMode(O_BINARY);
5992 #endif
5993 return fp;
5994 }
5995
5996 #if defined(MULTI_USER_UNIX)
5997 /*
5998 * Restore normal permissions to a copy of a file that we have created with
5999 * temp file restricted permissions. The normal umask should apply for user
6000 * files. - kw
6001 */
LYRelaxFilePermissions(const char * name)6002 void LYRelaxFilePermissions(const char *name)
6003 {
6004 mode_t mode;
6005 struct stat stat_buf;
6006
6007 if (stat(name, &stat_buf) == 0 &&
6008 S_ISREG(stat_buf.st_mode) &&
6009 (mode = (stat_buf.st_mode & 0777)) == HIDE_CHMOD) {
6010 /*
6011 * It looks plausible that this is a file we created with temp file
6012 * paranoid permissions (and the umask wasn't even more restrictive
6013 * when it was copied). - kw
6014 */
6015 mode_t save = umask(HIDE_UMASK);
6016
6017 mode = ((mode & 0700) | 0066) & ~save;
6018 (void) umask(save);
6019 (void) chmod(name, mode);
6020 }
6021 }
6022 #endif
6023
6024 /*
6025 * Check if the given anchor has an associated file-cache.
6026 */
LYCachedTemp(char * target,char ** cached)6027 BOOLEAN LYCachedTemp(char *target,
6028 char **cached)
6029 {
6030 BOOLEAN result = FALSE;
6031
6032 if (*cached) {
6033 LYStrNCpy(target, *cached, LY_MAXPATH);
6034 FREE(*cached);
6035 if (LYCanReadFile(target)) {
6036 if (remove(target) != 0) {
6037 CTRACE((tfp, "cannot remove %s\n", target));
6038 }
6039 }
6040 result = TRUE;
6041 }
6042 return result;
6043 }
6044
6045 #ifndef HAVE_MKDTEMP
6046 #define mkdtemp(path) ((mktemp(path) != 0) && (mkdir(path, 0700) == 0))
6047 #endif
6048
6049 /*
6050 * Open a temp-file, ensuring that it is unique, and not readable by other
6051 * users.
6052 *
6053 * The mode can be one of: "w", "a", "wb".
6054 */
LYOpenTemp(char * result,const char * suffix,const char * mode)6055 FILE *LYOpenTemp(char *result,
6056 const char *suffix,
6057 const char *mode)
6058 {
6059 FILE *fp = 0;
6060 BOOL txt = TRUE;
6061 char wrt = 'r';
6062 LY_TEMP *p;
6063
6064 CTRACE((tfp, "LYOpenTemp(,%s,%s)\n", suffix, mode));
6065 if (result == 0)
6066 return 0;
6067
6068 while (*mode != '\0') {
6069 switch (*mode++) {
6070 case 'w':
6071 wrt = 'w';
6072 break;
6073 case 'a':
6074 wrt = 'a';
6075 break;
6076 case 'b':
6077 txt = FALSE;
6078 break;
6079 default:
6080 CTRACE((tfp, "%s @%d: BUG\n", __FILE__, __LINE__));
6081 return 0;
6082 }
6083 }
6084
6085 /*
6086 * Verify if the given space looks secure enough. Otherwise, make a
6087 * secure subdirectory of that.
6088 */
6089 #if defined(MULTI_USER_UNIX) && (defined(HAVE_MKTEMP) || defined(HAVE_MKDTEMP))
6090 if (lynx_temp_subspace == 0) {
6091 BOOL make_it = FALSE;
6092 struct stat sb;
6093
6094 if (lstat(lynx_temp_space, &sb) == 0
6095 && S_ISDIR(sb.st_mode)) {
6096 if (sb.st_uid != getuid()
6097 || (sb.st_mode & (S_IWOTH | S_IWGRP)) != 0) {
6098 make_it = TRUE;
6099 CTRACE((tfp,
6100 "lynx_temp_space is not our directory %s owner %d mode %03o\n",
6101 lynx_temp_space, (int) sb.st_uid, (int) sb.st_mode & 0777));
6102 }
6103 } else {
6104 make_it = TRUE;
6105 CTRACE((tfp, "lynx_temp_space is not a directory %s\n", lynx_temp_space));
6106 }
6107 if (make_it) {
6108 mode_t old_mask = umask(HIDE_UMASK);
6109
6110 StrAllocCat(lynx_temp_space, "lynxXXXXXXXXXX");
6111 if (mkdtemp(lynx_temp_space) == 0) {
6112 printf("%s: %s\n", lynx_temp_space, LYStrerror(errno));
6113 exit_immediately(EXIT_FAILURE);
6114 }
6115 (void) umask(old_mask);
6116 lynx_temp_subspace = 1;
6117 StrAllocCat(lynx_temp_space, "/");
6118 CTRACE((tfp, "made subdirectory %s\n", lynx_temp_space));
6119 } else {
6120 lynx_temp_subspace = -1;
6121 }
6122 }
6123 #endif
6124
6125 do {
6126 if (!fmt_tempname(result, lynx_temp_space, suffix))
6127 return 0;
6128 if (txt) {
6129 switch (wrt) {
6130 case 'w':
6131 fp = LYNewTxtFile(result);
6132 break;
6133 case 'a':
6134 fp = LYAppendToTxtFile(result);
6135 break;
6136 }
6137 } else {
6138 fp = LYNewBinFile(result);
6139 }
6140 /*
6141 * If we get a failure to make a temporary file, don't bother to try a
6142 * different name unless the failure was because the file already
6143 * exists.
6144 */
6145 #ifdef EEXIST /* FIXME (need a better test) in fcntl.h or unistd.h */
6146 if ((fp == 0) && (errno != EEXIST)) {
6147 CTRACE((tfp, "... LYOpenTemp(%s) failed: %s\n",
6148 result, LYStrerror(errno)));
6149 return 0;
6150 }
6151 #endif
6152 } while (fp == 0);
6153
6154 if ((p = typecalloc(LY_TEMP)) != 0) {
6155 p->next = ly_temp;
6156 StrAllocCopy((p->name), result);
6157 p->file = fp;
6158 p->outs = (BOOLEAN) (wrt != 'r');
6159 ly_temp = p;
6160 } else {
6161 outofmem(__FILE__, "LYOpenTemp");
6162 }
6163
6164 CTRACE((tfp, "... LYOpenTemp(%s)\n", result));
6165 return fp;
6166 }
6167
6168 /*
6169 * Reopen a temporary file
6170 */
LYReopenTemp(char * name)6171 FILE *LYReopenTemp(char *name)
6172 {
6173 LY_TEMP *p;
6174 FILE *fp = 0;
6175
6176 LYCloseTemp(name);
6177 if ((p = FindTempfileByName(name)) != 0) {
6178 fp = p->file = LYAppendToTxtFile(name);
6179 }
6180 return fp;
6181 }
6182
6183 /*
6184 * Open a temp-file for writing, possibly re-using a previously used
6185 * name and file.
6186 * If a non-empty fname is given, it is reused if it indicates a file
6187 * previously registered as a temp file and, in case the file still
6188 * exists, if it looks like we can write to it safely. Otherwise a
6189 * new temp file (with new name) will be generated and returned in fname.
6190 *
6191 * File permissions are set so that the file is not readable by unprivileged
6192 * other users.
6193 *
6194 * Suffix is only used if fname is not being reused.
6195 * The mode should be "w", others are possible (they may be passed on)
6196 * but probably don't make sense. - kw
6197 */
LYOpenTempRewrite(char * fname,const char * suffix,const char * mode)6198 FILE *LYOpenTempRewrite(char *fname,
6199 const char *suffix,
6200 const char *mode)
6201 {
6202 FILE *fp = 0;
6203 BOOL txt = TRUE;
6204 char wrt = 'r';
6205 BOOL registered = NO;
6206 BOOL writable_exists = NO;
6207 BOOL is_ours = NO;
6208 BOOL still_open = NO;
6209 LY_TEMP *p;
6210 struct stat stat_buf;
6211
6212 CTRACE((tfp, "LYOpenTempRewrite(%s,%s,%s)\n", fname, suffix, mode));
6213 if (*fname == '\0') /* first time, no filename yet */
6214 return (LYOpenTemp(fname, suffix, mode));
6215
6216 if ((p = FindTempfileByName(fname)) != 0) {
6217 registered = YES;
6218 if (p->file != 0)
6219 still_open = YES;
6220 CTRACE((tfp, "...used before%s\n", still_open ? ", still open!" : "."));
6221 }
6222
6223 if (registered) {
6224 #ifndef NO_GROUPS
6225 writable_exists = HTEditable(fname); /* existing, can write */
6226 #define CTRACE_EXISTS "exists and is writable, "
6227 #else
6228 writable_exists = (BOOL) (stat(fname, &stat_buf) == 0); /* existing, assume can write */
6229 #define CTRACE_EXISTS "exists, "
6230 #endif
6231
6232 if (writable_exists) {
6233 is_ours = IsOurFile(fname);
6234 }
6235 CTRACE((tfp, "...%s%s\n",
6236 writable_exists ? CTRACE_EXISTS : "",
6237 is_ours ? "is our file." : "is NOT our file."));
6238 }
6239
6240 /*
6241 * Note that in cases where LYOpenTemp is called as fallback below, we
6242 * don't call LYRemoveTemp first. That may be appropriate in some cases,
6243 * but not trying to remove a weird existing file seems safer and could
6244 * help diagnose an unusual situation. (They may be removed anyway later.)
6245 */
6246 if (still_open) {
6247 /*
6248 * This should probably not happen. Make a new one.
6249 */
6250 return (LYOpenTemp(fname, suffix, mode));
6251 } else if (!registered) {
6252 /*
6253 * Not registered. It should have been registered at one point though,
6254 * otherwise we wouldn't be called like this.
6255 */
6256 return (LYOpenTemp(fname, suffix, mode));
6257 } else if (writable_exists && !is_ours) {
6258 /*
6259 * File exists, writable if we checked, but something is wrong with it.
6260 */
6261 return (LYOpenTemp(fname, suffix, mode));
6262 #ifndef NO_GROUPS
6263 } else if (!is_ours && (lstat(fname, &stat_buf) == 0)) {
6264 /*
6265 * Exists but not writable, and something is wrong with it.
6266 */
6267 return (LYOpenTemp(fname, suffix, mode));
6268 #endif
6269 }
6270
6271 while (*mode != '\0') {
6272 switch (*mode++) {
6273 case 'w':
6274 wrt = 'w';
6275 break;
6276 case 'a':
6277 wrt = 'a';
6278 break;
6279 case 'b':
6280 txt = FALSE;
6281 break;
6282 default:
6283 CTRACE((tfp, "%s @%d: BUG\n", __FILE__, __LINE__));
6284 return fp;
6285 }
6286 }
6287
6288 if (is_ours) {
6289 /*
6290 * Yes, it exists, is writable if we checked, and everything looks ok
6291 * so far. This should be the most regular case. - kw
6292 */
6293 #ifdef HAVE_TRUNCATE
6294 if (txt == TRUE) { /* limitation of LYReopenTemp. shrug */
6295 /*
6296 * We truncate and then append, this avoids having a small window
6297 * in which the file doesn't exist. - kw
6298 */
6299 if (truncate(fname, (off_t) 0) != 0) {
6300 CTRACE((tfp, "... truncate(%s,0) failed: %s\n",
6301 fname, LYStrerror(errno)));
6302 return (LYOpenTemp(fname, suffix, mode));
6303 } else {
6304 return (LYReopenTemp(fname));
6305 }
6306 }
6307 #endif
6308 remove(fname);
6309
6310 }
6311
6312 /* We come here in two cases: either the file existed and was ours and we
6313 * just got rid of it. Or the file did and does not exist, but is
6314 * registered as a temp file. It must have been removed by some means
6315 * other than LYRemoveTemp. In both cases, reuse the name! - kw
6316 */
6317
6318 if (txt) {
6319 switch (wrt) {
6320 case 'w':
6321 fp = LYNewTxtFile(fname);
6322 break;
6323 case 'a':
6324 fp = LYAppendToTxtFile(fname);
6325 break;
6326 }
6327 } else {
6328 fp = LYNewBinFile(fname);
6329 }
6330 p->file = fp;
6331
6332 CTRACE((tfp, "... LYOpenTempRewrite(%s), %s\n", fname,
6333 (fp) ? "ok" : "failed"));
6334 /*
6335 * We could fall back to trying LYOpenTemp() here in case of failure.
6336 * After all the checks already done above a filure here should be pretty
6337 * unusual though, so maybe it's better to let the user notice that
6338 * something went wrong, and not try to fix it up. - kw
6339 */
6340 return fp;
6341 }
6342
6343 /*
6344 * Special case of LYOpenTemp, used for manipulating bookmark file, i.e., with
6345 * renaming.
6346 */
LYOpenScratch(char * result,const char * prefix)6347 FILE *LYOpenScratch(char *result,
6348 const char *prefix)
6349 {
6350 FILE *fp;
6351 LY_TEMP *p;
6352
6353 if (!fmt_tempname(result, prefix, HTML_SUFFIX))
6354 return 0;
6355
6356 if ((fp = LYNewTxtFile(result)) != 0) {
6357 if ((p = typecalloc(LY_TEMP)) != 0) {
6358 p->next = ly_temp;
6359 StrAllocCopy((p->name), result);
6360 p->file = fp;
6361 ly_temp = p;
6362 } else {
6363 outofmem(__FILE__, "LYOpenScratch");
6364 }
6365 }
6366 CTRACE((tfp, "LYOpenScratch(%s)\n", result));
6367 return fp;
6368 }
6369
LY_close_temp(LY_TEMP * p)6370 static void LY_close_temp(LY_TEMP * p)
6371 {
6372 if (p->file != 0) {
6373 if (p->outs) {
6374 LYCloseOutput(p->file);
6375 } else {
6376 LYCloseInput(p->file);
6377 }
6378 p->file = 0;
6379 }
6380 }
6381
6382 /*
6383 * Close a temp-file, given its name
6384 */
LYCloseTemp(char * name)6385 void LYCloseTemp(char *name)
6386 {
6387 LY_TEMP *p;
6388
6389 CTRACE((tfp, "LYCloseTemp(%s)\n", name));
6390 if ((p = FindTempfileByName(name)) != 0) {
6391 CTRACE((tfp, "...LYCloseTemp(%s)%s\n", name,
6392 (p->file != 0) ? ", closed" : ""));
6393 LY_close_temp(p);
6394 }
6395 }
6396
6397 /*
6398 * Close a temp-file, given its file-pointer
6399 */
LYCloseTempFP(FILE * fp)6400 void LYCloseTempFP(FILE *fp)
6401 {
6402 LY_TEMP *p;
6403
6404 CTRACE((tfp, "LYCloseTempFP\n"));
6405 if ((p = FindTempfileByFP(fp)) != 0) {
6406 LY_close_temp(p);
6407 CTRACE((tfp, "...LYCloseTempFP(%s)\n", p->name));
6408 }
6409 }
6410
6411 /*
6412 * Close a temp-file, removing it.
6413 */
LYRemoveTemp(char * name)6414 int LYRemoveTemp(char *name)
6415 {
6416 LY_TEMP *p, *q;
6417 int code = -1;
6418
6419 if (non_empty(name)) {
6420 CTRACE((tfp, "LYRemoveTemp(%s)\n", name));
6421 for (p = ly_temp, q = 0; p != 0; q = p, p = p->next) {
6422 if (!strcmp(name, p->name)) {
6423 if (q != 0) {
6424 q->next = p->next;
6425 } else {
6426 ly_temp = p->next;
6427 }
6428 LY_close_temp(p);
6429 code = HTSYS_remove(name);
6430 CTRACE((tfp, "...LYRemoveTemp done(%d)%s\n", code,
6431 (p->file != 0) ? ", closed" : ""));
6432 CTRACE_FLUSH(tfp);
6433 FREE(p->name);
6434 FREE(p);
6435 break;
6436 }
6437 }
6438 }
6439 return code;
6440 }
6441
6442 /*
6443 * Remove all of the temp-files. Note that this assumes that they are closed,
6444 * since some systems will not allow us to remove a file which is open.
6445 */
LYCleanupTemp(void)6446 void LYCleanupTemp(void)
6447 {
6448 while (ly_temp != 0) {
6449 (void) LYRemoveTemp(ly_temp->name);
6450 }
6451 #if defined(MULTI_USER_UNIX)
6452 if (lynx_temp_subspace > 0) {
6453 char result[LY_MAXPATH];
6454
6455 LYStrNCpy(result, lynx_temp_space, sizeof(result) - 1);
6456 LYTrimPathSep(result);
6457 CTRACE((tfp, "LYCleanupTemp removing %s\n", result));
6458 rmdir(result);
6459 lynx_temp_subspace = -1;
6460 }
6461 #endif
6462 }
6463
6464 /*
6465 * We renamed a temporary file. Keep track so we can remove it on exit.
6466 */
LYRenamedTemp(char * oldname,char * newname)6467 void LYRenamedTemp(char *oldname,
6468 char *newname)
6469 {
6470 LY_TEMP *p;
6471
6472 CTRACE((tfp, "LYRenamedTemp(old=%s, new=%s)\n", oldname, newname));
6473 if ((p = FindTempfileByName(oldname)) != 0) {
6474 StrAllocCopy((p->name), newname);
6475 }
6476 }
6477
6478 #ifndef DISABLE_BIBP
6479 /*
6480 * Check that bibhost defines the BibP icon.
6481 */
LYCheckBibHost(void)6482 void LYCheckBibHost(void)
6483 {
6484 DocAddress bibhostIcon;
6485 BOOLEAN saveFlag;
6486
6487 bibhostIcon.address = NULL;
6488 StrAllocCopy(bibhostIcon.address, BibP_bibhost);
6489 StrAllocCat(bibhostIcon.address, "bibp1.0/bibpicon.jpg");
6490 bibhostIcon.post_data = NULL;
6491 bibhostIcon.post_content_type = NULL;
6492 bibhostIcon.bookmark = FALSE;
6493 bibhostIcon.isHEAD = FALSE;
6494 bibhostIcon.safe = FALSE;
6495 saveFlag = traversal;
6496 traversal = TRUE; /* Hack to force error response. */
6497 BibP_bibhost_available = (BOOLEAN) (HTLoadAbsolute(&bibhostIcon) == YES);
6498 traversal = saveFlag;
6499 BibP_bibhost_checked = TRUE;
6500 }
6501 #endif /* !DISABLE_BIBP */
6502
6503 /*
6504 * Management of User Interface Pages. - kw
6505 *
6506 * These are mostly temp files. Pages which can be recognized by their special
6507 * URL (after having been loaded) need not be tracked here.
6508 *
6509 * First some private stuff:
6510 */
6511 typedef struct uipage_entry {
6512 UIP_t type;
6513 unsigned flags;
6514 char *url;
6515 HTList *alturls;
6516 char *file;
6517 } uip_entry;
6518
6519 #define UIP_F_MULTI 0x0001 /* flag: track multiple instances */
6520 #define UIP_F_LIMIT 0x0002 /* flag: limit size of alturls list */
6521 #define UIP_F_LMULTI (UIP_F_MULTI | UIP_F_LIMIT)
6522 /* *INDENT-OFF* */
6523 static uip_entry ly_uip[] =
6524 {
6525 { UIP_HISTORY , UIP_F_LMULTI, NULL, NULL, NULL }
6526 , { UIP_DOWNLOAD_OPTIONS , 0 , NULL, NULL, NULL }
6527 , { UIP_PRINT_OPTIONS , 0 , NULL, NULL, NULL }
6528 , { UIP_SHOWINFO , UIP_F_LMULTI, NULL, NULL, NULL }
6529 , { UIP_LIST_PAGE , UIP_F_LMULTI, NULL, NULL, NULL }
6530 , { UIP_VLINKS , UIP_F_LMULTI, NULL, NULL, NULL }
6531 #if !defined(NO_OPTION_FORMS)
6532 , { UIP_OPTIONS_MENU , UIP_F_LMULTI, NULL, NULL, NULL }
6533 #endif
6534 #ifdef DIRED_SUPPORT
6535 , { UIP_DIRED_MENU , 0 , NULL, NULL, NULL }
6536 , { UIP_PERMIT_OPTIONS , 0 , NULL, NULL, NULL }
6537 , { UIP_UPLOAD_OPTIONS , UIP_F_LMULTI, NULL, NULL, NULL }
6538 #endif
6539 #ifdef USE_ADDRLIST_PAGE
6540 , { UIP_ADDRLIST_PAGE , UIP_F_LMULTI, NULL, NULL, NULL }
6541 #endif
6542 , { UIP_LYNXCFG , UIP_F_LMULTI, NULL, NULL, NULL }
6543 #if !defined(NO_CONFIG_INFO)
6544 , { UIP_CONFIG_DEF , UIP_F_LMULTI, NULL, NULL, NULL }
6545 #endif
6546 /* The following are not generated tempfiles: */
6547 , { UIP_TRACELOG , 0 , NULL, NULL, NULL }
6548 #if defined(DIRED_SUPPORT) && defined(OK_INSTALL)
6549 , { UIP_INSTALL , 0 , NULL, NULL, NULL }
6550 #endif
6551
6552 };
6553 /* *INDENT-ON* */
6554
6555 /* Public entry points for User Interface Page management: */
6556
LYIsUIPage3(const char * url,UIP_t type,int flagparam)6557 BOOL LYIsUIPage3(const char *url,
6558 UIP_t type,
6559 int flagparam)
6560 {
6561 unsigned int i;
6562 size_t l;
6563
6564 if (!url)
6565 return NO;
6566 for (i = 0; i < TABLESIZE(ly_uip); i++) {
6567 if (ly_uip[i].type == type) {
6568 if (!ly_uip[i].url) {
6569 return NO;
6570 } else if ((flagparam & UIP_P_FRAG) ?
6571 (!StrNCmp(ly_uip[i].url, url, (l = strlen(ly_uip[i].url)))
6572 && (url[l] == '\0' || url[l] == '#')) :
6573 !strcmp(ly_uip[i].url, url)) {
6574 return YES;
6575 } else if (ly_uip[i].flags & UIP_F_MULTI) {
6576 char *p;
6577 HTList *l0 = ly_uip[i].alturls;
6578
6579 while ((p = (char *) HTList_nextObject(l0)) != NULL) {
6580 if ((flagparam & UIP_P_FRAG) ?
6581 (!StrNCmp(p, url, (l = strlen(p)))
6582 && (url[l] == '\0' || url[l] == '#')) :
6583 !strcmp(p, url))
6584 return YES;
6585 }
6586 }
6587 return NO;
6588 }
6589 }
6590 return NO;
6591 }
6592
LYRegisterUIPage(const char * url,UIP_t type)6593 void LYRegisterUIPage(const char *url,
6594 UIP_t type)
6595 {
6596 unsigned int i;
6597
6598 for (i = 0; i < TABLESIZE(ly_uip); i++) {
6599 if (ly_uip[i].type == type) {
6600 if (ly_uip[i].url && url &&
6601 !strcmp(ly_uip[i].url, url)) {
6602
6603 } else if (!ly_uip[i].url || !url ||
6604 !(ly_uip[i].flags & UIP_F_MULTI)) {
6605 StrAllocCopy(ly_uip[i].url, url);
6606
6607 } else {
6608 char *p;
6609 int n = 0;
6610 HTList *l0 = ly_uip[i].alturls;
6611
6612 while ((p = (char *) HTList_nextObject(l0)) != NULL) {
6613 if (!strcmp(p, url))
6614 return;
6615 if (!strcmp(p, ly_uip[i].url)) {
6616 StrAllocCopy(ly_uip[i].url, url);
6617 return;
6618 }
6619 n++;
6620 }
6621 if (!ly_uip[i].alturls)
6622 ly_uip[i].alturls = HTList_new();
6623
6624 if (n >= HTCacheSize && (ly_uip[i].flags & UIP_F_LIMIT))
6625 HTList_removeFirstObject(ly_uip[i].alturls);
6626 HTList_addObject(ly_uip[i].alturls, ly_uip[i].url);
6627 ly_uip[i].url = NULL;
6628 StrAllocCopy(ly_uip[i].url, url);
6629 }
6630
6631 return;
6632 }
6633 }
6634 }
6635
LYUIPages_free(void)6636 void LYUIPages_free(void)
6637 {
6638 unsigned int i;
6639
6640 for (i = 0; i < TABLESIZE(ly_uip); i++) {
6641 FREE(ly_uip[i].url);
6642 FREE(ly_uip[i].file);
6643 LYFreeStringList(ly_uip[i].alturls);
6644 ly_uip[i].alturls = NULL;
6645 }
6646 }
6647
6648 /*
6649 * Convert local pathname to www name
6650 * (do not bother about file://localhost prefix at this point).
6651 */
wwwName(const char * pathname)6652 const char *wwwName(const char *pathname)
6653 {
6654 const char *cp = NULL;
6655
6656 #if defined(USE_DOS_DRIVES)
6657 cp = HTDOS_wwwName(pathname);
6658 #else
6659 #ifdef VMS
6660 cp = HTVMS_wwwName(pathname);
6661 #else
6662 cp = pathname;
6663 #endif /* VMS */
6664 #endif
6665
6666 return cp;
6667 }
6668
6669 /*
6670 * Given a user-specified filename, e.g., for download or print, validate and
6671 * expand it. Expand home-directory expressions in the given string. Only
6672 * allow pipes if the user can spawn shell commands.
6673 */
LYValidateFilename(bstring ** result,bstring ** given)6674 BOOLEAN LYValidateFilename(bstring **result,
6675 bstring **given)
6676 {
6677 BOOLEAN code = TRUE;
6678 char *cp = NULL;
6679
6680 /*
6681 * Cancel if the user entered "/dev/null" on Unix, or an "nl:" path on VMS.
6682 * - FM
6683 */
6684 if (LYIsNullDevice((*given)->str)) {
6685 /* just ignore it */
6686 code = FALSE;
6687 #ifdef HAVE_POPEN
6688 } else if (LYIsPipeCommand((*given)->str)) {
6689 if (no_shell) {
6690 HTUserMsg(SPAWNING_DISABLED);
6691 code = FALSE;
6692 } else {
6693 BStrCopy(*result, (*given));
6694 }
6695 #endif
6696 } else {
6697 if (FindLeadingTilde((*given)->str, TRUE) != 0) {
6698 char *cp1 = NULL;
6699
6700 StrAllocCopy(cp1, (*given)->str);
6701 LYTildeExpand(&cp1, TRUE);
6702 BStrCopy0(*result, cp1);
6703 BStrCopy0(*given, cp1);
6704 FREE(cp1);
6705 }
6706 #ifdef VMS
6707 if (strchr((*given)->str, '/') != NULL) {
6708 BStrCopy0(*result, HTVMS_name("", (*given)->str));
6709 BStrCopy(*given, *result);
6710 }
6711 if ((*given)->str[0] != '/'
6712 && strchr((*given)->str, ':') == NULL) {
6713 BStrCopy0(*result, "sys$disk:");
6714 if (strchr((*given)->str, ']') == NULL)
6715 BStrCat0(*result, "[]");
6716 BStrCat(*result, (*given));
6717 } else {
6718 BStrCopy(*result, (*given));
6719 }
6720 #else
6721
6722 #ifndef __EMX__
6723 if (!LYisAbsPath((*given)->str)) {
6724 #if defined(__DJGPP__) || defined(_WINDOWS)
6725 if (strchr((*result)->str, ':') != NULL)
6726 cp = NULL;
6727 else
6728 #endif /* __DJGPP__ || _WINDOWS */
6729 {
6730 #ifdef SUPPORT_CHDIR
6731 static char buf[LY_MAXPATH];
6732
6733 cp = Current_Dir(buf);
6734 #else
6735 cp = original_dir;
6736 #endif
6737 }
6738 } else
6739 #endif /* __EMX__ */
6740 cp = NULL;
6741
6742 if (cp) {
6743 LYTrimPathSep(cp);
6744 BStrCopy0(*result, cp);
6745 BStrCat0(*result, "/");
6746 } else {
6747 BStrCopy0(*result, "");
6748 }
6749 if (code) {
6750 cp = HTSYS_name((*given)->str);
6751 BStrCat0(*result, cp);
6752 }
6753 #endif /* VMS */
6754 }
6755 return code;
6756 }
6757
6758 /*
6759 * Given a valid filename, check if it exists. If so, we'll have to worry
6760 * about overwriting it.
6761 *
6762 * Returns:
6763 * 'Y' (yes/success)
6764 * 'N' (no/retry)
6765 * 3 (cancel)
6766 */
LYValidateOutput(char * filename)6767 int LYValidateOutput(char *filename)
6768 {
6769 int c;
6770
6771 /*
6772 * Assume we can write to a pipe
6773 */
6774 #ifdef HAVE_POPEN
6775 if (LYIsPipeCommand(filename))
6776 return 'Y';
6777 #endif
6778
6779 if (no_dotfiles || !show_dotfiles) {
6780 if (*LYPathLeaf(filename) == '.') {
6781 HTAlert(FILENAME_CANNOT_BE_DOT);
6782 return 'N';
6783 }
6784 }
6785
6786 /*
6787 * See if it already exists.
6788 */
6789 if (LYCanReadFile(filename)) {
6790 #ifdef VMS
6791 c = HTConfirm(FILE_EXISTS_HPROMPT);
6792 #else
6793 c = HTConfirm(FILE_EXISTS_OPROMPT);
6794 #endif /* VMS */
6795 if (HTLastConfirmCancelled()) {
6796 HTInfoMsg(SAVE_REQUEST_CANCELLED);
6797 return 3;
6798 } else if (c == NO) {
6799 return 'N';
6800 }
6801 } else if (!LYCanWriteFile(filename)) {
6802 return 'N';
6803 }
6804 return 'Y';
6805 }
6806
6807 /*
6808 * Convert a local filename to a URL
6809 */
LYLocalFileToURL(char ** target,const char * source)6810 void LYLocalFileToURL(char **target,
6811 const char *source)
6812 {
6813 const char *leaf;
6814
6815 StrAllocCopy(*target, "file://localhost");
6816
6817 leaf = wwwName(source);
6818
6819 if (!LYisAbsPath(source)) {
6820 char temp[LY_MAXPATH];
6821
6822 Current_Dir(temp);
6823 if (!LYIsHtmlSep(*temp))
6824 LYAddHtmlSep(target);
6825 StrAllocCat(*target, temp);
6826 }
6827 if (leaf && !LYIsHtmlSep(*leaf))
6828 LYAddHtmlSep(target);
6829 StrAllocCat(*target, leaf);
6830 }
6831
6832 #define MY_DOCTYPE "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"
6833 #define PUT_STRING(buf) (*(target)->isa->put_string)(target, buf)
6834
6835 /*
6836 * Like WriteInternalTitle, used for writing title on pages constructed via
6837 * streams.
6838 */
WriteStreamTitle(HTStream * target,const char * Title)6839 void WriteStreamTitle(HTStream *target, const char *Title)
6840 {
6841 char *buf = 0;
6842
6843 PUT_STRING(MY_DOCTYPE);
6844 PUT_STRING("<html>\n<head>\n");
6845 LYAddMETAcharsetToStream(target, -1);
6846 HTSprintf0(&buf, "<title>%s</title>\n</head>\n<body>\n", Title);
6847 PUT_STRING(buf);
6848 FREE(buf);
6849 }
6850
6851 /*
6852 * Open a temporary file for internal-pages, optionally reusing an existing
6853 * filename.
6854 */
InternalPageFP(char * filename,int reuse_flag)6855 FILE *InternalPageFP(char *filename,
6856 int reuse_flag)
6857 {
6858 FILE *fp;
6859
6860 if (LYReuseTempfiles && reuse_flag) {
6861 fp = LYOpenTempRewrite(filename, HTML_SUFFIX, BIN_W);
6862 } else {
6863 (void) LYRemoveTemp(filename);
6864 fp = LYOpenTemp(filename, HTML_SUFFIX, BIN_W);
6865 }
6866 if (fp == NULL) {
6867 HTAlert(CANNOT_OPEN_TEMP);
6868 }
6869 return fp;
6870 }
6871
6872 /*
6873 * This part is shared by all internal pages.
6874 */
WriteInternalTitle(FILE * fp0,const char * Title)6875 void WriteInternalTitle(FILE *fp0, const char *Title)
6876 {
6877 fprintf(fp0, MY_DOCTYPE);
6878
6879 fprintf(fp0, "<html>\n<head>\n");
6880 LYAddMETAcharsetToFD(fp0, -1);
6881 if (LYIsListpageTitle(Title)) {
6882 if (strchr(HTLoadedDocumentURL(), '"') == NULL) {
6883 char *Address = NULL;
6884
6885 /*
6886 * Insert a BASE tag so there is some way to relate the List Page
6887 * file to its underlying document after we are done. It won't be
6888 * actually used for resolving relative URLs. - kw
6889 */
6890 StrAllocCopy(Address, HTLoadedDocumentURL());
6891 LYEntify(&Address, FALSE);
6892 fprintf(fp0, "<base href=\"%s\">\n", Address);
6893 FREE(Address);
6894 }
6895 }
6896 fprintf(fp0, "<title>%s</title>\n</head>\n<body>\n", Title);
6897 }
6898
6899 /*
6900 * This is used to start most internal pages, except for special cases where
6901 * the embedded HREFs in the title differ.
6902 */
BeginInternalPage(FILE * fp0,const char * Title,const char * HelpURL)6903 void BeginInternalPage(FILE *fp0, const char *Title,
6904 const char *HelpURL)
6905 {
6906 WriteInternalTitle(fp0, Title);
6907
6908 if ((user_mode == NOVICE_MODE)
6909 && LYwouldPush(Title, NULL)
6910 && (HelpURL != 0)) {
6911 fprintf(fp0, "<h1>%s (%s%s%s), <a href=\"%s%s\">help</a></h1>\n",
6912 Title, LYNX_NAME, VERSION_SEGMENT, LYNX_VERSION,
6913 helpfilepath, HelpURL);
6914 } else {
6915 fprintf(fp0, "<h1>%s (%s%s%s)</h1>\n",
6916 Title, LYNX_NAME, VERSION_SEGMENT, LYNX_VERSION);
6917 }
6918 }
6919
EndInternalPage(FILE * fp0)6920 void EndInternalPage(FILE *fp0)
6921 {
6922 fprintf(fp0, "</body>\n</html>");
6923 }
6924
trimPoundSelector(char * address)6925 char *trimPoundSelector(char *address)
6926 {
6927 char *pound = findPoundSelector(address);
6928
6929 if (pound != 0)
6930 *pound = '\0';
6931 return pound;
6932 }
6933
6934 /*
6935 * Trim a trailing path-separator to avoid confusing other programs when we concatenate
6936 * to it. This only applies to local filesystems.
6937 */
LYTrimPathSep(char * path)6938 void LYTrimPathSep(char *path)
6939 {
6940 size_t len;
6941
6942 if (path != 0
6943 && (len = strlen(path)) != 0
6944 && LYIsPathSep(path[len - 1]))
6945 path[len - 1] = 0;
6946 }
6947
6948 /*
6949 * Add a trailing path-separator to avoid confusing other programs when we concatenate
6950 * to it. This only applies to local filesystems.
6951 */
LYAddPathSep(char ** path)6952 void LYAddPathSep(char **path)
6953 {
6954 size_t len;
6955 char *temp;
6956
6957 if ((path != 0)
6958 && ((temp = *path) != 0)
6959 && (len = strlen(temp)) != 0
6960 && !LYIsPathSep(temp[len - 1])) {
6961 StrAllocCat(*path, PATHSEP_STR);
6962 }
6963 }
6964
6965 /*
6966 * Add a trailing path-separator to avoid confusing other programs when we concatenate
6967 * to it. This only applies to local filesystems.
6968 */
LYAddPathSep0(char * path)6969 void LYAddPathSep0(char *path)
6970 {
6971 size_t len;
6972
6973 if ((path != 0)
6974 && (len = strlen(path)) != 0
6975 && (len < LY_MAXPATH - 2)
6976 && !LYIsPathSep(path[len - 1])) {
6977 strcat(path, PATHSEP_STR);
6978 }
6979 }
6980
6981 /*
6982 * Check if a given string contains a path separator
6983 */
LYLastPathSep(const char * path)6984 char *LYLastPathSep(const char *path)
6985 {
6986 char *result;
6987
6988 #if defined(USE_DOS_DRIVES)
6989 if ((result = strrchr(path, '\\')) == 0)
6990 result = strrchr(path, '/');
6991 #else
6992 result = strrchr(path, '/');
6993 #endif
6994 return result;
6995 }
6996
6997 /*
6998 * Trim a trailing path-separator to avoid confusing other programs when we concatenate
6999 * to it. This only applies to HTML paths.
7000 */
LYTrimHtmlSep(char * path)7001 void LYTrimHtmlSep(char *path)
7002 {
7003 size_t len;
7004
7005 if (path != 0
7006 && (len = strlen(path)) != 0
7007 && LYIsHtmlSep(path[len - 1]))
7008 path[len - 1] = 0;
7009 }
7010
7011 /*
7012 * Add a trailing path-separator to avoid confusing other programs when we concatenate
7013 * to it. This only applies to HTML paths.
7014 */
LYAddHtmlSep(char ** path)7015 void LYAddHtmlSep(char **path)
7016 {
7017 size_t len;
7018 char *temp;
7019
7020 if ((path != 0)
7021 && ((temp = *path) != 0)
7022 && (len = strlen(temp)) != 0
7023 && !LYIsHtmlSep(temp[len - 1])) {
7024 StrAllocCat(*path, "/");
7025 }
7026 }
7027
7028 /*
7029 * Add a trailing path-separator to avoid confusing other programs when we concatenate
7030 * to it. This only applies to HTML paths.
7031 */
LYAddHtmlSep0(char * path)7032 void LYAddHtmlSep0(char *path)
7033 {
7034 size_t len;
7035
7036 if ((path != 0)
7037 && (len = strlen(path)) != 0
7038 && (len < LY_MAXPATH - 2)
7039 && !LYIsHtmlSep(path[len - 1])) {
7040 strcat(path, "/");
7041 }
7042 }
7043
7044 /*
7045 * Copy a file
7046 */
LYCopyFile(char * src,char * dst)7047 int LYCopyFile(char *src,
7048 char *dst)
7049 {
7050 int code;
7051 const char *program;
7052
7053 if ((program = HTGetProgramPath(ppCOPY)) != NULL) {
7054 char *the_command = 0;
7055
7056 HTAddParam(&the_command, COPY_COMMAND, 1, program);
7057 HTAddParam(&the_command, COPY_COMMAND, 2, src);
7058 HTAddParam(&the_command, COPY_COMMAND, 3, dst);
7059 HTEndParam(&the_command, COPY_COMMAND, 3);
7060
7061 CTRACE((tfp, "command: %s\n", the_command));
7062 stop_curses();
7063 code = LYSystem(the_command);
7064 start_curses();
7065
7066 FREE(the_command);
7067 } else {
7068 FILE *fin, *fout;
7069 unsigned char buff[BUFSIZ];
7070 size_t len;
7071
7072 code = EOF;
7073 if ((fin = fopen(src, BIN_R)) != 0) {
7074 if ((fout = fopen(dst, BIN_W)) != 0) {
7075 code = 0;
7076 while ((len = fread(buff, (size_t) 1, sizeof(buff), fin)) != 0) {
7077 if (fwrite(buff, (size_t) 1, len, fout) < len
7078 || ferror(fout)) {
7079 code = EOF;
7080 break;
7081 }
7082 }
7083 LYCloseOutput(fout);
7084 }
7085 LYCloseInput(fin);
7086 }
7087 CTRACE((tfp, "builtin copy ->%d\n\tsource=%s\n\ttarget=%s\n",
7088 code, src, dst));
7089 }
7090
7091 if (code) {
7092 HTAlert(CANNOT_WRITE_TO_FILE);
7093 }
7094 return code;
7095 }
7096
7097 #ifdef __DJGPP__
escape_backslashes(char * source)7098 static char *escape_backslashes(char *source)
7099 {
7100 char *result = 0;
7101 int count = 0;
7102 int n;
7103
7104 for (n = 0; source[n] != '\0'; ++n) {
7105 if (source[n] == '\\')
7106 ++count;
7107 }
7108 if (count != 0) {
7109 result = malloc(count + n + 1);
7110 if (result != 0) {
7111 int ch;
7112 char *target = result;
7113
7114 while ((ch = *source++) != '\0') {
7115 if (ch == '\\')
7116 *target++ = ch;
7117 *target++ = ch;
7118 }
7119 *target = '\0';
7120 }
7121 }
7122 return result;
7123 }
7124 #endif /* __DJGPP__ */
7125 /*
7126 * Invoke a shell command, return nonzero on error.
7127 */
LYSystem(char * command)7128 int LYSystem(char *command)
7129 {
7130 int code;
7131 int do_free = 0;
7132
7133 #if defined(HAVE_SIGACTION) && defined(SIGTSTP) && !defined(USE_SLANG)
7134 struct sigaction saved_sigtstp_act;
7135 BOOLEAN sigtstp_saved = FALSE;
7136 #endif
7137 int saved_errno = 0;
7138
7139 #ifdef __EMX__
7140 int scrsize[4];
7141 #endif
7142
7143 fflush(stdout);
7144 fflush(stderr);
7145 CTRACE((tfp, "LYSystem(%s)\n", command));
7146 CTRACE_FLUSH(tfp);
7147
7148 #ifdef __DJGPP__
7149 __djgpp_set_ctrl_c(0);
7150 _go32_want_ctrl_break(1);
7151 #endif /* __DJGPP__ */
7152
7153 #ifdef VMS
7154 code = DCLsystem(command);
7155 #else
7156 # ifdef __EMX__ /* FIXME: Should be LY_CONVERT_SLASH? */
7157 /* Configure writes commands which contain direct slashes.
7158 Native command-(non)-shell will not tolerate this. */
7159 {
7160 char *space = command, *slash = command;
7161
7162 _scrsize(scrsize);
7163 while (*space && *space != ' ' && *space != '\t')
7164 space++;
7165 while (slash < space && *slash != '/')
7166 slash++;
7167 if (slash != space) {
7168 char *old = command;
7169
7170 command = NULL;
7171 StrAllocCopy(command, old);
7172 do_free = 1;
7173 slash = (slash - old) + command - 1;
7174 space = (space - old) + command;
7175 while (++slash < space)
7176 if (*slash == '/')
7177 *slash = '\\';
7178 }
7179 }
7180 # endif
7181
7182 /*
7183 * This chunk of code does not work, for two reasons:
7184 * a) the Cygwin system() function edits out the backslashes
7185 * b) it does not account for more than one parameter, e.g., +number
7186 */
7187 #if defined(__CYGWIN__) && defined(DOSPATH) /* 1999/02/26 (Fri) */
7188 {
7189 char cmd[LY_MAXPATH];
7190 char win32_name[LY_MAXPATH];
7191 char new_cmd[LY_MAXPATH];
7192 char new_command[LY_MAXPATH * 2 + 10];
7193 char *p, *q;
7194
7195 p = command;
7196 q = cmd;
7197 while (*p) {
7198 if (*p == ' ')
7199 break;
7200 else
7201 *q = *p;
7202 p++;
7203 q++;
7204 }
7205 *q = '\0';
7206
7207 if (cmd[0] == '/')
7208 cygwin_conv_to_full_posix_path(cmd, new_cmd);
7209 else
7210 strcpy(new_cmd, cmd);
7211
7212 while (*p == ' ')
7213 p++;
7214
7215 if (strchr(p, '\\') == NULL) {
7216 /* for Windows Application */
7217 cygwin_conv_to_full_win32_path(p, win32_name);
7218 sprintf(new_command, "%.*s \"%.*s\"",
7219 LY_MAXPATH, new_cmd, LY_MAXPATH, win32_name);
7220 } else {
7221 /* for DOS like editor */
7222 q = win32_name;
7223 while (*p) {
7224 if (*p == '\\') {
7225 if (*(p + 1) == '\\')
7226 p++;
7227 }
7228 *q = *p;
7229 q++, p++;
7230 }
7231 *q = '\0';
7232 sprintf(new_command, "%.*s %.*s", LY_MAXPATH, new_cmd, LY_MAXPATH, win32_name);
7233 }
7234 command = new_command;
7235 }
7236 #endif
7237
7238 #ifdef __DJGPP__
7239 if (dj_is_bash) {
7240 char *new_command = escape_backslashes(command);
7241
7242 if (new_command != 0) {
7243 if (do_free)
7244 free(command);
7245 command = new_command;
7246 }
7247 }
7248 #endif /* __DJGPP__ */
7249
7250 #ifdef _WIN_CC
7251 code = exec_command(command, TRUE); /* Wait exec */
7252 #else /* !_WIN_CC */
7253 #ifdef SIGPIPE
7254 if (restore_sigpipe_for_children)
7255 signal(SIGPIPE, SIG_DFL); /* Some commands expect the default */
7256 #endif
7257 #if defined(HAVE_SIGACTION) && defined(SIGTSTP) && !defined(USE_SLANG)
7258 if (!dump_output_immediately && !LYCursesON && !no_suspend)
7259 sigtstp_saved = LYToggleSigDfl(SIGTSTP, &saved_sigtstp_act, 1);
7260 #endif
7261 code = system(command);
7262 saved_errno = errno;
7263 #if defined(HAVE_SIGACTION) && defined(SIGTSTP) && !defined(USE_SLANG)
7264 if (sigtstp_saved)
7265 LYToggleSigDfl(SIGTSTP, &saved_sigtstp_act, 0);
7266 #endif
7267 #ifdef SIGPIPE
7268 if (restore_sigpipe_for_children)
7269 signal(SIGPIPE, SIG_IGN); /* Ignore it again - kw */
7270 #endif
7271 #endif
7272 #endif
7273
7274 #ifdef __DJGPP__
7275 __djgpp_set_ctrl_c(1);
7276 _go32_want_ctrl_break(0);
7277 #endif /* __DJGPP__ */
7278
7279 fflush(stdout);
7280 fflush(stderr);
7281
7282 if (do_free)
7283 FREE(command);
7284 #if !defined(UCX) || !defined(VAXC) /* errno not modifiable ?? */
7285 set_errno(saved_errno); /* may have been clobbered */
7286 #endif
7287 #ifdef __EMX__ /* Check whether the screen size changed */
7288 size_change(0);
7289 #endif
7290 return code;
7291 }
7292
7293 /*
7294 * Return a string which can be used in LYSystem() for spawning a subshell
7295 */
7296 #if defined(__CYGWIN__) /* 1999/02/26 (Fri) */
Cygwin_Shell(void)7297 int Cygwin_Shell(void)
7298 {
7299 char *shell;
7300 int code;
7301 STARTUPINFO startUpInfo;
7302 PROCESS_INFORMATION procInfo;
7303 SECURITY_ATTRIBUTES sa;
7304
7305 /* Set up security attributes to allow inheritance of the file handle */
7306
7307 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
7308 sa.lpSecurityDescriptor = 0;
7309 sa.bInheritHandle = TRUE;
7310
7311 /* Init a startup structure */
7312 GetStartupInfo(&startUpInfo);
7313
7314 shell = LYGetEnv("COMSPEC");
7315
7316 /* Create the child process, specifying
7317 inherited handles. Pass the value of the
7318 handle as a command line parameter */
7319 code = 0;
7320 if (shell) {
7321 code = CreateProcess(0, shell, 0, 0,
7322 TRUE, 0,
7323 0, 0, &startUpInfo, &procInfo);
7324
7325 if (!code) {
7326 printf("shell = [%s], code = %ld\n", shell, (long) GetLastError());
7327 }
7328
7329 /* wait for the child to return (this is not a requirement
7330 since the child is its own independent process) */
7331 WaitForSingleObject(procInfo.hProcess, INFINITE);
7332 }
7333
7334 return code;
7335 }
7336 #endif
7337
7338 #ifdef WIN_EX
7339 /*
7340 * Quote the path to make it safe for shell command processing.
7341 * We always quote it not only includes spaces in it.
7342 * At least we should quote paths which include "&".
7343 */
quote_pathname(char * pathname)7344 char *quote_pathname(char *pathname)
7345 {
7346 char *result = NULL;
7347
7348 HTSprintf0(&result, "\"%s\"", pathname);
7349 return result;
7350 }
7351 #endif
7352
LYSysShell(void)7353 const char *LYSysShell(void)
7354 {
7355 const char *shell = 0;
7356
7357 #ifdef DOSPATH
7358 #ifdef WIN_EX
7359 shell = LYGetEnv("SHELL");
7360 if (shell) {
7361 if (access(shell, 0) != 0)
7362 shell = LYGetEnv("COMSPEC");
7363 } else {
7364 shell = LYGetEnv("COMSPEC");
7365 }
7366 if (shell == NULL) {
7367 if (system_is_NT)
7368 shell = "cmd.exe";
7369 else
7370 shell = "command.com";
7371 }
7372 #else
7373 shell = LYGetEnv("SHELL");
7374 if (shell == NULL) {
7375 shell = LYGetEnv("COMSPEC");
7376 }
7377 if (shell == NULL) {
7378 shell = "command.com";
7379 }
7380 #endif /* WIN_EX */
7381 #else
7382 #ifdef __EMX__
7383 if (LYGetEnv("SHELL") != NULL) {
7384 shell = LYGetEnv("SHELL");
7385 } else {
7386 shell = (LYGetEnv("COMSPEC") == NULL) ? "cmd.exe" : LYGetEnv("COMSPEC");
7387 }
7388 #else
7389 #ifdef VMS
7390 shell = "";
7391 #else
7392 shell = "exec $SHELL";
7393 #endif /* __EMX__ */
7394 #endif /* VMS */
7395 #endif /* DOSPATH */
7396 return shell;
7397 }
7398
7399 #ifdef VMS
7400 #define DISPLAY "DECW$DISPLAY"
7401 #else
7402 #define DISPLAY "DISPLAY"
7403 #endif /* VMS */
7404
7405 /*
7406 * Return the X-Window $DISPLAY string if it is nonnull/nonempty
7407 */
LYgetXDisplay(void)7408 char *LYgetXDisplay(void)
7409 {
7410 return LYGetEnv(DISPLAY);
7411 }
7412
7413 /*
7414 * Set the value of the X-Window $DISPLAY variable (yes it leaks memory, but
7415 * that is putenv's fault).
7416 */
LYsetXDisplay(char * new_display)7417 void LYsetXDisplay(char *new_display)
7418 {
7419 if (new_display != 0) {
7420 #ifdef VMS
7421 LYUpperCase(new_display);
7422 Define_VMSLogical(DISPLAY, new_display);
7423 #else
7424 static char *display_putenv_command;
7425
7426 HTSprintf0(&display_putenv_command, "DISPLAY=%s", new_display);
7427 putenv(display_putenv_command);
7428 #endif /* VMS */
7429 if ((new_display = LYgetXDisplay()) != 0) {
7430 StrAllocCopy(x_display, new_display);
7431 }
7432 }
7433 }
7434
7435 #ifdef CAN_CUT_AND_PASTE
7436 #ifdef __EMX__
7437
7438 static int proc_type = -1;
7439 static PPIB pib;
7440 static HAB hab;
7441 static HMQ hmq;
7442
morph_PM(void)7443 static void morph_PM(void)
7444 {
7445 PTIB tib;
7446 int first = 0;
7447
7448 if (proc_type == -1) {
7449 DosGetInfoBlocks(&tib, &pib);
7450 proc_type = pib->pib_ultype;
7451 first = 1;
7452 }
7453 if (pib->pib_ultype != 3) /* 2 is VIO */
7454 pib->pib_ultype = 3; /* 3 is PM */
7455 if (first)
7456 hab = WinInitialize(0);
7457 /* 64 messages if before OS/2 3.0, ignored otherwise */
7458 hmq = WinCreateMsgQueue(hab, 64);
7459 WinCancelShutdown(hmq, 1); /* Do not inform us on shutdown */
7460 }
7461
unmorph_PM(void)7462 static void unmorph_PM(void)
7463 {
7464 WinDestroyMsgQueue(hmq);
7465 pib->pib_ultype = proc_type;
7466 }
7467
size_clip(void)7468 int size_clip(void)
7469 {
7470 return 8192;
7471 }
7472
7473 /* Code partially stolen from FED editor. */
7474
put_clip(const char * s)7475 int put_clip(const char *s)
7476 {
7477 int sz = strlen(s) + 1;
7478 int ret = EOF, nl = 0;
7479 char *pByte = 0, *s1 = s, c, *t;
7480
7481 while ((c = *s1++)) {
7482 if (c == '\r' && *s1 == '\n')
7483 s1++;
7484 else if (c == '\n')
7485 nl++;
7486 }
7487 if (DosAllocSharedMem((PPVOID) & pByte, 0, sz + nl,
7488 PAG_WRITE | PAG_COMMIT | OBJ_GIVEABLE | OBJ_GETTABLE))
7489 return ret;
7490
7491 if (!nl)
7492 memcpy(pByte, s, sz);
7493 else {
7494 t = pByte;
7495 while ((c = *t++ = *s++))
7496 if (c == '\n' && (t == pByte + 1 || t[-2] != '\r'))
7497 t[-1] = '\r', *t++ = '\n';
7498 }
7499
7500 morph_PM();
7501 if (!hab)
7502 goto fail;
7503
7504 WinOpenClipbrd(hab);
7505 WinEmptyClipbrd(hab);
7506 if (WinSetClipbrdData(hab, (ULONG) pByte, CF_TEXT, CFI_POINTER))
7507 ret = 0;
7508 WinCloseClipbrd(hab);
7509 unmorph_PM();
7510 if (ret == 0)
7511 return 0;
7512 fail:
7513 DosFreeMem((PPVOID) & pByte);
7514 return EOF;
7515 }
7516
7517 static int clip_open;
7518
7519 /* get_clip_grab() returns a pointer to the string in the system area.
7520 get_clip_release() should be called ASAP after this. */
7521
get_clip_grab(void)7522 char *get_clip_grab(void)
7523 {
7524 char *ClipData;
7525 ULONG ulFormat;
7526 int sz;
7527
7528 morph_PM();
7529 if (!hab)
7530 return 0;
7531 if (clip_open)
7532 get_clip_release();
7533
7534 WinQueryClipbrdFmtInfo(hab, CF_TEXT, &ulFormat);
7535 if (ulFormat != CFI_POINTER) {
7536 unmorph_PM();
7537 return 0;
7538 }
7539 WinOpenClipbrd(hab);
7540 clip_open = 1;
7541 ClipData = (char *) WinQueryClipbrdData(hab, CF_TEXT);
7542 sz = strlen(ClipData);
7543 if (!ClipData || !sz) {
7544 get_clip_release();
7545 return 0;
7546 }
7547 return ClipData;
7548 }
7549
get_clip_release(void)7550 void get_clip_release(void)
7551 {
7552 if (!clip_open)
7553 return;
7554 WinCloseClipbrd(hab);
7555 clip_open = 0;
7556 unmorph_PM();
7557 }
7558
7559 #else /* !( defined __EMX__ ) */
7560
7561 # if !defined(WIN_EX) && defined(HAVE_POPEN)
7562
7563 static FILE *paste_handle = 0;
7564 static char *paste_buf = NULL;
7565
get_clip_release(void)7566 void get_clip_release(void)
7567 {
7568 if (paste_handle != 0)
7569 pclose(paste_handle);
7570 if (paste_buf)
7571 FREE(paste_buf);
7572 }
7573
clip_grab(void)7574 static int clip_grab(void)
7575 {
7576 char *cmd = LYGetEnv("RL_PASTE_CMD");
7577
7578 if (paste_handle)
7579 pclose(paste_handle);
7580 if (!cmd)
7581 return 0;
7582
7583 paste_handle = popen(cmd, TXT_R);
7584 if (!paste_handle)
7585 return 0;
7586 return 1;
7587 }
7588
7589 #define PASTE_BUFFER 1008
7590 #define CF_TEXT 0 /* Not used */
7591
get_clip_grab(void)7592 char *get_clip_grab(void)
7593 {
7594 int len;
7595 unsigned size = PASTE_BUFFER;
7596 int off = 0;
7597
7598 if (!clip_grab())
7599 return NULL;
7600 if (!paste_handle)
7601 return NULL;
7602 if (paste_buf)
7603 FREE(paste_buf);
7604 paste_buf = typeMallocn(char, PASTE_BUFFER);
7605
7606 while (1) {
7607 len = (int) fread(paste_buf + off,
7608 (size_t) 1,
7609 (size_t) PASTE_BUFFER - 1,
7610 paste_handle);
7611 paste_buf[off + len] = '\0';
7612 if (len < PASTE_BUFFER - 1)
7613 break;
7614 if (strchr(paste_buf + off, '\r')
7615 || strchr(paste_buf + off, '\n'))
7616 break;
7617 paste_buf = typeRealloc(char, paste_buf, size += PASTE_BUFFER - 1);
7618
7619 off += len;
7620 }
7621 return paste_buf;
7622 }
7623
put_clip(const char * s)7624 int put_clip(const char *s)
7625 {
7626 char *cmd = LYGetEnv("RL_CLCOPY_CMD");
7627 FILE *fh;
7628 size_t l = strlen(s), res;
7629
7630 if (!cmd)
7631 return -1;
7632
7633 fh = popen(cmd, TXT_W);
7634 if (!fh)
7635 return -1;
7636 res = fwrite(s, (size_t) 1, l, fh);
7637 if (pclose(fh) != 0 || res != l)
7638 return -1;
7639 return 0;
7640 }
7641
7642 # endif /* !defined(WIN_EX) && defined(HAVE_POPEN) */
7643
7644 #endif /* __EMX__ */
7645
7646 #define WANT_LYmsec_delay
7647
7648 #if defined(WIN_EX) /* 1997/10/16 (Thu) 20:13:28 */
7649
put_clip(const char * szBuffer)7650 int put_clip(const char *szBuffer)
7651 {
7652 HANDLE hWnd;
7653 HANDLE m_hLogData;
7654 LPTSTR pLogData;
7655 HANDLE hClip;
7656 int len;
7657
7658 if (szBuffer == NULL)
7659 return EOF;
7660
7661 len = strlen(szBuffer);
7662 if (len == 0)
7663 return EOF;
7664 else
7665 len++;
7666
7667 m_hLogData = GlobalAlloc(GHND, len);
7668 if (m_hLogData == NULL) {
7669 return EOF;
7670 }
7671
7672 hWnd = NULL;
7673 if (!OpenClipboard(hWnd)) {
7674 return EOF;
7675 }
7676 /* Remove the current Clipboard contents */
7677 if (!EmptyClipboard()) {
7678 GlobalFree(m_hLogData);
7679 return EOF;
7680 }
7681
7682 /* Lock the global memory while we write to it. */
7683 pLogData = (LPTSTR) GlobalLock(m_hLogData);
7684
7685 lstrcpy((LPTSTR) pLogData, szBuffer);
7686 GlobalUnlock(m_hLogData);
7687
7688 /* If there were any lines at all then copy them to clipboard. */
7689 hClip = SetClipboardData(CF_TEXT, m_hLogData);
7690 if (!hClip) {
7691 /* If we couldn't clip the data then free the global handle. */
7692 GlobalFree(m_hLogData);
7693 }
7694
7695 CloseClipboard();
7696 return 0;
7697 }
7698
7699 static HANDLE m_hLogData;
7700 static int m_locked;
7701
7702 /* get_clip_grab() returns a pointer to the string in the system area.
7703 get_clip_release() should be called ASAP after this. */
7704
get_clip_grab()7705 char *get_clip_grab()
7706 {
7707 HANDLE hWnd;
7708 LPTSTR pLogData;
7709
7710 hWnd = NULL;
7711 if (!OpenClipboard(hWnd)) {
7712 return 0;
7713 }
7714
7715 m_hLogData = GetClipboardData(CF_TEXT);
7716
7717 if (m_hLogData == NULL) {
7718 CloseClipboard();
7719 m_locked = 0;
7720 return 0;
7721 }
7722 pLogData = (LPTSTR) GlobalLock(m_hLogData);
7723
7724 m_locked = 1;
7725 return pLogData;
7726 }
7727
get_clip_release()7728 void get_clip_release()
7729 {
7730 if (!m_locked)
7731 return;
7732 GlobalUnlock(m_hLogData);
7733 CloseClipboard();
7734 m_locked = 0;
7735 }
7736 #endif /* WIN_EX */
7737 #endif /* CAN_CUT_AND_PASTE */
7738
7739 #if defined(WIN_EX)
7740
7741 #ifndef WSABASEERR
7742 #define WSABASEERR 10000
7743 #endif
7744
7745 #ifdef ENABLE_IPV6
7746 #define WSOCK_NAME "ws2_32"
7747 #else
7748 #define WSOCK_NAME "wsock32"
7749 #endif
7750
7751 /*
7752 * Description: the windows32 version of perror()
7753 *
7754 * Returns: a pointer to a static error
7755 *
7756 * Notes/Dependencies: I got this from
7757 * comp.os.ms-windows.programmer.win32
7758 */
w32_strerror(DWORD ercode)7759 char *w32_strerror(DWORD ercode)
7760 {
7761 /* __declspec(thread) necessary if you will use multiple threads */
7762 #ifdef __CYGWIN__
7763 static char msg_buff[256];
7764
7765 #else
7766 __declspec(thread) static char msg_buff[256];
7767 #endif
7768 HMODULE hModule;
7769 int i, msg_type;
7770 unsigned char *p, *q, tmp_buff[256];
7771 DWORD rc;
7772
7773 hModule = NULL;
7774 msg_type = FORMAT_MESSAGE_FROM_SYSTEM;
7775 /* Fill message buffer with a default message in
7776 * case FormatMessage fails
7777 */
7778 wsprintf(msg_buff, "Error %ld", ercode);
7779
7780 /*
7781 * Special code for winsock error handling.
7782 */
7783 if (ercode > WSABASEERR) {
7784 hModule = GetModuleHandle(WSOCK_NAME);
7785 if (hModule)
7786 msg_type = FORMAT_MESSAGE_FROM_HMODULE;
7787 }
7788 /*
7789 * message handling. If not found in module, retry from system.
7790 */
7791 rc = FormatMessage(msg_type, hModule, ercode, LANG_NEUTRAL,
7792 msg_buff, sizeof(msg_buff), NULL);
7793
7794 if (rc == 0 && msg_type == FORMAT_MESSAGE_FROM_HMODULE) {
7795 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, ercode,
7796 LANG_NEUTRAL, msg_buff, sizeof(msg_buff), NULL);
7797 }
7798
7799 strcpy((char *) tmp_buff, msg_buff);
7800 p = q = tmp_buff;
7801 i = 0;
7802 while (*p) {
7803 if (!(*p == '\n' || *p == '\r'))
7804 msg_buff[i++] = *p;
7805 p++;
7806 }
7807 msg_buff[i] = '\0';
7808
7809 return msg_buff;
7810 }
7811
7812 #endif
7813
7814 #if defined(SYSLOG_REQUESTED_URLS)
7815 /*
7816 * syslog() interface
7817 */
LYOpenlog(const char * banner)7818 void LYOpenlog(const char *banner)
7819 {
7820 if (syslog_requested_urls) {
7821 CTRACE((tfp, "LYOpenlog(%s)\n", NONNULL(banner)));
7822 #if defined(DJGPP)
7823 openlog("lynx", LOG_PID | LOG_NDELAY, LOG_LOCAL5);
7824 #else
7825 openlog("lynx", LOG_PID, LOG_LOCAL5);
7826 #endif
7827
7828 if (banner) {
7829 syslog(LOG_INFO, "Session start:%s", banner);
7830 } else {
7831 syslog(LOG_INFO, "Session start");
7832 }
7833 }
7834 }
7835
looks_like_password(char * first,char * last)7836 static BOOLEAN looks_like_password(char *first,
7837 char *last)
7838 {
7839 BOOLEAN result = FALSE;
7840
7841 while (first <= last) {
7842 if (*first == '/'
7843 || *first == ':') {
7844 result = FALSE;
7845 break;
7846 }
7847 result = TRUE;
7848 first++;
7849 }
7850 return result;
7851 }
7852
LYSyslog(char * arg)7853 void LYSyslog(char *arg)
7854 {
7855 char *colon1;
7856 char *colon2;
7857 char *atsign;
7858
7859 if (syslog_requested_urls) {
7860
7861 CTRACE((tfp, "LYSyslog %s\n", arg));
7862
7863 if (is_url(arg)) { /* proto://user:password@host/path:port */
7864 /* ^this colon */
7865 if ((colon1 = strchr(arg, ':')) != 0
7866 && !StrNCmp(colon1, "://", 3)
7867 && (colon2 = strchr(colon1 + 3, ':')) != 0
7868 && (atsign = strchr(colon1, '@')) != 0
7869 && (colon2 < atsign)
7870 && looks_like_password(colon2 + 1, atsign - 1)) {
7871 char *buf = NULL;
7872
7873 StrAllocCopy(buf, arg);
7874 buf[colon2 - arg + 1] = 0;
7875 StrAllocCat(buf, "******");
7876 StrAllocCat(buf, atsign);
7877 syslog(LOG_INFO | LOG_LOCAL5, "%s", buf);
7878 CTRACE((tfp, "...alter %s\n", buf));
7879 FREE(buf);
7880 return;
7881 }
7882 }
7883 syslog(LOG_INFO | LOG_LOCAL5, "%s", NONNULL(arg));
7884 }
7885 }
7886
LYCloselog(void)7887 void LYCloselog(void)
7888 {
7889 if (syslog_requested_urls) {
7890 syslog(LOG_INFO, "Session over");
7891 closelog();
7892 }
7893 }
7894
7895 #endif /* SYSLOG_REQUESTED_URLS */
7896
7897 #if defined(WANT_LYmsec_delay) || defined(USE_MOUSE)
7898 /*
7899 * Sleep for a number of milli-sec.
7900 */
LYmsec_delay(unsigned msec)7901 void LYmsec_delay(unsigned msec)
7902 {
7903 #if defined(_WINDOWS)
7904 Sleep(msec);
7905
7906 #elif defined(HAVE_NAPMS)
7907 napms((int) msec);
7908
7909 #elif defined(DJGPP) || defined(HAVE_USLEEP)
7910 usleep(1000 * msec);
7911
7912 #else
7913 struct timeval tv;
7914 unsigned long usec = 1000UL * msec;
7915
7916 tv.tv_sec = usec / 1000000UL;
7917 tv.tv_usec = usec % 1000000UL;
7918 select(0, NULL, NULL, NULL, &tv);
7919 #endif
7920 }
7921 #endif /* WANT_LYmsec_delay || USE_MOUSE */
7922
7923 #if defined(WIN_EX) || defined(__CYGWIN__) /* 2000/03/07 (Tue) 17:17:46 */
7924
7925 #define IS_SEP(p) ((p == '\\') || (p == '/') || (p == ':'))
7926
7927 static char *black_list[] =
7928 {
7929 "con",
7930 "prn",
7931 "clock$",
7932 "config$",
7933 NULL
7934 };
7935
is_device(char * fname)7936 static int is_device(char *fname)
7937 {
7938 HANDLE fileHandle;
7939 DWORD val;
7940 int i;
7941
7942 i = 0;
7943 while (black_list[i] != NULL) {
7944 if (stricmp(fname, black_list[i]) == 0) {
7945 return 1; /* device file */
7946 }
7947 i++;
7948 }
7949
7950 fileHandle = CreateFile(fname, 0, 0, 0, OPEN_EXISTING, 0, 0);
7951
7952 if (fileHandle == INVALID_HANDLE_VALUE) {
7953 return 0; /* normal file */
7954 } else {
7955 val = GetFileType(fileHandle);
7956 switch (val) {
7957 case 1:
7958 val = 0;
7959 break;
7960 case 2:
7961 val = 1; /* device file */
7962 break;
7963 default:
7964 val = 0;
7965 break;
7966 }
7967
7968 CloseHandle(fileHandle);
7969 }
7970 return val;
7971 }
7972
7973 static char *device_list[] =
7974 {
7975 "con",
7976 "nul",
7977 "aux",
7978 "prn",
7979 NULL
7980 };
7981
unsafe_filename(const char * fname)7982 int unsafe_filename(const char *fname)
7983 {
7984 int i, len, sum;
7985 char *cp;
7986 char *save;
7987
7988 i = 0;
7989 while (device_list[i] != NULL) {
7990 if (stricmp(fname, device_list[i]) == 0) {
7991 return 0; /* device file (open OK) */
7992 }
7993 i++;
7994 }
7995
7996 save = cp = strdup(fname);
7997
7998 while (*cp) {
7999 if (IS_SJIS_HI1(UCH(*cp)) || IS_SJIS_HI2(UCH(*cp)))
8000 cp += 2; /* KANJI skip */
8001 if (IS_SEP(*cp)) {
8002 *cp = '\0';
8003 }
8004 cp++;
8005 }
8006
8007 sum = 0;
8008 cp = save;
8009 len = strlen(fname);
8010 while (cp < (save + len)) {
8011 if (*cp == '\0') {
8012 cp++;
8013 } else {
8014 char *q;
8015
8016 q = strchr(cp, '.');
8017 if (q)
8018 *q = '\0';
8019 if (is_device(cp)) {
8020 sum++;
8021 break;
8022 }
8023 if (q)
8024 cp = q + 1;
8025 while (*cp)
8026 cp++;
8027 }
8028 }
8029 free(save);
8030
8031 return (sum != 0);
8032 }
8033
safe_fopen(const char * fname,const char * mode)8034 FILE *safe_fopen(const char *fname, const char *mode)
8035 {
8036 if (unsafe_filename(fname)) {
8037 return (FILE *) NULL;
8038 } else {
8039 return fopen(fname, mode);
8040 }
8041 }
8042
8043 #endif /* defined(WIN_EX) || defined(__CYGWIN__) */
8044