summaryrefslogtreecommitdiffstats
path: root/src/HTFWriter.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/HTFWriter.c')
-rw-r--r--src/HTFWriter.c165
1 files changed, 157 insertions, 8 deletions
diff --git a/src/HTFWriter.c b/src/HTFWriter.c
index b07aa92..6e961e2 100644
--- a/src/HTFWriter.c
+++ b/src/HTFWriter.c
@@ -1,5 +1,5 @@
/*
- * $LynxId: HTFWriter.c,v 1.126 2024/04/11 20:21:33 tom Exp $
+ * $LynxId: HTFWriter.c,v 1.129 2024/05/27 17:02:24 tom Exp $
*
* FILE WRITER HTFWrite.h
* ===========
@@ -45,6 +45,10 @@
#include <LYCookie.h>
#endif
+#ifdef USE_BROTLI
+#include <brotli/decode.h>
+#endif
+
/* contains the name of the temp file which is being downloaded into */
char *WWW_Download_File = NULL;
BOOLEAN LYCancelDownload = FALSE; /* exported to HTFormat.c in libWWW */
@@ -186,15 +190,155 @@ static void decompress_gzip(HTStream *me)
#define FMT "%s %s"
const char *program;
- if (LYCopyFile(in_name, copied) == 0) {
+ if ((program = HTGetProgramPath(ppUNCOMPRESS)) == NULL) {
+ HTAlert(ERROR_UNCOMPRESSING_TEMP);
+ } else if (LYCopyFile(in_name, copied) == 0) {
char expanded[LY_MAXPATH];
char *command = NULL;
- if ((program = HTGetProgramPath(ppUNCOMPRESS)) != NULL) {
- HTAddParam(&command, FMT, 1, program);
- HTAddParam(&command, FMT, 2, copied);
- HTEndParam(&command, FMT, 2);
+ HTAddParam(&command, FMT, 1, program);
+ HTAddParam(&command, FMT, 2, copied);
+ HTEndParam(&command, FMT, 2);
+ if (LYSystem(command) == 0) {
+ struct stat stat_buf;
+
+ strcpy(expanded, copied);
+ *strrchr(expanded, '.') = '\0';
+ if (LYRenameFile(expanded, in_name) != 0) {
+ CTRACE((tfp, "rename failed %s to %s\n", expanded, in_name));
+ } else if (stat(in_name, &stat_buf) != 0) {
+ CTRACE((tfp, "stat failed for %s\n", in_name));
+ } else {
+ me->anchor->actual_length = stat_buf.st_size;
+ }
+ } else {
+ CTRACE((tfp, "command failed: %s\n", command));
}
+ free(command);
+ (void) LYRemoveTemp(copied);
+ }
+#undef FMT
+#endif
+ }
+}
+
+static void decompress_br(HTStream *me)
+{
+#undef THIS_FUNC
+#define THIS_FUNC "decompress_br\n"
+ char *in_name = me->anchor->FileCache;
+ char copied[LY_MAXPATH];
+ FILE *fp = LYOpenTemp(copied, ".br", BIN_W);
+
+ if (fp != 0) {
+#ifdef USE_BROTLI
+#define INPUT_BUFFER_SIZE BUFSIZ
+ char *brotli_buffer = NULL;
+ char *normal_buffer = NULL;
+ size_t brotli_size;
+ size_t brotli_limit = 0;
+ size_t brotli_offset;
+ size_t normal_size;
+ size_t normal_limit = 0;
+ BrotliDecoderResult status2 = BROTLI_DECODER_RESULT_ERROR;
+ int status;
+ off_t bytes = 0;
+ FILE *ifp = fopen(in_name, BIN_R);
+
+ if (ifp != NULL) {
+ CTRACE((tfp, "reading '%s'\n", in_name));
+ for (;;) {
+ size_t input_chunk = INPUT_BUFFER_SIZE;
+
+ brotli_offset = brotli_limit;
+ brotli_limit += input_chunk;
+ brotli_buffer = realloc(brotli_buffer, brotli_limit);
+ if (brotli_buffer == NULL)
+ outofmem(__FILE__, THIS_FUNC);
+ status = (int) fread(brotli_buffer + brotli_offset,
+ sizeof(char),
+ input_chunk,
+ ifp);
+
+ if (status <= 0) { /* EOF or error */
+ if (status == 0) {
+ break;
+ }
+ CTRACE((tfp, THIS_FUNC ": Read error, fread returns %d\n", status));
+ break;
+ }
+ bytes += status;
+ }
+ fclose(ifp);
+
+ CTRACE((tfp, "decompressing '%s'\n", in_name));
+ /*
+ * next, unless we encountered an error (and have no data), try
+ * decompressing with increasing output buffer sizes until the brotli
+ * library succeeds.
+ */
+ if (bytes > 0) {
+ do {
+ if (normal_limit == 0)
+ normal_limit = (10 * brotli_limit) + INPUT_BUFFER_SIZE;
+ else
+ normal_limit *= 2;
+ normal_buffer = realloc(normal_buffer, normal_limit);
+ if (normal_buffer == NULL)
+ outofmem(__FILE__, THIS_FUNC);
+ brotli_size = (size_t) bytes;
+ normal_size = normal_limit;
+ status2 = BrotliDecoderDecompress(brotli_size,
+ (uint8_t *) brotli_buffer,
+ &normal_size,
+ (uint8_t *) normal_buffer);
+ /*
+ * brotli library should return
+ * BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT,
+ * but actually returns
+ * BROTLI_DECODER_RESULT_ERROR
+ *
+ * Accommodate possible improvements...
+ */
+ } while (status2 != BROTLI_DECODER_RESULT_SUCCESS);
+ }
+
+ /*
+ * finally, pump that data into the output stream.
+ */
+ if (status2 == BROTLI_DECODER_RESULT_SUCCESS) {
+ CTRACE((tfp, THIS_FUNC ": decompressed %ld -> %ld (1:%.1f)\n",
+ brotli_size, normal_size,
+ (double) normal_size / (double) brotli_size));
+ fwrite(normal_buffer, sizeof(char), normal_size, fp);
+ }
+ free(brotli_buffer);
+ free(normal_buffer);
+
+ LYCloseTempFP(fp);
+
+ CTRACE((tfp, "...decompress %" PRI_off_t " to %lu\n",
+ CAST_off_t (me->anchor->actual_length),
+ (unsigned long)normal_size));
+ if (status2 == BROTLI_DECODER_RESULT_SUCCESS) {
+ if (LYRenameFile(copied, in_name) == 0)
+ me->anchor->actual_length = (off_t) normal_size;
+ }
+ (void) LYRemoveTemp(copied);
+ }
+#else
+#define FMT "%s -j -d %s"
+ const char *program;
+
+ if ((program = HTGetProgramPath(ppBROTLI)) == NULL) {
+ HTAlert(ERROR_UNCOMPRESSING_TEMP);
+ } else if (LYCopyFile(in_name, copied) == 0) {
+ char expanded[LY_MAXPATH];
+ char *command = NULL;
+
+ HTAddParam(&command, FMT, 1, program);
+ HTAddParam(&command, FMT, 2, copied);
+ HTEndParam(&command, FMT, 2);
if (LYSystem(command) == 0) {
struct stat stat_buf;
@@ -252,12 +396,17 @@ static void HTFWriter_free(HTStream *me)
*/
if (me->anchor->FileCache != NULL
&& me->anchor->no_content_encoding == FALSE
- && me->input_format == HTAtom_for("application/x-gzip")
+ && IsCompressionFormat(me->input_format, cftGzip)
&& !strcmp(me->anchor->content_encoding, "gzip")) {
decompress_gzip(me);
+ } else if (me->anchor->FileCache != NULL
+ && me->anchor->no_content_encoding == FALSE
+ && IsCompressionFormat(me->input_format, cftBrotli)
+ && !strcmp(me->anchor->content_encoding, "br")) {
+ decompress_br(me);
}
#ifdef VMS
- if (0 == strcmp(me->end_command, "SaveVMSBinaryFile")) {
+ else if (0 == strcmp(me->end_command, "SaveVMSBinaryFile")) {
/*
* It's a binary file saved to disk on VMS, which
* we want to convert to fixed records format. - FM