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

« back to all changes in this revision

Viewing changes to WWW/Library/Implementation/HTFormat.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
/*              Manage different file formats                   HTFormat.c
 
2
**              =============================
 
3
**
 
4
** Bugs:
 
5
**      Not reentrant.
 
6
**
 
7
**      Assumes the incoming stream is ASCII, rather than a local file
 
8
**      format, and so ALWAYS converts from ASCII on non-ASCII machines.
 
9
**      Therefore, non-ASCII machines can't read local files.
 
10
**
 
11
*/
 
12
 
 
13
#include <HTUtils.h>
 
14
 
 
15
/* Implements:
 
16
*/
 
17
#include <HTFormat.h>
 
18
 
 
19
PUBLIC float HTMaxSecs = 1e10;          /* No effective limit */
 
20
PUBLIC float HTMaxLength = 1e10;        /* No effective limit */
 
21
PUBLIC long int HTMaxBytes  = 0;        /* No effective limit */
 
22
 
 
23
#ifdef UNIX
 
24
#ifdef NeXT
 
25
#define PRESENT_POSTSCRIPT "open %s; /bin/rm -f %s\n"
 
26
#else
 
27
#define PRESENT_POSTSCRIPT "(ghostview %s ; /bin/rm -f %s)&\n"
 
28
                           /* Full pathname would be better! */
 
29
#endif /* NeXT */
 
30
#endif /* UNIX */
 
31
 
 
32
#include <HTML.h>
 
33
#include <HTMLDTD.h>
 
34
#include <HText.h>
 
35
#include <HTAlert.h>
 
36
#include <HTList.h>
 
37
#include <HTInit.h>
 
38
#include <HTTCP.h>
 
39
#include <HTTP.h>
 
40
/*      Streams and structured streams which we use:
 
41
*/
 
42
#include <HTFWriter.h>
 
43
#include <HTPlain.h>
 
44
#include <SGML.h>
 
45
#include <HTMLGen.h>
 
46
 
 
47
#include <LYexit.h>
 
48
#include <LYUtils.h>
 
49
#include <GridText.h>
 
50
#include <LYGlobalDefs.h>
 
51
#include <LYLeaks.h>
 
52
 
 
53
#ifdef DISP_PARTIAL
 
54
#include <LYMainLoop.h>
 
55
#endif
 
56
 
 
57
PUBLIC  BOOL HTOutputSource = NO;       /* Flag: shortcut parser to stdout */
 
58
 
 
59
#ifdef ORIGINAL
 
60
struct _HTStream {
 
61
      CONST HTStreamClass*      isa;
 
62
      /* ... */
 
63
};
 
64
#endif /* ORIGINAL */
 
65
 
 
66
/* this version used by the NetToText stream */
 
67
struct _HTStream {
 
68
        CONST HTStreamClass *   isa;
 
69
        BOOL                    had_cr;
 
70
        HTStream *              sink;
 
71
};
 
72
 
 
73
/*      Presentation methods
 
74
**      --------------------
 
75
*/
 
76
PUBLIC  HTList * HTPresentations = NULL;
 
77
PUBLIC  HTPresentation * default_presentation = NULL;
 
78
 
 
79
/*
 
80
 *      To free off the presentation list.
 
81
 */
 
82
#ifdef LY_FIND_LEAKS
 
83
PRIVATE void HTFreePresentations NOPARAMS;
 
84
#endif
 
85
 
 
86
/*      Define a presentation system command for a content-type
 
87
**      -------------------------------------------------------
 
88
*/
 
89
PUBLIC void HTSetPresentation ARGS6(
 
90
        CONST char *,   representation,
 
91
        CONST char *,   command,
 
92
        double,         quality,
 
93
        double,         secs,
 
94
        double,         secs_per_byte,
 
95
        long int,       maxbytes)
 
96
{
 
97
    HTPresentation * pres = typecalloc(HTPresentation);
 
98
    if (pres == NULL)
 
99
        outofmem(__FILE__, "HTSetPresentation");
 
100
 
 
101
    pres->rep = HTAtom_for(representation);
 
102
    pres->rep_out = WWW_PRESENT;                /* Fixed for now ... :-) */
 
103
    pres->converter = HTSaveAndExecute;         /* Fixed for now ...     */
 
104
    pres->quality = (float) quality;
 
105
    pres->secs = (float) secs;
 
106
    pres->secs_per_byte = (float) secs_per_byte;
 
107
    pres->maxbytes = maxbytes;
 
108
    pres->command = NULL;
 
109
    StrAllocCopy(pres->command, command);
 
110
 
 
111
    /*
 
112
     *  Memory leak fixed.
 
113
     *  05-28-94 Lynx 2-3-1 Garrett Arch Blythe
 
114
     */
 
115
    if (!HTPresentations)       {
 
116
        HTPresentations = HTList_new();
 
117
#ifdef LY_FIND_LEAKS
 
118
        atexit(HTFreePresentations);
 
119
#endif
 
120
    }
 
121
 
 
122
    if (strcmp(representation, "*")==0) {
 
123
        FREE(default_presentation);
 
124
        default_presentation = pres;
 
125
    } else {
 
126
        HTList_addObject(HTPresentations, pres);
 
127
    }
 
128
}
 
129
 
 
130
/*      Define a built-in function for a content-type
 
131
**      ---------------------------------------------
 
132
*/
 
133
PUBLIC void HTSetConversion ARGS7(
 
134
        CONST char *,   representation_in,
 
135
        CONST char *,   representation_out,
 
136
        HTConverter*,   converter,
 
137
        float,          quality,
 
138
        float,          secs,
 
139
        float,          secs_per_byte,
 
140
        long int,       maxbytes)
 
141
{
 
142
    HTPresentation * pres = typecalloc(HTPresentation);
 
143
    if (pres == NULL)
 
144
        outofmem(__FILE__, "HTSetConversion");
 
145
 
 
146
    pres->rep = HTAtom_for(representation_in);
 
147
    pres->rep_out = HTAtom_for(representation_out);
 
148
    pres->converter = converter;
 
149
    pres->command = NULL;               /* Fixed */
 
150
    pres->quality = quality;
 
151
    pres->secs = secs;
 
152
    pres->secs_per_byte = secs_per_byte;
 
153
    pres->maxbytes = maxbytes;
 
154
    pres->command = NULL;
 
155
 
 
156
    /*
 
157
     *  Memory Leak fixed.
 
158
     *  05-28-94 Lynx 2-3-1 Garrett Arch Blythe
 
159
     */
 
160
    if (!HTPresentations)       {
 
161
        HTPresentations = HTList_new();
 
162
#ifdef LY_FIND_LEAKS
 
163
        atexit(HTFreePresentations);
 
164
#endif
 
165
    }
 
166
 
 
167
    HTList_addObject(HTPresentations, pres);
 
168
}
 
169
 
 
170
#ifdef LY_FIND_LEAKS
 
171
/*
 
172
**      Purpose:        Free the presentation list.
 
173
**      Arguments:      void
 
174
**      Return Value:   void
 
175
**      Remarks/Portability/Dependencies/Restrictions:
 
176
**              Made to clean up Lynx's bad leakage.
 
177
**      Revision History:
 
178
**              05-28-94        created Lynx 2-3-1 Garrett Arch Blythe
 
179
*/
 
180
PRIVATE void HTFreePresentations NOARGS
 
181
{
 
182
    HTPresentation * pres = NULL;
 
183
 
 
184
    /*
 
185
     *  Loop through the list.
 
186
     */
 
187
    while (!HTList_isEmpty(HTPresentations)) {
 
188
        /*
 
189
         *  Free off each item.
 
190
         *  May also need to free off it's items, but not sure
 
191
         *  as of yet.
 
192
         */
 
193
        pres = (HTPresentation *)HTList_removeLastObject(HTPresentations);
 
194
        FREE(pres->command);
 
195
        FREE(pres);
 
196
    }
 
197
    /*
 
198
     *  Free the list itself.
 
199
     */
 
200
    HTList_delete(HTPresentations);
 
201
    HTPresentations = NULL;
 
202
}
 
203
#endif /* LY_FIND_LEAKS */
 
204
 
 
205
/*      File buffering
 
206
**      --------------
 
207
**
 
208
**      The input file is read using the macro which can read from
 
209
**      a socket or a file.
 
210
**      The input buffer size, if large will give greater efficiency and
 
211
**      release the server faster, and if small will save space on PCs etc.
 
212
*/
 
213
#define INPUT_BUFFER_SIZE 4096          /* Tradeoff */
 
214
PRIVATE char input_buffer[INPUT_BUFFER_SIZE];
 
215
PRIVATE char * input_pointer;
 
216
PRIVATE char * input_limit;
 
217
PRIVATE int input_file_number;
 
