summaryrefslogtreecommitdiffstats
path: root/storage/archive/azio.c
diff options
context:
space:
mode:
Diffstat (limited to 'storage/archive/azio.c')
-rw-r--r--storage/archive/azio.c946
1 files changed, 946 insertions, 0 deletions
diff --git a/storage/archive/azio.c b/storage/archive/azio.c
new file mode 100644
index 00000000..3529d875
--- /dev/null
+++ b/storage/archive/azio.c
@@ -0,0 +1,946 @@
+/*
+ azio is a modified version of gzio. It makes use of mysys and removes mallocs.
+ -Brian Aker
+*/
+
+/* gzio.c -- IO on .gz files
+ * Copyright (C) 1995-2005 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ *
+ */
+
+/* @(#) $Id$ */
+
+#include "azlib.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "my_sys.h"
+
+static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
+static int const az_magic[3] = {0xfe, 0x03, 0x01}; /* az magic header */
+
+/* gzip flag uchar */
+#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
+#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
+#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
+#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
+#define COMMENT 0x10 /* bit 4 set: file comment present */
+#define RESERVED 0xE0 /* bits 5..7: reserved */
+
+int az_open(azio_stream *s, const char *path, int Flags, File fd);
+int do_flush(azio_stream *file, int flush);
+int get_byte(azio_stream *s);
+void check_header(azio_stream *s);
+int write_header(azio_stream *s);
+int destroy(azio_stream *s);
+void putLong(File file, uLong x);
+uLong getLong(azio_stream *s);
+void read_header(azio_stream *s, unsigned char *buffer);
+
+#ifdef HAVE_PSI_INTERFACE
+extern PSI_file_key arch_key_file_data;
+#endif
+
+/* ===========================================================================
+ Opens a gzip (.gz) file for reading or writing. The mode parameter
+ is as in fopen ("rb" or "wb"). The file is given either by file descriptor
+ or path name (if fd == -1).
+ az_open returns NULL if the file could not be opened or if there was
+ insufficient memory to allocate the (de)compression state; errno
+ can be checked to distinguish the two cases (if errno is zero, the
+ zlib error is Z_MEM_ERROR).
+*/
+int az_open (azio_stream *s, const char *path, int Flags, File fd)
+{
+ int err;
+ int level = Z_DEFAULT_COMPRESSION; /* compression level */
+ int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */
+
+ s->stream.zalloc = my_az_allocator;
+ s->stream.zfree = my_az_free;
+ s->stream.opaque = (voidpf)0;
+ memset(s->inbuf, 0, AZ_BUFSIZE_READ);
+ memset(s->outbuf, 0, AZ_BUFSIZE_WRITE);
+ s->stream.next_in = s->inbuf;
+ s->stream.next_out = s->outbuf;
+ s->stream.avail_in = s->stream.avail_out = 0;
+ s->z_err = Z_OK;
+ s->z_eof = 0;
+ s->in = 0;
+ s->out = 0;
+ s->back = EOF;
+ s->crc = crc32(0L, Z_NULL, 0);
+ s->transparent = 0;
+ s->mode = 'r';
+ s->version = (unsigned char)az_magic[1]; /* this needs to be a define to version */
+ s->minor_version= (unsigned char) az_magic[2]; /* minor version */
+ s->dirty= AZ_STATE_CLEAN;
+ s->start= 0;
+
+ /*
+ We do our own version of append by nature.
+ We must always have write access to take card of the header.
+ */
+ DBUG_ASSERT(Flags | O_APPEND);
+ DBUG_ASSERT(Flags | O_WRONLY);
+
+ if (Flags & O_RDWR)
+ s->mode = 'w';
+
+ if (s->mode == 'w')
+ {
+ err = deflateInit2(&(s->stream), level,
+ Z_DEFLATED, -MAX_WBITS, 8, strategy);
+ /* windowBits is passed < 0 to suppress zlib header */
+
+ s->stream.next_out = s->outbuf;
+ if (err != Z_OK)
+ {
+ destroy(s);
+ return Z_NULL;
+ }
+ } else {
+ s->stream.next_in = s->inbuf;
+
+ err = inflateInit2(&(s->stream), -MAX_WBITS);
+ /* windowBits is passed < 0 to tell that there is no zlib header.
+ * Note that in this case inflate *requires* an extra "dummy" byte
+ * after the compressed stream in order to complete decompression and
+ * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are
+ * present after the compressed stream.
+ */
+ if (err != Z_OK)
+ {
+ destroy(s);
+ return Z_NULL;
+ }
+ }
+ s->stream.avail_out = AZ_BUFSIZE_WRITE;
+
+ errno = 0;
+ s->file = fd < 0 ? mysql_file_open(arch_key_file_data, path, Flags, MYF(0)) : fd;
+ DBUG_EXECUTE_IF("simulate_archive_open_failure",
+ {
+ if (s->file >= 0)
+ {
+ my_close(s->file, MYF(0));
+ s->file= -1;
+ my_errno= EMFILE;
+ }
+ });
+
+ if (s->file < 0 )
+ {
+ destroy(s);
+ return Z_NULL;
+ }
+
+ if (Flags & O_CREAT || Flags & O_TRUNC)
+ {
+ s->rows= 0;
+ s->forced_flushes= 0;
+ s->shortest_row= 0;
+ s->longest_row= 0;
+ s->auto_increment= 0;
+ s->check_point= 0;
+ s->comment_start_pos= 0;
+ s->comment_length= 0;
+ s->frm_start_pos= 0;
+ s->frm_length= 0;
+ s->dirty= 1; /* We create the file dirty */
+ s->start = AZHEADER_SIZE + AZMETA_BUFFER_SIZE;
+ write_header(s);
+ my_seek(s->file, 0, MY_SEEK_END, MYF(0));
+ }
+ else if (s->mode == 'w')
+ {
+ uchar buffer[AZHEADER_SIZE + AZMETA_BUFFER_SIZE];
+ my_pread(s->file, buffer, AZHEADER_SIZE + AZMETA_BUFFER_SIZE, 0,
+ MYF(0));
+ read_header(s, buffer); /* skip the .az header */
+ my_seek(s->file, 0, MY_SEEK_END, MYF(0));
+ }
+ else
+ {
+ /* Reset values in case of old version of archive file */
+ s->rows= 0;
+ s->forced_flushes= 0;
+ s->shortest_row= 0;
+ s->longest_row= 0;
+ s->auto_increment= 0;
+ s->check_point= 0;
+ s->comment_start_pos= 0;
+ s->comment_length= 0;
+ s->frm_start_pos= 0;
+ s->frm_length= 0;
+ check_header(s); /* skip the .az header */
+ }
+
+ return 1;
+}
+
+
+int write_header(azio_stream *s)
+{
+ char buffer[AZHEADER_SIZE + AZMETA_BUFFER_SIZE];
+ char *ptr= buffer;
+
+ if (s->version == 1)
+ return 0;
+
+ s->block_size= AZ_BUFSIZE_WRITE;
+ s->version = (unsigned char)az_magic[1];
+ s->minor_version = (unsigned char)az_magic[2];
+
+
+ /* Write a very simple .az header: */
+ memset(buffer, 0, AZHEADER_SIZE + AZMETA_BUFFER_SIZE);
+ *(ptr + AZ_MAGIC_POS)= az_magic[0];
+ *(ptr + AZ_VERSION_POS)= (unsigned char)s->version;
+ *(ptr + AZ_MINOR_VERSION_POS)= (unsigned char)s->minor_version;
+ *(ptr + AZ_BLOCK_POS)= (unsigned char)(s->block_size/1024); /* Reserved for block size */
+ *(ptr + AZ_STRATEGY_POS)= (unsigned char)Z_DEFAULT_STRATEGY; /* Compression Type */
+
+ int4store(ptr + AZ_FRM_POS, s->frm_start_pos); /* FRM Block */
+ int4store(ptr + AZ_FRM_LENGTH_POS, s->frm_length); /* FRM Block */
+ int4store(ptr + AZ_COMMENT_POS, s->comment_start_pos); /* COMMENT Block */
+ int4store(ptr + AZ_COMMENT_LENGTH_POS, s->comment_length); /* COMMENT Block */
+ int4store(ptr + AZ_META_POS, 0); /* Meta Block */
+ int4store(ptr + AZ_META_LENGTH_POS, 0); /* Meta Block */
+ int8store(ptr + AZ_START_POS, (unsigned long long)s->start); /* Start of Data Block Index Block */
+ int8store(ptr + AZ_ROW_POS, (unsigned long long)s->rows); /* Start of Data Block Index Block */
+ int8store(ptr + AZ_FLUSH_POS, (unsigned long long)s->forced_flushes); /* Start of Data Block Index Block */
+ int8store(ptr + AZ_CHECK_POS, (unsigned long long)s->check_point); /* Start of Data Block Index Block */
+ int8store(ptr + AZ_AUTOINCREMENT_POS, (unsigned long long)s->auto_increment); /* Start of Data Block Index Block */
+ int4store(ptr+ AZ_LONGEST_POS , s->longest_row); /* Longest row */
+ int4store(ptr+ AZ_SHORTEST_POS, s->shortest_row); /* Shorest row */
+ int4store(ptr+ AZ_FRM_POS,
+ AZHEADER_SIZE + AZMETA_BUFFER_SIZE); /* FRM position */
+ *(ptr + AZ_DIRTY_POS)= (unsigned char)s->dirty; /* Start of Data Block Index Block */
+
+ /* Always begin at the beginning, and end there as well */
+ return my_pwrite(s->file, (uchar*) buffer, AZHEADER_SIZE + AZMETA_BUFFER_SIZE,
+ 0, MYF(MY_NABP)) ? 1 : 0;
+}
+
+/* ===========================================================================
+ Opens a gzip (.gz) file for reading or writing.
+*/
+int azopen(azio_stream *s, const char *path, int Flags)
+{
+ return az_open(s, path, Flags, -1);
+}
+
+/* ===========================================================================
+ Associate a gzFile with the file descriptor fd. fd is not dup'ed here
+ to mimic the behavio(u)r of fdopen.
+*/
+int azdopen(azio_stream *s, File fd, int Flags)
+{
+ if (fd < 0) return 0;
+
+ return az_open (s, NULL, Flags, fd);
+}
+
+/* ===========================================================================
+ Read a byte from a azio_stream; update next_in and avail_in. Return EOF
+ for end of file.
+ IN assertion: the stream s has been sucessfully opened for reading.
+*/
+int get_byte(s)
+ azio_stream *s;
+{
+ if (s->z_eof) return EOF;
+ if (s->stream.avail_in == 0)
+ {
+ errno = 0;
+ s->stream.avail_in= (uInt) mysql_file_read(s->file, (uchar *)s->inbuf,
+ AZ_BUFSIZE_READ, MYF(0));
+ if (s->stream.avail_in == 0)
+ {
+ s->z_eof = 1;
+ return EOF;
+ }
+ else if (s->stream.avail_in == (uInt) -1)
+ {
+ s->z_eof= 1;
+ s->z_err= Z_ERRNO;
+ return EOF;
+ }
+ s->stream.next_in = s->inbuf;
+ }
+ s->stream.avail_in--;
+ return *(s->stream.next_in)++;
+}
+
+/* ===========================================================================
+ Check the gzip header of a azio_stream opened for reading. Set the stream
+ mode to transparent if the gzip magic header is not present; set s->err
+ to Z_DATA_ERROR if the magic header is present but the rest of the header
+ is incorrect.
+ IN assertion: the stream s has already been created sucessfully;
+ s->stream.avail_in is zero for the first time, but may be non-zero
+ for concatenated .gz files.
+*/
+void check_header(azio_stream *s)
+{
+ int method; /* method uchar */
+ int flags; /* flags uchar */
+ uInt len;
+ int c;
+
+ /* Assure two bytes in the buffer so we can peek ahead -- handle case
+ where first byte of header is at the end of the buffer after the last
+ gzip segment */
+ len = s->stream.avail_in;
+ if (len < 2) {
+ if (len) s->inbuf[0] = s->stream.next_in[0];
+ errno = 0;
+ len = (uInt)mysql_file_read(s->file, (uchar *)s->inbuf + len,
+ AZ_BUFSIZE_READ >> len, MYF(0));
+ if (len == (uInt)-1) s->z_err = Z_ERRNO;
+ s->stream.avail_in += len;
+ s->stream.next_in = s->inbuf;
+ if (s->stream.avail_in < 2) {
+ s->transparent = s->stream.avail_in;
+ return;
+ }
+ }
+
+ /* Peek ahead to check the gzip magic header */
+ if ( s->stream.next_in[0] == gz_magic[0] && s->stream.next_in[1] == gz_magic[1])
+ {
+ read_header(s, s->stream.next_in);
+ s->stream.avail_in -= 2;
+ s->stream.next_in += 2;
+
+ /* Check the rest of the gzip header */
+ method = get_byte(s);
+ flags = get_byte(s);
+ if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
+ s->z_err = Z_DATA_ERROR;
+ return;
+ }
+
+ /* Discard time, xflags and OS code: */
+ for (len = 0; len < 6; len++) (void)get_byte(s);
+
+ if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
+ len = (uInt)get_byte(s);
+ len += ((uInt)get_byte(s))<<8;
+ /* len is garbage if EOF but the loop below will quit anyway */
+ while (len-- != 0 && get_byte(s) != EOF) ;
+ }
+ if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
+ while ((c = get_byte(s)) != 0 && c != EOF) ;
+ }
+ if ((flags & COMMENT) != 0) { /* skip the .gz file comment */
+ while ((c = get_byte(s)) != 0 && c != EOF) ;
+ }
+ if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
+ for (len = 0; len < 2; len++) (void)get_byte(s);
+ }
+ s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
+ if (!s->start)
+ s->start= my_tell(s->file, MYF(0)) - s->stream.avail_in;
+ }
+ else if ( s->stream.next_in[0] == az_magic[0] && s->stream.next_in[1] == az_magic[1])
+ {
+ unsigned char buffer[AZHEADER_SIZE + AZMETA_BUFFER_SIZE];
+
+ for (len = 0; len < (AZHEADER_SIZE + AZMETA_BUFFER_SIZE); len++)
+ buffer[len]= get_byte(s);
+ s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
+ read_header(s, buffer);
+ for (; len < s->start; len++)
+ get_byte(s);
+ }
+ else
+ {
+ s->z_err = Z_OK;
+
+ return;
+ }
+}
+
+void read_header(azio_stream *s, unsigned char *buffer)
+{
+ if (buffer[0] == az_magic[0] && buffer[1] == az_magic[1])
+ {
+ uchar tmp[AZ_FRMVER_LEN + 2];
+
+ s->version= (unsigned int)buffer[AZ_VERSION_POS];
+ s->minor_version= (unsigned int)buffer[AZ_MINOR_VERSION_POS];
+ s->block_size= 1024 * buffer[AZ_BLOCK_POS];
+ s->start= (unsigned long long)uint8korr(buffer + AZ_START_POS);
+ s->rows= (unsigned long long)uint8korr(buffer + AZ_ROW_POS);
+ s->check_point= (unsigned long long)uint8korr(buffer + AZ_CHECK_POS);
+ s->forced_flushes= (unsigned long long)uint8korr(buffer + AZ_FLUSH_POS);
+ s->auto_increment= (unsigned long long)uint8korr(buffer + AZ_AUTOINCREMENT_POS);
+ s->longest_row= (unsigned int)uint4korr(buffer + AZ_LONGEST_POS);
+ s->shortest_row= (unsigned int)uint4korr(buffer + AZ_SHORTEST_POS);
+ s->frm_start_pos= (unsigned int)uint4korr(buffer + AZ_FRM_POS);
+ s->frm_length= (unsigned int)uint4korr(buffer + AZ_FRM_LENGTH_POS);
+ s->comment_start_pos= (unsigned int)uint4korr(buffer + AZ_COMMENT_POS);
+ s->comment_length= (unsigned int)uint4korr(buffer + AZ_COMMENT_LENGTH_POS);
+ s->dirty= (unsigned int)buffer[AZ_DIRTY_POS];
+
+ /*
+ we'll hard-code the current frm format for now, to avoid
+ changing archive table versions.
+ */
+ if (s->frm_length == 0 ||
+ my_pread(s->file, tmp, sizeof(tmp), s->frm_start_pos + 64, MYF(MY_NABP)) ||
+ tmp[0] != 0 || tmp[1] != AZ_FRMVER_LEN)
+ {
+ s->frmver_length= 0;
+ }
+ else
+ {
+ s->frmver_length= tmp[1];
+ memcpy(s->frmver, tmp+2, s->frmver_length);
+ }
+ }
+ else if (buffer[0] == gz_magic[0] && buffer[1] == gz_magic[1])
+ {
+ /*
+ Set version number to previous version (1).
+ */
+ s->version= 1;
+ s->auto_increment= 0;
+ s->frm_length= 0;
+ s->longest_row= 0;
+ s->shortest_row= 0;
+ } else {
+ /*
+ Unknown version.
+ Most probably due to a corrupt archive.
+ */
+ s->dirty= AZ_STATE_DIRTY;
+ s->z_err= Z_VERSION_ERROR;
+ }
+}
+
+/* ===========================================================================
+ * Cleanup then free the given azio_stream. Return a zlib error code.
+ Try freeing in the reverse order of allocations.
+ */
+int destroy (s)
+ azio_stream *s;
+{
+ int err = Z_OK;
+
+ if (s->stream.state != NULL)
+ {
+ if (s->mode == 'w')
+ err = deflateEnd(&(s->stream));
+ else if (s->mode == 'r')
+ err = inflateEnd(&(s->stream));
+ }
+
+ if (s->file > 0 && my_close(s->file, MYF(0)))
+ err = Z_ERRNO;
+
+ s->file= -1;
+
+ if (s->z_err < 0) err = s->z_err;
+
+ return err;
+}
+
+/* ===========================================================================
+ Reads the given number of uncompressed bytes from the compressed file.
+ azread returns the number of bytes actually read (0 for end of file).
+*/
+unsigned int ZEXPORT azread ( azio_stream *s, voidp buf, size_t len, int *error)
+{
+ Bytef *start = (Bytef*)buf; /* starting point for crc computation */
+ Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */
+ *error= 0;
+
+ if (s->mode != 'r')
+ {
+ *error= Z_STREAM_ERROR;
+ return 0;
+ }
+
+ if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)
+ {
+ *error= s->z_err;
+ return 0;
+ }
+
+ if (s->z_err == Z_STREAM_END) /* EOF */
+ {
+ return 0;
+ }
+
+ next_out = (Byte*)buf;
+ s->stream.next_out = (Bytef*)buf;
+ s->stream.avail_out = (uInt)len;
+
+ if (s->stream.avail_out && s->back != EOF) {
+ *next_out++ = s->back;
+ s->stream.next_out++;
+ s->stream.avail_out--;
+ s->back = EOF;
+ s->out++;
+ start++;
+ if (s->last) {
+ s->z_err = Z_STREAM_END;
+ {
+ return 1;
+ }
+ }
+ }
+
+ while (s->stream.avail_out != 0) {
+
+ if (s->transparent) {
+ /* Copy first the lookahead bytes: */
+ uInt n = s->stream.avail_in;
+ if (n > s->stream.avail_out) n = s->stream.avail_out;
+ if (n > 0) {
+ memcpy(s->stream.next_out, s->stream.next_in, n);
+ next_out += n;
+ s->stream.next_out = (Bytef *)next_out;
+ s->stream.next_in += n;
+ s->stream.avail_out -= n;
+ s->stream.avail_in -= n;
+ }
+ if (s->stream.avail_out > 0)
+ {
+ s->stream.avail_out -=
+ (uInt)mysql_file_read(s->file, (uchar *)next_out,
+ s->stream.avail_out, MYF(0));
+ }
+ len -= s->stream.avail_out;
+ s->in += len;
+ s->out += len;
+ if (len == 0) s->z_eof = 1;
+ {
+ return (uint)len;
+ }
+ }
+ if (s->stream.avail_in == 0 && !s->z_eof) {
+
+ errno = 0;
+ s->stream.avail_in = (uInt)mysql_file_read(s->file, (uchar *)s->inbuf,
+ AZ_BUFSIZE_READ, MYF(0));
+ if (s->stream.avail_in == 0)
+ {
+ s->z_eof = 1;
+ }
+ s->stream.next_in = (Bytef *)s->inbuf;
+ }
+ s->in += s->stream.avail_in;
+ s->out += s->stream.avail_out;
+ s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
+ s->in -= s->stream.avail_in;
+ s->out -= s->stream.avail_out;
+
+ if (s->z_err == Z_STREAM_END) {
+ /* Check CRC and original size */
+ s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
+ start = s->stream.next_out;
+
+ if (getLong(s) != s->crc) {
+ s->z_err = Z_DATA_ERROR;
+ } else {
+ (void)getLong(s);
+ /* The uncompressed length returned by above getlong() may be
+ * different from s->out in case of concatenated .gz files.
+ * Check for such files:
+ */
+ check_header(s);
+ if (s->z_err == Z_OK)
+ {
+ inflateReset(&(s->stream));
+ s->crc = crc32(0L, Z_NULL, 0);
+ }
+ }
+ }
+ if (s->z_err != Z_OK || s->z_eof) break;
+ }
+ s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start));
+
+ if (len == s->stream.avail_out &&
+ (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO))
+ {
+ *error= s->z_err;
+
+ return 0;
+ }
+
+ return (uint)(len - s->stream.avail_out);
+}
+
+
+/* ===========================================================================
+ Writes the given number of uncompressed bytes into the compressed file.
+ azwrite returns the number of bytes actually written (0 in case of error).
+*/
+unsigned int azwrite (azio_stream *s, const voidp buf, unsigned int len)
+{
+ s->stream.next_in = (Bytef*)buf;
+ s->stream.avail_in = len;
+
+ s->rows++;
+
+ while (s->stream.avail_in != 0)
+ {
+ if (s->stream.avail_out == 0)
+ {
+
+ s->stream.next_out = s->outbuf;
+ if (mysql_file_write(s->file, (uchar *)s->outbuf, AZ_BUFSIZE_WRITE,
+ MYF(0)) != AZ_BUFSIZE_WRITE)
+ {
+ s->z_err = Z_ERRNO;
+ break;
+ }
+ s->stream.avail_out = AZ_BUFSIZE_WRITE;
+ }
+ s->in += s->stream.avail_in;
+ s->out += s->stream.avail_out;
+ s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
+ s->in -= s->stream.avail_in;
+ s->out -= s->stream.avail_out;
+ if (s->z_err != Z_OK) break;
+ }
+ s->crc = crc32(s->crc, (const Bytef *)buf, len);
+
+ if (len > s->longest_row)
+ s->longest_row= len;
+
+ if (len < s->shortest_row || !(s->shortest_row))
+ s->shortest_row= len;
+
+ return (unsigned int)(len - s->stream.avail_in);
+}
+
+
+/* ===========================================================================
+ Flushes all pending output into the compressed file. The parameter
+ flush is as in the deflate() function.
+*/
+int do_flush (azio_stream *s, int flush)
+{
+ uInt len;
+ int done = 0;
+ my_off_t afterwrite_pos;
+
+ if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
+
+ s->stream.avail_in = 0; /* should be zero already anyway */
+
+ for (;;)
+ {
+ len = AZ_BUFSIZE_WRITE - s->stream.avail_out;
+
+ if (len != 0)
+ {
+ s->check_point= my_tell(s->file, MYF(0));
+ if ((uInt)mysql_file_write(s->file, (uchar *)s->outbuf, len, MYF(0)) != len)
+ {
+ s->z_err = Z_ERRNO;
+ return Z_ERRNO;
+ }
+ s->stream.next_out = s->outbuf;
+ s->stream.avail_out = AZ_BUFSIZE_WRITE;
+ }
+ if (done) break;
+ s->out += s->stream.avail_out;
+ s->z_err = deflate(&(s->stream), flush);
+ s->out -= s->stream.avail_out;
+
+ /* Ignore the second of two consecutive flushes: */
+ if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK;
+
+ /* deflate has finished flushing only when it hasn't used up
+ * all the available space in the output buffer:
+ */
+ done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
+
+ if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
+ }
+
+ if (flush == Z_FINISH)
+ s->dirty= AZ_STATE_CLEAN; /* Mark it clean, we should be good now */
+ else
+ s->dirty= AZ_STATE_SAVED; /* Mark it clean, we should be good now */
+
+ afterwrite_pos= my_tell(s->file, MYF(0));
+ write_header(s);
+ my_seek(s->file, afterwrite_pos, SEEK_SET, MYF(0));
+
+ return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
+}
+
+int ZEXPORT azflush (s, flush)
+ azio_stream *s;
+ int flush;
+{
+ int err;
+
+ if (s->mode == 'r')
+ {
+ unsigned char buffer[AZHEADER_SIZE + AZMETA_BUFFER_SIZE];
+ my_pread(s->file, (uchar*) buffer, AZHEADER_SIZE + AZMETA_BUFFER_SIZE, 0,
+ MYF(0));
+ read_header(s, buffer); /* skip the .az header */
+
+ return Z_OK;
+ }
+ else
+ {
+ s->forced_flushes++;
+ err= do_flush(s, flush);
+
+ if (err) return err;
+ my_sync(s->file, MYF(0));
+ return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
+ }
+}
+
+/* ===========================================================================
+ Rewinds input file.
+*/
+int azrewind (s)
+ azio_stream *s;
+{
+ if (s == NULL || s->mode != 'r') return -1;
+
+ s->z_err = Z_OK;
+ s->z_eof = 0;
+ s->back = EOF;
+ s->stream.avail_in = 0;
+ s->stream.next_in = (Bytef *)s->inbuf;
+ s->crc = crc32(0L, Z_NULL, 0);
+ if (!s->transparent) (void)inflateReset(&s->stream);
+ s->in = 0;
+ s->out = 0;
+ return my_seek(s->file, (int)s->start, MY_SEEK_SET, MYF(0)) == MY_FILEPOS_ERROR;
+}
+
+/* ===========================================================================
+ Sets the starting position for the next azread or azwrite on the given
+ compressed file. The offset represents a number of bytes in the
+ azseek returns the resulting offset location as measured in bytes from
+ the beginning of the uncompressed stream, or -1 in case of error.
+ SEEK_END is not implemented, returns error.
+ In this version of the library, azseek can be extremely slow.
+*/
+my_off_t azseek (s, offset, whence)
+ azio_stream *s;
+ my_off_t offset;
+ int whence;
+{
+
+ if (s == NULL || whence == SEEK_END ||
+ s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) {
+ return -1L;
+ }
+
+ if (s->mode == 'w')
+ {
+ if (whence == SEEK_SET)
+ offset -= s->in;
+
+ /* At this point, offset is the number of zero bytes to write. */
+ /* There was a zmemzero here if inbuf was null -Brian */
+ while (offset > 0)
+ {
+ uInt size = AZ_BUFSIZE_READ;
+ if (offset < AZ_BUFSIZE_READ) size = (uInt)offset;
+
+ size = azwrite(s, s->inbuf, size);
+ if (size == 0) return -1L;
+
+ offset -= size;
+ }
+ return s->in;
+ }
+ /* Rest of function is for reading only */
+
+ /* compute absolute position */
+ if (whence == SEEK_CUR) {
+ offset += s->out;
+ }
+
+ if (s->transparent) {
+ /* map to my_seek */
+ s->back = EOF;
+ s->stream.avail_in = 0;
+ s->stream.next_in = (Bytef *)s->inbuf;
+ if (my_seek(s->file, offset, MY_SEEK_SET, MYF(0)) == MY_FILEPOS_ERROR) return -1L;
+
+ s->in = s->out = offset;
+ return offset;
+ }
+
+ /* For a negative seek, rewind and use positive seek */
+ if (offset >= s->out) {
+ offset -= s->out;
+ } else if (azrewind(s)) {
+ return -1L;
+ }
+ /* offset is now the number of bytes to skip. */
+
+ if (offset && s->back != EOF) {
+ s->back = EOF;
+ s->out++;
+ offset--;
+ if (s->last) s->z_err = Z_STREAM_END;
+ }
+ while (offset > 0) {
+ int error;
+ unsigned int size = AZ_BUFSIZE_WRITE;
+ if (offset < AZ_BUFSIZE_WRITE) size = (int)offset;
+
+ size = azread(s, s->outbuf, size, &error);
+ if (error < 0) return -1L;
+ offset -= size;
+ }
+ return s->out;
+}
+
+/* ===========================================================================
+ Returns the starting position for the next azread or azwrite on the
+ given compressed file. This position represents a number of bytes in the
+ uncompressed data stream.
+*/
+my_off_t ZEXPORT aztell (file)
+ azio_stream *file;
+{
+ return azseek(file, 0L, SEEK_CUR);
+}
+
+
+/* ===========================================================================
+ Outputs a long in LSB order to the given file
+*/
+void putLong (File file, uLong x)
+{
+ int n;
+ uchar buffer[1];
+
+ for (n = 0; n < 4; n++)
+ {
+ buffer[0]= (int)(x & 0xff);
+ mysql_file_write(file, buffer, 1, MYF(0));
+ x >>= 8;
+ }
+}
+
+/* ===========================================================================
+ Reads a long in LSB order from the given azio_stream. Sets z_err in case
+ of error.
+*/
+uLong getLong (azio_stream *s)
+{
+ uLong x = (uLong)get_byte(s);
+ int c;
+
+ x += ((uLong)get_byte(s))<<8;
+ x += ((uLong)get_byte(s))<<16;
+ c = get_byte(s);
+ if (c == EOF) s->z_err = Z_DATA_ERROR;
+ x += ((uLong)c)<<24;
+ return x;
+}
+
+/* ===========================================================================
+ Flushes all pending output if necessary, closes the compressed file
+ and deallocates all the (de)compression state.
+*/
+int azclose (azio_stream *s)
+{
+
+ if (s == NULL) return Z_STREAM_ERROR;
+
+ if (s->file < 1) return Z_OK;
+
+ if (s->mode == 'w')
+ {
+ if (do_flush(s, Z_FINISH) != Z_OK)
+ {
+ destroy(s);
+ return Z_ERRNO;
+ }
+
+ putLong(s->file, s->crc);
+ putLong(s->file, (uLong)(s->in & 0xffffffff));
+ s->dirty= AZ_STATE_CLEAN;
+ s->check_point= my_tell(s->file, MYF(0));
+ write_header(s);
+ }
+
+ return destroy(s);
+}
+
+/*
+ Though this was added to support MySQL's FRM file, anything can be
+ stored in this location.
+*/
+int azwrite_frm(azio_stream *s, const uchar *blob, size_t length)
+{
+ if (s->mode == 'r')
+ return 1;
+
+ if (s->rows > 0)
+ return 1;
+
+ s->frm_start_pos= (uint) s->start;
+ s->frm_length= (uint)length;
+ s->start+= length;
+
+ if (my_pwrite(s->file, blob, s->frm_length,
+ s->frm_start_pos, MYF(MY_NABP)) ||
+ write_header(s) ||
+ (my_seek(s->file, 0, MY_SEEK_END, MYF(0)) == MY_FILEPOS_ERROR))
+ return 1;
+
+ return 0;
+}
+
+int azread_frm(azio_stream *s, uchar *blob)
+{
+ return my_pread(s->file, blob, s->frm_length,
+ s->frm_start_pos, MYF(MY_NABP)) ? 1 : 0;
+}
+
+
+/*
+ Simple comment field
+*/
+int azwrite_comment(azio_stream *s, const char *blob, size_t length)
+{
+ if (s->mode == 'r')
+ return 1;
+
+ if (s->rows > 0)
+ return 1;
+
+ s->comment_start_pos= (uint) s->start;
+ s->comment_length= (uint)length;
+ s->start+= length;
+
+ my_pwrite(s->file, (uchar*) blob, s->comment_length, s->comment_start_pos,
+ MYF(0));
+
+ write_header(s);
+ my_seek(s->file, 0, MY_SEEK_END, MYF(0));
+
+ return 0;
+}
+
+int azread_comment(azio_stream *s, char *blob)
+{
+ my_pread(s->file, (uchar*) blob, s->comment_length, s->comment_start_pos,
+ MYF(0));
+
+ return 0;
+}