/* * zlib.c -- * Tcl and C Interface to the "zlib" compression library. * * Copyright (C) 2004, 2005 Pascal Scheffers * Copyright (C) 2005 Unitas Software B.V. * Written by Pascal Scheffers * * Parts written by Jean-Claude Wippler, as part of Tclkit, placed in the * public domain March 2003. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * */ #include #include "zlib.h" #include #include "tclZlib.h" /* * Structure used for the Zlib_Stream* commands and [zlib stream ...] */ typedef struct { Tcl_Interp *interp; z_stream stream; int streamend; Tcl_Obj *indata, *outdata; /* Input / output buffers (lists) */ Tcl_Obj *current_input; /* pointer to what is currently being inflated */ int inpos, outpos; int mode; /* ZLIB_DEFLATE || ZLIB_INFLATE */ int format; /* ZLIB_FORMAT_* */ int level; /* default 5, 0-9 */ int flush; /* stores the flush param for deferred the decompression */ int wbits; Tcl_Obj *cmdname; /* Name of the associated Tcl command */ } zlibStreamHandle; /* * Prototypes for private procedures defined later in this file: */ static int ZlibCmd _ANSI_ARGS_((ClientData dummy, Tcl_Interp *ip, int objc, Tcl_Obj *CONST objv[])); static int ZlibStreamCmd _ANSI_ARGS_((ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])); static void ZlibStreamCmdDelete _ANSI_ARGS_((ClientData cd)); static void ZlibStreamCleanup _ANSI_ARGS_((zlibStreamHandle *zsh)); /* * Prototypes for private procedures used by channel stacking: */ #ifdef ENABLE_CHANSTACKING int ChanClose _ANSI_ARGS_((ClientData instanceData, Tcl_Interp *interp)); int ChanInput _ANSI_ARGS_((ClientData instanceData, char *buf, int toRead, int *errorCodePtr)); int ChanOutput _ANSI_ARGS_((ClientData instanceData, CONST84 char *buf, int toWrite, int*errorCodePtr)); int ChanSeek _ANSI_ARGS_((ClientData instanceData, long offset, int mode, int *errorCodePtr)); int ChanSetOption _ANSI_ARGS_((ClientData instanceData, Tcl_Interp *interp, CONST char *optionName, CONST char *value)); int ChanGetOption _ANSI_ARGS_((ClientData instanceData, Tcl_Interp *interp, CONST84 char *optionName, Tcl_DString *dsPtr)); void ChanWatch _ANSI_ARGS_((ClientData instanceData, int mask)); int ChanGetHandle _ANSI_ARGS_((ClientData instanceData, int direction, ClientData *handlePtr)); int ChanClose2 _ANSI_ARGS_((ClientData instanceData, Tcl_Interp *interp, int flags)); int ChanBlockMode _ANSI_ARGS_((ClientData instanceData, int mode)); int ChanFlush _ANSI_ARGS_((ClientData instanceData)); int ChanHandler _ANSI_ARGS_((ClientData instanceData, int interestMask)); Tcl_WideInt ChanWideSeek _ANSI_ARGS_((ClientData instanceData, Tcl_WideInt offset, int mode, int *errorCodePtr)); static Tcl_ChannelType zlibChannelType = { "zlib", TCL_CHANNEL_VERSION_3, ChanClose, ChanInput, ChanOutput, NULL, /* ChanSeek, */ NULL, /* ChanSetOption, */ NULL, /* ChanGetOption, */ ChanWatch, ChanGetHandle, NULL, /* ChanClose2, */ ChanBlockMode, ChanFlush, ChanHandler, NULL /* ChanWideSeek */ }; typedef struct { /* Generic channel info */ Tcl_Channel channel; Tcl_TimerToken timer; int flags; int mask; /* Zlib specific channel state */ int inFormat; int outFormat; z_stream instream; z_stream outstream; char *inbuffer; int inAllocated, inUsed, inPos; char *outbuffer; int outAllocated, outUsed, outPos; } Zlib_ChannelData; /* Flag values */ #define ASYNC 1 #endif /* ENABLE_CHANSTACKING */ #ifdef TCLKIT_BUILD /* * Structure for the old zlib sdeflate/sdecompress commands * Deprecated! */ typedef struct { z_stream stream; Tcl_Obj *indata; } zlibstream; static int zstreamincmd(ClientData cd, Tcl_Interp *ip, int objc, Tcl_Obj *CONST objv[]) { zlibstream *zp = (zlibstream*) cd; int count = 0; int e, index; Tcl_Obj *obj; static CONST84 char* cmds[] = { "fill", "drain", NULL, }; if (Tcl_GetIndexFromObj(ip, objv[1], cmds, "option", 0, &index) != TCL_OK) return TCL_ERROR; switch (index) { case 0: /* fill ?data? */ if (objc >= 3) { Tcl_IncrRefCount(objv[2]); Tcl_DecrRefCount(zp->indata); zp->indata = objv[2]; zp->stream.next_in = Tcl_GetByteArrayFromObj(zp->indata, &zp->stream.avail_in); } Tcl_SetObjResult(ip, Tcl_NewIntObj(zp->stream.avail_in)); break; case 1: /* drain count */ if (objc != 3) { Tcl_WrongNumArgs(ip, 2, objv, "count"); return TCL_ERROR; } if (Tcl_GetIntFromObj(ip, objv[2], &count) != TCL_OK) return TCL_ERROR; obj = Tcl_GetObjResult(ip); Tcl_SetByteArrayLength(obj, count); zp->stream.next_out = Tcl_GetByteArrayFromObj(obj, &zp->stream.avail_out); e = inflate(&zp->stream, Z_NO_FLUSH); if (e != 0 && e != Z_STREAM_END) { Tcl_SetResult(ip, (char*) zError(e), TCL_STATIC); return TCL_ERROR; } Tcl_SetByteArrayLength(obj, count - zp->stream.avail_out); break; } return TCL_OK; } void zstreamdelproc(ClientData cd) { zlibstream *zp = (zlibstream*) cd; inflateEnd(&zp->stream); Tcl_DecrRefCount(zp->indata); Tcl_Free((void*) zp); } static int ZlibCmdO(ClientData dummy, Tcl_Interp *ip, int objc, Tcl_Obj *CONST objv[]) { int e = TCL_OK, index, dlen, wbits = -MAX_WBITS; unsigned flag; Byte *data; z_stream stream; Tcl_Obj *obj = Tcl_GetObjResult(ip); static CONST84 char* cmds[] = { "adler32", "crc32", "compress", "deflate", "decompress", "inflate", "sdecompress", "sinflate", NULL, }; if (objc < 3 || objc > 4) { Tcl_WrongNumArgs(ip, 1, objv, "option data ?...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(ip, objv[1], cmds, "option", 0, &index) != TCL_OK || (objc > 3 && Tcl_GetIntFromObj(ip, objv[3], &flag)) != TCL_OK) return TCL_ERROR; data = Tcl_GetByteArrayFromObj(objv[2], &dlen); switch (index) { case 0: /* adler32 str ?start? -> checksum */ if (objc < 4) flag = adler32(0, 0, 0); Tcl_SetIntObj(obj, adler32(flag, data, dlen)); return TCL_OK; case 1: /* crc32 str ?start? -> checksum */ if (objc < 4) flag = crc32(0, 0, 0); Tcl_SetIntObj(obj, crc32(flag, data, dlen)); return TCL_OK; case 2: /* compress data ?level? -> data */ wbits = MAX_WBITS; case 3: /* deflate data ?level? -> data */ if (objc < 4) flag = Z_DEFAULT_COMPRESSION; stream.avail_in = (uInt) dlen; stream.next_in = data; stream.avail_out = (uInt) dlen + dlen / 1000 + 12; Tcl_SetByteArrayLength(obj, stream.avail_out); stream.next_out = Tcl_GetByteArrayFromObj(obj, NULL); stream.zalloc = 0; stream.zfree = 0; stream.opaque = 0; e = deflateInit2(&stream, flag, Z_DEFLATED, wbits, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); if (e != Z_OK) break; e = deflate(&stream, Z_FINISH); if (e != Z_STREAM_END) { deflateEnd(&stream); if (e == Z_OK) e = Z_BUF_ERROR; } else e = deflateEnd(&stream); break; case 4: /* decompress data ?bufsize? -> data */ wbits = MAX_WBITS; case 5: /* inflate data ?bufsize? -> data */ { if (objc < 4) flag = 16 * 1024; for (;;) { stream.zalloc = 0; stream.zfree = 0; /* +1 because ZLIB can "over-request" input (but ignore it) */ stream.avail_in = (uInt) dlen + 1; stream.next_in = data; stream.avail_out = (uInt) flag; Tcl_SetByteArrayLength(obj, stream.avail_out); stream.next_out = Tcl_GetByteArrayFromObj(obj, NULL); /* Negative value suppresses ZLIB header */ e = inflateInit2(&stream, wbits); if (e == Z_OK) { e = inflate(&stream, Z_FINISH); if (e != Z_STREAM_END) { inflateEnd(&stream); if (e == Z_OK) e = Z_BUF_ERROR; } else e = inflateEnd(&stream); } if (e == Z_OK || e != Z_BUF_ERROR) break; Tcl_SetByteArrayLength(obj, 0); flag *= 2; } break; } case 6: /* sdecompress cmdname -> */ wbits = MAX_WBITS; case 7: /* sinflate cmdname -> */ { zlibstream *zp = (zlibstream*) Tcl_Alloc(sizeof (zlibstream)); zp->indata = Tcl_NewObj(); Tcl_IncrRefCount(zp->indata); zp->stream.zalloc = 0; zp->stream.zfree = 0; zp->stream.opaque = 0; zp->stream.next_in = 0; zp->stream.avail_in = 0; inflateInit2(&zp->stream, wbits); Tcl_CreateObjCommand(ip, Tcl_GetStringFromObj(objv[2], 0), zstreamincmd, (ClientData) zp, zstreamdelproc); return TCL_OK; } } if (e != Z_OK) { Tcl_SetResult(ip, (char*) zError(e), TCL_STATIC); return TCL_ERROR; } Tcl_SetByteArrayLength(obj, stream.total_out); return TCL_OK; } #endif /* TCLKIT_BUILD */ /* *---------------------------------------------------------------------- * * Zlib_StreamInit -- * * This command initializes a (de)compression context/handle for * (de)compressing data in chunks. * * Results: * A standard Tcl result. * * Side effects: * zshandle is initialised and memory allocated for internal state. * Additionally, if interp is not null, a Tcl command is created * and its name placed in the interp result obj. * *---------------------------------------------------------------------- */ int Zlib_StreamInit(interp, mode, format, level, zshandle) Tcl_Interp *interp; int mode; /* ZLIB_INFLATE || ZLIB_DEFLATE */ int format; /* ZLIB_FORMAT_* */ int level; /* 0-9 or ZLIB_DEFAULT_COMPRESSION */ ZlibHandle *zshandle; { int wbits; int e; zlibStreamHandle *zsh=0; Tcl_DString cmdname; Tcl_CmdInfo cmdinfo; if ( mode == ZLIB_DEFLATE ) { /* compressed format is specified by the wbits parameter * See zlib.h for details. */ if ( format == ZLIB_FORMAT_RAW ) { wbits = -MAX_WBITS; } else if (format == ZLIB_FORMAT_GZIP) { wbits = MAX_WBITS+16; } else if (format == ZLIB_FORMAT_ZLIB) { wbits = MAX_WBITS; } else { if (interp) Tcl_SetResult(interp, "Incorrect zlib data format, must be ZLIB_FORMAT_ZLIB, ZLIB_FORMAT_GZIP or ZLIB_FORMAT_ZLIB", TCL_STATIC); return TCL_ERROR; } if ( level < -1 || level > 9 ) { if (interp) Tcl_SetResult(interp, "Compression level should be between 0 (no compression) and 9 (best compression) or -1 for default compression level.", TCL_STATIC); return TCL_ERROR; } } else { /* mode == ZLIB_INFLATE * wbits are the same as DEFLATE, but FORMAT_AUTO is valid too. */ if ( format == ZLIB_FORMAT_RAW ) { wbits = -MAX_WBITS; } else if (format == ZLIB_FORMAT_GZIP) { wbits = MAX_WBITS+16; } else if (format == ZLIB_FORMAT_ZLIB) { wbits = MAX_WBITS; } else if (format == ZLIB_FORMAT_AUTO) { wbits = MAX_WBITS+32; } else { if (interp) Tcl_SetResult(interp, "Incorrect zlib data format, must be ZLIB_FORMAT_ZLIB, ZLIB_FORMAT_GZIP, ZLIB_FORMAT_ZLIB or ZLIB_FORMAT_AUTO", TCL_STATIC); return TCL_ERROR; } } zsh = (zlibStreamHandle*) ckalloc(sizeof(zlibStreamHandle)); zsh->interp = interp; zsh->mode = mode; zsh->format = format; zsh->level = level; zsh->wbits = wbits; zsh->current_input = NULL; zsh->streamend = 0; zsh->stream.avail_in = 0; zsh->stream.next_in = 0; /* TODO: set to &ckalloc and &ckfree? */ zsh->stream.zalloc = 0; zsh->stream.zfree = 0; zsh->stream.opaque = 0; /* Must be initialized before calling (de|in)flateInit2 */ /* No output buffer available yet */ zsh->stream.avail_out=0; zsh->stream.next_out=NULL; if ( mode == ZLIB_DEFLATE ) { e = deflateInit2(&(zsh->stream), level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); } else { e = inflateInit2(&(zsh->stream), wbits); } if ( e != Z_OK ) { if (interp) Tcl_SetResult(interp, (char*) zError(e), TCL_STATIC); /* TODOcleanup */ return TCL_ERROR; } /* I could do all this in C, but this is easier */ if ( interp ) { if ( Tcl_Eval(interp, "namespace eval ::zlib {incr cmdcounter}") != TCL_OK ) { /* TODO cleanup */ return TCL_ERROR; } Tcl_DStringInit(&cmdname); Tcl_DStringAppend(&cmdname, "::zlib::streamcmd-", -1); Tcl_DStringAppend(&cmdname, Tcl_GetString(Tcl_GetObjResult(interp)), -1); if ( Tcl_GetCommandInfo(interp, Tcl_DStringValue(&cmdname), &cmdinfo) == 1 ) { /* TODO CLEANUP */ Tcl_SetResult(interp, "BUG: Stream command name already exists", TCL_STATIC); return TCL_ERROR; } Tcl_SetResult(interp,"", TCL_STATIC); /* Create the command */ Tcl_CreateObjCommand(interp, Tcl_DStringValue(&cmdname), ZlibStreamCmd, (ClientData) zsh, ZlibStreamCmdDelete); /* Create the cmdname obj for future reference */ zsh->cmdname = Tcl_NewStringObj(Tcl_DStringValue(&cmdname), Tcl_DStringLength(&cmdname)); Tcl_IncrRefCount(zsh->cmdname); Tcl_DStringFree(&cmdname); } else { zsh->cmdname = NULL; } /* Prepare the buffers for use */ zsh->indata = Tcl_NewListObj(0, NULL); Tcl_IncrRefCount(zsh->indata); zsh->outdata = Tcl_NewListObj(0, NULL); Tcl_IncrRefCount(zsh->outdata); zsh->inpos = 0; zsh->outpos = 0; //Tcl_SetObjResult(interp, zsh->cmdname); /* Now set the int pointed to by *zshandle to the pointer to the zsh struct */ if (zshandle) *zshandle = (ZlibHandle)zsh; return TCL_OK; } /* *---------------------------------------------------------------------- * * ZlibStreamCmdDelete -- * * This is the delete command which Tcl invokes when a zlibstream * command is deleted from the interpreter (on stream close, usually) * * Results: * void * * Side effects: * invalidates the zlib stream handle as obtained from * Zlib_StreamInit. * *---------------------------------------------------------------------- */ static void ZlibStreamCmdDelete (cd) ClientData cd; { zlibStreamHandle *zsh = (zlibStreamHandle*)cd; ZlibStreamCleanup(zsh); } /* *---------------------------------------------------------------------- * * Zlib_StreamClose -- * * This procedure must be called after (de)compression is done to * ensure memory is freed and the command is deleted from the * interpreter (if any). * * Results: * A standard Tcl result. * * Side effects: * invalidates the zlib stream handle as obtained from * Zlib_StreamInit. * *---------------------------------------------------------------------- */ int Zlib_StreamClose (zshandle) ZlibHandle zshandle; /* as obtained from Zlib_StreamInit */ { zlibStreamHandle *zsh = (zlibStreamHandle*)zshandle; /* If the interp is set, deleting the command will trigger * ZlibStreamCleanup() in ZlibStreamCmdDelete(). * If no interp is set, call ZlibStreamCleanup() directly. */ if ( zsh->interp && zsh->cmdname ) { Tcl_DeleteCommand(zsh->interp, Tcl_GetStringFromObj(zsh->cmdname, NULL)); } else { ZlibStreamCleanup(zsh); } return TCL_OK; } /* *---------------------------------------------------------------------- * * ZlibStreamCleanup -- * * This procedure is called by either Zlib_StreamClose() or * ZlibStreamCmdDelete() to cleanup the stream context. * * Results: * A standard Tcl result. * * Side effects: * invalidates the zlib stream handle. * *---------------------------------------------------------------------- */ void ZlibStreamCleanup (zsh) zlibStreamHandle *zsh; { if ( !zsh->streamend ) { if ( zsh->mode == ZLIB_DEFLATE ) { deflateEnd(&(zsh->stream)); } else { inflateEnd(&(zsh->stream)); } } if (zsh->indata) Tcl_DecrRefCount(zsh->indata); if (zsh->outdata) Tcl_DecrRefCount(zsh->outdata); if (zsh->cmdname) Tcl_DecrRefCount(zsh->cmdname); if (zsh->current_input) Tcl_DecrRefCount(zsh->current_input); ckfree((void*)zsh); } /* *---------------------------------------------------------------------- * * Zlib_StreamReset -- * * This procedure will reinitialize an existing stream handle. * * Results: * A standard Tcl result. * * Side effects: * Any data left in the (de)compression buffer is lost. * *---------------------------------------------------------------------- */ int Zlib_StreamReset (zshandle) ZlibHandle zshandle; /* as obtained from Zlib_StreamInit */ { zlibStreamHandle *zsh = (zlibStreamHandle*)zshandle; int e; //Tcl_DeleteCommand(zsh->interp, Tcl_GetStringFromObj(zsh->cmdname, NULL)); if ( !zsh->streamend ) { if ( zsh->mode == ZLIB_DEFLATE ) { deflateEnd(&(zsh->stream)); } else { inflateEnd(&(zsh->stream)); } } Tcl_SetByteArrayLength(zsh->indata, 0); Tcl_SetByteArrayLength(zsh->outdata, 0); if (zsh->current_input) { Tcl_DecrRefCount(zsh->current_input); zsh->current_input=NULL; } zsh->inpos = 0; zsh->outpos = 0; zsh->streamend = 0; zsh->stream.avail_in = 0; zsh->stream.next_in = 0; /* TODO: set to &ckalloc and &ckfree? */ zsh->stream.zalloc = 0; zsh->stream.zfree = 0; zsh->stream.opaque = 0; /* Must be initialized before calling (de|in)flateInit2 */ /* No output buffer available yet */ zsh->stream.avail_out=0; zsh->stream.next_out=NULL; if ( zsh->mode == ZLIB_DEFLATE ) { e = deflateInit2(&(zsh->stream), zsh->level, Z_DEFLATED, zsh->wbits, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); } else { e = inflateInit2(&(zsh->stream), zsh->wbits); } if ( e != Z_OK ) { if (zsh->interp) Tcl_SetResult(zsh->interp, (char*) zError(e), TCL_STATIC); /* TODOcleanup */ return TCL_ERROR; } return TCL_OK; } /* *---------------------------------------------------------------------- * * Zlib_StreamGetCommandName -- * * This procedure will return the command name associated * with the stream. * * Results: * A Tcl_Obj with the name of the Tcl command or NULL if no * command is associated with the stream. * * Side effects: * None. * *---------------------------------------------------------------------- */ Tcl_Obj * Zlib_StreamGetCommandName(zshandle) ZlibHandle zshandle; /* as obtained from Zlib_StreamInit */ { zlibStreamHandle *zsh = (zlibStreamHandle*)zshandle; return zsh->cmdname; } /* *---------------------------------------------------------------------- * * Zlib_StreamEof -- * * This procedure This function returns 0 or 1 depending on the state of * the (de)compressor. For decompression, eof is reached when the entire * compressed stream has been decompressed. For compression, eof is * reached when the stream has been flushed with ZLIB_FINALIZE. * * Results: * Integer. * * Side effects: * None. * *---------------------------------------------------------------------- */ int Zlib_StreamEof(zshandle) ZlibHandle zshandle; /* as obtained from Zlib_StreamInit */ { zlibStreamHandle *zsh = (zlibStreamHandle*)zshandle; return zsh->streamend; } int Zlib_StreamAdler32(zshandle) ZlibHandle zshandle; /* as obtained from Zlib_StreamInit */ { zlibStreamHandle *zsh = (zlibStreamHandle*)zshandle; return zsh->stream.adler; } #ifdef DISABLED_CODE int Zlib_StreamAdd(zshandle, data, size, flush, outdata, buffersize) ZlibHandle zshandle; /* as obtained from Zlib_StreamInit */ char *data; /* data to compress/decompress */ int size; /* byte length of data */ int flush; /* 0, ZLIB_FLUSH, ZLIB_FULLFLUSH, ZLIB_FINALIZE */ Tcl_Obj *outdata; /* An object to append the compressed data to */ int buffersize; /* Hint of the expected output size of inflate/deflate */ { char *datatmp=0, *outptr=0; int e, outsize; zlibStreamHandle *zsh = (zlibStreamHandle*)zshandle; zsh->stream.next_in = data; zsh->stream.avail_in = size; if ( zsh->mode == ZLIB_DEFLATE ) { if ( buffersize < 6 ) { /* The 6 comes from the zlib.h description of deflate. * if the suggested buffer size is below 6, use deflateBound * to get the minimum number of bytes needed from zlib. */ zsh->stream.avail_out=deflateBound(&(zsh->stream), zsh->stream.avail_in); } else { zsh->stream.avail_out=buffersize; } datatmp=(char*)ckalloc(zsh->stream.avail_out); zsh->stream.next_out=datatmp; e = deflate(&(zsh->stream), flush); while ( (e==Z_OK || e==Z_BUF_ERROR) && zsh->stream.avail_out==0 ) { /* Output buffer too small */ Tcl_Panic("StreamAdd/Deflate - Buffer growing not implemented yet"); } /* now append the (de)compressed data to outdata */ Tcl_GetByteArrayFromObj(outdata, &outsize); outptr = Tcl_SetByteArrayLength(outdata, outsize+zsh->stream.total_out); memcpy(&outptr[outsize], datatmp, zsh->stream.total_out); } else { if (buffersize == 0) { /* Start with a buffer 3 times the size of the input data */ /* TODO: integer bounds/overflow check */ buffersize = 3*zsh->stream.avail_in; } Tcl_Panic("StreamAdd/Inflate - not implemented yet"); } return TCL_OK; } #endif /* disabled code */ int Zlib_StreamPut(zshandle, data, flush) ZlibHandle zshandle; /* as obtained from Zlib_StreamInit */ Tcl_Obj *data; /* data to compress/decompress */ int flush; /* 0, ZLIB_FLUSH, ZLIB_FULLFLUSH, ZLIB_FINALIZE */ { char *datatmp=0; int e, size, outsize; Tcl_Obj *obj; zlibStreamHandle *zsh = (zlibStreamHandle*)zshandle; if ( zsh->streamend ) { if (zsh->interp) Tcl_SetResult(zsh->interp, "already past compressed stream end", TCL_STATIC); return TCL_ERROR; } if ( zsh->mode == ZLIB_DEFLATE ) { zsh->stream.next_in = Tcl_GetByteArrayFromObj(data, &size); zsh->stream.avail_in = size; /* Deflatebound doesn't seem to take various header sizes into account, so we add 100 extra bytes */ outsize=deflateBound(&(zsh->stream), zsh->stream.avail_in)+100; zsh->stream.avail_out=outsize; datatmp=(char*)ckalloc(zsh->stream.avail_out); zsh->stream.next_out=datatmp; e = deflate(&(zsh->stream), flush); if ( (e==Z_OK || e==Z_BUF_ERROR) && zsh->stream.avail_out==0 ) { /* Output buffer too small */ if (outsize - zsh->stream.avail_out > 0 ) { obj = Tcl_NewByteArrayObj(datatmp, outsize - zsh->stream.avail_out ); /* now append the compressed data to the outbuffer */ Tcl_ListObjAppendElement(zsh->interp, zsh->outdata, obj); } if ( outsize < 0xFFFF ) { outsize = 0xFFFF; /* There may be *lots* of data left to output... */ ckfree(datatmp); datatmp=(char*)ckalloc(outsize); } zsh->stream.avail_out=outsize; zsh->stream.next_out=datatmp; e = deflate(&(zsh->stream), flush); } /* And append the final data block */ if (outsize - zsh->stream.avail_out > 0 ) { obj = Tcl_NewByteArrayObj(datatmp, outsize - zsh->stream.avail_out ); /* now append the compressed data to the outbuffer */ Tcl_ListObjAppendElement(zsh->interp, zsh->outdata, obj); } } else { /* This is easy. Just append to inbuffer. */ Tcl_ListObjAppendElement(zsh->interp, zsh->indata, data); /* and we'll need the flush parameter for the Inflate call */ zsh->flush = flush; } return TCL_OK; } int Zlib_StreamGet(zshandle, data, count) ZlibHandle zshandle; /* as obtained from Zlib_StreamInit */ Tcl_Obj *data; /* A place to put the data */ int count; /* Number of bytes to grab as a maximum, you may get less! */ { int e, i; int llength, itemlen, datapos=0; Tcl_Obj *lobj; unsigned char *dataptr, *itemptr; zlibStreamHandle *zsh = (zlibStreamHandle*)zshandle; /* Getting beyond the of stream, just return empty string. */ if ( zsh->streamend ) return TCL_OK; if ( zsh->mode == ZLIB_INFLATE ) { if ( count == -1 ) { /* The only safe thing to do is restict to 65k. */ /* We might cause a panic for out of memory if we just kept growing the buffer */ count = 65536; } /* Prepare the place to store the data */ dataptr = Tcl_SetByteArrayLength(data, count); zsh->stream.next_out=dataptr; zsh->stream.avail_out=count; if ( zsh->stream.avail_in == 0 ) { /* zlib will probably need more data to decompress */ if (zsh->current_input) { Tcl_DecrRefCount(zsh->current_input); zsh->current_input=0; } if ( Tcl_ListObjLength(zsh->interp, zsh->indata, &llength)!=TCL_OK ) return TCL_ERROR; if ( llength > 0 ) { /* there is more input available, get it from the list and give it to zlib */ if (Tcl_ListObjIndex(zsh->interp, zsh->indata, 0, &lobj)!=TCL_OK) return TCL_ERROR; itemptr = Tcl_GetByteArrayFromObj(lobj, &itemlen); Tcl_IncrRefCount(lobj); zsh->current_input=lobj; zsh->stream.next_in=itemptr; zsh->stream.avail_in=itemlen; /* and remove it from the list */ Tcl_ListObjReplace(NULL, zsh->indata, 0, 1, 0, NULL); llength--; } } e = inflate(&(zsh->stream), zsh->flush); if ( Tcl_ListObjLength(zsh->interp, zsh->indata, &llength)!=TCL_OK ) return TCL_ERROR; /*printf("llength %d, e==%d, avail_out %d\n", llength, e, zsh->stream.avail_out);*/ while ( zsh->stream.avail_out > 0 && (e==Z_OK || e==Z_BUF_ERROR) && llength>0 ) { /* State: We have not satisfied the request yet and there may be more to inflate */ if ( zsh->stream.avail_in > 0 ) { if (zsh->interp) Tcl_SetResult(zsh->interp, "Unexpected zlib internal state during decompression.", TCL_STATIC); return TCL_ERROR; } if (zsh->current_input) { Tcl_DecrRefCount(zsh->current_input); zsh->current_input=0; } if (Tcl_ListObjIndex(zsh->interp, zsh->indata, 0, &lobj)!=TCL_OK) return TCL_ERROR; itemptr = Tcl_GetByteArrayFromObj(lobj, &itemlen); Tcl_IncrRefCount(lobj); zsh->current_input=lobj; zsh->stream.next_in=itemptr; zsh->stream.avail_in=itemlen; /* and remove it from the list */ Tcl_ListObjReplace(NULL, zsh->indata, 0, 1, 0, NULL); llength--; /* And call inflate again */ e = inflate(&(zsh->stream), zsh->flush); } if (zsh->stream.avail_out>0) { Tcl_SetByteArrayLength(data, count - zsh->stream.avail_out); } if ( !(e==Z_OK || e==Z_STREAM_END || e==Z_BUF_ERROR) ) { if (zsh->interp) Tcl_SetResult(zsh->interp, zsh->stream.msg, TCL_VOLATILE); return TCL_ERROR; } if ( e==Z_STREAM_END ) { zsh->streamend=1; if (zsh->current_input) { Tcl_DecrRefCount(zsh->current_input); zsh->current_input=0; } inflateEnd(&(zsh->stream)); } } else { if ( Tcl_ListObjLength(zsh->interp, zsh->outdata, &llength)!=TCL_OK ) return TCL_ERROR; if ( count == -1 ) { count=0; for ( i=0; iinterp, zsh->outdata, i, &lobj)!=TCL_OK) return TCL_ERROR; itemptr = Tcl_GetByteArrayFromObj(lobj, &itemlen); if (i==0) { count+=itemlen- zsh->outpos; } else { count+=itemlen; } } } /* Prepare the place to store the data */ dataptr = Tcl_SetByteArrayLength(data, count); while ( count>datapos && Tcl_ListObjLength(zsh->interp, zsh->outdata, &llength)==TCL_OK && llength > 0) { Tcl_ListObjIndex(zsh->interp, zsh->outdata, 0, &lobj); itemptr = Tcl_GetByteArrayFromObj(lobj, &itemlen); if ( itemlen - zsh->outpos >= count-datapos ) { memcpy(dataptr+datapos, itemptr+zsh->outpos, count-datapos); zsh->outpos+=count-datapos; datapos+=count-datapos; if (zsh->outpos == itemlen) zsh->outpos=0; } else { memcpy(dataptr+datapos, itemptr+zsh->outpos, itemlen-zsh->outpos); datapos += itemlen- zsh->outpos; zsh->outpos=0; } if (zsh->outpos == 0) { Tcl_ListObjReplace(NULL, zsh->outdata, 0, 1, 0, NULL); } } Tcl_SetByteArrayLength(data, datapos); } return TCL_OK; } int Zlib_Deflate(interp, format, data, level) Tcl_Interp *interp; int format; Tcl_Obj *data; int level; { /* Deflate the contents of Tcl_Obj *data with compression level * in output format. */ int wbits, bdlen=0, e=0; Byte *bdata=0; z_stream stream; /* We pass the data back in the interp result obj... */ if (!interp) return TCL_ERROR; Tcl_Obj *obj = Tcl_GetObjResult(interp); /* compressed format is specified by the wbits parameter * See zlib.h for details. */ if ( format == ZLIB_FORMAT_RAW ) { wbits = -MAX_WBITS; } else if (format == ZLIB_FORMAT_GZIP) { wbits = MAX_WBITS+16; } else if (format == ZLIB_FORMAT_ZLIB) { wbits = MAX_WBITS; } else { Tcl_SetResult(interp, "Incorrect zlib data format, must be ZLIB_FORMAT_ZLIB, ZLIB_FORMAT_GZIP or ZLIB_FORMAT_ZLIB", TCL_STATIC); return TCL_ERROR; } if ( level < -1 || level > 9 ) { Tcl_SetResult(interp, "Compression level should be between 0 (no compression) and 9 (best compression) or -1 for default compression level.", TCL_STATIC); return TCL_ERROR; } /* Obtain the pointer to the byte array, we'll pass this * pointer straight to the deflate command. */ bdata = Tcl_GetByteArrayFromObj(data, &bdlen); stream.avail_in = (uInt) bdlen; stream.next_in = bdata; /* TODO: set to &ckalloc and &ckfree? */ stream.zalloc = 0; stream.zfree = 0; stream.opaque = 0; /* Must be initialized before calling deflateInit2 */ /* No output buffer available yet, will alloc after deflateInit2 */ stream.avail_out=0; stream.next_out=NULL; e = deflateInit2(&stream, level, Z_DEFLATED, wbits, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); if (e != Z_OK) { Tcl_SetResult(interp, (char*) zError(e), TCL_STATIC); return TCL_ERROR; } /* Allocate the output buffer from the value of deflateBound() * This is probably too much space. * Before returning to the caller, we will reduce it back * to the actual compressed size. */ stream.avail_out = deflateBound(&stream, bdlen); /* TODO: What happens if this next call fails? */ Tcl_SetByteArrayLength(obj, stream.avail_out); /* and point the output buffer to the obj buffer */ stream.next_out = Tcl_GetByteArrayFromObj(obj, NULL); /* perform the compression, Z_FINISH means do it in one go. */ e = deflate(&stream, Z_FINISH); if (e != Z_STREAM_END) { deflateEnd(&stream); /* deflate return Z_OK when there are bytes left to compress, * at this point we consider that an error, although we could * continue by allocating more memory and calling deflate() again. */ if (e == Z_OK) e = Z_BUF_ERROR; } else { e = deflateEnd(&stream); } if (e != Z_OK) { Tcl_SetResult(interp, (char*) zError(e), TCL_STATIC); return TCL_ERROR; } /* Reduce the BA length to the actual data length produced by deflate. */ Tcl_SetByteArrayLength(obj, stream.total_out); return TCL_OK; } int Zlib_Inflate(Tcl_Interp *interp, int format, Tcl_Obj *data, int buffersize) { int wbits, inlen=0, e=0, newbuffersize; Byte *indata=0, *outdata=0, *newoutdata=0; z_stream stream; /* We pass the data back in the interp result obj... */ if (!interp) return TCL_ERROR; Tcl_Obj *obj = Tcl_GetObjResult(interp); /* compressed format is specified by the wbits parameter * See zlib.h for details. */ if ( format == ZLIB_FORMAT_RAW ) { wbits = -MAX_WBITS; } else if (format == ZLIB_FORMAT_GZIP) { wbits = MAX_WBITS+16; } else if (format == ZLIB_FORMAT_ZLIB) { wbits = MAX_WBITS; } else if (format == ZLIB_FORMAT_AUTO) { wbits = MAX_WBITS+32; } else { Tcl_SetResult(interp, "Incorrect zlib data format, must be ZLIB_FORMAT_ZLIB, ZLIB_FORMAT_GZIP, ZLIB_FORMAT_ZLIB or ZLIB_FORMAT_AUTO", TCL_STATIC); return TCL_ERROR; } indata=Tcl_GetByteArrayFromObj(data,&inlen); if (buffersize == 0) { /* Start with a buffer 3 times the size of the input data */ /* TODO: integer bounds/overflow check */ buffersize = 3*inlen; } outdata = Tcl_SetByteArrayLength(obj, buffersize); /* TODO: Set these to &ckalloc and &ckfree? */ stream.zalloc = 0; stream.zfree = 0; /* +1 because ZLIB can "over-request" input (but ignore it) */ stream.avail_in = (uInt) inlen+1; stream.next_in = indata; stream.avail_out = buffersize; stream.next_out = outdata; /* Start the decompression cycle. */ e = inflateInit2(&stream, wbits); if ( e != Z_OK ) { Tcl_SetResult(interp, (char*) zError(e), TCL_STATIC); return TCL_ERROR; } while ( (e=inflate(&stream,Z_FINISH))==Z_BUF_ERROR ) { /* Not enough room in the output buffer. Increase it by five times the * bytes still in the input buffer. (Because 3 times didn't do the trick * before, 5 times is what we do next.) * Further optimization should be done by the user, specify the decompressed size! */ if ( stream.avail_in == 0 && stream.avail_out > 0 ) { Tcl_SetResult(interp, "decompression failed, input truncated?", TCL_STATIC); return TCL_ERROR; } newbuffersize = buffersize + 5 * stream.avail_in; if (newbuffersize == buffersize) newbuffersize = buffersize+1000; newoutdata=Tcl_SetByteArrayLength(obj, newbuffersize); /* Set next out to the same offset in the new location */ stream.next_out=&newoutdata[stream.total_out]; /* And increase avail_out with the number of new bytes allocated */ stream.avail_out=stream.avail_out+(newbuffersize-buffersize); outdata=newoutdata; buffersize=newbuffersize; } if ( e != Z_STREAM_END ) { inflateEnd(&stream); Tcl_SetResult(interp, (char*) zError(e), TCL_STATIC); return TCL_ERROR; } e = inflateEnd(&stream); if ( e != Z_OK ) { Tcl_SetResult(interp, (char*) zError(e), TCL_STATIC); return TCL_ERROR; } /* Reduce the BA length to the actual data length produced by deflate. */ Tcl_SetByteArrayLength(obj, stream.total_out); return TCL_OK; } unsigned int Zlib_CRC32(unsigned int crc, const char *buf, unsigned int len) { /* Nothing much to do, just wrap the crc32(). */ return crc32(crc, buf, len); } unsigned int Zlib_Adler32(unsigned int adler, const char *buf, unsigned int len) { return adler32(adler, buf, len); } static int ZlibCmd(notUsed, interp, objc, objv) ClientData notUsed; Tcl_Interp *interp; int objc; Tcl_Obj *CONST objv[]; { int command, dlen, mode, format; #ifdef TCLKIT_BUILD int wbits=-MAX_WBITS; #endif unsigned start, level=-1, buffersize=0; ZlibHandle zh; Byte *data; Tcl_Obj *obj = Tcl_GetObjResult(interp); static CONST84 char* commands[] = { "adler32", "crc32", "deflate", "compress", "gzip", "inflate", "decompress", "gunzip", "stream", "stack", "unstack", #ifdef TCLKIT_BUILD "sdecompress", "sinflate", #endif NULL }; static CONST84 char* stream_formats[] = { "deflate", "inflate", "compress", "decompress", "gzip", "gunzip", NULL }; if (objc < 3) { Tcl_WrongNumArgs(interp, 1, objv, "command arg ?...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[1], commands, "command", 0, &command) != TCL_OK) return TCL_ERROR; switch (command) { case 0: /* adler32 str ?startvalue? -> checksum */ if (objc > 3 && Tcl_GetIntFromObj(interp, objv[3], &start) != TCL_OK) { return TCL_ERROR; } if (objc < 4) { start = Zlib_Adler32(0, 0, 0); } data=Tcl_GetByteArrayFromObj(objv[2],&dlen); Tcl_SetIntObj(obj, Zlib_Adler32(start, data, dlen)); return TCL_OK; case 1: /* crc32 str ?startvalue? -> checksum */ if (objc > 3 && Tcl_GetIntFromObj(interp, objv[3], &start) != TCL_OK) { return TCL_ERROR; } if (objc < 4) { start = Zlib_CRC32(0, 0, 0); } data=Tcl_GetByteArrayFromObj(objv[2],&dlen); Tcl_SetIntObj(obj, Zlib_CRC32(start, data, dlen)); return TCL_OK; case 2: /* deflate data ?level? -> rawCompressedData */ if (objc > 3 && Tcl_GetIntFromObj(interp, objv[3], &level) != TCL_OK) { return TCL_ERROR; } return Zlib_Deflate(interp, ZLIB_FORMAT_RAW, objv[2], level); case 3: /* compress data ?level? -> zlibCompressedData */ if (objc > 3 && Tcl_GetIntFromObj(interp, objv[3], &level) != TCL_OK) { return TCL_ERROR; } return Zlib_Deflate(interp, ZLIB_FORMAT_ZLIB, objv[2], level); case 4: /* gzip data ?level? -> gzippedCompressedData */ if (objc > 3 && Tcl_GetIntFromObj(interp, objv[3], &level) != TCL_OK) { return TCL_ERROR; } return Zlib_Deflate(interp, ZLIB_FORMAT_GZIP, objv[2], level); case 5: /* inflate rawcomprdata ?bufferSize? -> decompressedData */ if (objc > 3 && Tcl_GetIntFromObj(interp, objv[3], &buffersize) != TCL_OK) { return TCL_ERROR; } return Zlib_Inflate(interp, ZLIB_FORMAT_RAW, objv[2], buffersize); case 6: /* decompress zlibcomprdata ?bufferSize? -> decompressedData */ /* We rely on FORMAT_AUTO to determine type. */ case 7: /* gunzip gzippeddata ?bufferSize? -> decompressedData */ if (objc > 3 && Tcl_GetIntFromObj(interp, objv[3], &buffersize) != TCL_OK) { return TCL_ERROR; } return Zlib_Inflate(interp, ZLIB_FORMAT_AUTO, objv[2], buffersize); case 8: /* stream deflate/inflate/...gunzip ?level?*/ if (Tcl_GetIndexFromObj(interp, objv[2], stream_formats, "stream format", 0, &format) != TCL_OK) { Tcl_AppendResult(interp, "format error", "integer", NULL); return TCL_ERROR; } mode=ZLIB_INFLATE; switch (format) { case 0: mode = ZLIB_DEFLATE; case 1: format = ZLIB_FORMAT_RAW; break; case 2: mode = ZLIB_DEFLATE; case 3: format = ZLIB_FORMAT_ZLIB; break; case 4: mode = ZLIB_DEFLATE; case 5: format = ZLIB_FORMAT_GZIP; break; } if ( objc >= 4) { if (Tcl_GetIntFromObj(interp, objv[3], &level) != TCL_OK) { Tcl_AppendResult(interp, "level error", "integer", NULL); return TCL_ERROR; } } else { level = Z_DEFAULT_COMPRESSION; } if ( Zlib_StreamInit(interp, mode, format, level, &zh) != TCL_OK ) { Tcl_AppendResult(interp, "stream init error", "integer", NULL); return TCL_ERROR; } Tcl_SetObjResult(interp, Zlib_StreamGetCommandName(zh)); return TCL_OK; case 9: /* stack */ break; case 10: /* unstack */ break; #ifdef TCLKIT_BUILD case 11: /* sdecompress cmdname -> */ wbits = MAX_WBITS; case 12: /* sinflate cmdname -> */ { zlibstream *zp = (zlibstream*) Tcl_Alloc(sizeof (zlibstream)); zp->indata = Tcl_NewObj(); Tcl_IncrRefCount(zp->indata); zp->stream.zalloc = 0; zp->stream.zfree = 0; zp->stream.opaque = 0; zp->stream.next_in = 0; zp->stream.avail_in = 0; inflateInit2(&zp->stream, wbits); Tcl_CreateObjCommand(interp, Tcl_GetStringFromObj(objv[2], 0), zstreamincmd, (ClientData) zp, zstreamdelproc); return TCL_OK; } #endif /* TCLKIT_BUILD */ }; return TCL_ERROR; } static int ZlibStreamCmd(cd, interp, objc, objv) ClientData cd; Tcl_Interp *interp; int objc; Tcl_Obj *CONST objv[]; { zlibStreamHandle *zsh = (zlibStreamHandle*)cd; int command, index, count; Tcl_Obj *obj = Tcl_GetObjResult(interp); int buffersize; int flush=-1, i; static CONST84 char* cmds[] = { "add", "put", "get", "flush", "fullflush", "finalize", "close", "eof", "adler32", "reset", NULL }; static CONST84 char* add_options[] = { "-flush", "-fullflush", "-finalize", "-buffer", NULL }; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "option data ?...?"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[1], cmds, "option", 0, &command) != TCL_OK) return TCL_ERROR; switch (command) { case 0: /* add ?-flush|-fullflush|-finalize? /data/ */ for (i=2; i -1 ) flush = -2; else flush = Z_SYNC_FLUSH; break; case 1: /* -fullflush */ if ( flush > -1 ) flush = -2; else flush = Z_FULL_FLUSH; break; case 2: /* -finalize */ if ( flush > -1 ) flush = -2; else flush = Z_FINISH; break; case 3: /* -buffer */ if (i == (objc-2)) { Tcl_AppendResult(interp, "\"-buffer\" option must be followed ", "by integer decompression buffersize", NULL); return TCL_ERROR; } if ( Tcl_GetIntFromObj(interp, objv[i+1], &buffersize) != TCL_OK ) { return TCL_ERROR; } } if ( flush == -2 ) { Tcl_AppendResult(interp, "\"-flush\", \"-fullflush\" and \"-finalize\" options are ", "mutually exclusive", NULL); return TCL_ERROR; } } if (flush == -1) flush=0; if (Zlib_StreamPut((ZlibHandle)zsh, objv[objc-1], flush) != TCL_OK) return TCL_ERROR; return Zlib_StreamGet((ZlibHandle)zsh, obj, -1); return TCL_OK; case 1: /* put ?-flush|-fullflush|-finalize? /data/ */ for (i=2; i -1 ) flush = -2; else flush = Z_SYNC_FLUSH; break; case 1: /* -fullflush */ if ( flush > -1 ) flush = -2; else flush = Z_FULL_FLUSH; break; case 2: /* -finalize */ if ( flush > -1 ) flush = -2; else flush = Z_FINISH; break; } if ( flush == -2 ) { Tcl_AppendResult(interp, "\"-flush\", \"-fullflush\" and \"-finalize\" options are ", "mutually exclusive", NULL); return TCL_ERROR; } } if (flush == -1) flush=0; return Zlib_StreamPut((ZlibHandle)zsh, objv[objc-1], flush); case 2: /* get ?count? */ count=-1; if ( objc >= 3 ) { if ( Tcl_GetIntFromObj(interp, objv[2], &count) != TCL_OK ) return TCL_ERROR; } return Zlib_StreamGet((ZlibHandle)zsh, obj, count); case 3: /* flush */ Tcl_SetObjLength(obj, 0); return Zlib_StreamPut((ZlibHandle)zsh, obj, Z_SYNC_FLUSH); case 4: /* fullflush */ Tcl_SetObjLength(obj, 0); return Zlib_StreamPut((ZlibHandle)zsh, obj, Z_FULL_FLUSH); case 5: /* finalize */ /* The flush commands slightly abuse the empty result obj as input data */ Tcl_SetObjLength(obj, 0); return Zlib_StreamPut((ZlibHandle)zsh, obj, Z_FINISH); case 6: /* close */ return Zlib_StreamClose((ZlibHandle)zsh); case 7: /* eof */ Tcl_SetIntObj(obj, Zlib_StreamEof((ZlibHandle)zsh)); return TCL_OK; case 8: /* adler32 */ Tcl_SetIntObj(obj, Zlib_StreamAdler32((ZlibHandle)zsh)); return TCL_OK; case 9: /* reset */ return Zlib_StreamReset((ZlibHandle)zsh); } return TCL_OK; } /* * Set of functions to support channel stacking * * */ #ifdef ENABLE_CHANSTACKING int ChanClose(ClientData instanceData, Tcl_Interp *interp) { Zlib_ChannelData *cd=instanceData; Tcl_Channel parent; int e; parent = Tcl_GetStackedChannel (cd->channel); if ( cd->inFormat != ZLIB_PASSTHROUGH ) { if ( cd->inFormat && ZLIB_INFLATE ) { e = inflateEnd(&(cd->instream)); } else { e = deflateEnd(&(cd->instream)); } } if ( cd->outFormat != ZLIB_PASSTHROUGH ) { if ( cd->outFormat && ZLIB_INFLATE ) { e = inflateEnd(&(cd->outstream)); } else { e = deflateEnd(&(cd->outstream)); } } if ( cd->inbuffer ) { ckfree(cd->inbuffer); cd->inbuffer=NULL; } if ( cd->outbuffer ) { ckfree(cd->outbuffer); cd->outbuffer=NULL; } return TCL_OK; } int ChanInput(ClientData instanceData, char *buf, int toRead, int *errorCodePtr) { Zlib_ChannelData *cd=instanceData; return TCL_OK; } int ChanOutput(ClientData instanceData, CONST84 char *buf, int toWrite, int *errorCodePtr) { Zlib_ChannelData *cd=instanceData; return TCL_OK; } int ChanSeek(ClientData instanceData, long offset, int mode, int *errorCodePtr) { return TCL_OK; } int ChanSetOption(ClientData instanceData, Tcl_Interp *interp, CONST char *optionName, CONST char *value) { /* not used */ Zlib_ChannelData *cd; cd=instanceData; Tcl_Channel parent = Tcl_GetStackedChannel (cd->channel); Tcl_DriverSetOptionProc *setOptionProc = Tcl_ChannelSetOptionProc (Tcl_GetChannelType (parent)); if (setOptionProc != NULL) { return (*setOptionProc) (Tcl_GetChannelInstanceData (parent), interp, optionName, value); } else { return TCL_ERROR; } } int ChanGetOption(ClientData instanceData, Tcl_Interp *interp, CONST84 char *optionName, Tcl_DString *dsPtr) { /* not used */ return TCL_OK; } void ChanWatch(ClientData instanceData, int mask) { return; } int ChanGetHandle(ClientData instanceData, int direction, ClientData *handlePtr) { /* no such thing as an OS handle for Zlib: */ return 0; } int ChanClose2(ClientData instanceData, Tcl_Interp *interp, int flags) { /* not used */ return TCL_OK; } int ChanBlockMode(ClientData instanceData, int mode) { Zlib_ChannelData *cd=instanceData; if (mode == TCL_MODE_NONBLOCKING) { cd->flags |= ASYNC; } else { cd->flags &= ~ASYNC; } return TCL_OK; } int ChanFlush(ClientData instanceData) { Zlib_ChannelData *cd=instanceData; return TCL_OK; } int ChanHandler(ClientData instanceData, int interestMask) { Zlib_ChannelData *cd=instanceData; return TCL_OK; } Tcl_WideInt ChanWideSeek(ClientData instanceData, Tcl_WideInt offset, int mode, int *errorCodePtr) { /* not used */ return TCL_OK; } Tcl_Channel Zlib_StackChannel(interp, inFormat, inLevel, outFormat, outLevel, channel) Tcl_Interp *interp; int inFormat; int inLevel; int outFormat; int outLevel; Tcl_Channel channel; { Zlib_ChannelData *cd; int outwbits=0, inwbits=0; int e; if ( inFormat && ZLIB_FORMAT_RAW ) { inwbits = -MAX_WBITS; } else if (inFormat && ZLIB_FORMAT_GZIP) { inwbits = MAX_WBITS+16; } else if (inFormat && ZLIB_FORMAT_ZLIB) { inwbits = MAX_WBITS; } else if ((inFormat && ZLIB_FORMAT_AUTO) && (inFormat && ZLIB_INFLATE)) { inwbits = MAX_WBITS+32; } else if (inFormat != ZLIB_PASSTHROUGH) { Tcl_SetResult(interp, "Incorrect zlib read/input data format, must be ZLIB_FORMAT_ZLIB, ZLIB_FORMAT_GZIP, ZLIB_FORMAT_ZLIB or ZLIB_FORMAT_AUTO (only for inflate).", TCL_STATIC); return NULL; } if ( outFormat && ZLIB_FORMAT_RAW ) { outwbits = -MAX_WBITS; } else if (outFormat && ZLIB_FORMAT_GZIP) { outwbits = MAX_WBITS+16; } else if (outFormat && ZLIB_FORMAT_ZLIB) { outwbits = MAX_WBITS; } else if ((outFormat && ZLIB_FORMAT_AUTO) && (outFormat && ZLIB_INFLATE)) { outwbits = MAX_WBITS+32; } else if (outFormat != ZLIB_PASSTHROUGH) { Tcl_SetResult(interp, "Incorrect zlib write/ouput data format, must be ZLIB_FORMAT_ZLIB, ZLIB_FORMAT_GZIP, ZLIB_FORMAT_ZLIB or ZLIB_FORMAT_AUTO (only for inflate).", TCL_STATIC); return NULL; } cd = (Zlib_ChannelData*) ckalloc(sizeof(Zlib_ChannelData)); if (!cd) return NULL; 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; if ( inFormat != ZLIB_PASSTHROUGH ) { if ( inFormat && ZLIB_INFLATE ) { /* Initialize for Inflate */ e = inflateInit2(&(cd->instream), inwbits); } else { /* Initialize for Deflate */ e = deflateInit2(&(cd->instream), inLevel, Z_DEFLATED, inwbits, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); } } if ( outFormat != ZLIB_PASSTHROUGH ) { if ( outFormat && ZLIB_INFLATE ) { /* Initialize for Inflate */ e = inflateInit2(&(cd->outstream), outwbits); } else { /* Initialize for Deflate */ e = deflateInit2(&(cd->outstream), outLevel, Z_DEFLATED, outwbits, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); } } cd->channel = Tcl_StackChannel(interp, &zlibChannelType, cd, TCL_READABLE || TCL_WRITABLE || TCL_EXCEPTION, channel); Tcl_SetObjResult(interp, Tcl_NewStringObj(Tcl_GetChannelName(channel), -1)); return channel; } #endif /* ENABLE_CHANSTACKING */ /* * Finaly, the Zlib_Init command. * */ extern ZlibStubs zlibStubs; int Zlib_Init(Tcl_Interp *interp) { #ifdef USE_TCL_STUBS if (Tcl_InitStubs(interp, "8.1", 0) == NULL) { return TCL_ERROR; } #endif if (Tcl_PkgProvideEx(interp, PACKAGE_NAME, PACKAGE_VERSION, &zlibStubs) != TCL_OK) { return TCL_ERROR; } Tcl_Eval(interp, "namespace eval ::zlib {set cmdcounter 0}"); Tcl_CreateObjCommand(interp, "zlib", ZlibCmd, 0, 0); return TCL_OK; }