218
 
 
219
/*      Set up the buffering
 
220
**
 
221
**      These routines are public because they are in fact needed by
 
222
**      many parsers, and on PCs and Macs we should not duplicate
 
223
**      the static buffer area.
 
224
*/
 
225
PUBLIC void HTInitInput ARGS1 (int,file_number)
 
226
{
 
227
    input_file_number = file_number;
 
228
    input_pointer = input_limit = input_buffer;
 
229
}
 
230
 
 
231
PUBLIC int interrupted_in_htgetcharacter = 0;
 
232
PUBLIC int HTGetCharacter NOARGS
 
233
{
 
234
    char ch;
 
235
    interrupted_in_htgetcharacter = 0;
 
236
    do {
 
237
        if (input_pointer >= input_limit) {
 
238
            int status = NETREAD(input_file_number,
 
239
                                 input_buffer, INPUT_BUFFER_SIZE);
 
240
            if (status <= 0) {
 
241
                if (status == 0)
 
242
                    return EOF;
 
243
                if (status == HT_INTERRUPTED) {
 
244
                    CTRACE((tfp, "HTFormat: Interrupted in HTGetCharacter\n"));
 
245
                    interrupted_in_htgetcharacter = 1;
 
246
                    return EOF;
 
247
                }
 
248
                CTRACE((tfp, "HTFormat: File read error %d\n", status));
 
249
                return EOF; /* -1 is returned by UCX
 
250
                               at end of HTTP link */
 
251
            }
 
252
            input_pointer = input_buffer;
 
253
            input_limit = input_buffer + status;
 
254
        }
 
255
        ch = *input_pointer++;
 
256
    } while (ch == (char) 13); /* Ignore ASCII carriage return */
 
257
 
 
258
    return FROMASCII(UCH(ch));
 
259
}
 
260
 
 
261
#ifdef USE_SSL
 
262
PUBLIC char HTGetSSLCharacter ARGS1(void *, handle)
 
263
{
 
264
    char ch;
 
265
    interrupted_in_htgetcharacter = 0;
 
266
    if(!handle)
 
267
        return (char)EOF;
 
268
    do {
 
269
        if (input_pointer >= input_limit) {
 
270
            int status = SSL_read((SSL *)handle,
 
271
                                 input_buffer, INPUT_BUFFER_SIZE);
 
272
            if (status <= 0) {
 
273
                if (status == 0)
 
274
                    return (char)EOF;
 
275
                if (status == HT_INTERRUPTED) {
 
276
                    CTRACE((tfp, "HTFormat: Interrupted in HTGetSSLCharacter\n"));
 
277
                    interrupted_in_htgetcharacter = 1;
 
278
                    return (char)EOF;
 
279
                }
 
280
                CTRACE((tfp, "HTFormat: SSL_read error %d\n", status));
 
281
                return (char)EOF; /* -1 is returned by UCX
 
282
                                     at end of HTTP link */
 
283
            }
 
284
            input_pointer = input_buffer;
 
285
            input_limit = input_buffer + status;
 
286
        }
 
287
        ch = *input_pointer++;
 
288
    } while (ch == (char) 13); /* Ignore ASCII carriage return */
 
289
 
 
290
    return FROMASCII(ch);
 
291
}
 
292
#endif /* USE_SSL */
 
293
 
 
294
/*  Match maintype to any MIME type starting with maintype,
 
295
 *  for example:  image/gif should match image
 
296
 */
 
297
PRIVATE int half_match ARGS2(char *,trial_type, char *,target)
 
298
{
 
299
    char *cp = strchr(trial_type, '/');
 
300
 
 
301
    /* if no '/' or no '*' */
 
302
    if (!cp || *(cp+1) != '*')
 
303
        return 0;
 
304
 
 
305
    CTRACE((tfp, "HTFormat: comparing %s and %s for half match\n",
 
306
                trial_type, target));
 
307
 
 
308
        /* main type matches */
 
309
    if (!strncmp(trial_type, target, (cp-trial_type)-1))
 
310
        return 1;
 
311
 
 
312
    return 0;
 
313
}
 
314
 
 
315
#define WWW_WILDCARD_REP_OUT HTAtom_for("*")
 
316
 
 
317
/*              Look up a presentation
 
318
**              ----------------------
 
319
**
 
320
**      If fill_in is NULL, only look for an exact match.
 
321
**      If a wildcard match is made, *fill_in is used to store
 
322
**      a possibly modified presentation, and a pointer to it is
 
323
**      returned.  For an exact match, a pointer to the presentation
 
324
**      in the HTPresentations list is returned.  Returns NULL if
 
325
**      nothing found. - kw
 
326
**
 
327
*/
 
328
PRIVATE HTPresentation * HTFindPresentation ARGS3(
 
329
        HTFormat,               rep_in,
 
330
        HTFormat,               rep_out,
 
331
        HTPresentation*,        fill_in)
 
332
{
 
333
    HTAtom * wildcard = NULL; /* = HTAtom_for("*"); lookup when needed - kw */
 
334
 
 
335
    CTRACE((tfp, "HTFormat: Looking up presentation for %s to %s\n",
 
336
                HTAtom_name(rep_in), HTAtom_name(rep_out)));
 
337
 
 
338
    /* don't do anymore do it in the Lynx code at startup LJM */
 
339
    /* if (!HTPresentations) HTFormatInit(); */ /* set up the list */
 
340
 
 
341
    {
 
342
        int n = HTList_count(HTPresentations);
 
343
        int i;
 
344
        HTPresentation * pres, *match,
 
345
                        *strong_wildcard_match=0,
 
346
                        *weak_wildcard_match=0,
 
347
                        *last_default_match=0,
 
348
                        *strong_subtype_wildcard_match=0;
 
349
 
 
350
        for (i = 0; i < n; i++) {
 
351
            pres = (HTPresentation *)HTList_objectAt(HTPresentations, i);
 
352
            if (pres->rep == rep_in) {
 
353
                if (pres->rep_out == rep_out) {
 
354
                    CTRACE((tfp, "FindPresentation: found exact match: %s\n",
 
355
                                HTAtom_name(pres->rep)));
 
356
                    return pres;
 
357
 
 
358
                } else if (!fill_in) {
 
359
                    continue;
 
360
                } else {
 
361
                    if (!wildcard) wildcard = WWW_WILDCARD_REP_OUT;
 
362
                    if (pres->rep_out == wildcard) {
 
363
                        if (!strong_wildcard_match)
 
364
                            strong_wildcard_match = pres;
 
365
                        /* otherwise use the first one */
 
366
                        CTRACE((tfp, "StreamStack: found strong wildcard match: %s\n",
 
367
                                    HTAtom_name(pres->rep)));
 
368
                    }
 
369
                }
 
370
 
 
371
            } else if (!fill_in) {
 
372
                continue;
 
373
 
 
374
            } else if (half_match(HTAtom_name(pres->rep),
 
375
                                              HTAtom_name(rep_in))) {
 
376
                if (pres->rep_out == rep_out) {
 
377
                    if (!strong_subtype_wildcard_match)
 
378
                        strong_subtype_wildcard_match = pres;
 
379
                    /* otherwise use the first one */
 
380
                    CTRACE((tfp, "StreamStack: found strong subtype wildcard match: %s\n",
 
381
                                HTAtom_name(pres->rep)));
 
382
                }
 
383
            }
 
384
 
 
385
            if (pres->rep == WWW_SOURCE) {
 
386
                if (pres->rep_out == rep_out) {
 
387
                    if (!weak_wildcard_match)
 
388
                        weak_wildcard_match = pres;
 
389
                    /* otherwise use the first one */
 
390
                    CTRACE((tfp, "StreamStack: found weak wildcard match: %s\n",
 
391
                                HTAtom_name(pres->rep_out)));
 
392
 
 
393
                } else if (!last_default_match) {
 
394
                    if (!wildcard) wildcard = WWW_WILDCARD_REP_OUT;
 
395
                    if (pres->rep_out == wildcard)
 
396
                         last_default_match = pres;
 
397
                    /* otherwise use the first one */
 
398
                }
 
399
            }
 
400
        }
 
401
 
 
402
        match = strong_subtype_wildcard_match ? strong_subtype_wildcard_match :
 
403
                strong_wildcard_match ? strong_wildcard_match :
 
404
                weak_wildcard_match ? weak_wildcard_match :
 
405
                last_default_match;
 
406
 
 
407
        if (match) {
 
408
            *fill_in = *match;          /* Specific instance */
 
409
            fill_in->rep = rep_in;              /* yuk */
 
410
            fill_in->rep_out = rep_out; /* yuk */
 
411
            return fill_in;
 
412
        }
 
413
    }
 
414
 
 
415
    return NULL;
 
416
}
 
