summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog1
-rw-r--r--doc/TclZlib.3148
-rw-r--r--doc/zlib.n40
-rw-r--r--generic/tclZlib.c169
4 files changed, 296 insertions, 62 deletions
diff --git a/ChangeLog b/ChangeLog
index 7f57ec1..6122e8b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,6 @@
2008-12-13 Donal K. Fellows <dkf@users.sf.net>
+ * doc/TclZlib.3: Basic documentation of the C-level API.
* doc/zlib.n: Substantially improve documentation of Tcl-level API.
* generic/tclZlib.c (ZlibCmd): Flesh out the argument parsing for the
command to integrate with channels.
diff --git a/doc/TclZlib.3 b/doc/TclZlib.3
new file mode 100644
index 0000000..103f2e8
--- /dev/null
+++ b/doc/TclZlib.3
@@ -0,0 +1,148 @@
+'\"
+'\" Copyright (c) 2008 Donal K. Fellows
+'\"
+'\" See the file "license.terms" for information on usage and redistribution
+'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+'\"
+'\" RCS: @(#) $Id: TclZlib.3,v 1.1 2008/12/13 17:36:34 dkf Exp $
+'\"
+.so man.macros
+.TH TclZlib 3 8.6 Tcl "Tcl Built-In Commands"
+.BS
+'\" Note: do not modify the .SH NAME line immediately below!
+.SH NAME
+Tcl_ZlibAdler32, Tcl_ZlibCRC32, Tcl_ZlibDeflate, Tcl_ZlibInflate, Tcl_ZlibStreamAdler32, Tcl_ZlibStreamClose, Tcl_ZlibStreamEof, Tcl_ZlibStreamGet, Tcl_ZlibStreamGetCommandName, Tcl_ZlibStreamInit, Tcl_ZlibStreamPut \- compression and decompression functions
+.SH SYNOPSIS
+.nf
+#include <tcl.h>
+.sp
+Tcl_Obj *
+\fBTcl_ZlibDeflate\fR(\fIinterp, format, dataObj, level, dictObj\fR)
+.sp
+Tcl_Obj *
+\fBTcl_ZlibInflate\fR(\fIinterp, format, dataObj, dictObj\fR)
+.sp
+unsigned int
+\fBTcl_ZlibCRC32\fR(\fIinitValue, bytes, length\fR)
+.sp
+unsigned int
+\fBTcl_ZlibAdler32\fR(\fIinitValue, bytes, length\fR)
+.sp
+int
+\fBTcl_ZlibStreamInit\fR(\fIinterp, mode, format, level, dictObj, zshandlePtr\fR)
+.sp
+Tcl_Obj *
+\fBTcl_ZlibStreamGetCommandName\fR(\fIzshandle\fR)
+.sp
+int
+\fBTcl_ZlibStreamEof\fR(\fIzshandle\fR)
+.sp
+int
+\fBTcl_ZlibStreamClose\fR(\fIzshandle\fR)
+.sp
+int
+\fBTcl_ZlibStreamAdler32\fR(\fIzshandle\fR)
+.sp
+int
+\fBTcl_ZlibStreamPut\fR(\fIzshandle, dataObj, flush\fR)
+.sp
+int
+\fBTcl_ZlibStreamGet\fR(\fIzshandle, dataObj, count\fR)
+.fi
+.SH ARGUMENTS
+.AS Tcl_ZlibStream *zshandlePtr out
+.AP Tcl_Interp *interp in
+The interpreter to store resulting compressed or uncompressed data in. Also
+where any error messages are written.
+.AP int format in
+What format of compressed data to work with. Must be one of
+\fBTCL_ZLIB_FORMAT_ZLIB\fR for zlib-format data, \fBTCL_ZLIB_FORMAT_GZIP\fR
+for gzip-format data, or \fBTCL_ZLIB_FORMAT_RAW\fR for raw compressed data. In
+addition, for decompression only, \fBTCL_ZLIB_FORMAT_AUTO\fR may also be
+chosen which can automatically detect whether the compressed data was in zlib
+or gzip format.
+.AP Tcl_Obj *dataObj in/out
+A byte-array object containing the data to be compressed or decompressed, or
+which is set to the data extracted from the stream when passed to
+\fBTcl_ZlibStreamGet\fR.
+.AP int level in
+What level of compression to use. Should be a number from 0 to 9 or one of the
+following: \fBTCL_ZLIB_COMPRESS_NONE\fR for no compression,
+\fBTCL_ZLIB_COMPRESS_FAST\fR for fast but inefficient compression,
+\fBTCL_ZLIB_COMPRESS_BEST\fR for slow but maximal compression, or
+\fBTCL_ZLIB_COMPRESS_DEFAULT\fR for the level recommended by the zlib library.
+.AP Tcl_Obj *dictObj in/out
+A dictionary that contains, or which will be updated to contain, a description
+of the gzip header associated with the compressed data. Only useful when the
+\fIformat\fR is \fBTCL_ZLIB_FORMAT_GZIP\fR or \fBTCL_ZLIB_FORMAT_AUTO\fR. If
+a NULL is passed, a default header will be used on compression and the header
+will be ignored (apart from integrity checks) on decompression.
+.AP "unsigned int" initValue in
+The initial value for the checksum algorithm.
+.AP "unsigned char" *bytes in
+An array of bytes to run the checksum algorithm over, or NULL to get the
+recommended initial value for the checksum algorithm.
+.AP int length in
+The number of bytes in the array.
+.AP int mode in
+What mode to operate the stream in. Should be either
+\fBTCL_ZLIB_STREAM_DEFLATE\fR for a compressing stream or
+\fBTCL_ZLIB_STREAM_INFLATE\fR for a decompressing stream.
+.AP Tcl_ZlibStream *zshandlePtr out
+A pointer to a variable in which to write the abstract token for the stream
+upon successful creation.
+.AP Tcl_ZlibStream zshandle in
+The abstract token for the stream to operate on.
+.AP int flush in
+Whether and how to flush the stream after writing the data to it. Must be one
+of: \fBTCL_ZLIB_NO_FLUSH\fR if no flushing is to be done, \fBTCL_ZLIB_FLUSH\fR
+if the currently compressed data must be made available for access using
+\fBTcl_ZlibStreamGet\fR, \fBTCL_ZLIB_FULLFLUSH\fR if the stream must be put
+into a state where the decompressor can recover from on corruption, or
+\fBTCL_ZLIB_FINALIZE\fR to ensure that the stream is finished and that any
+trailer demanded by the format is written.
+.AP int count in
+The maximum number of bytes to get from the stream, or -1 to get all remaining
+bytes from the stream's buffers.
+.BE
+.SH DESCRIPTION
+These functions form the interface from the Tcl library to the Zlib
+library by Jean-loup Gailly and Mark Adler.
+.PP
+\fBTcl_ZlibDeflate\fR and \fBTcl_ZlibInflate\fR compress and decompress data
+respectively. Upon success, they leave the resulting compressed or
+decompressed data in a byte-array object that is the Tcl interpreter's
+result. Note that the \fIdictObj\fR parameter is only used when the
+\fIformat\fR parameter is \fBTCL_ZLIB_FORMAT_GZIP\fR or
+\fBTCL_ZLIB_FORMAT_AUTO\fR.
+.PP
+\fBTcl_ZlibAdler32\fR and \fBTcl_ZlibCRC32\fR compute checksums on arrays of
+bytes. Typical usage is:
+.PP
+.CS
+checksum = Tcl_ZlibCRC32(Tcl_ZlibCRC32(0,NULL,0), data, length);
+.CE
+.PP
+\fBTcl_ZlibStreamInit\fR creates a compressing or decompressing stream that is
+linked to a Tcl command, according to its arguments, and provides an abstract
+token for the stream; \fBTcl_ZlibStreamGetCommandName\fR returns the name of
+that command given the stream token. Once a stream has been constructed,
+\fBTcl_ZlibStreamPut\fR is used to add data to the stream and
+\fBTcl_ZlibStreamGet\fR is used to retrieve data from the stream after
+processing. \fBTcl_ZlibStreamAdler32\fR returns the checksum computed over the
+uncompressed data, and \fBTcl_ZlibStreamEof\fR returns whether the end of the
+uncompressed data has been reached. Finally, \fBTcl_ZlibStreamClose\fR will
+clean up the stream and delete the associated command: using
+\fBTcl_DeleteCommand\fR on the stream's command is equivalent.
+.SH "PORTABILITY NOTES"
+These functions will fail gracefully if Tcl is not linked with the zlib
+library.
+.SH "SEE ALSO"
+Tcl_NewByteArrayObj(3), zlib(n)
+'\"Tcl_StackChannel(3)
+.SH "KEYWORDS"
+compress, decompress, deflate, gzip, inflate
+'\" Local Variables:
+'\" mode: nroff
+'\" fill-column: 78
+'\" End:
diff --git a/doc/zlib.n b/doc/zlib.n
index 8937e78..b010eb4 100644
--- a/doc/zlib.n
+++ b/doc/zlib.n
@@ -4,7 +4,7 @@
'\" See the file "license.terms" for information on usage and redistribution
'\" of this file, and for a DISCLAIMER OF ALL WARRANTIES.
'\"
-'\" RCS: @(#) $Id: zlib.n,v 1.3 2008/12/13 09:19:06 dkf Exp $
+'\" RCS: @(#) $Id: zlib.n,v 1.4 2008/12/13 17:36:34 dkf Exp $
'\"
.so man.macros
.TH zlib n 8.6 Tcl "Tcl Built-In Commands"
@@ -130,7 +130,43 @@ is to be used to receive the data.
.TP
\fBzlib push\fI mode channel\fR ?\fIoptions ...\fR
.
+Pushes a compressing or decompressing transformation onto the channel
+\fIchannel\fR. The \fImode\fR argument determines what type of transformation
+is pushed; the following are supported:
+.RS
+.TP
+\fBcompress\fR
+.
+The transformation will be a compressing transformation that produces
+zlib-format data on \fIchannel\fR, which must be writable.
+.TP
+\fBdecompress\fR
+.
+The transformation will be a decompressing transformation that reads
+zlib-format data from \fIchannel\R, which must be readable.
+.TP
+\fBdeflate\fR
+.
+The transformation will be a compressing transformation that produces raw
+compressed data on \fIchannel\fR, which must be writable.
+.TP
+\fBgunzip\fR
+.
+The transformation will be a decompressing transformation that reads
+gzip-format data from \fIchannel\R, which must be readable.
+.TP
+\fBgzip\fR
+.
+The transformation will be a compressing transformation that produces
+gzip-format data on \fIchannel\fR, which must be writable.
+.TP
+\fBinflate\fR
+.
+The transformation will be a decompressing transformation that reads raw
+compressed data from \fIchannel\R, which must be readable.
+.PP
\fITODO: not yet implemented!\fR
+.RE
.TP
\fBzlib stream\fI mode\fR ?\fIlevel\fR?
.
@@ -302,7 +338,7 @@ set compData [$\fIstrm \fBget\fR]
$\fIstrm \fBclose\fR
.CE
.SH "SEE ALSO"
-binary(n), chan(n), encoding(n)
+binary(n), chan(n), encoding(n), Tcl_ZlibDeflate(3)
.br
RFC1950 \- RFC1952
.SH "KEYWORDS"
diff --git a/generic/tclZlib.c b/generic/tclZlib.c
index 6f40744..00d54e6 100644
--- a/generic/tclZlib.c
+++ b/generic/tclZlib.c
@@ -13,7 +13,7 @@
* See the file "license.terms" for information on usage and redistribution of
* this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclZlib.c,v 1.9 2008/12/13 09:19:06 dkf Exp $
+ * RCS: @(#) $Id: tclZlib.c,v 1.10 2008/12/13 17:36:34 dkf Exp $
*/
#include "tclInt.h"
@@ -79,8 +79,6 @@ static int ChanGetOption(ClientData instanceData,
static void ChanWatch(ClientData instanceData, int mask);
static int ChanGetHandle(ClientData instanceData, int direction,
ClientData *handlePtr);
-static int ChanClose2(ClientData instanceData,
- Tcl_Interp *interp, int flags);
static int ChanBlockMode(ClientData instanceData, int mode);
static int ChanFlush(ClientData instanceData);
static int ChanHandler(ClientData instanceData,
@@ -94,10 +92,10 @@ static const Tcl_ChannelType zlibChannelType = {
ChanOutput,
NULL, /* seekProc */
NULL, /* ChanSetOption, */
- NULL, /* ChanGetOption, */
+ ChanGetOption,
ChanWatch,
ChanGetHandle,
- NULL, /* ChanClose2, */
+ NULL, /* close2Proc, */
ChanBlockMode,
ChanFlush,
ChanHandler,
@@ -113,12 +111,19 @@ typedef struct {
/* Zlib specific channel state */
int inFormat;
int outFormat;
- z_stream instream;
- z_stream outstream;
- char *inbuffer;
+ z_stream inStream;
+ z_stream outStream;
+ char *inBuffer;
int inAllocated, inUsed, inPos;
- char *outbuffer;
+ char *outBuffer;
int outAllocated, outUsed, outPos;
+
+ gz_header inHeader;
+ char inFilenameBuffer[MAXPATHLEN];
+ char inCommentBuffer[256];
+ gz_header outHeader;
+ Tcl_DString outFilenameDString;
+ Tcl_DString outCommentDString;
} ZlibChannelData;
/* Flag values */
@@ -225,6 +230,7 @@ GenerateHeader(
if (GetValue(interp, dictObj, "comment", &value) != TCL_OK) {
return TCL_ERROR;
} else if (value != NULL) {
+ /* TODO: Convert to external */
headerPtr->comment = (Bytef *) Tcl_GetStringFromObj(value, &extra);
*extraSizePtr += extra;
}
@@ -239,6 +245,7 @@ GenerateHeader(
if (GetValue(interp, dictObj, "filename", &value) != TCL_OK) {
return TCL_ERROR;
} else if (value != NULL) {
+ /* TODO: Convert to external */
headerPtr->name = (Bytef *) Tcl_GetStringFromObj(value, &extra);
*extraSizePtr += extra;
}
@@ -308,11 +315,13 @@ ExtractHeader(
Tcl_Obj *dictObj) /* The dictionary to store in. */
{
if (headerPtr->comment != Z_NULL) {
+ /* TODO: Convert from external */
SetValue(dictObj, "comment",
Tcl_NewStringObj((char *) headerPtr->comment, -1));
}
SetValue(dictObj, "crc", Tcl_NewBooleanObj(headerPtr->hcrc));
if (headerPtr->name != Z_NULL) {
+ /* TODO: Convert from external */
SetValue(dictObj, "filename",
Tcl_NewStringObj((char *) headerPtr->name, -1));
}
@@ -799,7 +808,7 @@ Tcl_ZlibStreamPut(
obj = Tcl_NewByteArrayObj((unsigned char *) dataTmp,
outSize - zsh->stream.avail_out);
/*
- * Now append the compressed data to the outbuffer.
+ * Now append the compressed data to the outData list.
*/
Tcl_ListObjAppendElement(zsh->interp, zsh->outData, obj);
@@ -825,14 +834,14 @@ Tcl_ZlibStreamPut(
outSize - zsh->stream.avail_out);
/*
- * Now append the compressed data to the outbuffer.
+ * Now append the compressed data to the outData list.
*/
Tcl_ListObjAppendElement(zsh->interp, zsh->outData, obj);
}
} else {
/*
- * This is easy. Just append to inbuffer.
+ * This is easy. Just append to the inData list.
*/
Tcl_ListObjAppendElement(zsh->interp, zsh->inData, data);
@@ -1760,7 +1769,11 @@ ZlibCmd(
}
}
+#if 0
+ Tcl_ZlibStackChannel(interp, 0/*inFormat*/,level,0/*outFormat*/,level, chan, headerObj);
+#else
Tcl_AppendResult(interp, "not yet implemented", NULL);
+#endif
break;
}
};
@@ -1968,35 +1981,33 @@ ChanClose(
Tcl_Interp *interp)
{
ZlibChannelData *cd = instanceData;
- Tcl_Channel parent;
+ Tcl_Channel parent = Tcl_GetStackedChannel(cd->channel);
int e;
- parent = Tcl_GetStackedChannel(cd->channel);
-
if (cd->inFormat != ZLIB_PASSTHROUGH) {
- if (cd->inFormat && ZLIB_INFLATE) {
- e = inflateEnd(&cd->instream);
+ if (cd->inFormat & ZLIB_INFLATE) {
+ e = inflateEnd(&cd->inStream);
} else {
- e = deflateEnd(&cd->instream);
+ e = deflateEnd(&cd->inStream);
}
}
if (cd->outFormat != ZLIB_PASSTHROUGH) {
- if (cd->outFormat && ZLIB_INFLATE) {
- e = inflateEnd(&cd->outstream);
+ if (cd->outFormat & ZLIB_INFLATE) {
+ e = inflateEnd(&cd->outStream);
} else {
- e = deflateEnd(&cd->outstream);
+ e = deflateEnd(&cd->outStream);
}
}
- if (cd->inbuffer) {
- ckfree(cd->inbuffer);
- cd->inbuffer = NULL;
+ if (cd->inBuffer) {
+ ckfree(cd->inBuffer);
+ cd->inBuffer = NULL;
}
- if (cd->outbuffer) {
- ckfree(cd->outbuffer);
- cd->outbuffer = NULL;
+ if (cd->outBuffer) {
+ ckfree(cd->outBuffer);
+ cd->outBuffer = NULL;
}
return TCL_OK;
}
@@ -2009,7 +2020,16 @@ ChanInput(
int *errorCodePtr)
{
ZlibChannelData *cd = instanceData;
+ Tcl_Channel parent = Tcl_GetStackedChannel(cd->channel);
+ Tcl_DriverInputProc *inProc =
+ Tcl_ChannelInputProc(Tcl_GetChannelType(parent));
+ if (!(cd->flags & TCL_READABLE)) {
+ return inProc(Tcl_GetChannelInstanceData(parent), buf, toRead,
+ errorCodePtr);
+ }
+
+ // TODO
return TCL_OK;
}
@@ -2021,7 +2041,16 @@ ChanOutput(
int *errorCodePtr)
{
ZlibChannelData *cd = instanceData;
+ Tcl_Channel parent = Tcl_GetStackedChannel(cd->channel);
+ Tcl_DriverOutputProc *outProc =
+ Tcl_ChannelOutputProc(Tcl_GetChannelType(parent));
+
+ if (!(cd->flags & TCL_WRITABLE)) {
+ return outProc(Tcl_GetChannelInstanceData(parent), buf, toWrite,
+ errorCodePtr);
+ }
+ // TODO
return TCL_OK;
}
@@ -2046,12 +2075,42 @@ ChanSetOption( /* not used */
}
static int
-ChanGetOption( /* not used */
+ChanGetOption(
ClientData instanceData,
Tcl_Interp *interp,
const char *optionName,
Tcl_DString *dsPtr)
{
+ ZlibChannelData *cd = instanceData;
+ Tcl_Channel parent = Tcl_GetStackedChannel(cd->channel);
+ Tcl_DriverSetOptionProc *getOptionProc =
+ Tcl_ChannelGetOptionProc(Tcl_GetChannelType(parent));
+
+ if (strcmp(optionName, "-crc") == 0) {
+ uLong crc;
+ char buf[12];
+
+ if (cd->flags & TCL_WRITABLE) {
+ crc = cd->outStream.adler;
+ } else {
+ crc = cd->inStream.adler;
+ }
+
+ sprintf(buf, "0x%lx", crc);
+ Tcl_DStringAppend(dsPtr, buf, -1);
+ return TCL_OK;
+ }
+
+ if (getOptionProc && getOptionProc(Tcl_GetChannelInstanceData(parent),
+ interp, optionName, dsPtr) != TCL_OK) {
+ return TCL_ERROR;
+ } else if (optionName != NULL) {
+ return TCL_ERROR;
+ }
+
+ if (optionName == NULL) {
+ Tcl_DStringAppendElement(dsPtr, "-crc");
+ }
return TCL_OK;
}
@@ -2069,20 +2128,10 @@ ChanGetHandle(
int direction,
ClientData *handlePtr)
{
- /*
- * No such thing as an OS handle for Zlib.
- */
-
- return 0;
-}
+ ZlibChannelData *cd = instanceData;
+ Tcl_Channel parent = Tcl_GetStackedChannel(cd->channel);
-static int
-ChanClose2( /* not used */
- ClientData instanceData,
- Tcl_Interp *interp,
- int flags)
-{
- return TCL_OK;
+ return Tcl_GetChannelHandle(parent, direction, handlePtr);
}
static int
@@ -2165,29 +2214,29 @@ Tcl_ZlibStackChannel(
cd->inFormat = inFormat;
cd->outFormat = outFormat;
- cd->instream.zalloc = 0;
- cd->instream.zfree = 0;
- cd->instream.opaque = 0;
- cd->instream.avail_in = 0;
- cd->instream.next_in = NULL;
- cd->instream.avail_out = 0;
- cd->instream.next_out = NULL;
-
- cd->outstream.zalloc = 0;
- cd->outstream.zfree = 0;
- cd->outstream.opaque = 0;
- cd->outstream.avail_in = 0;
- cd->outstream.next_in = NULL;
- cd->outstream.avail_out = 0;
- cd->outstream.next_out = NULL;
+ cd->inStream.zalloc = 0;
+ cd->inStream.zfree = 0;
+ cd->inStream.opaque = 0;
+ cd->inStream.avail_in = 0;
+ cd->inStream.next_in = NULL;
+ cd->inStream.avail_out = 0;
+ cd->inStream.next_out = NULL;
+
+ cd->outStream.zalloc = 0;
+ cd->outStream.zfree = 0;
+ cd->outStream.opaque = 0;
+ cd->outStream.avail_in = 0;
+ cd->outStream.next_in = NULL;
+ cd->outStream.avail_out = 0;
+ cd->outStream.next_out = NULL;
if (inFormat != ZLIB_PASSTHROUGH) {
if (inFormat & ZLIB_INFLATE) {
/* Initialize for Inflate */
- e = inflateInit2(&cd->instream, inwbits);
+ e = inflateInit2(&cd->inStream, inwbits);
} else {
/* Initialize for Deflate */
- e = deflateInit2(&cd->instream, inLevel, Z_DEFLATED, inwbits,
+ e = deflateInit2(&cd->inStream, inLevel, Z_DEFLATED, inwbits,
MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
}
}
@@ -2195,10 +2244,10 @@ Tcl_ZlibStackChannel(
if (outFormat != ZLIB_PASSTHROUGH) {
if (outFormat && ZLIB_INFLATE) {
/* Initialize for Inflate */
- e = inflateInit2(&cd->outstream, outwbits);
+ e = inflateInit2(&cd->outStream, outwbits);
} else {
/* Initialize for Deflate */
- e = deflateInit2(&cd->outstream, outLevel, Z_DEFLATED, outwbits,
+ e = deflateInit2(&cd->outStream, outLevel, Z_DEFLATED, outwbits,
MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
}
}