~ubuntu-branches/ubuntu/edgy/lynx/edgy

« back to all changes in this revision

Viewing changes to src/LYUtils.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2004-09-16 12:14:10 UTC
  • Revision ID: james.westby@ubuntu.com-20040916121410-cz1gu92c4nqfeyrg
Tags: upstream-2.8.5
ImportĀ upstreamĀ versionĀ 2.8.5

Show diffs side-by-side

added added

removed removed

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