summaryrefslogtreecommitdiffstats
path: root/generic/tclZlib.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/tclZlib.c')
-rw-r--r--generic/tclZlib.c279
1 files changed, 251 insertions, 28 deletions
diff --git a/generic/tclZlib.c b/generic/tclZlib.c
index 341f8e0..9b231df 100644
--- a/generic/tclZlib.c
+++ b/generic/tclZlib.c
@@ -64,6 +64,11 @@ typedef struct {
int wbits; /* The encoded compression mode, so we can
* restart the stream if necessary. */
Tcl_Command cmd; /* Token for the associated Tcl command. */
+ Tcl_Obj *compDictObj; /* Byte-array object containing compression
+ * dictionary (not dictObj!) to use if
+ * necessary. */
+ GzipHeader *gzHeaderPtr; /* If we've allocated a gzip header
+ * structure. */
} ZlibStreamHandle;
/*
@@ -90,6 +95,9 @@ typedef struct {
GzipHeader outHeader; /* Header to write to an output stream, when
* compressing a gzip stream. */
Tcl_TimerToken timer; /* Timer used for keeping events fresh. */
+ Tcl_Obj *compDictObj; /* Byte-array object containing compression
+ * dictionary (not dictObj!) to use if
+ * necessary. */
} ZlibChannelData;
/*
@@ -141,7 +149,8 @@ static int GenerateHeader(Tcl_Interp *interp, Tcl_Obj *dictObj,
GzipHeader *headerPtr, int *extraSizePtr);
static Tcl_Channel ZlibStackChannelTransform(Tcl_Interp *interp,
int mode, int format, int level,
- Tcl_Channel channel, Tcl_Obj *gzipHeaderDictPtr);
+ Tcl_Channel channel, Tcl_Obj *gzipHeaderDictPtr,
+ Tcl_Obj *compDictObj);
static void ZlibStreamCleanup(ZlibStreamHandle *zshPtr);
static void ZlibTransformTimerKill(ZlibChannelData *cd);
static void ZlibTransformTimerRun(ClientData clientData);
@@ -209,6 +218,7 @@ ConvertError(
case Z_MEM_ERROR: codeStr = "MEM"; break;
case Z_BUF_ERROR: codeStr = "BUF"; break;
case Z_VERSION_ERROR: codeStr = "VERSION"; break;
+ case Z_NEED_DICT: codeStr = "NEED_DICT"; break;
default:
codeStr = "unknown";
codeStr2 = codeStrBuf;
@@ -294,7 +304,9 @@ GenerateHeader(
NULL);
headerPtr->nativeCommentBuf[len] = '\0';
headerPtr->header.comment = (Bytef *) headerPtr->nativeCommentBuf;
- *extraSizePtr += len;
+ if (extraSizePtr != NULL) {
+ *extraSizePtr += len;
+ }
}
if (GetValue(interp, dictObj, "crc", &value) != TCL_OK) {
@@ -312,7 +324,9 @@ GenerateHeader(
headerPtr->nativeFilenameBuf, MAXPATHLEN-1, NULL, &len, NULL);
headerPtr->nativeFilenameBuf[len] = '\0';
headerPtr->header.name = (Bytef *) headerPtr->nativeFilenameBuf;
- *extraSizePtr += len;
+ if (extraSizePtr != NULL) {
+ *extraSizePtr += len;
+ }
}
if (GetValue(interp, dictObj, "os", &value) != TCL_OK) {
@@ -438,6 +452,34 @@ ExtractHeader(
}
}
+static int
+SetInflateDictionary(
+ z_streamp strm,
+ Tcl_Obj *compDictObj)
+{
+ if (compDictObj != NULL) {
+ int length;
+ unsigned char *bytes = Tcl_GetByteArrayFromObj(compDictObj, &length);
+
+ return inflateSetDictionary(strm, bytes, (unsigned) length);
+ }
+ return Z_OK;
+}
+
+static int
+SetDeflateDictionary(
+ z_streamp strm,
+ Tcl_Obj *compDictObj)
+{
+ if (compDictObj != NULL) {
+ int length;
+ unsigned char *bytes = Tcl_GetByteArrayFromObj(compDictObj, &length);
+
+ return deflateSetDictionary(strm, bytes, (unsigned) length);
+ }
+ return Z_OK;
+}
+
/*
*----------------------------------------------------------------------
*
@@ -476,6 +518,7 @@ Tcl_ZlibStreamInit(
ZlibStreamHandle *zshPtr = NULL;
Tcl_DString cmdname;
Tcl_CmdInfo cmdinfo;
+ GzipHeader *gzHeaderPtr = NULL;
switch (mode) {
case TCL_ZLIB_STREAM_DEFLATE:
@@ -490,6 +533,15 @@ Tcl_ZlibStreamInit(
break;
case TCL_ZLIB_FORMAT_GZIP:
wbits = WBITS_GZIP;
+ if (dictObj) {
+ gzHeaderPtr = ckalloc(sizeof(GzipHeader));
+ memset(gzHeaderPtr, 0, sizeof(GzipHeader));
+ if (GenerateHeader(interp, dictObj, gzHeaderPtr,
+ NULL) != TCL_OK) {
+ ckfree(gzHeaderPtr);
+ return TCL_ERROR;
+ }
+ }
break;
case TCL_ZLIB_FORMAT_ZLIB:
wbits = WBITS_ZLIB;
@@ -516,6 +568,14 @@ Tcl_ZlibStreamInit(
break;
case TCL_ZLIB_FORMAT_GZIP:
wbits = WBITS_GZIP;
+ gzHeaderPtr = ckalloc(sizeof(GzipHeader));
+ memset(gzHeaderPtr, 0, sizeof(GzipHeader));
+ gzHeaderPtr->header.name = (Bytef *)
+ gzHeaderPtr->nativeFilenameBuf;
+ gzHeaderPtr->header.name_max = MAXPATHLEN - 1;
+ gzHeaderPtr->header.comment = (Bytef *)
+ gzHeaderPtr->nativeCommentBuf;
+ gzHeaderPtr->header.name_max = MAX_COMMENT_LEN - 1;
break;
case TCL_ZLIB_FORMAT_ZLIB:
wbits = WBITS_ZLIB;
@@ -542,6 +602,8 @@ Tcl_ZlibStreamInit(
zshPtr->wbits = wbits;
zshPtr->currentInput = NULL;
zshPtr->streamEnd = 0;
+ zshPtr->compDictObj = NULL;
+ zshPtr->gzHeaderPtr = gzHeaderPtr;
memset(&zshPtr->stream, 0, sizeof(z_stream));
/*
@@ -551,8 +613,22 @@ Tcl_ZlibStreamInit(
if (mode == TCL_ZLIB_STREAM_DEFLATE) {
e = deflateInit2(&zshPtr->stream, level, Z_DEFLATED, wbits,
MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+ if (e == Z_OK && zshPtr->gzHeaderPtr) {
+ e = deflateSetHeader(&zshPtr->stream,
+ &zshPtr->gzHeaderPtr->header);
+ }
+ if (e == Z_OK && zshPtr->compDictObj) {
+ e = SetDeflateDictionary(&zshPtr->stream, zshPtr->compDictObj);
+ }
} else {
e = inflateInit2(&zshPtr->stream, wbits);
+ if (e == Z_OK && zshPtr->gzHeaderPtr) {
+ e = inflateGetHeader(&zshPtr->stream,
+ &zshPtr->gzHeaderPtr->header);
+ }
+ if (format==TCL_ZLIB_FORMAT_RAW && zshPtr->compDictObj && e==Z_OK) {
+ e = SetInflateDictionary(&zshPtr->stream, zshPtr->compDictObj);
+ }
}
if (e != Z_OK) {
@@ -617,7 +693,14 @@ Tcl_ZlibStreamInit(
}
return TCL_OK;
- error:
+
+ error:
+ if (zshPtr->compDictObj) {
+ Tcl_DecrRefCount(zshPtr->compDictObj);
+ }
+ if (zshPtr->gzHeaderPtr) {
+ ckfree(zshPtr->gzHeaderPtr);
+ }
ckfree(zshPtr);
return TCL_ERROR;
}
@@ -725,6 +808,12 @@ ZlibStreamCleanup(
if (zshPtr->currentInput) {
Tcl_DecrRefCount(zshPtr->currentInput);
}
+ if (zshPtr->compDictObj) {
+ Tcl_DecrRefCount(zshPtr->compDictObj);
+ }
+ if (zshPtr->gzHeaderPtr) {
+ ckfree(zshPtr->gzHeaderPtr);
+ }
ckfree(zshPtr);
}
@@ -777,8 +866,15 @@ Tcl_ZlibStreamReset(
if (zshPtr->mode == TCL_ZLIB_STREAM_DEFLATE) {
e = deflateInit2(&zshPtr->stream, zshPtr->level, Z_DEFLATED,
zshPtr->wbits, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+ if (e == Z_OK && zshPtr->compDictObj) {
+ e = SetDeflateDictionary(&zshPtr->stream, zshPtr->compDictObj);
+ }
} else {
e = inflateInit2(&zshPtr->stream, zshPtr->wbits);
+ if (zshPtr->format == TCL_ZLIB_FORMAT_RAW && zshPtr->compDictObj
+ && e == Z_OK) {
+ e = SetInflateDictionary(&zshPtr->stream, zshPtr->compDictObj);
+ }
}
if (e != Z_OK) {
@@ -875,6 +971,27 @@ Tcl_ZlibStreamChecksum(
/*
*----------------------------------------------------------------------
*
+ * Tcl_ZlibStreamGetZstreamp --
+ *
+ * Return the z_streamp for the stream (though not typed as such, so as
+ * to avoid type interface poisoning). Shouldn't be used to poke around
+ * excessively.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void *
+Tcl_ZlibStreamGetZstreamp(
+ Tcl_ZlibStream zshandle)
+{
+ ZlibStreamHandle *zshPtr = (ZlibStreamHandle *) zshandle;
+
+ return &zshPtr->stream;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* Tcl_ZlibStreamPut --
*
* Add data to the stream for compression or decompression from a
@@ -1071,6 +1188,12 @@ Tcl_ZlibStreamGet(
}
e = inflate(&zshPtr->stream, zshPtr->flush);
+ if (e == Z_NEED_DICT && zshPtr->compDictObj) {
+ e = SetInflateDictionary(&zshPtr->stream, zshPtr->compDictObj);
+ if (e == Z_OK) {
+ e = inflate(&zshPtr->stream, zshPtr->flush);
+ }
+ };
Tcl_ListObjLength(NULL, zshPtr->inData, &listLen);
while ((zshPtr->stream.avail_out > 0)
@@ -1125,6 +1248,12 @@ Tcl_ZlibStreamGet(
*/
e = inflate(&zshPtr->stream, zshPtr->flush);
+ if (e == Z_NEED_DICT && zshPtr->compDictObj) {
+ e = SetInflateDictionary(&zshPtr->stream,zshPtr->compDictObj);
+ if (e == Z_OK) {
+ e = inflate(&zshPtr->stream, zshPtr->flush);
+ }
+ }
}
if (zshPtr->stream.avail_out > 0) {
Tcl_SetByteArrayLength(data,
@@ -1800,52 +1929,111 @@ ZlibCmd(
return TCL_ERROR;
}
return TCL_OK;
- case CMD_STREAM: /* stream deflate/inflate/...gunzip \
- * ?level?
+ case CMD_STREAM: { /* stream deflate/inflate/...gunzip \
+ * ?options...?
* -> handleCmd */
- if (objc < 3 || objc > 4) {
- Tcl_WrongNumArgs(interp, 2, objv, "mode ?level?");
+ typedef struct {
+ const char *name;
+ Tcl_Obj **valueVar;
+ } OptDescriptor;
+ Tcl_Obj *compDictObj = NULL;
+ Tcl_Obj *gzipHeaderObj = NULL;
+ Tcl_Obj *levelObj = NULL;
+ OptDescriptor compressionOpts[] = {
+ { "-dictionary", &compDictObj },
+ { "-level", &levelObj },
+ { NULL, NULL }
+ };
+ OptDescriptor gzipOpts[] = {
+ { "-dictionary", &compDictObj },
+ { "-header", &gzipHeaderObj },
+ { "-level", &levelObj },
+ { NULL, NULL }
+ };
+ OptDescriptor expansionOpts[] = {
+ { "-dictionary", &compDictObj },
+ { NULL, NULL }
+ };
+ OptDescriptor *desc;
+
+ if (objc < 3 || !(objc & 1)) {
+ Tcl_WrongNumArgs(interp, 2, objv, "mode ?-option value...?");
return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(interp, objv[2], stream_formats, "mode", 0,
&format) != TCL_OK) {
return TCL_ERROR;
}
- mode = TCL_ZLIB_STREAM_INFLATE;
switch ((enum zlibFormats) format) {
case FMT_DEFLATE:
+ desc = compressionOpts;
mode = TCL_ZLIB_STREAM_DEFLATE;
+ format = TCL_ZLIB_FORMAT_RAW;
+ break;
case FMT_INFLATE:
+ desc = expansionOpts;
+ mode = TCL_ZLIB_STREAM_INFLATE;
format = TCL_ZLIB_FORMAT_RAW;
break;
case FMT_COMPRESS:
+ desc = compressionOpts;
mode = TCL_ZLIB_STREAM_DEFLATE;
+ format = TCL_ZLIB_FORMAT_ZLIB;
+ break;
case FMT_DECOMPRESS:
+ desc = expansionOpts;
+ mode = TCL_ZLIB_STREAM_INFLATE;
format = TCL_ZLIB_FORMAT_ZLIB;
break;
case FMT_GZIP:
+ desc = gzipOpts;
mode = TCL_ZLIB_STREAM_DEFLATE;
+ format = TCL_ZLIB_FORMAT_GZIP;
+ break;
case FMT_GUNZIP:
+ desc = expansionOpts;
+ mode = TCL_ZLIB_STREAM_INFLATE;
format = TCL_ZLIB_FORMAT_GZIP;
break;
}
- if (objc == 4) {
- if (Tcl_GetIntFromObj(interp, objv[3],
- (int *) &level) != TCL_OK) {
+
+ for (i=3 ; i<objc ; i+=2) {
+ if (Tcl_GetIndexFromObjStruct(interp, objv[i], desc,
+ sizeof(OptDescriptor), "option", 0, &option) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ *desc[option].valueVar = objv[i+1];
+
+ /*
+ * Drop the cache on the option name; table address not constant.
+ */
+
+ TclFreeIntRep(objv[i]);
+ }
+
+ level = Z_DEFAULT_COMPRESSION;
+ if (levelObj != NULL) {
+ if (Tcl_GetIntFromObj(interp, levelObj, &level) != TCL_OK) {
return TCL_ERROR;
}
if (level < 0 || level > 9) {
goto badLevel;
}
- } else {
- level = Z_DEFAULT_COMPRESSION;
}
- if (Tcl_ZlibStreamInit(interp, mode, format, level, NULL,
+
+ if (Tcl_ZlibStreamInit(interp, mode, format, level, gzipHeaderObj,
&zh) != TCL_OK) {
return TCL_ERROR;
}
+ if (compDictObj != NULL) {
+ ZlibStreamHandle *zshPtr = (ZlibStreamHandle *) zh;
+
+ zshPtr->compDictObj = compDictObj;
+ Tcl_IncrRefCount(compDictObj);
+ }
Tcl_SetObjResult(interp, Tcl_ZlibStreamGetCommandName(zh));
return TCL_OK;
+ }
case CMD_PUSH: { /* push mode channel options...
* -> channel */
Tcl_Channel chan;
@@ -1982,7 +2170,7 @@ ZlibCmd(
}
if (ZlibStackChannelTransform(interp, mode, format, level, chan,
- headerObj) == NULL) {
+ headerObj, NULL) == NULL) {
return TCL_ERROR;
}
Tcl_SetObjResult(interp, objv[3]);
@@ -2303,12 +2491,10 @@ ZlibTransformClose(
/* TODO: is this the right way to do errors on close?
* Note: when close is called from FinalizeIOSubsystem
* then interp may be NULL */
- if (!TclInThreadExit()) {
- if (interp) {
- Tcl_AppendResult(interp,
- "error while finalizing file: ",
- Tcl_PosixError(interp), NULL);
- }
+ if (!TclInThreadExit() && interp) {
+ Tcl_AppendResult(interp,
+ "error while finalizing file: ",
+ Tcl_PosixError(interp), NULL);
}
result = TCL_ERROR;
break;
@@ -2360,6 +2546,12 @@ ZlibTransformInput(
}
while (1) {
e = inflate(&cd->inStream, flush);
+ if (e == Z_NEED_DICT && cd->compDictObj) {
+ e = SetInflateDictionary(&cd->inStream, cd->compDictObj);
+ if (e == Z_OK) {
+ continue;
+ }
+ }
if ((e == Z_STREAM_END) || (e==Z_OK && cd->inStream.avail_out==0)) {
return toRead - cd->inStream.avail_out;
}
@@ -2473,9 +2665,13 @@ ZlibTransformSetOption( /* not used */
ZlibChannelData *cd = instanceData;
Tcl_DriverSetOptionProc *setOptionProc =
Tcl_ChannelSetOptionProc(Tcl_GetChannelType(cd->parent));
- static const char *chanOptions = "flush";
+ static const char *chanOptions = "dictionary flush";
int haveFlushOpt = (cd->mode == TCL_ZLIB_STREAM_DEFLATE);
+ if (optionName && strcmp(optionName, "-dictionary") == 0) {
+ // TODO dictionary option
+ }
+
if (haveFlushOpt && optionName && strcmp(optionName, "-flush") == 0) {
int flushType;
@@ -2539,7 +2735,7 @@ ZlibTransformGetOption(
ZlibChannelData *cd = instanceData;
Tcl_DriverGetOptionProc *getOptionProc =
Tcl_ChannelGetOptionProc(Tcl_GetChannelType(cd->parent));
- static const char *chanOptions = "checksum header";
+ static const char *chanOptions = "checksum dictionary header";
/*
* The "crc" option reports the current CRC (calculated with the Adler32
@@ -2567,6 +2763,10 @@ ZlibTransformGetOption(
}
}
+ if (optionName == NULL || strcmp(optionName, "-dictionary") == 0) {
+ // TODO dictionary option
+ }
+
/*
* The "header" option, which is only valid on inflating gzip channels,
* reports the header that has been read from the start of the stream.
@@ -2725,9 +2925,12 @@ ZlibStackChannelTransform(
int level, /* What compression level to use. Ignored for
* decompressing transforms. */
Tcl_Channel channel, /* The channel to attach to. */
- Tcl_Obj *gzipHeaderDictPtr) /* A description of header to use, or NULL to
+ Tcl_Obj *gzipHeaderDictPtr, /* A description of header to use, or NULL to
* use a default. Ignored if not compressing
* to produce gzip-format data. */
+ Tcl_Obj *compDictObj) /* Byte-array object containing compression
+ * dictionary (not dictObj!) to use if
+ * necessary. */
{
ZlibChannelData *cd = ckalloc(sizeof(ZlibChannelData));
Tcl_Channel chan;
@@ -2744,11 +2947,9 @@ ZlibStackChannelTransform(
if (format == TCL_ZLIB_FORMAT_GZIP || format == TCL_ZLIB_FORMAT_AUTO) {
if (mode == TCL_ZLIB_STREAM_DEFLATE) {
if (gzipHeaderDictPtr) {
- int dummy = 0;
-
cd->flags |= OUT_HEADER;
if (GenerateHeader(interp, gzipHeaderDictPtr, &cd->outHeader,
- &dummy) != TCL_OK) {
+ NULL) != TCL_OK) {
goto error;
}
}
@@ -2763,6 +2964,12 @@ ZlibStackChannelTransform(
}
}
+ if (compDictObj != NULL) {
+ cd->compDictObj = Tcl_DuplicateObj(compDictObj);
+ Tcl_IncrRefCount(cd->compDictObj);
+ Tcl_GetByteArrayFromObj(cd->compDictObj, NULL);
+ }
+
if (format == TCL_ZLIB_FORMAT_RAW) {
wbits = WBITS_RAW;
} else if (format == TCL_ZLIB_FORMAT_ZLIB) {
@@ -2806,6 +3013,12 @@ ZlibStackChannelTransform(
goto error;
}
}
+ if (cd->compDictObj) {
+ e = SetDeflateDictionary(&cd->outStream, cd->compDictObj);
+ if (e != Z_OK) {
+ goto error;
+ }
+ }
}
chan = Tcl_StackChannel(interp, &zlibChannelType, cd,
@@ -2827,6 +3040,9 @@ ZlibStackChannelTransform(
ckfree(cd->outBuffer);
deflateEnd(&cd->outStream);
}
+ if (cd->compDictObj) {
+ Tcl_DecrRefCount(cd->compDictObj);
+ }
ckfree(cd);
return NULL;
}
@@ -2975,6 +3191,13 @@ Tcl_ZlibAdler32(
{
return 0;
}
+
+void *
+Tcl_ZlibStreamGetZstreamp(
+ Tcl_ZlibStream zshandle)
+{
+ return NULL;
+}
#endif /* HAVE_ZLIB */
/*