~ubuntu-branches/ubuntu/warty/lynx/warty-security

« back to all changes in this revision

Viewing changes to src/LYBookmark.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 <HTAlert.h>
 
3
#include <HTFile.h>
 
4
#include <LYUtils.h>
 
5
#include <LYStrings.h>
 
6
#include <LYBookmark.h>
 
7
#include <LYGlobalDefs.h>
 
8
#include <LYClean.h>
 
9
#include <LYKeymap.h>
 
10
#include <LYCharUtils.h> /* need for META charset */
 
11
#include <UCAux.h>
 
12
#include <LYCharSets.h>  /* need for LYHaveCJKCharacterSet */
 
13
#include <LYCurses.h>
 
14
#include <GridText.h>
 
15
#include <HTCJK.h>
 
16
 
 
17
#ifdef VMS
 
18
#include <nam.h>
 
19
#endif /* VMS */
 
20
 
 
21
#include <LYLeaks.h>
 
22
 
 
23
PUBLIC char *MBM_A_subbookmark[MBM_V_MAXFILES+1];
 
24
PUBLIC char *MBM_A_subdescript[MBM_V_MAXFILES+1];
 
25
 
 
26
PRIVATE BOOLEAN is_mosaic_hotlist = FALSE;
 
27
PRIVATE char * convert_mosaic_bookmark_file PARAMS((char *filename_buffer));
 
28
 
 
29
PUBLIC int LYindex2MBM ARGS1(int, n)
 
30
{
 
31
    static char MBMcodes[MBM_V_MAXFILES+2] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 
32
    return n >= 0 && n <= MBM_V_MAXFILES ? MBMcodes[n] : '?';
 
33
}
 
34
 
 
35
PUBLIC int LYMBM2index ARGS1(int, ch)
 
36
{
 
37
    if ((ch = TOUPPER(ch)) > 0) {
 
38
        char *letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 
39
        char *result = strchr(letters, ch);
 
40
        if (result != 0
 
41
         && (result - letters) <= MBM_V_MAXFILES)
 
42
            return (result - letters);
 
43
    }
 
44
    return -1;
 
45
}
 
46
 
 
47
PRIVATE void
 
48
show_bookmark_not_defined NOARGS
 
49
{
 
50
    char *string_buffer = 0;
 
51
 
 
52
    HTSprintf0(&string_buffer,
 
53
            BOOKMARK_FILE_NOT_DEFINED,
 
54
            key_for_func(LYK_OPTIONS));
 
55
    LYMBM_statusline(string_buffer);
 
56
    FREE(string_buffer);
 
57
}
 
58
 
 
59
/*
 
60
 *  Tries to open a bookmark file for reading, which may be
 
61
 *  the default, or based on offering the user a choice from
 
62
 *  the MBM_A_subbookmark[] array.  If successful the file is
 
63
 *  closed, and the filename in system path specs is returned,
 
64
 *  the URL is allocated into *URL, and the MBM_A_subbookmark[]
 
65
 *  filepath is allocated into the BookmarkPage global.  Returns
 
66
 *  a zero-length pointer to flag a cancel, or a space to flag
 
67
 *  an undefined selection, without allocating into *URL or
 
68
 *  BookmarkPage.  Returns NULL with allocating into BookmarkPage
 
69
 *  but not *URL is the selection is valid but the file doesn't
 
70
 *  yet exist. - FM
 
71
 */
 
72
PUBLIC char * get_bookmark_filename ARGS1(
 
73
        char **,        URL)
 
74
{
 
75
    static char filename_buffer[LY_MAXPATH];
 
76
    char *string_buffer = 0;
 
77
    FILE *fp;
 
78
    int MBM_tmp;
 
79
 
 
80
    /*
 
81
     *  Multi_Bookmarks support. - FMG & FM
 
82
     *  Let user select a bookmark file.
 
83
     */
 
84
    MBM_tmp = select_multi_bookmarks();
 
85
    if (MBM_tmp == -2)
 
86
        /*
 
87
         *  Zero-length pointer flags a cancel. - FM
 
88
         */
 
89
        return("");
 
90
    if (MBM_tmp == -1) {
 
91
        show_bookmark_not_defined();
 
92
        /*
 
93
         *  Space flags an undefined selection. - FMG
 
94
         */
 
95
        return(" ");
 
96
    } else {
 
97
        /*
 
98
         *  Save the filepath as a global.  The system path will be
 
99
         *  loaded into to the (static) filename_buffer as the return
 
100
         *  value, the URL will be allocated into *URL, and we also
 
101
         *  need the filepath available to calling functions.  This
 
102
         *  is all pitifully non-reentrant, a la the original Lynx,
 
103
         *  and should be redesigned someday. - FM
 
104
         */
 
105
        StrAllocCopy(BookmarkPage, MBM_A_subbookmark[MBM_tmp]);
 
106
    }
 
107
 
 
108
    /*
 
109
     *  Seek it in the home path. - FM
 
110
     */
 
111
    LYAddPathToHome(filename_buffer,
 
112
                    sizeof(filename_buffer),
 
113
                    BookmarkPage);
 
114
    CTRACE((tfp, "\nget_bookmark_filename: SEEKING %s\n   AS %s\n\n",
 
115
                BookmarkPage, filename_buffer));
 
116
    if ((fp = fopen(filename_buffer, TXT_R)) != NULL) {
 
117
        /*
 
118
         * We now have the file open.
 
119
         * Check if it is a mosaic hotlist.
 
120
         */
 
121
        if (LYSafeGets(&string_buffer, fp) != 0
 
122
         && *LYTrimNewline(string_buffer) != '\0'
 
123
         && !strncmp(string_buffer, "ncsa-xmosaic-hotlist-format-1", 29)) {
 
124
            char *newname;
 
125
            /*
 
126
             *  It is a mosaic hotlist file.
 
127
             */
 
128
            is_mosaic_hotlist = TRUE;
 
129
            newname = convert_mosaic_bookmark_file(filename_buffer);
 
130
            LYLocalFileToURL(URL, newname);
 
131
        } else {
 
132
            is_mosaic_hotlist = FALSE;
 
133
            LYLocalFileToURL(URL, filename_buffer);
 
134
        }
 
135
        FREE(string_buffer);
 
136
        LYCloseInput(fp);
 
137
 
 
138
        return(filename_buffer);  /* bookmark file exists */
 
139
    }
 
140
    return(NULL);
 
141
 
 
142
} /* big end */
 
