/* Manage different file formats HTFormat.c ** ============================= ** ** Bugs: ** Not reentrant. ** ** Assumes the incoming stream is ASCII, rather than a local file ** format, and so ALWAYS converts from ASCII on non-ASCII machines. ** Therefore, non-ASCII machines can't read local files. ** */ #include /* Implements: */ #include PUBLIC float HTMaxSecs = 1e10; /* No effective limit */ PUBLIC float HTMaxLength = 1e10; /* No effective limit */ PUBLIC long int HTMaxBytes = 0; /* No effective limit */ #ifdef unix #ifdef NeXT #define PRESENT_POSTSCRIPT "open %s; /bin/rm -f %s\n" #else #define PRESENT_POSTSCRIPT "(ghostview %s ; /bin/rm -f %s)&\n" /* Full pathname would be better! */ #endif /* NeXT */ #endif /* unix */ #include #include #include #include #include #include #include /* Streams and structured streams which we use: */ #include #include #include #include #include #include #include #include #include #include PUBLIC BOOL HTOutputSource = NO; /* Flag: shortcut parser to stdout */ /* extern BOOL interactive; LJM */ #ifdef ORIGINAL struct _HTStream { CONST HTStreamClass* isa; /* ... */ }; #endif /* ORIGINAL */ /* this version used by the NetToText stream */ struct _HTStream { CONST HTStreamClass * isa; BOOL had_cr; HTStream * sink; }; /* Presentation methods ** -------------------- */ PUBLIC HTList * HTPresentations = NULL; PUBLIC HTPresentation * default_presentation = NULL; /* * To free off the presentation list. */ PRIVATE void HTFreePresentations NOPARAMS; /* Define a presentation system command for a content-type ** ------------------------------------------------------- */ PUBLIC void HTSetPresentation ARGS6( CONST char *, representation, CONST char *, command, float, quality, float, secs, float, secs_per_byte, long int, maxbytes) { HTPresentation * pres = (HTPresentation *)malloc(sizeof(HTPresentation)); if (pres == NULL) outofmem(__FILE__, "HTSetPresentation"); pres->rep = HTAtom_for(representation); pres->rep_out = WWW_PRESENT; /* Fixed for now ... :-) */ pres->converter = HTSaveAndExecute; /* Fixed for now ... */ pres->quality = quality; pres->secs = secs; pres->secs_per_byte = secs_per_byte; pres->maxbytes = maxbytes; pres->command = NULL; StrAllocCopy(pres->command, command); /* * Memory leak fixed. * 05-28-94 Lynx 2-3-1 Garrett Arch Blythe */ if (!HTPresentations) { HTPresentations = HTList_new(); atexit(HTFreePresentations); } if (strcmp(representation, "*")==0) { FREE(default_presentation); default_presentation = pres; } else { HTList_addObject(HTPresentations, pres); } } /* Define a built-in function for a content-type ** --------------------------------------------- */ PUBLIC void HTSetConversion ARGS7( CONST char *, representation_in, CONST char *, representation_out, HTConverter*, converter, float, quality, float, secs, float, secs_per_byte, long int, maxbytes) { HTPresentation * pres = (HTPresentation *)malloc(sizeof(HTPresentation)); if (pres == NULL) outofmem(__FILE__, "HTSetConversion"); pres->rep = HTAtom_for(representation_in); pres->rep_out = HTAtom_for(representation_out); pres->converter = converter; pres->command = NULL; /* Fixed */ pres->quality = quality; pres->secs = secs; pres->secs_per_byte = secs_per_byte; pres->maxbytes = maxbytes; pres->command = NULL; /* * Memory Leak fixed. * 05-28-94 Lynx 2-3-1 Garrett Arch Blythe */ if (!HTPresentations) { HTPresentations = HTList_new(); atexit(HTFreePresentations); } HTList_addObject(HTPresentations, pres); } /* ** Purpose: Free the presentation list. ** Arguments: void ** Return Value: void ** Remarks/Portability/Dependencies/Restrictions: ** Made to clean up Lynx's bad leakage. ** Revision History: ** 05-28-94 created Lynx 2-3-1 Garrett Arch Blythe */ PRIVATE void HTFreePresentations NOARGS { HTPresentation * pres = NULL; /* * Loop through the list. */ while (!HTList_isEmpty(HTPresentations)) { /* * Free off each item. * May also need to free off it's items, but not sure * as of yet. */ pres = (HTPresentation *)HTList_removeLastObject(HTPresentations); FREE(pres->command); FREE(pres); } /* * Free the list itself. */ HTList_delete(HTPresentations); HTPresentations = NULL; } /* File buffering ** -------------- ** ** The input file is read using the macro which can read from ** a socket or a file. ** The input buffer size, if large will give greater efficiency and ** release the server faster, and if small will save space on PCs etc. */ #define INPUT_BUFFER_SIZE 4096 /* Tradeoff */ PRIVATE char input_buffer[INPUT_BUFFER_SIZE]; PRIVATE char * input_pointer; PRIVATE char * input_limit; PRIVATE int input_file_number; /* Set up the buffering ** ** These routines are public because they are in fact needed by ** many parsers, and on PCs and Macs we should not duplicate ** the static buffer area. */ PUBLIC void HTInitInput ARGS1 (int,file_number) { input_file_number = file_number; input_pointer = input_limit = input_buffer; } PUBLIC int interrupted_in_htgetcharacter = 0; PUBLIC char HTGetCharacter NOARGS { char ch; interrupted_in_htgetcharacter = 0; do { if (input_pointer >= input_limit) { int status = NETREAD(input_file_number, input_buffer, INPUT_BUFFER_SIZE); if (status <= 0) { if (status == 0) return (char)EOF; if (status == HT_INTERRUPTED) { CTRACE(tfp, "HTFormat: Interrupted in HTGetCharacter\n"); interrupted_in_htgetcharacter = 1; return (char)EOF; } CTRACE(tfp, "HTFormat: File read error %d\n", status); return (char)EOF; /* -1 is returned by UCX at end of HTTP link */ } input_pointer = input_buffer; input_limit = input_buffer + status; } ch = *input_pointer++; } while (ch == (char) 13); /* Ignore ASCII carriage return */ return FROMASCII(ch); } /* Match maintype to any MIME type starting with maintype, * for example: image/gif should match image */ PRIVATE int half_match ARGS2(char *,trial_type, char *,target) { char *cp=strchr(trial_type,'/'); /* if no '/' or no '*' */ if (!cp || *(cp+1) != '*') return 0; CTRACE(tfp, "HTFormat: comparing %s and %s for half match\n", trial_type, target); /* main type matches */ if (!strncmp(trial_type, target, (cp-trial_type)-1)) return 1; return 0; } /* Look up a presentation ** ---------------------- ** ** If fill_in is NULL, only look for an exact match. ** If a wildcard match is made, *fill_in is used to store ** a possibly modified presentation, and a pointer to it is ** returned. For an exact match, a pointer to the presentation ** in the HTPresentations list is returned. Returns NULL if ** nothing found. - kw ** */ PRIVATE HTPresentation * HTFindPresentation ARGS3( HTFormat, rep_in, HTFormat, rep_out, HTPresentation*, fill_in) { HTAtom * wildcard = HTAtom_for("*"); CTRACE(tfp, "HTFormat: Looking up presentation for %s to %s\n", HTAtom_name(rep_in), HTAtom_name(rep_out)); /* don't do anymore do it in the Lynx code at startup LJM */ /* if (!HTPresentations) HTFormatInit(); */ /* set up the list */ { int n = HTList_count(HTPresentations); int i; HTPresentation * pres, *match, *strong_wildcard_match=0, *weak_wildcard_match=0, *last_default_match=0, *strong_subtype_wildcard_match=0; for (i = 0; i < n; i++) { pres = (HTPresentation *)HTList_objectAt(HTPresentations, i); if (pres->rep == rep_in) { if (pres->rep_out == rep_out) { CTRACE(tfp, "FindPresentation: found exact match: %s\n", HTAtom_name(pres->rep)); return pres; } else if (!fill_in) { continue; } else if (pres->rep_out == wildcard) { if (!strong_wildcard_match) strong_wildcard_match = pres; /* otherwise use the first one */ CTRACE(tfp, "StreamStack: found strong wildcard match: %s\n", HTAtom_name(pres->rep)); } } else if (!fill_in) { continue; } else if (half_match(HTAtom_name(pres->rep), HTAtom_name(rep_in))) { if (pres->rep_out == rep_out) { if (!strong_subtype_wildcard_match) strong_subtype_wildcard_match = pres; /* otherwise use the first one */ CTRACE(tfp, "StreamStack: found strong subtype wildcard match: %s\n", HTAtom_name(pres->rep)); } } if (pres->rep == WWW_SOURCE) { if (pres->rep_out == rep_out) { if (!weak_wildcard_match) weak_wildcard_match = pres; /* otherwise use the first one */ CTRACE(tfp, "StreamStack: found weak wildcard match: %s\n", HTAtom_name(pres->rep_out)); } if (pres->rep_out == wildcard) { if (!last_default_match) last_default_match = pres; /* otherwise use the first one */ } } } match = strong_subtype_wildcard_match ? strong_subtype_wildcard_match : strong_wildcard_match ? strong_wildcard_match : weak_wildcard_match ? weak_wildcard_match : last_default_match; if (match) { *fill_in = *match; /* Specific instance */ fill_in->rep = rep_in; /* yuk */ fill_in->rep_out = rep_out; /* yuk */ return fill_in; } } return NULL; } /* Create a filter stack ** --------------------- ** ** If a wildcard match is made, a temporary HTPresentation ** structure is made to hold the destination format while the ** new stack is generated. This is just to pass the out format to ** MIME so far. Storing the format of a stream in the stream might ** be a lot neater. ** */ PUBLIC HTStream * HTStreamStack ARGS4( HTFormat, rep_in, HTFormat, rep_out, HTStream*, sink, HTParentAnchor*, anchor) { HTPresentation temp; HTPresentation *match; CTRACE(tfp, "HTFormat: Constructing stream stack for %s to %s\n", HTAtom_name(rep_in), HTAtom_name(rep_out)); /* don't return on WWW_SOURCE some people might like * to make use of the source!!!! LJM *//* if (rep_out == WWW_SOURCE || rep_out == rep_in) return sink; LJM */ if (rep_out == rep_in) return sink; if ((match = HTFindPresentation(rep_in, rep_out, &temp))) { if (match == &temp) { CTRACE(tfp, "StreamStack: Using %s\n", HTAtom_name(temp.rep_out)); } else { CTRACE(tfp, "StreamStack: found exact match: %s\n", HTAtom_name(match->rep)); } return (*match->converter)(match, anchor, sink); } else { return NULL; } } /* Put a presentation near start of list ** ------------------------------------- ** ** Look up a presentation (exact match only) and, if found, reorder ** it to the start of the HTPresentations list. - kw */ PUBLIC void HTReorderPresentation ARGS2( HTFormat, rep_in, HTFormat, rep_out) { HTPresentation *match; if ((match = HTFindPresentation(rep_in, rep_out, NULL))) { HTList_removeObject(HTPresentations, match); HTList_addObject(HTPresentations, match); } } /* Find the cost of a filter stack ** ------------------------------- ** ** Must return the cost of the same stack which StreamStack would set up. ** ** On entry, ** length The size of the data to be converted */ PUBLIC float HTStackValue ARGS4( HTFormat, rep_in, HTFormat, rep_out, float, initial_value, long int, length) { HTAtom * wildcard = HTAtom_for("*"); CTRACE(tfp, "HTFormat: Evaluating stream stack for %s worth %.3f to %s\n", HTAtom_name(rep_in), initial_value, HTAtom_name(rep_out)); if (rep_out == WWW_SOURCE || rep_out == rep_in) return 0.0; /* don't do anymore do it in the Lynx code at startup LJM */ /* if (!HTPresentations) HTFormatInit(); */ /* set up the list */ { int n = HTList_count(HTPresentations); int i; HTPresentation * pres; for (i = 0; i < n; i++) { pres = (HTPresentation *)HTList_objectAt(HTPresentations, i); if (pres->rep == rep_in && (pres->rep_out == rep_out || pres->rep_out == wildcard)) { float value = initial_value * pres->quality; if (HTMaxSecs != 0.0) value = value - (length*pres->secs_per_byte + pres->secs) /HTMaxSecs; return value; } } } return -1e30; /* Really bad */ } /* Display the page while transfer in progress ** ------------------------------------------- ** ** Repaint the page only when necessary. ** */ #ifdef DISP_PARTIAL PRIVATE void HTDisplayPartial NOARGS { if (display_partial) { /* ** HText_getNumOfLines() = "current" number of lines received ** NumOfLines_partial = number of lines at the moment of last repaint. ** ** We update NumOfLines_partial only when we repaint the display. ** -1 is the special value: ** This is a synchronization flag switched to 0 when HText_new() ** starts a new HTMainText object - all hypertext functions use it, ** lines counter in particular [we call it from HText_getNumOfLines()]. ** ** Otherwise HTMainText holds info from the previous document ** and we may repaint it instead of the new one: ** prev doc scrolled to the first line (=Newline_partial) ** is not good looking :-) 23 Aug 1998 Leonid Pauzner ** ** So repaint the page only when necessary: */ if ((NumOfLines_partial != -1) /* new hypertext document available */ && ((Newline_partial + display_lines) > NumOfLines_partial) /* current page not complete... */ && (partial_threshold > 0 ? ((Newline_partial + partial_threshold) < HText_getNumOfLines()) : ((Newline_partial + display_lines) < HText_getNumOfLines()))) { /* * Originally we rendered by increments of 2 lines, * but that got annoying on slow network connections. * Then we switched to full-pages. Now it's configurable. * If partial_threshold < 0, then it's a full page */ NumOfLines_partial = HText_getNumOfLines(); HText_pageDisplay(Newline_partial, ""); } } } #else #define HTDisplayPartial() /*nothing*/ #endif /* Push data from a socket down a stream ** ------------------------------------- ** ** This routine is responsible for creating and PRESENTING any ** graphic (or other) objects described by the file. ** ** The file number given is assumed to be a TELNET stream, i.e., containing ** CRLF at the end of lines which need to be stripped to LF for unix ** when the format is textual. ** ** State of socket and target stream on entry: ** socket (file_number) assumed open, ** target (sink) assumed valid. ** ** Return values: ** HT_INTERRUPTED Interruption or error after some data received. ** -2 Unexpected disconnect before any data received. ** -1 Interruption or error before any data received, or ** (UNIX) other read error before any data received, or ** download cancelled. ** HT_LOADED Normal close of socket (end of file indication ** received), or ** unexpected disconnect after some data received, or ** other read error after some data received, or ** (not UNIX) other read error before any data received. ** ** State of socket and target stream on return depends on return value: ** HT_INTERRUPTED socket still open, target aborted. ** -2 socket still open, target stream still valid. ** -1 socket still open, target aborted. ** otherwise socket closed, target stream still valid. */ PUBLIC int HTCopy ARGS4( HTParentAnchor *, anchor, int, file_number, void*, handle GCC_UNUSED, HTStream*, sink) { HTStreamClass targetClass; int bytes; int rv = 0; /* Push the data down the stream */ targetClass = *(sink->isa); /* Copy pointers to procedures */ /* Push binary from socket down sink ** ** This operation could be put into a main event loop */ HTReadProgress(bytes = 0, 0); for (;;) { int status; if (LYCancelDownload) { LYCancelDownload = FALSE; (*targetClass._abort)(sink, NULL); rv = -1; goto finished; } if (HTCheckForInterrupt()) { _HTProgress (TRANSFER_INTERRUPTED); (*targetClass._abort)(sink, NULL); if (bytes) rv = HT_INTERRUPTED; else rv = -1; goto finished; } status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE); if (status <= 0) { if (status == 0) { break; } else if (status == HT_INTERRUPTED) { _HTProgress (TRANSFER_INTERRUPTED); (*targetClass._abort)(sink, NULL); if (bytes) rv = HT_INTERRUPTED; else rv = -1; goto finished; } else if (SOCKET_ERRNO == ENOTCONN || SOCKET_ERRNO == ECONNRESET || SOCKET_ERRNO == EPIPE) { /* * Arrrrgh, HTTP 0/1 compatibility problem, maybe. */ if (bytes <= 0) { /* * Don't have any data, so let the calling * function decide what to do about it. - FM */ rv = -2; goto finished; } else { #ifdef UNIX /* * Treat what we've received already as the complete * transmission, but not without giving the user * an alert. I don't know about all the different * TCP stacks for VMS etc., so this is currently * only for UNIX. - kw */ HTInetStatus("NETREAD"); HTAlert("Unexpected server disconnect."); CTRACE(tfp, "HTCopy: Unexpected server disconnect. Treating as completed.\n"); status = 0; break; #else /* !UNIX */ /* * Treat what we've gotten already * as the complete transmission. - FM */ CTRACE(tfp, "HTCopy: Unexpected server disconnect. Treating as completed.\n"); status = 0; break; #endif /* UNIX */ } #ifdef UNIX } else { /* status < 0 and other errno */ /* * Treat what we've received already as the complete * transmission, but not without giving the user * an alert. I don't know about all the different * TCP stacks for VMS etc., so this is currently * only for UNIX. - kw */ HTInetStatus("NETREAD"); HTAlert("Unexpected read error."); if (bytes) { (void)NETCLOSE(file_number); rv = HT_LOADED; } else { (*targetClass._abort)(sink, NULL); rv = -1; } goto finished; #endif } break; } #ifdef NOT_ASCII { char * p; for (p = input_buffer; p < input_buffer+status; p++) { *p = FROMASCII(*p); } } #endif /* NOT_ASCII */ (*targetClass.put_block)(sink, input_buffer, status); bytes += status; HTReadProgress(bytes, anchor ? anchor->content_length : 0); HTDisplayPartial(); } /* next bufferload */ _HTProgress(gettext("Data transfer complete")); (void)NETCLOSE(file_number); rv = HT_LOADED; finished: return(rv); } /* Push data from a file pointer down a stream ** ------------------------------------- ** ** This routine is responsible for creating and PRESENTING any ** graphic (or other) objects described by the file. ** ** ** State of file and target stream on entry: ** FILE* (fp) assumed open, ** target (sink) assumed valid. ** ** Return values: ** HT_INTERRUPTED Interruption after some data read. ** HT_PARTIAL_CONTENT Error after some data read. ** -1 Error before any data read. ** HT_LOADED Normal end of file indication on reading. ** ** State of file and target stream on return: ** always fp still open, target stream still valid. */ PUBLIC int HTFileCopy ARGS2( FILE *, fp, HTStream*, sink) { HTStreamClass targetClass; int status, bytes; int rv = HT_OK; /* Push the data down the stream */ targetClass = *(sink->isa); /* Copy pointers to procedures */ /* Push binary from socket down sink */ HTReadProgress(bytes = 0, 0); for (;;) { status = fread(input_buffer, 1, INPUT_BUFFER_SIZE, fp); if (status == 0) { /* EOF or error */ if (ferror(fp) == 0) { rv = HT_LOADED; break; } CTRACE(tfp, "HTFormat: Read error, read returns %d\n", ferror(fp)); if (bytes) { rv = HT_PARTIAL_CONTENT; } else { rv = -1; } break; } (*targetClass.put_block)(sink, input_buffer, status); bytes += status; HTReadProgress(bytes, 0); HTDisplayPartial(); if (HTCheckForInterrupt()) { _HTProgress (TRANSFER_INTERRUPTED); if (bytes) { rv = HT_INTERRUPTED; } else { rv = -1; } break; } } /* next bufferload */ return rv; } #ifdef USE_ZLIB /* Push data from a gzip file pointer down a stream ** ------------------------------------- ** ** This routine is responsible for creating and PRESENTING any ** graphic (or other) objects described by the file. ** ** ** State of file and target stream on entry: ** gzFile (gzfp) assumed open (should have gzipped content), ** target (sink) assumed valid. ** ** Return values: ** HT_INTERRUPTED Interruption after some data read. ** HT_PARTIAL_CONTENT Error after some data read. ** -1 Error before any data read. ** HT_LOADED Normal end of file indication on reading. ** ** State of file and target stream on return: ** always gzfp still open, target stream still valid. */ PRIVATE int HTGzFileCopy ARGS2( gzFile, gzfp, HTStream*, sink) { HTStreamClass targetClass; int status, bytes; int gzerrnum; int rv = HT_OK; /* Push the data down the stream */ targetClass = *(sink->isa); /* Copy pointers to procedures */ /* read and inflate gzip'd file, and push binary down sink */ HTReadProgress(bytes = 0, 0); for (;;) { status = gzread(gzfp, input_buffer, INPUT_BUFFER_SIZE); if (status <= 0) { /* EOF or error */ if (status == 0) { rv = HT_LOADED; break; } CTRACE(tfp, "HTGzFileCopy: Read error, gzread returns %d\n", status); CTRACE(tfp, "gzerror : %s\n", gzerror(gzfp, &gzerrnum)); if (TRACE) { if (gzerrnum == Z_ERRNO) perror("gzerror "); } if (bytes) { rv = HT_PARTIAL_CONTENT; } else { rv = -1; } break; } (*targetClass.put_block)(sink, input_buffer, status); bytes += status; HTReadProgress(bytes, -1); HTDisplayPartial(); if (HTCheckForInterrupt()) { _HTProgress (TRANSFER_INTERRUPTED); if (bytes) { rv = HT_INTERRUPTED; } else { rv = -1; } break; } } /* next bufferload */ return rv; } #endif /* USE_ZLIB */ /* Push data from a socket down a stream STRIPPING CR ** -------------------------------------------------- ** ** This routine is responsible for creating and PRESENTING any ** graphic (or other) objects described by the socket. ** ** The file number given is assumed to be a TELNET stream ie containing ** CRLF at the end of lines which need to be stripped to LF for unix ** when the format is textual. ** */ PUBLIC void HTCopyNoCR ARGS3( HTParentAnchor *, anchor GCC_UNUSED, int, file_number, HTStream*, sink) { HTStreamClass targetClass; char character; /* Push the data, ignoring CRLF, down the stream */ targetClass = *(sink->isa); /* Copy pointers to procedures */ /* Push text from telnet socket down sink ** ** @@@@@ To push strings could be faster? (especially is we ** cheat and don't ignore CR! :-} */ HTInitInput(file_number); for (;;) { character = HTGetCharacter(); if (character == (char)EOF) break; (*targetClass.put_character)(sink, character); } } /* Parse a socket given format and file number ** ** This routine is responsible for creating and PRESENTING any ** graphic (or other) objects described by the file. ** ** The file number given is assumed to be a TELNET stream ie containing ** CRLF at the end of lines which need to be stripped to LF for unix ** when the format is textual. ** ** State of socket and target stream on entry: ** socket (file_number) assumed open, ** target (sink) usually NULL (will call stream stack). ** ** Return values: ** HT_INTERRUPTED Interruption or error after some data received. ** -501 Stream stack failed (cannot present or convert). ** -2 Unexpected disconnect before any data received. ** -1 Stream stack failed (cannot present or convert), or ** Interruption or error before any data received, or ** (UNIX) other read error before any data received, or ** download cancelled. ** HT_LOADED Normal close of socket (end of file indication ** received), or ** unexpected disconnect after some data received, or ** other read error after some data received, or ** (not UNIX) other read error before any data received. ** ** State of socket and target stream on return depends on return value: ** HT_INTERRUPTED socket still open, target aborted. ** -501 socket still open, target stream NULL. ** -2 socket still open, target freed. ** -1 socket still open, target stream aborted or NULL. ** otherwise socket closed, target stream freed. */ PUBLIC int HTParseSocket ARGS5( HTFormat, rep_in, HTFormat, format_out, HTParentAnchor *, anchor, int, file_number, HTStream*, sink) { HTStream * stream; HTStreamClass targetClass; int rv; stream = HTStreamStack(rep_in, format_out, sink, anchor); if (!stream) { char buffer[1024]; /* @@@@@@@@ */ if (LYCancelDownload) { LYCancelDownload = FALSE; return -1; } sprintf(buffer, gettext("Sorry, can't convert from %s to %s."), HTAtom_name(rep_in), HTAtom_name(format_out)); CTRACE(tfp, "HTFormat: %s\n", buffer); return HTLoadError(sink, 501, buffer); /* returns -501 */ } /* ** Push the data, don't worry about CRLF we can strip them later. */ targetClass = *(stream->isa); /* Copy pointers to procedures */ rv = HTCopy(anchor, file_number, NULL, stream); if (rv != -1 && rv != HT_INTERRUPTED) (*targetClass._free)(stream); return rv; /* Originally: full: HT_LOADED; partial: HT_INTERRUPTED; no bytes: -1 */ } /* Parse a file given format and file pointer ** ** This routine is responsible for creating and PRESENTING any ** graphic (or other) objects described by the file. ** ** The file number given is assumed to be a TELNET stream ie containing ** CRLF at the end of lines which need to be stripped to \n for unix ** when the format is textual. ** ** State of file and target stream on entry: ** FILE* (fp) assumed open, ** target (sink) usually NULL (will call stream stack). ** ** Return values: ** -501 Stream stack failed (cannot present or convert). ** -1 Download cancelled. ** HT_NO_DATA Error before any data read. ** HT_PARTIAL_CONTENT Interruption or error after some data read. ** HT_LOADED Normal end of file indication on reading. ** ** State of file and target stream on return: ** always fp still open; target freed, aborted, or NULL. */ PUBLIC int HTParseFile ARGS5( HTFormat, rep_in, HTFormat, format_out, HTParentAnchor *, anchor, FILE *, fp, HTStream*, sink) { HTStream * stream; HTStreamClass targetClass; int rv; stream = HTStreamStack(rep_in, format_out, sink , anchor); if (!stream) { char buffer[1024]; /* @@@@@@@@ */ if (LYCancelDownload) { LYCancelDownload = FALSE; return -1; } sprintf(buffer, gettext("Sorry, can't convert from %s to %s."), HTAtom_name(rep_in), HTAtom_name(format_out)); CTRACE(tfp, "HTFormat(in HTParseFile): %s\n", buffer); return HTLoadError(sink, 501, buffer); } /* Push the data down the stream ** ** @@ Bug: This decision ought to be made based on "encoding" ** rather than on content-type. @@@ When we handle encoding. ** The current method smells anyway. */ targetClass = *(stream->isa); /* Copy pointers to procedures */ rv = HTFileCopy(fp, stream); if (rv == -1 || rv == HT_INTERRUPTED) { (*targetClass._abort)(stream, NULL); } else { (*targetClass._free)(stream); } if (rv == -1) return HT_NO_DATA; else if (rv == HT_INTERRUPTED || (rv > 0 && rv != HT_LOADED)) return HT_PARTIAL_CONTENT; else return HT_LOADED; } #ifdef USE_ZLIB PRIVATE int HTCloseGzFile ARGS1( gzFile, gzfp) { int gzres; if (gzfp == NULL) return 0; gzres = gzclose(gzfp); if (TRACE) { if (gzres == Z_ERRNO) { perror("gzclose "); } else if (gzres != Z_OK) { CTRACE(tfp, "gzclose : error number %d\n", gzres); } } return(gzres); } /* HTParseGzFile ** ** State of file and target stream on entry: ** gzFile (gzfp) assumed open, ** target (sink) usually NULL (will call stream stack). ** ** Return values: ** -501 Stream stack failed (cannot present or convert). ** -1 Download cancelled. ** HT_NO_DATA Error before any data read. ** HT_PARTIAL_CONTENT Interruption or error after some data read. ** HT_LOADED Normal end of file indication on reading. ** ** State of file and target stream on return: ** always gzfp closed; target freed, aborted, or NULL. */ PUBLIC int HTParseGzFile ARGS5( HTFormat, rep_in, HTFormat, format_out, HTParentAnchor *, anchor, gzFile, gzfp, HTStream*, sink) { HTStream * stream; HTStreamClass targetClass; int rv; stream = HTStreamStack(rep_in, format_out, sink , anchor); if (!stream) { char buffer[1024]; /* @@@@@@@@ */ HTCloseGzFile(gzfp); if (LYCancelDownload) { LYCancelDownload = FALSE; return -1; } sprintf(buffer, gettext("Sorry, can't convert from %s to %s."), HTAtom_name(rep_in), HTAtom_name(format_out)); CTRACE(tfp, "HTFormat(in HTParseGzFile): %s\n", buffer); return HTLoadError(sink, 501, buffer); } /* Push the data down the stream ** ** @@ Bug: This decision ought to be made based on "encoding" ** rather than on content-type. @@@ When we handle encoding. ** The current method smells anyway. */ targetClass = *(stream->isa); /* Copy pointers to procedures */ rv = HTGzFileCopy(gzfp, stream); if (rv == -1 || rv == HT_INTERRUPTED) { (*targetClass._abort)(stream, NULL); } else { (*targetClass._free)(stream); } HTCloseGzFile(gzfp); if (rv == -1) return HT_NO_DATA; else if (rv == HT_INTERRUPTED || (rv > 0 && rv != HT_LOADED)) return HT_PARTIAL_CONTENT; else return HT_LOADED; } #endif /* USE_ZLIB */ /* Converter stream: Network Telnet to internal character text ** ----------------------------------------------------------- ** ** The input is assumed to be in ASCII, with lines delimited ** by (13,10) pairs, These pairs are converted into (CR,LF) ** pairs in the local representation. The (CR,LF) sequence ** when found is changed to a '\n' character, the internal ** C representation of a new line. */ PRIVATE void NetToText_put_character ARGS2(HTStream *, me, char, net_char) { char c = FROMASCII(net_char); if (me->had_cr) { if (c == LF) { me->sink->isa->put_character(me->sink, '\n'); /* Newline */ me->had_cr = NO; return; } else { me->sink->isa->put_character(me->sink, CR); /* leftover */ } } me->had_cr = (c == CR); if (!me->had_cr) me->sink->isa->put_character(me->sink, c); /* normal */ } PRIVATE void NetToText_put_string ARGS2(HTStream *, me, CONST char *, s) { CONST char * p; for (p = s; *p; p++) NetToText_put_character(me, *p); } PRIVATE void NetToText_put_block ARGS3(HTStream *, me, CONST char*, s, int, l) { CONST char * p; for (p = s; p < (s+l); p++) NetToText_put_character(me, *p); } PRIVATE void NetToText_free ARGS1(HTStream *, me) { (me->sink->isa->_free)(me->sink); /* Close rest of pipe */ FREE(me); } PRIVATE void NetToText_abort ARGS2(HTStream *, me, HTError, e) { me->sink->isa->_abort(me->sink,e); /* Abort rest of pipe */ FREE(me); } /* The class structure */ PRIVATE HTStreamClass NetToTextClass = { "NetToText", NetToText_free, NetToText_abort, NetToText_put_character, NetToText_put_string, NetToText_put_block }; /* The creation method */ PUBLIC HTStream * HTNetToText ARGS1(HTStream *, sink) { HTStream* me = (HTStream*)malloc(sizeof(*me)); if (me == NULL) outofmem(__FILE__, "NetToText"); me->isa = &NetToTextClass; me->had_cr = NO; me->sink = sink; return me; }