diff --git a/src/modules/Gz/configure.in b/src/modules/Gz/configure.in index 91ecb5fee47e62994524772aaa17c3ce1d09343d..7fe0b68d1dab7a27639fbbb7b5b2cfb39aab84f3 100644 --- a/src/modules/Gz/configure.in +++ b/src/modules/Gz/configure.in @@ -1,4 +1,4 @@ -# $Id: configure.in,v 1.24 2002/09/10 14:31:26 grubba Exp $ +# $Id: configure.in,v 1.25 2003/04/14 17:10:57 marcus Exp $ AC_INIT(zlibmod.c) AC_CONFIG_HEADER(zlib_machine.h) AC_ARG_WITH(zlib, [ --with(out)-zlib Support gzip compression],[],[with_zlib=yes]) @@ -68,9 +68,6 @@ int foo = (int)(Z_NO_COMPRESSION | Z_VERSION_ERROR); AC_CHECK_GZ(z,[AC_CHECK_GZ(gz,[ ac_cv_lib_z_main=no ] ) ]) - if test "x$done" = "xyes"; then - AC_HAVE_FUNCS(gzseek gztell gzeof gzsetparams) - fi fi fi fi diff --git a/src/modules/Gz/module.pmod.in b/src/modules/Gz/module.pmod.in index 6bb3c466dfc7e0d506c04b3664b87d1b19a5fd63..949189dd237bb4725c945019a5e746061b25e3d0 100644 --- a/src/modules/Gz/module.pmod.in +++ b/src/modules/Gz/module.pmod.in @@ -1,9 +1,262 @@ -// $Id: module.pmod.in,v 1.8 2002/11/25 15:47:34 grubba Exp $ +// $Id: module.pmod.in,v 1.9 2003/04/14 17:10:57 marcus Exp $ #pike __REAL_VERSION__ inherit @module@; -#if constant(@module@._file) +//! Low-level implementation of read/write support for GZip files +class _file { + + static private Stdio.Stream f; + static private inflate inf; + static private deflate def; + static private int level, strategy; + static private string read_buf; + static private int file_pos, crc, write_mode; + + constant SEEK_SET = 0; + constant SEEK_CUR = 1; + constant SEEK_END = 2; + + static int check_header() + { + int magic1, magic2, method, flags, len; + + if(sscanf(f->read(4), "%1c%1c%1c%1c", magic1, magic2, method, flags)!=4 || + magic1 != 0x1f || magic2 != 0x8b) + return 0; + + if(method != 8 || (flags & 0xe0)) + return 0; + + if(sizeof(f->read(6)) != 6) + return 0; + + if(flags & 4) + if(sscanf(f->read(2), "%-2c", len) != 1 || + sizeof(f->read(len)) != len) + return 0; + + if(flags & 8) + loop: for(;;) + switch(f->read(1)) { + case 0: case "": return 0; + case "\0": break loop; + } + + if(flags & 16) + loop: for(;;) + switch(f->read(1)) { + case 0: case "": return 0; + case "\0": break loop; + } + + if(flags & 2) + if(sizeof(f->read(2)) != 2) + return 0; + + return 1; + } + + static int make_header() + { + return f->write(sprintf("%1c%1c%1c%1c%4c%1c%1c", + 0x1f, 0x8b, 8, 0, 0, 0, 3)) == 10; + } + + //! Opens a file for I/O. + //! + //! @param file + //! The filename or an open filedescriptor or Stream for the GZip + //! file to use. + //! @param mode + //! Mode for the fileoperations. Defaults to read only. + //! + //! @note + //! If the object already has been opened, it will first be closed. + int open(string|int|Stdio.Stream file, void|string mode) + { + close(); + write_mode = 0; + level = 6; + strategy = DEFAULT_STRATEGY; + if(mode) { + mode = filter(mode, lambda(int n) { + if(n == 'w' || n == 'a') + write_mode = 1; + if(n >= '0' && n <= '9') + level = n - '0'; + else if(n == 'f') + strategy = FILTERED; + else if(n == 'h') + strategy = HUFFMAN_ONLY; + else + return 1; + }); + if(write_mode) { + if(!has_value(mode, 'a')) + mode += "t"; + mode += "c"; + } + } + file_pos = 0; + if(objectp(file)) + f = file; + else { + f = Stdio.File(); + if(!f->open(file, mode||"rb")) + return 0; + } + return write_mode? make_header() : check_header(); + } + + //! Opens a gzip file for reading. + static void create(void|string|Stdio.Stream gzFile, void|string mode) + { + if(!zero_type(gzFile) && !open(gzFile, mode)) + error("Failed to open file.\n"); + } + + //! closes the file + //! @returns + //! 1 if successful + int close() + { + if(def) { + string s = def->deflate("", FINISH); + if(sizeof(s) && f->write(s) != sizeof(s)) + return 0; + if(f->write(sprintf("%-4c%-4c", crc, file_pos)) != 8) + return 0; + } + inf = 0; + def = 0; + read_buf = ""; + Stdio.File oldf = f; + f = 0; + return !oldf || oldf->close(); + } + + //! Reads len (uncompressed) bytes from the file. + //! If read is unsuccessful, 0 is returned. + int|string read(int len) + { + if(!inf) inf = inflate(-15); + while(sizeof(read_buf) < len) { + string r = f->read(16384); + if(!sizeof(r)) + break; + read_buf += inf->inflate(r); + } + string res = read_buf[..len-1]; + read_buf = read_buf[len..]; + file_pos += sizeof(res); + return res; + } + + //! Writes the data to the file. + //! @returns + //! the number of bytes written to the file. + int write(string data) + { + if(!def) { def = deflate(-level, strategy); crc = crc32(""); } + string comp = def->deflate(data, NO_FLUSH); + if(f->write(comp) != sizeof(comp)) + return 0; + else { + file_pos += sizeof(data); + crc = crc32(data, crc); + return sizeof(data); + } + } + + //! Seeks within the file. + //! @param pos + //! Position relative to the searchtype. + //! @param type + //! SEEK_SET = set current position in file to pos + //! SEEK_CUR = new position is current+pos + //! SEEK_END is not supported. + //! @returns + //! New position or negative number if seek failed. + int seek(int pos, void|int type) + { + if(type != SEEK_SET && type != SEEK_CUR) + return -1; + if(write_mode) { + if(type == SEEK_SET) + pos -= file_pos; + if(pos < 0) + return -1; + while(pos > 0) { + int n = write("\0"*(pos>16384? 16384:pos)); + if(!n) + return -1; + pos -= n; + } + return file_pos; + } else { + if(type == SEEK_CUR) + pos += file_pos; + if(pos < 0) + return -1; + if(!f->seek || f->seek(0)<0) + return -1; + file_pos = 0; + read_buf = ""; + while(pos > 0) { + string r = read(pos>16384? 16384:pos); + if(!sizeof(r)) + return -1; + pos -= sizeof(r); + } + return file_pos; + } + } + + //! @returns + //! the current position within the file. + int tell() + { + return file_pos; + } + + //! @returns + //! 1 if EOF has been reached. + int(0..1) eof() + { + if(def || sizeof(read_buf)) return 0; + if(!inf) inf = inflate(-15); + string r = f->read(16384); + if(!sizeof(r)) + return 1; + read_buf = inf->inflate(r); + return !sizeof(read_buf); + } + + //! Sets the encoding level and strategy + //! @param level + //! Level of the compression. + //! 0 is the least compression, 9 is max. 8 is default. + //! @param strategy + //! Set strategy for encoding to one of the following: + //! DEFAULT_STRATEGY + //! FILTERED + //! HUFFMAN_ONLY + int setparams(int level, int strategy) + { + if(def) { + string s = def->deflate("", FINISH); + if(sizeof(s) && f->write(s) != sizeof(s)) + return 0; + def = 0; + } + _file::level = level; + _file::strategy = strategy; + return 1; + } + +} + //! Allows the user to open a Gzip archive and read and write //! it's contents in an uncompressed form, emulating the @[Stdio.File] @@ -19,9 +272,10 @@ class File { private int is_open = 0; - //! @decl void create(void|string|int file, void|string mode) + //! @decl void create(void|string|int|Stdio.Stream file, void|string mode) //! @param file - //! Filename or filedescriptor of the gzip file to open. + //! Filename or filedescriptor of the gzip file to open, or an already + //! open Stream. //! @param mode //! mode for the file. Defaults to "rb". //! @seealso @@ -57,7 +311,8 @@ class File { } //! @param file - //! Filename or filedescriptor of the gzip file to open. + //! Filename or filedescriptor of the gzip file to open, or an already + //! open Stream. //! @param mode //! mode for the file. Defaults to "rb". //! May be one of the following: @@ -73,7 +328,7 @@ class File { //! be specified. Please se zlib manual for more info. //! @returns //! non-zero if successful. - int open(string|int file, void|string mode) { + int open(string|int|Stdio.Stream file, void|string mode) { string open_mode="rb"; if (is_open) { ::close(); @@ -107,4 +362,3 @@ class File { } } -#endif /* constant(@module@._file) */ diff --git a/src/modules/Gz/zlibmod.c b/src/modules/Gz/zlibmod.c index ea37738bac5eb08031c032a6eef538398ca5faa8..13e3f5a1b933ea933c7a94387c99b74517625deb 100644 --- a/src/modules/Gz/zlibmod.c +++ b/src/modules/Gz/zlibmod.c @@ -2,11 +2,11 @@ || This file is part of Pike. For copyright information see COPYRIGHT. || Pike is distributed under GPL, LGPL and MPL. See the file COPYING || for more information. -|| $Id: zlibmod.c,v 1.62 2003/03/31 14:26:42 per Exp $ +|| $Id: zlibmod.c,v 1.63 2003/04/14 17:10:57 marcus Exp $ */ #include "global.h" -RCSID("$Id: zlibmod.c,v 1.62 2003/03/31 14:26:42 per Exp $"); +RCSID("$Id: zlibmod.c,v 1.63 2003/04/14 17:10:57 marcus Exp $"); #include "zlib_machine.h" #include "module.h" @@ -39,7 +39,6 @@ struct zipper int level; int state; struct z_stream_s gz; - gzFile gzfile; #ifdef _REENTRANT DEFINE_MUTEX(lock); #endif /* _REENTRANT */ @@ -84,9 +83,9 @@ struct zipper *! @[Gz.inflate()] */ -/*! @decl void create(int(0..9)|void X) +/*! @decl void create(int(0..9)|void level, int|void strategy) *! - *! If given, @[X] should be a number from 0 to 9 indicating the + *! If given, @[level] should be a number from 0 to 9 indicating the *! packing / CPU ratio. Zero means no packing, 2-3 is considered 'fast', *! 6 is default and higher is considered 'slow' but gives better packing. *! @@ -96,10 +95,14 @@ struct zipper *! If the argument is negative, no headers will be emitted. This is *! needed to produce ZIP-files, as an example. The negative value is *! then negated, and handled as a positive value. + *! + *! @[strategy], if given, should be one of DEFAULT_STRATEGY, FILTERED or + *! HUFFMAN_ONLY. */ static void gz_deflate_create(INT32 args) { int tmp, wbits = 15; + int strategy = Z_DEFAULT_STRATEGY; THIS->level=Z_DEFAULT_COMPRESSION; if(THIS->gz.state) @@ -126,13 +129,25 @@ static void gz_deflate_create(INT32 args) } } + if(args>1) + { + if(sp[1-args].type != T_INT) + Pike_error("Bad argument 2 to gz->create()\n"); + strategy=sp[1-args].u.integer; + if(strategy != Z_DEFAULT_STRATEGY && strategy != Z_FILTERED && + strategy != Z_HUFFMAN_ONLY) + { + Pike_error("Invalid compression strategy for gz_deflate->create()\n"); + } + } + THIS->gz.zalloc=Z_NULL; THIS->gz.zfree=Z_NULL; THIS->gz.opaque=(void *)THIS; pop_n_elems(args); /* mt_lock(& THIS->lock); */ - tmp=deflateInit2(&THIS->gz, THIS->level, Z_DEFLATED, wbits, 9, Z_DEFAULT_STRATEGY ); + tmp=deflateInit2(&THIS->gz, THIS->level, Z_DEFLATED, wbits, 9, strategy ); /* mt_unlock(& THIS->lock); */ switch(tmp) { @@ -560,312 +575,6 @@ static void gz_crc32(INT32 args) push_int((INT32)crc); } -/*! @class _file - *! Low-level implementation of read/write support for GZip files - */ - -/*! @decl int open(string|int file, void|string mode) - *! Opens a file for I/O. - *! @param file - *! The filename or an open filedescriptor for the GZip file to use. - *! @param mode - *! Mode for the fileoperations. Defaults to read only. - *! - *! @note - *! If the object already has been opened, it will first be closed. - */ -void gz_file_open(INT32 args) -{ - char *mode = "rb"; - - if (THIS->gzfile!=NULL) { - gzclose(THIS->gzfile); - THIS->gzfile = NULL; - } - - if (args<1 || args>2) { - Pike_error("Bad number of arguments to file->open()\n" - "Got %d, expected 1 or 2.\n", args); - } - - if (sp[-args].type != PIKE_T_STRING && - sp[-args].type != PIKE_T_INT) { - Pike_error("Bad parameter 1 to file->open()\n"); - } - - if (args == 2 && sp[1-args].type != PIKE_T_STRING) { - Pike_error("Bad parameter 2 to file->open()\n"); - } else if (args == 2) { - mode = STR0(sp[1-args].u.string); - } - - if (sp[-args].type == PIKE_T_INT) { - /* FIXME: This is not likely to work on NT. */ - THIS->gzfile = gzdopen(sp[-args].u.integer, mode); - } else { - THIS->gzfile = gzopen(STR0(sp[-args].u.string), mode); - } - - pop_n_elems(args); - push_int(THIS->gzfile != NULL); -} - -/*! @decl void create(void|string gzFile, void|string mode) - *! Opens a gzip file for reading. - */ -void gz_file_create(INT32 args) -{ - THIS->gzfile = NULL; - if (args) { - gz_file_open(args); - if (sp[-1].u.integer == 0) { - Pike_error("Failed to open file.\n"); - } - pop_stack(); - } -} - -/*! @decl int close() - *! closes the file - *! @returns - *! 1 if successful - */ -void gz_file_close(INT32 args) -{ - if (args!=0) { - Pike_error("Bad number of arguments to file->close()\n" - "Got %d, expected 0.\n", args); - } - - if (THIS->gzfile!=NULL) { - gzclose(THIS->gzfile); - THIS->gzfile = NULL; - } - - push_int(1); -} - -/*! @decl int|string read(int len) - *! Reads len (uncompressed) bytes from the file. - *! If read is unsuccessful, 0 is returned. - */ -void gz_file_read(INT32 args) -{ - struct pike_string *buf; - int len; - int res; - - if (args!=1) { - Pike_error("Bad number of arguments to gz_file->read()\n" - "Got %d, expected 1.\n", args); - } - - if (sp[-args].type != PIKE_T_INT) { - Pike_error("Bad argument 1 to gz_file->read()\n"); - } - - if (THIS->gzfile == NULL) { - Pike_error("File not open!\n"); - } - - len = sp[-args].u.integer; - - buf = begin_shared_string(len); - - pop_n_elems(args); - - res = gzread(THIS->gzfile, STR0(buf), len); - - /* Check to make sure read went well */ - if (res<0) { - push_int(0); - free_string(end_shared_string(buf)); - return; - } - - /* Make sure the returned string is the same length as - * the data read. - */ - push_string(end_and_resize_shared_string(buf, res)); -} - -/*! @decl int write(string data) - *! Writes the data to the file. - *! @returns - *! the number of bytes written to the file. - */ -void gz_file_write(INT32 args) -{ - int res = 0; - - if (args!=1) { - Pike_error("Bad number of arguments to gz_file->write()\n" - "Got %d, expected 1.\n", args); - } - - if (sp[-args].type != PIKE_T_STRING) { - Pike_error("Bad argument 1 to gz_file->write()\n"); - } - - if (THIS->gzfile == NULL) { - Pike_error("File not open!\n"); - } - - res = gzwrite(THIS->gzfile, - sp[-args].u.string->str, - (unsigned INT32)sp[-args].u.string->len); - - pop_n_elems(args); - push_int(res); -} - -#ifdef HAVE_GZSEEK -/*! @decl int seek(int pos, void|int type) - *! Seeks within the file. - *! @param pos - *! Position relative to the searchtype. - *! @param type - *! SEEK_SET = set current position in file to pos - *! SEEK_CUR = new position is current+pos - *! SEEK_END is not supported. - *! @returns - *! New position or negative number if seek failed. - *! - *! @note - *! Not supported on all operating systems. - */ -void gz_file_seek(INT32 args) -{ - int res, newpos; - int type = SEEK_SET; - - if (args>2) { - Pike_error("Bad number of arguments to file->seek()\n" - "Got %d, expected 1 or 2.\n", args); - } - - if (sp[-args].type != PIKE_T_INT) { - Pike_error("Bad argument 1 to file->seek()\n"); - } - - if (args == 2 && sp[1-args].type != PIKE_T_INT) { - Pike_error("Bad argument 2 to file->seek()\n"); - } - else if (args == 2) { - type = sp[1-args].u.integer; - } - - if (THIS->gzfile == NULL) { - Pike_error("File not open!\n"); - } - - newpos = sp[-args].u.integer; - - pop_n_elems(args); - - res = gzseek(THIS->gzfile, newpos, type); - - push_int(res); -} -#endif /* HAVE_GZSEEK */ - -#ifdef HAVE_GZTELL -/*! @decl int tell() - *! @returns - *! the current position within the file. - *! - *! @note - *! Not supported on all operating systems. - */ -void gz_file_tell(INT32 args) -{ - if (args!=0) { - Pike_error("Bad number of arguments to file->tell()\n" - "Got %d, expected 0.\n", args); - } - - if (THIS->gzfile == NULL) { - Pike_error("File not open!\n"); - } - - push_int(gztell(THIS->gzfile)); - -} -#endif /* HAVE_GZTELL */ - -#ifdef HAVE_GZEOF -/*! @decl int(0..1) eof() - *! @returns - *! 1 if EOF has been reached. - *! - *! @note - *! Not supported on all operating systems. - */ -void gz_file_eof(INT32 args) -{ - if (args!=0) { - Pike_error("Bad number of arguments to file->eof()\n" - "Got %d, expected 0.\n", args); - } - - push_int(gzeof(THIS->gzfile)); -} -#endif /* HAVE_GZEOF */ - -#ifdef HAVE_GZSETPARAMS -/*! @decl int setparams(int level, int strategy) - *! Sets the encoding level and strategy - *! @param level - *! Level of the compression. - *! 0 is the least compression, 9 is max. 8 is default. - *! @param strategy - *! Set strategy for encoding to one of the following: - *! Z_DEFAULT_STRATEGY - *! Z_FILTERED - *! Z_HUFFMAN_ONLY - *! - *! @note - *! Not supported on all operating systems. - */ -void gz_file_setparams(INT32 args) -{ - int res; - if (args!=2) { - Pike_error("Bad number of arguments to file->setparams()\n" - "Got %d, expected 2.\n", args); - } - - if (sp[-args].type != PIKE_T_INT || - sp[1-args].type != PIKE_T_INT) { - Pike_error("Bad type in argument\n"); - } - - res = gzsetparams(THIS->gzfile, - sp[-args].u.integer, - sp[1-args].u.integer); - - pop_n_elems(args); - push_int(res == Z_OK); -} -#endif /* HAVE_GZSETPARAMS */ - -static void init_gz_file(struct object *o) -{ - mt_init(& THIS->lock); - THIS->gzfile = NULL; -} - -static void exit_gz_file(struct object *o) -{ - if (THIS->gzfile != NULL) - gzclose(THIS->gzfile); - - mt_destroy( & THIS->lock ); -} - -/*! @endclass - */ - /*! @endmodule */ #endif @@ -878,8 +587,8 @@ PIKE_MODULE_INIT start_new_program(); ADD_STORAGE(struct zipper); - /* function(int|void:void) */ - ADD_FUNCTION("create",gz_deflate_create,tFunc(tOr(tInt,tVoid),tVoid),0); + /* function(int|void,int|void:void) */ + ADD_FUNCTION("create",gz_deflate_create,tFunc(tOr(tInt,tVoid) tOr(tInt,tVoid),tVoid),0); /* function(string,int|void:string) */ ADD_FUNCTION("deflate",gz_deflate,tFunc(tStr tOr(tInt,tVoid),tStr),0); @@ -887,6 +596,9 @@ PIKE_MODULE_INIT add_integer_constant("PARTIAL_FLUSH",Z_PARTIAL_FLUSH,0); add_integer_constant("SYNC_FLUSH",Z_SYNC_FLUSH,0); add_integer_constant("FINISH",Z_FINISH,0); + add_integer_constant("DEFAULT_STRATEGY", Z_DEFAULT_STRATEGY,0); + add_integer_constant("FILTERED", Z_FILTERED,0); + add_integer_constant("HUFFMAN_ONLY", Z_HUFFMAN_ONLY,0); set_init_callback(init_gz_deflate); set_exit_callback(exit_gz_deflate); @@ -911,40 +623,13 @@ PIKE_MODULE_INIT end_class("inflate",0); - start_new_program(); - ADD_STORAGE(struct zipper); - - ADD_FUNCTION("create", gz_file_create, tFunc(tOr(tVoid, tString) tOr(tVoid, tString), tVoid), 0); - ADD_FUNCTION("open", gz_file_open, tFunc(tString tOr(tVoid, tString), tInt), 0); - ADD_FUNCTION("close", gz_file_close, tFunc(tVoid, tInt), 0); - ADD_FUNCTION("read", gz_file_read, tFunc(tInt,tOr(tString,tInt)), 0); - ADD_FUNCTION("write", gz_file_write, tFunc(tString,tInt), 0); -#ifdef HAVE_GZSEEK - ADD_FUNCTION("seek", gz_file_seek, tFunc(tInt tOr(tVoid,tInt), tInt), 0); -#endif /* HAVE_GZSEEK */ -#ifdef HAVE_GZTELL - ADD_FUNCTION("tell", gz_file_tell, tFunc(tVoid, tInt), 0); -#endif /* HAVE_GZTELL */ -#ifdef HAVE_GZEOF - ADD_FUNCTION("eof", gz_file_eof, tFunc(tVoid, tInt), 0); -#endif /* HAVE_GZEOF */ -#ifdef HAVE_GZSETPARAMS - ADD_FUNCTION("setparams", gz_file_setparams, tFunc(tInt tInt, tInt), 0); -#endif /* HAVE_GZSETPARAMS */ - - add_integer_constant("SEEK_SET", SEEK_SET, 0); - add_integer_constant("SEEK_CUR", SEEK_CUR, 0); - add_integer_constant("Z_DEFAULT_STRATEGY", Z_DEFAULT_STRATEGY,0); - add_integer_constant("Z_FILTERED", Z_FILTERED,0); - add_integer_constant("Z_HUFFMAN_ONLY", Z_HUFFMAN_ONLY,0); - set_init_callback(init_gz_file); - set_exit_callback(exit_gz_file); - end_class("_file", 0); - add_integer_constant("NO_FLUSH",Z_NO_FLUSH,0); add_integer_constant("PARTIAL_FLUSH",Z_PARTIAL_FLUSH,0); add_integer_constant("SYNC_FLUSH",Z_SYNC_FLUSH,0); add_integer_constant("FINISH",Z_FINISH,0); + add_integer_constant("DEFAULT_STRATEGY", Z_DEFAULT_STRATEGY,0); + add_integer_constant("FILTERED", Z_FILTERED,0); + add_integer_constant("HUFFMAN_ONLY", Z_HUFFMAN_ONLY,0); /* function(string,void|int:int) */ ADD_FUNCTION("crc32",gz_crc32,tFunc(tStr tOr(tVoid,tInt),tInt),