1
/* Manage different file formats HTFormat.c
2
** =============================
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.
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 */
25
#define PRESENT_POSTSCRIPT "open %s; /bin/rm -f %s\n"
27
#define PRESENT_POSTSCRIPT "(ghostview %s ; /bin/rm -f %s)&\n"
28
/* Full pathname would be better! */
40
/* Streams and structured streams which we use:
42
#include <HTFWriter.h>
50
#include <LYGlobalDefs.h>
54
#include <LYMainLoop.h>
57
PUBLIC BOOL HTOutputSource = NO; /* Flag: shortcut parser to stdout */
61
CONST HTStreamClass* isa;
66
/* this version used by the NetToText stream */
68
CONST HTStreamClass * isa;
73
/* Presentation methods
74
** --------------------
76
PUBLIC HTList * HTPresentations = NULL;
77
PUBLIC HTPresentation * default_presentation = NULL;
80
* To free off the presentation list.
83
PRIVATE void HTFreePresentations NOPARAMS;
86
/* Define a presentation system command for a content-type
87
** -------------------------------------------------------
89
PUBLIC void HTSetPresentation ARGS6(
90
CONST char *, representation,
91
CONST char *, command,
94
double, secs_per_byte,
97
HTPresentation * pres = typecalloc(HTPresentation);
99
outofmem(__FILE__, "HTSetPresentation");
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);
113
* 05-28-94 Lynx 2-3-1 Garrett Arch Blythe
115
if (!HTPresentations) {
116
HTPresentations = HTList_new();
118
atexit(HTFreePresentations);
122
if (strcmp(representation, "*")==0) {
123
FREE(default_presentation);
124
default_presentation = pres;
126
HTList_addObject(HTPresentations, pres);
130
/* Define a built-in function for a content-type
131
** ---------------------------------------------
133
PUBLIC void HTSetConversion ARGS7(
134
CONST char *, representation_in,
135
CONST char *, representation_out,
136
HTConverter*, converter,
139
float, secs_per_byte,
142
HTPresentation * pres = typecalloc(HTPresentation);
144
outofmem(__FILE__, "HTSetConversion");
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;
152
pres->secs_per_byte = secs_per_byte;
153
pres->maxbytes = maxbytes;
154
pres->command = NULL;
158
* 05-28-94 Lynx 2-3-1 Garrett Arch Blythe
160
if (!HTPresentations) {
161
HTPresentations = HTList_new();
163
atexit(HTFreePresentations);
167
HTList_addObject(HTPresentations, pres);
172
** Purpose: Free the presentation list.
174
** Return Value: void
175
** Remarks/Portability/Dependencies/Restrictions:
176
** Made to clean up Lynx's bad leakage.
178
** 05-28-94 created Lynx 2-3-1 Garrett Arch Blythe
180
PRIVATE void HTFreePresentations NOARGS
182
HTPresentation * pres = NULL;
185
* Loop through the list.
187
while (!HTList_isEmpty(HTPresentations)) {
189
* Free off each item.
190
* May also need to free off it's items, but not sure
193
pres = (HTPresentation *)HTList_removeLastObject(HTPresentations);
198
* Free the list itself.
200
HTList_delete(HTPresentations);
201
HTPresentations = NULL;
203
#endif /* LY_FIND_LEAKS */
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.
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;
219
/* Set up the buffering
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.
225
PUBLIC void HTInitInput ARGS1 (int,file_number)
227
input_file_number = file_number;
228
input_pointer = input_limit = input_buffer;
231
PUBLIC int interrupted_in_htgetcharacter = 0;
232
PUBLIC int HTGetCharacter NOARGS
235
interrupted_in_htgetcharacter = 0;
237
if (input_pointer >= input_limit) {
238
int status = NETREAD(input_file_number,
239
input_buffer, INPUT_BUFFER_SIZE);
243
if (status == HT_INTERRUPTED) {
244
CTRACE((tfp, "HTFormat: Interrupted in HTGetCharacter\n"));
245
interrupted_in_htgetcharacter = 1;
248
CTRACE((tfp, "HTFormat: File read error %d\n", status));
249
return EOF; /* -1 is returned by UCX
250
at end of HTTP link */
252
input_pointer = input_buffer;
253
input_limit = input_buffer + status;
255
ch = *input_pointer++;
256
} while (ch == (char) 13); /* Ignore ASCII carriage return */
258
return FROMASCII(UCH(ch));
262
PUBLIC char HTGetSSLCharacter ARGS1(void *, handle)
265
interrupted_in_htgetcharacter = 0;
269
if (input_pointer >= input_limit) {
270
int status = SSL_read((SSL *)handle,
271
input_buffer, INPUT_BUFFER_SIZE);
275
if (status == HT_INTERRUPTED) {
276
CTRACE((tfp, "HTFormat: Interrupted in HTGetSSLCharacter\n"));
277
interrupted_in_htgetcharacter = 1;
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 */
284
input_pointer = input_buffer;
285
input_limit = input_buffer + status;
287
ch = *input_pointer++;
288
} while (ch == (char) 13); /* Ignore ASCII carriage return */
290
return FROMASCII(ch);
294
/* Match maintype to any MIME type starting with maintype,
295
* for example: image/gif should match image
297
PRIVATE int half_match ARGS2(char *,trial_type, char *,target)
299
char *cp = strchr(trial_type, '/');
301
/* if no '/' or no '*' */
302
if (!cp || *(cp+1) != '*')
305
CTRACE((tfp, "HTFormat: comparing %s and %s for half match\n",
306
trial_type, target));
308
/* main type matches */
309
if (!strncmp(trial_type, target, (cp-trial_type)-1))
315
#define WWW_WILDCARD_REP_OUT HTAtom_for("*")
317
/* Look up a presentation
318
** ----------------------
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
328
PRIVATE HTPresentation * HTFindPresentation ARGS3(
331
HTPresentation*, fill_in)
333
HTAtom * wildcard = NULL; /* = HTAtom_for("*"); lookup when needed - kw */
335
CTRACE((tfp, "HTFormat: Looking up presentation for %s to %s\n",
336
HTAtom_name(rep_in), HTAtom_name(rep_out)));
338
/* don't do anymore do it in the Lynx code at startup LJM */
339
/* if (!HTPresentations) HTFormatInit(); */ /* set up the list */
342
int n = HTList_count(HTPresentations);
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;
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)));
358
} else if (!fill_in) {
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)));
371
} else if (!fill_in) {
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)));
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)));
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 */
402
match = strong_subtype_wildcard_match ? strong_subtype_wildcard_match :
403
strong_wildcard_match ? strong_wildcard_match :
404
weak_wildcard_match ? weak_wildcard_match :
408
*fill_in = *match; /* Specific instance */
409
fill_in->rep = rep_in; /* yuk */
410
fill_in->rep_out = rep_out; /* yuk */
418
/* Create a filter stack
419
** ---------------------
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
428
PUBLIC HTStream * HTStreamStack ARGS4(
432
HTParentAnchor*, anchor)
435
HTPresentation *match;
438
CTRACE((tfp, "HTFormat: Constructing stream stack for %s to %s\n",
439
HTAtom_name(rep_in), HTAtom_name(rep_out)));
441
/* don't return on WWW_SOURCE some people might like
442
* to make use of the source!!!! LJM
445
if (rep_out == WWW_SOURCE || rep_out == rep_in)
446
return sink; /* LJM */
449
if (rep_out == rep_in) {
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)));
456
CTRACE((tfp, "StreamStack: found exact match: %s\n",
457
HTAtom_name(match->rep)));
459
result = (*match->converter)(match, anchor, sink);
464
if (result && result->isa && result->isa->name) {
465
CTRACE((tfp, "StreamStack: Returning \"%s\"\n", result->isa->name));
467
CTRACE((tfp, "StreamStack: Returning *unknown* stream!\n"));
469
CTRACE((tfp, "StreamStack: Returning NULL!\n"));
470
CTRACE_FLUSH(tfp); /* a crash may be imminent... - kw */
476
/* Put a presentation near start of list
477
** -------------------------------------
479
** Look up a presentation (exact match only) and, if found, reorder
480
** it to the start of the HTPresentations list. - kw
482
PUBLIC void HTReorderPresentation ARGS2(
486
HTPresentation *match;
487
if ((match = HTFindPresentation(rep_in, rep_out, NULL))) {
488
HTList_removeObject(HTPresentations, match);
489
HTList_addObject(HTPresentations, match);
494
* Setup 'get_accept' flag to denote presentations that are not redundant,
495
* and will be listed in "Accept:" header.
497
PUBLIC void HTFilterPresentations NOARGS
500
int n = HTList_count(HTPresentations);
501
HTPresentation *p, *q;
505
for (i = 0; i < n; i++) {
506
p = (HTPresentation *)HTList_objectAt(HTPresentations, i);
507
s = HTAtom_name(p->rep);
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);
518
if (!strcasecomp(s, t)) {
522
if ((x = strchr(s, '/')) != 0
523
&& (y = strchr(t, '/')) != 0) {
526
int lens = (len1 > len2) ? len1 : len2;
528
if ((*t == '*' || !strncasecomp(s, t, lens))
529
&& (*y == '*' || !strcasecomp(x, y))) {
536
p->get_accept = TRUE;
542
/* Find the cost of a filter stack
543
** -------------------------------
545
** Must return the cost of the same stack which StreamStack would set up.
548
** length The size of the data to be converted
550
PUBLIC float HTStackValue ARGS4(
553
float, initial_value,
556
HTAtom * wildcard = WWW_WILDCARD_REP_OUT;
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)));
561
if (rep_out == WWW_SOURCE || rep_out == rep_in)
564
/* don't do anymore do it in the Lynx code at startup LJM */
565
/* if (!HTPresentations) HTFormatInit(); */ /* set up the list */
568
int n = HTList_count(HTPresentations);
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)
584
return (float) -1e30; /* Really bad */
588
/* Display the page while transfer in progress
589
** -------------------------------------------
591
** Repaint the page only when necessary.
592
** This is a traverse call for HText_pageDisplay() - it works!.
595
PUBLIC void HTDisplayPartial NOARGS
598
if (display_partial) {
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.)
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.
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
613
** So repaint the page only when necessary:
615
int Newline_partial = LYGetNewline();
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()))
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
629
if (LYMainLoop_pageDisplay(Newline_partial))
630
NumOfLines_partial = HText_getNumOfLines();
634
#endif /* DISP_PARTIAL */
637
/* Put this as early as possible, OK just after HTDisplayPartial() */
638
PUBLIC void HTFinishDisplayPartial NOARGS
642
* End of incremental rendering stage here.
644
display_partial = FALSE;
645
#endif /* DISP_PARTIAL */
648
/* Push data from a socket down a stream
649
** -------------------------------------
651
** This routine is responsible for creating and PRESENTING any
652
** graphic (or other) objects described by the file.
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.
658
** State of socket and target stream on entry:
659
** socket (file_number) assumed open,
660
** target (sink) assumed valid.
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
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.
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.
680
PUBLIC int HTCopy ARGS4(
681
HTParentAnchor *, anchor,
683
void*, handle GCC_UNUSED,
686
HTStreamClass targetClass;
687
BOOL suppress_readprogress = NO;
691
/* Push the data down the stream
693
targetClass = *(sink->isa); /* Copy pointers to procedures */
695
/* Push binary from socket down sink
697
** This operation could be put into a main event loop
699
HTReadProgress(bytes = 0, 0);
703
if (LYCancelDownload) {
704
LYCancelDownload = FALSE;
705
(*targetClass._abort)(sink, NULL);
710
if (HTCheckForInterrupt()) {
711
_HTProgress (TRANSFER_INTERRUPTED);
712
(*targetClass._abort)(sink, NULL);
722
status = SSL_read((SSL *)handle, input_buffer, INPUT_BUFFER_SIZE);
724
status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE);
726
status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE);
732
} else if (status == HT_INTERRUPTED) {
733
_HTProgress (TRANSFER_INTERRUPTED);
734
(*targetClass._abort)(sink, NULL);
740
} else if (SOCKET_ERRNO == ENOTCONN ||
741
#ifdef _WINDOWS /* 1997/11/10 (Mon) 16:57:18 */
742
SOCKET_ERRNO == ETIMEDOUT ||
744
SOCKET_ERRNO == ECONNRESET ||
745
SOCKET_ERRNO == EPIPE) {
747
* Arrrrgh, HTTP 0/1 compatibility problem, maybe.
751
* Don't have any data, so let the calling
752
* function decide what to do about it. - FM
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
765
HTInetStatus("NETREAD");
766
HTAlert("Unexpected server disconnect.");
768
"HTCopy: Unexpected server disconnect. Treating as completed.\n"));
773
* Treat what we've gotten already
774
* as the complete transmission. - FM
777
"HTCopy: Unexpected server disconnect. Treating as completed.\n"));
783
} else { /* status < 0 and other errno */
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
791
HTInetStatus("NETREAD");
792
HTAlert("Unexpected read error.");
794
(void)NETCLOSE(file_number);
797
(*targetClass._abort)(sink, NULL);
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
813
suppress_readprogress = (anchor && anchor->content_type &&
814
!strcmp(anchor->content_type,
815
"message/x-http-redirection"));
819
for (p = input_buffer; p < input_buffer+status; p++) {
823
#endif /* NOT_ASCII */
825
(*targetClass.put_block)(sink, input_buffer, status);
827
if (!suppress_readprogress)
828
HTReadProgress(bytes, anchor ? anchor->content_length : 0);
831
} /* next bufferload */
833
_HTProgress(TRANSFER_COMPLETE);
834
(void)NETCLOSE(file_number);
838
HTFinishDisplayPartial();
842
/* Push data from a file pointer down a stream
843
** -------------------------------------
845
** This routine is responsible for creating and PRESENTING any
846
** graphic (or other) objects described by the file.
849
** State of file and target stream on entry:
850
** FILE* (fp) assumed open,
851
** target (sink) assumed valid.
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.
859
** State of file and target stream on return:
860
** always fp still open, target stream still valid.
862
PUBLIC int HTFileCopy ARGS2(
866
HTStreamClass targetClass;
870
/* Push the data down the stream
872
targetClass = *(sink->isa); /* Copy pointers to procedures */
874
/* Push binary from socket down sink
876
HTReadProgress(bytes = 0, 0);
878
status = fread(input_buffer, 1, INPUT_BUFFER_SIZE, fp);
879
if (status == 0) { /* EOF or error */
880
if (ferror(fp) == 0) {
884
CTRACE((tfp, "HTFormat: Read error, read returns %d\n",
887
rv = HT_PARTIAL_CONTENT;
894
(*targetClass.put_block)(sink, input_buffer, 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
901
if (display_partial && bytes != HTMainAnchor->content_length)
905
if (HTCheckForInterrupt()) {
906
_HTProgress (TRANSFER_INTERRUPTED);
914
} /* next bufferload */
916
HTFinishDisplayPartial();
920
#ifdef USE_SOURCE_CACHE
921
/* Push data from an HTChunk down a stream
922
** ---------------------------------------
924
** This routine is responsible for creating and PRESENTING any
925
** graphic (or other) objects described by the file.
927
** State of memory and target stream on entry:
928
** HTChunk* (chunk) and target (sink) assumed valid.
931
** HT_LOADED All data sent.
932
** HT_INTERRUPTED Interruption after some data read.
934
** State of memory and target stream on return:
935
** always chunk unchanged, target stream still valid.
937
PUBLIC int HTMemCopy ARGS2(
941
HTStreamClass targetClass;
943
CONST char *data = chunk->data;
946
targetClass = *(sink->isa);
947
HTReadProgress(0, 0);
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.
952
int n = INPUT_BUFFER_SIZE;
953
if (n > chunk->size - bytes)
954
n = chunk->size - bytes;
957
(*targetClass.put_block)(sink, data, n);
960
HTReadProgress(bytes, 0);
963
if (HTCheckForInterrupt()) {
964
_HTProgress (TRANSFER_INTERRUPTED);
974
HTFinishDisplayPartial();
980
/* Push data from a gzip file pointer down a stream
981
** -------------------------------------
983
** This routine is responsible for creating and PRESENTING any
984
** graphic (or other) objects described by the file.
987
** State of file and target stream on entry:
988
** gzFile (gzfp) assumed open (should have gzipped content),
989
** target (sink) assumed valid.
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.
997
** State of file and target stream on return:
998
** always gzfp still open, target stream still valid.
1000
PRIVATE int HTGzFileCopy ARGS2(
1004
HTStreamClass targetClass;
1009
/* Push the data down the stream
1011
targetClass = *(sink->isa); /* Copy pointers to procedures */
1013
/* read and inflate gzip'd file, and push binary down sink
1015
HTReadProgress(bytes = 0, 0);
1017
status = gzread(gzfp, input_buffer, INPUT_BUFFER_SIZE);
1018
if (status <= 0) { /* EOF or error */
1023
CTRACE((tfp, "HTGzFileCopy: Read error, gzread returns %d\n",
1025
CTRACE((tfp, "gzerror : %s\n",
1026
gzerror(gzfp, &gzerrnum)));
1028
if (gzerrnum == Z_ERRNO)
1032
rv = HT_PARTIAL_CONTENT;
1039
(*targetClass.put_block)(sink, input_buffer, status);
1041
HTReadProgress(bytes, -1);
1044
if (HTCheckForInterrupt()) {
1045
_HTProgress (TRANSFER_INTERRUPTED);
1047
rv = HT_INTERRUPTED;
1053
} /* next bufferload */
1055
HTFinishDisplayPartial();
1058
#endif /* USE_ZLIB */
1061
/* Push data from a bzip file pointer down a stream
1062
** -------------------------------------
1064
** This routine is responsible for creating and PRESENTING any
1065
** graphic (or other) objects described by the file.
1068
** State of file and target stream on entry:
1069
** BZFILE (bzfp) assumed open (should have bzipped content),
1070
** target (sink) assumed valid.
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.
1078
** State of file and target stream on return:
1079
** always bzfp still open, target stream still valid.
1081
PRIVATE int HTBzFileCopy ARGS2(
1085
HTStreamClass targetClass;
1090
/* Push the data down the stream
1092
targetClass = *(sink->isa); /* Copy pointers to procedures */
1094
/* read and inflate bzip'd file, and push binary down sink
1096
HTReadProgress(bytes = 0, 0);
1098
status = BZ2_bzread(bzfp, input_buffer, INPUT_BUFFER_SIZE);
1099
if (status <= 0) { /* EOF or error */
1104
CTRACE((tfp, "HTBzFileCopy: Read error, bzread returns %d\n",
1106
CTRACE((tfp, "bzerror : %s\n",
1107
BZ2_bzerror(bzfp, &bzerrnum)));
1109
rv = HT_PARTIAL_CONTENT;
1116
(*targetClass.put_block)(sink, input_buffer, status);
1118
HTReadProgress(bytes, -1);
1121
if (HTCheckForInterrupt()) {
1122
_HTProgress (TRANSFER_INTERRUPTED);
1124
rv = HT_INTERRUPTED;
1130
} /* next bufferload */
1132
HTFinishDisplayPartial();
1135
#endif /* USE_BZLIB */
1137
/* Push data from a socket down a stream STRIPPING CR
1138
** --------------------------------------------------
1140
** This routine is responsible for creating and PRESENTING any
1141
** graphic (or other) objects described by the socket.
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.
1148
PUBLIC void HTCopyNoCR ARGS3(
1149
HTParentAnchor *, anchor GCC_UNUSED,
1153
HTStreamClass targetClass;
1156
/* Push the data, ignoring CRLF, down the stream
1158
targetClass = *(sink->isa); /* Copy pointers to procedures */
1160
/* Push text from telnet socket down sink
1162
** @@@@@ To push strings could be faster? (especially is we
1163
** cheat and don't ignore CR! :-}
1165
HTInitInput(file_number);
1167
character = HTGetCharacter();
1168
if (character == EOF)
1170
(*targetClass.put_character)(sink, UCH(character));
1174
/* Parse a socket given format and file number
1176
** This routine is responsible for creating and PRESENTING any
1177
** graphic (or other) objects described by the file.
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.
1183
** State of socket and target stream on entry:
1184
** socket (file_number) assumed open,
1185
** target (sink) usually NULL (will call stream stack).
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
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.
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.
1208
PUBLIC int HTParseSocket ARGS5(
1210
HTFormat, format_out,
1211
HTParentAnchor *, anchor,
1216
HTStreamClass targetClass;
1219
stream = HTStreamStack(rep_in, format_out, sink, anchor);
1223
if (LYCancelDownload) {
1224
LYCancelDownload = FALSE;
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 */
1234
** Push the data, don't worry about CRLF we can strip them later.
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);
1242
/* Originally: full: HT_LOADED; partial: HT_INTERRUPTED; no bytes: -1 */
1245
/* Parse a file given format and file pointer
1247
** This routine is responsible for creating and PRESENTING any
1248
** graphic (or other) objects described by the file.
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.
1254
** State of file and target stream on entry:
1255
** FILE* (fp) assumed open,
1256
** target (sink) usually NULL (will call stream stack).
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.
1265
** State of file and target stream on return:
1266
** always fp still open; target freed, aborted, or NULL.
1268
PUBLIC int HTParseFile ARGS5(
1270
HTFormat, format_out,
1271
HTParentAnchor *, anchor,
1276
HTStreamClass targetClass;
1279
#ifdef SH_EX /* 1998/01/04 (Sun) 16:04:09 */
1284
stream = HTStreamStack(rep_in, format_out, sink, anchor);
1288
if (LYCancelDownload) {
1289
LYCancelDownload = FALSE;
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);
1300
/* Push the data down the stream
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.
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);
1311
(*targetClass._free)(stream);
1316
else if (rv == HT_INTERRUPTED || (rv > 0 && rv != HT_LOADED))
1317
return HT_PARTIAL_CONTENT;
1322
#ifdef USE_SOURCE_CACHE
1323
/* Parse a document in memory given format and memory block pointer
1325
** This routine is responsible for creating and PRESENTING any
1326
** graphic (or other) objects described by the file.
1328
** State of memory and target stream on entry:
1329
** HTChunk* (chunk) assumed valid,
1330
** target (sink) usually NULL (will call stream stack).
1333
** -501 Stream stack failed (cannot present or convert).
1334
** HT_LOADED All data sent.
1336
** State of memory and target stream on return:
1337
** always chunk unchanged; target freed, aborted, or NULL.
1339
PUBLIC int HTParseMem ARGS5(
1341
HTFormat, format_out,
1342
HTParentAnchor *, anchor,
1347
HTStreamClass targetClass;
1350
stream = HTStreamStack(rep_in, format_out, sink, anchor);
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);
1361
/* Push the data down the stream
1363
targetClass = *(stream->isa);
1364
rv = HTMemCopy(chunk, stream);
1365
(*targetClass._free)(stream);
1371
PRIVATE int HTCloseGzFile ARGS1(
1377
gzres = gzclose(gzfp);
1379
if (gzres == Z_ERRNO) {
1381
} else if (gzres != Z_OK) {
1382
CTRACE((tfp, "gzclose : error number %d\n", gzres));
1390
** State of file and target stream on entry:
1391
** gzFile (gzfp) assumed open,
1392
** target (sink) usually NULL (will call stream stack).
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.
1401
** State of file and target stream on return:
1402
** always gzfp closed; target freed, aborted, or NULL.
1404
PUBLIC int HTParseGzFile ARGS5(
1406
HTFormat, format_out,
1407
HTParentAnchor *, anchor,
1412
HTStreamClass targetClass;
1415
stream = HTStreamStack(rep_in, format_out, sink, anchor);
1419
HTCloseGzFile(gzfp);
1420
if (LYCancelDownload) {
1421
LYCancelDownload = FALSE;
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);
1432
/* Push the data down the stream
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.
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);
1443
(*targetClass._free)(stream);
1446
HTCloseGzFile(gzfp);
1449
else if (rv == HT_INTERRUPTED || (rv > 0 && rv != HT_LOADED))
1450
return HT_PARTIAL_CONTENT;
1454
#endif /* USE_ZLIB */
1457
PRIVATE void HTCloseBzFile ARGS1(
1466
** State of file and target stream on entry:
1467
** bzFile (bzfp) assumed open,
1468
** target (sink) usually NULL (will call stream stack).
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.
1477
** State of file and target stream on return:
1478
** always bzfp closed; target freed, aborted, or NULL.
1480
PUBLIC int HTParseBzFile ARGS5(
1482
HTFormat, format_out,
1483
HTParentAnchor *, anchor,
1488
HTStreamClass targetClass;
1491
stream = HTStreamStack(rep_in, format_out, sink, anchor);
1495
HTCloseBzFile(bzfp);
1496
if (LYCancelDownload) {
1497
LYCancelDownload = FALSE;
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);
1508
/* Push the data down the stream
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.
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);
1519
(*targetClass._free)(stream);
1522
HTCloseBzFile(bzfp);
1525
else if (rv == HT_INTERRUPTED || (rv > 0 && rv != HT_LOADED))
1526
return HT_PARTIAL_CONTENT;
1530
#endif /* USE_BZLIB */
1532
/* Converter stream: Network Telnet to internal character text
1533
** -----------------------------------------------------------
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.
1542
PRIVATE void NetToText_put_character ARGS2(HTStream *, me, char, net_char)
1544
char c = FROMASCII(net_char);
1547
me->sink->isa->put_character(me->sink, '\n'); /* Newline */
1551
me->sink->isa->put_character(me->sink, CR); /* leftover */
1554
me->had_cr = (BOOL) (c == CR);
1556
me->sink->isa->put_character(me->sink, c); /* normal */
1559
PRIVATE void NetToText_put_string ARGS2(HTStream *, me, CONST char *, s)
1563
for (p = s; *p; p++)
1564
NetToText_put_character(me, *p);
1567
PRIVATE void NetToText_put_block ARGS3(HTStream *, me, CONST char*, s, int, l)
1571
for (p = s; p < (s+l); p++)
1572
NetToText_put_character(me, *p);
1575
PRIVATE void NetToText_free ARGS1(HTStream *, me)
1577
(me->sink->isa->_free)(me->sink); /* Close rest of pipe */
1581
PRIVATE void NetToText_abort ARGS2(HTStream *, me, HTError, e)
1583
me->sink->isa->_abort(me->sink,e); /* Abort rest of pipe */
1587
/* The class structure
1589
PRIVATE HTStreamClass NetToTextClass = {
1593
NetToText_put_character,
1594
NetToText_put_string,
1598
/* The creation method
1600
PUBLIC HTStream * HTNetToText ARGS1(HTStream *, sink)
1602
HTStream* me = typecalloc(HTStream);
1605
outofmem(__FILE__, "NetToText");
1606
me->isa = &NetToTextClass;
1613
PRIVATE HTStream HTBaseStreamInstance; /* Made static */
1617
** There is only one error stream shared by anyone who wants a
1618
** generic error returned from all stream methods.
1620
PRIVATE void HTErrorStream_put_character ARGS2(HTStream *, me GCC_UNUSED, char, c GCC_UNUSED)
1622
LYCancelDownload = TRUE;
1625
PRIVATE void HTErrorStream_put_string ARGS2(HTStream *, me GCC_UNUSED, CONST char *, s)
1628
LYCancelDownload = TRUE;
1631
PRIVATE void HTErrorStream_write ARGS3(HTStream *, me GCC_UNUSED, CONST char *, s, int, l)
1634
LYCancelDownload = TRUE;
1637
PRIVATE void HTErrorStream_free ARGS1(HTStream *, me GCC_UNUSED)
1642
PRIVATE void HTErrorStream_abort ARGS2(HTStream *, me GCC_UNUSED, HTError, e GCC_UNUSED)
1647
PRIVATE CONST HTStreamClass HTErrorStreamClass =
1651
HTErrorStream_abort,
1652
HTErrorStream_put_character,
1653
HTErrorStream_put_string,
1657
PUBLIC HTStream * HTErrorStream NOARGS
1659
CTRACE((tfp, "ErrorStream. Created\n"));
1660
HTBaseStreamInstance.isa = &HTErrorStreamClass; /* The rest is random */
1661
return &HTBaseStreamInstance;