417
 
 
418
/*              Create a filter stack
 
419
**              ---------------------
 
420
**
 
421
**      If a wildcard match is made, a temporary HTPresentation
 
422
**      structure is made to hold the destination format while the
 
423
**      new stack is generated. This is just to pass the out format to
 
424
**      MIME so far.  Storing the format of a stream in the stream might
 
425
**      be a lot neater.
 
426
**
 
427
*/
 
428
PUBLIC HTStream * HTStreamStack ARGS4(
 
429
        HTFormat,               rep_in,
 
430
        HTFormat,               rep_out,
 
431
        HTStream*,              sink,
 
432
        HTParentAnchor*,        anchor)
 
433
{
 
434
    HTPresentation temp;
 
435
    HTPresentation *match;
 
436
    HTStream *result;
 
437
 
 
438
    CTRACE((tfp, "HTFormat: Constructing stream stack for %s to %s\n",
 
439
                HTAtom_name(rep_in), HTAtom_name(rep_out)));
 
440
 
 
441
    /* don't return on WWW_SOURCE some people might like
 
442
     * to make use of the source!!!!  LJM
 
443
     */
 
444
#if 0
 
445
    if (rep_out == WWW_SOURCE || rep_out == rep_in)
 
446
        return sink;    /*  LJM */
 
447
#endif
 
448
 
 
449
    if (rep_out == rep_in) {
 
450
        result = sink;
 
451
 
 
452
    } else if ((match = HTFindPresentation(rep_in, rep_out, &temp))) {
 
453
        if (match == &temp) {
 
454
            CTRACE((tfp, "StreamStack: Using %s\n", HTAtom_name(temp.rep_out)));
 
455
        } else {
 
456
            CTRACE((tfp, "StreamStack: found exact match: %s\n",
 
457
                        HTAtom_name(match->rep)));
 
458
        }
 
459
        result = (*match->converter)(match, anchor, sink);
 
460
    } else {
 
461
        result = NULL;
 
462
    }
 
463
    if (TRACE) {
 
464
        if (result && result->isa && result->isa->name) {
 
465
            CTRACE((tfp, "StreamStack: Returning \"%s\"\n", result->isa->name));
 
466
        } else if (result) {
 
467
            CTRACE((tfp, "StreamStack: Returning *unknown* stream!\n"));
 
468
        } else {
 
469
            CTRACE((tfp, "StreamStack: Returning NULL!\n"));
 
470
            CTRACE_FLUSH(tfp);  /* a crash may be imminent... - kw */
 
471
        }
 
472
    }
 
473
    return result;
 
474
}
 
475
 
 
476
/*              Put a presentation near start of list
 
477
**              -------------------------------------
 
478
**
 
479
**      Look up a presentation (exact match only) and, if found, reorder
 
480
**      it to the start of the HTPresentations list. - kw
 
481
*/
 
482
PUBLIC void HTReorderPresentation ARGS2(
 
483
        HTFormat,               rep_in,
 
484
        HTFormat,               rep_out)
 
485
{
 
486
    HTPresentation *match;
 
487
    if ((match = HTFindPresentation(rep_in, rep_out, NULL))) {
 
488
        HTList_removeObject(HTPresentations, match);
 
489
        HTList_addObject(HTPresentations, match);
 
490
    }
 
491
}
 
492
 
 
493
/*
 
494
 * Setup 'get_accept' flag to denote presentations that are not redundant,
 
495
 * and will be listed in "Accept:" header.
 
496
 */
 
497
PUBLIC void HTFilterPresentations NOARGS
 
498
{
 
499
    int i, j;
 
500
    int n = HTList_count(HTPresentations);
 
501
    HTPresentation *p, *q;
 
502
    BOOL matched;
 
503
    char *s, *t, *x, *y;
 
504
 
 
505
    for (i = 0; i < n; i++) {
 
506
        p = (HTPresentation *)HTList_objectAt(HTPresentations, i);
 
507
        s = HTAtom_name(p->rep);
 
508
 
 
509
        if (p->rep_out == WWW_PRESENT) {
 
510
            if (p->rep != WWW_SOURCE
 
511
             && strcasecomp(s, "www/mime")
 
512
             && strcasecomp(s, "www/compressed")
 
513
             && p->quality <= 1.0 && p->quality >= 0.0) {
 
514
                for (j = 0, matched = FALSE; j < i; j++) {
 
515
                    q = (HTPresentation *)HTList_objectAt(HTPresentations, j);
 
516
                    t = HTAtom_name(q->rep);
 
517
 
 
518
                    if (!strcasecomp(s, t)) {
 
519
                        matched = TRUE;
 
520
                        break;
 
521
                    }
 
522
                    if ((x = strchr(s, '/')) != 0
 
523
                     && (y = strchr(t, '/')) != 0) {
 
524
                        int len1 = x++ - s;
 
525
                        int len2 = y++ - t;
 
526
                        int lens = (len1 > len2) ? len1 : len2;
 
527
 
 
528
                        if ((*t == '*' || !strncasecomp(s, t, lens))
 
529
                         && (*y == '*' || !strcasecomp(x, y))) {
 
530
                            matched = TRUE;
 
531
                            break;
 
532
                        }
 
533
                    }
 
534
                }
 
535
                if (!matched)
 
536
                    p->get_accept = TRUE;
 
537
            }
 
538
        }
 
539
    }
 
540
}
 
541
 
 
542
/*              Find the cost of a filter stack
 
543
**              -------------------------------
 
544
**
 
545
**      Must return the cost of the same stack which StreamStack would set up.
 
546
**
 
547
** On entry,
 
548
**      length  The size of the data to be converted
 
549
*/
 
550
PUBLIC float HTStackValue ARGS4(
 
551
        HTFormat,               rep_in,
 
552
        HTFormat,               rep_out,
 
553
        float,                  initial_value,
 
554
        long int,               length)
 
555
{
 
556
    HTAtom * wildcard = WWW_WILDCARD_REP_OUT;
 
557
 
 
558
    CTRACE((tfp, "HTFormat: Evaluating stream stack for %s worth %.3f to %s\n",
 
559
                HTAtom_name(rep_in), initial_value, HTAtom_name(rep_out)));
 
560
 
 
561
    if (rep_out == WWW_SOURCE || rep_out == rep_in)
 
562
        return 0.0;
 
563
 
 
564
    /* don't do anymore do it in the Lynx code at startup LJM */
 
565
    /* if (!HTPresentations) HTFormatInit(); */ /* set up the list */
 
566
 
 
567
    {
 
568
        int n = HTList_count(HTPresentations);
 
569
        int i;
 
570
        HTPresentation * pres;
 
571
        for (i = 0; i < n; i++) {
 
572
            pres = (HTPresentation *)HTList_objectAt(HTPresentations, i);
 
573
            if (pres->rep == rep_in &&
 
574
                (pres->rep_out == rep_out || pres->rep_out == wildcard)) {
 
575
                float value = initial_value * pres->quality;
 
576
                if (HTMaxSecs != 0.0)
 
577
                    value = value - (length*pres->secs_per_byte + pres->secs)
 
578
                                         /HTMaxSecs;
 
579
                return value;
 
580
            }
 
581
        }
 
582
    }
 
583
 
 
584
    return (float) -1e30;       /* Really bad */
 
585
 
 
586
}
 
587
 
 
588
/*      Display the page while transfer in progress
 
589
**      -------------------------------------------
 
590
**
 
591
**   Repaint the page only when necessary.
 
592
**   This is a traverse call for HText_pageDisplay() - it works!.
 
593
**
 
594
*/
 
595
PUBLIC void HTDisplayPartial NOARGS
 
596
{
 
597
#ifdef DISP_PARTIAL
 
598
    if (display_partial) {
 
599
        /*
 
600
        **  HText_getNumOfLines() = "current" number of complete lines received
 
601
        **  NumOfLines_partial = number of lines at the moment of last repaint.
 
602
        **  (we update NumOfLines_partial only when we repaint the display.)
 
603
        **
 
604
        **  display_partial could only be enabled in HText_new()
 
605
        **  so a new HTMainText object available - all HText_ functions use it,
 
606
        **  lines counter HText_getNumOfLines() in particular.
 
607
        **
 
608
        **  Otherwise HTMainText holds info from the previous document
 
609
        **  and we may repaint it instead of the new one:
 
610
        **  prev doc scrolled to the first line (=Newline_partial)
 
611
        **  is not good looking :-)       23 Aug 1998 Leonid Pauzner
 
612
        **
 
613
        **  So repaint the page only when necessary:
 
614
        */
 
615
        int Newline_partial = LYGetNewline();
 
616
 
 
617
        if (((Newline_partial + display_lines) - 1 > NumOfLines_partial)
 
618
                /* current page not complete... */
 
619
        && (partial_threshold > 0 ?
 
620
                ((Newline_partial + partial_threshold) -1 <= HText_getNumOfLines()) :
 
621
                ((Newline_partial + display_lines) - 1 <= HText_getNumOfLines()))
 
622
                /*
 
623
                 * Originally we rendered by increments of 2 lines,
 
624
                 * but that got annoying on slow network connections.
 
625
                 * Then we switched to full-pages.  Now it's configurable.
 
626
                 * If partial_threshold <= 0, then it's a full page
 
627
                 */
 
628
        ) {
 
629
            if (LYMainLoop_pageDisplay(Newline_partial))
 
630
                NumOfLines_partial = HText_getNumOfLines();
 
631
        }
 
632
    }
 
633
#else /* nothing */
 
634
#endif  /* DISP_PARTIAL */
 
635
}
 
