From e383ee2adaf83682c1807fd8677281af8e14a7b4 Mon Sep 17 00:00:00 2001 From: dkf Date: Sat, 13 Dec 2008 09:19:06 +0000 Subject: Many improvements to docs --- ChangeLog | 18 ++-- doc/zlib.n | 249 ++++++++++++++++++++++++++++++++++++++++++++++++------ generic/tclZlib.c | 127 +++++++++++++++++++++++++--- tests/zlib.test | 4 +- 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 + + * 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 - * 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 @@ -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 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-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] -- cgit v0.12