143
 
 
144
/*
 
145
 *  Converts a Mosaic hotlist file into an HTML
 
146
 *  file for handling as a Lynx bookmark file. - FM
 
147
 */
 
148
PRIVATE char * convert_mosaic_bookmark_file ARGS1(
 
149
        char *,         filename_buffer)
 
150
{
 
151
    static char newfile[LY_MAXPATH];
 
152
    FILE *fp, *nfp;
 
153
    char *buf = NULL;
 
154
    int line = -2;
 
155
 
 
156
    LYRemoveTemp(newfile);
 
157
    if ((nfp = LYOpenTemp(newfile, HTML_SUFFIX, "w")) == NULL) {
 
158
        LYMBM_statusline(NO_TEMP_FOR_HOTLIST);
 
159
        LYSleepAlert();
 
160
        return ("");
 
161
    }
 
162
 
 
163
    if ((fp = fopen(filename_buffer, TXT_R)) == NULL)
 
164
        return ("");  /* should always open */
 
165
 
 
166
    fprintf(nfp,"<head>\n<title>%s</title>\n</head>\n",MOSAIC_BOOKMARK_TITLE);
 
167
    fprintf(nfp, "%s\n\n<p>\n<ol>\n", gettext("\
 
168
     This file is an HTML representation of the X Mosaic hotlist file.\n\
 
169
     Outdated or invalid links may be removed by using the\n\
 
170
     remove bookmark command, it is usually the 'R' key but may have\n\
 
171
     been remapped by you or your system administrator."));
 
172
 
 
173
    while ((LYSafeGets(&buf, fp)) != NULL) {
 
174
        if (line >= 0) {
 
175
            LYTrimNewline(buf);
 
176
            if ((line % 2) == 0) { /* even lines */
 
177
                if(*buf != '\0') {
 
178
                    strtok(buf," "); /* kill everything after the space */
 
179
                    fprintf(nfp,"<LI><a href=\"%s\">",buf); /* the URL */
 
180
                }
 
181
            } else { /* odd lines */
 
182
                fprintf(nfp,"%s</a>\n",buf);  /* the title */
 
183
            }
 
184
        }
 
185
        /* else - ignore the line (this gets rid of first two lines) */
 
186
        line++;
 
187
    }
 
188
    LYCloseTempFP(nfp);
 
189
    LYCloseInput(fp);
 
190
    return(newfile);
 
191
}
 
192
 
 
193
PRIVATE  BOOLEAN havevisible PARAMS((CONST char *Title));
 
194
PRIVATE  BOOLEAN have8bit PARAMS((CONST char *Title));
 
195
PRIVATE  char* title_convert8bit PARAMS((CONST char *Title));
 
196
 
 
197
/*
 
198
 *  Adds a link to a bookmark file, creating the file
 
199
 *  if it doesn't already exist, and making sure that
 
200
 *  no_cache is set for a pre-existing, cached file,
 
201
 *  so that the change will be evident on return to
 
202
 *  to that file. - FM
 
203
 */
 
204
PUBLIC void save_bookmark_link ARGS2(
 
205
        char *,         address,
 
206
        char *,         title)
 
207
{
 
208
    FILE *fp;
 
209
    BOOLEAN first_time = FALSE;
 
210
    char *filename;
 
211
    char *bookmark_URL = NULL;
 
212
    char filename_buffer[LY_MAXPATH];
 
213
    char string_buffer[BUFSIZ];
 
214
    char tmp_buffer[BUFSIZ];
 
215
    char *Address = NULL;
 
216
    char *Title = NULL;
 
217
    int i, c;
 
218
    DocAddress WWWDoc;
 
219
    HTParentAnchor *tmpanchor;
 
220
    HText *text;
 
221
 
 
222
    /*
 
223
     *  Make sure we were passed something to save. - FM
 
224
     */
 
225
    if (!(address && *address)) {
 
226
        HTAlert(MALFORMED_ADDRESS);
 
227
        return;
 
228
    }
 
229
 
 
230
    /*
 
231
     *  Offer a choice of bookmark files,
 
232
     *  or get the default. - FMG
 
233
     */
 
234
    filename = get_bookmark_filename(&bookmark_URL);
 
235
 
 
236
    /*
 
237
     *  If filename is NULL, must create a new file.  If
 
238
     *  filename is a space, an invalid bookmark file was
 
239
     *  selected, or if zero-length, the user cancelled.
 
240
     *  Ignore request in both cases.  Otherwise, make
 
241
     *  a copy before anything might change the static
 
242
     *  get_bookmark_filename() buffer. - FM
 
243
     */
 
244
    if (filename == NULL) {
 
245
        first_time = TRUE;
 
246
        filename_buffer[0] = '\0';
 
247
    } else {
 
248
        if (*filename == '\0' || !strcmp(filename," ")) {
 
249
            FREE(bookmark_URL);
 
250
            return;
 
251
        }
 
252
        LYstrncpy(filename_buffer, filename, sizeof(filename_buffer)-1);
 
253
    }
 
254
 
 
255
    /*
 
256
     *  If BookmarkPage is NULL, something went
 
257
     *  wrong, so ignore the request. - FM
 
258
     */
 
259
    if (BookmarkPage == NULL) {
 
260
        FREE(bookmark_URL);
 
261
        return;
 
262
    }
 
263
 
 
264
    /*
 
265
     *  If the link will be added to the same
 
266
     *  bookmark file, get confirmation. - FM
 
267
     */
 
268
    if (LYMultiBookmarks != MBM_OFF) {
 
269
        CONST char *url = HTLoadedDocumentURL();
 
270
        CONST char *page = (*BookmarkPage == '.')
 
271
                            ? (BookmarkPage + 1)
 
272
                            : BookmarkPage;
 
273
        if (strstr(url, page) != NULL) {
 
274
            LYMBM_statusline(MULTIBOOKMARKS_SELF);
 
275
            c = LYgetch_single();
 
276
            if (c != 'L') {
 
277
                FREE(bookmark_URL);
 
278
                return;
 
279
            }
 
280
        }
 
281
    }
 
282
 
 
283
    /*
 
284
     *  Allow user to change the title. - FM
 
285
     */
 
286
    do {
 
287
        if (HTCJK == JAPANESE) {
 
288
            switch(kanji_code) {
 
289
            case EUC:
 
290
                TO_EUC((CONST unsigned char *) title, (unsigned char *) tmp_buffer);
 
291
                break;
 
292
            case SJIS:
 
293
                TO_SJIS((CONST unsigned char *) title, (unsigned char *) tmp_buffer);
 
294
                break;
 
295
            default:
 
296
                break;
 
297
            }
 
298
            LYstrncpy(string_buffer, tmp_buffer, sizeof(string_buffer)-1);
 
299
        } else {
 
300
            LYstrncpy(string_buffer, title, sizeof(string_buffer)-1);
 
301
        }
 
302
        convert_to_spaces(string_buffer, FALSE);
 
303
        LYMBM_statusline(TITLE_PROMPT);
 
304
        LYgetstr(string_buffer, VISIBLE, sizeof(string_buffer), NORECALL);
 
305
        if (*string_buffer == '\0') {
 
306
            LYMBM_statusline(CANCELLED);
 
307
            LYSleepMsg();
 
308
            FREE(bookmark_URL);
 
309
            return;
 
310
        }
 
311
    } while(!havevisible(string_buffer));
 
312
 
 
313
    /*
 
314
     *  Create the Title with any left-angle-brackets
 
315
     *  converted to &lt; entities and any ampersands
 
316
     *  converted to &amp; entities.  - FM
 
317
     *
 
318
     *  Convert 8-bit letters to &#xUUUU to avoid dependencies
 
319
     *  from display character set which may need changing.
 
320
     *  Do NOT convert any 8-bit chars if we have CJK display. - LP
 
321
     */
 
322
    LYformTitle(&Title, string_buffer);
 
323
    LYEntify(&Title, TRUE);
 
324
    if (UCSaveBookmarksInUnicode &&
 
325
        have8bit(Title) && (!LYHaveCJKCharacterSet)) {
 
326
        char *p = title_convert8bit(Title);
 
327
        FREE(Title);
 
328
        Title = p;
 
329
    }
 
330
 
 
331
    /*
 
332
     *  Create the bookmark file, if it doesn't exist already,
 
333
     *  Otherwise, open the pre-existing bookmark file. - FM
 
334
     */
 
335
    SetDefaultMode(O_TEXT);
 
336
    if (first_time) {
 
337
        /*
 
338
         *  Seek it in the home path. - FM
 
339
         */
 
340
        LYAddPathToHome(filename_buffer,
 
341
                        sizeof(filename_buffer),
 
342
                        BookmarkPage);
 
343
    }
 
344
    CTRACE((tfp, "\nsave_bookmark_link: SEEKING %s\n   AS %s\n\n",
 
345
                BookmarkPage, filename_buffer));
 
346
    if ((fp = fopen(filename_buffer, (first_time ? TXT_W : TXT_A))) == NULL) {
 
347
        LYMBM_statusline(BOOKMARK_OPEN_FAILED);
 
348
        LYSleepAlert();
 
349
        FREE(Title);
 
350
        FREE(bookmark_URL);
 
351
        return;
 
352
    }
 
353
 
 
354
    /*
 
355
     *  Convert all ampersands in the address to &amp; entities. - FM
 
356
     */
 
357
    StrAllocCopy(Address, address);
 
358
    LYEntify(&Address, FALSE);
 
359
 
 
360
    /*
 
361
     *  If we created a new bookmark file, write the headers. - FM
 
362
     *  Once and forever...
 
363
     */
 
364
    if (first_time) {
 
365
        fprintf(fp, "<head>\n");
 
366
#if defined(SH_EX) && !defined(_WINDOWS)        /* 1997/12/11 (Thu) 19:13:40 */
 
367
        if (HTCJK != JAPANESE)
 
368
            LYAddMETAcharsetToFD(fp, -1);
 
369
        else
 
370
            fprintf(fp, "<META %s %s>\n",
 
371
                    "http-equiv=\"content-type\"",
 
372
                    "content=\"text/html;charset=iso-2022-jp\"");
 
373
#else
 
374
        LYAddMETAcharsetToFD(fp, -1);
 
375
#endif  /* !_WINDOWS */
 
376
        fprintf(fp,"<title>%s</title>\n</head>\n", BOOKMARK_TITLE);
 
377
#ifdef _WINDOWS
 
378
        fprintf(fp,
 
379
            gettext("     You can delete links by the 'R' key<br>\n<ol>\n"));
 
380
#else
 
381
        fprintf(fp, "%s<br>\n%s\n\n<!--\n%s\n-->\n\n<p>\n<ol>\n",
 
382
                    gettext("\
 
383
     You can delete links using the remove bookmark command.  It is usually\n\
 
384
     the 'R' key but may have been remapped by you or your system\n\
 
385
     administrator."),
 
386
                    gettext("\
 
387
     This file also may be edited with a standard text editor to delete\n\
 
388
     outdated or invalid links, or to change their order."),
 
389
                    gettext("\
 
390
Note: if you edit this file manually\n\
 
391
      you should not change the format within the lines\n\
 
392
      or add other HTML markup.\n\
 
393
      Make sure any bookmark link is saved as a single line."));
 
394
#endif  /* _WINDOWS */
 
395
    }
 
396
 
 
397
    /*
 
398
     *  Add the bookmark link, in Mosaic hotlist or Lynx format. - FM
 
399
     */
 
400
    if (is_mosaic_hotlist) {
 
401
        time_t NowTime = time(NULL);
 
402
        char *TimeString = (char *)ctime (&NowTime);
 
403
        /*
 
404
         *  TimeString has a \n at the end.
 
405
         */
 
406
        fprintf(fp,"%s %s%s\n", Address, TimeString, Title);
 
407
    } else {
 
408
        fprintf(fp,"<LI><a href=\"%s\">%s</a>\n", Address, Title);
 
409
    }
 
410
    LYCloseOutput(fp);
 
411
 
 
412
    SetDefaultMode(O_BINARY);
 
413
    /*
 
414
     *  If this is a cached bookmark file, set nocache for
 
415
     *  it so we'll see the new bookmark link when that
 
416
     *  cache is retrieved. - FM
 
417
     */
 
418
    if (!first_time && nhist > 0 && bookmark_URL) {
 
419
        for (i = 0; i < nhist; i++) {
 
420
            if (HDOC(i).bookmark &&
 
421
                !strcmp(HDOC(i).address, bookmark_URL)) {
 
422
                WWWDoc.address = HDOC(i).address;
 
423
                WWWDoc.post_data = NULL;
 
424
                WWWDoc.post_content_type = NULL;
 
425
                WWWDoc.bookmark = HDOC(i).bookmark;
 
426
                WWWDoc.isHEAD = FALSE;
 
427
                WWWDoc.safe = FALSE;
 
428
                tmpanchor = HTAnchor_findAddress(&WWWDoc);
 
429
                if ((text = (HText *)HTAnchor_document(tmpanchor)) != NULL) {
 
430
                    HText_setNoCache(text);
 
431
                }
 
432
                break;
 
433
            }
 
434
        }
 
435
    }
 
436
 
 
437
    /*
 
438
     *  Clean up and report success.
 
439
     */
 
440
    FREE(Title);
 
441
    FREE(Address);
 
442
    FREE(bookmark_URL);
 
443
    LYMBM_statusline(OPERATION_DONE);
 
444
    LYSleepMsg();
 
445
}
 
446
 
 
447
/*
 
448
 *  Remove a link from a bookmark file.  The calling
 
449
 *  function is expected to have used get_filename_link(),
 
450
 *  pass us the link number as cur, the MBM_A_subbookmark[]
 
451
 *  string as cur_bookmark_page, and to have set up no_cache
 
452
 *  itself. - FM
 
453
 */
 
454
PUBLIC void remove_bookmark_link ARGS2(
 
455
        int,            cur,
 
456
        char *,         cur_bookmark_page)
 
457
{
 
458
    FILE *fp, *nfp;
 
459
    char *buf = NULL;
 
460
    int n;
 
461
#ifdef VMS
 
462
    char filename_buffer[NAM$C_MAXRSS+12];
 
463
    char newfile[NAM$C_MAXRSS+12];
 
464
#define keep_tempfile FALSE
 
465
#else
 
466
    char filename_buffer[LY_MAXPATH];
 
467
    char newfile[LY_MAXPATH];
 
468
    BOOLEAN keep_tempfile = FALSE;
 
469
#ifdef UNIX
 
470
    struct stat stat_buf;
 
471
    mode_t mode;
 
472
    BOOLEAN regular = FALSE;
 
473
#endif /* UNIX */
 
474
#endif /* VMS */
 
475
    char homepath[LY_MAXPATH];
 
476
 
 
477
    CTRACE((tfp, "remove_bookmark_link: deleting link number: %d\n", cur));
 
478
 
 
479
    if (!cur_bookmark_page)
 
480
        return;
 
481
    LYAddPathToHome(filename_buffer,
 
482
                    sizeof(filename_buffer),
 
483
                    cur_bookmark_page);
 
484
    CTRACE((tfp, "\nremove_bookmark_link: SEEKING %s\n   AS %s\n\n",
 
485
                cur_bookmark_page, filename_buffer));
 
486
    if ((fp = fopen(filename_buffer, TXT_R)) == NULL) {
 
487
        HTAlert(BOOKMARK_OPEN_FAILED_FOR_DEL);
 
488
        return;
 
489
    }
 
490
 
 
491
    LYAddPathToHome(homepath, sizeof(homepath), "");
 
492
    if ((nfp = LYOpenScratch(newfile, homepath)) == 0) {
 
493
        LYCloseInput(fp);
 
494
        HTAlert(BOOKSCRA_OPEN_FAILED_FOR_DEL);
 
495
        return;
 
496
    }
 
497
 
 
498
#ifdef UNIX
 
499
    /*
 
500
     *  Explicitly preserve bookmark file mode on Unix. - DSL
 
501
     */
 
502
    if (stat(filename_buffer, &stat_buf) == 0) {
 
503
        regular = (S_ISREG(stat_buf.st_mode) && stat_buf.st_nlink == 1);
 
504
        mode = ((stat_buf.st_mode & 0777) | 0600); /* make it writable */
 
505
        (void) chmod(newfile, mode);
 
506
        if ((nfp = LYReopenTemp(newfile)) == NULL) {
 
507
            (void) LYCloseInput(fp);
 
508
            HTAlert(BOOKTEMP_REOPEN_FAIL_FOR_DEL);
 
509
            return;
 
510
        }
 
511
    }
 
512
#endif /* UNIX */
 
513
 
 
514
    if (is_mosaic_hotlist) {
 
515
        int del_line = cur*2;  /* two lines per entry */
 
516
        n = -3;  /* skip past cookie and name lines */
 
517
        while (LYSafeGets(&buf, fp) != NULL) {
 
518
            n++;
 
519
            if (n == del_line || n == del_line+1)
 
520
                continue;  /* remove two lines */
 
521
            if (fputs(buf, nfp) == EOF)
 
522
                goto failure;
 
523
        }
 
524
 
 
525
    } else {
 
526
        char *cp, *cp2;
 
527
        BOOLEAN retain;
 
528
        int seen;
 
529
 
 
530
        n = -1;
 
531
        while (LYSafeGets(&buf, fp) != NULL) {
 
532
            int keep_ol = FALSE;
 
533
            retain = TRUE;
 
534
            seen = 0;
 
535
            cp = buf;
 
536
            if ((cur == 0) && (cp2 = LYstrstr(cp,"<ol><LI>")))
 
537
                keep_ol = TRUE; /* Do not erase, this corrects a bug in an
 
538
                                   older version */
 
539
            while (n < cur && (cp = LYstrstr(cp, "<a href="))) {
 
540
                seen++;
 
541
                if (++n == cur) {
 
542
                    if (seen != 1 || !LYstrstr(buf, "</a>") ||
 
543
                        LYstrstr((cp + 1), "<a href=")) {
 
544
                        HTAlert(BOOKMARK_LINK_NOT_ONE_LINE);
 
545
                        goto failure;
 
546
                    }
 
547
                    CTRACE((tfp, "remove_bookmark_link: skipping link %d\n", n));
 
548
                    if (keep_ol)
 
549
                        fprintf(nfp,"<ol>\n");
 
550
                    retain = FALSE;
 
551
                }
 
552
                cp += 8;
 
553
            }
 
554
            if (retain && fputs(buf, nfp) == EOF)
 
555
                goto failure;
 
556
        }
 
557
    }
 
558
 
 
559
    FREE(buf);
 
560
    CTRACE((tfp, "remove_bookmark_link: files: %s %s\n",
 
561
                        newfile, filename_buffer));
 
562
 
 
563
    LYCloseInput(fp);
 
564
    fp = NULL;
 
565
    if (fflush(nfp) == EOF) {
 
566
        CTRACE((tfp, "fflush(nfp): %s", LYStrerror(errno)));
 
567
        goto failure;
 
568
    }
 
569
    LYCloseTempFP(nfp);
 
570
    nfp = NULL;
 
571
#if defined(DOSPATH) || defined(__EMX__)
 
572
    remove(filename_buffer);
 
573
#endif /* DOSPATH */
 
574
 
 
575
#ifdef UNIX
 
576
    /*
 
577
     *  By copying onto the bookmark file, rather than renaming it, we
 
578
     *  can preserve the original ownership of the file, provided that
 
579
     *  it is writable by the current process.
 
580
     *  Changed to copy  1998-04-26 -- gil
 
581
     *  But if the copy fails, for example because the filesystem is full,
 
582
     *  we are left with a corrupt bookmark file.  Changed back to use
 
583
     *  the previous mechanism [try rename(), then mv for EXDEV], except
 
584
     *  in usual cases (not a regular file e.g., symbolic link, or has hard
 
585
     *  links).  This will let bookmarks survive a filesystem full condition
 
586
     *  in the "normal" case (bookmark is on same filesystem as home directory,
 
587
     *  is a regular file, has no additional hard links).
 
588
     *  If we first tried LYCopyFile, and that fails, also fall back to trying
 
589
     *  the other stuff.  That gives a chance to recover in case the LYCopyFile
 
590
     *  left a corrupt target file.
 
591
     *  If there is an error, and that error may mean that the bookmark file
 
592
     *  has been corrupted, don't remove the temporary newfile (which should
 
593
     *  always be uncorrupted) in place, it may still be used to recover
 
594
     *  manually.  If this applies, produce an additional message to that
 
595
     *  effect.  The temp file will still be removed by normal program exit
 
596
     *  cleanup. - kw 1999-11-12
 
597
     */
 
598
    if (!regular) {
 
599
        if (LYCopyFile(newfile, filename_buffer) == 0) {
 
600
            LYRemoveTemp(newfile);
 
601
            return;
 
602
        }
 
603
        LYSleepAlert(); /* give a chance to see error from cp - kw */
 
604
        HTUserMsg(BOOKTEMP_COPY_FAIL);
 
605
        keep_tempfile = TRUE;
 
606
    }
 
607
#endif  /* UNIX */
 
608
 
 
609
    if (rename(newfile, filename_buffer) != -1) {
 
610
#ifdef MULTI_USER_UNIX
 
611
        if (regular)
 
612
            chmod(filename_buffer, stat_buf.st_mode & 07777);
 
613
#endif
 
614
        HTSYS_purge(filename_buffer);
 
615
        return;
 
616
    } else {
 
617
#ifndef VMS
 
618
        /*
 
619
         *  Rename won't work across file systems.
 
620
         *  Check if this is the case and do something appropriate.
 
621
         *  Used to be ODD_RENAME
 
622
         */
 
623
#if defined(_WINDOWS) || defined(WIN_EX)
 
624
#if defined(WIN_EX)
 
625
        if (GetLastError() == ERROR_NOT_SAME_DEVICE)
 
626
#else /* !_WIN_EX */
 
627
        if (errno == ENOTSAM)
 
628
#endif /* _WIN_EX */
 
629
        {
 
630
            if (rename(newfile, filename_buffer) != 0) {
 
631
                if (LYCopyFile(newfile, filename_buffer) == 0)
 
632
                    remove(newfile);
 
633
            }
 
634
        }
 
635
#else
 
636
        if (errno == EXDEV) {
 
637
            static CONST char MV_FMT[] = "%s %s %s";
 
638
            char *buffer = 0;
 
639
            CONST char *program;
 
640
 
 
641
            if ((program = HTGetProgramPath(ppMV)) != NULL) {
 
642
                HTAddParam(&buffer, MV_FMT, 1, program);
 
643
                HTAddParam(&buffer, MV_FMT, 2, newfile);
 
644
                HTAddParam(&buffer, MV_FMT, 3, filename_buffer);
 
645
                HTEndParam(&buffer, MV_FMT, 3);
 
646
                if (LYSystem(buffer) == 0) {
 
647
#ifdef MULTI_USER_UNIX
 
648
                    if (regular)
 
649
                        chmod(filename_buffer, stat_buf.st_mode & 07777);
 
650
#endif
 
651
                    FREE(buffer);
 
652
                    return;
 
653
                }
 
654
            }
 
655
            FREE(buffer);
 
656
            keep_tempfile = TRUE;
 
657
            goto failure;
 
658
        }
 
659
        CTRACE((tfp, "rename(): %s", LYStrerror(errno)));
 
660
#endif /* _WINDOWS */
 
661
#endif /* !VMS */
 
662
 
 
663
#ifdef VMS
 
664
        HTAlert(ERROR_RENAMING_SCRA);
 
665
#else
 
666
        HTAlert(ERROR_RENAMING_TEMP);
 
667
#endif /* VMS */
 
668
        if (TRACE)
 
669
            perror("renaming the file");
 
670
    }
 
671
 
 
672
 
 
673
failure:
 
674
    FREE(buf);
 
675
    HTAlert(BOOKMARK_DEL_FAILED);
 
676
    if (nfp)
 
677
        LYCloseTempFP(nfp);
 
678
    if (fp != NULL)
 
679
        LYCloseInput(fp);
 
680
    if (keep_tempfile) {
 
681
        HTUserMsg2(gettext("File may be recoverable from %s during this session"),
 
682
                   newfile);
 
683
    } else {
 
684
        LYRemoveTemp(newfile);
 
685
    }
 
686
}
 
687
 
 
688
/*
 
689
 *  Allows user to select sub-bookmarks files. - FMG & FM
 
690
 */
 
691
PUBLIC int select_multi_bookmarks NOARGS
 
692
{
 
693
    int c;
 
694
 
 
695
    /*
 
696
     *  If not enabled, pick the "default" (0).
 
697
     */
 
698
    if (LYMultiBookmarks == MBM_OFF || LYHaveSubBookmarks() == FALSE) {
 
699
        if (MBM_A_subbookmark[0]) /* If it exists! */
 
700
            return(0);
 
701
        else
 
702
            return(-1);
 
703
    }
 
704
 
 
705
    /*
 
706
     *  For ADVANCED users, we can just mess with the status line to save
 
707
     *  the 2 redraws of the screen, if LYMBMAdvnced is TRUE.  '=' will
 
708
     *  still show the screen and let them do it the "long" way.
 
709
     */
 
710
    if (LYMultiBookmarks == MBM_ADVANCED && user_mode == ADVANCED_MODE) {
 
711
        LYMBM_statusline(MULTIBOOKMARKS_SELECT);
 
712
get_advanced_choice:
 
713
        c = LYgetch();
 
714
#ifdef VMS
 
715
        if (HadVMSInterrupt) {
 
716
            HadVMSInterrupt = FALSE;
 
717
            c = LYCharINTERRUPT2;
 
718
        }
 
719
#endif /* VMS */
 
720
        if (LYisNonAlnumKeyname(c, LYK_PREV_DOC) || LYCharIsINTERRUPT_HARD(c)) {
 
721
            /*
 
722
             *  Treat left-arrow, ^G, or ^C as cancel.
 
723
             */
 
724
            return(-2);
 
725
        }
 
726
        if (LYisNonAlnumKeyname(c, LYK_REFRESH)) {
 
727
            /*
 
728
             *  Refresh the screen.
 
729
             */
 
730
            lynx_force_repaint();
 
731
            LYrefresh();
 
732
            goto get_advanced_choice;
 
733
        }
 
734
        if (LYisNonAlnumKeyname(c, LYK_ACTIVATE)) {
 
735
            /*
 
736
             *  Assume default bookmark file on ENTER or right-arrow.
 
737
             */
 
738
            return (MBM_A_subbookmark[0] ? 0 : -1);
 
739
        }
 
740
        switch (c) {
 
741
            case '=':
 
742
                /*
 
743
                 *  Get the choice via the menu.
 
744
                 */
 
745
                return(select_menu_multi_bookmarks());
 
746
 
 
747
            default:
 
748
                /*
 
749
                 *  Convert to an array index, act on it if valid.
 
750
                 *  Otherwise, get another keystroke.
 
751
                 */
 
752
                if ((c = LYMBM2index(c)) < 0) {
 
753
                    goto get_advanced_choice;
 
754
                }
 
755
        }
 
756
        /*
 
757
         *  See if we have a bookmark like that.
 
758
         */
 
759
        return (MBM_A_subbookmark[c] ? c : -1);
 
760
    } else {
 
761
        /*
 
762
         *  Get the choice via the menu.
 
763
         */
 
764
        return(select_menu_multi_bookmarks());
 
765
    }
 
766
}
 
767
 
 
768
/*
 
769
 *  Allows user to select sub-bookmarks files. - FMG & FM
 
770
 */
 
771
PUBLIC int select_menu_multi_bookmarks NOARGS
 
772
{
 
773
    int c, d, MBM_tmp_count, MBM_allow;
 
774
    int MBM_screens, MBM_from, MBM_to, MBM_current;
 
775
 
 
776
    /*
 
777
     *  If not enabled, pick the "default" (0).
 
778
     */
 
779
    if (LYMultiBookmarks == MBM_OFF)
 
780
        return(0);
 
781
 
 
782
    /*
 
783
     *  Filip M. Gieszczykiewicz (filipg@paranoia.com) & FM
 
784
     *  ---------------------------------------------------
 
785
     *  MBM_A_subbookmark[n] - Hold values of the respective
 
786
     *  "multi_bookmarkn" in the lynxrc file.
 
787
     *
 
788
     *  MBM_A_subdescript[n] - Hold description entries in the
 
789
     *  lynxrc file.
 
790
     *
 
791
     *  Note: MBM_A_subbookmark[0] is defined to be same value as
 
792
     *        "bookmark_file" in the lynxrc file and/or the startup
 
793
     *        "bookmark_page".
 
794
     *
 
795
     *  We make the display of bookmarks depend on rows we have
 
796
     *  available.
 
797
     *
 
798
     *  We load BookmarkPage with the valid MBM_A_subbookmark[n]
 
799
     *  via get_bookmark_filename().  Otherwise, that function
 
800
     *  returns a zero-length string to indicate a cancel, a
 
801
     *  single space to indicate an invalid choice, or NULL to
 
802
     *  indicate an inaccessible file.
 
803
     */
 
804
    MBM_allow=(LYlines-7);      /* We need 7 for header and footer */
 
805
    /*
 
806
     *  Screen big enough?
 
807
     */
 
808
    if (MBM_allow <= 0) {
 
809
        /*
 
810
         *  Too small.
 
811
         */
 
812
        HTAlert(MULTIBOOKMARKS_SMALL);
 
813
        return (-2);
 
814
    }
 
815
 
 
816
    MBM_screens = (MBM_V_MAXFILES/MBM_allow)+1; /* int rounds off low. */
 
817
 
 
818
    MBM_current = 1; /* Gotta start somewhere :-) */
 
819
 
 
820
    for (;;) {
 
821
        MBM_from = MBM_allow * MBM_current - MBM_allow;
 
822
        if (MBM_from < 0)
 
823
            MBM_from = 0; /* 0 is default bookmark... */
 
824
        if (MBM_current != 1)
 
825
            MBM_from++;
 
826
 
 
827
        MBM_to = (MBM_allow * MBM_current);
 
828
        if (MBM_to > MBM_V_MAXFILES)
 
829
            MBM_to = MBM_V_MAXFILES;
 
830
 
 
831
        /*
 
832
         *  Display menu of bookmarks.  NOTE that we avoid printw()'s
 
833
         *  to increase the chances that any non-ASCII or multibyte/CJK
 
834
         *  characters will be handled properly. - FM
 
835
         */
 
836
        LYclear();
 
837
        LYmove(1, 5);
 
838
        lynx_start_h1_color ();
 
839
        if (MBM_screens > 1) {
 
840
            char *shead_buffer = 0;
 
841
            HTSprintf0(&shead_buffer,
 
842
                    MULTIBOOKMARKS_SHEAD_MASK, MBM_current, MBM_screens);
 
843
            LYaddstr(shead_buffer);
 
844
            FREE(shead_buffer);
 
845
        } else {
 
846
            LYaddstr(MULTIBOOKMARKS_SHEAD);
 
847
        }
 
848
 
 
849
        lynx_stop_h1_color ();
 
850
 
 
851
        MBM_tmp_count = 0;
 
852
        for (c = MBM_from; c <= MBM_to; c++) {
 
853
            LYmove(3+MBM_tmp_count, 5);
 
854
            LYaddch(LYindex2MBM(c));
 
855
            LYaddstr(" : ");
 
856
            if (MBM_A_subdescript[c])
 
857
                LYaddstr(MBM_A_subdescript[c]);
 
858
            LYmove(3+MBM_tmp_count,36);
 
859
            LYaddch('(');
 
860
            if (MBM_A_subbookmark[c])
 
861
                LYaddstr(MBM_A_subbookmark[c]);
 
862
            LYaddch(')');
 
863
            MBM_tmp_count++;
 
864
        }
 
865
 
 
866
        /*
 
867
         *  Don't need to show it if it all fits on one screen!
 
868
         */
 
869
        if (MBM_screens > 1) {
 
870
            LYmove(LYlines-2, 0);
 
871
            LYaddstr("'");
 
872
            lynx_start_bold();
 
873
            LYaddstr("[");
 
874
            lynx_stop_bold();
 
875
            LYaddstr("' ");
 
876
            LYaddstr(PREVIOUS);
 
877
            LYaddstr(", '");
 
878
            lynx_start_bold();
 
879
            LYaddstr("]");
 
880
            lynx_stop_bold();
 
881
            LYaddstr("' ");
 
882
            LYaddstr(NEXT_SCREEN);
 
883
        }
 
884
 
 
885
        LYMBM_statusline(MULTIBOOKMARKS_SAVE);
 
886
 
 
887
        for (;;) {
 
888
            c = LYgetch();
 
889
#ifdef VMS
 
890
            if (HadVMSInterrupt) {
 
891
                HadVMSInterrupt = FALSE;
 
892
                c = 7;
 
893
            }
 
894
#endif /* VMS */
 
895
 
 
896
            if ((d = LYMBM2index(c)) >= 0) {
 
897
                /*
 
898
                 *  See if we have a bookmark like that.
 
899
                 */
 
900
                if (MBM_A_subbookmark[d] != NULL)
 
901
                    return(d);
 
902
 
 
903
                show_bookmark_not_defined();
 
904
                LYMBM_statusline(MULTIBOOKMARKS_SAVE);
 
905
            } else if (LYisNonAlnumKeyname(c, LYK_PREV_DOC) ||
 
906
                c == 7 || c == 3) {
 
907
                /*
 
908
                 *  Treat left-arrow, ^G, or ^C as cancel.
 
909
                 */
 
910
                return(-2);
 
911
            } else if (LYisNonAlnumKeyname(c, LYK_REFRESH)) {
 
912
                /*
 
913
                 *  Refresh the screen.
 
914
                 */
 
915
                lynx_force_repaint();
 
916
                LYrefresh();
 
917
            } else if (LYisNonAlnumKeyname(c, LYK_ACTIVATE)) {
 
918
                /*
 
919
                 *  Assume default bookmark file on ENTER or right-arrow.
 
920
                 */
 
921
                return(MBM_A_subbookmark[0] ? 0 : -1);
 
922
            } else if ((c == ']' ||  LYisNonAlnumKeyname(c, LYK_NEXT_PAGE)) &&
 
923
                MBM_screens > 1) {
 
924
                /*
 
925
                 *  Next range, if available.
 
926
                 */
 
927
                if (++MBM_current > MBM_screens)
 
928
                    MBM_current = 1;
 
929
                break;
 
930
            }
 
931
 
 
932
            else if ((c == '[' ||  LYisNonAlnumKeyname(c, LYK_PREV_PAGE)) &&
 
933
                MBM_screens > 1) {
 
934
                /*
 
935
                 *  Previous range, if available.
 
936
                 */
 
937
                if (--MBM_current <= 0)
 
938
                    MBM_current = MBM_screens;
 
939
                break;
 
940
            }
 
941
        }
 
942
    }
 
943
}
 
944
 
 
945
/*
 
946
 *  This function returns TRUE if we have sub-bookmarks defined.
 
947
 *  Otherwise (i.e., only the default bookmark file is defined),
 
948
 *  it returns FALSE. - FM
 
949
 */
 
950
PUBLIC BOOLEAN LYHaveSubBookmarks NOARGS
 
951
{
 
952
    int i;
 
953
 
 
954
    for (i = 1; i < MBM_V_MAXFILES; i++) {
 
955
        if (MBM_A_subbookmark[i] != NULL && *MBM_A_subbookmark[i] != '\0')
 
956
            return(TRUE);
 
957
    }
 
958
 
 
959
    return(FALSE);
 
960
}
 
961
 
 
962
/*
 
963
 *  This function passes a string to _statusline(), making
 
964
 *  sure it is at the bottom of the screen if LYMultiBookmarks
 
965
 *  is not MBM_OFF, otherwise, letting it go to the normal statusline
 
966
 *  position based on the current user mode.  We want to use
 
967
 *  _statusline() so that any multibyte/CJK characters in the
 
968
 *  string will be handled properly. - FM
 
969
 */
 
970
PUBLIC void LYMBM_statusline  ARGS1(
 
971
        char *,         text)
 
972
{
 
973
    if (LYMultiBookmarks != MBM_OFF && user_mode == NOVICE_MODE) {
 
974
        LYStatusLine = (LYlines - 1);
 
975
        _statusline(text);
 
976
        LYStatusLine = -1;
 
977
    } else {
 
978
        _statusline(text);
 
979
    }
 
980
}
 
981
 
 
982
/*
 
983
 * Check whether we have any visible (non-blank) chars.
 
984
 */
 
985
PRIVATE  BOOLEAN havevisible ARGS1(CONST char *, Title)
 
986
{
 
987
    CONST char *p = Title;
 
988
    unsigned char c;
 
989
    long unicode;
 
990
 
 
991
    for ( ; *p; p++) {
 
992
        c = UCH(TOASCII(*p));
 
993
        if (c > 32 && c < 127)
 
994
            return(TRUE);
 
995
        if (c <= 32 || c == 127)
 
996
            continue;
 
997
        if (LYHaveCJKCharacterSet || !UCCanUniTranslateFrom(current_char_set))
 
998
            return(TRUE);
 
999
        unicode = UCTransToUni(*p, current_char_set);
 
1000
        if (unicode > 32 && unicode < 127)
 
1001
            return(TRUE);
 
1002
        if (unicode <= 32 || unicode == 0xa0 || unicode == 0xad)
 
1003
            continue;
 
1004
        if (unicode >= 0x2000 && unicode < 0x200f)
 
1005
            continue;
 
1006
        return(TRUE);
 
1007
    }
 
1008
    return(FALSE); /* if we came here */
 
1009
}
 
1010
 
 
1011
/*
 
1012
 * Check whether string have 8 bit chars.
 
1013
 */
 
1014
PRIVATE  BOOLEAN have8bit ARGS1(CONST char *, Title)
 
1015
{
 
1016
    CONST char *p = Title;
 
1017
 
 
1018
    for ( ; *p; p++) {
 
1019
        if (UCH(*p) > 127)
 
1020
        return(TRUE);
 
1021
    }
 
1022
    return(FALSE); /* if we came here */
 
1023
}
 
1024
 
 
1025
/*
 
1026
 *  Ok, title have 8-bit characters and they are in display charset.
 
1027
 *  Bookmarks is a permanent file.  To avoid dependencies from display
 
1028
 *  character set which may be changed with time
 
1029
 *  we store 8-bit characters as numeric character reference (NCR),
 
1030
 *  so where the character encoded as unicode number in form of &#xUUUU;
 
1031
 *
 
1032
 *  To make bookmarks more readable for human (&#xUUUU certainly not)
 
1033
 *  we add a comment with '7-bit approximation' from the converted string.
 
1034
 *  This is a valid HTML and bookmarks code.
 
1035
 *
 
1036
 *  We do not want use META charset tag in bookmarks file:
 
1037
 *  it will never be changed later :-(
 
1038
 *  NCR's translation is part of I18N and HTML4.0
 
1039
 *  supported starting with Lynx 2.7.2,
 
1040
 *  Netscape 4.0 and MSIE 4.0.
 
1041
 *  Older versions fail.
 
1042
 *
 
1043
 */
 
1044
PRIVATE  char* title_convert8bit ARGS1(CONST char *, Title)
 
1045
{
 
1046
    CONST char *p = Title;
 
1047
    char *p0;
 
1048
    char *q;
 
1049
    char *comment = NULL;
 
1050
    char *ncr     = NULL;
 
1051
    char *buf = NULL;
 
1052
    int charset_in  = current_char_set;
 
1053
    int charset_out = UCGetLYhndl_byMIME("us-ascii");
 
1054
 
 
1055
    for ( ; *p; p++) {
 
1056
        char temp[2];
 
1057
        LYstrncpy(temp, p, sizeof(temp)-1);
 
1058
        if (UCH(*temp) <= 127) {
 
1059
            StrAllocCat(comment, temp);
 
1060
            StrAllocCat(ncr, temp);
 
1061
        } else {
 
1062
            long unicode;
 
1063
            char replace_buf [32];
 
1064
 
 
1065
            if (UCTransCharStr(replace_buf, sizeof(replace_buf), *temp,
 
1066
                                  charset_in, charset_out, YES) > 0)
 
1067
                StrAllocCat(comment, replace_buf);
 
1068
 
 
1069
            unicode = UCTransToUni( *temp, charset_in);
 
1070
 
 
1071
            StrAllocCat(ncr, "&#");
 
1072
            sprintf(replace_buf, "%ld", unicode);
 
1073
            StrAllocCat(ncr, replace_buf);
 
1074
            StrAllocCat(ncr, ";");
 
1075
        }
 
1076
    }
 
1077
 
 
1078
    /*
 
1079
     *  Cleanup comment, collapse multiple dashes into one dash,
 
1080
     *  skip '>'.
 
1081
     */
 
1082
    for (q = p0 = comment; *p0; p0++) {
 
1083
        if (UCH(TOASCII(*p0)) >= 32 &&
 
1084
            *p0 != '>' &&
 
1085
            (q == comment || *p0 != '-' || *(q-1) != '-')) {
 
1086
            *q++ = *p0;
 
1087
        }
 
1088
    }
 
1089
    *q = '\0';
 
1090
 
 
1091
    /*
 
1092
     * valid bookmark should be a single line (no linebreaks!).
 
1093
     */
 
1094
    StrAllocCat(buf, "<!-- ");
 
1095
    StrAllocCat(buf, comment);
 
1096
    StrAllocCat(buf, " -->");
 
1097
    StrAllocCat(buf, ncr);
 
1098
 
 
1099
    FREE(comment);
 
1100
    FREE(ncr);
 
1101
    return(buf);
 
1102
}
 
1103
 
 
1104
/*
 
1105
 * Since this is the "Default Bookmark File", we save it as a global, and as
 
1106
 * the first MBM_A_subbookmark entry.
 
1107
 */
 
1108
PUBLIC void set_default_bookmark_page ARGS1(
 
1109
        char *,         value)
 
1110
{
 
1111
    if (value != 0) {
 
1112
        if (bookmark_page == 0
 
1113
         || strcmp(bookmark_page, value)) {
 
1114
            StrAllocCopy(bookmark_page, value);
 
1115
        }
 
1116
        StrAllocCopy(BookmarkPage, bookmark_page);
 
1117
        StrAllocCopy(MBM_A_subbookmark[0], bookmark_page);
 
1118
        StrAllocCopy(MBM_A_subdescript[0], MULTIBOOKMARKS_DEFAULT);
 
1119
    }
 
1120
}