636
 
 
637
/* Put this as early as possible, OK just after HTDisplayPartial() */
 
638
PUBLIC void HTFinishDisplayPartial NOARGS
 
639
{
 
640
#ifdef DISP_PARTIAL
 
641
                    /*
 
642
                     *  End of incremental rendering stage here.
 
643
                     */
 
644
                    display_partial = FALSE;
 
645
#endif /* DISP_PARTIAL */
 
646
}
 
647
 
 
648
/*      Push data from a socket down a stream
 
649
**      -------------------------------------
 
650
**
 
651
**   This routine is responsible for creating and PRESENTING any
 
652
**   graphic (or other) objects described by the file.
 
653
**
 
654
**   The file number given is assumed to be a TELNET stream, i.e., containing
 
655
**   CRLF at the end of lines which need to be stripped to LF for unix
 
656
**   when the format is textual.
 
657
**
 
658
**  State of socket and target stream on entry:
 
659
**                      socket (file_number) assumed open,
 
660
**                      target (sink) assumed valid.
 
661
**
 
662
**  Return values:
 
663
**      HT_INTERRUPTED  Interruption or error after some data received.
 
664
**      -2              Unexpected disconnect before any data received.
 
665
**      -1              Interruption or error before any data received, or
 
666
**                      (UNIX) other read error before any data received, or
 
667
**                      download cancelled.
 
668
**      HT_LOADED       Normal close of socket (end of file indication
 
669
**                      received), or
 
670
**                      unexpected disconnect after some data received, or
 
671
**                      other read error after some data received, or
 
672
**                      (not UNIX) other read error before any data received.
 
673
**
 
674
**  State of socket and target stream on return depends on return value:
 
675
**      HT_INTERRUPTED  socket still open, target aborted.
 
676
**      -2              socket still open, target stream still valid.
 
677
**      -1              socket still open, target aborted.
 
678
**      otherwise       socket closed,  target stream still valid.
 
679
*/
 
680
PUBLIC int HTCopy ARGS4(
 
681
        HTParentAnchor *,       anchor,
 
682
        int,                    file_number,
 
683
        void*,                  handle GCC_UNUSED,
 
684
        HTStream*,              sink)
 
685
{
 
686
    HTStreamClass targetClass;
 
687
    BOOL suppress_readprogress = NO;
 
688
    int bytes;
 
689
    int rv = 0;
 
690
 
 
691
    /*  Push the data down the stream
 
692
    */
 
693
    targetClass = *(sink->isa); /* Copy pointers to procedures */
 
694
 
 
695
    /*  Push binary from socket down sink
 
696
    **
 
697
    **  This operation could be put into a main event loop
 
698
    */
 
699
    HTReadProgress(bytes = 0, 0);
 
700
    for (;;) {
 
701
        int status;
 
702
 
 
703
        if (LYCancelDownload) {
 
704
            LYCancelDownload = FALSE;
 
705
            (*targetClass._abort)(sink, NULL);
 
706
            rv = -1;
 
707
            goto finished;
 
708
        }
 
709
 
 
710
        if (HTCheckForInterrupt()) {
 
711
            _HTProgress (TRANSFER_INTERRUPTED);
 
712
            (*targetClass._abort)(sink, NULL);
 
713
            if (bytes)
 
714
                rv = HT_INTERRUPTED;
 
715
            else
 
716
                rv = -1;
 
717
            goto finished;
 
718
        }
 
719
 
 
720
#ifdef USE_SSL
 
721
        if (handle)
 
722
            status = SSL_read((SSL *)handle, input_buffer, INPUT_BUFFER_SIZE);
 
723
        else
 
724
            status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE);
 
725
#else
 
726
        status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE);
 
727
#endif /* USE_SSL */
 
728
 
 
729
        if (status <= 0) {
 
730
            if (status == 0) {
 
731
                break;
 
732
            } else if (status == HT_INTERRUPTED) {
 
733
                _HTProgress (TRANSFER_INTERRUPTED);
 
734
                (*targetClass._abort)(sink, NULL);
 
735
                if (bytes)
 
736
                    rv = HT_INTERRUPTED;
 
737
                else
 
738
                    rv = -1;
 
739
                goto finished;
 
740
            } else if (SOCKET_ERRNO == ENOTCONN ||
 
741
#ifdef _WINDOWS /* 1997/11/10 (Mon) 16:57:18 */
 
742
                       SOCKET_ERRNO == ETIMEDOUT ||
 
743
#endif
 
744
                       SOCKET_ERRNO == ECONNRESET ||
 
745
                       SOCKET_ERRNO == EPIPE) {
 
746
                /*
 
747
                 *  Arrrrgh, HTTP 0/1 compatibility problem, maybe.
 
748
                 */
 
749
                if (bytes <= 0) {
 
750
                    /*
 
751
                     *  Don't have any data, so let the calling
 
752
                     *  function decide what to do about it. - FM
 
753
                     */
 
754
                    rv = -2;
 
755
                    goto finished;
 
756
                } else {
 
757
#ifdef UNIX
 
758
                   /*
 
759
                    *  Treat what we've received already as the complete
 
760
                    *  transmission, but not without giving the user
 
761
                    *  an alert.  I don't know about all the different
 
762
                    *  TCP stacks for VMS etc., so this is currently
 
763
                    *  only for UNIX. - kw
 
764
                    */
 
765
                   HTInetStatus("NETREAD");
 
766
                   HTAlert("Unexpected server disconnect.");
 
767
                   CTRACE((tfp,
 
768
            "HTCopy: Unexpected server disconnect. Treating as completed.\n"));
 
769
                   status = 0;
 
770
                   break;
 
771
#else  /* !UNIX */
 
772
                   /*
 
773
                    *  Treat what we've gotten already
 
774
                    *  as the complete transmission. - FM
 
775
                    */
 
776
                   CTRACE((tfp,
 
777
            "HTCopy: Unexpected server disconnect.  Treating as completed.\n"));
 
778
                   status = 0;
 
779
                   break;
 
780
#endif /* UNIX */
 
781
                }
 
782
#ifdef UNIX
 
783
            } else {            /* status < 0 and other errno */
 
784
                /*
 
785
                 *  Treat what we've received already as the complete
 
786
                 *  transmission, but not without giving the user
 
787
                 *  an alert.  I don't know about all the different
 
788
                 *  TCP stacks for VMS etc., so this is currently
 
789
                 *  only for UNIX. - kw
 
790
                 */
 
791
                HTInetStatus("NETREAD");
 
792
                HTAlert("Unexpected read error.");
 
793
                if (bytes) {
 
794
                    (void)NETCLOSE(file_number);
 
795
                    rv = HT_LOADED;
 
796
                } else {
 
797
                    (*targetClass._abort)(sink, NULL);
 
798
                    rv = -1;
 
799
                }
 
800
                goto finished;
 
801
#endif
 
802
            }
 
803
            break;
 
804
        }
 
805
 
 
806
        /*
 
807
         *  Suppress ReadProgress messages when collecting a redirection
 
808
         *  message, at least initially (unless/until anchor->content_type
 
809
         *  gets changed, probably by the MIME message parser).  That way
 
810
         *  messages put up by the HTTP module or elsewhere can linger in
 
811
         *  the statusline for a while. - kw
 
812
         */
 
813
        suppress_readprogress = (anchor && anchor->content_type &&
 
814
                                 !strcmp(anchor->content_type,
 
815
                                         "message/x-http-redirection"));
 
816
#ifdef NOT_ASCII
 
817
        {
 
818
            char * p;
 
819
            for (p = input_buffer; p < input_buffer+status; p++) {
 
820
                *p = FROMASCII(*p);
 
821
            }
 
822
        }
 
823
#endif /* NOT_ASCII */
 
824
 
 
825
        (*targetClass.put_block)(sink, input_buffer, status);
 
826
        bytes += status;
 
827
        if (!suppress_readprogress)
 
828
            HTReadProgress(bytes, anchor ? anchor->content_length : 0);
 
829
        HTDisplayPartial();
 
830
 
 
831
    } /* next bufferload */
 
832
 
 
833
    _HTProgress(TRANSFER_COMPLETE);
 
834
    (void)NETCLOSE(file_number);
 
835
    rv = HT_LOADED;
 
836
 
 
837
finished:
 
