diff options
Diffstat (limited to 'Bzip2.xs')
-rw-r--r-- | Bzip2.xs | 2619 |
1 files changed, 2619 insertions, 0 deletions
diff --git a/Bzip2.xs b/Bzip2.xs new file mode 100644 index 0000000..b675820 --- /dev/null +++ b/Bzip2.xs @@ -0,0 +1,2619 @@ +/* Bzip2.xs -- Bzip2 bindings for Perl5 -- -*- mode: c -*- */ + +#include "EXTERN.h" +#include "perl.h" +#include "XSUB.h" + +#include "ppport.h" + +#include <bzlib.h> + +#include "const-c.inc" + +typedef unsigned char Bool; + +#define True ((Bool)1) +#define False ((Bool)0) + +#define BZ_IO_EOF (-100) + +#define BZERRNO "Compress::Bzip2::bzerrno" +int global_bzip_errno = 0; + +#define BZ_SETERR(obj, eee, infomsg) bzfile_seterror(obj, eee, infomsg) + +#define OPEN_STATUS_ISCLOSED 0 +#define OPEN_STATUS_READ 1 +#define OPEN_STATUS_WRITE 2 +#define OPEN_STATUS_WRITESTREAM 3 +#define OPEN_STATUS_READSTREAM 4 + +typedef struct bzFile_s { + bz_stream strm; + PerlIO* handle; + int bzip_errno; + + char bufferOfCompressed[BZ_MAX_UNUSED]; + int nCompressed; + int compressedOffset_addmore; + int compressedOffset_takeout; + + char bufferOfHolding[BZ_MAX_UNUSED]; + int nHolding; + + char bufferOfLines[BZ_MAX_UNUSED]; + int bufferOffset; + int nBufferBytes; + + char* streamBuf; + int streamBufSize; + int streamBufLen; + int streamBufOffset; + + int open_status; + int run_progress; + int io_error; + Bool pending_io_error; + + Bool allowUncompressedRead; + Bool notCompressed; + int scan_BZh9; + char BZh9[5]; + int BZh9_count; + + int verbosity; + int small; + int blockSize100k; + int workFactor; + + long total_in; + long total_out; +} bzFile ; + +typedef bzFile* Compress__Bzip2; + +#ifdef CAN_PROTOTYPE +void bzfile_streambuf_set( bzFile* obj, char* buffer, int bufsize ); +int bzfile_closeread( bzFile* obj, int abandon ); +int bzfile_closewrite( bzFile* obj, int abandon ); +int bzfile_read( bzFile* obj, char *bufferOfUncompress, int nUncompress ); +#else +void bzfile_streambuf_set( ); +int bzfile_closeread( ); +int bzfile_closewrite( ); +int bzfile_read( ); +#endif + + +static SV* +#ifdef CAN_PROTOTYPE +deRef(SV * sv, char * string) +#else +deRef(sv, string) +SV * sv ; +char * string; +#endif +{ + SV *last_sv = NULL; + + while(SvROK(sv) && sv != last_sv) { + last_sv = sv; + sv = SvRV(sv) ; + switch(SvTYPE(sv)) { + case SVt_PVAV: + case SVt_PVHV: + case SVt_PVCV: + croak("%s: buffer parameter is not a SCALAR reference", string); + break; + default: + ; + } + /* if (SvROK(sv)) + croak("%s: buffer parameter is a reference to a reference", string) ;*/ + } + + if (!SvOK(sv)) croak("%s: buffer parameter is not a SCALAR reference", string); + /*sv = newSVpv("", 0);*/ + + return sv ; +} + +static char *bzerrorstrings[] = { + "OK" + ,"SEQUENCE_ERROR" + ,"PARAM_ERROR" + ,"MEM_ERROR" + ,"DATA_ERROR" + ,"DATA_ERROR_MAGIC" + ,"IO_ERROR" + ,"UNEXPECTED_EOF" + ,"OUTBUFF_FULL" + ,"CONFIG_ERROR" + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ +}; + + +/* memory allocator */ +static void* +#ifdef CAN_PROTOTYPE +bzmemalloc(void* opaque, int n, int m) +#else +bzalloc(opaque,n,m) void* opaque; int n; int m; +#endif +{ + New(0,opaque,n*m,char); + return opaque; +} + +/* memory deallocator */ +static void +#ifdef CAN_PROTOTYPE +bzmemfree(void* opaque, void* p) +#else +bzfree(opaque, p) void* opaque; void* p; +#endif +{ + Safefree(p); +} + +int +#ifdef CAN_PROTOTYPE +bzfile_seterror(bzFile* obj, int error_num, char *error_info) +#else +bzfile_seterror(obj, error_num, error_info) +bzFile* obj; +int error_num; +char* error_info; +#endif +{ + char *errstr ; + SV * bzerror_sv = perl_get_sv(BZERRNO, FALSE) ; + + global_bzip_errno = error_num; + sv_setiv(bzerror_sv, error_num) ; /* set the integer part of the perl thing */ + + errstr = error_num * -1 < 0 || error_num * -1 > 9 ? "Unknown" : (char *) bzerrorstrings[ error_num * -1 ]; + + if ( obj!=NULL ) { + obj->bzip_errno = error_num; + obj->io_error = error_num == BZ_IO_ERROR ? errno : 0; + } + + /* set the string part of the perl thing */ + if ( error_info == NULL ) { + if (error_num == BZ_IO_ERROR) + sv_setpvf(bzerror_sv, "%s (%d): %d %s", errstr, error_num, errno, Strerror(errno)); + else + sv_setpvf(bzerror_sv, "%s (%d)", errstr, error_num); + } + else { + if (error_num == BZ_IO_ERROR) + sv_setpvf(bzerror_sv, "%s (%d): %s - %d %s", errstr, error_num, error_info, errno, Strerror(errno)); + else + sv_setpvf(bzerror_sv, "%s (%d): %s", errstr, error_num, error_info); + } + + SvIOK_on(bzerror_sv) ; /* say "I AM INTEGER (too)" */ + + return error_num; +} + +#ifdef CAN_PROTOTYPE +PerlIO* bzfile_getiohandle( bzFile *obj ) { +#else +PerlIO* bzfile_getiohandle( obj ) bzFile *obj; { +#endif + return obj->handle; +} + +#ifdef CAN_PROTOTYPE +Bool bzfile_error( bzFile *obj ) { +#else +Bool bzfile_error( obj ) bzFile *obj; { +#endif + return obj!=NULL ? ( obj->bzip_errno ? True : False ) : global_bzip_errno ? True : False; +} + +#ifdef CAN_PROTOTYPE +int bzfile_geterrno( bzFile *obj ) { +#else +int bzfile_geterrno( obj ) bzFile *obj; { +#endif + return obj==NULL ? global_bzip_errno : obj->bzip_errno; +} + +#ifdef CAN_PROTOTYPE +const char *bzfile_geterrstr( bzFile *obj ) { +#else +const char *bzfile_geterrstr( obj ) bzFile *obj; { +#endif + int error_num = obj==NULL ? global_bzip_errno : obj->bzip_errno; + char *errstr = error_num * -1 < 0 || error_num * -1 > 9 ? "Unknown" : (char *) bzerrorstrings[ error_num * -1 ]; + return errstr; +} + +#ifdef CAN_PROTOTYPE +Bool bzfile_eof( bzFile *obj ) { +#else +Bool bzfile_eof( obj ) bzFile *obj; { +#endif + return obj==NULL ? False : + obj->bzip_errno == BZ_UNEXPECTED_EOF ? True : + obj->bzip_errno == BZ_OK && obj->pending_io_error && obj->io_error == BZ_IO_EOF ? True : + obj->bzip_errno != BZ_IO_ERROR ? False : + obj->io_error == BZ_IO_EOF ? True : False; +} + +#ifdef CAN_PROTOTYPE +long bzfile_total_in( bzFile *obj ) { +#else +long bzfile_total_in( obj ) bzFile *obj; { +#endif + return obj->total_in; +} + +#ifdef CAN_PROTOTYPE +long bzfile_total_out( bzFile *obj ) { +#else +long bzfile_total_out( obj ) bzFile *obj; { +#endif + return obj->total_out; +} + +#ifdef CAN_PROTOTYPE +long bzfile_clear_totals( bzFile *obj ) { +#else +long bzfile_clear_totals( obj ) bzFile *obj; { +#endif + obj->total_in = 0; + obj->total_out = 0; + return 0; +} + +#ifdef CAN_PROTOTYPE +int bzfile_clearerr( bzFile *obj ) { +#else +int bzfile_clearerr( obj ) bzFile *obj; { +#endif + int error_num = obj == NULL ? global_bzip_errno : obj->bzip_errno; + int clear_flag = 1; + + if ( error_num == BZ_IO_ERROR ) { + PerlIO_clearerr( obj->handle ); + } + else if ( error_num == BZ_SEQUENCE_ERROR ) { + /* program error */ + } + else if ( error_num == BZ_PARAM_ERROR ) { + /* program error */ + } + else if ( error_num == BZ_MEM_ERROR ) { + clear_flag = 0; /* must close */ + } + else if ( error_num == BZ_DATA_ERROR ) { + clear_flag = 0; /* must close or flush */ + } + else if ( error_num == BZ_DATA_ERROR_MAGIC ) { + clear_flag = 0; /* must close or flush */ + } + else if ( error_num == BZ_UNEXPECTED_EOF ) { + clear_flag = 0; /* must close */ + } + else if ( error_num == BZ_OUTBUFF_FULL ) { + } + else if ( error_num == BZ_CONFIG_ERROR ) { + clear_flag = 0; /* we don't like the version of bzlib */ + } + else if ( error_num == BZ_OK ) { + if ( obj->pending_io_error ) { + if ( obj->io_error == BZ_IO_EOF ) { + PerlIO_clearerr( obj->handle ); + clear_flag = 0; + } + } + else { + clear_flag = 0; /* this is a state, not an error */ + return 1; /* but return success anyways */ + } + } + else if ( error_num == BZ_RUN_OK ) { + clear_flag = 0; /* this is a state, not an error */ + } + else if ( error_num == BZ_FLUSH_OK ) { + clear_flag = 0; /* this is a state, not an error */ + } + else if ( error_num == BZ_FINISH_OK ) { + clear_flag = 0; /* this is a state, not an error */ + } + else if ( error_num == BZ_STREAM_END ) { + clear_flag = 0; /* this is a state, not an error */ + } + + if ( clear_flag ) { + if ( obj != NULL ) { + obj->bzip_errno = 0; + obj->io_error = 0; + obj->pending_io_error = False; + } + + global_bzip_errno = 0; + } + + return clear_flag; +} + +#ifdef CAN_PROTOTYPE +bzFile* bzfile_new( int verbosity, int small, int blockSize100k, int workFactor ) { +#else +bzFile* bzfile_new( verbosity, small, blockSize100k, workFactor ) + int verbosity; int small; int blockSize100k; int workFactor; { +#endif + bzFile* obj = NULL; + + /* creates a new bzFile object */ + /* sets parameters */ + + if (small != 0 && small != 1) { + BZ_SETERR(NULL, BZ_PARAM_ERROR, "bzfile_new small out of range"); + return NULL; + } + if (verbosity < 0 || verbosity > 4) { + BZ_SETERR(NULL, BZ_PARAM_ERROR, "bzfile_new verbosity out of range"); + return NULL; + } + + Newz(idthing, obj, 1, bzFile); + + BZ_SETERR(obj, BZ_OK, NULL); + + obj->open_status = OPEN_STATUS_ISCLOSED; + obj->run_progress = 0; + obj->io_error = 0; + obj->pending_io_error = False; + obj->handle = NULL; + obj->nCompressed = 0; + obj->compressedOffset_addmore = 0; + obj->compressedOffset_takeout = 0; + obj->verbosity = verbosity; + obj->small = small; + obj->blockSize100k = blockSize100k; + obj->workFactor = workFactor; + + obj->bufferOffset = 0; + obj->nBufferBytes = 0; + + obj->bzip_errno = 0; + + obj->total_in = 0; + obj->total_out = 0; + + obj->strm.bzalloc = bzmemalloc; + obj->strm.bzfree = bzmemfree; + obj->strm.opaque = NULL; + + obj->allowUncompressedRead = False; + + bzfile_streambuf_set( obj, NULL, 0 ); + + if (obj->verbosity >= 4) + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_new(%d,%d,%d,%d) called %p\n", verbosity, small, blockSize100k, workFactor, obj); + + return obj; +} + +#ifdef CAN_PROTOTYPE +void bzfile_free( bzFile* obj ) { +#else +void bzfile_free( obj ) bzFile* obj; { +#endif + if ( obj!=NULL ) Safefree((void*) obj); +} + +/* query and/or set param setting of bzFile */ +/* param may be verbosity, small, blockSize100k or workFactor */ +/* if setting is -1, the param is not changed, but the current value is returned */ +/* returns -1 on error */ +#ifdef CAN_PROTOTYPE +int bzfile_setparams( bzFile* obj, char* param, int setting ) { +#else +int bzfile_setparams( obj, param, setting ) bzFile* obj; char* param; int setting; { +#endif + int savsetting = -1; + + if ( param[0] == '-' ) param++; + if ( param[0] == '-' ) param++; + + if ( strEQ( param, "verbosity" ) ) { + savsetting = obj->verbosity; + + if ( setting >= 0 && setting <= 4 ) + obj->verbosity = setting; + else if ( setting != -1 ) { + BZ_SETERR(obj, BZ_PARAM_ERROR, NULL); + savsetting = -1; + } + } + else if ( strEQ( param, "buffer" ) ) { + savsetting = BZ_MAX_UNUSED; + } + else if ( strEQ( param, "small" ) ) { + savsetting = obj->small; + + if ( setting == 0 || setting == 1 ) + obj->small = setting; + else if ( setting != -1 ) { + BZ_SETERR(obj, BZ_PARAM_ERROR, NULL); + savsetting = -1; + } + } + else if ( strEQ( param, "blockSize100k" ) || strEQ( param, "level" ) ) { + savsetting = obj->blockSize100k; + + if ( setting >= 1 && setting <= 9 ) + obj->blockSize100k = setting; + else if ( setting != -1 ) { + BZ_SETERR(obj, BZ_PARAM_ERROR, NULL); + savsetting = -1; + } + } + else if ( strEQ( param, "workFactor" ) ) { + savsetting = obj->workFactor; + + if ( setting >= 0 && setting <= 250 ) + obj->workFactor = setting; + else if ( setting != -1 ) { + BZ_SETERR(obj, BZ_PARAM_ERROR, NULL); + savsetting = -1; + } + } + else if ( strEQ( param, "readUncompressed" ) ) { + savsetting = obj->allowUncompressedRead ? 1 : 0; + + if ( setting >= 0 && setting <= 1 ) + obj->allowUncompressedRead = setting ? True : False; + else if ( setting != -1 ) { + BZ_SETERR(obj, BZ_PARAM_ERROR, NULL); + savsetting = -1; + } + } + else { + BZ_SETERR(obj, BZ_PARAM_ERROR, NULL); + savsetting = -1; + } + + if (obj->verbosity>1) { + if ( savsetting == -1 ) + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_setparams invalid param %s => %d\n", param, setting); + else + if ( setting == -1 ) + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_setparams query %s is %d\n", param, savsetting); + else + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_setparams set %s (is %d) => %d\n", param, savsetting, setting); + } + return savsetting; +} + +#ifdef CAN_PROTOTYPE +bzFile* bzfile_open( char *filename, char *mode, bzFile *obj ) { +#else +bzFile* bzfile_open( filename, mode, obj ) char *filename; char *mode; bzFile *obj; { +#endif + PerlIO *io; + + io = PerlIO_open( filename, mode ); + if ( io == NULL ) { + BZ_SETERR(obj, BZ_IO_ERROR, NULL); + + if (obj && obj->verbosity > 0) warn( "Error: PerlIO_open( %s, %s ) failed: %s\n", filename, mode, Strerror(errno) ); + + return NULL; + } + +#if defined(_WIN32) || defined(OS2) || defined(MSDOS) || defined(__CYGWIN__) || defined(WIN32) + PerlIO_binmode(aTHX_ io, mode[0]=='w' ? '>' : '<', O_BINARY, Nullch); +#endif + + if ( obj == NULL ) obj = bzfile_new( 0, 0, 9, 0 ); + + obj->handle = io; + obj->open_status = mode && mode[0] == 'w' ? OPEN_STATUS_WRITE : OPEN_STATUS_READ; + + if (obj->verbosity>=2) + PerlIO_printf(PerlIO_stderr(), "Info: PerlIO_open( %s, %s ) succeeded, obj=%p\n", filename, mode, obj ); + + return obj; +} + +#ifdef CAN_PROTOTYPE +bzFile* bzfile_fdopen( PerlIO *io, char *mode, bzFile *obj ) { +#else +bzFile* bzfile_fdopen( io, mode, obj ) PerlIO *io; char *mode; bzFile *obj; { +#endif + + if ( io == NULL ) { + BZ_SETERR(obj, BZ_PARAM_ERROR, NULL); + return NULL; + } + +#if defined(_WIN32) || defined(OS2) || defined(MSDOS) || defined(__CYGWIN__) || defined(WIN32) + PerlIO_binmode(aTHX_ io, mode[0]=='w' ? '>' : '<', O_BINARY, Nullch); +#endif + + if ( obj == NULL ) obj = bzfile_new( 0, 0, 9, 0 ); + + obj->handle = io; + obj->open_status = mode && mode[0] == 'w' ? OPEN_STATUS_WRITE : OPEN_STATUS_READ; + + return obj; +} + +#ifdef CAN_PROTOTYPE +bzFile* bzfile_openstream( char *mode, bzFile *obj ) { +#else +bzFile* bzfile_openstream( mode, obj ) char *mode; bzFile *obj; { +#endif + if ( obj == NULL ) obj = bzfile_new( 0, 0, 1, 0 ); + if ( obj == NULL ) return NULL; + + obj->open_status = mode && mode[0] == 'w' ? OPEN_STATUS_WRITESTREAM : OPEN_STATUS_READSTREAM; + + return obj; +} + +#ifdef CAN_PROTOTYPE +void bzfile_streambuf_deposit( bzFile* obj, char* buffer, int buflen ) { +#else +void bzfile_streambuf_deposit( obj, buffer, buflen ) bzFile* obj; char* buffer; int buflen; { +#endif + /* inflate */ + /* insert compressed data into reading stream */ + obj->streamBuf = buffer; + obj->streamBufSize = buflen; + obj->streamBufLen = buflen; + obj->streamBufOffset = 0; +} + +#ifdef CAN_PROTOTYPE +int bzfile_streambuf_read( bzFile* obj, char* out, int outlen ) { +#else +int bzfile_streambuf_read( obj, out, outlen ) bzFile* obj; char* out; int outlen; { +#endif + /* inflate */ + /* read compressed data from buffer */ + char *in; + int i; + int n = obj->streamBufLen - obj->streamBufOffset; + + if (obj->verbosity>=4) + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_streambuf_read( %p, %d ), buffer %p, sz=%d, len=%d, offset=%d\n", + out, outlen, obj->streamBuf, obj->streamBufSize, obj->streamBufLen, obj->streamBufOffset ); + + if ( n <= 0 ) { + /* EAGAIN */ + errno = EAGAIN; + return -1; + } + + in = obj->streamBuf + obj->streamBufOffset; + + for ( i=0; i<outlen && i<n; i++) + *out++ = *in++; + + obj->streamBufOffset += i; + + return i; +} + +#ifdef CAN_PROTOTYPE +void bzfile_streambuf_set( bzFile* obj, char* buffer, int bufsize ) { +#else +void bzfile_streambuf_set( obj, buffer, bufsize ) bzFile* obj; char* buffer; int bufsize; { +#endif + /* deflate */ + obj->streamBuf = buffer; + obj->streamBufSize = bufsize; + obj->streamBufLen = 0; + obj->streamBufOffset = 0; +} + +#ifdef CAN_PROTOTYPE +int bzfile_streambuf_write( bzFile* obj, char* in, int inlen ) { +#else +int bzfile_streambuf_write( obj, in, inlen ) bzFile* obj; char* in; int inlen; { +#endif + /* deflate */ + /* write compressed data to buffer */ + + char *out; + int i; + int available_space = obj->streamBufSize - obj->streamBufLen; + + if (obj->verbosity>=4) + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_streambuf_write( %p, %d ), buffer %p, sz=%d, len=%d, offset=%d\n", + in, inlen, obj->streamBuf, obj->streamBufSize, obj->streamBufLen, obj->streamBufOffset ); + + if ( available_space <= 0 ) { + errno = EAGAIN; + return -1; /* EAGAIN */ + } + + out = obj->streamBuf + obj->streamBufOffset; + + for ( i=0; i<inlen && i<available_space; i++) + *out++ = *in++; + + obj->streamBufLen += i; + + return i; +} + +#ifdef CAN_PROTOTYPE +int bzfile_streambuf_collect( bzFile* obj, char* out, int outlen ) { +#else +int bzfile_streambuf_collect( obj, out, outlen ) bzFile* obj; char* out; int outlen; { +#endif + /* deflate */ + /* pull collected compressed data from buffer */ + int ret; + + ret = bzfile_streambuf_read( obj, out, outlen ); + + if ( ret == -1 ) { + /* got all the data out, reset the counters */ + obj->streamBufLen = 0; + obj->streamBufOffset = 0; + } + + return ret; +} + +/* success: 0 returned */ +/* failure: -1 returned, global error set */ +/* other error: -2 returned, global error already set */ +#ifdef CAN_PROTOTYPE +int bzfile_flush( bzFile* obj ) { +#else +int bzfile_flush( obj ) bzFile* obj; { +#endif + int error_num = bzfile_geterrno( obj ); + int tracker; + int compressed_bytes_count; + + if ( obj == NULL ) return 0; + if ( obj->run_progress == 0 || obj->run_progress == 10 ) return 0; + + if (obj->verbosity>=4) + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_flush called, error_num=%d, open_status %d\n", + error_num, obj->open_status); + + if ( error_num == BZ_OK ) { + } + else if ( error_num == BZ_IO_ERROR ) { + if ( obj->io_error == EAGAIN || obj->io_error == EINTR ) { + obj->io_error = 0; + BZ_SETERR(obj, BZ_OK, NULL); + } + else if ( obj->io_error == BZ_IO_EOF ) { + PerlIO_clearerr( obj->handle ); + } + else { + return -2; + } + } + else if ( error_num == BZ_DATA_ERROR ) { + /* a read error */ + } + else if ( error_num == BZ_UNEXPECTED_EOF ) { + /* a read error */ + } + else if ( error_num == BZ_OUTBUFF_FULL ) { + /* only when compressing or decompressing a buffer */ + return -2; + } + else { + return -2; + } + + if (obj->open_status == OPEN_STATUS_WRITE || obj->open_status == OPEN_STATUS_WRITESTREAM) { + int ret = BZ_OK; + + while (True) { + obj->strm.next_out = obj->bufferOfCompressed + obj->compressedOffset_addmore; + obj->strm.avail_out = sizeof(obj->bufferOfCompressed) - obj->compressedOffset_addmore; + + if (obj->verbosity>=4) + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_flush: call to BZ2_bzCompress with avail_in %d, next_in %p, avail_out %d, next_out %p, run_progress %d\n", + obj->strm.avail_in, obj->strm.next_in, obj->strm.avail_out, obj->strm.next_out, obj->run_progress); + + compressed_bytes_count = obj->strm.avail_out; + tracker = obj->strm.avail_in; + + if ( obj->strm.avail_out <= 0 || obj->run_progress > 2 ) + ret = obj->run_progress <= 2 ? BZ_FLUSH_OK : BZ_RUN_OK; + else { + ret = BZ2_bzCompress( &(obj->strm), BZ_FLUSH ); + if ( ret == BZ_RUN_OK ) obj->run_progress = 3; + } + + if (ret != BZ_RUN_OK && ret != BZ_FLUSH_OK) { + BZ_SETERR(obj, ret, NULL); + + if (obj->verbosity>1) + warn("Error: bzfile_flush, BZ2_bzCompress error %d, strm is %p, strm.state is %p, in state %d\n", + ret, &(obj->strm), obj->strm.state, *((int*)obj->strm.state)); + + return -1; + } + + obj->total_in += tracker - obj->strm.avail_in; + compressed_bytes_count -= obj->strm.avail_out; + obj->compressedOffset_addmore += compressed_bytes_count; + obj->nCompressed += compressed_bytes_count; + + if (obj->verbosity>=4) + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_flush BZ2_bzCompress, took in %d, put out %d bytes, ret %d\n", + tracker-obj->strm.avail_in, compressed_bytes_count, ret); + + if ( obj->nCompressed ) { + int n, n2; + + n = obj->nCompressed; + + while ( n > 0 ) { + if ( obj->open_status == OPEN_STATUS_WRITESTREAM ) + n2 = bzfile_streambuf_write( obj, obj->bufferOfCompressed + obj->compressedOffset_takeout, n ); + else + if ( obj->handle ) + n2 = PerlIO_write( obj->handle, obj->bufferOfCompressed + obj->compressedOffset_takeout, n ); + else + n2 = n; + + if ( n2==-1 ) { + BZ_SETERR(obj, BZ_IO_ERROR, NULL); + + if ( errno != EINTR && errno != EAGAIN ) { + if (obj->verbosity>0) + warn("Error: bzfile_flush io error %d '%s'\n", errno, Strerror(errno)); + } + else { + if (obj->verbosity>=4) + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_flush: file write error %s\n", Strerror(errno)); + } + + return -1; + } + else { + if (obj->verbosity>=4) + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_flush: file write took in %d, put out %d\n", n, n2); + + obj->compressedOffset_takeout += n2; + obj->nCompressed -= n2; + n -= n2; + + obj->total_out += n2; + } + } + + obj->nCompressed = 0; + obj->compressedOffset_addmore = 0; + obj->compressedOffset_takeout = 0; + } + + if (obj->verbosity>1) + PerlIO_printf(PerlIO_stderr(), "Info: bzfile_flush ret %d, total written %ld\n", ret, obj->total_out ); + + if (ret == BZ_RUN_OK) { + obj->run_progress = 1; + break; + } + } + + if ( obj->handle && !PerlIO_error( obj->handle ) ) { + /* ok, we got bzip flushed out, now flush out the IO buffers themselves */ + if ( -1 == PerlIO_flush( obj->handle ) ) { + BZ_SETERR(obj, BZ_IO_ERROR, NULL); + return -1; + } + } + } + else { + /* decompressing from a read IO handle */ + + obj->nBufferBytes = 0; /* toss getreadline data */ + /* can't flush the file handle, that will cause the compression stream to break up */ + /* the program will be unable to uncompress subsequent data, up to the next checkpoint */ + + if ( error_num == BZ_DATA_ERROR ) { + /* a read error */ + /* could look ahead for that 49 bit pattern ... */ + return -2; /* for now */ + } + else if ( error_num == BZ_UNEXPECTED_EOF ) { + return -2; + } + } + + return 0; +} + +#ifdef CAN_PROTOTYPE +int bzfile_close( bzFile* obj, int abandon ) { +#else +int bzfile_close( obj, abandon ) bzFile* obj; int abandon; { +#endif + /* returns zero on success, -1 on error */ + int ret; + + if ( obj->open_status == OPEN_STATUS_ISCLOSED ) { + BZ_SETERR(obj, BZ_SEQUENCE_ERROR, NULL); + return -1; + } + + if (obj->open_status == OPEN_STATUS_WRITE || obj->open_status == OPEN_STATUS_WRITESTREAM) + ret = bzfile_closewrite( obj, abandon ); + else + ret = bzfile_closeread( obj, abandon ); + + if ( ret == BZ_OK ) obj->open_status = OPEN_STATUS_ISCLOSED; + + return ret != BZ_OK ? -1 : 0; +} + +#ifdef CAN_PROTOTYPE +int bzfile_closeread( bzFile* obj, int abandon ) { +#else +int bzfile_closeread( obj, abandon ) bzFile* obj; int abandon; { +#endif + int ret = BZ_OK; + + if (obj->open_status == OPEN_STATUS_WRITE || obj->open_status == OPEN_STATUS_WRITESTREAM) + return BZ_SETERR(obj, BZ_SEQUENCE_ERROR, NULL); + + if ( obj->run_progress!=0 && obj->run_progress!=10 ) + ret = BZ2_bzDecompressEnd( &(obj->strm) ); + + obj->run_progress = 0; + obj->nBufferBytes = 0; /* toss getreadline data */ + obj->pending_io_error = False; + + if ( obj->handle ) + if ( 0 != PerlIO_close( obj->handle ) ) + ret = BZ_SETERR(obj, BZ_IO_ERROR, NULL); + + return BZ_SETERR(obj, ret, NULL); +} + +#ifdef CAN_PROTOTYPE +int bzfile_closewrite( bzFile* obj, int abandon ) { +#else +int bzfile_closewrite( obj, abandon ) bzFile* obj; int abandon; { +#endif + int error_num = bzfile_geterrno( obj ); + int ret = BZ_OK; + int tracker; + int compressed_bytes_count; + + if (obj->verbosity>=2) + PerlIO_printf(PerlIO_stderr(), "Info: bzfile_closewrite called, abandon=%d, error_num=%d, open_status %d\n", + abandon, error_num, obj->open_status); + + if ( obj == NULL ) return BZ_SETERR(NULL, BZ_OK, NULL); + if (obj->open_status != OPEN_STATUS_WRITE && obj->open_status != OPEN_STATUS_WRITESTREAM) + return BZ_SETERR(obj, BZ_SEQUENCE_ERROR, NULL); + + if ( error_num == BZ_OK ) { + } + else if ( error_num == BZ_IO_ERROR ) { + if ( obj->io_error == EAGAIN || obj->io_error == EINTR ) { + obj->io_error = 0; + BZ_SETERR(obj, BZ_OK, NULL); + } + else if ( !abandon ) + return error_num; + } + else if ( error_num == BZ_DATA_ERROR ) { + /* a read error */ + if ( !abandon ) return error_num; + } + else if ( error_num == BZ_UNEXPECTED_EOF ) { + /* a read error */ + if ( !abandon ) return error_num; + } + else if ( error_num == BZ_OUTBUFF_FULL ) { + /* only when compressing or decompressing a buffer */ + if ( !abandon ) return error_num; + } + else { + if ( !abandon ) return error_num; + } + + if ( obj->run_progress!=0 ) { + if ( !abandon ) { + while (True) { + obj->strm.next_out = obj->bufferOfCompressed + obj->compressedOffset_addmore; + obj->strm.avail_out = sizeof(obj->bufferOfCompressed) - obj->compressedOffset_addmore; + + if (obj->verbosity>=4) + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_closewrite: call to BZ2_bzCompress with avail_in %d, next_in %p, avail_out %d, next_out %p, run_progress %d\n", + obj->strm.avail_in, obj->strm.next_in, obj->strm.avail_out, obj->strm.next_out, obj->run_progress); + + compressed_bytes_count = obj->strm.avail_out; + tracker = obj->strm.avail_in; + + if ( obj->strm.avail_out <= 0 || obj->run_progress > 2 ) + ret = obj->run_progress <= 2 ? BZ_FINISH_OK : BZ_STREAM_END; + else { + ret = BZ2_bzCompress( &(obj->strm), BZ_FINISH ); + if ( ret == BZ_STREAM_END ) obj->run_progress = 9; + } + + if (ret != BZ_FINISH_OK && ret != BZ_STREAM_END) { + BZ_SETERR(obj, ret, NULL); + if (obj->verbosity>=1) + PerlIO_printf(PerlIO_stderr(), "Warning: bzfile_closewrite BZ2_bzCompress error %d\n", ret); + return ret; + } + + obj->total_in += tracker - obj->strm.avail_in; + compressed_bytes_count -= obj->strm.avail_out; + obj->compressedOffset_addmore += compressed_bytes_count; + obj->nCompressed += compressed_bytes_count; + + if (obj->verbosity>=4) + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_closewrite BZ2_bzCompress, took in %d, put out %d bytes, ret %d\n", + tracker-obj->strm.avail_in, compressed_bytes_count, ret); + + if ( obj->nCompressed ) { + int n, n2; + + n = obj->nCompressed; + + while ( n > 0 ) { + if ( obj->open_status == OPEN_STATUS_WRITESTREAM ) + n2 = bzfile_streambuf_write( obj, obj->bufferOfCompressed + obj->compressedOffset_takeout, n ); + else + if ( obj->handle ) + n2 = PerlIO_write( obj->handle, obj->bufferOfCompressed + obj->compressedOffset_takeout, n ); + else + n2 = n; + + if ( n2==-1 ) { + BZ_SETERR(obj, BZ_IO_ERROR, NULL); + + if ( errno != EINTR && errno != EAGAIN ) { + if (obj->verbosity>0) + warn("Error: bzfile_closewrite io error %d '%s'\n", errno, Strerror(errno)); + } + else { + if (obj->verbosity>=4) + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_closewrite: file write error %s\n", Strerror(errno)); + } + + return BZ_IO_ERROR; + } + else { + if (obj->verbosity>=4) + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_closewrite: file write took in %d, put out %d\n", n, n2); + + obj->compressedOffset_takeout += n2; + obj->nCompressed -= n2; + n -= n2; + + obj->total_out += n2; + } + } + + obj->nCompressed = 0; + obj->compressedOffset_addmore = 0; + obj->compressedOffset_takeout = 0; + } + + if (obj->verbosity>1) + PerlIO_printf(PerlIO_stderr(), "Info: bzfile_closewrite ret %d, total written %ld\n", ret, obj->total_out ); + + if (ret == BZ_STREAM_END) break; + } + } + + ret = BZ2_bzCompressEnd ( &(obj->strm) ); + obj->run_progress = 0; + } + + obj->pending_io_error = False; + + if ( obj->handle ) + if ( 0 != PerlIO_close( obj->handle ) ) + ret = BZ_SETERR(obj, BZ_IO_ERROR, NULL); + + return BZ_SETERR(obj, ret, NULL); +} + +#ifdef CAN_PROTOTYPE +int bzfile_readline( bzFile* obj, char *lineOfUncompress, int maxLineLength ) { +#else +int bzfile_readline( obj, lineOfUncompress, maxLineLength ) bzFile* obj; char *lineOfUncompress; int maxLineLength; { +#endif + int n = 0; + char *p = NULL; + int bytes_read = 0; + char lastch = 0; + int error_num = 0; + int done_flag = 0; + + if ( maxLineLength>0 ) lineOfUncompress[0]=0; + + while ( !done_flag && bytes_read < maxLineLength && lastch != '\n' ) { + if ( obj->nBufferBytes - obj->bufferOffset > 0 ) { + n = obj->nBufferBytes - obj->bufferOffset; + p = obj->bufferOfLines + obj->bufferOffset; + } + else { + n = bzfile_read( obj, obj->bufferOfLines, sizeof(obj->bufferOfLines) ); + if ( n < 0 ) { + error_num = bzfile_geterrno( obj ); + + if ( error_num == BZ_IO_ERROR ) { + if ( obj->io_error == EINTR || obj->io_error == EAGAIN ) + continue; + done_flag = 1; + } + else if ( error_num == BZ_UNEXPECTED_EOF ) { + done_flag = 1; + } + else { + done_flag = 1; + } + } + else if ( n == 0 ) { + done_flag = 1; + } + + p = obj->bufferOfLines; + obj->bufferOffset = 0; + obj->nBufferBytes = n; + n = 0; + } + + if ( obj->nBufferBytes - obj->bufferOffset > 0 ) { + lastch = *p; + *lineOfUncompress++ = lastch; + bytes_read++; + obj->bufferOffset++; + } + } + + if ( done_flag && bytes_read <= 0 && error_num ) return -1; + + if ( maxLineLength>bytes_read ) lineOfUncompress[bytes_read]=0; + + return bytes_read; +} + +#ifdef CAN_PROTOTYPE +int bzfile_read_notCompressed( bz_stream* strm, int *scan_BZh9 ) { +#else +int bzfile_read_notCompressed( strm, scan_BZh9 ) bz_stream* strm; int *scan_BZh9; { +#endif + char ch; + + while ( strm->avail_in>0 && strm->avail_out>0 ) { + ch = *(strm->next_out++) = *(strm->next_in++); + strm->avail_in--; + strm->avail_out--; + switch (*scan_BZh9) { + case 0: if ( ch=='B' ) *scan_BZh9=1; break; + case 1: *scan_BZh9 = ch=='Z' ? 2 : 0; break; + case 2: *scan_BZh9 = ch=='h' ? 3 : 0; break; + case 3: *scan_BZh9 = ch-'0'>=1 && ch-'0'<=9 ? ch : 0; break; + } + } + + if ( *scan_BZh9 > 4 ) return BZ_DATA_ERROR_MAGIC; + + return BZ_OK; +} + +#ifdef CAN_PROTOTYPE +int bzfile_read( bzFile* obj, char *bufferOfUncompress, int nUncompress ) { +#else +int bzfile_read( obj, bufferOfUncompress, nUncompress ) bzFile* obj; char *bufferOfUncompress; int nUncompress; { +#endif + int ret; + int tracker, rewind_mark; + int bytes_uncompressed_count; + int error_num = bzfile_geterrno( obj ); + + if (obj == NULL || bufferOfUncompress == NULL || nUncompress < 0) { + BZ_SETERR(obj, BZ_PARAM_ERROR, NULL); + + if ( obj != NULL && obj->verbosity>1 ) { + if ( bufferOfUncompress == NULL ) warn("Error: bzfile_read buf is NULL\n"); + if ( nUncompress < 0 ) warn("Error: bzfile_read n is negative %d\n", nUncompress); + } + + return -1; + } + if (obj->verbosity>=4) + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_read(obj, %p, %d) obj->open_status=%d\n", + bufferOfUncompress, nUncompress, obj->open_status); + + if (obj->open_status == OPEN_STATUS_WRITE || obj->open_status == OPEN_STATUS_WRITESTREAM) { + BZ_SETERR(obj, BZ_SEQUENCE_ERROR, NULL); + + if ( obj->verbosity>1 ) warn("Error: bzfile_read attempted on a writing stream\n"); + + return -1; + } + + if ( error_num == BZ_OK ) { + if ( obj->pending_io_error ) { + if ( obj->io_error == BZ_UNEXPECTED_EOF ) { + obj->io_error = 0; + BZ_SETERR(obj, BZ_UNEXPECTED_EOF, NULL); + } + else if ( obj->io_error == BZ_IO_EOF ) { + return 0; + } + else { + errno = obj->io_error; + obj->io_error = 0; + BZ_SETERR(obj, BZ_IO_ERROR, NULL); + } + + obj->pending_io_error = False; + return -1; + } + } + else if ( error_num == BZ_IO_ERROR ) { + if ( obj->io_error == EINTR || obj->io_error == EAGAIN ) { + obj->io_error=0; + BZ_SETERR(obj, BZ_OK, NULL); + } + else + return -2; + } + else if ( error_num == BZ_DATA_ERROR ) { + /* a read error */ + return -2; + } + else if ( error_num == BZ_UNEXPECTED_EOF ) { + /* a read error */ + return -2; + } + else { + return -2; + } + + if (nUncompress == 0) return 0; + + BZ_SETERR(obj, BZ_OK, NULL); + + obj->nHolding = 0; + bytes_uncompressed_count = 0; + + /******************** + * note: obj->run_progress is used to detect proper end of file + * an end of file that doesn't have an end-of-stream marker is INVALID and a result of data corruption + * so, here's the run_progress settings: + * 0: stream has never been initialized, a call to BZ2_bzDecompressInit is necessary. + * 1: stream has just been initialized, for the first time. No data has yet been read. + * 2: stream has been initialized, data has been read from the file handle. + * in the middle of processing what is probably a LARGE file: + * 10: an end of stream marker has been seen, and BZ2_bzDecompressEnd has been called. + * before the stream can be used again, a call to BZ2_bzDecompressInit must be made. + * 11: ready to read more data - BZ2_bzDecompressInit has been called (but no data has been read yet) + * 12: stream has been initialized, data has been read. + * the sequence of changes is + * 0 => 1 => 2 => 10 => 11 => 12 + * A FILE EOF, or PerlIO EOF, is only valid when run_progress is 0 or 10, ie when no data has been read, + * or just after we've received a valid end-of-stream marker. + */ + + while (True) { + if ( obj->nBufferBytes - obj->bufferOffset > 0 ) { + char *p, *s; + int i,n; + + p = obj->bufferOfLines + obj->bufferOffset; /* point to next byte */ + n = obj->nBufferBytes - obj->bufferOffset; /* count of bytes ready to go */ + + /* move as much as we can to the Uncompress hopper */ + for (i=0; i<n && bytes_uncompressed_count+i < nUncompress; i++) bufferOfUncompress[bytes_uncompressed_count+i] = *p++; + bytes_uncompressed_count+=i; + + n -= i; /* update number of bytes still to go */ + for (s = obj->bufferOfLines; i < n; i++) *s++ = *p++; /* move remaining bytes to top of the buffer */ + obj->nBufferBytes = n; + obj->bufferOffset = 0; + + if (bytes_uncompressed_count >= nUncompress) { + BZ_SETERR(obj, BZ_OK, NULL); + return bytes_uncompressed_count; + } + } + + obj->strm.avail_out = nUncompress - bytes_uncompressed_count; + obj->strm.next_out = bufferOfUncompress + bytes_uncompressed_count; + + if (obj->strm.avail_in == 0) { + char *buf = obj->bufferOfCompressed; + int bufln = sizeof(obj->bufferOfCompressed); + + char *p; + int i,n; + + if ( obj->nHolding ) { + /* move held bytes into the hopper */ + p = obj->bufferOfHolding; + n = obj->nHolding; + + for (i=0; i<n; i++) buf[i] = *p++; + obj->nHolding = 0; + } + else if ( obj->BZh9_count ) { + /* move header bytes into the hopper */ + p = obj->BZh9; + n = obj->BZh9_count; + + for (i=0; i<n; i++) buf[i] = *p++; + obj->BZh9_count = 0; + } + else { + if ( obj->open_status == OPEN_STATUS_READSTREAM ) + n = bzfile_streambuf_read( obj, buf, bufln ); + else + n = PerlIO_read( obj->handle, buf, bufln ); + + if (obj->verbosity>=4) + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_read file read got %d bytes\n", n); + + if ( n == -1 ) { + if ( bytes_uncompressed_count ) { + obj->pending_io_error = True; + obj->io_error = errno; + n = 0; + } + else { + BZ_SETERR(obj, BZ_IO_ERROR, NULL); + return -1; + } + } + else if ( n == 0 ) { + /* end of file */ + } + } + + obj->total_in += n; + obj->strm.avail_in = obj->nCompressed = n; + obj->strm.next_in = obj->bufferOfCompressed; + } + + if ( obj->strm.avail_in == 0 ) { + /* still zero bytes in the input hopper? why? ... */ + if ( !obj->pending_io_error ) { + if ( obj->run_progress != 0 && obj->run_progress != 10 ) { + if ( !bytes_uncompressed_count ) { + BZ_SETERR(obj, BZ_UNEXPECTED_EOF, NULL); + + if (obj->verbosity>=2) { + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_read got an unexpected EOF, run_progress=%d, avail_in=%d, avail_out=%d\n", + obj->run_progress, + obj->strm.avail_in, + obj->strm.avail_out + ); + } + + return -1; + } + + /* hold off on the BZ_UNEXPECTED_EOF until the caller gets their data */ + obj->pending_io_error = True; + obj->io_error = BZ_UNEXPECTED_EOF; + + if (obj->verbosity>=2) { + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_read got an unexpected EOF, run_progress=%d, set pending with %d bytes to go, avail_in=%d, avail_out=%d\n", + obj->run_progress, + bytes_uncompressed_count, + obj->strm.avail_in, + obj->strm.avail_out + ); + } + } + else { + obj->pending_io_error = True; + obj->io_error = BZ_IO_EOF; + + if (obj->verbosity>=2) { + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_read got an EOF, run_progress=%d, set pending with %d bytes to go, avail_in=%d, avail_out=%d\n", + obj->run_progress, + bytes_uncompressed_count, + obj->strm.avail_in, + obj->strm.avail_out + ); + } + } + } + /* if no io_error is pending, this is a proper end of file */ + return bytes_uncompressed_count; + } + else { + if ( obj->run_progress == 1 || obj->run_progress == 11 ) { + /* indicate we have data to uncompress */ + obj->run_progress = obj->run_progress == 1 ? 2 : 12; + } else if ( obj->run_progress == 0 || obj->run_progress == 10 ) { + ret = BZ2_bzDecompressInit ( &(obj->strm), obj->verbosity, obj->small ); + + if (ret != BZ_OK) { + if (obj->verbosity>1) { + warn("Error: bzfile_read: BZ2_bzDecompressInit error %d on %d, %d\n", + ret, obj->verbosity, obj->small); + } + + BZ_SETERR(obj, ret, NULL); + return -1; + } + + obj->run_progress = obj->run_progress == 0 ? 1 : 11; + obj->notCompressed = False; + } + + rewind_mark = obj->strm.avail_in; + tracker = obj->strm.avail_out; + if ( obj->notCompressed ) + ret = bzfile_read_notCompressed( &(obj->strm), &(obj->scan_BZh9) ); + else + ret = BZ2_bzDecompress( &(obj->strm) ); + + if (obj->verbosity>=4) { + PerlIO_printf(PerlIO_stderr(), "\ndebug: bzfile_read BZ2_bzDecompress ret %d, run_progress=%d, avail_in=%d/%d, avail_out=%d/%d\n", + ret, + obj->run_progress, + rewind_mark, + obj->strm.avail_in, + tracker, + obj->strm.avail_out + ); + } + + if (ret != BZ_OK && ret != BZ_STREAM_END) { + if ( ret != BZ_DATA_ERROR_MAGIC || !obj->allowUncompressedRead ) { + BZ_SETERR(obj, ret, NULL); + + if (obj->verbosity>1) + warn("Error: bzfile_read, BZ2_bzDecompress error %d, strm is %p, strm.state is %p, in state %d\n", + ret, &(obj->strm), obj->strm.state, *((int*)obj->strm.state)); + + return -1; + } + else if ( !obj->notCompressed ) { + /* a compressed stream that turns out not to be compressed */ + obj->strm.avail_in = rewind_mark; + obj->notCompressed = True; + obj->scan_BZh9 = 0; + + ret = BZ2_bzDecompressEnd( &(obj->strm) ); + obj->run_progress = 0; + + ret = bzfile_read_notCompressed( &(obj->strm), &(obj->scan_BZh9) ); + } + else { + /* an uncompressed stream that turns out to be compressed actually */ + obj->BZh9[0] = 'B'; + obj->BZh9[1] = 'Z'; + obj->BZh9[2] = 'h'; + obj->BZh9[3] = obj->scan_BZh9; + obj->BZh9[4] = 0; + obj->BZh9_count = 4; + + continue; + } + } + + obj->total_out += tracker - obj->strm.avail_out; + bytes_uncompressed_count += tracker - obj->strm.avail_out; + + if (ret == BZ_STREAM_END) { + char *p; + int i,n; + + /* move unused bytes to another place */ + p = obj->strm.next_in; + n = obj->strm.avail_in; + for (i=0; i<n; i++) obj->bufferOfHolding[i] = *p++; + obj->nHolding = n; + + ret = BZ2_bzDecompressEnd( &(obj->strm) ); + + obj->run_progress = 10; + + obj->nCompressed = 0; + obj->strm.avail_in = 0; + obj->strm.next_in = obj->bufferOfCompressed; + } + } + + if (bytes_uncompressed_count >= nUncompress) { + BZ_SETERR(obj, BZ_OK, NULL); + return bytes_uncompressed_count; + } + + /* obj->strm.avail_out > 0 */ + /* need to read more to fill the output hopper, keep going */ + } +} + +#ifdef CAN_PROTOTYPE +int bzfile_write( bzFile* obj, char *bufferOfUncompressed, int nUncompressed ) { +#else +int bzfile_write( obj, bufferOfUncompressed, nUncompressed ) bzFile* obj; char *bufferOfUncompressed; int nUncompressed; { +#endif + int ret; + int tracker; + int bytes_compressed_count = 0; + int compressed_bytes_count = 0; + int error_num = bzfile_geterrno( obj ); + + if (obj == NULL || bufferOfUncompressed == NULL || nUncompressed < 0) { + BZ_SETERR(obj, BZ_PARAM_ERROR, NULL); + + if ( obj != NULL && obj->verbosity>1 ) { + if ( bufferOfUncompressed == NULL ) warn("Error: bzfile_write buf is NULL\n"); + if ( nUncompressed < 0 ) warn("Error: bzfile_write n is negative %d\n", nUncompressed); + } + + return -1; + } + + if (obj->open_status != OPEN_STATUS_WRITE && obj->open_status != OPEN_STATUS_WRITESTREAM) { + BZ_SETERR(obj, BZ_SEQUENCE_ERROR, NULL); + + if ( obj->verbosity>1 ) warn("Error: bzfile_write attempted on a reading stream\n"); + + return -1; + } + + if ( error_num == BZ_OK ) { + if ( obj->pending_io_error ) { + errno = obj->io_error; + obj->io_error = 0; + BZ_SETERR(obj, BZ_IO_ERROR, NULL); + + obj->pending_io_error = False; + return -1; + } + } + else if ( error_num == BZ_IO_ERROR ) { + if ( obj->io_error == EINTR || obj->io_error == EAGAIN ) { + obj->io_error=0; + BZ_SETERR(obj, BZ_OK, NULL); + } + else + return -2; + } + else { + return -2; + } + + while (True) { + if ( obj->run_progress == 0 ) { + ret = BZ2_bzCompressInit ( &(obj->strm), obj->blockSize100k, obj->verbosity, obj->workFactor ); + + if (ret != BZ_OK) { + BZ_SETERR(obj, ret, NULL); + + if (obj->verbosity>1) + warn("Error: bzfile_write: BZ2_bzCompressInit error %d on %d, %d, %d\n", + ret, obj->blockSize100k, obj->verbosity, obj->workFactor); + + return -1; + } + + obj->run_progress = 1; + } + + obj->strm.avail_in = nUncompressed - bytes_compressed_count; + obj->strm.next_in = bufferOfUncompressed + bytes_compressed_count; + + obj->strm.avail_out = sizeof(obj->bufferOfCompressed) - obj->compressedOffset_addmore; + obj->strm.next_out = obj->bufferOfCompressed + obj->compressedOffset_addmore; + + if (obj->verbosity>=4) + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_write: call to BZ2_bzCompress with avail_in %d, next_in %p, avail_out %d, next_out %p\n", + obj->strm.avail_in, obj->strm.next_in, obj->strm.avail_out, obj->strm.next_out); + + compressed_bytes_count = obj->strm.avail_out; + + tracker = obj->strm.avail_in; + if ( tracker == 0 ) + return nUncompressed; + + /* indicate we have data to compress */ + if ( obj->run_progress == 1 && tracker > 0 ) obj->run_progress = 2; + + if ( obj->strm.avail_out <= 0 ) + ret = BZ_RUN_OK; + else + ret = BZ2_bzCompress ( &(obj->strm), BZ_RUN ) ; + + obj->total_in += tracker - obj->strm.avail_in; + bytes_compressed_count += tracker - obj->strm.avail_in; + + compressed_bytes_count -= obj->strm.avail_out; + obj->compressedOffset_addmore += compressed_bytes_count; + obj->nCompressed += compressed_bytes_count; + + if (ret != BZ_RUN_OK) { + BZ_SETERR(obj, ret, NULL); + + if (obj->verbosity>1) + warn("Error: bzfile_write, BZ2_bzCompress error %d, strm is %p, strm.state is %p, in state %d\n", + ret, &(obj->strm), obj->strm.state, *((int*)obj->strm.state)); + + return -1; + } + + if (obj->verbosity>=4) + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_write: BZ2_bzCompress took in %d, put out %d \n", + tracker-obj->strm.avail_in, compressed_bytes_count); + + if ( obj->nCompressed ) { + int n, n2; + + n = obj->nCompressed; + + while ( n > 0 ) { + if ( obj->open_status == OPEN_STATUS_WRITESTREAM ) + n2 = bzfile_streambuf_write( obj, obj->bufferOfCompressed + obj->compressedOffset_takeout, n ); + else + if ( obj->handle ) + n2 = PerlIO_write( obj->handle, obj->bufferOfCompressed + obj->compressedOffset_takeout, n ); + else + n2 = n; + + if ( n2==-1 ) { + if ( bytes_compressed_count ) { + obj->pending_io_error = True; + obj->io_error = errno; + + if ( errno != EINTR && errno != EAGAIN ) { + if (obj->verbosity>0) + warn("Error: bzfile_write file write error %d '%s'\n", errno, Strerror(errno)); + } + else { + if (obj->verbosity>=4) + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_write file write error pending %d '%s'\n", errno, Strerror(errno)); + } + + return bytes_compressed_count; + } + else { + BZ_SETERR(obj, BZ_IO_ERROR, NULL); + + if ( errno != EINTR && errno != EAGAIN ) { + if (obj->verbosity>0) + warn("Error: bzfile_write io error %d '%s'\n", errno, Strerror(errno)); + } + else { + if (obj->verbosity>=4) + PerlIO_printf(PerlIO_stderr(), "debug: bzfile_write: file write error %d '%s'\n", errno, Strerror(errno)); + } + + return -1; + } + } + else { + if (obj->verbosity>=4) PerlIO_printf(PerlIO_stderr(), "debug: bzfile_write: file write took in %d, put out %d\n", n, n2); + + obj->compressedOffset_takeout += n2; + obj->nCompressed -= n2; + n -= n2; + + obj->total_out += n2; + } + } + + obj->nCompressed = 0; + obj->compressedOffset_takeout = 0; + obj->compressedOffset_addmore = 0; + } + + if (bytes_compressed_count == nUncompressed) { + BZ_SETERR(obj, BZ_OK, NULL); + return nUncompressed; + } + } +} + +/*********************************************************************** + * XSUB start + ***********************************************************************/ + +MODULE = Compress::Bzip2 PACKAGE = Compress::Bzip2 PREFIX = MY_ + +INCLUDE: const-xs.inc + +REQUIRE: 0.0 +PROTOTYPES: ENABLE + +BOOT: + if (BZ2_bzlibVersion()[0] != '1') + croak("Compress::Bzip2 needs bzlib version 1.x, not %s\n", BZ2_bzlibVersion()) ; + + { + /* Create the $bzerror scalar */ + SV * bzerror_sv = perl_get_sv(BZERRNO, GV_ADDMULTI) ; + sv_setiv(bzerror_sv, 0) ; + sv_setpv(bzerror_sv, "") ; + SvIOK_on(bzerror_sv) ; + } + +void +MY_new(...) + + PROTOTYPE: @ + + INIT: + bzFile* obj; + SV *perlobj; + char *class, *param; + STRLEN lnclass, lnparam; + int setting; + + PPCODE: + { + int i; + + perlobj=NULL; + obj=NULL; + if ( items == 0 ) { + class = "Compress::Bzip2"; + } + else if ( SvPOK( ST(0) ) ) { + /* this is the name of a class */ + class = (char *) SvPV( ST(0), lnclass ); + } + else if ( SvROK( ST(0) ) ) { + if (sv_derived_from(ST(0), "Compress::Bzip2")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + perlobj = ST(0); + obj = INT2PTR(bzFile*, tmp); + } + } + + if ( obj == NULL ) { + obj = bzfile_new( 0, 0, 9, 0 ); + + perlobj = newSV(0); + sv_setref_iv( perlobj, class, PTR2IV(obj) ); + sv_2mortal(perlobj); + } + + if ( obj == NULL ) + XSRETURN_UNDEF; + + for (i=1; i<items-1; i+=2) { + param = (char*) SvPV( ST(i), lnparam ); + setting = SvIV( ST(i+1) ); + bzfile_setparams( obj, param, setting ); + } + + PUSHs(perlobj); + } + +void +DESTROY(obj) + Compress::Bzip2 obj + + CODE: + { + if (obj->verbosity>=1) + PerlIO_printf(PerlIO_stderr(), "debug: DESTROY on %p\n", obj); + bzfile_close( obj, 0 ); + bzfile_free( obj ); + } + +char * +MY_bzlibversion() + + PROTOTYPE: + + CODE: + RETVAL = (char *) BZ2_bzlibVersion(); + + OUTPUT: + RETVAL + + +int +MY_bz_seterror(error_num, error_str) + int error_num; + char *error_str; + + PROTOTYPE: $$ + + CODE: + { + SV * bzerror_sv = perl_get_sv(BZERRNO, GV_ADDMULTI); + sv_setiv(bzerror_sv, error_num); + sv_setpv(bzerror_sv, error_str); + SvIOK_on(bzerror_sv); + + RETVAL = error_num; + } + + OUTPUT: + RETVAL + +SV * +memBzip(sv, level = 6) + SV* sv; + int level + + PROTOTYPE: $;$ + + ALIAS: + compress = 1 + + PREINIT: + STRLEN len; + unsigned char * in; + unsigned char * out; + unsigned int in_len; + unsigned int out_len; + unsigned int new_len; + int err; + + CODE: + { + if ( !SvOK(sv) ) + croak(ix==1 ? "compress: buffer is undef" : "memBzip: buffer is undef");; + + sv = deRef(sv, ix==1 ? "compress" : "memBzip"); + + in = (unsigned char*) SvPV(sv, len); + in_len = len; + + /* use an extra 1% + 600 bytes (see libbz2 documentation) */ + out_len = in_len + ( in_len + 99 ) / 100 + 600; + RETVAL = newSV(5+out_len); + SvPOK_only(RETVAL); + + out = (unsigned char*)SvPVX(RETVAL); + new_len = out_len; + + out[0] = 0xf0; + err = BZ2_bzBuffToBuffCompress((char*)out+5,&new_len,(char*)in,in_len,level,0,240); + + if (err != BZ_OK || new_len > out_len) { + SvREFCNT_dec(RETVAL); + BZ_SETERR(NULL, err, ix==1 ? "compress" : "memBzip"); + XSRETURN_UNDEF; + } + + SvCUR_set(RETVAL,5+new_len); + out[1] = (in_len >> 24) & 0xff; + out[2] = (in_len >> 16) & 0xff; + out[3] = (in_len >> 8) & 0xff; + out[4] = (in_len >> 0) & 0xff; + } + + OUTPUT: + RETVAL + +SV * +memBunzip(sv) + SV* sv + + PROTOTYPE: $ + + ALIAS: + decompress = 1 + + PREINIT: + STRLEN len; + unsigned char * in; + unsigned char * out; + unsigned int in_len; + unsigned int out_len; + unsigned int new_len; + int err; + int noprefix = 0; + + CODE: + { + if ( !SvOK(sv) ) + croak(ix==1 ? "decompress: buffer is undef" : "memBunzip: buffer is undef");; + + sv = deRef(sv, ix==1 ? "decompress" : "memBunzip"); + + in = (unsigned char*)SvPV(sv, len); + if (len < 5 + 3 || in[0] < 0xf0 || in[0] > 0xf1) { + if (len > 16 && in[0] == 'B' && in[1] == 'Z' && in[2] == 'h') { + in_len = len; + out_len = len * 5; /* guess uncompressed size */ + noprefix = 1; + RETVAL = newSV(len * 10); + } else { + warn("invalid buffer (too short %ld or bad marker %d)",len,in[0]); + XSRETURN_UNDEF; + } + } else { + in_len = len - 5; + out_len = (in[1] << 24) | (in[2] << 16) | (in[3] << 8) | in[4]; + RETVAL = newSV(out_len > 0 ? out_len : 1); + } + SvPOK_only(RETVAL); + out = (unsigned char*)SvPVX(RETVAL); + new_len = out_len; + err = BZ2_bzBuffToBuffDecompress((char*)out,&new_len, + noprefix ? (char*)in:(char *)in+5, in_len,0,0); + while (noprefix && (err == BZ_OUTBUFF_FULL)) { + new_len = SvLEN(RETVAL) * 2; + SvGROW(RETVAL, new_len); + err = BZ2_bzBuffToBuffDecompress((char*)out,&new_len, + (char *)in,in_len,0,0); + } + if (err != BZ_OK) { + SvREFCNT_dec(RETVAL); + BZ_SETERR(NULL, err, ix==1 ? "decompress" : "memBunzip"); + XSRETURN_UNDEF; + } + if (!noprefix && new_len != out_len) { + SvREFCNT_dec(RETVAL); + BZ_SETERR(NULL, err, ix==1 ? "decompress" : "memBunzip"); + XSRETURN_UNDEF; + } + SvCUR_set(RETVAL, new_len); + } + + OUTPUT: + RETVAL + +void +MY_bzopen(...) + +## xxx->bzopen( $filename or filehandle, $mode ) + + PROTOTYPE: $$;$ + + INIT: + PerlIO *io; + char *filename, *mode, *class; + STRLEN ln, lnfilename, lnclass; + + bzFile* obj; + SV *perlobj; + + PPCODE: + { + int i; + + perlobj=NULL; + obj=NULL; + if ( items == 2 ) { + class = "Compress::Bzip2"; + } + else if ( SvPOK( ST(0) ) ) { + /* this is the name of a class */ + class = (char *) SvPV( ST(0), lnclass ); + } + else if ( SvROK( ST(0) ) ) { + if (sv_derived_from(ST(0), "Compress::Bzip2")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + perlobj = ST(0); + obj = INT2PTR(bzFile*, tmp); + } + } + + i = items==3 ? 2 : 1; + mode = (char *) SvPV(ST(i), ln); + if (ln==0) { + BZ_SETERR(obj, BZ_PARAM_ERROR, NULL); + + if ( obj && obj->verbosity>1 ) warn( "Error: invalid file mode for bzopen %s", mode ); + + XSRETURN_UNDEF; + } + + i = items==3 ? 1 : 0; + if ( SvPOK( ST(i) ) ) { + /* is the first argument a filename string or a filehandle?? */ + filename = (char *) SvPV(ST(i), lnfilename); + if (lnfilename==0) + XSRETURN_UNDEF; + + filename[lnfilename]=0; + + obj = bzfile_open( filename, mode, obj ); + } + else if ( SvROK( ST(i) ) || SVt_PVIO == SvTYPE( ST(i) ) ) { + /* a reference or an IO handle */ + if ( mode && mode[0] == 'w' ) { + io = IoOFP(sv_2io( ST(i) )); + } + else { + io = IoIFP(sv_2io( ST(i) )); + } + + obj = bzfile_fdopen( io, mode, obj ); + } + else { + BZ_SETERR(obj, BZ_PARAM_ERROR, NULL); + + if ( obj && obj->verbosity>1 ) warn( "Error: invalid file or handle for bzopen" ); + + XSRETURN_UNDEF; + } + + if ( obj == NULL ) + XSRETURN_UNDEF; + + if ( perlobj == NULL ) { + perlobj = newSV(0); + sv_setref_iv( perlobj, class, PTR2IV(obj) ); + sv_2mortal(perlobj); + } + + PUSHs(perlobj); + } + +void +MY_bzclose(obj, abandon=0) + Compress::Bzip2 obj + int abandon + + PROTOTYPE: $;$ + + PPCODE: + { + int i, ret, amt_collected; + char *inp; + int error_flag = 0; + + if ( obj->open_status != OPEN_STATUS_READSTREAM && obj->open_status != OPEN_STATUS_WRITESTREAM ) { + ret = bzfile_close( obj, abandon ); + XPUSHs(sv_2mortal(newSViv(ret))); + } + else { + char *firstp, *outp; + SV *outbuf = NULL; + STRLEN outbufl = 0; + + char collect_buffer[10000]; + + while ( !error_flag ) { + ret = bzfile_close( obj, abandon ); + + if ( obj->open_status == OPEN_STATUS_READSTREAM ) break; + + if ( ret == -1 && errno != EAGAIN ) { + error_flag =1; + break; + } + + if ( obj->verbosity>=4 ) + PerlIO_printf(PerlIO_stderr(), "debug: bzstreamclose, bzfile_close returned %d, errno is %d %s\n", ret, errno, Strerror(errno)); + + while ( -1 != ( amt_collected = bzfile_streambuf_collect( obj, collect_buffer, sizeof(collect_buffer) ) ) ) { + if ( obj->verbosity>=4 ) + PerlIO_printf(PerlIO_stderr(), "debug: bzstreamclose, bzfile_streambuf_collect returned %d bytes\n", amt_collected); + + /* put the stuff into the SV output buffer */ + if ( outbuf == NULL ) { + outbuf = newSVpv( collect_buffer, amt_collected ); + outbufl = amt_collected; + firstp = SvPV_nolen( outbuf ); + outp = firstp; + } + else { + outbufl += amt_collected; + SvGROW( outbuf, outbufl ); + firstp = SvPV_nolen( outbuf ); + outp = SvEND( outbuf ); + } + + for ( inp=collect_buffer, i=0; i<amt_collected; i++ ) *outp++ = *inp++; + SvCUR_set( outbuf, outp-firstp) ; + } + if ( errno != EAGAIN ) + error_flag = 1; + + if ( ret == 0 ) break; + } + + if (outbuf==NULL) { + if ( error_flag ) + XPUSHs(sv_newmortal()); + else + XPUSHs(sv_2mortal(newSVpv("",0))); + } + else + XPUSHs(sv_2mortal(outbuf)); + + if (GIMME == G_ARRAY) + XPUSHs(sv_2mortal(newSViv(global_bzip_errno))); + } + } + +void +MY_bzflush(obj, flag=0) + Compress::Bzip2 obj + int flag + + PROTOTYPE: $;$ + + PPCODE: + { + int i, ret, amt_collected; + char *inp; + + if ( obj->open_status != OPEN_STATUS_READSTREAM && obj->open_status != OPEN_STATUS_WRITESTREAM ) { + ret = !flag || flag!=BZ_FINISH ? bzfile_flush( obj ) : bzfile_close( obj, 0 ); + XPUSHs(sv_2mortal(newSViv(ret))); + } + else { + char *firstp, *outp; + SV *outbuf = NULL; + STRLEN outbufl = 0; + + char collect_buffer[10000]; + + while ( True ) { + ret = !flag || flag!=BZ_FLUSH ? bzfile_flush( obj ) : bzfile_close( obj, 0 ); + + if ( obj->open_status == OPEN_STATUS_READSTREAM ) break; + + while ( -1 != ( amt_collected = bzfile_streambuf_collect( obj, collect_buffer, sizeof(collect_buffer) ) ) ) { + if ( obj->verbosity>=4 ) + PerlIO_printf(PerlIO_stderr(), "debug: bzstreamflush, bzfile_streambuf_collect returned %d bytes\n", amt_collected); + + /* put the stuff into the SV output buffer */ + if ( outbuf == NULL ) { + outbuf = newSVpv( collect_buffer, amt_collected ); + outbufl = amt_collected; + firstp = SvPV_nolen( outbuf ); + outp = firstp; + } + else { + outbufl += amt_collected; + SvGROW( outbuf, outbufl ); + firstp = SvPV_nolen( outbuf ); + outp = SvEND( outbuf ); + } + + for ( inp=collect_buffer, i=0; i<amt_collected; i++ ) *outp++ = *inp++; + SvCUR_set( outbuf, outp-firstp) ; + } + + if ( ret != -1 ) break; + } + + if (outbuf==NULL) + XPUSHs(sv_newmortal()); + else + XPUSHs(sv_2mortal(outbuf)); + + if (GIMME == G_ARRAY) + XPUSHs(sv_2mortal(newSViv(global_bzip_errno))); + } + } + +SV* +MY_bzerror(obj) + Compress::Bzip2 obj + + PROTOTYPE: $ + + CODE: + { + int err_num; + err_num = bzfile_geterrno( obj ); + + if ( err_num == 0 ) + XSRETURN_NO; + + RETVAL = newSViv( err_num ); + + sv_setiv(RETVAL, err_num) ; + sv_setpv(RETVAL, (char*) bzfile_geterrstr( obj )); + SvIOK_on(RETVAL); /* say "IAM integer (too)" */ + } + + OUTPUT: + RETVAL + +int +MY_bzclearerr(obj) + Compress::Bzip2 obj + + PROTOTYPE: $ + + CODE: + { + if ( bzfile_clearerr( obj ) ) + RETVAL = 1; + else + RETVAL = 0; + } + + OUTPUT: + RETVAL + +SV* +MY_bzeof(obj) + Compress::Bzip2 obj + + PROTOTYPE: $ + + CODE: + { + if ( bzfile_eof( obj ) ) + XSRETURN_YES; + else + XSRETURN_NO; + } + +long +MY_total_in(obj) + Compress::Bzip2 obj + + PROTOTYPE: $ + + CODE: + RETVAL = bzfile_total_in( obj ); + + OUTPUT: + RETVAL + +long +MY_total_out(obj) + Compress::Bzip2 obj + + PROTOTYPE: $ + + CODE: + RETVAL = bzfile_total_out( obj ); + + OUTPUT: + RETVAL + +int +MY_bzsetparams(obj, param, setting = -1) + Compress::Bzip2 obj + char* param + int setting + + PROTOTYPE: $$;$ + + CODE: + RETVAL = bzfile_setparams( obj, param, setting ); + + OUTPUT: + RETVAL + +int +MY_bzread(obj, buf, len=4096) + Compress::Bzip2 obj + unsigned int len + SV *buf + + PROTOTYPE: $$;$ + + CODE: + { + if (SvREADONLY(buf) && PL_curcop != &PL_compiling) + croak("bzread: buffer parameter is read-only"); + SvUPGRADE(buf, SVt_PV); + SvPOK_only(buf); + SvCUR_set(buf, 0); + + if (len) { + char* bufp = SvGROW(buf, len+1); + + RETVAL = bzfile_read( obj, bufp, len); + + if (RETVAL >= 0) { + SvCUR_set(buf, RETVAL) ; + *SvEND(buf) = '\0'; + } + } + else { + RETVAL = 0; + } + } + + OUTPUT: + RETVAL + buf + + +int +MY_bzreadline(obj, buf, len=4096) + Compress::Bzip2 obj + unsigned int len + SV *buf + + PROTOTYPE: $$;$ + + CODE: + { + if (SvREADONLY(buf) && PL_curcop != &PL_compiling) + croak("bzreadline: buffer parameter is read-only"); + SvUPGRADE(buf, SVt_PV); + SvPOK_only(buf); + SvCUR_set(buf, 0); + + if (len) { + char* bufp = SvGROW(buf, len+1); + + RETVAL = bzfile_readline( obj, bufp, len); + + if (RETVAL >= 0) { + SvCUR_set(buf, RETVAL) ; + *SvEND(buf) = '\0'; + } + } + else { + RETVAL = 0; + } + } + + OUTPUT: + RETVAL + buf + +int +MY_bzwrite(obj, buf, limit=0) + Compress::Bzip2 obj + SV *buf + SV *limit + + PROTOTYPE: $$;$ + + CODE: + { + char *bufp; + STRLEN len; + + if ( SvTRUE(limit) ) { + len = SvUV(limit); + SvGROW( buf, len ); + bufp = SvPV_nolen(buf); + } + else + bufp = SvPV(buf, len); + + RETVAL = bzfile_write( obj, bufp, len); + + if ( RETVAL >= 0 ) + SvCUR_set( buf, RETVAL ); + } + + OUTPUT: + RETVAL + +void +bzdeflateInit(...) + + PROTOTYPE: @ + + ALIAS: + compress_init = 1 + + INIT: + bzFile* obj = NULL; + SV *perlobj = NULL; + char *param; + STRLEN lnparam; + int setting; + + PPCODE: + { + int i; + + if (items % 2) croak("Compress::Bzip2::%s has odd parameter count", ix==0 ? "bzdeflateInit" : "compress_init"); + + obj = bzfile_new( 0, 0, 1, 0 ); + bzfile_openstream( "w", obj ); + + perlobj = newSV(0); + sv_setref_iv( perlobj, "Compress::Bzip2", PTR2IV(obj) ); + sv_2mortal(perlobj); + + if ( obj == NULL ) { + XPUSHs(sv_newmortal()); + if (GIMME == G_ARRAY) + XPUSHs(sv_2mortal(newSViv(global_bzip_errno))); + } + else { + for (i=0; i<items-1; i+=2) { + param = (char*) SvPV( ST(i), lnparam ); + setting = SvIV( ST(i+1) ); + bzfile_setparams( obj, param, setting ); + } + + bzfile_streambuf_set( obj, (char*) obj->bufferOfHolding, sizeof( obj->bufferOfHolding ) ); + + XPUSHs(perlobj); + if (GIMME == G_ARRAY) + XPUSHs(sv_2mortal(newSViv(global_bzip_errno))); + } + } + +void +MY_bzdeflate(obj, buffer) + Compress::Bzip2 obj + SV *buffer + + PROTOTYPE: $$ + + PPCODE: + { + char *firstp, *outp; + SV *outbuf = NULL; + STRLEN outbufl = 0; + + char *bufp, *inp; + STRLEN bufl; + + STRLEN bytes_to_go; + + char collect_buffer[1000]; + int i, amt_written, amt_collected; + int error_flag = 0; + + bufp = (char*) SvPV( buffer, bufl ); + + for ( bytes_to_go = bufl; bytes_to_go>0; ) { + amt_written = bzfile_write( obj, bufp, bytes_to_go ); + + if ( amt_written == -1 ) { + if ( errno != EAGAIN ) + error_flag =1; + else { + while ( -1 != ( amt_collected = bzfile_streambuf_collect( obj, collect_buffer, sizeof(collect_buffer) ) ) ) { + /* put the stuff into the SV output buffer */ + if ( outbuf == NULL ) { + outbuf = newSVpv( collect_buffer, amt_collected ); + outbufl = amt_collected; + firstp = SvPV_nolen( outbuf ); + outp = firstp; + } + else { + outbufl += amt_collected; + SvGROW( outbuf, outbufl ); + firstp = SvPV_nolen( outbuf ); + outp = SvEND( outbuf ); + } + + for ( inp=collect_buffer, i=0; i<amt_collected; i++ ) *outp++ = *inp++; + SvCUR_set( outbuf, outp-firstp) ; + + if ( obj->verbosity>=4 ) + PerlIO_printf(PerlIO_stderr(), "debug: bzdeflate collected %d, outbuf is now %ld\n", + amt_collected, outp-firstp); + } + + if ( errno != EAGAIN ) error_flag = 1; + } + } + else { + bytes_to_go -= amt_written; + bufp += amt_written; + } + } + + while ( -1 != ( amt_collected = bzfile_streambuf_collect( obj, collect_buffer, sizeof(collect_buffer) ) ) ) { + /* put the stuff into the SV output buffer */ + if ( outbuf == NULL ) { + outbuf = newSVpv( collect_buffer, amt_collected ); + outbufl = amt_collected; + firstp = SvPV_nolen( outbuf ); + outp = firstp; + } + else { + outbufl += amt_collected; + SvGROW( outbuf, outbufl ); + firstp = SvPV_nolen( outbuf ); + outp = SvEND( outbuf ); + } + + for ( inp=collect_buffer, i=0; i<amt_collected; i++ ) *outp++ = *inp++; + SvCUR_set( outbuf, outp-firstp) ; + + if ( obj->verbosity>=4 ) + PerlIO_printf(PerlIO_stderr(), "debug: bzdeflate collected %d, outbuf is now %ld\n", + amt_collected, outp-firstp); + } + + if ( errno != EAGAIN ) error_flag = 1; + + if (outbuf==NULL) { + if ( error_flag ) + XPUSHs(sv_newmortal()); + else + XPUSHs(sv_2mortal(newSVpv("",0))); + } + else + XPUSHs(sv_2mortal(outbuf)); + + if (GIMME == G_ARRAY) + XPUSHs(sv_2mortal(newSViv(global_bzip_errno))); + } + + +void +bzinflateInit(...) + + PROTOTYPE: @ + + ALIAS: + decompress_init = 1 + + INIT: + bzFile* obj = NULL; + SV *perlobj = NULL; + char *param; + STRLEN lnparam; + int setting; + + PPCODE: + { + int i; + + if (items % 2) + croak("Compress::Bzip2::%s has odd parameter count", ix==0 ? "bzinflateInit" : "decompress_init"); + + obj = bzfile_new( 0, 0, 1, 0 ); + bzfile_openstream( "r", obj ); + if ( obj == NULL ) { + XPUSHs(sv_newmortal()); + if (GIMME == G_ARRAY) + XPUSHs(sv_2mortal(newSViv(global_bzip_errno))); + } + + perlobj = newSV(0); + sv_setref_iv( perlobj, "Compress::Bzip2", PTR2IV(obj) ); + + for (i=0; i < items; i+=2) { + param = (char*) SvPV( ST(i), lnparam ); + setting = SvIV( ST(i+1) ); + bzfile_setparams( obj, param, setting ); + } + + XPUSHs(sv_2mortal(perlobj)); + if (GIMME == G_ARRAY) + XPUSHs(sv_2mortal(newSViv(global_bzip_errno))); + } + +void +MY_bzinflate(obj, buffer) + Compress::Bzip2 obj + SV *buffer + + PROTOTYPE: $$ + + PPCODE: + { + char *firstp, *outp; + SV *outbuf = NULL; + STRLEN outbufl = 0; + + STRLEN bufl; + char collect_buffer[1000]; + int i, amt_collected; + char *bufp, *inp; + int error_flag = 0; + + if (SvTYPE(buffer) == SVt_RV) + buffer = SvRV(buffer); + bufp = (char*) SvPV( buffer, bufl ); + bzfile_streambuf_deposit( obj, bufp, bufl ); + + while ( ( amt_collected = bzfile_read( obj, collect_buffer, sizeof(collect_buffer) ) ) >= 0 ) { + if ( obj->verbosity>=4 ) + PerlIO_printf(PerlIO_stderr(), "debug: bzinflate, bzfile_read returned %d bytes\n", amt_collected); + + /* put the stuff into the SV output buffer */ + if ( outbuf == NULL ) { + outbuf = newSVpv( collect_buffer, amt_collected ); + outbufl = amt_collected; + firstp = SvPV_nolen( outbuf ); + outp = firstp; + } + else { + outbufl += amt_collected; + SvGROW( outbuf, outbufl ); + firstp = SvPV_nolen( outbuf ); + outp = SvEND( outbuf ); + } + + for ( inp=collect_buffer, i=0; i<amt_collected; i++ ) *outp++ = *inp++; + SvCUR_set( outbuf, outp-firstp) ; + } + + if ( errno != EAGAIN ) error_flag = 1; + + if (outbuf==NULL) { + if ( error_flag ) + XPUSHs(sv_newmortal()); + else + XPUSHs(sv_2mortal(newSVpv("",0))); + } + else + XPUSHs(sv_2mortal(outbuf)); + + if (GIMME == G_ARRAY) + XPUSHs(sv_2mortal(newSViv(global_bzip_errno))); + } + + +## $stream->prefix: Compress::Bzip2 1.03 compatibility function +## ... only call this for a compress/deflate stream + +SV* +MY_prefix(obj) + Compress::Bzip2 obj + + CODE: + { + if (obj->strm.total_in_hi32) + XSRETURN_UNDEF; + else { + unsigned int in_len = obj->strm.total_in_lo32; + char out[6]; + + out[0] = 0xf0; + out[1] = (in_len >> 24) & 0xff; + out[2] = (in_len >> 16) & 0xff; + out[3] = (in_len >> 8) & 0xff; + out[4] = (in_len >> 0) & 0xff; + out[5] = 0; + + RETVAL = newSVpvn(out,5); + } + } + + OUTPUT: + RETVAL + +int +MY_is_write(obj) + Compress::Bzip2 obj + + PROTOTYPE: $ + + CODE: + RETVAL = obj->open_status == OPEN_STATUS_WRITE || obj->open_status == OPEN_STATUS_WRITESTREAM ? 1 : 0; + + OUTPUT: + RETVAL + +int +MY_is_read(obj) + Compress::Bzip2 obj + + PROTOTYPE: $ + + CODE: + RETVAL = obj->open_status == OPEN_STATUS_READ || obj->open_status == OPEN_STATUS_READSTREAM ? 1 : 0; + + OUTPUT: + RETVAL + +int +MY_is_stream(obj) + Compress::Bzip2 obj + + PROTOTYPE: $ + + CODE: + RETVAL = obj->open_status == OPEN_STATUS_WRITESTREAM || obj->open_status == OPEN_STATUS_READSTREAM ? 1 : 0; + + OUTPUT: + RETVAL |