13
#endif /* __MINGW32__ */
16
#include <LYHistory.h>
17
#include <LYStrings.h>
18
#include <LYGlobalDefs.h>
23
#include <LYCharSets.h>
24
#include <LYCharUtils.h>
26
#include <LYMainLoop.h>
31
#include <sys/exceptn.h>
32
#endif /* __DJGPP__ */
39
extern int exec_command(char * cmd, int wait_flag); /* xsystem.c */
42
#ifdef _WINDOWS /* 1998/04/30 (Thu) 19:04:25 */
43
#define GETPID() (getpid() & 0xffff)
45
#define GETPID() getpid()
48
#ifdef DJGPP_KEYHANDLER
50
#endif /* DJGPP_KEYHANDLER */
53
# define BOOLEAN OS2_BOOLEAN /* Conflicts, but is used */
54
# undef HT_ERROR /* Conflicts too */
55
# define INCL_PM /* I want some PM functions.. */
56
# define INCL_DOSPROCESS /* TIB PIB. */
63
#include <libclidef.h>
64
#include <lib$routines.h>
75
#endif /* UTMP_FILE */
76
#define UTMP_FILE UTMPX_FILE
79
#define UTMP_FILE __UTMPX_FILE /* at least in OS/390 S/390 -- gil -- 2100 */
81
#define UTMP_FILE "/var/adm/utmpx" /* Digital Unix 4.0 */
83
#endif /* UTMPX_FILE */
86
#endif /* UTMPX_FOR_UTMP */
87
#endif /* HAVE_UTMP */
90
/* they neglected to define struct winsize in termios.h -- it's only in
91
* termio.h and ptem.h (the former conflicts with other definitions).
93
#include <sys/stream.h>
99
#ifdef USE_COLOR_STYLE
100
#include <AttrList.h>
105
#ifdef SVR4_BSDSELECT
106
extern int BSDselect PARAMS((int nfds, fd_set * readfds, fd_set * writefds,
107
fd_set * exceptfds, struct timeval * timeout));
111
#define select BSDselect
116
#define Rselect BSDselect
118
#endif /* SVR4_BSDSELECT */
121
#undef select /* defined to select_s in www_tcp.h */
125
#if defined(__FreeBSD__) || defined(__bsdi__)
126
#define UTMP_FILE _PATH_UTMP
128
#define UTMP_FILE "/etc/utmp"
129
#endif /* __FreeBSD__ || __bsdi__ */
130
#endif /* !UTMP_FILE */
133
* experimental - make temporary filenames random to make the scheme less
134
* obvious. However, as noted by KW, there are instances (such as the
135
* 'O'ption page, for which Lynx will store a temporary filename even when
136
* it no longer applies, since it will reuse that filename at a later time.
138
#ifdef EXP_RAND_TEMPNAME
139
#if defined(LYNX_RAND_MAX)
140
#define USE_RAND_TEMPNAME 1
141
#define MAX_TEMPNAME 10000
142
#ifndef BITS_PER_CHAR
143
#define BITS_PER_CHAR 8
148
#define COPY_COMMAND "%s %s %s"
150
PRIVATE HTList * localhost_aliases = NULL; /* Hosts to treat as local */
151
PRIVATE char *HomeDir = NULL; /* HOME directory */
153
PUBLIC HTList * sug_filenames = NULL; /* Suggested filenames */
156
* Maintain a list of all of the temp-files we create so that we can remove
157
* them during the cleanup.
159
typedef struct _LYTemp {
160
struct _LYTemp *next;
166
PRIVATE LY_TEMP *ly_temp;
168
PRIVATE LY_TEMP *FindTempfileByName ARGS1(CONST char *, name)
172
for (p = ly_temp; p != 0; p = p->next) {
173
if (!strcmp(p->name, name)) {
180
PRIVATE LY_TEMP *FindTempfileByFP ARGS1(FILE *, fp)
184
for (p = ly_temp; p != 0; p = p->next) {
193
* Get an environment variable, rejecting empty strings
195
PUBLIC char *LYGetEnv ARGS1(CONST char *, name)
197
char *result = getenv(name);
198
return non_empty(result) ? result : 0;
202
* ascii versions of locale sensitive functions needed because in
203
* Turkish locales tolower("I") is not "i". That's fatal for case
204
* sensitive operations with charset names, HTML tags etc.
206
#ifdef EXP_ASCII_CTYPES
207
PUBLIC int ascii_tolower ARGS1(int, i)
209
if ( 91 > i && i > 64 )
215
PUBLIC int ascii_toupper ARGS1(int, i)
217
if ( 123 > i && i > 96 )
223
PUBLIC int ascii_isupper ARGS1(int, i)
225
if ( 91 > i && i > 64 )
230
#endif /* EXP_ASCII_CTYPES */
233
* Check for UTF-8 data, returning the length past the first character.
234
* Return zero if we found an ordinary character rather than UTF-8.
236
PUBLIC size_t utf8_length ARGS2(
240
size_t utf_extra = 0;
242
if (utf_flag && is8bits(*data)) {
243
if ((*data & 0xe0) == 0xc0) {
245
} else if ((*data & 0xf0) == 0xe0) {
247
} else if ((*data & 0xf8) == 0xf0) {
249
} else if ((*data & 0xfc) == 0xf8) {
251
} else if ((*data & 0xfe) == 0xfc) {
259
if (strlen(data+1) < utf_extra) {
270
* Set the initial highlight information for a given link.
272
PUBLIC void LYSetHilite ARGS2(
276
links[cur].list.hl_base.hl_text = text;
277
links[cur].list.hl_len = (text != NULL) ? 1 : 0;
278
FREE(links[cur].list.hl_info);
282
* Add highlight information for the next line of a link.
284
PUBLIC void LYAddHilite ARGS3(
289
HiliteList *list = &(links[cur].list);
290
HiliteInfo *have = list->hl_info;
291
unsigned need = (list->hl_len - 1);
292
unsigned want = (list->hl_len += 1) * sizeof(HiliteInfo);
295
have = realloc(have, want);
299
list->hl_info = have;
300
have[need].hl_text = text;
305
* Get the highlight text, counting from zero.
307
PUBLIC char *LYGetHiliteStr ARGS2(
313
if (count >= links[cur].list.hl_len)
316
result = links[cur].list.hl_info[count - 1].hl_text;
318
result = links[cur].list.hl_base.hl_text;
323
* Get the X-ordinate at which to draw the corresponding highlight-text
325
PUBLIC int LYGetHilitePos ARGS2(
331
if (count >= links[cur].list.hl_len)
334
result = links[cur].list.hl_info[count - 1].hl_x;
336
result = links[cur].lx;
340
#define LXP (links[cur].lx)
341
#define LYP (links[cur].ly)
343
#ifdef SHOW_WHEREIS_TARGETS
345
#define SKIP_GLYPHS(theFlag, theData, theOffset) \
347
? LYmbcs_skip_glyphs(theData, (theOffset), theFlag) \
348
: (theData + (theOffset)))
351
* If we have an emphasized WHEREIS hit in the highlighted text, restore the
352
* emphasis. Note that we never emphasize the first and last characters of the
353
* highlighted text when we are making the link current, so the link attributes
354
* for the current link will persist at the beginning and end, providing an
355
* indication to the user that it has been made current. Also note that we use
356
* HText_getFirstTargetInLine() to determine if there's a hit in the HText
357
* structure line containing the link, and if so, get back a copy of the line
358
* starting at that first hit (which might be before or after our link), and
359
* with all IsSpecial characters stripped, so we don't need to deal with them
362
PRIVATE BOOL show_whereis_targets ARGS6(
367
BOOL, TargetEmphasisON,
372
char *theData = NULL;
373
char buffer[MAX_LINE];
380
tmp[0] = tmp[1] = tmp[2] = '\0';
382
if (non_empty(target)
383
&& (links[cur].type & WWW_LINK_TYPE)
384
&& non_empty(LYGetHiliteStr(cur, count))
385
&& links[cur].ly + count < display_lines
386
&& HText_getFirstTargetInLine(HTMainText,
387
links[cur].anchor_line_num + count,
393
int itmp, written, len, y, offset;
395
int tlen = strlen(target);
397
int hLine = links[cur].ly + count;
398
int hoffset = LYGetHilitePos(cur, count);
399
size_t utf_extra = 0;
402
* Copy into the buffer only what will fit up to the right border of
405
LYmbcsstrncpy(buffer,
406
(LYGetHiliteStr(cur, count) ?
407
LYGetHiliteStr(cur, count) : ""),
408
(sizeof(buffer) - 1),
409
((LYcols - 1) - LYGetHilitePos(cur, count)),
411
hlen = strlen(buffer);
412
hLen = ((HTCJK != NOCJK || utf_flag) ?
413
LYmbcsstrlen(buffer, utf_flag, YES) : hlen);
416
* Break out if the first hit in the line starts after this link. -FM
418
if (Offset < (hoffset + hLen)) {
420
* Recursively skip hits that end before this link, and break out
421
* if there is no hit beyond those. -FM
424
while ((Offset < hoffset) &&
425
((Offset + tLen) <= hoffset)) {
426
data = (Data + tlen);
427
offset = (Offset + tLen);
428
if (((cp = LYno_attr_mb_strstr(data,
432
&LenNeeded)) != NULL)
433
&& (offset + LenNeeded) < LYcols) {
435
Offset = (offset + HitOffset);
437
goto highlight_search_done;
444
* If the hit starts before the hightext, and ends in or beyond the
445
* hightext, restore the emphasis, skipping the first and last
446
* characters of the hightext if we're making the link current.
449
if ((Offset < offset) &&
450
((Offset + tLen) > offset)) {
453
len = (tlen - (offset - Offset));
456
* Go to the start of the hightext and handle its first
459
LYmove(hLine, offset);
461
utf_extra = utf8_length(utf_flag, data + itmp);
463
LYstrncpy(&tmp[1], &data[itmp+1], utf_extra);
466
* Start emphasis immediately if we are making the link
470
LYstartTargetEmphasis();
471
TargetEmphasisON = TRUE;
474
LYmove(hLine, (offset + 1));
477
written += (utf_extra + 1);
479
} else if (HTCJK != NOCJK && is8bits(tmp[0])) {
481
* For CJK strings, by Masanobu Kimura.
483
tmp[1] = data[++itmp];
485
* Start emphasis immediately if we are making the link
489
LYstartTargetEmphasis();
490
TargetEmphasisON = TRUE;
493
LYmove(hLine, (offset + 1));
499
* Start emphasis immediately if we are making the link
503
LYstartTargetEmphasis();
504
TargetEmphasisON = TRUE;
507
LYmove(hLine, (offset + 1));
513
* Start emphasis after the first character if we are making
514
* the link current and this is not the last character. -FM
516
if (!TargetEmphasisON &&
517
data[itmp] != '\0') {
518
LYstartTargetEmphasis();
519
TargetEmphasisON = TRUE;
523
* Handle the remaining characters. -FM
526
written < len && (tmp[0] = data[itmp]) != '\0';
529
* Print all the other target chars, except the last
530
* character if it is also the last character of hightext
531
* and we are making the link current. -FM
533
utf_extra = utf8_length(utf_flag, data + itmp);
535
LYstrncpy(&tmp[1], &data[itmp+1], utf_extra);
538
* Make sure we don't restore emphasis to the last
539
* character of hightext if we are making the link
542
if (flag == ON && data[(itmp + 1)] == '\0') {
543
LYstopTargetEmphasis();
544
TargetEmphasisON = FALSE;
546
LYmove(hLine, (offset + 1));
551
written += (utf_extra + 1);
553
} else if (HTCJK != NOCJK && is8bits(tmp[0])) {
555
* For CJK strings, by Masanobu Kimura.
557
tmp[1] = data[++itmp];
559
* Make sure we don't restore emphasis to the last
560
* character of hightext if we are making the link
563
if (flag == ON && data[(itmp + 1)] == '\0') {
564
LYstopTargetEmphasis();
565
TargetEmphasisON = FALSE;
567
LYmove(hLine, (offset + 1));
575
* Make sure we don't restore emphasis to the last
576
* character of hightext if we are making the link
579
if (flag == ON && data[(itmp + 1)] == '\0') {
580
LYstopTargetEmphasis();
581
TargetEmphasisON = FALSE;
583
LYmove(hLine, (offset + 1));
592
* Stop the emphasis if we haven't already, then reset the
593
* offset to our current position in the line, and if that is
594
* beyond the link, or or we are making the link current and it
595
* is the last character of the hightext, we are done. -FM
597
if (TargetEmphasisON) {
598
LYstopTargetEmphasis();
599
TargetEmphasisON = FALSE;
602
if (offset < (hoffset + (flag == ON ? (hLen - 1) : hLen))
604
* See if we have another hit that starts within the
607
&& ((cp = LYno_attr_mb_strstr(data = SKIP_GLYPHS(utf_flag, Data, offset - Offset),
611
&LenNeeded)) != NULL)
612
&& (offset + LenNeeded) < LYcols
614
* If the hit starts after the end of the hightext, or we
615
* are making the link current and the hit starts at its
616
* last character, we are done. -FM
618
&& (HitOffset + offset) <
620
(flag == ON ? (hLen - 1) : hLen))) {
622
* Set up the data and offset for the hit, and let the code
623
* for within hightext hits handle it. -FM
626
Offset = (offset + HitOffset);
629
goto highlight_hit_within_hightext;
631
goto highlight_search_done;
634
highlight_hit_within_hightext:
636
* If we get to here, the hit starts within the hightext. If we
637
* are making the link current and it's the last character in the
638
* hightext, we are done. Otherwise, move there and start
639
* restoring the emphasis. -FM
641
if ((Offset - offset) <= (flag == ON ? (hLen - 1) : hLen)) {
642
data = SKIP_GLYPHS(utf_flag, data, Offset - offset);
652
* Go to the start of the hit and handle its first character.
655
LYmove(hLine, offset);
657
utf_extra = utf8_length(utf_flag, data + itmp);
659
LYstrncpy(&tmp[1], &data[itmp+1], utf_extra);
662
* Start emphasis immediately if we are making the link
663
* non-current, or we are making it current but this is not
664
* the first or last character of the hightext. -FM
667
(offset > hoffset && data[itmp+1] != '\0')) {
668
LYstartTargetEmphasis();
669
TargetEmphasisON = TRUE;
672
LYmove(hLine, (offset + 1));
675
written += (utf_extra + 1);
677
} else if (HTCJK != NOCJK && is8bits(tmp[0])) {
679
* For CJK strings, by Masanobu Kimura.
681
tmp[1] = data[++itmp];
683
* Start emphasis immediately if we are making the link
684
* non-current, or we are making it current but this is not
685
* the first or last character of the hightext. -FM
688
(offset > hoffset && data[itmp+1] != '\0')) {
689
LYstartTargetEmphasis();
690
TargetEmphasisON = TRUE;
693
LYmove(hLine, (offset + 2));
699
* Start emphasis immediately if we are making the link
700
* non-current, or we are making it current but this is not
701
* the first or last character of the hightext. -FM
704
(offset > hoffset && data[itmp+1] != '\0')) {
705
LYstartTargetEmphasis();
706
TargetEmphasisON = TRUE;
709
LYmove(hLine, (offset + 1));
715
* Start emphasis after the first character if we are making
716
* the link current and this is not the last character. -FM
718
if (!TargetEmphasisON &&
719
data[itmp] != '\0') {
720
LYstartTargetEmphasis();
721
TargetEmphasisON = TRUE;
725
written < len && (tmp[0] = data[itmp]) != '\0';
728
* Print all the other target chars, except the last
729
* character if it is also the last character of hightext
730
* and we are making the link current. -FM
732
utf_extra = utf8_length(utf_flag, data + itmp);
734
LYstrncpy(&tmp[1], &data[itmp+1], utf_extra);
737
* Make sure we don't restore emphasis to the last
738
* character of hightext if we are making the link
741
if (flag == ON && data[(itmp + 1)] == '\0') {
742
LYstopTargetEmphasis();
743
TargetEmphasisON = FALSE;
745
LYmove(hLine, (offset + 1));
750
written += (utf_extra + 1);
752
} else if (HTCJK != NOCJK && is8bits(tmp[0])) {
754
* For CJK strings, by Masanobu Kimura.
756
tmp[1] = data[++itmp];
758
* Make sure we don't restore emphasis to the last
759
* character of hightext if we are making the link
762
if (flag == ON && data[(itmp + 1)] == '\0') {
763
LYstopTargetEmphasis();
764
TargetEmphasisON = FALSE;
766
LYmove(hLine, (offset + 1));
774
* Make sure we don't restore emphasis to the last
775
* character of hightext if we are making the link
778
if (flag == ON && data[(itmp + 1)] == '\0') {
779
LYstopTargetEmphasis();
780
TargetEmphasisON = FALSE;
782
LYmove(hLine, (offset + 1));
791
* Stop the emphasis if we haven't already, then reset the
792
* offset to our current position in the line, and if that is
793
* beyond the link, or we are making the link current and it is
794
* the last character in the hightext, we are done. -FM
796
if (TargetEmphasisON) {
797
LYstopTargetEmphasis();
798
TargetEmphasisON = FALSE;
801
if (offset < (hoffset + (flag == ON ? (hLen - 1) : hLen))
803
* See if we have another hit that starts within the
806
&& ((cp = LYno_attr_mb_strstr(data = SKIP_GLYPHS(utf_flag, Data, offset - Offset),
810
&LenNeeded)) != NULL)
811
&& (offset + LenNeeded) < LYcols
813
* If the hit starts after the end of the hightext, or we
814
* are making the link current and the hit starts at its
815
* last character, we are done. -FM
817
&& (HitOffset + offset) < (hoffset + (flag == ON ? (hLen - 1) : hLen))) {
819
* If the target extends beyond our buffer, emphasize
820
* everything in the hightext starting at this hit.
821
* Otherwise, set up the data and offsets, and loop back.
824
if ((HitOffset + (offset + tLen)) >= (hoffset + hLen)) {
825
offset = (HitOffset + offset);
826
data = SKIP_GLYPHS(utf_flag, Data, offset - hoffset);
830
LYmove(hLine, offset);
836
* Turn the emphasis back on. -FM
838
LYstartTargetEmphasis();
839
TargetEmphasisON = TRUE;
841
written < len && (tmp[0] = data[itmp]) != '\0';
844
* Print all the other target chars, except the
845
* last character if it is also the last character
846
* of hightext and we are making the link current.
849
utf_extra = utf8_length(utf_flag, data + itmp);
851
LYstrncpy(&tmp[1], &data[itmp+1], utf_extra);
854
* Make sure we don't restore emphasis to the
855
* last character of hightext if we are making
856
* the link current. -FM
858
if (flag == ON && data[(itmp + 1)] == '\0') {
859
LYstopTargetEmphasis();
860
TargetEmphasisON = FALSE;
862
LYmove(hLine, (offset + 1));
867
written += (utf_extra + 1);
869
} else if (HTCJK != NOCJK && is8bits(tmp[0])) {
871
* For CJK strings, by Masanobu Kimura.
873
tmp[1] = data[++itmp];
875
* Make sure we don't restore emphasis to the
876
* last character of hightext if we are making
877
* the link current. -FM
879
if (flag == ON && data[(itmp + 1)] == '\0') {
880
LYstopTargetEmphasis();
881
TargetEmphasisON = FALSE;
889
* Make sure we don't restore emphasis to the
890
* last character of hightext if we are making
891
* the link current. -FM
893
if (flag == ON && data[(itmp + 1)] == '\0') {
894
LYstopTargetEmphasis();
895
TargetEmphasisON = FALSE;
903
* Turn off the emphasis if we haven't already, and
904
* then we're done. -FM
906
if (TargetEmphasisON) {
907
LYstopTargetEmphasis();
911
Offset = (offset + HitOffset);
914
goto highlight_hit_within_hightext;
920
highlight_search_done:
922
return TargetEmphasisON;
924
#endif /* SHOW_WHEREIS_TARGETS */
926
#ifdef USE_COLOR_STYLE
927
PRIVATE int find_cached_style ARGS2(
933
#ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
934
if ( textfields_need_activation
935
&& links[cur].type == WWW_FORM_LINK_TYPE
936
&& F_TEXTLIKE(links[cur].l_form->type) )
943
* This is where we try to restore the original style when a link is
944
* unhighlighted. The purpose of cached_styles[][] is to save the
945
* original style just for this case. If it doesn't have a color
946
* change saved at just the right position, we look at preceding
947
* positions in the same line until we find one.
949
if (LYP >= 0 && LYP < CACHEH && LXP >= 0 && LXP < CACHEW) {
951
(tfp, "STYLE.highlight.off: cached style @(%d,%d): ",
953
s = cached_styles[LYP][LXP];
955
for (x = LXP-1; x >= 0; x--) {
956
if (cached_styles[LYP][x]) {
957
if (cached_styles[LYP][x] > 0) {
958
s = cached_styles[LYP][x];
959
cached_styles[LYP][LXP] = s;
961
CTRACE((tfp, "found %d, x_offset=%d.\n",
962
cached_styles[LYP][x], (int)x-LXP));
967
CTRACE((tfp, "not found, assume <a>.\n"));
971
CTRACE((tfp, "found %d.\n", s));
974
CTRACE2(TRACE_STYLE, (tfp, "STYLE.highlight.off: can't use cache.\n"));
978
CTRACE2(TRACE_STYLE, (tfp, "STYLE.highlight.on: @(%d,%d).\n", LYP, LXP));
982
#endif /* USE_COLOR_STYLE */
985
* Highlight (or unhighlight) a given link.
987
PUBLIC void LYhighlight ARGS3(
992
char buffer[MAX_LINE];
998
#ifdef SHOW_WHEREIS_TARGETS
999
BOOL TargetEmphasisON = FALSE;
1000
BOOL target1_drawn = NO;
1002
BOOL utf_flag = (BOOL)(LYCharSet_UC[current_char_set].enc == UCT_ENC_UTF8);
1003
BOOL hl1_drawn = NO;
1004
#ifdef USE_COLOR_STYLE
1005
BOOL hl2_drawn = FALSE; /* whether links[cur].l_hightext2 is already drawn
1008
tmp[0] = tmp[1] = tmp[2] = '\0';
1011
* Bugs in the history code might cause -1 to be sent for cur, which yields
1012
* a crash when LYstrncpy() is called with a nonsense pointer. As far as I
1013
* know, such bugs have been squashed, but if they should reappear, this
1014
* works around them. -FM
1018
#if defined(TEXTFIELDS_MAY_NEED_ACTIVATION) && defined(INACTIVE_INPUT_STYLE_VH)
1020
textinput_redrawn = FALSE;
1024
#ifdef USE_COLOR_STYLE
1025
if (flag == ON || links[cur].type == WWW_FORM_LINK_TYPE) {
1027
LynxChangeStyle(find_cached_style(cur, flag), STACK_ON);
1030
if (links[cur].type == WWW_FORM_LINK_TYPE
1031
|| LYGetHiliteStr(cur, 0) == NULL) {
1032
LYMoveToLink(cur, target, NULL,
1033
flag, links[cur].inUnderline, utf_flag);
1034
lynx_start_link_color (flag == ON, links[cur].inUnderline);
1036
LYMoveToLink(cur, target, LYGetHiliteStr(cur, 0),
1037
flag, links[cur].inUnderline, utf_flag);
1039
#ifdef SHOW_WHEREIS_TARGETS
1040
target1_drawn = YES;
1045
if (links[cur].type == WWW_FORM_LINK_TYPE) {
1047
int avail_space = (LYcols - links[cur].lx) - 1;
1048
char *text = LYGetHiliteStr(cur, 0);
1050
if (avail_space > links[cur].l_form->size)
1051
avail_space = links[cur].l_form->size;
1052
if (avail_space > (int) sizeof(buffer) - 1)
1053
avail_space = (int) sizeof(buffer) - 1;
1055
LYstrncpy(buffer, NonNull(text), avail_space);
1058
len = strlen(buffer);
1059
for (; len < links[cur].l_form->size && len < avail_space; len++)
1062
#ifdef USE_COLOR_STYLE
1063
} else if (flag == OFF) {
1065
redraw_lines_of_link(cur);
1066
CTRACE2(TRACE_STYLE, (tfp, "STYLE.highlight.off: NOFIX branch @(%d,%d).\n", LYP, LXP));
1068
} else if (!hl1_drawn) {
1070
* Copy into the buffer only what will fit within the width of the
1073
LYmbcsstrncpy(buffer,
1074
(LYGetHiliteStr(cur, 0) ?
1075
LYGetHiliteStr(cur, 0) : ""),
1076
(sizeof(buffer) - 1),
1077
((LYcols - 1) - links[cur].lx),
1083
* Display a second line as well.
1085
#ifdef USE_COLOR_STYLE
1086
if (hl2_drawn == FALSE)
1090
(hi_string = LYGetHiliteStr(cur, hi_count)) != NULL
1091
&& links[cur].ly + hi_count <= display_lines;
1094
hi_offset = LYGetHilitePos(cur, hi_count);
1095
lynx_stop_link_color (flag == ON, links[cur].inUnderline);
1096
LYmove(links[cur].ly + hi_count, hi_offset);
1098
#ifdef USE_COLOR_STYLE
1099
CTRACE2(TRACE_STYLE,
1100
(tfp, "STYLE.highlight.line2: @(%d,%d), style=%d.\n",
1101
links[cur].ly + hi_count, hi_offset,
1102
flag == ON ? s_alink : s_a));
1103
LynxChangeStyle(flag == ON ? s_alink : s_a, ABS_ON);
1105
lynx_start_link_color (flag == ON, links[cur].inUnderline);
1108
for (i = 0; (tmp[0] = hi_string[i]) != '\0'
1109
&& (i + hi_offset) < LYcols; i++) {
1110
if (!IsSpecialAttrChar(hi_string[i])) {
1112
* For CJK strings, by Masanobu Kimura.
1114
if (HTCJK != NOCJK && is8bits(tmp[0])) {
1115
tmp[1] = hi_string[++i];
1124
lynx_stop_link_color (flag == ON, links[cur].inUnderline);
1127
#ifdef SHOW_WHEREIS_TARGETS
1128
for (hi_count = target1_drawn ? 1 : 0;
1129
LYGetHiliteStr(cur, hi_count) != NULL;
1131
TargetEmphasisON = show_whereis_targets(flag,
1141
* Get cursor out of the way.
1145
#endif /* SHOW_WHEREIS_TARGETS */
1147
* Never hide the cursor if there's no FANCY CURSES or SLANG.
1149
LYmove(links[cur].ly,
1150
((links[cur].lx > 0) ? (links[cur].lx - 1) : 0));
1159
* free_and_clear will free a pointer if it
1160
* is non-zero and then set it to zero.
1162
PUBLIC void free_and_clear ARGS1(
1173
* Convert single or serial newlines to single spaces throughout a string
1174
* (ignore newlines if the preceding character is a space) and convert
1175
* tabs to single spaces. Don't ignore any explicit tabs or spaces if
1176
* the condense argument is FALSE, otherwise, condense any serial spaces
1177
* or tabs to one space. - FM
1179
PUBLIC void convert_to_spaces ARGS2(
1185
BOOL last_is_space = FALSE;
1190
for ( ; (*s && !isspace(*s)); s++)
1198
if (!(condense && last_is_space))
1200
last_is_space = TRUE;
1205
if (!last_is_space) {
1207
last_is_space = TRUE;
1213
last_is_space = FALSE;
1223
* Strip trailing slashes from directory paths.
1225
PUBLIC char * strip_trailing_slash ARGS1(
1230
i = strlen(dirname) - 1;
1231
while (i >= 0 && dirname[i] == '/')
1232
dirname[i--] = '\0';
1237
* Display (or hide) the status line.
1239
BOOLEAN mustshow = FALSE;
1241
PUBLIC void statusline ARGS1(
1244
char buffer[MAX_LINE];
1245
unsigned char *temp = NULL;
1246
int max_length, len, i, j;
1249
char text_buff[MAX_LINE];
1255
* Don't print statusline messages if dumping to stdout.
1257
if (dump_output_immediately)
1261
* Don't print statusline message if turned off.
1263
if (mustshow != TRUE) {
1264
if (no_statusline == TRUE) {
1270
/* "LYNXDOWNLOAD://Method=-1/File=%s/SugFile=%s%s\">Save to disk</a>\n" */
1271
LYstrncpy(text_buff, text, sizeof(text_buff)-1);
1272
p = strchr(text_buff, '\n');
1277
* Deal with any CJK escape sequences and Kanji if we have a CJK
1278
* character set selected, otherwise, strip any escapes. Also,
1279
* make sure text is not longer than the statusline window. - FM
1281
max_length = ((LYcols - 2) < (int)sizeof(buffer))
1282
? (LYcols - 2) : (int)sizeof(buffer)-1;
1283
if ((text_buff[0] != '\0') &&
1284
(LYHaveCJKCharacterSet)) {
1286
* Translate or filter any escape sequences. - FM
1288
if ((temp = typecallocn(unsigned char, strlen(text_buff) + 1)) == NULL)
1289
outofmem(__FILE__, "statusline");
1290
if (kanji_code == EUC) {
1291
TO_EUC((CONST unsigned char *)text_buff, temp);
1292
} else if (kanji_code == SJIS) {
1293
#ifdef KANJI_CODE_OVERRIDE
1294
if (!LYRawMode || last_kcode == SJIS)
1295
strcpy(temp, text_buff);
1297
TO_SJIS((CONST unsigned char *)text_buff, temp);
1299
strcpy((char *) temp, text_buff);
1302
for (i = 0, j = 0; text_buff[i]; i++) {
1303
if (text_buff[i] != CH_ESC) { /* S/390 -- gil -- 2119 */
1304
temp[j++] = text_buff[i];
1311
* Deal with any newlines or tabs in the string. - FM
1313
convert_to_spaces((char *)temp, FALSE);
1316
* Handle the Kanji, making sure the text is not
1317
* longer than the statusline window. - FM
1319
for (i = 0, j = 0, len = 0, k = '\0';
1320
temp[i] != '\0' && len < max_length; i++) {
1323
buffer[j++] = temp[i];
1326
} else if ((temp[i] & 0200) != 0) {
1329
buffer[j++] = temp[i];
1337
* Strip any escapes, and shorten text if necessary. Note
1338
* that we don't deal with the possibility of UTF-8 characters
1339
* in the string. This is unlikely, but if strings with such
1340
* characters are used in LYMessages_en.h, a compilation
1341
* symbol of HAVE_UTF8_STATUSLINES could be added there, and
1342
* code added here for determining the displayed string length,
1343
* as we do above for CJK. - FM
1345
for (i = 0, len = 0; text_buff[i] != '\0' && len < max_length; i++) {
1346
if (text_buff[i] != CH_ESC) { /* S/390 -- gil -- 2119 */
1347
buffer[len++] = text_buff[i];
1352
* Deal with any newlines or tabs in the string. - FM
1354
convert_to_spaces(buffer, FALSE);
1358
* Move to the desired statusline window and
1359
* output the text highlighted. - FM
1361
if (LYStatusLine >= 0) {
1362
if (LYStatusLine < LYlines-1) {
1363
LYmove(LYStatusLine, 0);
1365
LYmove(LYlines-1, 0);
1367
} else if (user_mode == NOVICE_MODE) {
1368
LYmove(LYlines-3, 0);
1370
LYmove(LYlines-1, 0);
1374
if (text != NULL && text[0] != '\0') {
1375
BOOLEAN has_CJK = FALSE;
1377
if (HTCJK != NOCJK) {
1378
for (i = 0; buffer[i] != '\0'; i++) {
1379
if (buffer[i] & 0x80) {
1387
#ifdef HAVE_UTF8_STATUSLINES
1388
|| (LYCharSet_UC[current_char_set].enc == UCT_ENC_UTF8)
1394
#ifndef USE_COLOR_STYLE
1395
lynx_start_status_color ();
1397
lynx_stop_status_color ();
1399
/* draw the status bar in the STATUS style */
1401
int a = (strncmp(buffer, ALERT_FORMAT, ALERT_PREFIX_LEN)
1402
|| !hashStyles[s_alert].name)
1405
LynxChangeStyle (a, STACK_ON);
1408
((lynx_has_color && LYShowColor >= SHOW_COLOR_ON)
1409
? hashStyles[a].color
1412
if (!(lynx_has_color && LYShowColor >= SHOW_COLOR_ON))
1413
wbkgdset(LYwin, A_NORMAL | ' ');
1414
else if (s_normal != NOSTYLE)
1415
wbkgdset(LYwin, hashStyles[s_normal].color | ' ');
1417
wbkgdset(LYwin, displayStyles[DSTYLE_NORMAL].color | ' ');
1418
LynxChangeStyle (a, STACK_OFF);
1427
PRIVATE char *novice_lines ARGS1(
1432
return NOVICE_LINE_TWO_A;
1434
return NOVICE_LINE_TWO_B;
1436
return NOVICE_LINE_TWO_C;
1442
static int lineno = 0;
1444
PUBLIC void toggle_novice_line NOARGS
1447
if (*novice_lines(lineno) == '\0')
1452
PUBLIC void noviceline ARGS1(
1453
int, more_flag GCC_UNUSED)
1456
if (dump_output_immediately)
1459
LYmove(LYlines-2,0);
1460
/* lynx_stop_reverse(); */
1462
LYaddstr(NOVICE_LINE_ONE);
1464
#if defined(DIRED_SUPPORT ) && defined(OK_OVERRIDE)
1465
if (lynx_edit_mode && !no_dired_support)
1466
LYaddstr(DIRED_NOVICELINE);
1468
#endif /* DIRED_SUPPORT && OK_OVERRIDE */
1470
if (LYUseNoviceLineTwo)
1471
LYaddstr(NOVICE_LINE_TWO);
1473
LYaddstr((char *)novice_lines(lineno));
1479
#if defined(NSL_FORK) || defined(MISC_EXP)
1481
* Returns the file descriptor from which keyboard input is expected,
1482
* or INVSOC (-1) if not available.
1483
* If need_selectable is true, returns non-INVSOC fd only if select()
1484
* is possible - actually, currently only checks if fd is connected
1487
PUBLIC int LYConsoleInputFD ARGS1(
1488
BOOLEAN, need_selectable)
1494
#if ((SLANG_VERSION >= 9919) && defined(REAL_UNIX_SYSTEM) && !defined(__CYGWIN__))
1495
/* SLang_TT_Read_FD introduced in slang 0.99.19, from its changelog:
1496
* SLang_TT_Read_FD variable is now available for unix. This is the file
1497
* descriptor used by SLang_getkey. */
1499
fd = SLang_TT_Read_FD;
1500
#endif /* SLANG_VERSION >= 9919 */
1501
#else /* !USE_SLANG */
1503
#endif /* !USE_SLANG */
1505
if (need_selectable && fd != INVSOC) {
1514
#endif /* NSL_FORK || MISC_EXP */
1516
PRIVATE int fake_zap = 0;
1518
PUBLIC void LYFakeZap ARGS1(
1521
if (set && fake_zap < 1) {
1522
CTRACE((tfp, "\r *** Set simulated 'Z'"));
1524
CTRACE((tfp, ", %d pending", fake_zap));
1525
CTRACE((tfp, " ***\n"));
1527
} else if (!set && fake_zap) {
1528
CTRACE((tfp, "\r *** Unset simulated 'Z'"));
1529
CTRACE((tfp, ", %d pending", fake_zap));
1530
CTRACE((tfp, " ***\n"));
1536
PRIVATE int DontCheck NOARGS
1541
/** Curses or slang setup was not invoked **/
1542
if (dump_output_immediately)
1545
if (LYHaveCmdScript()) /* we may be running from a script */
1553
* Avoid checking interrupts more than one per second, since it is a slow
1554
* and expensive operation - TD
1556
#ifdef HAVE_GETTIMEOFDAY
1557
#undef timezone /* U/Win defines a conflicting macro */
1560
gettimeofday(&tv, (struct timezone *)0);
1561
next = tv.tv_usec / 100000L; /* 0.1 seconds is a compromise */
1564
next = time((time_t*)0);
1573
PUBLIC int HTCheckForInterrupt NOARGS
1577
#ifndef VMS /* UNIX stuff: */
1578
#if !defined(USE_SLANG)
1579
struct timeval socket_timeout;
1582
#endif /* !USE_SLANG */
1586
CTRACE((tfp, "\r *** Got simulated 'Z' ***\n"));
1588
CTRACE_SLEEP(AlertSecs);
1592
/** Curses or slang setup was not invoked **/
1596
#if !defined(_WINDOWS) || defined(__MINGW32__)
1598
/** No keystroke was entered
1599
Note that this isn't taking possible SOCKSification
1600
and the socks_flag into account, and may fail on the
1601
slang library's select() when SOCKSified. - FM **/
1602
#ifdef DJGPP_KEYHANDLER
1603
if (0 == _bios_keybrd(_NKEYBRD_READY))
1605
if (0 == SLang_input_pending(0))
1606
#endif /* DJGPP_KEYHANDLER */
1609
#else /* Unix curses: */
1611
socket_timeout.tv_sec = 0;
1612
socket_timeout.tv_usec = 0;
1614
FD_SET(0, &readfds);
1617
ret = Rselect(1, (void *)&readfds, NULL, NULL,
1621
ret = select(1, (void *)&readfds, NULL, NULL,
1625
if ((ret == -1) && (SOCKET_ERRNO == EINTR))
1628
/** No keystroke was entered? **/
1629
if (!FD_ISSET(0,&readfds))
1631
#endif /* USE_SLANG */
1632
#endif /* !_WINDOWS */
1634
#if defined(PDCURSES)
1635
nodelay(LYwin,TRUE);
1636
#endif /* PDCURSES */
1638
* 'c' contains whatever character we're able to read from keyboard
1641
#if defined(PDCURSES)
1642
nodelay(LYwin,FALSE);
1643
#endif /* PDCURSES */
1646
extern int typeahead();
1650
CTRACE((tfp, "\r *** Got simulated 'Z' ***\n"));
1652
CTRACE_SLEEP(AlertSecs);
1656
/** Curses or slang setup was not invoked **/
1660
/** Control-C or Control-Y and a 'N'o reply to exit query **/
1661
if (HadVMSInterrupt) {
1662
HadVMSInterrupt = FALSE;
1667
* 'c' contains whatever character we're able to read from keyboard
1674
* 'c' contains whatever character we're able to read from keyboard
1677
/** Keyboard 'Z' or 'z', or Control-G or Control-C **/
1678
if (LYCharIsINTERRUPT(c))
1681
/* There is a subset of mainloop() actions available at this stage:
1682
** no new getfile() cycle is possible until the previous finished.
1683
** Currently we have scrolling in partial mode, toggling of trace
1684
** log, and pasting. User search now in progress...
1686
cmd = (LKC_TO_LAC(keymap,c));
1688
case LYK_TRACE_TOGGLE : /* Toggle TRACE mode. */
1689
handle_LYK_TRACE_TOGGLE();
1691
#ifdef CAN_CUT_AND_PASTE
1692
case LYK_TO_CLIPBOARD: { /* ^S */
1693
char *s = LYDownLoadAddress();
1695
if (!s || !*s || put_clip(s))
1696
HTInfoMsg(gettext("Copy to clipboard failed."));
1698
HTInfoMsg(gettext("Download document URL put to clipboard."));
1701
#endif /* defined CAN_CUT_AND_PASTE */
1704
/* OK, we got several lines from new document and want to scroll... */
1705
if (display_partial && (NumOfLines_partial > 2)) {
1708
int Newline_partial = LYGetNewline();
1711
case LYK_WHEREIS: /* search within the document */
1712
case LYK_NEXT: /* search for the next occurrence in the document */
1713
case LYK_PREV: /* search for the previous occurrence in the document */
1714
handle_LYK_WHEREIS(cmd, &do_refresh);
1715
if (www_search_result != -1) {
1716
Newline_partial = www_search_result;
1717
www_search_result = -1; /* reset */
1721
case LYK_FASTBACKW_LINK :
1722
if (Newline_partial <= (display_lines)+1) {
1723
Newline_partial -= display_lines ;
1725
HTGetLinkOrFieldStart(-1,
1726
&Newline_partial, NULL,
1727
-1, TRUE)) == LINK_LINE_FOUND) {
1729
} else if (res == LINK_DO_ARROWUP) {
1730
Newline_partial -= display_lines ;
1733
case LYK_FASTFORW_LINK :
1734
if (HText_canScrollDown()) {
1735
/* This is not an exact science... - kw */
1737
HTGetLinkOrFieldStart(HText_LinksInLines(HTMainText,
1741
&Newline_partial, NULL,
1742
1, TRUE)) == LINK_LINE_FOUND) {
1747
case LYK_PREV_PAGE :
1748
if (Newline_partial > 1)
1749
Newline_partial -= display_lines ;
1751
case LYK_NEXT_PAGE :
1752
if (HText_canScrollDown())
1753
Newline_partial += display_lines ;
1756
if (Newline_partial > 1)
1757
Newline_partial -= (display_lines/2) ;
1759
case LYK_DOWN_HALF :
1760
if (HText_canScrollDown())
1761
Newline_partial += (display_lines/2) ;
1764
if (Newline_partial > 1)
1765
Newline_partial -= 2 ;
1768
if (HText_canScrollDown())
1769
Newline_partial += 2 ;
1772
if (Newline_partial > 1)
1773
Newline_partial = 1;
1776
if (HText_canScrollDown())
1777
Newline_partial = HText_getNumOfLines() - display_lines + 1;
1778
/* calculate for "current" bottom value */
1783
/** Other or no keystrokes **/
1784
return ((int)FALSE) ;
1786
if (Newline_partial < 1)
1787
Newline_partial = 1;
1788
if (LYMainLoop_pageDisplay(Newline_partial))
1789
NumOfLines_partial = HText_getNumOfLines();
1791
#endif /* DISP_PARTIAL */
1794
/** Other or no keystrokes **/
1799
* Check if the given filename looks like it's an absolute pathname, i.e.,
1800
* references a directory.
1802
PUBLIC BOOLEAN LYisAbsPath ARGS1(
1805
BOOLEAN result = FALSE;
1806
if (non_empty(path)) {
1810
#if defined(USE_DOS_DRIVES)
1811
result = (BOOL) (LYIsPathSep(path[0])
1812
|| (LYIsDosDrive(path)
1813
&& LYIsPathSep(path[2])));
1815
result = (LYIsPathSep(path[0]));
1816
#endif /* USE_DOS_DRIVES */
1823
* Check if the given filename is the root path, e.g., "/" on Unix.
1825
PUBLIC BOOLEAN LYisRootPath ARGS1(
1828
#if defined(USE_DOS_DRIVES)
1829
if (strlen(path) == 3
1830
&& LYIsDosDrive(path)
1831
&& LYIsPathSep(path[2]))
1834
return (BOOL) ((strlen(path) == 1) && LYIsPathSep(path[0]));
1838
* A file URL for a remote host is an obsolete ftp URL.
1839
* Return YES only if we're certain it's a local file. - FM
1841
PUBLIC BOOLEAN LYisLocalFile ARGS1(
1842
CONST char *, filename)
1845
char *acc_method = NULL;
1850
if (!(host = HTParse(filename, "", PARSE_HOST)))
1857
if ((cp = strchr(host, ':')) != NULL)
1860
if ((acc_method = HTParse(filename, "", PARSE_ACCESS))) {
1861
if (0==strcmp("file", acc_method) &&
1862
(0==strcmp(host, "localhost") ||
1863
LYSameFilename(host, HTHostName()))) {
1876
* Utility for checking URLs with a host field.
1877
* Return YES only if we're certain it's the local host. - FM
1879
PUBLIC BOOLEAN LYisLocalHost ARGS1(
1880
CONST char *, filename)
1887
if (!(host = HTParse(filename, "", PARSE_HOST)))
1894
if ((cp = strchr(host, ':')) != NULL)
1897
if ((LYSameFilename(host, "localhost") ||
1898
LYSameFilename(host, LYHostName) ||
1899
LYSameFilename(host, HTHostName()))) {
1909
* Utility for freeing the list of local host aliases. - FM
1911
PUBLIC void LYLocalhostAliases_free NOARGS
1914
HTList *cur = localhost_aliases;
1919
while (NULL != (alias = (char *)HTList_nextObject(cur))) {
1922
HTList_delete(localhost_aliases);
1923
localhost_aliases = NULL;
1928
* Utility for listing hosts to be treated as local aliases. - FM
1930
PUBLIC void LYAddLocalhostAlias ARGS1(
1933
char *LocalAlias = NULL;
1935
if (!non_empty(alias))
1938
if (!localhost_aliases) {
1939
localhost_aliases = HTList_new();
1940
#ifdef LY_FIND_LEAKS
1941
atexit(LYLocalhostAliases_free);
1945
StrAllocCopy(LocalAlias, alias);
1946
HTList_addObject(localhost_aliases, LocalAlias);
1952
* Utility for checking URLs with a host field.
1953
* Return YES only if we've listed the host as a local alias. - FM
1955
PUBLIC BOOLEAN LYisLocalAlias ARGS1(
1956
CONST char *, filename)
1961
HTList *cur = localhost_aliases;
1963
if (!cur || !filename)
1965
if (!(host = HTParse(filename, "", PARSE_HOST)))
1972
if ((cp = strchr(host, ':')) != NULL)
1975
while (NULL != (alias = (char *)HTList_nextObject(cur))) {
1976
if (LYSameFilename(host, alias)) {
1987
** This function checks for a URL with an unknown scheme,
1988
** but for which proxying has been set up, and if so,
1989
** returns PROXY_URL_TYPE. - FM
1991
** If a colon is present but the string segment which
1992
** precedes it is not being proxied, and we can be sure
1993
** that what follows the colon is not a port field,
1994
** it returns UNKNOWN_URL_TYPE. Otherwise, it returns
1995
** 0 (not a URL). - FM
1997
PUBLIC int LYCheckForProxyURL ARGS1(
2000
char *cp = filename;
2005
* Don't crash on an empty argument.
2008
return(NOT_A_URL_TYPE);
2010
/* kill beginning spaces */
2011
cp = LYSkipBlanks(cp);
2014
* Check for a colon, and if present,
2015
* see if we have proxying set up.
2017
if ((cp1 = strchr((cp+1), ':')) != NULL) {
2019
StrAllocCopy(cp2, cp);
2021
StrAllocCat(cp2, "_proxy");
2022
if (LYGetEnv(cp2) != NULL) {
2024
return(PROXY_URL_TYPE);
2027
#if defined (USE_DOS_DRIVES)
2028
if (LYIsDosDrive(cp))
2029
return(NOT_A_URL_TYPE);
2033
return(NOT_A_URL_TYPE);
2034
} else if (isdigit(UCH(*cp1))) {
2035
while (*cp1 && isdigit(UCH(*cp1)))
2037
if (*cp1 && !LYIsHtmlSep(*cp1))
2038
return(UNKNOWN_URL_TYPE);
2040
return(UNKNOWN_URL_TYPE);
2044
return(NOT_A_URL_TYPE);
2048
* Compare a "type:" string, replacing it by the comparison-string if it
2049
* matches (and return true in that case).
2051
static BOOLEAN compare_type ARGS3(
2056
if (!strncasecomp(tst, cmp, len)) {
2057
if (strncmp(tst, cmp, len)) {
2059
for (i = 0; i < len; i++)
2067
#define DoubleHtmlSep(s) (LYIsHtmlSep((s)[0]) && LYIsHtmlSep((s)[1]))
2068
#define compare_two(tst,cmp,len,limit) \
2069
((len + 2) <= limit \
2070
&& DoubleHtmlSep(tst + len) \
2071
&& compare_type(tst, cmp, len))
2074
** Must recognize a URL and return the type.
2075
** If recognized, based on a case-insensitive
2076
** analysis of the scheme field, ensures that
2077
** the scheme field has the expected case.
2079
** Returns 0 (not a URL) for a NULL argument,
2080
** one which lacks a colon.
2082
** Chains to LYCheckForProxyURL() if a colon
2083
** is present but the type is not recognized.
2085
PUBLIC int is_url ARGS1(
2088
char *cp = filename;
2090
int result = NOT_A_URL_TYPE;
2095
* Don't crash on an empty argument.
2101
* Can't be a URL if it lacks a colon.
2103
if (NULL == strchr(cp, ':'))
2107
* Kill beginning spaces.
2109
cp = LYSkipBlanks(cp);
2112
* Can't be a URL if it starts with a slash.
2113
* So return immediately for this common case,
2114
* also to avoid false positives if there was
2115
* a colon later in the string. Also can't be
2116
* a URL if it starts with a colon. - KW
2118
if (*cp == ':' || LYIsHtmlSep(*cp)) {
2119
result = NOT_A_URL_TYPE;
2127
* Lynx internal pages ("LYNXfoo:" or "lynxfoo:")
2128
* start with 'l' or 'L', other URLs aren't.
2130
if (compare_type(cp, STR_LYNXEXEC, LEN_LYNXEXEC)) {
2132
* Special External Lynx type to handle execution
2133
* of commands or scripts which require a pause to
2134
* read the screen upon completion.
2136
result = LYNXEXEC_URL_TYPE;
2138
} else if (compare_type(cp, STR_LYNXPROG, LEN_LYNXPROG)) {
2140
* Special External Lynx type to handle execution
2141
* of commands, scripts or programs with do not
2142
* require a pause to read screen upon completion.
2144
result = LYNXPROG_URL_TYPE;
2146
} else if (compare_type(cp, STR_LYNXCGI, LEN_LYNXCGI)) {
2148
* Special External Lynx type to handle cgi scripts.
2150
result = LYNXCGI_URL_TYPE;
2152
} else if (compare_type(cp, STR_LYNXPRINT, LEN_LYNXPRINT)) {
2154
* Special Internal Lynx type.
2156
result = LYNXPRINT_URL_TYPE;
2158
} else if (compare_type(cp, STR_LYNXOPTIONS, LEN_LYNXOPTIONS)) {
2160
* Special Internal Lynx type.
2162
result = LYNXOPTIONS_URL_TYPE;
2164
} else if (compare_type(cp, STR_LYNXCFG, LEN_LYNXCFG)) {
2166
* Special Internal Lynx type.
2168
result = LYNXCFG_URL_TYPE;
2170
} else if (compare_type(cp, STR_LYNXMESSAGES, LEN_LYNXMESSAGES)) {
2172
* Special Internal Lynx type.
2174
result = LYNXMESSAGES_URL_TYPE;
2176
} else if (compare_type(cp, STR_LYNXCFLAGS, LEN_LYNXCFLAGS)) {
2178
* Special Internal Lynx type.
2180
result = LYNXCOMPILE_OPTS_URL_TYPE;
2182
} else if (compare_type(cp, STR_LYNXDOWNLOAD, LEN_LYNXDOWNLOAD)) {
2184
* Special Internal Lynx type.
2186
result = LYNXDOWNLOAD_URL_TYPE;
2188
} else if (compare_type(cp, STR_LYNXDIRED, LEN_LYNXDIRED)) {
2190
* Special Internal Lynx type.
2192
result = LYNXDIRED_URL_TYPE;
2194
} else if (compare_type(cp, STR_LYNXHIST, LEN_LYNXHIST)) {
2196
* Special Internal Lynx type.
2198
result = LYNXHIST_URL_TYPE;
2200
} else if (compare_type(cp, STR_LYNXKEYMAP, LEN_LYNXKEYMAP)) {
2202
* Special Internal Lynx type.
2204
result = LYNXKEYMAP_URL_TYPE;
2206
} else if (compare_type(cp, STR_LYNXIMGMAP, LEN_LYNXIMGMAP)) {
2208
* Special Internal Lynx type.
2210
/* force lower/uppercase of next part */
2211
(void)is_url(&cp[LEN_LYNXIMGMAP]);
2212
result = LYNXIMGMAP_URL_TYPE;
2214
} else if (compare_type(cp, STR_LYNXCOOKIE, LEN_LYNXCOOKIE)) {
2216
* Special Internal Lynx type.
2218
result = LYNXCOOKIE_URL_TYPE;
2221
#ifndef DISABLE_NEWS
2223
* NEWSfoo: schemes -
2227
if (compare_type(cp, STR_NEWS_URL, LEN_NEWS_URL)) {
2228
result = NEWS_URL_TYPE;
2230
} else if (compare_type(cp, STR_NNTP_URL, LEN_NNTP_URL)) {
2231
result = NNTP_URL_TYPE;
2233
} else if (compare_type(cp, "newspost:", 9)) {
2235
* Special Lynx type to handle news posts.
2237
result = NEWSPOST_URL_TYPE;
2239
} else if (compare_type(cp, "newsreply:", 10)) {
2241
* Special Lynx type to handle news replies (followups).
2243
result = NEWSREPLY_URL_TYPE;
2248
* SNEWSfoo: schemes -
2252
if (compare_type(cp, STR_SNEWS_URL, LEN_SNEWS_URL)) {
2253
result = SNEWS_URL_TYPE;
2255
} else if (compare_type(cp, "snewspost:", 10)) {
2257
* Special Lynx type to handle snews posts.
2259
result = NEWSPOST_URL_TYPE;
2261
} else if (compare_type(cp, "snewsreply:", 11)) {
2263
* Special Lynx type to handle snews replies (followups).
2265
result = NEWSREPLY_URL_TYPE;
2271
if (compare_type(cp, STR_MAILTO_URL, LEN_MAILTO_URL)) {
2272
result = MAILTO_URL_TYPE;
2278
if (compare_type(cp, STR_FILE_URL, len = LEN_FILE_URL)) {
2279
if (LYisLocalFile(cp)) {
2280
result = FILE_URL_TYPE;
2281
} else if (DoubleHtmlSep(cp + len)) {
2282
result = FTP_URL_TYPE;
2286
else if (compare_two(cp, STR_FTP_URL, LEN_FTP_URL, limit)) {
2287
result = FTP_URL_TYPE;
2290
#ifndef DISABLE_FINGER
2291
else if (compare_two(cp, STR_FINGER_URL, LEN_FINGER_URL, limit)) {
2292
result = FINGER_URL_TYPE;
2299
#ifndef DISABLE_BIBP
2300
if (compare_type(cp, STR_BIBP_URL, LEN_BIBP_URL)) {
2301
result = BIBP_URL_TYPE;
2308
if (compare_type(cp, "data:", 5)) {
2309
result = DATA_URL_TYPE;
2315
&& ((cp1 = strchr(cp + 3, ':')) == NULL
2316
|| !DoubleHtmlSep(cp1 + 1))) {
2318
* If it doesn't contain "://", and it's not one of the the
2319
* above, it can't be a URL with a scheme we know, so check if
2320
* it's an unknown scheme for which proxying has been set up.
2324
&& (cp1 - cp) > 1 /* exclude DOS-style device:/path */
2325
&& LYisAbsPath(cp1+1)) {
2326
result = NCFTP_URL_TYPE;
2333
if (compare_type(cp, STR_HTTP_URL, LEN_HTTP_URL)) {
2334
result = HTTP_URL_TYPE;
2336
} else if (compare_type(cp, STR_HTTPS_URL, LEN_HTTPS_URL)) {
2337
result = HTTPS_URL_TYPE;
2341
#ifndef DISABLE_GOPHER
2344
if (compare_type(cp, STR_GOPHER_URL, LEN_GOPHER_URL)) {
2345
if (strlen(cp) >= 11
2346
&& (cp1 = strchr(cp+11,'/')) != NULL) {
2348
if (TOUPPER(*(cp1+1)) == 'H' || *(cp1+1) == 'w')
2349
/* if this is a gopher html type */
2350
result = HTML_GOPHER_URL_TYPE;
2351
else if (*(cp1+1) == 'T' || *(cp1+1) == '8')
2352
result = TELNET_GOPHER_URL_TYPE;
2353
else if (*(cp1+1) == '7')
2354
result = INDEX_GOPHER_URL_TYPE;
2356
result = GOPHER_URL_TYPE;
2358
result = GOPHER_URL_TYPE;
2365
if (compare_type(cp, STR_WAIS_URL, LEN_WAIS_URL)) {
2366
result = WAIS_URL_TYPE;
2372
if (compare_type(cp, STR_TELNET_URL, LEN_TELNET_URL)) {
2373
result = TELNET_URL_TYPE;
2375
} else if (compare_type(cp, STR_TN3270_URL, LEN_TN3270_URL)) {
2376
result = TN3270_URL_TYPE;
2382
if (compare_type(cp, STR_RLOGIN_URL, LEN_RLOGIN_URL)) {
2383
result = RLOGIN_URL_TYPE;
2389
if (compare_type(cp, STR_CSO_URL, LEN_CSO_URL)) {
2390
result = CSO_URL_TYPE;
2396
if (compare_type(cp, "afs:", 4)) {
2397
result = AFS_URL_TYPE;
2403
if (compare_type(cp, "prospero:", 9)) {
2404
result = PROSPERO_URL_TYPE;
2411
* Check if it is an unknown scheme for which proxying has been set up.
2413
if (result == NOT_A_URL_TYPE)
2414
result = LYCheckForProxyURL(filename);
2420
* Sometimes it is just expected that curses is on when an alert or
2421
* other statusline message needs to be shown and we are not just
2422
* dumping immediately. Calling this will 'fix' it, but may not
2423
* always be appropriate. - kw
2425
PUBLIC void LYFixCursesOn ARGS1(
2426
CONST char *, reason)
2428
if (dump_output_immediately || LYCursesON)
2431
CTRACE((tfp, "Forcing curses on to %s\n", reason));
2437
* Most protocol modules called through HTLoad* expect that curses is on
2438
* unless dump_output_immediately is set, so that statusline messages
2439
* can be shown. Some protocols expect the opposite, namely telnet and
2440
* friends. This function should be called after the 'physical' URL
2441
* for accessing addr has been established. It does the right thing
2442
* to the degree that curses is turned on for known problem cases.
2443
* In any normal circumstances this should never apply, but proxying
2444
* or rule substitution is not prevented for telnet-like URLs, and
2445
* this 'fix' avoids some crashes that can otherwise occur. - kw
2447
PUBLIC BOOLEAN LYFixCursesOnForAccess ARGS2(
2449
CONST char *, physical)
2452
* If curses is off when maybe it shouldn't...
2454
if (!dump_output_immediately && !LYCursesON && physical) {
2457
* If requested resource wants to be accessed with curses off, and
2458
* getfile() would indeed have turned curses off for it...
2460
if (strstr(addr, "://") != NULL &&
2461
(isTELNET_URL(addr) ||
2462
isRLOGIN_URL(addr) ||
2463
isTN3270_URL(addr) ||
2464
(!isGOPHER_URL(addr) &&
2465
(cp1 = strchr(addr+11,'/')) != NULL &&
2466
(*(cp1+1) == 'T' || *(cp1+1) == '8')))) {
2468
* If actual access that will be done is ok with curses off,
2469
* then do nothing special, else force curses on. - kw
2471
if (!isTELNET_URL(physical) &&
2472
!isRLOGIN_URL(physical) &&
2473
!isTN3270_URL(physical)) {
2476
gettext("Unexpected access protocol for this URL scheme."));
2485
* Determine whether we allow HEAD and related flags for a URL. - kw
2487
PUBLIC BOOLEAN LYCanDoHEAD ARGS1(
2488
CONST char *, address)
2492
if (!non_empty(address))
2494
if (!strncmp(address, "http", 4))
2496
/* Make copy for is_url() since caller may not care for case changes */
2497
StrAllocCopy(temp0, address);
2498
isurl = is_url(temp0);
2503
if (isurl == LYNXCGI_URL_TYPE) {
2505
#if defined(LYNXCGI_LINKS) && !defined(VMS)
2512
* The idea of the following is to allow HEAD for news URLs that
2513
* identify single articles, not those that identify ranges of
2514
* articles or groups or a list of groups. - kw
2516
if (isurl == NEWS_URL_TYPE || isurl == NNTP_URL_TYPE) {
2517
char *temp = HTParse(address, "", PARSE_PATH);
2518
char *cp = strrchr(temp, '/');
2519
if (strchr((cp ? cp : temp), '@') != NULL) {
2524
if (cp && isdigit(UCH(cp[1])) && strchr(cp, '-') == NULL) {
2532
#define ALLOW_PROXY_HEAD
2533
/* If defined, also allow head requests for URLs proxied through the
2534
* "http" or "lynxcgi" protocols, which understand HEAD. Only the
2535
* proxy environment variables are checked, not the HTRules system. - kw
2537
#ifdef ALLOW_PROXY_HEAD
2538
if (isurl != FILE_URL_TYPE) {
2539
char *acc_method = HTParse(temp0, "", PARSE_ACCESS);
2540
if (non_empty(acc_method)) {
2542
StrAllocCat(acc_method, "_proxy");
2543
proxy = LYGetEnv(acc_method);
2544
if (proxy && (isHTTP_URL(proxy) ||
2545
isLYNXCGI(proxy)) &&
2546
!override_proxy(temp0)) {
2554
#endif /* ALLOW_PROXY_HEAD */
2561
* Close an input file.
2563
PUBLIC BOOLEAN LYCloseInput ARGS1(
2567
int err = ferror(fp);
2577
* Close an output file, reporting any problems with writing to it.
2579
PUBLIC BOOLEAN LYCloseOutput ARGS1(
2583
int err = ferror(fp);
2589
HTAlert(CANNOT_WRITE_TO_FILE);
2594
* Test if we'll be able to write a file. If not, warn the user.
2596
PUBLIC BOOLEAN LYCanWriteFile ARGS1(
2597
CONST char*, filename)
2599
if (LYCloseOutput(fopen(filename, "w"))) {
2603
_statusline(NEW_FILENAME_PROMPT);
2609
* Test if we'll be able to read a file.
2611
PUBLIC BOOLEAN LYCanReadFile ARGS1(
2612
CONST char*, filename)
2616
if ((fp = fopen(filename, "r")) != 0) {
2617
return LYCloseInput(fp);
2623
* Remove backslashes from any string.
2625
PUBLIC void remove_backslashes ARGS1(
2630
for (cp = buf; *cp != '\0' ; cp++) {
2632
if (*cp != '\\') { /* don't print slashes */
2635
} else if (*cp == '\\' && /* print one slash if there */
2636
*(cp+1) == '\\') { /* are two in a row */
2646
* Checks to see if the current process is attached
2647
* via a terminal in the local domain.
2650
PUBLIC BOOLEAN inlocaldomain NOARGS
2656
char *cp, *mytty = NULL;
2658
if ((cp = ttyname(0)))
2659
mytty = strrchr(cp, '/');
2661
if (mytty && (fp = fopen(UTMP_FILE, "r")) != NULL) {
2664
n = fread((char *) &me, sizeof(struct utmp), 1, fp);
2665
} while (n > 0 && !STREQ(me.ut_line, mytty));
2666
(void) LYCloseInput(fp);
2669
strlen(me.ut_host) > strlen(LYLocalDomain) &&
2670
STREQ(LYLocalDomain,
2671
me.ut_host + strlen(me.ut_host) - strlen(LYLocalDomain)) )
2674
/* Linux fix to check for local user. J.Cullen 11Jul94 */
2675
if ((n > 0) && (strlen(me.ut_host) == 0))
2680
CTRACE((tfp, "Could not get ttyname (returned %s) or open UTMP file %s\n",
2681
(cp != 0) ? cp : "<null>", UTMP_FILE));
2686
CTRACE((tfp, "LYUtils: inlocaldomain() not support.\n"));
2688
#endif /* HAVE_UTMP */
2691
#ifdef HAVE_SIGACTION
2693
* An extended alternative for calling signal(), sets some flags for
2694
* signal handler as we want them if that functionality is available.
2695
* (We don't return anything from this function since the return
2696
* value would currently be ignored anyway.) - kw
2699
PUBLIC void LYExtSignal ARGS2(
2701
LYSigHandlerFunc_t *, handler)
2704
/* add more cases to if(condition) if required... */
2705
if (sig == SIGWINCH && LYNonRestartingSIGWINCH) {
2706
struct sigaction act;
2707
act.sa_handler = handler;
2708
sigemptyset(&act.sa_mask);
2711
if (sig != SIGWINCH)
2712
act.sa_flags |= SA_RESTART;
2713
#endif /* SA_RESTART */
2714
sigaction(sig, &act, NULL);
2716
#endif /* defined(SIGWINCH) */
2717
signal(sig, handler);
2719
#endif /* HAVE_SIGACTION */
2721
#if defined(SIGTSTP) && !defined(USE_SLANG)
2722
#ifdef HAVE_SIGACTION
2724
* For switching a signal's handling between SIG_DFL and something
2725
* (possibly) different that may have been set up by lynx code or
2726
* e.g. by curses library. Uses sigaction to preserve / restore as
2727
* much state as possible.
2728
* Second arg is where to save or restore from.
2729
* Third arg to_dfl specifies what to do:
2730
* 1 Save current state in where, set handling to SIG_DFL
2731
* 0 Restore current state to previously saved one in where
2733
* Currently only used for SIGTSTP without SLANG, to prevent (n)curses
2734
* signal handler from running while lynx is waiting in system() for
2735
* an interactive command like an editor. - kw
2737
PRIVATE BOOLEAN LYToggleSigDfl ARGS3(
2739
struct sigaction *, where,
2743
struct sigaction oact;
2746
rv = sigaction(sig, NULL, &oact);
2748
if (oact.sa_handler != SIG_DFL) {
2749
oact.sa_handler = SIG_DFL;
2750
rv = sigaction(sig, &oact, where);
2752
memcpy(where, &oact, sizeof(oact));
2757
rv = sigaction(sig, where, NULL);
2760
CTRACE((tfp, "Error in LYToggleSigDfl: %s\n", LYStrerror(errno)));
2765
#endif /* HAVE_SIGACTION */
2766
#endif /* SIGTSTP && !USE_SLANG */
2769
** This bit of code catches window size change signals
2772
#ifdef HAVE_SYS_IOCTL_H
2773
#include <sys/ioctl.h>
2776
/* For systems that have both, but both can't be included, duh (or neither) */
2777
/* FIXME: this whole chunk may be redundant */
2778
#ifdef TERMIO_AND_CURSES
2779
# ifdef TERMIO_AND_TERMIOS
2780
# include <termio.h>
2782
# ifdef HAVE_TERMIOS_H
2783
# include <termios.h>
2785
# ifdef HAVE_TERMIO_H
2786
# include <termio.h>
2787
# endif /* HAVE_TERMIO_H */
2788
# endif /* HAVE_TERMIOS_H */
2789
# endif /* TERMIO_AND_TERMIOS */
2790
#endif /* TERMIO_AND_CURSES */
2792
PUBLIC void size_change ARGS1(
2793
int, sig GCC_UNUSED)
2795
int old_lines = LYlines;
2796
int old_cols = LYcols;
2799
#if defined(VMS) || defined(UNIX)
2800
SLtt_get_screen_size();
2801
#endif /* VMS || UNIX */
2802
LYlines = SLtt_Screen_Rows;
2803
LYcols = SLtt_Screen_Cols;
2804
#ifdef SLANG_MBCS_HACK
2805
PHYSICAL_SLtt_Screen_Cols = LYcols;
2806
#ifdef SLANG_NO_LIMIT /* define this if slang has been fixed */
2807
SLtt_Screen_Cols = (LYcols-1) * 6;
2809
/* Needs to be limited: fixed buffer bugs in slang can cause crash,
2810
see slang's SLtt_smart_puts - kw */
2811
SLtt_Screen_Cols = HTMIN((LYcols-1) * 6, 255);
2813
#endif /* SLANG_MBCS_HACK */
2816
* Called from start_curses().
2820
#ifdef HAVE_SIZECHANGE
2826
#endif /* TIOCGWINSZ */
2827
#endif /* TIOCGSIZE */
2830
if (ioctl(0, TIOCGSIZE, &win) == 0) {
2831
if (win.ts_lines != 0) {
2832
LYlines = win.ts_lines;
2834
if (win.ts_cols != 0) {
2835
LYcols = win.ts_cols;
2840
if (ioctl(0, TIOCGWINSZ, &win) == 0) {
2841
if (win.ws_row != 0) {
2842
LYlines = win.ws_row;
2844
if (win.ws_col != 0) {
2845
LYcols = win.ws_col;
2848
#endif /* TIOCGWINSZ */
2849
#endif /* TIOCGSIZE */
2850
#endif /* HAVE_SIZECHANGE */
2857
LYcols = scrsize[0];
2858
LYlines = scrsize[1];
2866
#endif /* USE_SLANG */
2869
* Check if the screen size has actually changed. - AJL
2871
if (LYlines != old_lines || LYcols != old_cols) {
2872
recent_sizechange = TRUE;
2873
CTRACE((tfp, "Window size changed from (%d,%d) to (%d,%d)\n",
2874
old_lines, old_cols, LYlines, LYcols));
2875
#if defined(CAN_SWITCH_DISPLAY_CHARSET) && defined(CAN_AUTODETECT_DISPLAY_CHARSET)
2876
/* May need to reload the font due to different char-box size */
2877
if (current_char_set != auto_display_charset)
2878
Switch_Display_Charset(current_char_set, SWITCH_DISPLAY_CHARSET_RESIZE);
2882
LYExtSignal (SIGWINCH, size_change);
2883
#endif /* SIGWINCH */
2889
* Utility for freeing the list of previous suggested filenames. - FM
2891
PUBLIC void HTSugFilenames_free NOARGS
2894
HTList *cur = sug_filenames;
2899
while (NULL != (fname = (char *)HTList_nextObject(cur))) {
2902
HTList_delete(sug_filenames);
2903
sug_filenames = NULL;
2908
* Utility for listing suggested filenames, making any
2909
* repeated filenames the most current in the list. - FM
2911
PUBLIC void HTAddSugFilename ARGS1(
2918
if (!non_empty(fname))
2921
StrAllocCopy(new, fname);
2923
if (!sug_filenames) {
2924
sug_filenames = HTList_new();
2925
#ifdef LY_FIND_LEAKS
2926
atexit(HTSugFilenames_free);
2928
HTList_addObject(sug_filenames, new);
2932
cur = sug_filenames;
2933
while (NULL != (old = (char *)HTList_nextObject(cur))) {
2934
if (!strcmp(old, new)) {
2935
HTList_removeObject(sug_filenames, old);
2940
HTList_addObject(sug_filenames, new);
2946
* CHANGE_SUG_FILENAME -- Foteos Macrides 29-Dec-1993
2947
* Upgraded for use with Lynx2.2 - FM 17-Jan-1994
2949
PUBLIC void change_sug_filename ARGS1(
2953
char *temp = 0, *cp, *cp1, *end;
2960
* Establish the current end of fname.
2962
end = fname + strlen(fname);
2970
* Rename any temporary files.
2972
cp2 = wwwName(lynx_temp_space);
2974
if (LYIsHtmlSep(*cp2)) {
2975
HTSprintf0(&temp, "file://localhost%s%04x", cp2, GETPID());
2977
HTSprintf0(&temp, "file://localhost/%s%04x", cp2, GETPID());
2980
if (LYIsHtmlSep(*cp2)) {
2981
HTSprintf0(&temp, "file://localhost%s%d", cp2, (int)getpid());
2983
HTSprintf0(&temp, "file://localhost/%s%d", cp2, (int)getpid());
2986
if (!strncmp(fname, temp, strlen(temp))) {
2987
cp = strrchr(fname, '.');
2988
if (strlen(cp) > (strlen(temp) - 4))
2990
StrAllocCopy(temp, NonNull(cp));
2991
sprintf(fname, "temp%.*s", LY_MAXPATH - 10, temp);
2995
if (fname[strlen(fname) - 1] == '/')
2997
* Hmm... we have a directory name.
2998
* It is annoying to see a scheme+host+path name as a suggested one,
2999
* let's remove the last_slash and go ahead like we have a file name. - LP
3001
fname[strlen(fname) - 1] = '\0';
3004
* Remove everything up the the last_slash if there is one.
3006
if ((cp = strrchr(fname,'/')) != NULL && strlen(cp) > 1) {
3009
* Go past the slash.
3012
for (; *cp != '\0'; cp++, cp1++) {
3017
#ifdef _WINDOWS /* 1998/05/05 (Tue) 10:08:05 */
3018
if ((cp = strrchr(fname,'=')) != NULL && strlen(cp) > 1) {
3024
for (; *cp != '\0'; cp++, cp1++) {
3032
* Trim off date-size suffix, if present.
3034
if ((*(end - 1) == ']') && ((cp = strrchr(fname, '[')) != NULL) &&
3035
(cp > fname) && *(--cp) == ' ') {
3036
while (*cp == ' ') {
3042
* Trim off VMS device and/or directory specs, if present.
3044
if ((cp = strchr(fname,'[')) != NULL &&
3045
(cp1 = strrchr(cp,']')) != NULL && strlen(cp1) > 1) {
3047
for (cp=fname; *cp1 != '\0'; cp1++) {
3055
* Replace illegal or problem characters.
3057
dot = fname + strlen(fname);
3058
for (cp = fname; cp < dot; cp++) {
3060
* Replace with underscores.
3062
if (*cp == ' ' || *cp == '/' || *cp == ':' ||
3063
*cp == '[' || *cp == ']' || *cp == '&') {
3066
* Replace with dashes.
3068
} else if (*cp == '!' || *cp == '?' || *cp == '\'' ||
3069
*cp == ',' || *cp == ':' || *cp == '\"' ||
3070
*cp == '+' || *cp == '@' || *cp == '\\' ||
3071
*cp == '(' || *cp == ')' || *cp == '=' ||
3072
*cp == '<' || *cp == '>' || *cp == '#' ||
3073
*cp == '%' || *cp == '*' || *cp == '`' ||
3074
*cp == '~' || *cp == '^' || *cp == '|' ||
3075
*cp < ' ' || (UCH(*cp)) > 126) {
3081
* Collapse any serial underscores.
3086
if (fname[j] == '_' && *cp == '_') {
3095
* Collapse any serial dashes.
3097
dot = fname + (strlen(fname));
3101
if (fname[j] == '-' && *cp == '-') {
3110
* Trim any trailing or leading
3111
* underscores or dashes.
3113
cp = fname + (strlen(fname)) - 1;
3114
while (*cp == '_' || *cp == '-') {
3117
if (fname[0] == '_' || fname[0] == '-') {
3118
dot = fname + (strlen(fname));
3120
while ((*cp == '_' || *cp == '-') && cp < dot) {
3131
* Replace all but the last period with _'s, or second
3132
* to last if last is followed by a terminal Z or z,
3134
* e.g., convert foo.tar.Z to
3136
* or, convert foo.tar.gz to
3139
j = strlen(fname) - 1;
3140
if ((dot = strrchr(fname, '.')) != NULL) {
3141
if (TOUPPER(fname[j]) == 'Z') {
3142
if ((fname[j-1] == '.') &&
3143
(((cp = strchr(fname, '.')) != NULL) && cp < dot)) {
3145
dot = strrchr(fname, '.');
3146
} else if (((TOUPPER(fname[j-1]) == 'G') &&
3147
fname[j-2] == '.') &&
3148
(((cp = strchr(fname, '.')) != NULL) && cp < dot)) {
3150
dot = strrchr(fname, '.');
3154
while ((cp = strchr(cp, '.')) != NULL && cp < dot) {
3159
* But if the root is > 39 characters, move
3160
* the period appropriately to the left.
3162
while (dot - fname > 39) {
3164
if ((cp = strrchr(fname, '_')) != NULL) {
3167
} else if ((cp = strrchr(fname, '-')) != NULL) {
3170
} else if (*(dot + 1) == '\0') {
3173
fname[j] = fname[j-1];
3181
while (dot[k] != '\0') {
3182
fname[j++] = dot[k++];
3186
dot = strrchr(fname, '.');
3190
* Make sure the extension is < 40 characters.
3192
if ((fname + strlen(fname) - dot) > 39) {
3197
* Trim trailing dashes or underscores.
3199
j = (strlen(fname) - 1);
3200
while (fname[j] == '_' || fname[j] == '-') {
3205
* No period, so put one on the end, or after
3206
* the 39th character, trimming trailing dashes
3209
if (strlen(fname) > 39) {
3212
j = (strlen(fname) - 1);
3213
while ((fname[j] == '_') || (fname[j] == '-')) {
3220
#else /* Not VMS (UNIX): */
3223
* Replace problem characters.
3225
for (cp = fname; *cp != '\0'; cp++) {
3234
#endif /* VMS (UNIX) */
3237
* Make sure the rest of the original string in nulled.
3239
cp = fname + strlen(fname);
3248
* Construct a temporary-filename. Assumes result is LY_MAXPATH chars long.
3250
PRIVATE int fmt_tempname ARGS3(
3252
CONST char *, prefix,
3253
CONST char *, suffix)
3256
#ifdef USE_RAND_TEMPNAME
3257
#define SIZE_TEMPNAME ((MAX_TEMPNAME / BITS_PER_CHAR) + 1)
3258
static BOOL first = TRUE;
3259
static int names_used = 0;
3260
static unsigned char used_tempname[SIZE_TEMPNAME];
3261
unsigned offset, mask;
3263
static unsigned counter;
3264
char leaf[LY_MAXPATH];
3271
* Prefer a random value rather than a counter.
3273
#ifdef USE_RAND_TEMPNAME
3275
lynx_srand((unsigned)((long)time((time_t *)0) + (long)result));
3279
/* We don't really need all of the bits from rand(). The high-order bits
3280
* are the more-random portion in any case, but limiting the width of the
3281
* generated name is done partly to avoid problems on systems that may not
3282
* support long filenames.
3284
counter = MAX_TEMPNAME;
3285
while (names_used < MAX_TEMPNAME) {
3286
counter = (unsigned)(( (float)MAX_TEMPNAME * lynx_rand() ) / LYNX_RAND_MAX + 1);
3287
counter %= SIZE_TEMPNAME; /* just in case... */
3289
* Avoid reusing a temporary name, since there are places in the code
3290
* which can refer to a temporary filename even after it has been
3291
* closed and removed from the filesystem.
3293
offset = counter / BITS_PER_CHAR;
3294
mask = 1 << (counter % BITS_PER_CHAR);
3295
if ((used_tempname[offset] & mask) == 0) {
3297
used_tempname[offset] |= mask;
3301
if (names_used >= MAX_TEMPNAME)
3302
HTAlert(gettext("Too many tempfiles"));
3309
* The 'lynx_temp_space' string ends with a '/' or '\\', so we only have to
3310
* limit the length of the leaf. As received (e.g., from HTCompressed),
3311
* the suffix may contain more than a ".htm", e.g., "-txt.gz", so we trim
3312
* off from the filename portion to make room.
3315
sprintf(leaf, "%04x%04x", counter, (unsigned)GETPID());
3317
sprintf(leaf, "%u%u", counter, (unsigned)getpid());
3319
if (strlen(leaf) > 8)
3321
if (strlen(suffix) > 4 || *suffix != '.') {
3322
CONST char *tail = strchr(suffix, '.');
3324
tail = suffix + strlen(suffix);
3325
if (8 - (tail - suffix) >= 0)
3326
leaf[8 - (tail - suffix)] = 0;
3328
strcat(leaf, suffix);
3330
sprintf(leaf, "L%u-%uTMP%s", (unsigned)getpid(), counter, suffix);
3333
* Someone could have configured the temporary pathname to be too long.
3335
if ((strlen(prefix) + strlen(leaf)) < LY_MAXPATH) {
3336
sprintf(result, "%s%s", prefix, leaf);
3339
sprintf(result, "%.*s", LY_MAXPATH-1, leaf);
3342
CTRACE((tfp, "-> '%s'\n", result));
3347
* Convert 4, 6, 2, 8 to left, right, down, up, etc.
3349
PUBLIC int number2arrows ARGS1(
3386
* parse_restrictions takes a string of comma-separated restrictions
3387
* and sets the corresponding flags to restrict the facilities available.
3389
/* The first two are special: we want to record whether "default" or
3390
* "all" restrictions were applied, in addition to the detailed effects
3391
* of those options. - kw
3393
/* skip the special flags when processing "all" and "default": */
3394
#define N_SPECIAL_RESTRICT_OPTIONS 2
3396
PRIVATE CONST struct {
3400
} restrictions[] = {
3401
{ "default", &had_restrictions_default, TRUE },
3402
{ "all", &had_restrictions_all, TRUE },
3403
{ "inside_telnet", &no_inside_telnet, CAN_ANONYMOUS_INSIDE_DOMAIN_TELNET },
3404
{ "outside_telnet", &no_outside_telnet, CAN_ANONYMOUS_OUTSIDE_DOMAIN_TELNET },
3405
{ "telnet_port", &no_telnet_port, CAN_ANONYMOUS_GOTO_TELNET_PORT },
3406
{ "inside_ftp", &no_inside_ftp, CAN_ANONYMOUS_INSIDE_DOMAIN_FTP },
3407
{ "outside_ftp", &no_outside_ftp, CAN_ANONYMOUS_OUTSIDE_DOMAIN_FTP },
3408
{ "inside_rlogin", &no_inside_rlogin, CAN_ANONYMOUS_INSIDE_DOMAIN_RLOGIN },
3409
{ "outside_rlogin", &no_outside_rlogin, CAN_ANONYMOUS_OUTSIDE_DOMAIN_RLOGIN },
3410
{ "suspend", &no_suspend, FALSE },
3411
{ "editor", &no_editor, FALSE },
3412
{ "shell", &no_shell, FALSE },
3413
{ "bookmark", &no_bookmark, FALSE },
3414
{ "multibook", &no_multibook, FALSE },
3415
{ "bookmark_exec", &no_bookmark_exec, FALSE },
3416
{ "option_save", &no_option_save, FALSE },
3417
{ "print", &no_print, CAN_ANONYMOUS_PRINT },
3418
{ "download", &no_download, FALSE },
3419
{ "disk_save", &no_disk_save, FALSE },
3420
#if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)
3421
{ "exec", &no_exec, LOCAL_EXECUTION_LINKS_ALWAYS_OFF_FOR_ANONYMOUS },
3423
{ "lynxcgi", &no_lynxcgi, FALSE },
3424
{ "exec_frozen", &exec_frozen, FALSE },
3425
{ "goto", &no_goto, CAN_ANONYMOUS_GOTO },
3426
{ "jump", &no_jump, CAN_ANONYMOUS_JUMP },
3427
{ "file_url", &no_file_url, FALSE },
3428
#ifndef DISABLE_NEWS
3429
{ "news_post", &no_newspost, FALSE },
3430
{ "inside_news", &no_inside_news, CAN_ANONYMOUS_INSIDE_DOMAIN_READ_NEWS },
3431
{ "outside_news", &no_outside_news, CAN_ANONYMOUS_OUTSIDE_DOMAIN_READ_NEWS },
3433
{ "mail", &no_mail, CAN_ANONYMOUS_MAIL },
3434
{ "dotfiles", &no_dotfiles, FALSE },
3435
{ "useragent", &no_useragent, FALSE },
3436
#ifdef SUPPORT_CHDIR
3437
{ "chdir", &no_chdir, FALSE },
3439
#ifdef DIRED_SUPPORT
3440
{ "dired_support", &no_dired_support, FALSE },
3442
{ "change_exec_perms", &no_change_exec_perms, FALSE },
3443
#endif /* OK_PERMIT */
3444
#endif /* DIRED_SUPPORT */
3445
#ifdef USE_EXTERNALS
3446
{ "externals", &no_externals, FALSE },
3448
{ "lynxcfg_info", &no_lynxcfg_info, CAN_ANONYMOUS_VIEW_LYNXCFG_INFO },
3449
#ifndef NO_CONFIG_INFO
3450
{ "lynxcfg_xinfo", &no_lynxcfg_xinfo, CAN_ANONYMOUS_VIEW_LYNXCFG_EXTENDED_INFO },
3451
#ifdef HAVE_CONFIG_H
3452
{ "compileopts_info", &no_compileopts_info, CAN_ANONYMOUS_VIEW_COMPILEOPTS_INFO },
3455
/* put "goto" restrictions on the end, since they are a refinement */
3456
#ifndef DISABLE_BIBP
3457
{ "goto_bibp", &no_goto_bibp, CAN_ANONYMOUS_GOTO_BIBP },
3459
#ifdef HAVE_CONFIG_H
3460
#ifndef NO_CONFIG_INFO
3461
{ "goto_configinfo", &no_goto_configinfo, CAN_ANONYMOUS_GOTO_CONFIGINFO },
3464
{ "goto_cso", &no_goto_cso, CAN_ANONYMOUS_GOTO_CSO },
3465
{ "goto_file", &no_goto_file, CAN_ANONYMOUS_GOTO_FILE },
3466
#ifndef DISABLE_FINGER
3467
{ "goto_finger", &no_goto_finger, CAN_ANONYMOUS_GOTO_FINGER },
3469
{ "goto_ftp", &no_goto_ftp, CAN_ANONYMOUS_GOTO_FTP },
3470
#ifndef DISABLE_GOPHER
3471
{ "goto_gopher", &no_goto_gopher, CAN_ANONYMOUS_GOTO_GOPHER },
3473
{ "goto_http", &no_goto_http, CAN_ANONYMOUS_GOTO_HTTP },
3474
{ "goto_https", &no_goto_https, CAN_ANONYMOUS_GOTO_HTTPS },
3475
{ "goto_lynxcgi", &no_goto_lynxcgi, CAN_ANONYMOUS_GOTO_LYNXCGI },
3476
{ "goto_lynxexec", &no_goto_lynxexec, CAN_ANONYMOUS_GOTO_LYNXEXEC },
3477
{ "goto_lynxprog", &no_goto_lynxprog, CAN_ANONYMOUS_GOTO_LYNXPROG },
3478
{ "goto_mailto", &no_goto_mailto, CAN_ANONYMOUS_GOTO_MAILTO },
3479
#ifndef DISABLE_NEWS
3480
{ "goto_news", &no_goto_news, CAN_ANONYMOUS_GOTO_NEWS },
3481
{ "goto_nntp", &no_goto_nntp, CAN_ANONYMOUS_GOTO_NNTP },
3483
{ "goto_rlogin", &no_goto_rlogin, CAN_ANONYMOUS_GOTO_RLOGIN },
3484
#ifndef DISABLE_NEWS
3485
{ "goto_snews", &no_goto_snews, CAN_ANONYMOUS_GOTO_SNEWS },
3487
{ "goto_telnet", &no_goto_telnet, CAN_ANONYMOUS_GOTO_TELNET },
3488
{ "goto_tn3270", &no_goto_tn3270, CAN_ANONYMOUS_GOTO_TN3270 },
3489
{ "goto_wais", &no_goto_wais, CAN_ANONYMOUS_GOTO_WAIS },
3492
/* This will make no difference between '-' and '_'. It does only in/equality
3493
compare. It assumes that p2 can't contain dashes, but p1 can.
3494
This function is also used (if macro OPTNAME_ALLOW_DASHES doesn't have
3495
value of zero) for compare of commandline options -VH
3497
PUBLIC BOOL strn_dash_equ ARGS3(
3504
return 0;/* canonical name is shorter */
3523
/* Uncomment following lines to allow only exact string matching */
3524
/* #define RESTRICT_NM_ALLOW_DASHES 0 */
3526
#ifndef RESTRICT_NM_ALLOW_DASHES
3527
# define RESTRICT_NM_ALLOW_DASHES 1
3530
#if RESTRICT_NM_ALLOW_DASHES
3531
# define RESTRICT_NM_EQU(a,b,len) strn_dash_equ(a,b,len)
3533
# define RESTRICT_NM_EQU(a,b,len) STRNEQ(a,b,len)
3537
* Returns the inx'th name from the restrictions table, or null if inx is
3540
PUBLIC CONST char *index_to_restriction ARGS1(
3543
if (inx >= 0 && inx < (int) TABLESIZE(restrictions))
3544
return restrictions[inx].name;
3549
* Returns the value TRUE/FALSE of a given restriction, or -1 if it is not
3550
* one that we recognize.
3552
PUBLIC int find_restriction ARGS2(
3559
for (i=0; i < TABLESIZE(restrictions); i++) {
3560
if (RESTRICT_NM_EQU(name, restrictions[i].name, len)) {
3561
return (*restrictions[i].flag);
3567
PUBLIC void parse_restrictions ARGS1(
3577
p = LYSkipCBlanks(p);
3581
while (*p != ',' && *p != '\0')
3585
if (RESTRICT_NM_EQU(word, "all", p-word)) {
3587
for (i = N_SPECIAL_RESTRICT_OPTIONS; i < TABLESIZE(restrictions); i++)
3588
*(restrictions[i].flag) = TRUE;
3589
} else if (RESTRICT_NM_EQU(word, "default", p-word)) {
3591
for (i = N_SPECIAL_RESTRICT_OPTIONS; i < TABLESIZE(restrictions); i++)
3592
*(restrictions[i].flag) = !restrictions[i].can;
3594
for (i=0; i < TABLESIZE(restrictions); i++) {
3595
if (RESTRICT_NM_EQU(word, restrictions[i].name, p-word)) {
3596
*(restrictions[i].flag) = TRUE;
3603
printf("%s: %.*s\n", gettext("unknown restriction"), p-word, word);
3611
* If shell is restricted, set restrictions on related topics.
3614
no_goto_lynxexec = TRUE;
3615
no_goto_lynxprog = TRUE;
3616
no_goto_lynxcgi = TRUE;
3618
local_exec_on_local_files = TRUE;
3623
PUBLIC void print_restrictions_to_fd ARGS1(
3626
unsigned i, count = 0;
3628
for (i=0; i < TABLESIZE(restrictions); i++) {
3629
if (*(restrictions[i].flag) == TRUE) {
3634
fprintf(fp, gettext("No restrictions set.\n"));
3637
fprintf(fp, gettext("Restrictions set:\n"));
3638
for (i=0; i < TABLESIZE(restrictions); i++) {
3639
if (*(restrictions[i].flag) == TRUE) {
3640
/* if "goto" is restricted, don't bother tell about its
3643
if (strncmp(restrictions[i].name, "goto_", 5)
3645
fprintf(fp, " %s\n", restrictions[i].name);
3652
#include <maildef.h>
3653
#include <starlet.h>
3655
typedef struct _VMSMailItemList
3657
short buffer_length;
3659
void *buffer_address;
3660
long *return_length_address;
3663
PUBLIC void LYCheckMail NOARGS
3665
static BOOL firsttime = TRUE, failure = FALSE;
3666
static char user[13], dir[252];
3667
static long userlen = 0, dirlen;
3668
static time_t lastcheck = 0;
3670
static short new, lastcount;
3671
long ucontext = 0, status;
3672
short flags = MAIL$M_NEWMSG;
3674
null_list[] = {{0,0,0,0}},
3675
jpi_list[] = {{sizeof(user) - 1,JPI$_USERNAME,(void *)user,&userlen},
3677
uilist[] = {{0,MAIL$_USER_USERNAME,0,0},
3679
uolist[] = {{sizeof(new),MAIL$_USER_NEW_MESSAGES,&new,0},
3680
{sizeof(dir),MAIL$_USER_FULL_DIRECTORY,dir,&dirlen},
3682
extern long mail$user_begin();
3683
extern long mail$user_get_info();
3684
extern long mail$user_end();
3691
/* Get the username. */
3692
status = sys$getjpiw(0,0,0,jpi_list,0,0,0);
3693
if (!(status & 1)) {
3697
user[userlen] = '\0';
3698
LYTrimTrailing(user);
3701
/* Minimum report interval is 60 sec. */
3703
if (now - lastcheck < 60)
3707
/* Get the current newmail count. */
3708
status = mail$user_begin(&ucontext,null_list,null_list);
3709
if (!(status & 1)) {
3713
uilist[0].buffer_length = strlen(user);
3714
uilist[0].buffer_address = user;
3715
status = mail$user_get_info(&ucontext,uilist,uolist);
3716
if (!(status & 1)) {
3721
/* Should we report anything to the user? */
3724
/* Have newmail at startup of Lynx. */
3725
HTUserMsg(HAVE_UNREAD_MAIL_MSG);
3726
else if (new > lastcount)
3727
/* Have additional mail since last report. */
3728
HTUserMsg(HAVE_NEW_MAIL_MSG);
3734
/* Clear the context */
3735
mail$user_end((long *)&ucontext,null_list,null_list);
3739
PUBLIC void LYCheckMail NOARGS
3741
static BOOL firsttime = TRUE;
3743
static time_t lastcheck;
3744
static time_t lasttime;
3745
static long lastsize;
3750
mf = LYGetEnv("MAIL");
3759
if (now - lastcheck < 60)
3763
if ((stat(mf,&st) < 0)
3764
|| !S_ISREG(st.st_mode)) {
3769
if (st.st_size > 0) {
3770
if (((lasttime != st.st_mtime) && (st.st_mtime > st.st_atime))
3771
|| ((lastsize != 0) && (st.st_size > lastsize)))
3772
HTUserMsg(HAVE_NEW_MAIL_MSG);
3773
else if (lastsize == 0)
3774
HTUserMsg(HAVE_MAIL_MSG);
3776
lastsize = st.st_size;
3777
lasttime = st.st_mtime;
3783
** This function ensures that an href will be
3784
** converted to a fully resolved, absolute URL,
3785
** with guessing of the host or expansions of
3786
** lead tildes via LYConvertToURL() if needed,
3787
** and tweaking/simplifying via HTParse(). It
3788
** is used for LynxHome, startfile, homepage,
3789
** and 'g'oto entries, after they have been
3790
** passed to LYFillLocalFileURL(). - FM
3791
** Such URLs have no `base' reference to which they
3792
** could be resolved. LYLegitimizeHREF could not be used.
3794
PUBLIC void LYEnsureAbsoluteURL ARGS3(
3805
* Check whether to fill in localhost. - FM
3807
LYFillLocalFileURL(href, "file://localhost");
3810
* If it is not a URL then make it one.
3812
if (!strcasecomp(*href, STR_NEWS_URL)) {
3813
StrAllocCat(*href, "*");
3814
} else if (!strcasecomp(*href, STR_SNEWS_URL)) {
3815
StrAllocCat(*href, "/*");
3818
if (!is_url(*href)) {
3819
CTRACE((tfp, "%s%s'%s' is not a URL\n",
3820
NonNull(name), (name ? " " : ""), *href));
3821
LYConvertToURL(href, fixit);
3824
temp = HTParse(*href, "", PARSE_ALL);
3825
if (non_empty(temp))
3826
StrAllocCopy(*href, temp);
3831
* Rewrite and reallocate a previously allocated string
3832
* as a file URL if the string resolves to a file or
3833
* directory on the local system, otherwise as an
3836
PUBLIC void LYConvertToURL ARGS2(
3837
char **, AllocatedString,
3840
char *old_string = *AllocatedString;
3847
if (!old_string || *old_string == '\0')
3850
#if defined(USE_DOS_DRIVES)
3852
char *cp_url = *AllocatedString;
3853
for(; *cp_url != '\0'; cp_url++)
3854
if (*cp_url == '\\')
3857
if (LYIsDosDrive(*AllocatedString) && *cp_url == ':')
3858
LYAddPathSep(AllocatedString);
3860
#endif /* USE_DOS_DRIVES */
3862
*AllocatedString = NULL; /* so StrAllocCopy doesn't free it */
3863
StrAllocCopy(*AllocatedString, "file://localhost");
3865
if (*old_string != '/') {
3866
char *fragment = NULL;
3867
#if defined(USE_DOS_DRIVES)
3868
StrAllocCat(*AllocatedString,"/");
3869
#endif /* USE_DOS_DRIVES */
3872
* Not a SHELL pathspec. Get the full VMS spec and convert it.
3874
char *cur_dir = NULL;
3875
static char url_file[LY_MAXPATH], file_name[LY_MAXPATH], dir_name[LY_MAXPATH];
3876
unsigned long context = 0;
3877
$DESCRIPTOR(url_file_dsc, url_file);
3878
$DESCRIPTOR(file_name_dsc, file_name);
3879
if (*old_string == '~') {
3881
* On VMS, we'll accept '~' on the command line as
3882
* Home_Dir(), and assume the rest of the path, if
3883
* any, has SHELL syntax.
3885
StrAllocCat(*AllocatedString, HTVMS_wwwName(Home_Dir()));
3886
if ((cp = strchr(old_string, '/')) != NULL) {
3888
* Append rest of path, if present, skipping "user" if
3889
* "~user" was entered, simplifying, and eliminating
3890
* any residual relative elements. - FM
3892
StrAllocCopy(temp, cp);
3893
LYTrimRelFromAbsPath(temp);
3894
StrAllocCat(*AllocatedString, temp);
3899
fragment = trimPoundSelector(old_string);
3900
LYstrncpy(url_file, old_string, sizeof(url_file)-1);
3902
url_file_dsc.dsc$w_length = (short) strlen(url_file);
3903
if (1&lib$find_file(&url_file_dsc, &file_name_dsc, &context,
3906
* We found the file. Convert to a URL pathspec.
3908
if ((cp = strchr(file_name, ';')) != NULL) {
3911
LYLowerCase(file_name);
3912
StrAllocCat(*AllocatedString, HTVMS_wwwName(file_name));
3913
if ((cp = strchr(old_string, ';')) != NULL) {
3914
StrAllocCat(*AllocatedString, cp);
3916
if (fragment != NULL) {
3917
restorePoundSelector(fragment);
3918
StrAllocCat(*AllocatedString, fragment);
3921
} else if ((NULL != getcwd(dir_name, sizeof(dir_name)-1, 0)) &&
3922
0 == chdir(old_string)) {
3924
* Probably a directory. Try converting that.
3926
StrAllocCopy(cur_dir, dir_name);
3927
restorePoundSelector(fragment);
3928
if (NULL != getcwd(dir_name, sizeof(dir_name)-1, 0)) {
3932
LYLowerCase(dir_name);
3933
StrAllocCat(*AllocatedString, dir_name);
3934
if (fragment != NULL) {
3935
StrAllocCat(*AllocatedString, fragment);
3940
* Nope. Assume it's an http URL with
3941
* the "http://" defaulted, if we can't
3942
* rule out a bad VMS path.
3945
if (strchr(old_string, '[') ||
3946
((cp = strchr(old_string, ':')) != NULL &&
3947
!isdigit(UCH(cp[1]))) ||
3948
!LYExpandHostForURL((char **)&old_string,
3950
URLDomainSuffixes)) {
3952
* Probably a bad VMS path (but can't be
3953
* sure). Use original pathspec for the
3954
* error message that will result.
3956
sprintf(url_file, "/%.*s", sizeof(url_file)-2, old_string);
3957
CTRACE((tfp, "Can't find '%s' Will assume it's a bad path.\n",
3959
StrAllocCat(*AllocatedString, url_file);
3962
* Assume a URL is wanted, so guess the
3963
* scheme with "http://" as the default. - FM
3965
if (!LYAddSchemeForURL((char **)&old_string, "http://")) {
3966
StrAllocCopy(*AllocatedString, "http://");
3967
StrAllocCat(*AllocatedString, old_string);
3969
StrAllocCopy(*AllocatedString, old_string);
3975
* Nothing found. Assume it's an http URL
3976
* with the "http://" defaulted, if we can't
3977
* rule out a bad VMS path.
3979
restorePoundSelector(fragment);
3982
if (strchr(old_string, '[') ||
3983
((cp = strchr(old_string, ':')) != NULL &&
3984
!isdigit(UCH(cp[1]))) ||
3985
!LYExpandHostForURL((char **)&old_string,
3987
URLDomainSuffixes)) {
3989
* Probably a bad VMS path (but can't be
3990
* sure). Use original pathspec for the
3991
* error message that will result.
3993
sprintf(url_file, "/%.*s", sizeof(url_file)-2, old_string);
3994
CTRACE((tfp, "Can't find '%s' Will assume it's a bad path.\n",
3996
StrAllocCat(*AllocatedString, url_file);
3999
* Assume a URL is wanted, so guess the
4000
* scheme with "http://" as the default. - FM
4002
if (!LYAddSchemeForURL((char **)&old_string, "http://")) {
4003
StrAllocCopy(*AllocatedString, "http://");
4004
StrAllocCat(*AllocatedString, old_string);
4006
StrAllocCopy(*AllocatedString, old_string);
4010
lib$find_file_end(&context);
4013
CTRACE((tfp, "Trying: '%s'\n", *AllocatedString));
4014
#else /* not VMS: */
4015
#if defined(USE_DOS_DRIVES)
4017
if (*old_string == '.') {
4018
char fullpath[MAX_PATH + 1];
4019
char *filepart = NULL;
4022
chk = GetFullPathNameA(old_string, MAX_PATH + 1,
4023
fullpath, &filepart);
4025
StrAllocCopy(temp, wwwName(fullpath));
4026
StrAllocCat(*AllocatedString, temp);
4028
CTRACE((tfp, "Converted '%s' to '%s'\n",
4029
old_string, *AllocatedString));
4031
StrAllocCat(*AllocatedString, old_string);
4035
if (strlen(old_string) == 1 && *old_string == '.') {
4039
char curdir[LY_MAXPATH];
4040
StrAllocCopy(temp, wwwName(Current_Dir(curdir)));
4041
StrAllocCat(*AllocatedString, temp);
4043
CTRACE((tfp, "Converted '%s' to '%s'\n",
4044
old_string, *AllocatedString));
4048
#endif /* USE_DOS_DRIVES */
4049
if (*old_string == '~') {
4051
* On Unix, convert '~' to Home_Dir().
4053
StrAllocCat(*AllocatedString, wwwName(Home_Dir()));
4054
if ((cp = strchr(old_string, '/')) != NULL) {
4056
* Append rest of path, if present, skipping "user" if
4057
* "~user" was entered, simplifying, and eliminating
4058
* any residual relative elements. - FM
4060
StrAllocCopy(temp, cp);
4061
LYTrimRelFromAbsPath(temp);
4062
StrAllocCat(*AllocatedString, temp);
4065
CTRACE((tfp, "Converted '%s' to '%s'\n",
4066
old_string, *AllocatedString));
4069
* Create a full path to the current default directory.
4071
char curdir[LY_MAXPATH];
4073
BOOL is_local = FALSE;
4074
Current_Dir (curdir);
4076
* Concatenate and simplify, trimming any
4077
* residual relative elements. - FM
4079
#if defined (USE_DOS_DRIVES)
4080
if (old_string[1] != ':' && old_string[1] != '|') {
4081
StrAllocCopy(temp, wwwName(curdir));
4082
LYAddHtmlSep(&temp);
4083
LYstrncpy(curdir, temp, (sizeof(curdir) - 1));
4084
StrAllocCat(temp, old_string);
4087
/* 1998/01/13 (Tue) 12:24:33 */
4088
if (old_string[1] == '|')
4089
old_string[1] = ':';
4090
StrAllocCopy(temp, old_string);
4092
if (strlen(temp) == 2 && LYIsDosDrive(temp))
4093
LYAddPathSep(&temp);
4096
StrAllocCopy(temp, curdir);
4097
StrAllocCat(temp, "/");
4098
StrAllocCat(temp, old_string);
4099
#endif /* USE_DOS_DRIVES */
4100
LYTrimRelFromAbsPath(temp);
4101
CTRACE((tfp, "Converted '%s' to '%s'\n", old_string, temp));
4102
if ((stat(temp, &st) > -1) ||
4103
LYCanReadFile(temp)) {
4105
* It is a subdirectory or file on the local system.
4107
#if defined (USE_DOS_DRIVES)
4108
/* Don't want to see DOS local paths like c: escaped */
4109
/* especially when we really have file://localhost/ */
4110
/* at the beginning. To avoid any confusion we allow */
4111
/* escaping the path if URL specials % or # present. */
4112
if (strchr(temp, '#') == NULL && strchr(temp, '%') == NULL)
4113
StrAllocCopy(cp, temp);
4115
cp = HTEscape(temp, URL_PATH);
4117
cp = HTEscape(temp, URL_PATH);
4118
#endif /* USE_DOS_DRIVES */
4119
StrAllocCat(*AllocatedString, cp);
4121
CTRACE((tfp, "Converted '%s' to '%s'\n",
4122
old_string, *AllocatedString));
4126
StrAllocCopy(temp2, curdir);
4127
LYAddPathSep(&temp2);
4128
StrAllocCopy(cp, old_string);
4129
fragment = trimPoundSelector(cp);
4130
HTUnEscape(cp); /* unescape given path without fragment */
4131
StrAllocCat(temp2, cp); /* append to current dir */
4132
StrAllocCopy(cp2, temp2); /* keep a copy in cp2 */
4133
LYTrimRelFromAbsPath(temp2);
4134
#ifdef WIN_EX /* 1998/07/31 (Fri) 09:09:03 */
4135
HTUnEscape(temp2); /* for LFN */
4138
if (strcmp(temp2, temp) != 0 &&
4139
((stat(temp2, &st) > -1) ||
4140
LYCanReadFile(temp2))) {
4142
* It is a subdirectory or file on the local system
4143
* with escaped characters and/or a fragment to be
4144
* appended to the URL. - FM
4148
if (strcmp(cp2, temp2) == 0) {
4150
* LYTrimRelFromAbsPath did nothing, use
4151
* old_string as given. - kw
4153
temp = HTEscape(curdir, URL_PATH);
4154
LYAddHtmlSep(&temp);
4155
StrAllocCat(temp, old_string);
4157
temp = HTEscape(temp2, URL_PATH);
4158
if (fragment != NULL) {
4159
restorePoundSelector(fragment);
4160
StrAllocCat(temp, fragment);
4163
StrAllocCat(*AllocatedString, temp);
4164
CTRACE((tfp, "Converted '%s' to '%s'\n",
4165
old_string, *AllocatedString));
4168
} else if (strchr(curdir, '#') != NULL ||
4169
strchr(curdir, '%') != NULL) {
4171
* If PWD has some unusual characters, construct a
4172
* filename in temp where those are escaped. This
4173
* is mostly to prevent this function from returning
4174
* with some weird URL if the LYExpandHostForURL tests
4175
* further down fail. - kw
4178
if (strcmp(cp2, temp2) == 0) {
4180
* LYTrimRelFromAbsPath did nothing, use
4181
* old_string as given. - kw
4183
temp = HTEscape(curdir, URL_PATH);
4184
LYAddHtmlSep(&temp);
4185
StrAllocCat(temp, old_string);
4187
temp = HTEscape(temp2, URL_PATH);
4188
if (fragment != NULL) {
4189
restorePoundSelector(fragment);
4190
StrAllocCat(temp, fragment);
4197
if (is_local == FALSE) {
4199
* It's not an accessible subdirectory or file on the
4200
* local system, so assume it's a URL request and guess
4201
* the scheme with "http://" as the default.
4203
CTRACE((tfp, "Can't stat() or fopen() '%s'\n",
4204
temp2 ? temp2 : temp));
4205
#ifdef WIN_EX /* 1998/01/13 (Tue) 09:07:37 */
4208
char buff[LY_MAXPATH + 128];
4211
q = temp2 ? temp2 : temp;
4213
if (strlen(q) == 3 && LYIsDosDrive(q)) {
4215
"'%s' not exist, Goto LynxHome '%s'.", q, p);
4219
StrAllocCat(*AllocatedString, p);
4224
if (LYExpandHostForURL((char **)&old_string,
4228
if (!LYAddSchemeForURL((char **)&old_string, "http://")) {
4229
StrAllocCopy(*AllocatedString, "http://");
4230
StrAllocCat(*AllocatedString, old_string);
4232
StrAllocCopy(*AllocatedString, old_string);
4235
/* RW 1998Mar16 Restore AllocatedString to 'old_string' */
4236
StrAllocCopy(*AllocatedString, old_string);
4238
/* Return file URL for the file that does not exist */
4239
StrAllocCat(*AllocatedString, temp);
4244
CTRACE((tfp, "Trying: '%s'\n", *AllocatedString));
4252
* Path begins with a slash. Simplify and use it.
4254
if (old_string[1] == '\0') {
4256
* Request for root. Respect it on Unix, but
4257
* on VMS we treat that as a listing of the
4258
* login directory. - FM
4261
StrAllocCat(*AllocatedString, HTVMS_wwwName(Home_Dir()));
4263
StrAllocCat(*AllocatedString, "/");
4264
} else if ((stat(old_string, &st) > -1) ||
4265
LYCanReadFile(old_string)) {
4267
* It is an absolute directory or file
4268
* on the local system. - KW
4270
StrAllocCopy(temp, old_string);
4271
LYTrimRelFromAbsPath(temp);
4272
CTRACE((tfp, "Converted '%s' to '%s'\n", old_string, temp));
4273
cp = HTEscape(temp, URL_PATH);
4274
StrAllocCat(*AllocatedString, cp);
4277
CTRACE((tfp, "Converted '%s' to '%s'\n",
4278
old_string, *AllocatedString));
4280
} else if (old_string[1] == '~') {
4282
* Has a Home_Dir() reference. Handle it
4283
* as if there weren't a lead slash. - FM
4285
StrAllocCat(*AllocatedString, wwwName(Home_Dir()));
4286
if ((cp = strchr((old_string + 1), '/')) != NULL) {
4288
* Append rest of path, if present, skipping "user" if
4289
* "~user" was entered, simplifying, and eliminating
4290
* any residual relative elements. - FM
4292
StrAllocCopy(temp, cp);
4293
LYTrimRelFromAbsPath(temp);
4294
StrAllocCat(*AllocatedString, temp);
4299
* Normal absolute path. Simplify, trim any
4300
* residual relative elements, and append it. - FM
4302
StrAllocCopy(temp, old_string);
4303
LYTrimRelFromAbsPath(temp);
4304
StrAllocCat(*AllocatedString, temp);
4307
CTRACE((tfp, "Converted '%s' to '%s'\n",
4308
old_string, *AllocatedString));
4311
/* Pause so we can read the messages before invoking curses */
4312
CTRACE_SLEEP(AlertSecs);
4315
#if defined(_WINDOWS) /* 1998/06/23 (Tue) 16:45:20 */
4317
PUBLIC int win32_check_interrupt(void)
4323
/** Keyboard 'Z' or 'z', or Control-G or Control-C **/
4324
if (LYCharIsINTERRUPT(c) || c == 0x1b) {
4331
void sleep(unsigned sec)
4336
for (j = 0; j < sec; j++) {
4337
for (i = 0; i < 10; i++) {
4349
* This function rewrites and reallocates a previously allocated
4350
* string so that the first element is a confirmed Internet host,
4351
* and returns TRUE, otherwise it does not modify the string and
4352
* returns FALSE. It first tries the element as is, then, if the
4353
* element does not end with a dot, it adds prefixes from the
4354
* (comma separated) prefix list argument, and, if the element
4355
* does not begin with a dot, suffixes from the (comma separated)
4356
* suffix list arguments (e.g., www.host.com, then www.host,edu,
4357
* then www.host.net, then www.host.org). The remaining path, if
4358
* one is present, will be appended to the expanded host. It also
4359
* takes into account whether a colon is in the element or suffix,
4360
* and includes that and what follows as a port field for the
4361
* expanded host field (e.g, wfbr:8002/dir/lynx should yield
4362
* www.wfbr.edu:8002/dir/lynx). The calling function should
4363
* prepend the scheme field (e.g., http://), or pass the string
4364
* to LYAddSchemeForURL(), if this function returns TRUE. - FM
4366
PUBLIC BOOLEAN LYExpandHostForURL ARGS3(
4367
char **, AllocatedString,
4368
char *, prefix_list,
4369
char *, suffix_list)
4371
char DomainPrefix[80], *StartP, *EndP;
4372
char DomainSuffix[80], *StartS, *EndS;
4373
char *Str = NULL, *StrColon = NULL, *MsgStr = NULL;
4374
char *Host = NULL, *HostColon = NULL, *host = NULL;
4376
char *Fragment = NULL;
4377
BOOLEAN GotHost = FALSE;
4378
BOOLEAN Startup = (BOOL) (helpfilepath == NULL);
4380
struct addrinfo hints, *res;
4385
* If it's a NULL or zero-length string,
4386
* or if it begins with a slash or hash,
4387
* don't continue pointlessly. - FM
4389
if (!(*AllocatedString) || *AllocatedString[0] == '\0' ||
4390
*AllocatedString[0] == '/' || *AllocatedString[0] == '#') {
4395
* If it's a partial or relative path,
4396
* don't continue pointlessly. - FM
4398
if (!strncmp(*AllocatedString, "..", 2) ||
4399
!strncmp(*AllocatedString, "./", 2)) {
4404
* Make a clean copy of the string, and trim off the
4405
* path if one is present, but save the information
4406
* so we can restore the path after filling in the
4407
* Host[:port] field. - FM
4409
StrAllocCopy(Str, *AllocatedString);
4410
if ((Path = strchr(Str, '/')) != NULL) {
4412
* Have a path. Any fragment should
4413
* already be included in Path. - FM
4418
* No path, so check for a fragment and
4419
* trim that, to be restored after filling
4420
* in the Host[:port] field. - FM
4422
Fragment = trimPoundSelector(Str);
4426
* If the potential host string has a colon, assume it
4427
* begins a port field, and trim it off, but save the
4428
* information so we can restore the port field after
4429
* filling in the host field. - FM
4431
if ((StrColon = strrchr(Str, ':')) != NULL &&
4432
isdigit(UCH(StrColon[1]))) {
4433
if (StrColon == Str) {
4441
* Do a DNS test on the potential host field
4442
* as presently trimmed. - FM
4444
StrAllocCopy(host, Str);
4447
StrAllocCopy(MsgStr, WWW_FIND_MESSAGE);
4448
StrAllocCat(MsgStr, host);
4449
StrAllocCat(MsgStr, FIRST_SEGMENT);
4451
} else if (Startup && !dump_output_immediately) {
4452
fprintf(stdout, "%s '%s'%s\r\n", WWW_FIND_MESSAGE, host, FIRST_SEGMENT);
4456
memset(&hints, 0, sizeof(hints));
4457
hints.ai_family = PF_UNSPEC;
4458
hints.ai_socktype = SOCK_STREAM;
4459
error = getaddrinfo(host, "80", &hints, &res);
4463
if (LYGetHostByName(host) != NULL)
4467
* Clear any residual interrupt. - FM
4469
if (LYCursesON && HTCheckForInterrupt()) {
4471
"LYExpandHostForURL: Ignoring interrupt because '%s' resolved.\n",
4476
* Return success. - FM
4484
else if (LYCursesON && (lynx_nsl_status == HT_INTERRUPTED))
4487
* Give the user chance to interrupt lookup cycles. - KW & FM
4490
"LYExpandHostForURL: Interrupted while '%s' failed to resolve.\n",
4494
* Return failure. - FM
4503
* Set the first prefix, making it a zero-length string
4504
* if the list is NULL or if the potential host field
4505
* ends with a dot. - FM
4507
StartP = ((prefix_list && Str[strlen(Str)-1] != '.') ?
4510
* If we have a prefix, but the allocated string is
4511
* one of the common host prefixes, make our prefix
4512
* a zero-length string. - FM
4514
if (*StartP && *StartP != '.') {
4515
if (!strncasecomp(*AllocatedString, "www.", 4) ||
4516
!strncasecomp(*AllocatedString, "ftp.", 4) ||
4517
!strncasecomp(*AllocatedString, "gopher.", 7) ||
4518
!strncasecomp(*AllocatedString, "wais.", 5) ||
4519
!strncasecomp(*AllocatedString, "cso.", 4) ||
4520
!strncasecomp(*AllocatedString, "ns.", 3) ||
4521
!strncasecomp(*AllocatedString, "ph.", 3) ||
4522
!strncasecomp(*AllocatedString, "finger.", 7) ||
4523
!strncasecomp(*AllocatedString, "news.", 5) ||
4524
!strncasecomp(*AllocatedString, "nntp.", 5)) {
4528
while ((*StartP) && (WHITE(*StartP) || *StartP == ',')) {
4529
StartP++; /* Skip whitespace and separators */
4532
while (*EndP && !WHITE(*EndP) && *EndP != ',') {
4533
EndP++; /* Find separator */
4535
LYstrncpy(DomainPrefix, StartP, (EndP - StartP));
4538
* Test each prefix with each suffix. - FM
4542
* Set the first suffix, making it a zero-length string
4543
* if the list is NULL or if the potential host field
4544
* begins with a dot. - FM
4546
StartS = ((suffix_list && *Str != '.') ?
4548
while ((*StartS) && (WHITE(*StartS) || *StartS == ',')) {
4549
StartS++; /* Skip whitespace and separators */
4552
while (*EndS && !WHITE(*EndS) && *EndS != ',') {
4553
EndS++; /* Find separator */
4555
LYstrncpy(DomainSuffix, StartS, (EndS - StartS));
4558
* Create domain names and do DNS tests. - FM
4561
StrAllocCopy(Host, DomainPrefix);
4562
StrAllocCat(Host, ((*Str == '.') ? (Str + 1) : Str));
4563
if (Host[strlen(Host)-1] == '.') {
4564
Host[strlen(Host)-1] = '\0';
4566
StrAllocCat(Host, DomainSuffix);
4567
if ((HostColon = strrchr(Host, ':')) != NULL &&
4568
isdigit(UCH(HostColon[1]))) {
4571
StrAllocCopy(host, Host);
4574
StrAllocCopy(MsgStr, WWW_FIND_MESSAGE);
4575
StrAllocCat(MsgStr, host);
4576
StrAllocCat(MsgStr, GUESSING_SEGMENT);
4578
} else if (Startup && !dump_output_immediately) {
4579
fprintf(stdout, "%s '%s'%s\n", WWW_FIND_MESSAGE, host, GUESSING_SEGMENT);
4581
GotHost = (BOOL) (LYGetHostByName(host) != NULL);
4582
if (HostColon != NULL) {
4585
if (GotHost == FALSE) {
4587
* Give the user chance to interrupt lookup cycles. - KW
4589
if (LYCursesON && (lynx_nsl_status == HT_INTERRUPTED))
4592
"LYExpandHostForURL: Interrupted while '%s' failed to resolve.\n",
4598
return FALSE; /* We didn't find a valid name. */
4602
* Advance to the next suffix, or end of suffix list. - FM
4604
StartS = ((*EndS == '\0') ? EndS : (EndS + 1));
4605
while ((*StartS) && (WHITE(*StartS) || *StartS == ',')) {
4606
StartS++; /* Skip whitespace and separators */
4609
while (*EndS && !WHITE(*EndS) && *EndS != ',') {
4610
EndS++; /* Find separator */
4612
LYstrncpy(DomainSuffix, StartS, (EndS - StartS));
4614
} while ((GotHost == FALSE) && (*DomainSuffix != '\0'));
4616
if (GotHost == FALSE) {
4618
* Advance to the next prefix, or end of prefix list. - FM
4620
StartP = ((*EndP == '\0') ? EndP : (EndP + 1));
4621
while ((*StartP) && (WHITE(*StartP) || *StartP == ',')) {
4622
StartP++; /* Skip whitespace and separators */
4625
while (*EndP && !WHITE(*EndP) && *EndP != ',') {
4626
EndP++; /* Find separator */
4628
LYstrncpy(DomainPrefix, StartP, (EndP - StartP));
4630
} while ((GotHost == FALSE) && (*DomainPrefix != '\0'));
4633
* If a test passed, restore the port field if we had one
4634
* and there is no colon in the expanded host, and the path
4635
* if we had one, and reallocate the original string with
4636
* the expanded Host[:port] field included. - FM
4639
if (StrColon && strchr(Host, ':') == NULL) {
4641
StrAllocCat(Host, StrColon);
4645
StrAllocCat(Host, Path);
4646
} else if (Fragment) {
4647
StrAllocCat(Host, "/");
4648
restorePoundSelector(Fragment);
4649
StrAllocCat(Host, Fragment);
4651
StrAllocCopy(*AllocatedString, Host);
4655
* Clear any residual interrupt. - FM
4657
if (LYCursesON && HTCheckForInterrupt()) {
4658
CTRACE((tfp, "LYExpandHostForURL: Ignoring interrupt because '%s' %s.\n",
4660
(GotHost ? "resolved" : "timed out")));
4664
* Clean up and return the last test result. - FM
4674
* This function rewrites and reallocates a previously allocated
4675
* string that begins with an Internet host name so that the string
4676
* begins with its guess of the scheme based on the first field of
4677
* the host name, or the default scheme if no guess was made, and
4678
* returns TRUE, otherwise it does not modify the string and returns
4679
* FALSE. It also returns FALSE without modifying the string if the
4680
* default_scheme argument was NULL or zero-length and no guess was
4683
PUBLIC BOOLEAN LYAddSchemeForURL ARGS2(
4684
char **, AllocatedString,
4685
char *, default_scheme)
4688
BOOLEAN GotScheme = FALSE;
4691
* If we were passed a NULL or zero-length string,
4692
* don't continue pointlessly. - FM
4694
if (!(*AllocatedString) || *AllocatedString[0] == '\0') {
4699
* Try to guess the appropriate scheme. - FM
4701
if (0 == strncasecomp(*AllocatedString, "www", 3)) {
4703
* This could be either http or https, so check
4704
* the default and otherwise use "http". - FM
4706
if (default_scheme != NULL &&
4707
NULL != strstr(default_scheme, "http")) {
4708
StrAllocCopy(Str, default_scheme);
4710
StrAllocCopy(Str, "http://");
4714
} else if (0 == strncasecomp(*AllocatedString, "ftp", 3)) {
4715
StrAllocCopy(Str, "ftp://");
4718
} else if (0 == strncasecomp(*AllocatedString, "gopher", 6)) {
4719
StrAllocCopy(Str, "gopher://");
4722
} else if (0 == strncasecomp(*AllocatedString, "wais", 4)) {
4723
StrAllocCopy(Str, "wais://");
4726
} else if (0 == strncasecomp(*AllocatedString, "cso", 3) ||
4727
0 == strncasecomp(*AllocatedString, "ns.", 3) ||
4728
0 == strncasecomp(*AllocatedString, "ph.", 3)) {
4729
StrAllocCopy(Str, "cso://");
4732
} else if (0 == strncasecomp(*AllocatedString, "finger", 6)) {
4733
StrAllocCopy(Str, "finger://");
4736
} else if (0 == strncasecomp(*AllocatedString, "news", 4)) {
4738
* This could be either news, snews, or nntp, so
4739
* check the default, and otherwise use news. - FM
4741
if ((default_scheme != NULL) &&
4742
(NULL != strstr(default_scheme, "news") ||
4743
NULL != strstr(default_scheme, "nntp"))) {
4744
StrAllocCopy(Str, default_scheme);
4746
StrAllocCopy(Str, "news://");
4750
} else if (0 == strncasecomp(*AllocatedString, "nntp", 4)) {
4751
StrAllocCopy(Str, "nntp://");
4757
* If we've make a guess, use it. Otherwise, if we
4758
* were passed a default scheme prefix, use that. - FM
4760
if (GotScheme == TRUE) {
4761
StrAllocCat(Str, *AllocatedString);
4762
StrAllocCopy(*AllocatedString, Str);
4766
} else if (non_empty(default_scheme)) {
4767
StrAllocCopy(Str, default_scheme);
4769
StrAllocCat(Str, *AllocatedString);
4770
StrAllocCopy(*AllocatedString, Str);
4779
* This function expects an absolute Unix or VMS SHELL path
4780
* spec as an allocated string, simplifies it, and trims out
4781
* any residual relative elements. It also checks whether
4782
* the path had a terminal slash, and if it didn't, makes
4783
* sure that the simplified path doesn't either. If it's
4784
* a directory, our convention is to exclude "Up to parent"
4785
* links when a terminal slash is present. - FM
4787
PUBLIC void LYTrimRelFromAbsPath ARGS1(
4795
* Make sure we have a pointer to an absolute path. - FM
4797
if (path == NULL || !LYIsPathSep(*path))
4801
* Check whether the path has a terminal slash. - FM
4803
TerminalSlash = (BOOL) (LYIsPathSep(path[(strlen(path) - 1)]));
4806
* Simplify the path and then do any necessary trimming. - FM
4810
while (cp[1] == '.') {
4811
if (cp[2] == '\0') {
4813
* Eliminate trailing dot. - FM
4816
} else if (LYIsPathSep(cp[2])) {
4818
* Skip over the "/." of a "/./". - FM
4821
} else if (cp[2] == '.' && cp[3] == '\0') {
4823
* Eliminate trailing dotdot. - FM
4826
} else if (cp[2] == '.' && cp[3] == '/') {
4828
* Skip over the "/.." of a "/../". - FM
4833
* Done trimming. - FM
4840
* Load any shifts into path, and eliminate any
4841
* terminal slash created by HTSimplify() or our
4842
* walk, but not present originally. - FM
4845
for (i = 0; cp[i] != '\0'; i++)
4849
if (TerminalSlash == FALSE) {
4850
LYTrimPathSep(path);
4855
* Example Client-Side Include interface.
4857
* This is called from SGML.c and simply returns markup for reporting
4858
* the URL of the document being loaded if a comment begins with
4859
* "<!--#lynxCSI". The markup will be included as if it were in the
4860
* document. Move this function to a separate module for doing this
4861
* kind of thing seriously, someday. - FM
4863
PUBLIC void LYDoCSI ARGS3(
4865
CONST char *, comment,
4868
CONST char *cp = comment;
4873
if (strncmp(cp, "!--#", 4))
4877
if (!strncasecomp(cp, "lynxCSI", 7)) {
4878
StrAllocCat(*csi, "\n<p align=\"center\">URL: ");
4879
StrAllocCat(*csi, url);
4880
StrAllocCat(*csi, "</p>\n\n");
4888
* Define_VMSLogical -- Fote Macrides 04-Apr-1995
4889
* Define VMS logicals in the process table.
4891
PUBLIC void Define_VMSLogical ARGS2(
4892
char *, LogicalName,
4893
char *, LogicalValue)
4895
$DESCRIPTOR(lname, "");
4896
$DESCRIPTOR(lvalue, "");
4897
$DESCRIPTOR(ltable, "LNM$PROCESS");
4899
if (!LogicalName || *LogicalName == '\0')
4902
lname.dsc$w_length = strlen(LogicalName);
4903
lname.dsc$a_pointer = LogicalName;
4905
if (!LogicalValue || *LogicalValue == '\0') {
4906
lib$delete_logical(&lname, <able);
4910
lvalue.dsc$w_length = strlen(LogicalValue);
4911
lvalue.dsc$a_pointer = LogicalValue;
4912
lib$set_logical(&lname, &lvalue, <able, 0, 0);
4917
#ifdef LY_FIND_LEAKS
4918
PRIVATE void LYHomeDir_free NOARGS
4922
#endif /* LY_FIND_LEAKS */
4924
PUBLIC char * Current_Dir ARGS1(
4929
result = getcwd (pathname, LY_MAXPATH);
4931
result = getwd (pathname);
4932
#endif /* NO_GETCWD */
4934
strcpy(pathname, ".");
4939
* Verify that the given path refers to an existing directory, returning the
4940
* string if the directory exists. If not, return null.
4942
PRIVATE char * CheckDir ARGS1(
4945
struct stat stat_info;
4946
if (!LYisAbsPath(path)
4947
|| (HTStat(path, &stat_info) < 0
4948
|| !S_ISDIR(stat_info.st_mode))) {
4955
* Lookup various possibilities for $HOME, and check that the directory exists.
4957
PRIVATE char *HomeEnv NOARGS
4959
char *result = CheckDir(LYGetEnv("HOME"));
4961
#if defined (USE_DOS_DRIVES)
4965
static char *temp = NULL;
4968
if ((result = LYGetEnv("USERPROFILE")) != 0) {
4969
HTSprintf0(&temp, "%s%sMy Documents", result, PATHSEP_STR);
4970
result = CheckDir(temp);
4974
if ((head = LYGetEnv("HOMEDRIVE")) != 0) {
4975
if ((leaf = LYGetEnv("HOMEPATH")) != 0) {
4976
HTSprintf0(&temp, "%s%s%s", head, PATHSEP_STR, leaf);
4977
result = CheckDir(temp);
4983
result = CheckDir(LYGetEnv("TEMP"));
4985
result = CheckDir(LYGetEnv("TMP"));
4987
if ((head = LYGetEnv("SystemDrive")) != 0) {
4988
HTSprintf0(&temp, "%s%s", head, PATHSEP_STR);
4989
result = CheckDir(temp);
4993
result = CheckDir("C:" PATHSEP_STR);
5000
PUBLIC CONST char * Home_Dir NOARGS
5002
static CONST char *homedir = NULL;
5005
if (homedir == NULL) {
5006
if ((cp = HomeEnv()) == NULL) {
5008
if ((cp = LYGetEnv("SYS$LOGIN")) == NULL
5009
&& (cp = LYGetEnv("SYS$SCRATCH")) == NULL) {
5010
cp = "sys$scratch:";
5012
StrAllocCopy(HomeDir, cp);
5017
* One could use getlogin() and getpwnam() here instead.
5019
struct passwd *pw = getpwuid(geteuid());
5021
if (pw && pw->pw_dir) {
5022
StrAllocCopy(HomeDir, pw->pw_dir);
5027
* Use /tmp; it should be writable.
5029
StrAllocCopy(HomeDir, "/tmp");
5034
StrAllocCopy(HomeDir, cp);
5036
homedir = (CONST char *)HomeDir;
5037
#ifdef LY_FIND_LEAKS
5038
atexit(LYHomeDir_free);
5041
if (homedir == NULL) {
5042
printf("%s\n", gettext("Cannot find HOME directory"));
5049
* Return a pointer to the final leaf of the given pathname, If no pathname
5050
* separators are found, returns the original pathname. The leaf may be
5053
PUBLIC char *LYPathLeaf ARGS1(char *, pathname)
5057
if ((leaf = strrchr(pathname, '/')) != 0) {
5062
if ((leaf = strrchr(pathname, ']')) == 0)
5063
leaf = strrchr(pathname, ':');
5068
for (leaf = 0, n = strlen(pathname)-1; n >= 0; n--) {
5069
if (strchr("\\/:", pathname[n]) != 0) {
5070
leaf = pathname + n + 1;
5076
return (leaf != 0) ? leaf : pathname;
5080
* This function checks the acceptability of file paths that
5081
* are intended to be off the home directory. The file path
5082
* should be passed in fbuffer, together with the size of the
5083
* buffer. The function simplifies the file path, and if it
5084
* is acceptable, loads it into fbuffer and returns TRUE.
5085
* Otherwise, it does not modify fbuffer and returns FALSE.
5086
* If a subdirectory is present and the path does not begin
5087
* with "./", that is prefixed to make the situation clear. - FM
5089
PUBLIC BOOLEAN LYPathOffHomeOK ARGS2(
5091
size_t, fbuffer_size)
5097
* Make sure we have an fbuffer and a string in it. - FM
5099
if (!fbuffer || fbuffer_size < 2 || fbuffer[0] == '\0') {
5102
StrAllocCopy(file, fbuffer);
5106
* Check for an inappropriate reference to the
5107
* home directory, and correct it if we can. - FM
5110
if (!strncasecomp(cp, "sys$login", 9)) {
5111
if (*(cp + 9) == '\0') {
5113
* Reject "sys$login". - FM
5118
if (*(cp + 9) == ':') {
5122
* Reject "sys$login:". Otherwise, we have
5123
* converted "sys$login:file" to "file", or
5124
* have left a strange path for VMS as it
5125
* was originally. - FM
5134
if (*(cp + 1) == '/') {
5135
if (*(cp + 2) != '\0') {
5136
if ((cp1 = strchr((cp + 2), '/')) != NULL) {
5138
* Convert "~/subdir(s)/file"
5139
* to "./subdir(s)/file". - FM
5144
* Convert "~/file" to "file". - FM
5155
} else if ((*(cp + 1) != '\0') &&
5156
(cp1 = strchr((cp + 1), '/')) != NULL) {
5158
if (*(cp + 2) != '\0') {
5159
if ((cp1 = strchr((cp + 2), '/')) != NULL) {
5161
* Convert "~user/subdir(s)/file" to
5162
* "./subdir(s)/file". If user is someone
5163
* else, we covered a spoof. Otherwise,
5164
* we simplified. - FM
5169
* Convert "~user/file" to "file". - FM
5175
* Reject "~user/". - FM
5182
* Reject "~user". - FM
5191
* Check for VMS path specs, and reject if still present. - FM
5193
if (strchr(cp, ':') != NULL || strchr(cp, ']') != NULL) {
5200
* Check for a URL or absolute path, and reject if present. - FM
5202
if (is_url(cp) || LYIsPathSep(*cp)) {
5213
* Check if it has a pointless "./". - FM
5215
if (!strncmp(cp, "./", 2)) {
5216
if ((cp1 = strchr((cp + 2), '/')) == NULL) {
5222
* Check for spoofing. - FM
5226
|| LYIsPathSep(cp[(strlen(cp) - 1)])
5227
|| strstr(cp, "..") != NULL
5228
|| !strcmp(cp, ".")) {
5234
* Load what we have at this point into fbuffer,
5235
* trimming if too long, and claim it's OK. - FM
5237
if (fbuffer_size > 3 && strncmp(cp, "./", 2) && strchr(cp, '/')) {
5239
* We have a subdirectory and no lead "./", so
5240
* prefix it to make the situation clear. - FM
5242
strcpy(fbuffer, "./");
5243
if (strlen(cp) > (fbuffer_size - 3))
5244
cp[(fbuffer_size - 3)] = '\0';
5245
strcat(fbuffer, cp);
5247
if (strlen(cp) > (fbuffer_size - 1))
5248
cp[(fbuffer_size - 1)] = '\0';
5249
strcpy(fbuffer, cp);
5256
* This function appends fname to the home path and returns
5257
* the full path and filename. The fname string can be just
5258
* a filename (e.g., "lynx_bookmarks.html"), or include a
5259
* subdirectory off the home directory, in which case fname
5260
* should begin with "./" (e.g., ./BM/lynx_bookmarks.html)
5261
* Use LYPathOffHomeOK() to check and/or fix up fname before
5262
* calling this function. On VMS, the resultant full path
5263
* and filename are converted to VMS syntax. - FM
5265
PUBLIC void LYAddPathToHome ARGS3(
5267
size_t, fbuffer_size,
5275
* Make sure we have a buffer. - FM
5279
if (fbuffer_size < 2) {
5283
fbuffer[(fbuffer_size - 1)] = '\0';
5286
* Make sure we have a file name. - FM
5292
* Set up home string and length. - FM
5294
StrAllocCopy(home, Home_Dir());
5297
#define NO_HOMEPATH "Error:"
5299
#define NO_HOMEPATH "/error"
5301
if (!non_empty(home))
5303
* Home_Dir() has a bug if this ever happens. - FM
5305
StrAllocCopy(home, NO_HOMEPATH);
5307
len = fbuffer_size - (strlen(home) + 1);
5310
* Buffer is smaller than or only big enough for the home path.
5311
* Load what fits of the home path and return. This will fail,
5312
* but we need something in the buffer. - FM
5314
LYstrncpy(fbuffer, home, (fbuffer_size - 1));
5321
* Check whether we have a subdirectory path or just a filename. - FM
5323
if (!strncmp(file, "./", 2)) {
5325
* We have a subdirectory path. - FM
5327
if (home[strlen(home)-1] == ']') {
5329
* We got the home directory, so convert it to
5330
* SHELL syntax and append subdirectory path,
5331
* then convert that to VMS syntax. - FM
5334
HTSprintf0(&temp, "%s%s", HTVMS_wwwName(home), (file + 1));
5335
sprintf(fbuffer, "%.*s",
5336
(fbuffer_size - 1), HTVMS_name("", temp));
5340
* This will fail, but we need something in the buffer. - FM
5342
sprintf(fbuffer, "%s%.*s", home, len, file);
5346
* We have a file in the home directory. - FM
5348
sprintf(fbuffer, "%s%.*s", home, len, file);
5352
* Check whether we have a subdirectory path or just a filename. - FM
5354
sprintf(fbuffer, "%s/%.*s", home, len,
5355
(strncmp(file, "./", 2) ? file : (file + 2)));
5361
* Given a filename, concatenate it to the save-space pathname, unless it is
5362
* an absolute pathname. If there is no save-space defined, use the home
5363
* directory. Return a new string with the result.
5365
PUBLIC char * LYAddPathToSave ARGS1(
5368
char *result = NULL;
5370
if (LYisAbsPath(fname)) {
5371
StrAllocCopy(result, fname);
5373
if (lynx_save_space != NULL) {
5374
StrAllocCopy(result, lynx_save_space);
5376
char temp[LY_MAXPATH];
5377
LYAddPathToHome(temp, sizeof(temp), fname);
5378
StrAllocCopy(result, temp);
5385
* This function takes a string in the format
5386
* "Mon, 01-Jan-96 13:45:35 GMT" or
5387
* "Mon, 1 Jan 1996 13:45:35 GMT"" or
5389
* as an argument, and returns its conversion to clock format
5390
* (seconds since 00:00:00 Jan 1 1970), or 0 if the string
5391
* doesn't match the expected pattern. It also returns 0 if
5392
* the time is in the past and the "absolute" argument is FALSE.
5393
* It is intended for handling 'expires' strings in Version 0
5394
* cookies homologously to 'max-age' strings in Version 1 cookies,
5395
* for which 0 is the minimum, and greater values are handled as
5396
* '[max-age seconds] + time(NULL)'. If "absolute" if TRUE, we
5397
* return the clock format value itself, but if anything goes wrong
5398
* when parsing the expected patterns, we still return 0. - FM
5400
PUBLIC time_t LYmktime ARGS2(
5406
int day, month, year, hour, minutes, seconds;
5411
* Make sure we have a string to parse. - FM
5413
if (!non_empty(string))
5416
CTRACE((tfp, "LYmktime: Parsing '%s'\n", s));
5419
* Skip any lead alphabetic "Day, " field and
5420
* seek a numeric day field. - FM
5422
while (*s != '\0' && !isdigit(UCH(*s)))
5428
* Get the numeric day and convert to an integer. - FM
5431
while (*s != '\0' && isdigit(UCH(*s)))
5433
if (*s == '\0' || (s - start) > 2)
5435
LYstrncpy(temp, start, (int)(s - start));
5437
if (day < 1 || day > 31)
5441
* Get the month string and convert to an integer. - FM
5443
while (*s != '\0' && !isalnum(UCH(*s)))
5448
while (*s != '\0' && isalnum(UCH(*s)))
5451
(s - start) < (isdigit(UCH(*(s - 1))) ? 2 : 3) ||
5452
(s - start) > (isdigit(UCH(*(s - 1))) ? 2 : 9))
5454
LYstrncpy(temp, start, (isdigit(UCH(*(s - 1))) ? 2 : 3));
5455
switch (TOUPPER(temp[0])) {
5459
if (month < 1 || month > 12) {
5464
if (!strcasecomp(temp, "Apr")) {
5466
} else if (!strcasecomp(temp, "Aug")) {
5473
if (!strcasecomp(temp, "Dec")) {
5480
if (!strcasecomp(temp, "Feb")) {
5487
if (!strcasecomp(temp, "Jan")) {
5489
} else if (!strcasecomp(temp, "Jun")) {
5491
} else if (!strcasecomp(temp, "Jul")) {
5498
if (!strcasecomp(temp, "Mar")) {
5500
} else if (!strcasecomp(temp, "May")) {
5507
if (!strcasecomp(temp, "Nov")) {
5514
if (!strcasecomp(temp, "Oct")) {
5521
if (!strcasecomp(temp, "Sep")) {
5532
* Get the numeric year string and convert to an integer. - FM
5534
while (*s != '\0' && !isdigit(UCH(*s)))
5539
while (*s != '\0' && isdigit(UCH(*s)))
5541
if ((s - start) == 4) {
5542
LYstrncpy(temp, start, 4);
5543
} else if ((s - start) == 2) {
5546
* Assume that received 2-digit dates >= 70 are 19xx; others
5547
* are 20xx. Only matters when dealing with broken software
5548
* (HTTP server or web page) which is not Y2K compliant. The
5549
* line is drawn on a best-guess basis; it is impossible for
5550
* this to be completely accurate because it depends on what
5551
* the broken sender software intends. (This totally breaks
5552
* in 2100 -- setting up the next crisis...) - BL
5554
if (atoi(start) >= 70)
5555
LYstrncpy(temp, "19", 2);
5557
LYstrncpy(temp, "20", 2);
5558
strncat(temp, start, 2);
5566
* Get the numeric hour string and convert to an integer. - FM
5568
while (*s != '\0' && !isdigit(UCH(*s)))
5576
while (*s != '\0' && isdigit(UCH(*s)))
5578
if (*s != ':' || (s - start) > 2)
5580
LYstrncpy(temp, start, (int)(s - start));
5584
* Get the numeric minutes string and convert to an integer. - FM
5586
while (*s != '\0' && !isdigit(UCH(*s)))
5591
while (*s != '\0' && isdigit(UCH(*s)))
5593
if (*s != ':' || (s - start) > 2)
5595
LYstrncpy(temp, start, (int)(s - start));
5596
minutes = atoi(temp);
5599
* Get the numeric seconds string and convert to an integer. - FM
5601
while (*s != '\0' && !isdigit(UCH(*s)))
5606
while (*s != '\0' && isdigit(UCH(*s)))
5608
if (*s == '\0' || (s - start) > 2)
5610
LYstrncpy(temp, start, (int)(s - start));
5611
seconds = atoi(temp);
5615
* Convert to clock format (seconds since 00:00:00 Jan 1 1970),
5616
* but then zero it if it's in the past and "absolute" is not
5624
day += (year - 1968)*1461/4;
5625
day += ((((month*153) + 2)/5) - 672);
5626
clock2 = (time_t)((day * 60 * 60 * 24) +
5630
if (absolute == FALSE && (long)(time((time_t *)0) - clock2) >= 0)
5633
CTRACE((tfp, "LYmktime: clock=%ld, ctime=%s",
5640
#if !defined(HAVE_PUTENV) && !defined(_WINDOWS)
5642
* No putenv on the NeXT so we use this code instead!
5645
/* Copyright (C) 1991 Free Software Foundation, Inc.
5646
This file is part of the GNU C Library.
5648
The GNU C Library is free software; you can redistribute it and/or
5649
modify it under the terms of the GNU Library General Public License as
5650
published by the Free Software Foundation; either version 2 of the
5651
License, or (at your option) any later version.
5653
The GNU C Library is distributed in the hope that it will be useful,
5654
but WITHOUT ANY WARRANTY; without even the implied warranty of
5655
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5656
Library General Public License for more details.
5658
You should have received a copy of the GNU Library General Public
5659
License along with the GNU C Library; see the file COPYING.LIB. If
5660
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
5661
Cambridge, MA 02139, USA. */
5663
#if defined(STDC_HEADERS) || defined(USG)
5665
#else /* Not (STDC_HEADERS or USG): */
5666
#include <strings.h>
5667
#endif /* STDC_HEADERS or USG */
5673
extern char **environ;
5676
* Put STRING, which is of the form "NAME=VALUE", in the environment.
5678
PUBLIC int putenv ARGS1(
5679
CONST char *, string)
5681
char *name_end = strchr(string, '=');
5682
register size_t size;
5685
if (name_end == NULL)
5687
/* Remove the variable from the environment. */
5688
size = strlen (string);
5689
for (ep = environ; *ep != NULL; ++ep)
5690
if (!strncmp (*ep, string, size) && (*ep)[size] == '=')
5692
while (ep[1] != NULL)
5703
for (ep = environ; *ep != NULL; ++ep)
5704
if (!strncmp (*ep, string, name_end - string) && (*ep)[name_end - string] == '=')
5711
static char **last_environ = NULL;
5712
char **new_environ = (char **) malloc ((size + 2) * sizeof (char *));
5713
if (new_environ == NULL)
5715
(void) memcpy((char *)new_environ, (char *)environ, size * sizeof(char *));
5716
new_environ[size] = (char *) string;
5717
new_environ[size + 1] = NULL;
5718
if (last_environ != NULL)
5719
FREE (last_environ);
5720
last_environ = new_environ;
5721
environ = new_environ;
5724
*ep = (char *) string;
5728
#endif /* !HAVE_PUTENV */
5731
int remove ARGS1(char *, name)
5733
return unlink(name);
5738
* Default, for single-user systems such as Cygwin and OS/2 EMX:
5740
#define IsOurFile(name) TRUE
5741
#define OpenHiddenFile(name, mode) fopen(name, mode)
5743
#if defined(MULTI_USER_UNIX)
5746
#undef OpenHiddenFile
5749
* Verify if this is really a file, not accessed by a link, except for the
5750
* special case of its directory being pointed to by a link from a directory
5751
* owned by root and not writable by other users.
5753
PRIVATE BOOL IsOurFile ARGS1(char *, name)
5757
if (lstat(name, &data) == 0
5758
&& S_ISREG(data.st_mode)
5759
&& data.st_nlink == 1
5760
&& data.st_uid == getuid()) {
5763
* ( If this is not a single-user system, the other user is presumed by
5764
* some people busy trying to use a symlink attack on our files ;-)
5766
#if defined(HAVE_LSTAT)
5770
StrAllocCopy(path, name);
5772
if ((leaf = LYPathLeaf(path)) != path)
5773
*--leaf = '\0'; /* write a null on the '/' */
5774
if (lstat(*path ? path : "/", &data) != 0) {
5778
* If we find a symbolic link, it has to be in a directory that's
5779
* protected. Otherwise someone could have switched it to point
5780
* to one of the real user's files.
5782
if (S_ISLNK(data.st_mode)) {
5783
linked = TRUE; /* could be link-to-link; doesn't matter */
5784
} else if (S_ISDIR(data.st_mode)) {
5788
* We assume that a properly-configured system has the
5789
* unwritable directories owned by root. This is not
5790
* necessarily so (bin, news, etc., may), but the only
5791
* uid we can count on is 0. It would be nice to add a
5792
* check for the gid also, but that wouldn't be
5795
if (data.st_uid != 0
5796
|| (data.st_mode & S_IWOTH) != 0) {
5797
linked = TRUE; /* force an error-return */
5801
} else if (linked) {
5804
} while (leaf != path);
5813
* Open a file that we don't want other users to see.
5815
PRIVATE FILE *OpenHiddenFile ARGS2(char *, name, char *, mode)
5819
BOOLEAN binary = strchr(mode, 'b') != 0;
5821
#if defined(O_CREAT) && defined(O_EXCL) /* we have fcntl.h or kindred? */
5823
* This is the preferred method for creating new files, since it ensures
5824
* that no one has an existing file or link that they happen to own.
5827
int fd = open(name, O_CREAT|O_EXCL|O_WRONLY, HIDE_CHMOD);
5830
&& IsOurFile(name)) {
5832
/* FIXME: there's a race at this point if directory is open */
5833
fd = open(name, O_CREAT|O_EXCL|O_WRONLY, HIDE_CHMOD);
5836
#if defined(O_BINARY) && defined(__CYGWIN__)
5838
setmode(fd, O_BINARY);
5840
fp = fdopen(fd, mode);
5847
&& chmod(name, HIDE_CHMOD) == 0)
5848
fp = fopen(name, mode);
5849
else if (lstat(name, &data) != 0)
5850
fp = OpenHiddenFile(name, binary ? BIN_W : TXT_W);
5852
* This is less stringent, but reasonably portable. For new files, the
5853
* umask will suffice; however if the file already exists we'll change
5854
* permissions first, before opening it. If the chmod fails because of
5855
* some reason other than a non-existent file, there's no point in trying
5858
* This won't work properly if the user is root, since the chmod succeeds.
5860
} else if (*mode != 'a') {
5861
mode_t save = umask(HIDE_UMASK);
5862
if (chmod(name, HIDE_CHMOD) == 0 || errno == ENOENT)
5863
fp = fopen(name, mode);
5868
#endif /* MULTI_USER_UNIX */
5870
PUBLIC FILE *LYNewBinFile ARGS1(char *, name)
5873
FILE *fp = fopen (name, BIN_W, "mbc=32");
5874
chmod(name, HIDE_CHMOD);
5876
FILE *fp = OpenHiddenFile(name, BIN_W);
5881
PUBLIC FILE *LYNewTxtFile ARGS1(char *, name)
5886
fp = fopen (name, TXT_W, "shr=get");
5887
chmod(name, HIDE_CHMOD);
5889
SetDefaultMode(O_TEXT);
5891
fp = OpenHiddenFile(name, TXT_W);
5893
SetDefaultMode(O_BINARY);
5899
PUBLIC FILE *LYAppendToTxtFile ARGS1(char *, name)
5904
fp = fopen (name, TXT_A, "shr=get");
5905
chmod(name, HIDE_CHMOD);
5907
SetDefaultMode(O_TEXT);
5909
fp = OpenHiddenFile(name, TXT_A);
5911
SetDefaultMode(O_BINARY);
5916
#if defined(MULTI_USER_UNIX)
5918
* Restore normal permissions to a copy of a file that we have created
5919
* with temp file restricted permissions. The normal umask should
5920
* apply for user files. - kw
5922
PUBLIC void LYRelaxFilePermissions ARGS1(CONST char *, name)
5925
struct stat stat_buf;
5926
if (stat(name, &stat_buf) == 0 &&
5927
S_ISREG(stat_buf.st_mode) &&
5928
(mode = (stat_buf.st_mode & 0777)) == HIDE_CHMOD) {
5930
* It looks plausible that this is a file we created with
5931
* temp file paranoid permissions (and the umask wasn't even
5932
* more restrictive when it was copied). - kw
5934
mode_t save = umask(HIDE_UMASK);
5935
mode = ((mode & 0700) | 0066) & ~save;
5943
* Check if the given anchor has an associated file-cache.
5945
PUBLIC BOOLEAN LYCachedTemp ARGS2(
5950
LYstrncpy(result, *cached, LY_MAXPATH);
5952
if (LYCanReadFile(result)) {
5960
#ifndef HAVE_MKDTEMP
5961
#define mkdtemp(path) ((mktemp(path) != 0) && (mkdir(path, 0700) == 0))
5965
* Open a temp-file, ensuring that it is unique, and not readable by other
5968
* The mode can be one of: "w", "a", "wb".
5970
PUBLIC FILE *LYOpenTemp ARGS3(
5972
CONST char *, suffix,
5980
CTRACE((tfp, "LYOpenTemp(,%s,%s)\n", suffix, mode));
5984
while (*mode != '\0') {
5986
case 'w': wrt = 'w'; break;
5987
case 'a': wrt = 'a'; break;
5988
case 'b': txt = FALSE; break;
5990
CTRACE((tfp, "%s @%d: BUG\n", __FILE__, __LINE__));
5996
* Verify if the given space looks secure enough. Otherwise, make a
5997
* secure subdirectory of that.
5999
#if defined(MULTI_USER_UNIX) && (defined(HAVE_MKTEMP) || defined(HAVE_MKDTEMP))
6000
if (lynx_temp_subspace == 0)
6002
BOOL make_it = FALSE;
6005
if (lstat(lynx_temp_space, &sb) == 0
6006
&& S_ISDIR(sb.st_mode)) {
6007
if (sb.st_uid != getuid()
6008
|| (sb.st_mode & (S_IWOTH | S_IWGRP)) != 0) {
6010
CTRACE((tfp, "lynx_temp_space is not our directory %s owner %d mode %03o\n",
6011
lynx_temp_space, (int) sb.st_uid, (int) sb.st_mode & 0777));
6015
CTRACE((tfp, "lynx_temp_space is not a directory %s\n", lynx_temp_space));
6018
int old_mask = umask(HIDE_UMASK);
6019
StrAllocCat(lynx_temp_space, "XXXXXXXXXX");
6020
if (mkdtemp(lynx_temp_space) == 0) {
6021
printf("%s: %s\n", lynx_temp_space, LYStrerror(errno));
6025
lynx_temp_subspace = 1;
6026
StrAllocCat(lynx_temp_space, "/");
6027
CTRACE((tfp, "made subdirectory %s\n", lynx_temp_space));
6029
lynx_temp_subspace = -1;
6035
if (!fmt_tempname(result, lynx_temp_space, suffix))
6040
fp = LYNewTxtFile (result);
6043
fp = LYAppendToTxtFile (result);
6047
fp = LYNewBinFile (result);
6050
* If we get a failure to make a temporary file, don't bother to try a
6051
* different name unless the failure was because the file already
6054
#ifdef EEXIST /* FIXME (need a better test) in fcntl.h or unistd.h */
6055
if ((fp == 0) && (errno != EEXIST)) {
6056
CTRACE((tfp, "... LYOpenTemp(%s) failed: %s\n",
6057
result, LYStrerror(errno)));
6063
if ((p = typecalloc(LY_TEMP)) != 0) {
6065
StrAllocCopy((p->name), result);
6067
p->outs = (wrt != 'r');
6070
outofmem(__FILE__, "LYOpenTemp");
6073
CTRACE((tfp, "... LYOpenTemp(%s)\n", result));
6078
* Reopen a temporary file
6080
PUBLIC FILE *LYReopenTemp ARGS1(
6087
if ((p = FindTempfileByName(name)) != 0) {
6088
fp = p->file = LYAppendToTxtFile (name);
6094
* Open a temp-file for writing, possibly re-using a previously used
6096
* If a non-empty fname is given, it is reused if it indicates a file
6097
* previously registered as a temp file and, in case the file still
6098
* exists, if it looks like we can write to it safely. Otherwise a
6099
* new temp file (with new name) will be generated and returned in fname.
6101
* File permissions are set so that the file is not readable by unprivileged
6104
* Suffix is only used if fname is not being reused.
6105
* The mode should be "w", others are possible (they may be passed on)
6106
* but probably don't make sense. - kw
6108
PUBLIC FILE *LYOpenTempRewrite ARGS3(
6110
CONST char *, suffix,
6116
BOOL registered = NO;
6117
BOOL writable_exists = NO;
6119
BOOL still_open = NO;
6121
struct stat stat_buf;
6123
CTRACE((tfp, "LYOpenTempRewrite(%s,%s,%s)\n", fname, suffix, mode));
6124
if (*fname == '\0') /* first time, no filename yet */
6125
return (LYOpenTemp(fname, suffix, mode));
6127
if ((p = FindTempfileByName(fname)) != 0) {
6131
CTRACE((tfp, "...used before%s\n", still_open ? ", still open!" : "."));
6136
writable_exists = HTEditable(fname); /* existing, can write */
6137
#define CTRACE_EXISTS "exists and is writable, "
6139
writable_exists = (BOOL) (stat(fname, &stat_buf) == 0); /* existing, assume can write */
6140
#define CTRACE_EXISTS "exists, "
6143
if (writable_exists) {
6144
is_ours = IsOurFile(fname);
6146
CTRACE((tfp, "...%s%s\n",
6147
writable_exists ? CTRACE_EXISTS : "",
6148
is_ours ? "is our file." : "is NOT our file."));
6152
* Note that in cases where LYOpenTemp is called as fallback below,
6153
* we don't call LYRemoveTemp first. That may be appropriate in some
6154
* cases, but not trying to remove a weird existing file seems safer
6155
* and could help diagnose an unusual situation. (They may be removed
6160
* This should probably not happen. Make a new one.
6162
return (LYOpenTemp(fname, suffix, mode));
6163
} else if (!registered) {
6165
* Not registered. It should have been registered at one point
6166
* though, otherwise we wouldn't be called like this.
6168
return (LYOpenTemp(fname, suffix, mode));
6169
} else if (writable_exists && !is_ours) {
6171
* File exists, writable if we checked, but something is wrong
6174
return (LYOpenTemp(fname, suffix, mode));
6176
} else if (!is_ours && (lstat(fname, &stat_buf) == 0)) {
6178
* Exists but not writable, and something is wrong with it.
6180
return (LYOpenTemp(fname, suffix, mode));
6184
while (*mode != '\0') {
6186
case 'w': wrt = 'w'; break;
6187
case 'a': wrt = 'a'; break;
6188
case 'b': txt = FALSE; break;
6190
CTRACE((tfp, "%s @%d: BUG\n", __FILE__, __LINE__));
6197
* Yes, it exists, is writable if we checked, and everything
6198
* looks ok so far. This should be the most regular case. - kw
6200
#ifdef HAVE_TRUNCATE
6201
if (txt == TRUE) { /* limitation of LYReopenTemp. shrug */
6203
* We truncate and then append, this avoids having a small
6204
* window in which the file doesn't exist. - kw
6206
if (truncate(fname, 0) != 0) {
6207
CTRACE((tfp, "... truncate(%s,0) failed: %s\n",
6208
fname, LYStrerror(errno)));
6209
return (LYOpenTemp(fname, suffix, mode));
6211
return (LYReopenTemp(fname));
6219
/* We come here in two cases: either the file existed and was
6220
* ours and we just got rid of it.
6221
* Or the file did and does not exist, but is registered as a
6222
* temp file. It must have been removed by some means other than
6224
* In both cases, reuse the name! - kw
6230
fp = LYNewTxtFile (fname);
6233
fp = LYAppendToTxtFile (fname);
6237
fp = LYNewBinFile (fname);
6241
CTRACE((tfp, "... LYOpenTempRewrite(%s), %s\n", fname,
6242
(fp) ? "ok" : "failed"));
6244
* We could fall back to trying LYOpenTemp() here in case of failure.
6245
* After all the checks already done above a filure here should be
6246
* pretty unusual though, so maybe it's better to let the user notice
6247
* that something went wrong, and not try to fix it up. - kw
6253
* Special case of LYOpenTemp, used for manipulating bookmark file, i.e., with
6256
PUBLIC FILE *LYOpenScratch ARGS2(
6258
CONST char *, prefix)
6263
if (!fmt_tempname(result, prefix, HTML_SUFFIX))
6266
if ((fp = LYNewTxtFile (result)) != 0) {
6267
if ((p = typecalloc(LY_TEMP)) != 0) {
6269
StrAllocCopy((p->name), result);
6273
outofmem(__FILE__, "LYOpenScratch");
6276
CTRACE((tfp, "LYOpenScratch(%s)\n", result));
6280
PRIVATE void LY_close_temp ARGS1(
6285
LYCloseOutput(p->file);
6287
LYCloseInput(p->file);
6294
* Close a temp-file, given its name
6296
PUBLIC void LYCloseTemp ARGS1(
6301
CTRACE((tfp, "LYCloseTemp(%s)\n", name));
6302
if ((p = FindTempfileByName(name)) != 0) {
6303
CTRACE((tfp, "...LYCloseTemp(%s)%s\n", name,
6304
(p->file != 0) ? ", closed" : ""));
6310
* Close a temp-file, given its file-pointer
6312
PUBLIC void LYCloseTempFP ARGS1(
6317
CTRACE((tfp, "LYCloseTempFP\n"));
6318
if ((p = FindTempfileByFP(fp)) != 0) {
6320
CTRACE((tfp, "...LYCloseTempFP(%s)\n", p->name));
6325
* Close a temp-file, removing it.
6327
PUBLIC int LYRemoveTemp ARGS1(
6333
if (non_empty(name)) {
6334
CTRACE((tfp, "LYRemoveTemp(%s)\n", name));
6335
for (p = ly_temp, q = 0; p != 0; q = p, p = p->next) {
6336
if (!strcmp(name, p->name)) {
6343
code = HTSYS_remove(name);
6344
CTRACE((tfp, "...LYRemoveTemp done(%d)%s\n", code,
6345
(p->file != 0) ? ", closed" : ""));
6357
* Remove all of the temp-files. Note that this assumes that they are closed,
6358
* since some systems will not allow us to remove a file which is open.
6360
PUBLIC void LYCleanupTemp NOARGS
6362
while (ly_temp != 0) {
6363
LYRemoveTemp(ly_temp->name);
6365
#if defined(MULTI_USER_UNIX)
6366
if (lynx_temp_subspace > 0) {
6367
char result[LY_MAXPATH];
6368
LYstrncpy(result, lynx_temp_space, sizeof(result)-1);
6369
LYTrimPathSep(result);
6370
CTRACE((tfp, "LYCleanupTemp removing %s\n", result));
6372
lynx_temp_subspace = -1;
6378
* We renamed a temporary file. Keep track so we can remove it on exit.
6380
PUBLIC void LYRenamedTemp ARGS2(
6386
CTRACE((tfp, "LYRenamedTemp(old=%s, new=%s)\n", oldname, newname));
6387
if ((p = FindTempfileByName(oldname)) != 0) {
6388
StrAllocCopy((p->name), newname);
6392
#ifndef DISABLE_BIBP
6394
* Check that bibhost defines the BibP icon.
6396
PUBLIC void LYCheckBibHost NOARGS
6398
DocAddress bibhostIcon;
6401
bibhostIcon.address = NULL;
6402
StrAllocCopy(bibhostIcon.address, BibP_bibhost);
6403
StrAllocCat(bibhostIcon.address, "bibp1.0/bibpicon.jpg");
6404
bibhostIcon.post_data = NULL;
6405
bibhostIcon.post_content_type = NULL;
6406
bibhostIcon.bookmark = FALSE;
6407
bibhostIcon.isHEAD = FALSE;
6408
bibhostIcon.safe = FALSE;
6409
saveFlag = traversal;
6410
traversal = TRUE; /* Hack to force error response. */
6411
BibP_bibhost_available = HTLoadAbsolute(&bibhostIcon) == YES;
6412
traversal = saveFlag;
6413
BibP_bibhost_checked = TRUE;
6415
#endif /* !DISABLE_BIBP */
6418
* Management of User Interface Pages. - kw
6420
* These are mostly temp files. Pages which can be recognized by their
6421
* special URL (after having been loaded) need not be tracked here.
6423
* First some private stuff:
6425
typedef struct uipage_entry {
6433
#define UIP_F_MULTI 0x0001 /* flag: track multiple instances */
6434
#define UIP_F_LIMIT 0x0002 /* flag: limit size of alturl list */
6435
#define UIP_F_LMULTI (UIP_F_MULTI | UIP_F_LIMIT)
6437
static uip_entry ly_uip[] =
6439
{ UIP_HISTORY , UIP_F_LMULTI, NULL, NULL, NULL }
6440
, { UIP_DOWNLOAD_OPTIONS , 0 , NULL, NULL, NULL }
6441
, { UIP_PRINT_OPTIONS , 0 , NULL, NULL, NULL }
6442
, { UIP_SHOWINFO , UIP_F_LMULTI, NULL, NULL, NULL }
6443
, { UIP_LIST_PAGE , UIP_F_LMULTI, NULL, NULL, NULL }
6444
, { UIP_VLINKS , UIP_F_LMULTI, NULL, NULL, NULL }
6445
#if !defined(NO_OPTION_FORMS)
6446
, { UIP_OPTIONS_MENU , UIP_F_LMULTI, NULL, NULL, NULL }
6448
#ifdef DIRED_SUPPORT
6449
, { UIP_DIRED_MENU , 0 , NULL, NULL, NULL }
6450
, { UIP_PERMIT_OPTIONS , 0 , NULL, NULL, NULL }
6451
, { UIP_UPLOAD_OPTIONS , UIP_F_LMULTI, NULL, NULL, NULL }
6453
#ifdef EXP_ADDRLIST_PAGE
6454
, { UIP_ADDRLIST_PAGE , UIP_F_LMULTI, NULL, NULL, NULL }
6456
, { UIP_LYNXCFG , UIP_F_LMULTI, NULL, NULL, NULL }
6457
#if !defined(NO_CONFIG_INFO)
6458
, { UIP_CONFIG_DEF , UIP_F_LMULTI, NULL, NULL, NULL }
6460
/* The following are not generated tempfiles: */
6461
, { UIP_TRACELOG , 0 , NULL, NULL, NULL }
6462
#if defined(DIRED_SUPPORT) && defined(OK_INSTALL)
6463
, { UIP_INSTALL , 0 , NULL, NULL, NULL }
6468
/* Public entry points for User Interface Page management: */
6470
PUBLIC BOOL LYIsUIPage3 ARGS3(
6479
for (i = 0; i < TABLESIZE(ly_uip); i++) {
6480
if (ly_uip[i].type == type) {
6481
if (!ly_uip[i].url) {
6483
} else if ((flagparam & UIP_P_FRAG) ?
6484
(!strncmp(ly_uip[i].url, url, (l=strlen(ly_uip[i].url)))
6485
&& (url[l] == '\0' || url[l] == '#')) :
6486
!strcmp(ly_uip[i].url, url)) {
6488
} else if (ly_uip[i].flags & UIP_F_MULTI) {
6490
HTList *l0 = ly_uip[i].alturls;
6492
while ((p = HTList_nextObject(l0)) != NULL) {
6493
if ((flagparam & UIP_P_FRAG) ?
6494
(!strncmp(p, url, (l=strlen(p)))
6495
&& (url[l] == '\0' || url[l] == '#')) :
6506
PUBLIC void LYRegisterUIPage ARGS2(
6511
for (i = 0; i < TABLESIZE(ly_uip); i++) {
6512
if (ly_uip[i].type == type) {
6513
if (ly_uip[i].url && url &&
6514
!strcmp(ly_uip[i].url, url)) {
6516
} else if (!ly_uip[i].url || !url ||
6517
!(ly_uip[i].flags & UIP_F_MULTI)) {
6518
StrAllocCopy(ly_uip[i].url, url);
6523
HTList *l0 = ly_uip[i].alturls;
6525
while ((p = HTList_nextObject(l0)) != NULL) {
6526
if (!strcmp(p, url))
6528
if (!strcmp(p, ly_uip[i].url)) {
6529
StrAllocCopy(ly_uip[i].url, url);
6534
if (!ly_uip[i].alturls)
6535
ly_uip[i].alturls = HTList_new();
6537
if (n >= HTCacheSize && (ly_uip[i].flags & UIP_F_LIMIT))
6538
HTList_removeFirstObject(ly_uip[i].alturls);
6539
HTList_addObject(ly_uip[i].alturls, ly_uip[i].url);
6540
ly_uip[i].url = NULL;
6541
StrAllocCopy(ly_uip[i].url, url);
6549
PUBLIC void LYUIPages_free NOARGS
6554
for (i = 0; i < TABLESIZE(ly_uip); i++) {
6555
FREE(ly_uip[i].url);
6556
FREE(ly_uip[i].file);
6557
l0 = ly_uip[i].alturls;
6558
while ((p = HTList_nextObject(l0)) != NULL) {
6561
HTList_delete(ly_uip[i].alturls);
6562
ly_uip[i].alturls = NULL;
6567
* Convert local pathname to www name
6568
* (do not bother about file://localhost prefix at this point).
6570
PUBLIC CONST char * wwwName ARGS1(
6571
CONST char *, pathname)
6573
CONST char *cp = NULL;
6575
#if defined(USE_DOS_DRIVES)
6576
cp = HTDOS_wwwName(pathname);
6579
cp = HTVMS_wwwName(pathname);
6589
* Given a user-specified filename, e.g., for download or print, validate and
6590
* expand it. Expand home-directory expressions in the given string. Only
6591
* allow pipes if the user can spawn shell commands.
6593
* Both strings are fixed buffer sizes, LY_MAXPATH.
6595
PUBLIC BOOLEAN LYValidateFilename ARGS2(
6603
* Cancel if the user entered "/dev/null" on Unix,
6604
* or an "nl:" path on VMS. - FM
6606
if (LYIsNullDevice(given))
6608
/* just ignore it */
6612
if (LYIsPipeCommand(given)) {
6614
HTUserMsg(SPAWNING_DISABLED);
6617
LYstrncpy(result, given, LY_MAXPATH);
6621
if ((cp = strchr(given, '~')) != 0
6622
&& (cp2 = wwwName(Home_Dir())) != 0
6623
&& strlen(cp2) + strlen(given) < LY_MAXPATH) {
6625
strcpy(result, given);
6626
LYTrimPathSep(result);
6627
strcat(result, cp2);
6629
strcpy(given, result);
6632
if (strchr(given, '/') != NULL) {
6633
strcpy(result, HTVMS_name("", given));
6634
strcpy(given, result);
6637
&& strchr(given, ':') == NULL
6638
&& strlen(given) < LY_MAXPATH - 13) {
6639
strcpy(result, "sys$disk:");
6640
if (strchr(given, ']') == NULL)
6641
strcat(result, "[]");
6642
strcat(result, given);
6644
strcpy(result, given);
6649
if (!LYisAbsPath(given)) {
6650
#if defined(__DJGPP__) || defined(_WINDOWS)
6651
if (strchr(result, ':') != NULL)
6654
#endif /* __DJGPP__ || _WINDOWS */
6656
#ifdef SUPPORT_CHDIR
6657
static char buf[LY_MAXPATH];
6658
cp = Current_Dir(buf);
6671
if (strlen(cp) >= LY_MAXPATH - 2)
6673
sprintf(result, "%s/", cp);
6675
cp = HTSYS_name(given);
6676
if (strlen(result) + strlen(cp) >= LY_MAXPATH - 1)
6684
* Given a valid filename, check if it exists. If so, we'll have to worry
6685
* about overwriting it.
6692
PUBLIC int LYValidateOutput ARGS1(
6698
* Assume we can write to a pipe
6701
if (LYIsPipeCommand(filename))
6705
if (no_dotfiles || !show_dotfiles) {
6706
if (*LYPathLeaf(filename) == '.') {
6707
HTAlert(FILENAME_CANNOT_BE_DOT);
6713
* See if it already exists.
6715
if (LYCanReadFile(filename)) {
6717
c = HTConfirm(FILE_EXISTS_HPROMPT);
6719
c = HTConfirm(FILE_EXISTS_OPROMPT);
6721
if (HTLastConfirmCancelled()) {
6722
HTInfoMsg(SAVE_REQUEST_CANCELLED);
6724
} else if (c == NO) {
6732
* Convert a local filename to a URL
6734
PUBLIC void LYLocalFileToURL ARGS2(
6736
CONST char *, source)
6740
StrAllocCopy(*target, "file://localhost");
6742
leaf = wwwName(source);
6744
if (!LYisAbsPath(source)) {
6745
char temp[LY_MAXPATH];
6747
if (!LYIsHtmlSep(*temp))
6748
LYAddHtmlSep(target);
6749
StrAllocCat(*target, temp);
6751
if (!LYIsHtmlSep(*leaf))
6752
LYAddHtmlSep(target);
6753
StrAllocCat(*target, leaf);
6757
* Open a temporary file for internal-pages, optionally reusing an existing
6760
PUBLIC FILE *InternalPageFP ARGS2(
6766
if (LYReuseTempfiles && reuse_flag) {
6767
fp = LYOpenTempRewrite(filename, HTML_SUFFIX, BIN_W);
6769
LYRemoveTemp(filename);
6770
fp = LYOpenTemp(filename, HTML_SUFFIX, BIN_W);
6773
HTAlert(CANNOT_OPEN_TEMP);
6778
PUBLIC void BeginInternalPage ARGS3(
6783
fprintf(fp0, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n");
6785
fprintf(fp0, "<html>\n<head>\n");
6786
LYAddMETAcharsetToFD(fp0, -1);
6787
if (LYIsListpageTitle(Title)) {
6788
if (strchr(HTLoadedDocumentURL(), '"') == NULL) {
6789
char *Address = NULL;
6791
* Insert a BASE tag so there is some way to relate the List Page
6792
* file to its underlying document after we are done. It won't be
6793
* actually used for resolving relative URLs. - kw
6795
StrAllocCopy(Address, HTLoadedDocumentURL());
6796
LYEntify(&Address, FALSE);
6797
fprintf(fp0, "<base href=\"%s\">\n", Address);
6801
fprintf(fp0, "<title>%s</title>\n</head>\n<body>\n",
6804
if ((user_mode == NOVICE_MODE)
6805
&& LYwouldPush(Title, NULL)
6806
&& (HelpURL != 0)) {
6807
fprintf(fp0, "<h1>%s (%s%s%s), <a href=\"%s%s\">help</a></h1>\n",
6808
Title, LYNX_NAME, VERSION_SEGMENT, LYNX_VERSION,
6809
helpfilepath, HelpURL);
6811
fprintf(fp0, "<h1>%s (%s%s%s)</h1>\n",
6812
Title, LYNX_NAME, VERSION_SEGMENT, LYNX_VERSION);
6816
PUBLIC void EndInternalPage ARGS1(
6819
fprintf(fp0, "</body>\n</html>");
6822
PUBLIC char *trimPoundSelector ARGS1(
6825
char *pound = findPoundSelector(address);
6832
* Trim a trailing path-separator to avoid confusing other programs when we concatenate
6833
* to it. This only applies to local filesystems.
6835
PUBLIC void LYTrimPathSep ARGS1(
6841
&& (len = strlen(path)) != 0
6842
&& LYIsPathSep(path[len-1]))
6847
* Add a trailing path-separator to avoid confusing other programs when we concatenate
6848
* to it. This only applies to local filesystems.
6850
PUBLIC void LYAddPathSep ARGS1(
6857
&& ((temp = *path) != 0)
6858
&& (len = strlen(temp)) != 0
6859
&& !LYIsPathSep(temp[len-1])) {
6860
StrAllocCat(*path, PATHSEP_STR);
6865
* Add a trailing path-separator to avoid confusing other programs when we concatenate
6866
* to it. This only applies to local filesystems.
6868
PUBLIC void LYAddPathSep0 ARGS1(
6874
&& (len = strlen(path)) != 0
6875
&& (len < LY_MAXPATH - 2)
6876
&& !LYIsPathSep(path[len-1])) {
6877
strcat(path, PATHSEP_STR);
6882
* Check if a given string contains a path separator
6884
PUBLIC char * LYLastPathSep ARGS1(
6888
#if defined(USE_DOS_DRIVES)
6889
if ((result = strrchr(path, '\\')) == 0)
6890
result = strrchr(path, '/');
6892
result = strrchr(path, '/');
6898
* Trim a trailing path-separator to avoid confusing other programs when we concatenate
6899
* to it. This only applies to HTML paths.
6901
PUBLIC void LYTrimHtmlSep ARGS1(
6907
&& (len = strlen(path)) != 0
6908
&& LYIsHtmlSep(path[len-1]))
6913
* Add a trailing path-separator to avoid confusing other programs when we concatenate
6914
* to it. This only applies to HTML paths.
6916
PUBLIC void LYAddHtmlSep ARGS1(
6923
&& ((temp = *path) != 0)
6924
&& (len = strlen(temp)) != 0
6925
&& !LYIsHtmlSep(temp[len-1])) {
6926
StrAllocCat(*path, "/");
6931
* Add a trailing path-separator to avoid confusing other programs when we concatenate
6932
* to it. This only applies to HTML paths.
6934
PUBLIC void LYAddHtmlSep0 ARGS1(
6940
&& (len = strlen(path)) != 0
6941
&& (len < LY_MAXPATH - 2)
6942
&& !LYIsHtmlSep(path[len-1])) {
6950
PUBLIC int LYCopyFile ARGS2(
6955
CONST char *program;
6957
if ((program = HTGetProgramPath(ppCOPY)) != NULL) {
6958
char *the_command = 0;
6960
HTAddParam(&the_command, COPY_COMMAND, 1, program);
6961
HTAddParam(&the_command, COPY_COMMAND, 2, src);
6962
HTAddParam(&the_command, COPY_COMMAND, 3, dst);
6963
HTEndParam(&the_command, COPY_COMMAND, 3);
6965
CTRACE((tfp, "command: %s\n", the_command));
6967
code = LYSystem(the_command);
6973
unsigned char buff[BUFSIZ];
6977
if ((fin = fopen(src, BIN_R)) != 0) {
6978
if ((fout = fopen(dst, BIN_W)) != 0) {
6980
while ((len = fread(buff, 1, sizeof(buff), fin)) > 0) {
6981
fwrite(buff, 1, len, fout);
6987
LYCloseOutput(fout);
6994
HTAlert(CANNOT_WRITE_TO_FILE);
7000
PRIVATE char *escape_backslashes ARGS1(char *, source)
7006
for (n = 0; source[n] != '\0'; ++n) {
7007
if (source[n] == '\\')
7011
result = malloc(count + n + 1);
7014
char *target = result;
7015
while ((ch = *source++) != '\0') {
7025
#endif /* __DJGPP__ */
7027
* Invoke a shell command, return nonzero on error.
7029
PUBLIC int LYSystem ARGS1(
7034
#if defined(HAVE_SIGACTION) && defined(SIGTSTP) && !defined(USE_SLANG)
7035
struct sigaction saved_sigtstp_act;
7036
BOOLEAN sigtstp_saved = FALSE;
7038
int saved_errno = 0;
7045
CTRACE((tfp, "LYSystem(%s)\n", command));
7049
__djgpp_set_ctrl_c(0);
7050
_go32_want_ctrl_break(1);
7051
#endif /* __DJGPP__ */
7054
code = DCLsystem(command);
7056
# ifdef __EMX__ /* FIXME: Should be LY_CONVERT_SLASH? */
7057
/* Configure writes commands which contain direct slashes.
7058
Native command-(non)-shell will not tolerate this. */
7060
char *space = command, *slash = command;
7063
while (*space && *space != ' ' && *space != '\t')
7065
while (slash < space && *slash != '/')
7067
if (slash != space) {
7068
char *old = command;
7071
StrAllocCopy(command, old);
7073
slash = (slash - old) + command - 1;
7074
space = (space - old) + command;
7075
while (++slash < space)
7083
* This chunk of code does not work, for two reasons:
7084
* a) the Cygwin system() function edits out the backslashes
7085
* b) it does not account for more than one parameter, e.g., +number
7087
#if defined(__CYGWIN__) && defined(DOSPATH) /* 1999/02/26 (Fri) */
7089
char cmd[LY_MAXPATH];
7090
char win32_name[LY_MAXPATH];
7091
char new_cmd[LY_MAXPATH];
7092
char new_command[LY_MAXPATH * 2 + 10];
7108
cygwin_conv_to_full_posix_path(cmd, new_cmd);
7110
strcpy(new_cmd, cmd);
7115
if (strchr(p, '\\') == NULL) {
7116
/* for Windows Application */
7117
cygwin_conv_to_full_win32_path(p, win32_name);
7118
sprintf(new_command, "%.*s \"%.*s\"", LY_MAXPATH, new_cmd, LY_MAXPATH, win32_name);
7120
/* for DOS like editor */
7131
sprintf(new_command, "%.*s %.*s", LY_MAXPATH, new_cmd, LY_MAXPATH, win32_name);
7133
command = new_command;
7139
char *new_command = escape_backslashes(command);
7140
if (new_command != 0) {
7143
command = new_command;
7146
#endif /* __DJGPP__ */
7149
code = exec_command(command, TRUE); /* Wait exec */
7150
#else /* !_WIN_CC */
7152
if (restore_sigpipe_for_children)
7153
signal(SIGPIPE, SIG_DFL); /* Some commands expect the default */
7155
#if defined(HAVE_SIGACTION) && defined(SIGTSTP) && !defined(USE_SLANG)
7156
if (!dump_output_immediately && !LYCursesON && !no_suspend)
7157
sigtstp_saved = LYToggleSigDfl(SIGTSTP, &saved_sigtstp_act, 1);
7159
code = system(command);
7160
saved_errno = errno;
7161
#if defined(HAVE_SIGACTION) && defined(SIGTSTP) && !defined(USE_SLANG)
7163
LYToggleSigDfl(SIGTSTP, &saved_sigtstp_act, 0);
7166
if (restore_sigpipe_for_children)
7167
signal(SIGPIPE, SIG_IGN); /* Ignore it again - kw */
7173
__djgpp_set_ctrl_c(1);
7174
_go32_want_ctrl_break(0);
7175
#endif /* __DJGPP__ */
7182
#if !defined(UCX) || !defined(VAXC) /* errno not modifiable ?? */
7183
set_errno(saved_errno); /* may have been clobbered */
7185
#ifdef __EMX__ /* Check whether the screen size changed */
7192
* Return a string which can be used in LYSystem() for spawning a subshell
7194
#if defined(__CYGWIN__) /* 1999/02/26 (Fri) */
7195
PUBLIC int Cygwin_Shell NOARGS
7199
STARTUPINFO startUpInfo;
7200
PROCESS_INFORMATION procInfo;
7201
SECURITY_ATTRIBUTES sa;
7203
/* Set up security attributes to allow inheritance of the file handle */
7205
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
7206
sa.lpSecurityDescriptor = 0;
7207
sa.bInheritHandle=TRUE;
7209
/* Init a startup structure */
7210
GetStartupInfo(&startUpInfo);
7212
shell = LYGetEnv("COMSPEC");
7214
/* Create the child process, specifying
7215
inherited handles. Pass the value of the
7216
handle as a command line parameter */
7219
code = CreateProcess(0, shell, 0, 0,
7221
0, 0, &startUpInfo, &procInfo);
7224
printf("shell = [%s], code = %ld\n", shell, GetLastError());
7227
/* wait for the child to return (this is not a requirement
7228
since the child is its own independent process) */
7229
WaitForSingleObject(procInfo.hProcess, INFINITE);
7236
PUBLIC char *LYSysShell NOARGS
7241
shell = LYGetEnv("SHELL");
7243
if (access(shell, 0) != 0)
7244
shell = LYGetEnv("COMSPEC");
7246
if (shell == NULL) {
7250
shell = "command.com";
7253
shell = LYGetEnv("SHELL");
7254
if (shell == NULL) {
7255
shell = LYGetEnv("COMSPEC");
7257
if (shell == NULL) {
7258
shell = "command.com";
7263
if (LYGetEnv("SHELL") != NULL) {
7264
shell = LYGetEnv("SHELL");
7266
shell = (LYGetEnv("COMSPEC") == NULL) ? "cmd.exe" : LYGetEnv("COMSPEC");
7272
shell = "exec $SHELL";
7273
#endif /* __EMX__ */
7275
#endif /* DOSPATH */
7280
#define DISPLAY "DECW$DISPLAY"
7282
#define DISPLAY "DISPLAY"
7286
* Return the X-Window $DISPLAY string if it is nonnull/nonempty
7288
PUBLIC char *LYgetXDisplay NOARGS
7290
return LYGetEnv(DISPLAY);
7294
* Set the value of the X-Window $DISPLAY variable (yes it leaks memory, but
7295
* that is putenv's fault).
7297
PUBLIC void LYsetXDisplay ARGS1(
7298
char *, new_display)
7300
if (new_display != 0) {
7302
LYUpperCase(new_display);
7303
Define_VMSLogical(DISPLAY, new_display);
7305
static char *display_putenv_command;
7307
HTSprintf0(&display_putenv_command, "DISPLAY=%s", new_display);
7308
putenv(display_putenv_command);
7310
if ((new_display = LYgetXDisplay()) != 0) {
7311
StrAllocCopy(x_display, new_display);
7316
#ifdef CAN_CUT_AND_PASTE
7319
static int proc_type = -1;
7324
PRIVATE void morph_PM NOARGS
7329
if (proc_type == -1) {
7330
DosGetInfoBlocks(&tib, &pib);
7331
proc_type = pib->pib_ultype;
7334
if (pib->pib_ultype != 3) /* 2 is VIO */
7335
pib->pib_ultype = 3; /* 3 is PM */
7337
hab = WinInitialize(0);
7338
/* 64 messages if before OS/2 3.0, ignored otherwise */
7339
hmq = WinCreateMsgQueue(hab, 64);
7340
WinCancelShutdown(hmq, 1); /* Do not inform us on shutdown */
7343
PRIVATE void unmorph_PM NOARGS
7345
WinDestroyMsgQueue(hmq);
7346
pib->pib_ultype = proc_type;
7349
PUBLIC int size_clip NOARGS
7354
/* Code partially stolen from FED editor. */
7356
PUBLIC int put_clip ARGS1(char *, s)
7358
int sz = strlen(s) + 1;
7359
int ret = EOF, nl = 0;
7360
char *pByte = 0, *s1 = s, c, *t;
7362
while ((c = *s1++)) {
7363
if (c == '\r' && *s1 == '\n')
7368
if (DosAllocSharedMem((PPVOID)&pByte, 0, sz + nl,
7369
PAG_WRITE | PAG_COMMIT | OBJ_GIVEABLE | OBJ_GETTABLE))
7373
memcpy(pByte, s, sz);
7376
while ((c = *t++ = *s++))
7377
if (c == '\n' && (t == pByte + 1 || t[-2] != '\r'))
7378
t[-1] = '\r', *t++ = '\n';
7385
WinOpenClipbrd(hab);
7386
WinEmptyClipbrd(hab);
7387
if (WinSetClipbrdData(hab, (ULONG) pByte, CF_TEXT, CFI_POINTER))
7389
WinCloseClipbrd(hab);
7394
DosFreeMem((PPVOID)&pByte);
7398
static int clip_open;
7400
/* get_clip_grab() returns a pointer to the string in the system area.
7401
get_clip_release() should be called ASAP after this. */
7403
PUBLIC char* get_clip_grab NOARGS
7415
WinQueryClipbrdFmtInfo(hab, CF_TEXT, &ulFormat);
7416
if(ulFormat != CFI_POINTER) {
7420
WinOpenClipbrd(hab);
7422
ClipData = (char *)WinQueryClipbrdData(hab, CF_TEXT);
7423
sz = strlen(ClipData);
7424
if(!ClipData || !sz) {
7431
PUBLIC void get_clip_release NOARGS
7435
WinCloseClipbrd(hab);
7440
#else /* !( defined __EMX__ ) */
7442
# if !defined(WIN_EX) && defined(HAVE_POPEN)
7444
static FILE* paste_handle = 0;
7445
static char *paste_buf = NULL;
7447
PUBLIC void get_clip_release NOARGS
7449
if (paste_handle != 0)
7450
pclose(paste_handle);
7455
PRIVATE int clip_grab NOARGS
7457
char *cmd = LYGetEnv("RL_PASTE_CMD");
7460
pclose(paste_handle);
7464
paste_handle = popen(cmd, "rt");
7470
#define PASTE_BUFFER 1008
7471
#define CF_TEXT 0 /* Not used */
7473
PUBLIC char* get_clip_grab NOARGS
7476
int size = PASTE_BUFFER;
7485
paste_buf = (char*)malloc (PASTE_BUFFER);
7487
len = fread (paste_buf + off, 1, PASTE_BUFFER - 1, paste_handle);
7488
paste_buf[off + len] = '\0';
7489
if (len < PASTE_BUFFER - 1)
7491
if (strchr (paste_buf + off, '\r')
7492
|| strchr (paste_buf + off, '\n'))
7494
paste_buf = realloc (paste_buf, size += PASTE_BUFFER - 1);
7501
put_clip ARGS1(char *, s)
7503
char *cmd = LYGetEnv("RL_CLCOPY_CMD");
7505
int l = strlen(s), res;
7510
fh = popen (cmd, "wt");
7513
res = fwrite (s, 1, l, fh);
7514
if (pclose (fh) != 0 || res != l)
7519
# endif /* !defined(WIN_EX) && defined(HAVE_POPEN) */
7521
#endif /* __EMX__ */
7523
#if defined(WIN_EX) /* 1997/10/16 (Thu) 20:13:28 */
7525
PUBLIC int put_clip(char *szBuffer)
7533
if (szBuffer == NULL)
7536
len = strlen(szBuffer);
7542
m_hLogData = GlobalAlloc(GHND, len);
7543
if (m_hLogData == NULL) {
7548
if (!OpenClipboard(hWnd)) {
7551
/* Remove the current Clipboard contents */
7552
if (!EmptyClipboard()) {
7553
GlobalFree(m_hLogData);
7557
/* Lock the global memory while we write to it. */
7558
pLogData = (LPTSTR) GlobalLock(m_hLogData);
7560
lstrcpy((LPTSTR) pLogData, szBuffer);
7561
GlobalUnlock(m_hLogData);
7563
/* If there were any lines at all then copy them to clipboard. */
7564
hClip = SetClipboardData(CF_TEXT, m_hLogData);
7566
/* If we couldn't clip the data then free the global handle. */
7567
GlobalFree(m_hLogData);
7574
static HANDLE m_hLogData;
7575
static int m_locked;
7577
/* get_clip_grab() returns a pointer to the string in the system area.
7578
get_clip_release() should be called ASAP after this. */
7580
PUBLIC char* get_clip_grab()
7586
if (!OpenClipboard(hWnd)) {
7590
m_hLogData = GetClipboardData(CF_TEXT);
7592
if (m_hLogData == NULL) {
7597
pLogData = (LPTSTR) GlobalLock(m_hLogData);
7603
PUBLIC void get_clip_release()
7607
GlobalUnlock(m_hLogData);
7612
#endif /* CAN_CUT_AND_PASTE */
7617
#define WSABASEERR 10000
7621
* Description: the windows32 version of perror()
7623
* Returns: a pointer to a static error
7625
* Notes/Dependencies: I got this from
7626
* comp.os.ms-windows.programmer.win32
7628
PUBLIC char * w32_strerror(DWORD ercode)
7630
/* __declspec(thread) necessary if you will use multiple threads */
7632
static char msg_buff[256];
7634
__declspec(thread) static char msg_buff[256];
7638
unsigned char *p, *q, tmp_buff[256];
7641
msg_type = FORMAT_MESSAGE_FROM_SYSTEM;
7642
/* Fill message buffer with a default message in
7643
* case FormatMessage fails
7645
wsprintf(msg_buff, "Error %ld", ercode);
7648
* Special code for winsock error handling.
7650
if (ercode > WSABASEERR) {
7651
hModule = GetModuleHandle("wsock32");
7652
if (hModule == NULL)
7653
ercode = GetLastError();
7655
msg_type = FORMAT_MESSAGE_FROM_HMODULE;
7660
FormatMessage(msg_type,
7668
strcpy(tmp_buff, msg_buff);
7672
if (!(*p == '\n' || *p == '\r'))
7683
#if !defined(VMS) && defined(SYSLOG_REQUESTED_URLS)
7685
* syslog() interface
7687
PUBLIC void LYOpenlog ARGS1(
7688
CONST char *, banner)
7691
openlog("lynx", LOG_PID|LOG_NDELAY, LOG_LOCAL5);
7693
openlog("lynx", LOG_PID, LOG_LOCAL5);
7697
syslog(LOG_INFO, "Session start:%s", banner);
7699
syslog(LOG_INFO, "Session start");
7703
PRIVATE BOOLEAN looks_like_password ARGS2(
7707
BOOLEAN result = FALSE;
7709
while (first <= last) {
7721
PUBLIC void LYSyslog ARGS1(
7728
CTRACE((tfp, "LYSyslog %s\n", arg));
7730
if (is_url(arg)) { /* proto://user:password@host/path:port */
7732
if ((colon1 = strchr(arg, ':')) != 0
7733
&& !strncmp(colon1, "://", 3)
7734
&& (colon2 = strchr(colon1+3, ':')) != 0
7735
&& (atsign = strchr(colon1, '@')) != 0
7736
&& (colon2 < atsign)
7737
&& looks_like_password(colon2 + 1, atsign - 1)) {
7740
StrAllocCopy(buf, arg);
7741
buf[colon2 - arg + 1] = 0;
7742
StrAllocCat(buf, "******");
7743
StrAllocCat(buf, atsign);
7744
syslog (LOG_INFO|LOG_LOCAL5, "%s", buf);
7745
CTRACE((tfp, "...alter %s\n", buf));
7750
syslog (LOG_INFO|LOG_LOCAL5, "%s", NONNULL(arg));
7753
PUBLIC void LYCloselog NOARGS
7755
syslog(LOG_INFO, "Session over");
7759
#endif /* !VMS && SYSLOG_REQUESTED_URLS */