838
    HTFinishDisplayPartial();
 
839
    return(rv);
 
840
}
 
841
 
 
842
/*      Push data from a file pointer down a stream
 
843
**      -------------------------------------
 
844
**
 
845
**   This routine is responsible for creating and PRESENTING any
 
846
**   graphic (or other) objects described by the file.
 
847
**
 
848
**
 
849
**  State of file and target stream on entry:
 
850
**                      FILE* (fp) assumed open,
 
851
**                      target (sink) assumed valid.
 
852
**
 
853
**  Return values:
 
854
**      HT_INTERRUPTED  Interruption after some data read.
 
855
**      HT_PARTIAL_CONTENT      Error after some data read.
 
856
**      -1              Error before any data read.
 
857
**      HT_LOADED       Normal end of file indication on reading.
 
858
**
 
859
**  State of file and target stream on return:
 
860
**      always          fp still open, target stream still valid.
 
861
*/
 
862
PUBLIC int HTFileCopy ARGS2(
 
863
        FILE *,                 fp,
 
864
        HTStream*,              sink)
 
865
{
 
866
    HTStreamClass targetClass;
 
867
    int status, bytes;
 
868
    int rv = HT_OK;
 
869
 
 
870
    /*  Push the data down the stream
 
871
    */
 
872
    targetClass = *(sink->isa); /* Copy pointers to procedures */
 
873
 
 
874
    /*  Push binary from socket down sink
 
875
    */
 
876
    HTReadProgress(bytes = 0, 0);
 
877
    for (;;) {
 
878
        status = fread(input_buffer, 1, INPUT_BUFFER_SIZE, fp);
 
879
        if (status == 0) { /* EOF or error */
 
880
            if (ferror(fp) == 0) {
 
881
                rv = HT_LOADED;
 
882
                break;
 
883
            }
 
884
            CTRACE((tfp, "HTFormat: Read error, read returns %d\n",
 
885
                        ferror(fp)));
 
886
            if (bytes) {
 
887
                rv = HT_PARTIAL_CONTENT;
 
888
            } else {
 
889
                rv = -1;
 
890
            }
 
891
            break;
 
892
        }
 
893
 
 
894
        (*targetClass.put_block)(sink, input_buffer, status);
 
895
        bytes += status;
 
896
        HTReadProgress(bytes, 0);
 
897
        /*  Suppress last screen update in partial mode - a regular update
 
898
         *  under control of mainloop() should follow anyway. - kw
 
899
         */
 
900
#ifdef DISP_PARTIAL
 
901
        if (display_partial && bytes != HTMainAnchor->content_length)
 
902
            HTDisplayPartial();
 
903
#endif
 
904
 
 
905
        if (HTCheckForInterrupt()) {
 
906
            _HTProgress (TRANSFER_INTERRUPTED);
 
907
            if (bytes) {
 
908
                rv = HT_INTERRUPTED;
 
909
            } else {
 
910
                rv = -1;
 
911
            }
 
912
            break;
 
913
        }
 
914
    } /* next bufferload */
 
915
 
 
916
    HTFinishDisplayPartial();
 
917
    return rv;
 
918
}
 
919
 
 
920
#ifdef USE_SOURCE_CACHE
 
921
/*      Push data from an HTChunk down a stream
 
922
**      ---------------------------------------
 
923
**
 
924
**   This routine is responsible for creating and PRESENTING any
 
925
**   graphic (or other) objects described by the file.
 
926
**
 
927
**  State of memory and target stream on entry:
 
928
**                      HTChunk* (chunk) and target (sink) assumed valid.
 
929
**
 
930
**  Return values:
 
931
**      HT_LOADED       All data sent.
 
932
**      HT_INTERRUPTED  Interruption after some data read.
 
933
**
 
934
**  State of memory and target stream on return:
 
935
**      always          chunk unchanged, target stream still valid.
 
936
*/
 
937
PUBLIC int HTMemCopy ARGS2(
 
938
        HTChunk *,              chunk,
 
939
        HTStream *,             sink)
 
940
{
 
941
    HTStreamClass targetClass;
 
942
    int bytes = 0;
 
943
    CONST char *data = chunk->data;
 
944
    int rv = HT_OK;
 
945
 
 
946
    targetClass = *(sink->isa);
 
947
    HTReadProgress(0, 0);
 
948
    for (;;) {
 
949
        /* Push the data down the stream a piece at a time, in case we're
 
950
        ** running a large document on a slow machine.
 
951
        */
 
952
        int n = INPUT_BUFFER_SIZE;
 
953
        if (n > chunk->size - bytes)
 
954
            n = chunk->size - bytes;
 
955
        if (n == 0)
 
956
            break;
 
957
        (*targetClass.put_block)(sink, data, n);
 
958
        bytes += n;
 
959
        data += n;
 
960
        HTReadProgress(bytes, 0);
 
961
        HTDisplayPartial();
 
962
 
 
963
        if (HTCheckForInterrupt()) {
 
964
            _HTProgress (TRANSFER_INTERRUPTED);
 
965
            if (bytes) {
 
966
                rv = HT_INTERRUPTED;
 
967
            } else {
 
968
                rv = -1;
 
969
            }
 
970
            break;
 
971
        }
 
972
    }
 
973
 
 
974
    HTFinishDisplayPartial();
 
975
    return rv;
 
976
}
 
977
#endif
 
978
 
 
979
#ifdef USE_ZLIB
 
980
/*      Push data from a gzip file pointer down a stream
 
981
**      -------------------------------------
 
982
**
 
983
**   This routine is responsible for creating and PRESENTING any
 
984
**   graphic (or other) objects described by the file.
 
985
**
 
986
**
 
987
**  State of file and target stream on entry:
 
988
**                    gzFile (gzfp) assumed open (should have gzipped content),
 
989
**                    target (sink) assumed valid.
 
990
**
 
991
**  Return values:
 
992
**      HT_INTERRUPTED  Interruption after some data read.
 
993
**      HT_PARTIAL_CONTENT      Error after some data read.
 
994
**      -1              Error before any data read.
 
995
**      HT_LOADED       Normal end of file indication on reading.
 
996
**
 
997
**  State of file and target stream on return:
 
998
**      always          gzfp still open, target stream still valid.
 
999
*/
 
1000
PRIVATE int HTGzFileCopy ARGS2(
 
1001
        gzFile,                 gzfp,
 
1002
        HTStream*,              sink)
 
1003
{
 
1004
    HTStreamClass targetClass;
 
1005
    int status, bytes;
 
1006
    int gzerrnum;
 
1007
    int rv = HT_OK;
 
1008
 
 
1009
    /*  Push the data down the stream
 
1010
    */
 
1011
    targetClass = *(sink->isa); /* Copy pointers to procedures */
 
1012
 
 
1013
    /*  read and inflate gzip'd file, and push binary down sink
 
1014
    */
 
1015
    HTReadProgress(bytes = 0, 0);
 
1016
    for (;;) {
 
1017
        status = gzread(gzfp, input_buffer, INPUT_BUFFER_SIZE);
 
1018
        if (status <= 0) { /* EOF or error */
 
1019
            if (status == 0) {
 
1020
                rv = HT_LOADED;
 
1021
                break;
 
1022
            }
 
1023
            CTRACE((tfp, "HTGzFileCopy: Read error, gzread returns %d\n",
 
1024
                        status));
 
1025
            CTRACE((tfp, "gzerror   : %s\n",
 
1026
                        gzerror(gzfp, &gzerrnum)));
 
1027
            if (TRACE) {
 
1028
                if (gzerrnum == Z_ERRNO)
 
1029
                    perror("gzerror   ");
 
1030
            }
 
1031
            if (bytes) {
 
1032
                rv = HT_PARTIAL_CONTENT;
 
1033
            } else {
 
1034
                rv = -1;
 
1035
            }
 
1036
            break;
 
1037
        }
 
1038
 
 
1039
        (*targetClass.put_block)(sink, input_buffer, status);
 
1040
        bytes += status;
 
1041
        HTReadProgress(bytes, -1);
 
1042
        HTDisplayPartial();
 
1043
 
 
1044
        if (HTCheckForInterrupt()) {
 
1045
            _HTProgress (TRANSFER_INTERRUPTED);
 
1046
            if (bytes) {
 
1047
                rv = HT_INTERRUPTED;
 
1048
            } else {
 
1049
                rv = -1;
 
1050
            }
 
1051
            break;
 
1052
        }
 
1053
    } /* next bufferload */
 
1054
 
 
1055
    HTFinishDisplayPartial();
 
1056
    return rv;
 
1057
}
 
1058
#endif /* USE_ZLIB */
 
1059
 
 
1060
#ifdef USE_BZLIB
 
