summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog18
-rw-r--r--doc/zlib.n249
-rw-r--r--generic/tclZlib.c127
-rw-r--r--tests/zlib.test4
4 files changed, 354 insertions, 44 deletions
diff --git a/ChangeLog b/ChangeLog
index a322c94..dac080a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,9 +1,17 @@
+2008-12-13 Donal K. Fellows <dkf@users.sf.net>
+
+ * 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.
+
2008-12-12 Jan Nijtmans <nijtmans@users.sf.net>
- * generic/tclZlib.c change PATH_MAX to MAXPATHLEN (msvc doesn't have PATH_MAX)
+ * generic/tclZlib.c (Tcl_ZlibInflate): Change PATH_MAX to MAXPATHLEN,
+ since MSVC doesn't have PATH_MAX.
+
* doc/clock.n: Document new DST fallback rules.
- * library/clock.tcl (ProcessPosixTimeZone): Fix time change
- in Eastern Europe (not 3:00 but 4:00 local time) [Bug 2207436]
+ * library/clock.tcl (ProcessPosixTimeZone): Fix time change in Eastern
+ Europe (not 3:00 but 4:00 local time). [Bug 2207436]
2008-12-12 Donal K. Fellows <dkf@users.sf.net>
@@ -27,8 +35,8 @@
TIP #322 IMPLEMENTATION
- * doc/NRE.3 (new file): Added documentation of the published
- API for Non-Recursive Evaluation (NRE).
+ * doc/NRE.3 (new file): Added documentation of the published API for
+ Non-Recursive Evaluation (NRE).
2008-12-11 Jan Nijtmans <nijtmans@users.sf.net>
diff --git a/doc/zlib.n b/doc/zlib.n
index d1f1eed..8937e78 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.2 2008/12/12 16:34:45 dkf Exp $
+'\" RCS: @(#) $Id: zlib.n,v 1.3 2008/12/13 09:19:06 dkf Exp $
'\"
.so man.macros
.TH zlib n 8.6 Tcl "Tcl Built-In Commands"
@@ -19,35 +19,69 @@ zlib \- compression and decompression operations
.BE
.SH DESCRIPTION
.PP
-The \fBzlib\fR command provides access to the compression facilities of the
-Zlib library. It has the following subcommands
-.TP
-\fBzlib adler32\fI string\fR ?\fIinitValue\fR?
-.
-Compute a checksum of binary string \fIstring\fR using the Adler-32 algorithm.
-If given, \fIinitValue\fR is used to initialize the checksum engine.
+The \fBzlib\fR command provides access to the compression and check-summing
+facilities of the Zlib library by Jean-loup Gailly and Mark Adler. It has the
+following subcommands.
+.SS "COMPRESSION SUBCOMMANDS"
.TP
\fBzlib compress\fI string\fR ?\fIlevel\fR?
.
-.TP
-\fBzlib crc32\fI string\fR ?\fIinitValue\fR?
-.
-Compute a checksum of binary string \fIstring\fR using the CRC-32 algorithm.
-If given, \fIinitValue\fR is used to initialize the checksum engine.
+Returns the zlib-format compressed binary data of the binary string in
+\fIstring\fR. If present, \fIlevel\fR gives the compression level to use (from
+0, which is uncompressed, to 9, maximally compressed).
.TP
\fBzlib decompress\fI string\fR ?\fIbufferSize\fR?
.
+Returns the uncompressed version of the raw compressed binary data in
+\fIstring\fR. If present, \fIbufferSize\fR is a hint as to what size of buffer
+is to be used to receive the data.
.TP
\fBzlib deflate\fI string\fR ?\fIlevel\fR?
.
+Returns the raw compressed binary data of the binary string in \fIstring\fR.
+If present, \fIlevel\fR gives the compression level to use (from 0, which is
+uncompressed, to 9, maximally compressed).
.TP
\fBzlib gunzip\fI string\fR ?\fB\-headerVar \fIvarName\fR?
.
Return the uncompressed contents of binary string \fIstring\fR, which must
have been in gzip format. If \fB\-headerVar\fR is given, store a dictionary
describing the contents of the gzip header in the variable called
-\fIvarName\fR.
-'\" TODO: describe dict fields
+\fIvarName\fR. The keys of the dictionary that may be present are:
+.RS
+.TP
+\fBcomment\fR
+.
+The comment field from the header, if present.
+.TP
+\fBcrc\fR
+.
+A boolean value describing whether a CRC of the header is computed.
+.TP
+\fBfilename\fR
+.
+The filename field from the header, if present.
+.TP
+\fBos\fR
+.
+The operating system type code field from the header (if not the
+QW unknown
+value). See RFC 1952 for the meaning of these codes.
+.TP
+\fBsize\fR
+.
+The size of the uncompressed data.
+.TP
+\fBtime\fR
+.
+The time field from the header if non-zero, expected to be time that the file
+named by the \fBfilename\fR field was modified. Suitable for use with \fBclock
+format\fR.
+.TP
+\fBtype\fR
+.
+The type of the uncompressed data (\fBbinary\fR or \fBtext\fR) if known.
+.RE
.TP
\fBzlib gzip\fI string\fR ?\fB\-level \fIlevel\fR? ?\fB\-header \fIdict\fR?
.
@@ -55,61 +89,222 @@ Return the compressed contents of binary string \fIstring\fR in gzip format.
If \fB\-level\fR is given, \fIlevel\fR gives the compression level to use
(from 0, which is uncompressed, to 9, maximally compressed). If \fB\-header\fR
is given, \fIdict\fR is a dictionary containing values used for the gzip
-header.
-'\" TODO: describe dict fields
+header. The following keys may be defined:
+.RS
+.TP
+\fBcomment\fR
+.
+Add the given comment to the header of the gzip-format data.
+.TP
+\fBcrc\fR
+.
+A boolean saying whether to compute a CRC of the header. Note that if the data
+is to be interchanged with the \fBgzip\fR program, a header CRC should
+\fInot\fR be computed.
+.TP
+\fBfilename\fR
+.
+The name of the file that the data to be compressed came from.
+.TP
+\fBos\fR
+.
+The operating system type code, which should be one of the values described in
+RFC 1952.
+.TP
+\fBtime\fR
+.
+The time that the file named in the \fBfilename\fR key was last modified. This
+will be in the same as is returned by \fBclock seconds\fR or \fBfile mtime\fR.
+.TP
+\fBtype\fR
+.
+The type of the data being compressed, being \fBbinary\fR or \fBtext\fR.
+.RE
.TP
\fBzlib inflate\fI string\fR ?\fIbufferSize\fR?
.
+Returns the uncompressed version of the raw compressed binary data in
+\fIstring\fR. If present, \fIbufferSize\fR is a hint as to what size of buffer
+is to be used to receive the data.
+.SS "STREAMING AND CHANNEL SUBCOMMANDS"
.TP
-\fBzlib stack\fI channel\fR
+\fBzlib push\fI mode channel\fR ?\fIoptions ...\fR
.
+\fITODO: not yet implemented!\fR
.TP
\fBzlib stream\fI mode\fR ?\fIlevel\fR?
.
Creates a streaming compression or decompression command based on the
\fImode\fR, and return the name of the command. For a description of how that
-command works, see \fBSTREAMING COMMAND\fR below. The following modes are
-supported:
+command works, see \fBSTREAMING INSTANCE COMMAND\fR below. The following modes
+are supported:
.RS
.TP
-\fBzlib stream compress\fR
+\fBzlib stream compress\fR ?\fIlevel\fR?
.
+The stream will be a compressing stream that produces zlib-format output,
+using compression level \fIlevel\fR (if specified) which will be an integer
+from 0 to 9.
.TP
\fBzlib stream decompress\fR
.
+The stream will be a decompressing stream that takes zlib-format input and
+produces uncompressed output.
.TP
-\fBzlib stream deflate\fR
+\fBzlib stream deflate\fR ?\fIlevel\fR?
.
+The stream will be a compressing stream that produces raw output, using
+compression level \fIlevel\fR (if specified) which will be an integer from 0
+to 9.
.TP
\fBzlib stream gunzip\fR
.
+The stream will be a decompressing stream that takes gzip-format input and
+produces uncompressed output.
.TP
-\fBzlib stream gzip\fR
+\fBzlib stream gzip\fR ?\fIlevel\fR?
.
+The stream will be a compressing stream that produces gzip-format output,
+using compression level \fIlevel\fR (if specified) which will be an integer
+from 0 to 9.
+'\" TODO: Header dictionary!
.TP
\fBzlib stream inflate\fR
.
+The stream will be a decompressing stream that takes raw compressed input and
+produces uncompressed output.
+.RE
+.SS "CHECKSUMMING SUBCOMMANDS"
+.TP
+\fBzlib adler32\fI string\fR ?\fIinitValue\fR?
+.
+Compute a checksum of binary string \fIstring\fR using the Adler-32 algorithm.
+If given, \fIinitValue\fR is used to initialize the checksum engine.
+.TP
+\fBzlib crc32\fI string\fR ?\fIinitValue\fR?
+.
+Compute a checksum of binary string \fIstring\fR using the CRC-32 algorithm.
+If given, \fIinitValue\fR is used to initialize the checksum engine.
+.SH "STREAMING INSTANCE COMMAND"
+.PP
+Streaming compression instance commands are produced by the \fBzlib stream\fR
+command. They are used by calling their \fBput\fR subcommand one or more times
+to load data in, and their \fBget\fR subcommand one or more times to extract
+the transformed data.
+.PP
+The full set of subcommands supported by a streaming instance command,
+\fIstream\fR, is as follows:
+.TP
+\fIstream \fBadd\fR ?\fIoption\fR? \Idata\fR
+.
+A short-cut for
+.QW "\fIstream \fBput \fIoption data\fR"
+followed by
+.QW "\fIstream \fBget\fR" .
+.TP
+\fIstream \fBadler32\fR
+.
+'\" Change name?
+Returns the checksum of the uncompressed data seen so far by this stream.
+.TP
+\fIstream \fBclose\fR
+.
+Deletes this stream and frees up all resources associated with it.
+.TP
+\fIstream \fBeof\fR
+.
+Returns a boolean indicating whether the end of the stream (as determined by
+the compressed data itself) has been reached. Not all formats support
+detection of the end of the stream.
+.TP
+\fIstream \fBfinalize\fR
+.
+A short-cut for
+.QW "\fIstream \fBput \-finalize {}\fR" .
+.TP
+\fIstream \fBflush\fR
+.
+A short-cut for
+.QW "\fIstream \fBput \-flush {}\fR" .
+.TP
+\fIstream \fBfullflush\fR
+.
+A short-cut for
+.QW "\fIstream \fBput \-fullflush {}\fR" .
+.TP
+\fIstream \fBget \fR?\fIcount\fR?
+.
+Return up to \fIcount\fR bytes from \fIstream\fR's internal buffers with the
+transformation applied. If \fIcount\fR is omitted, the entire contents of the
+buffers are returned.
+.TP
+\fIstream \fBput\fR ?\fIoption\fR? \fIdata\fR
+.
+Append the contents of the binary string \fIdata\fR to \fIstream\fR's internal
+buffers while applying the transformation. If present, \fIoption\fR must be
+one of the following (or an unambiguous prefix) which are used to modify the
+way in which the transformation is applied:
+.RS
+.TP
+\fB\-finalize\fR
+.
+Mark the stream as finished, ensuring that all bytes have been wholly
+compressed or decompressed. For gzip streams, this also ensures that the
+footer is written to the stream. The stream will need to be reset before
+having more data written to it after this, though data can still be read out
+of the stream with the \fBget\fR subcommand.
+.TP
+\fB\-flush\fR
+.
+Ensure that a decompressor consuming the bytes that the current (compressing)
+stream is producing will be able to produce all the bytes that have been
+compressed so far, at some performance penalty.
+.TP
+\fB\-fullflush\fR
+.
+Ensure that not only can a decompressor handle all the bytes produced so far
+(as with \fB\-flush\fR above) but also that it can restart from this point if
+it detects that the stream is partially corrupt. This incurs a substantial
+performance penalty.
.RE
.TP
-\fBzlib unstack\fI channel\fR
+\fIstream \fBreset\fR
.
-Reverses the effects of \fBzlib stack\fR on the channel called \fIchannel\fR.
+Puts any stream, including those that have been finalized or that have reached
+eof, back into a state where it can process more data. Throws away all
+internally buffered data.
.SH EXAMPLES
.PP
To compress a Tcl string, it should be first converted to a particular charset
encoding since the \fBzlib\fR command always operates on binary strings.
.PP
.CS
-set compressed [\fBzlib deflate\fR [encoding convertto utf8 $string]]
+set binData [encoding convertto utf-8 $string]
+set compData [\fBzlib compress\fR $binData]
.CE
.PP
When converting back, it is also important to reverse the charset encoding:
.PP
.CS
-set string [encoding convertfrom utf8 [\fBzlib inflate\fR $compressed]]
+set binData [\fBzlib decompress\fR $compData]
+set string [encoding convertfrom utf-8 $binData]
+.CE
+.PP
+The compression operation from above can also be done with streams, which is
+especially helpful when you want to accumulate the data by stages:
+.PP
+.CS
+set strm [\fBzlib stream\fR compress]
+$\fIstrm \fBput\fR [encoding convertto utf-8 $string]
+# ...
+$\fIstrm \fBfinalize\fR
+set compData [$\fIstrm \fBget\fR]
+$\fIstrm \fBclose\fR
.CE
.SH "SEE ALSO"
binary(n), chan(n), encoding(n)
+.br
+RFC1950 \- RFC1952
.SH "KEYWORDS"
compress, decompress, deflate, gzip, inflate
'\" Local Variables:
diff --git a/generic/tclZlib.c b/generic/tclZlib.c
index 75aea90..6f40744 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.8 2008/12/12 21:43:35 nijtmans Exp $
+ * RCS: @(#) $Id: tclZlib.c,v 1.9 2008/12/13 09:19:06 dkf Exp $
*/
#include "tclInt.h"
@@ -1401,14 +1401,15 @@ ZlibCmd(
Byte *data;
Tcl_Obj *obj = Tcl_GetObjResult(interp);
Tcl_Obj *headerDictObj, *headerVarObj;
+ const char *extraInfoStr = NULL;
static const char *const commands[] = {
"adler32", "compress", "crc32", "decompress", "deflate", "gunzip",
- "gzip", "inflate", "stack", "stream", "unstack",
+ "gzip", "inflate", "push", "stream",
NULL
};
enum zlibCommands {
z_adler32, z_compress, z_crc32, z_decompress, z_deflate, z_gunzip,
- z_gzip, z_inflate, z_stack, z_stream, z_unstack
+ z_gzip, z_inflate, z_push, z_stream,
};
static const char *const stream_formats[] = {
"compress", "decompress", "deflate", "gunzip", "gzip", "inflate",
@@ -1521,6 +1522,7 @@ ZlibCmd(
return TCL_ERROR;
}
if (level < 0 || level > 9) {
+ extraInfoStr = "\n (in -level option)";
goto badLevel;
}
break;
@@ -1614,8 +1616,8 @@ ZlibCmd(
Tcl_WrongNumArgs(interp, 2, objv, "mode ?level?");
return TCL_ERROR;
}
- if (Tcl_GetIndexFromObj(interp, objv[2], stream_formats,
- "stream format", 0, &format) != TCL_OK) {
+ if (Tcl_GetIndexFromObj(interp, objv[2], stream_formats, "mode", 0,
+ &format) != TCL_OK) {
return TCL_ERROR;
}
mode = TCL_ZLIB_STREAM_INFLATE;
@@ -1639,7 +1641,6 @@ ZlibCmd(
if (objc == 4) {
if (Tcl_GetIntFromObj(interp, objv[3],
(int *) &level) != TCL_OK) {
- Tcl_AppendResult(interp, "level error: integer", NULL);
return TCL_ERROR;
}
if (level < 0 || level > 9) {
@@ -1650,21 +1651,127 @@ ZlibCmd(
}
if (Tcl_ZlibStreamInit(interp, mode, format, level, NULL,
&zh) != TCL_OK) {
- Tcl_AppendResult(interp, "stream init error: integer", NULL);
return TCL_ERROR;
}
Tcl_SetObjResult(interp, Tcl_ZlibStreamGetCommandName(zh));
return TCL_OK;
- case z_stack: /* stack */
- break;
- case z_unstack: /* unstack */
+ case z_push: { /* push mode channel options...*/
+ Tcl_Channel chan;
+ int chanMode;
+ static const char *pushOptions[] = {
+ "-header", "-headerVar", "-level", "-limit",
+ NULL
+ };
+ enum pushOptions {poHeader, poHeadVar, poLevel, poLimit};
+ Tcl_Obj *headerObj = NULL, *varObj = NULL;
+ int limit = 1, dummy;
+
+ if (objc < 4) {
+ Tcl_WrongNumArgs(interp, 2, objv, "mode channel ?options...?");
+ 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 f_deflate:
+ mode = TCL_ZLIB_STREAM_DEFLATE;
+ case f_inflate:
+ format = TCL_ZLIB_FORMAT_RAW;
+ break;
+ case f_compress:
+ mode = TCL_ZLIB_STREAM_DEFLATE;
+ case f_decompress:
+ format = TCL_ZLIB_FORMAT_ZLIB;
+ break;
+ case f_gzip:
+ mode = TCL_ZLIB_STREAM_DEFLATE;
+ case f_gunzip:
+ format = TCL_ZLIB_FORMAT_GZIP;
+ break;
+ }
+
+ if (TclGetChannelFromObj(interp, objv[3], &chan, &chanMode,
+ 0) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ level = Z_DEFAULT_COMPRESSION;
+ for (i=4 ; i<objc ; i++) {
+ if (Tcl_GetIndexFromObj(interp, objv[i], pushOptions, "option", 0,
+ &option) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ switch ((enum pushOptions) option) {
+ case poHeader:
+ if (++i > objc-1) {
+ Tcl_AppendResult(interp,
+ "value missing for -header option", NULL);
+ return TCL_ERROR;
+ }
+ headerObj = objv[i];
+ if (Tcl_DictObjSize(interp, headerObj, &dummy) != TCL_OK) {
+ Tcl_AddErrorInfo(interp, "\n (in -header option)");
+ return TCL_ERROR;
+ }
+ break;
+ case poHeadVar:
+ if (++i > objc-1) {
+ Tcl_AppendResult(interp,
+ "value missing for -headerVar option", NULL);
+ return TCL_ERROR;
+ }
+ varObj = objv[i];
+ break;
+ case poLevel:
+ if (++i > objc-1) {
+ Tcl_AppendResult(interp,
+ "value missing for -level option", NULL);
+ return TCL_ERROR;
+ }
+ if (Tcl_GetIntFromObj(interp, objv[i],
+ (int *) &level) != TCL_OK) {
+ Tcl_AddErrorInfo(interp, "\n (in -level option)");
+ return TCL_ERROR;
+ }
+ if (level < 0 || level > 9) {
+ extraInfoStr = "\n (in -level option)";
+ goto badLevel;
+ }
+ break;
+ case poLimit:
+ if (++i > objc-1) {
+ Tcl_AppendResult(interp,
+ "value missing for -limit option", NULL);
+ return TCL_ERROR;
+ }
+ if (Tcl_GetIntFromObj(interp, objv[i],
+ (int *) &limit) != TCL_OK) {
+ Tcl_AddErrorInfo(interp, "\n (in -limit option)");
+ return TCL_ERROR;
+ }
+ if (limit < 1) {
+ limit = 1;
+ }
+ break;
+ }
+ }
+
+ Tcl_AppendResult(interp, "not yet implemented", NULL);
break;
+ }
};
return TCL_ERROR;
badLevel:
Tcl_AppendResult(interp, "level must be 0 to 9", NULL);
+ if (extraInfoStr) {
+ Tcl_AddErrorInfo(interp, extraInfoStr);
+ }
return TCL_ERROR;
badBuffer:
Tcl_AppendResult(interp, "buffer size must be 32 to 65536", NULL);
diff --git a/tests/zlib.test b/tests/zlib.test
index 7ce56f2..3f88565 100644
--- a/tests/zlib.test
+++ b/tests/zlib.test
@@ -10,7 +10,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.test,v 1.2 2008/12/12 15:00:01 dkf Exp $
+# RCS: @(#) $Id: zlib.test,v 1.3 2008/12/13 09:19:06 dkf Exp $
if {[lsearch [namespace children] ::tcltest] == -1} {
package require tcltest 2.1
@@ -22,7 +22,7 @@ test zlib-1.1 {zlib basics} -returnCodes error -body {
} -result {wrong # args: should be "zlib command arg ?...?"}
test zlib-1.2 {zlib basics} -returnCodes error -body {
zlib ? {}
-} -result {bad command "?": must be adler32, compress, crc32, decompress, deflate, gunzip, gzip, inflate, stack, stream, or unstack}
+} -result {bad command "?": must be adler32, compress, crc32, decompress, deflate, gunzip, gzip, inflate, push, or stream}
test zlib-2.1 {zlib compress/decompress} {
zlib decompress [zlib compress abcdefghijklm]