1061
/*      Push data from a bzip file pointer down a stream
 
1062
**      -------------------------------------
 
1063
**
 
1064
**   This routine is responsible for creating and PRESENTING any
 
1065
**   graphic (or other) objects described by the file.
 
1066
**
 
1067
**
 
1068
**  State of file and target stream on entry:
 
1069
**                    BZFILE (bzfp) assumed open (should have bzipped content),
 
1070
**                    target (sink) assumed valid.
 
1071
**
 
1072
**  Return values:
 
1073
**      HT_INTERRUPTED  Interruption after some data read.
 
1074
**      HT_PARTIAL_CONTENT      Error after some data read.
 
1075
**      -1              Error before any data read.
 
1076
**      HT_LOADED       Normal end of file indication on reading.
 
1077
**
 
1078
**  State of file and target stream on return:
 
1079
**      always          bzfp still open, target stream still valid.
 
1080
*/
 
1081
PRIVATE int HTBzFileCopy ARGS2(
 
1082
        BZFILE *,               bzfp,
 
1083
        HTStream*,              sink)
 
1084
{
 
1085
    HTStreamClass targetClass;
 
1086
    int status, bytes;
 
1087
    int bzerrnum;
 
1088
    int rv = HT_OK;
 
1089
 
 
1090
    /*  Push the data down the stream
 
1091
    */
 
1092
    targetClass = *(sink->isa); /* Copy pointers to procedures */
 
1093
 
 
1094
    /*  read and inflate bzip'd file, and push binary down sink
 
1095
    */
 
1096
    HTReadProgress(bytes = 0, 0);
 
1097
    for (;;) {
 
1098
        status = BZ2_bzread(bzfp, input_buffer, INPUT_BUFFER_SIZE);
 
1099
        if (status <= 0) { /* EOF or error */
 
1100
            if (status == 0) {
 
1101
                rv = HT_LOADED;
 
1102
                break;
 
1103
            }
 
1104
            CTRACE((tfp, "HTBzFileCopy: Read error, bzread returns %d\n",
 
1105
                        status));
 
1106
            CTRACE((tfp, "bzerror   : %s\n",
 
1107
                        BZ2_bzerror(bzfp, &bzerrnum)));
 
1108
            if (bytes) {
 
1109
                rv = HT_PARTIAL_CONTENT;
 
1110
            } else {
 
1111
                rv = -1;
 
1112
            }
 
1113
            break;
 
1114
        }
 
1115
 
 
1116
        (*targetClass.put_block)(sink, input_buffer, status);
 
1117
        bytes += status;
 
1118
        HTReadProgress(bytes, -1);
 
1119
        HTDisplayPartial();
 
1120
 
 
1121
        if (HTCheckForInterrupt()) {
 
1122
            _HTProgress (TRANSFER_INTERRUPTED);
 
1123
            if (bytes) {
 
1124
                rv = HT_INTERRUPTED;
 
1125
            } else {
 
1126
                rv = -1;
 
1127
            }
 
1128
            break;
 
1129
        }
 
1130
    } /* next bufferload */
 
1131
 
 
1132
    HTFinishDisplayPartial();
 
1133
    return rv;
 
1134
}
 
1135
#endif /* USE_BZLIB */
 
1136
 
 
1137
/*      Push data from a socket down a stream STRIPPING CR
 
1138
**      --------------------------------------------------
 
1139
**
 
1140
**   This routine is responsible for creating and PRESENTING any
 
1141
**   graphic (or other) objects described by the socket.
 
1142
**
 
1143
**   The file number given is assumed to be a TELNET stream ie containing
 
1144
**   CRLF at the end of lines which need to be stripped to LF for unix
 
1145
**   when the format is textual.
 
1146
**
 
1147
*/
 
1148
PUBLIC void HTCopyNoCR ARGS3(
 
1149
        HTParentAnchor *,       anchor GCC_UNUSED,
 
1150
        int,                    file_number,
 
1151
        HTStream*,              sink)
 
1152
{
 
1153
    HTStreamClass targetClass;
 
1154
    int character;
 
1155
 
 
1156
    /*  Push the data, ignoring CRLF, down the stream
 
1157
    */
 
1158
    targetClass = *(sink->isa); /* Copy pointers to procedures */
 
1159
 
 
1160
    /*  Push text from telnet socket down sink
 
1161
    **
 
1162
    **  @@@@@ To push strings could be faster? (especially is we
 
1163
    **  cheat and don't ignore CR! :-}
 
1164
    */
 
1165
    HTInitInput(file_number);
 
1166
    for (;;) {
 
1167
        character = HTGetCharacter();
 
1168
        if (character == EOF)
 
1169
            break;
 
1170
        (*targetClass.put_character)(sink, UCH(character));
 
1171
    }
 
1172
}
 
1173
 
 
1174
/*      Parse a socket given format and file number
 
1175
**
 
1176
**   This routine is responsible for creating and PRESENTING any
 
1177
**   graphic (or other) objects described by the file.
 
1178
**
 
1179
**   The file number given is assumed to be a TELNET stream ie containing
 
1180
**   CRLF at the end of lines which need to be stripped to LF for unix
 
1181
**   when the format is textual.
 
1182
**
 
1183
**  State of socket and target stream on entry:
 
1184
**                      socket (file_number) assumed open,
 
1185
**                      target (sink) usually NULL (will call stream stack).
 
1186
**
 
1187
**  Return values:
 
1188
**      HT_INTERRUPTED  Interruption or error after some data received.
 
1189
**      -501            Stream stack failed (cannot present or convert).
 
1190
**      -2              Unexpected disconnect before any data received.
 
1191
**      -1              Stream stack failed (cannot present or convert), or
 
1192
**                      Interruption or error before any data received, or
 
1193
**                      (UNIX) other read error before any data received, or
 
1194
**                      download cancelled.
 
1195
**      HT_LOADED       Normal close of socket (end of file indication
 
1196
**                      received), or
 
1197
**                      unexpected disconnect after some data received, or
 
1198
**                      other read error after some data received, or
 
1199
**                      (not UNIX) other read error before any data received.
 
1200
**
 
1201
**  State of socket and target stream on return depends on return value:
 
1202
**      HT_INTERRUPTED  socket still open, target aborted.
 
1203
**      -501            socket still open, target stream NULL.
 
1204
**      -2              socket still open, target freed.
 
1205
**      -1              socket still open, target stream aborted or NULL.
 
1206
**      otherwise       socket closed,  target stream freed.
 
1207
*/
 
1208
PUBLIC int HTParseSocket ARGS5(
 
1209
        HTFormat,               rep_in,
 
1210
        HTFormat,               format_out,
 
1211
        HTParentAnchor *,       anchor,
 
1212
        int,                    file_number,
 
1213
        HTStream*,              sink)
 
1214
{
 
1215
    HTStream * stream;
 
1216
    HTStreamClass targetClass;
 
1217
    int rv;
 
1218
 
 
1219
    stream = HTStreamStack(rep_in, format_out, sink, anchor);
 
1220
 
 
1221
    if (!stream) {
 
1222
        char *buffer = 0;
 
1223
        if (LYCancelDownload) {
 
1224
            LYCancelDownload = FALSE;
 
1225
            return -1;
 
1226
        }
 
1227
        HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
 
1228
                HTAtom_name(rep_in), HTAtom_name(format_out));
 
1229
        CTRACE((tfp, "HTFormat: %s\n", buffer));
 
1230
        rv = HTLoadError(sink, 501, buffer); /* returns -501 */
 
1231
        FREE(buffer);
 
1232
    } else {
 
1233
        /*
 
1234
        ** Push the data, don't worry about CRLF we can strip them later.
 
1235
        */
 
1236
        targetClass = *(stream->isa);   /* Copy pointers to procedures */
 
1237
        rv = HTCopy(anchor, file_number, NULL, stream);
 
1238
        if (rv != -1 && rv != HT_INTERRUPTED)
 
1239
            (*targetClass._free)(stream);
 
1240
    }
 
1241
    return rv;
 
1242
    /* Originally:  full: HT_LOADED;  partial: HT_INTERRUPTED;  no bytes: -1 */
 
1243
}
 
1244
 
 
1245
/*      Parse a file given format and file pointer
 
1246
**
 
1247
**   This routine is responsible for creating and PRESENTING any
 
1248
**   graphic (or other) objects described by the file.
 
1249
**
 
1250
**   The file number given is assumed to be a TELNET stream ie containing
 
1251
**   CRLF at the end of lines which need to be stripped to \n for unix
 
1252
**   when the format is textual.
 
1253
**
 
1254
**  State of file and target stream on entry:
 
1255
**                      FILE* (fp) assumed open,
 
1256
**                      target (sink) usually NULL (will call stream stack).
 
1257
**
 
1258
**  Return values:
 
1259
**      -501            Stream stack failed (cannot present or convert).
 
1260
**      -1              Download cancelled.
 
1261
**      HT_NO_DATA      Error before any data read.
 
1262
**      HT_PARTIAL_CONTENT      Interruption or error after some data read.
 
1263
**      HT_LOADED       Normal end of file indication on reading.
 
1264
**
 
1265
**  State of file and target stream on return:
 
1266
**      always          fp still open; target freed, aborted, or NULL.
 
1267
*/
 
1268
PUBLIC int HTParseFile ARGS5(
 
1269
        HTFormat,               rep_in,
 
1270
        HTFormat,               format_out,
 
1271
        HTParentAnchor *,       anchor,
 
1272
        FILE *,                 fp,
 
1273
        HTStream*,              sink)
 
1274
{
 
1275
    HTStream * stream;
 
1276
    HTStreamClass targetClass;
 
1277
    int rv;
 
1278
 
 
1279
#ifdef SH_EX            /* 1998/01/04 (Sun) 16:04:09 */
 
1280
    if (fp == NULL)
 
1281
        return HT_LOADED;
 
1282
#endif
 
1283
 
 
1284
    stream = HTStreamStack(rep_in, format_out, sink, anchor);
 
1285
 
 
1286
    if (!stream) {
 
1287
        char *buffer = 0;
 
1288
        if (LYCancelDownload) {
 
1289
            LYCancelDownload = FALSE;
 
1290
            return -1;
 
1291
        }
 
1292
        HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
 
1293
                HTAtom_name(rep_in), HTAtom_name(format_out));
 
1294
        CTRACE((tfp, "HTFormat(in HTParseFile): %s\n", buffer));
 
1295
        rv = HTLoadError(sink, 501, buffer);
 
1296
        FREE(buffer);
 
1297
        return rv;
 
1298
    }
 
1299
 
 
1300
    /*  Push the data down the stream
 
1301
    **
 
1302
    **  @@  Bug:  This decision ought to be made based on "encoding"
 
1303
    **  rather than on content-type.  @@@  When we handle encoding.
 
1304
    **  The current method smells anyway.
 
1305
    */
 
1306
    targetClass = *(stream->isa);       /* Copy pointers to procedures */
 
1307
    rv = HTFileCopy(fp, stream);
 
1308
    if (rv == -1 || rv == HT_INTERRUPTED) {
 
1309
        (*targetClass._abort)(stream, NULL);
 
1310
    } else {
 
1311
        (*targetClass._free)(stream);
 
1312
    }
 
1313
 
 
1314
    if (rv == -1)
 
1315
        return HT_NO_DATA;
 
1316
    else if (rv == HT_INTERRUPTED || (rv > 0 && rv != HT_LOADED))
 
1317
        return HT_PARTIAL_CONTENT;
 
1318
    else
 
1319
        return HT_LOADED;
 
1320
}
 
1321
 
 
1322
#ifdef USE_SOURCE_CACHE
 
1323
/*      Parse a document in memory given format and memory block pointer
 
1324
**
 
1325
**   This routine is responsible for creating and PRESENTING any
 
1326
**   graphic (or other) objects described by the file.
 
1327
**
 
1328
**  State of memory and target stream on entry:
 
1329
**                      HTChunk* (chunk) assumed valid,
 
1330
**                      target (sink) usually NULL (will call stream stack).
 
1331
**
 
1332
**  Return values:
 
1333
**      -501            Stream stack failed (cannot present or convert).
 
1334
**      HT_LOADED       All data sent.
 
1335
**
 
1336
**  State of memory and target stream on return:
 
1337
**      always          chunk unchanged; target freed, aborted, or NULL.
 
1338
*/
 
1339
PUBLIC int HTParseMem ARGS5(
 
1340
        HTFormat,               rep_in,
 
1341
        HTFormat,               format_out,
 
1342
        HTParentAnchor *,       anchor,
 
1343
        HTChunk *,              chunk,
 
1344
        HTStream *,             sink)
 
1345
{
 
1346
    HTStream * stream;
 
1347
    HTStreamClass targetClass;
 
1348
    int rv;
 
1349
 
 
1350
    stream = HTStreamStack(rep_in, format_out, sink, anchor);
 
1351
    if (!stream) {
 
1352
        char *buffer = 0;
 
1353
        HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
 
1354
                   HTAtom_name(rep_in), HTAtom_name(format_out));
 
1355
        CTRACE((tfp, "HTFormat(in HTParseMem): %s\n", buffer));
 
1356
        rv = HTLoadError(sink, 501, buffer);
 
1357
        FREE(buffer);
 
1358
        return rv;
 
1359
    }
 
1360
 
 
1361
    /* Push the data down the stream
 
1362
    */
 
1363
    targetClass = *(stream->isa);
 
1364
    rv = HTMemCopy(chunk, stream);
 
1365
    (*targetClass._free)(stream);
 
1366
    return HT_LOADED;
 
1367
}
 
1368
#endif
 
1369
 
 
1370
#ifdef USE_ZLIB
 
1371
PRIVATE int HTCloseGzFile ARGS1(
 
1372
        gzFile,                 gzfp)
 
1373
{
 
1374
    int gzres;
 
1375
    if (gzfp == NULL)
 
1376
        return 0;
 
1377
    gzres = gzclose(gzfp);
 
1378
    if (TRACE) {
 
1379
        if (gzres == Z_ERRNO) {
 
1380
            perror("gzclose   ");
 
1381
        } else if (gzres != Z_OK) {
 
1382
            CTRACE((tfp, "gzclose   : error number %d\n", gzres));
 
1383
        }
 
1384
    }
 
1385
    return(gzres);
 
1386
}
 
1387
 
 
1388
/*      HTParseGzFile
 
1389
**
 
1390
**  State of file and target stream on entry:
 
1391
**                      gzFile (gzfp) assumed open,
 
1392
**                      target (sink) usually NULL (will call stream stack).
 
1393
**
 
1394
**  Return values:
 
1395
**      -501            Stream stack failed (cannot present or convert).
 
1396
**      -1              Download cancelled.
 
1397
**      HT_NO_DATA      Error before any data read.
 
1398
**      HT_PARTIAL_CONTENT      Interruption or error after some data read.
 
1399
**      HT_LOADED       Normal end of file indication on reading.
 
1400
**
 
1401
**  State of file and target stream on return:
 
1402
**      always          gzfp closed; target freed, aborted, or NULL.
 
1403
*/
 
1404
PUBLIC int HTParseGzFile ARGS5(
 
1405
        HTFormat,               rep_in,
 
1406
        HTFormat,               format_out,
 
1407
        HTParentAnchor *,       anchor,
 
1408
        gzFile,                 gzfp,
 
1409
        HTStream*,              sink)
 
1410
{
 
1411
    HTStream * stream;
 
1412
    HTStreamClass targetClass;
 
1413
    int rv;
 
1414
 
 
1415
    stream = HTStreamStack(rep_in, format_out, sink, anchor);
 
1416
 
 
1417
    if (!stream) {
 
1418
        char *buffer = 0;
 
1419
        HTCloseGzFile(gzfp);
 
1420
        if (LYCancelDownload) {
 
1421
            LYCancelDownload = FALSE;
 
1422
            return -1;
 
1423
        }
 
1424
        HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
 
1425
                HTAtom_name(rep_in), HTAtom_name(format_out));
 
1426
        CTRACE((tfp, "HTFormat(in HTParseGzFile): %s\n", buffer));
 
1427
        rv = HTLoadError(sink, 501, buffer);
 
1428
        FREE(buffer);
 
1429
        return rv;
 
1430
    }
 
1431
 
 
1432
    /*  Push the data down the stream
 
1433
    **
 
1434
    **  @@  Bug:  This decision ought to be made based on "encoding"
 
1435
    **  rather than on content-type.  @@@  When we handle encoding.
 
1436
    **  The current method smells anyway.
 
1437
    */
 
1438
    targetClass = *(stream->isa);       /* Copy pointers to procedures */
 
1439
    rv = HTGzFileCopy(gzfp, stream);
 
1440
    if (rv == -1 || rv == HT_INTERRUPTED) {
 
1441
        (*targetClass._abort)(stream, NULL);
 
1442
    } else {
 
1443
        (*targetClass._free)(stream);
 
1444
    }
 
1445
 
 
1446
    HTCloseGzFile(gzfp);
 
1447
    if (rv == -1)
 
1448
        return HT_NO_DATA;
 
1449
    else if (rv == HT_INTERRUPTED || (rv > 0 && rv != HT_LOADED))
 
1450
        return HT_PARTIAL_CONTENT;
 
1451
    else
 
1452
        return HT_LOADED;
 
1453
}
 
1454
#endif /* USE_ZLIB */
 
1455
 
 
1456
#ifdef USE_BZLIB
 
1457
PRIVATE void HTCloseBzFile ARGS1(
 
1458
        BZFILE *,               bzfp)
 
1459
{
 
1460
    if (bzfp)
 
1461
        BZ2_bzclose(bzfp);
 
1462
}
 
1463
 
 
1464
/*      HTParseBzFile
 
1465
**
 
1466
**  State of file and target stream on entry:
 
1467
**                      bzFile (bzfp) assumed open,
 
1468
**                      target (sink) usually NULL (will call stream stack).
 
1469
**
 
1470
**  Return values:
 
1471
**      -501            Stream stack failed (cannot present or convert).
 
1472
**      -1              Download cancelled.
 
1473
**      HT_NO_DATA      Error before any data read.
 
1474
**      HT_PARTIAL_CONTENT      Interruption or error after some data read.
 
1475
**      HT_LOADED       Normal end of file indication on reading.
 
1476
**
 
1477
**  State of file and target stream on return:
 
1478
**      always          bzfp closed; target freed, aborted, or NULL.
 
1479
*/
 
1480
PUBLIC int HTParseBzFile ARGS5(
 
1481
        HTFormat,               rep_in,
 
1482
        HTFormat,               format_out,
 
1483
        HTParentAnchor *,       anchor,
 
1484
        BZFILE*,                bzfp,
 
1485
        HTStream*,              sink)
 
1486
{
 
1487
    HTStream * stream;
 
1488
    HTStreamClass targetClass;
 
1489
    int rv;
 
1490
 
 
1491
    stream = HTStreamStack(rep_in, format_out, sink, anchor);
 
1492
 
 
1493
    if (!stream) {
 
1494
        char *buffer = 0;
 
1495
        HTCloseBzFile(bzfp);
 
1496
        if (LYCancelDownload) {
 
1497
            LYCancelDownload = FALSE;
 
1498
            return -1;
 
1499
        }
 
1500
        HTSprintf0(&buffer, CANNOT_CONVERT_I_TO_O,
 
1501
                HTAtom_name(rep_in), HTAtom_name(format_out));
 
1502
        CTRACE((tfp, "HTFormat(in HTParseBzFile): %s\n", buffer));
 
1503
        rv = HTLoadError(sink, 501, buffer);
 
1504
        FREE(buffer);
 
1505
        return rv;
 
1506
    }
 
1507
 
 
1508
    /*  Push the data down the stream
 
1509
    **
 
1510
    **  @@  Bug:  This decision ought to be made based on "encoding"
 
1511
    **  rather than on content-type.  @@@  When we handle encoding.
 
1512
    **  The current method smells anyway.
 
1513
    */
 
1514
    targetClass = *(stream->isa);       /* Copy pointers to procedures */
 
1515
    rv = HTBzFileCopy(bzfp, stream);
 
1516
    if (rv == -1 || rv == HT_INTERRUPTED) {
 
1517
        (*targetClass._abort)(stream, NULL);
 
1518
    } else {
 
1519
        (*targetClass._free)(stream);
 
1520
    }
 
1521
 
 
1522
    HTCloseBzFile(bzfp);
 
1523
    if (rv == -1)
 
1524
        return HT_NO_DATA;
 
1525
    else if (rv == HT_INTERRUPTED || (rv > 0 && rv != HT_LOADED))
 
1526
        return HT_PARTIAL_CONTENT;
 
1527
    else
 
1528
        return HT_LOADED;
 
1529
}
 
1530
#endif /* USE_BZLIB */
 
1531
 
 
1532
/*      Converter stream: Network Telnet to internal character text
 
1533
**      -----------------------------------------------------------
 
1534
**
 
1535
**      The input is assumed to be in ASCII, with lines delimited
 
1536
**      by (13,10) pairs, These pairs are converted into (CR,LF)
 
1537
**      pairs in the local representation.  The (CR,LF) sequence
 
1538
**      when found is changed to a '\n' character, the internal
 
1539
**      C representation of a new line.
 
1540
*/
 
1541
 
 
1542
PRIVATE void NetToText_put_character ARGS2(HTStream *, me, char, net_char)
 
1543
{
 
1544
    char c = FROMASCII(net_char);
 
1545
    if (me->had_cr) {
 
1546
        if (c == LF) {
 
1547
            me->sink->isa->put_character(me->sink, '\n');       /* Newline */
 
1548
            me->had_cr = NO;
 
1549
            return;
 
1550
        } else {
 
1551
            me->sink->isa->put_character(me->sink, CR); /* leftover */
 
1552
        }
 
1553
    }
 
1554
    me->had_cr = (BOOL) (c == CR);
 
1555
    if (!me->had_cr)
 
1556
        me->sink->isa->put_character(me->sink, c);              /* normal */
 
1557
}
 
1558
 
 
1559
PRIVATE void NetToText_put_string ARGS2(HTStream *, me, CONST char *, s)
 
1560
{
 
1561
    CONST char * p;
 
1562
 
 
1563
    for (p = s; *p; p++)
 
1564
        NetToText_put_character(me, *p);
 
1565
}
 
1566
 
 
1567
PRIVATE void NetToText_put_block ARGS3(HTStream *, me, CONST char*, s, int, l)
 
1568
{
 
1569
    CONST char * p;
 
1570
 
 
1571
    for (p = s; p < (s+l); p++)
 
1572
        NetToText_put_character(me, *p);
 
1573
}
 
1574
 
 
1575
PRIVATE void NetToText_free ARGS1(HTStream *, me)
 
1576
{
 
1577
    (me->sink->isa->_free)(me->sink);           /* Close rest of pipe */
 
1578
    FREE(me);
 
1579
}
 
1580
 
 
1581
PRIVATE void NetToText_abort ARGS2(HTStream *, me, HTError, e)
 
1582
{
 
1583
    me->sink->isa->_abort(me->sink,e);          /* Abort rest of pipe */
 
1584
    FREE(me);
 
1585
}
 
1586
 
 
1587
/*      The class structure
 
1588
*/
 
1589
PRIVATE HTStreamClass NetToTextClass = {
 
1590
    "NetToText",
 
1591
    NetToText_free,
 
1592
    NetToText_abort,
 
1593
    NetToText_put_character,
 
1594
    NetToText_put_string,
 
1595
    NetToText_put_block
 
1596
};
 
1597
 
 
1598
/*      The creation method
 
1599
*/
 
1600
PUBLIC HTStream * HTNetToText ARGS1(HTStream *, sink)
 
1601
{
 
1602
    HTStream* me = typecalloc(HTStream);
 
1603
 
 
1604
    if (me == NULL)
 
1605
        outofmem(__FILE__, "NetToText");
 
1606
    me->isa = &NetToTextClass;
 
1607
 
 
1608
    me->had_cr = NO;
 
1609
    me->sink = sink;
 
1610
    return me;
 
1611
}
 
1612
 
 
1613
PRIVATE HTStream        HTBaseStreamInstance;                 /* Made static */
 
1614
/*
 
1615
**      ERROR STREAM
 
1616
**      ------------
 
1617
**      There is only one error stream shared by anyone who wants a
 
1618
**      generic error returned from all stream methods.
 
1619
*/
 
1620
PRIVATE void HTErrorStream_put_character ARGS2(HTStream *, me GCC_UNUSED, char, c GCC_UNUSED)
 
1621
{
 
1622
    LYCancelDownload = TRUE;
 
1623
}
 
1624
 
 
1625
PRIVATE void HTErrorStream_put_string ARGS2(HTStream *, me GCC_UNUSED, CONST char *, s)
 
1626
{
 
1627
    if (s && *s)
 
1628
        LYCancelDownload = TRUE;
 
1629
}
 
1630
 
 
1631
PRIVATE void HTErrorStream_write ARGS3(HTStream *, me GCC_UNUSED, CONST char *, s, int, l)
 
1632
{
 
1633
    if (l && s)
 
1634
        LYCancelDownload = TRUE;
 
1635
}
 
1636
 
 
1637
PRIVATE void HTErrorStream_free ARGS1(HTStream *, me GCC_UNUSED)
 
1638
{
 
1639
    return;
 
1640
}
 
1641
 
 
1642
PRIVATE void HTErrorStream_abort ARGS2(HTStream *, me GCC_UNUSED, HTError, e GCC_UNUSED)
 
1643
{
 
1644
    return;
 
1645
}
 
1646
 
 
1647
PRIVATE CONST HTStreamClass HTErrorStreamClass =
 
1648
{
 
1649
    "ErrorStream",
 
1650
    HTErrorStream_free,
 
1651
    HTErrorStream_abort,
 
1652
    HTErrorStream_put_character,
 
1653
    HTErrorStream_put_string,
 
1654
    HTErrorStream_write
 
1655
};
 
1656
 
 
1657
PUBLIC HTStream * HTErrorStream NOARGS
 
1658
{
 
1659
    CTRACE((tfp, "ErrorStream. Created\n"));
 
1660
    HTBaseStreamInstance.isa = &HTErrorStreamClass;    /* The rest is random */
 
1661
    return &HTBaseStreamInstance;
 
1662
}