From: John Darrington Date: Fri, 1 Jul 2011 15:22:02 +0000 (+0200) Subject: Implemented a zip-writer to correspond to zip-reader X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=pspp;a=commitdiff_plain;h=551202adbc334f9c33f8290a4e407b422651bbd5 Implemented a zip-writer to correspond to zip-reader --- diff --git a/src/libpspp/automake.mk b/src/libpspp/automake.mk index 8845525f92..805887410b 100644 --- a/src/libpspp/automake.mk +++ b/src/libpspp/automake.mk @@ -30,6 +30,8 @@ src_libpspp_libpspp_la_SOURCES = \ src/libpspp/freaderror.h \ src/libpspp/hash-functions.c \ src/libpspp/hash-functions.h \ + src/libpspp/inflate.c \ + src/libpspp/inflate.h \ src/libpspp/heap.c \ src/libpspp/heap.h \ src/libpspp/hmap.c \ @@ -87,6 +89,9 @@ src_libpspp_libpspp_la_SOURCES = \ src/libpspp/u8-istream.c \ src/libpspp/u8-istream.h \ src/libpspp/version.h \ + src/libpspp/zip-private.h \ + src/libpspp/zip-reader.c \ + src/libpspp/zip-reader.h \ src/libpspp/zip-writer.c \ src/libpspp/zip-writer.h diff --git a/src/libpspp/inflate.c b/src/libpspp/inflate.c new file mode 100644 index 0000000000..478747843b --- /dev/null +++ b/src/libpspp/inflate.c @@ -0,0 +1,148 @@ +/* PSPP - a program for statistical analysis. + Copyright (C) 2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + + +#include + +#include "inflate.h" + +#if HAVE_ZLIB_H + +#include +#include +#include +#include +#include "zip-reader.h" + +#include "str.h" + +#include "gettext.h" +#define _(msgid) gettext (msgid) +#define N_(msgid) (msgid) + +#define UCOMPSIZE 4096 + +struct inflator +{ + z_stream zss; + int state; + unsigned char ucomp[UCOMPSIZE]; + size_t bytes_uncomp; + size_t ucomp_bytes_read; + + /* Two bitfields as defined by RFC1950 */ + uint16_t cmf_flg ; +}; + +void +inflate_finish (struct zip_member *zm) +{ + struct inflator *inf = zm->aux; + + inflateEnd (&inf->zss); + + free (inf); +} + +bool +inflate_init (struct zip_member *zm) +{ + int r; + struct inflator *inf = xzalloc (sizeof *inf); + + uint16_t flg = 0 ; + uint16_t cmf = 0x8; /* Always 8 for inflate */ + + const uint16_t cinfo = 7; /* log_2(Window size) - 8 */ + + cmf |= cinfo << 4; /* Put cinfo into the high nibble */ + + /* make these into a 16 bit word */ + inf->cmf_flg = (cmf << 8 ) | flg; + + /* Set the check bits */ + inf->cmf_flg += 31 - (inf->cmf_flg % 31); + assert (inf->cmf_flg % 31 == 0); + + inf->zss.next_in = Z_NULL; + inf->zss.avail_in = 0; + inf->zss.zalloc = Z_NULL; + inf->zss.zfree = Z_NULL; + inf->zss.opaque = Z_NULL; + r = inflateInit (&inf->zss); + + if ( Z_OK != r) + { + ds_put_format (zm->errs, _("Cannot initialize inflator: %s"), zError (r)); + return false; + } + + zm->aux = inf; + + return true; +} + + +int +inflate_read (struct zip_member *zm, void *buf, size_t n) +{ + struct inflator *inf = zm->aux; + + if (inf->zss.avail_in == 0) + { + int bytes_read; + int bytes_to_read; + int pad = 0; + + if ( inf->state == 0) + { + inf->ucomp[1] = inf->cmf_flg ; + inf->ucomp[0] = inf->cmf_flg >> 8 ; + + pad = 2; + inf->state++; + } + + bytes_to_read = zm->comp_size - inf->ucomp_bytes_read; + + if (bytes_to_read == 0) + return 0; + + if (bytes_to_read > UCOMPSIZE) + bytes_to_read = UCOMPSIZE; + + bytes_read = fread (inf->ucomp + pad, 1, bytes_to_read - pad, zm->fp); + + inf->ucomp_bytes_read += bytes_read; + + inf->zss.avail_in = bytes_read + pad; + inf->zss.next_in = inf->ucomp; + } + inf->zss.avail_out = n; + inf->zss.next_out = buf; + + int r = inflate (&inf->zss, Z_NO_FLUSH); + if ( Z_OK == r) + { + return n - inf->zss.avail_out; + } + + ds_put_format (zm->errs, _("Error inflating: %s"), zError (r)); + + return -1; +} + +#endif diff --git a/src/libpspp/inflate.h b/src/libpspp/inflate.h new file mode 100644 index 0000000000..c487687ba3 --- /dev/null +++ b/src/libpspp/inflate.h @@ -0,0 +1,32 @@ +/* PSPP - a program for statistical analysis. + Copyright (C) 2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef INFLATE_H +#define INFLATE_H 1 + +#include +#include + +struct zip_member ; + +bool inflate_init (struct zip_member *zm); + +int inflate_read (struct zip_member *zm, void *buf, size_t n); + +void inflate_finish (struct zip_member *zm); + + +#endif diff --git a/src/libpspp/zip-private.h b/src/libpspp/zip-private.h new file mode 100644 index 0000000000..5c956dd8ae --- /dev/null +++ b/src/libpspp/zip-private.h @@ -0,0 +1,26 @@ +/* PSPP - a program for statistical analysis. + Copyright (C) 2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef ZIP_PRIVATE_H +#define ZIP_PRIVATE_H 1 + +#define MAGIC_EOCD ( (uint32_t) 0x06054b50) /* End of directory */ +#define MAGIC_SOCD ( (uint32_t) 0x02014b50) /* Start of directory */ +#define MAGIC_LHDR ( (uint32_t) 0x04034b50) /* Local Header */ +#define MAGIC_DDHD ( (uint32_t) 0x08074b50) /* Data Descriptor Header */ + +#endif + diff --git a/src/libpspp/zip-reader.c b/src/libpspp/zip-reader.c new file mode 100644 index 0000000000..bee7e2f240 --- /dev/null +++ b/src/libpspp/zip-reader.c @@ -0,0 +1,559 @@ +/* PSPP - a program for statistical analysis. + Copyright (C) 2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "inflate.h" + +#include "str.h" + +#include "zip-reader.h" +#include "zip-private.h" + +#include "gettext.h" +#define _(msgid) gettext (msgid) +#define N_(msgid) (msgid) + + +static bool find_eocd (FILE *fp, off_t *off); + +static int +stored_read (struct zip_member *zm, void *buf, size_t n) +{ + return fread (buf, 1, n, zm->fp); +} + +static bool +stored_init (struct zip_member *zm UNUSED) +{ + return true; +} + +static void +stored_finish (struct zip_member *zm UNUSED) +{ + /* Nothing required */ +} + + +static struct decompressor decompressors[n_COMPRESSION] = + { + {stored_init, stored_read, stored_finish}, +#if HAVE_ZLIB_H + {inflate_init, inflate_read, inflate_finish} +#endif + }; + +static enum compression +comp_code (struct zip_member *zm, uint16_t c) +{ + enum compression which; + switch (c) + { + case 0: + which = COMPRESSION_STORED; + break; +#if HAVE_ZLIB_H + case 8: + which = COMPRESSION_INFLATE; + break; +#endif + default: + ds_put_format (zm->errs, _("Unsupported compression type (%d)"), c); + which = n_COMPRESSION; + break; + } + return which; +} + + +struct zip_reader +{ + char *filename; /* The name of the file from which the data is read */ + FILE *fr; /* The stream from which the meta data is read */ + uint16_t n_members; /* The number of members in this archive */ + struct zip_member **members; /* The members (may be null pointers until the headers have been read */ + int nm; + struct string *errs; +}; + +void +zip_member_finish (struct zip_member *zm) +{ + ds_clear (zm->errs); + /* Probably not useful, because we would have to read right to the end of the member + if (zm->expected_crc != zm->crc) + { + ds_put_cstr (zm->errs, _("CRC error reading zip")); + } + */ + zip_member_unref (zm); +} + + + +/* Destroy the zip reader */ +void +zip_reader_destroy (struct zip_reader *zr) +{ + int i; + if (zr == NULL) + return; + + fclose (zr->fr); + free (zr->filename); + + for (i = 0; i < zr->n_members; ++i) + { + zip_member_unref (zr->members[i]); + } + free (zr->members); + free (zr); +} + + +void +zm_dump (const struct zip_member *zm) +{ + printf ("%d\t%08x\t %s\n", zm->ucomp_size, zm->expected_crc, zm->name); +} + + +/* Skip N bytes in F */ +static void +skip_bytes (FILE *f, size_t n) +{ + fseeko (f, n, SEEK_CUR); +} + +/* Read N bytes from F, storing the result in X */ +static void +get_bytes (FILE *f, void *x, size_t n) +{ + fread (x, 1, n, f); +} + +/* Read a 32 bit value from F */ +static void +get_u32 (FILE *f, uint32_t *x) +{ + get_bytes (f, x, sizeof *x); +} + + +/* Read 32 bit integer and compare it with EXPECTED. + place an error string in ERR if necessary. */ +static bool +check_magic (FILE *f, uint32_t expected, struct string *err) +{ + uint32_t magic; + + get_u32 (f, &magic); + + if ((expected != magic)) + { + ds_put_format (err, + _("Corrupt file at 0x%llx: Expected %"PRIx32"; got %"PRIx32), + (long long int) ftello (f) - sizeof (uint32_t), expected, magic); + + return false; + } + return true; +} + + +/* Read a 16 bit value from F */ +static void +get_u16 (FILE *f, uint16_t *x) +{ + get_bytes (f, x, sizeof *x); +} + +/* Reads upto BYTES bytes from ZM and puts them in BUF. + Returns the number of bytes read, or -1 on error */ +int +zip_member_read (struct zip_member *zm, void *buf, size_t bytes) +{ + int bytes_read = 0; + + ds_clear (zm->errs); + + if ( bytes > zm->bytes_unread) + bytes = zm->bytes_unread; + + bytes_read = decompressors[zm->compression].read (zm, buf, bytes); + if ( bytes_read < 0) + return bytes_read; + + zm->crc = crc32_update (zm->crc, buf, bytes_read); + + zm->bytes_unread -= bytes_read; + + return bytes_read; +} + + +/* + Read a local file header from ZR and add it to ZR's internal array. + Returns a pointer to the member read. This pointer belongs to ZR. + If the caller wishes to control it, she should ref it with + zip_member_ref. +*/ +static struct zip_member * +zip_header_read_next (struct zip_reader *zr) +{ + struct zip_member *zm = xzalloc (sizeof *zm); + + uint16_t v, nlen, extralen; + uint16_t gp, time, date; + + uint16_t clen, diskstart, iattr; + uint32_t eattr; + uint16_t comp_type; + + ds_clear (zr->errs); + + if ( ! check_magic (zr->fr, MAGIC_SOCD, zr->errs)) + return NULL; + + get_u16 (zr->fr, &v); + + get_u16 (zr->fr, &v); + get_u16 (zr->fr, &gp); + get_u16 (zr->fr, &comp_type); + + zm->compression = comp_code (zm, comp_type); + + get_u16 (zr->fr, &time); + get_u16 (zr->fr, &date); + get_u32 (zr->fr, &zm->expected_crc); + get_u32 (zr->fr, &zm->comp_size); + get_u32 (zr->fr, &zm->ucomp_size); + get_u16 (zr->fr, &nlen); + get_u16 (zr->fr, &extralen); + get_u16 (zr->fr, &clen); + get_u16 (zr->fr, &diskstart); + get_u16 (zr->fr, &iattr); + get_u32 (zr->fr, &eattr); + get_u32 (zr->fr, &zm->offset); + + zm->name = calloc (nlen + 1, 1); + get_bytes (zr->fr, zm->name, nlen); + + skip_bytes (zr->fr, extralen); + + zr->members[zr->nm++] = zm; + + zm->fp = fopen (zr->filename, "r"); + zm->ref_cnt = 1; + zm->errs = zr->errs; + + return zm; +} + + +/* Create a reader from the zip called FILENAME */ +struct zip_reader * +zip_reader_create (const char *filename, struct string *errs) +{ + uint16_t disknum, total_members; + off_t offset = 0; + uint32_t central_dir_start, central_dir_length; + + struct zip_reader *zr = malloc (sizeof *zr); + zr->errs = errs; + if ( zr->errs) + ds_init_empty (zr->errs); + + zr->nm = 0; + + zr->fr = fopen (filename, "r"); + if (NULL == zr->fr) + { + ds_put_cstr (zr->errs, strerror (errno)); + free (zr); + return NULL; + } + + if ( ! check_magic (zr->fr, MAGIC_LHDR, zr->errs)) + { + fclose (zr->fr); + free (zr); + return NULL; + } + + if ( ! find_eocd (zr->fr, &offset)) + { + ds_put_format (zr->errs, _("Cannot find central directory")); + fclose (zr->fr); + free (zr); + return NULL; + } + + if ( 0 != fseeko (zr->fr, offset, SEEK_SET)) + { + const char *mm = strerror (errno); + ds_put_format (zr->errs, _("Failed to seek to end of central directory record: %s"), mm); + fclose (zr->fr); + free (zr); + return NULL; + } + + + if ( ! check_magic (zr->fr, MAGIC_EOCD, zr->errs)) + { + fclose (zr->fr); + free (zr); + return NULL; + } + + get_u16 (zr->fr, &disknum); + get_u16 (zr->fr, &disknum); + + get_u16 (zr->fr, &zr->n_members); + get_u16 (zr->fr, &total_members); + + get_u32 (zr->fr, ¢ral_dir_length); + get_u32 (zr->fr, ¢ral_dir_start); + + if ( 0 != fseeko (zr->fr, central_dir_start, SEEK_SET)) + { + const char *mm = strerror (errno); + ds_put_format (zr->errs, _("Failed to seek to central directory: %s"), mm); + fclose (zr->fr); + free (zr); + return NULL; + } + + zr->members = calloc (zr->n_members, sizeof (*zr->members)); + + zr->filename = strdup (filename); + + return zr; +} + + + +/* Return the member called MEMBER from the reader ZR */ +struct zip_member * +zip_member_open (struct zip_reader *zr, const char *member) +{ + uint16_t v, nlen, extra_len; + uint16_t gp, comp_type, time, date; + uint32_t ucomp_size, comp_size; + + uint32_t crc; + + char *name = NULL; + + int i; + struct zip_member *zm = NULL; + + if ( zr == NULL) + return NULL; + + for (i = 0 ; i < zr->n_members; ++i) + { + zm = zr->members[i] = zip_header_read_next (zr); + if (zm && 0 == strcmp (zm->name, member)) + { + break; + } + else + { + zm = NULL; + } + } + + if ( zm == NULL) + return NULL; + + if ( 0 != fseeko (zm->fp, zm->offset, SEEK_SET)) + { + const char *mm = strerror (errno); + ds_put_format (zm->errs, _("Failed to seek to start of member `%s': %s"), zm->name, mm); + return NULL; + } + + if ( ! check_magic (zm->fp, MAGIC_LHDR, zr->errs)) + { + return NULL; + } + + get_u16 (zm->fp, &v); + get_u16 (zm->fp, &gp); + get_u16 (zm->fp, &comp_type); + zm->compression = comp_code (zm, comp_type); + get_u16 (zm->fp, &time); + get_u16 (zm->fp, &date); + get_u32 (zm->fp, &crc); + get_u32 (zm->fp, &comp_size); + + get_u32 (zm->fp, &ucomp_size); + get_u16 (zm->fp, &nlen); + get_u16 (zm->fp, &extra_len); + + name = calloc (nlen + 1, sizeof (char)); + + get_bytes (zm->fp, name, nlen); + + skip_bytes (zm->fp, extra_len); + + if (strcmp (name, zm->name) != 0) + { + ds_put_format (zm->errs, + _("Name mismatch in zip archive. Central directory says `%s'; local file header says `%s'"), + zm->name, name); + free (name); + free (zm); + return NULL; + } + + free (name); + + zm->bytes_unread = zm->ucomp_size; + + if ( ! decompressors[zm->compression].init (zm) ) + return NULL; + + return zm; +} + +void +zip_member_ref (struct zip_member *zm) +{ + zm->ref_cnt++; +} + + + + +void +zip_member_unref (struct zip_member *zm) +{ + if ( zm == NULL) + return; + + if (--zm->ref_cnt == 0) + { + decompressors[zm->compression].finish (zm); + if (zm->fp) + fclose (zm->fp); + free (zm->name); + free (zm); + } +} + + + + +static bool probe_magic (FILE *fp, uint32_t magic, off_t start, off_t stop, off_t *off); + + +/* Search for something that looks like the End Of Central Directory in FP. + If found, the offset of the record will be placed in OFF. + Returns true if found false otherwise. +*/ +static bool +find_eocd (FILE *fp, off_t *off) +{ + off_t start, stop; + const uint32_t magic = MAGIC_EOCD; + bool found = false; + + /* The magic cannot be more than 22 bytes from the end of the file, + because that is the minimum length of the EndOfCentralDirectory + record. + */ + if ( 0 > fseeko (fp, -22, SEEK_END)) + { + return false; + } + start = ftello (fp); + stop = start + sizeof (magic); + do + { + found = probe_magic (fp, magic, start, stop, off); + /* FIXME: For extra confidence lookup the directory start record here*/ + if ( start == 0) + break; + stop = start + sizeof (magic); + start >>= 1; + } + while (!found ); + + return found; +} + + +/* + Search FP for MAGIC starting at START and reaching until STOP. + Returns true iff MAGIC is found. False otherwise. + OFF receives the location of the magic. +*/ +static bool +probe_magic (FILE *fp, uint32_t magic, off_t start, off_t stop, off_t *off) +{ + int i; + int state = 0; + unsigned char seq[4]; + unsigned char byte; + + if ( 0 > fseeko (fp, start, SEEK_SET)) + { + return -1; + } + + for (i = 0; i < 4 ; ++i) + { + seq[i] = (magic >> i * 8) & 0xFF; + } + + do + { + fread (&byte, 1, 1, fp); + + if ( byte == seq[state]) + state++; + else + state = 0; + + if ( state == 4) + { + *off = ftello (fp) - 4; + return true; + } + start++; + if ( start >= stop) + break; + } + while (!feof (fp)); + + return false; +} + diff --git a/src/libpspp/zip-reader.h b/src/libpspp/zip-reader.h new file mode 100644 index 0000000000..966bd5990f --- /dev/null +++ b/src/libpspp/zip-reader.h @@ -0,0 +1,87 @@ +/* PSPP - a program for statistical analysis. + Copyright (C) 2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + + +#ifndef ZIP_READER_H +#define ZIP_READER_H 1 + +#include + +struct zip_reader; +struct string; + +enum compression + { + COMPRESSION_STORED = 0, +#if HAVE_ZLIB_H + COMPRESSION_INFLATE, +#endif + n_COMPRESSION + }; + +struct zip_member +{ + FILE *fp; /* The stream from which the data is read */ + uint32_t offset; /* Starting offset in file. */ + uint32_t comp_size; /* Length of member file data, in bytes. */ + uint32_t ucomp_size; /* Uncompressed length of member file data, in bytes. */ + uint32_t expected_crc; /* CRC-32 of member file data.. */ + char *name; /* Name of member file. */ + uint32_t crc; + enum compression compression; + + size_t bytes_unread; /* Number of bytes left in the member available for reading */ + int ref_cnt; + struct string *errs; + void *aux; +}; + +struct decompressor +{ + bool (*init) (struct zip_member *); + int (*read) (struct zip_member *, void *, size_t); + void (*finish) (struct zip_member *); +}; + + +void zm_dump (const struct zip_member *zm); + +/* Create zip reader to read the file called FILENAME. + If ERRS is non-null if will be used to contain any error messages + which the reader wishes to report. + */ +struct zip_reader *zip_reader_create (const char *filename, struct string *errs); + +/* Destroy the zip reader */ +void zip_reader_destroy (struct zip_reader *zr); + +/* Return the zip member in the reader ZR, called MEMBER */ +struct zip_member *zip_member_open (struct zip_reader *zr, const char *member); + +/* Read upto N bytes from ZM, storing them in BUF. + Returns the number of bytes read, or -1 on error */ +int zip_member_read (struct zip_member *zm, void *buf, size_t n); + +/* Unref (and possibly destroy) the zip member ZM */ +void zip_member_unref (struct zip_member *zm); + +/* Ref the zip member */ +void zip_member_ref (struct zip_member *zm); + +void zip_member_finish (struct zip_member *zm); + + +#endif diff --git a/src/libpspp/zip-writer.c b/src/libpspp/zip-writer.c index 7e1a6ccc17..e93ec887e8 100644 --- a/src/libpspp/zip-writer.c +++ b/src/libpspp/zip-writer.c @@ -17,6 +17,7 @@ #include #include "libpspp/zip-writer.h" +#include "libpspp/zip-private.h" #include #include @@ -120,7 +121,7 @@ zip_writer_add (struct zip_writer *zw, FILE *file, const char *member_name) /* Local file header. */ offset = ftello (zw->file); - put_u32 (zw, 0x04034b50); /* local file header signature */ + put_u32 (zw, MAGIC_LHDR); /* local file header signature */ put_u16 (zw, 10); /* version needed to extract */ put_u16 (zw, 1 << 3); /* general purpose bit flag */ put_u16 (zw, 0); /* compression method */ @@ -144,7 +145,7 @@ zip_writer_add (struct zip_writer *zw, FILE *file, const char *member_name) } /* Data descriptor. */ - put_u32 (zw, 0x08074b50); + put_u32 (zw, MAGIC_DDHD); put_u32 (zw, crc); put_u32 (zw, size); put_u32 (zw, size); @@ -179,7 +180,7 @@ zip_writer_close (struct zip_writer *zw) struct zip_member *m = &zw->members[i]; /* Central directory file header. */ - put_u32 (zw, 0x02014b50); /* central file header signature */ + put_u32 (zw, MAGIC_SOCD); /* central file header signature */ put_u16 (zw, 63); /* version made by */ put_u16 (zw, 10); /* version needed to extract */ put_u16 (zw, 1 << 3); /* general purpose bit flag */ @@ -203,7 +204,7 @@ zip_writer_close (struct zip_writer *zw) dir_end = ftello (zw->file); /* End of central directory record. */ - put_u32 (zw, 0x06054b50); /* end of central dir signature */ + put_u32 (zw, MAGIC_EOCD); /* end of central dir signature */ put_u16 (zw, 0); /* number of this disk */ put_u16 (zw, 0); /* number of the disk with the start of the central directory */ diff --git a/tests/automake.mk b/tests/automake.mk index c6c98f6dc7..7d4afef707 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -27,6 +27,7 @@ check_PROGRAMS += \ tests/libpspp/stringi-set-test \ tests/libpspp/tower-test \ tests/libpspp/u8-istream-test \ + tests/libpspp/zip-test \ tests/output/render-test check-programs: $(check_PROGRAMS) @@ -213,6 +214,18 @@ tests_language_lexer_segment_test_SOURCES = \ tests/language/lexer/segment-test.c tests_language_lexer_segment_test_CFLAGS = $(AM_CFLAGS) +check_PROGRAMS += tests/libpspp/zip-test +tests_libpspp_zip_test_SOURCES = \ + src/libpspp/str.c \ + src/libpspp/pool.c \ + src/libpspp/temp-file.c \ + src/libpspp/inflate.c \ + src/libpspp/zip-reader.c \ + src/libpspp/zip-writer.c \ + tests/libpspp/zip-test.c +tests_libpspp_zip_test_CFLAGS = $(AM_CFLAGS) + + check_PROGRAMS += tests/output/render-test tests_output_render_test_SOURCES = tests/output/render-test.c tests_output_render_test_LDADD = \ @@ -349,6 +362,7 @@ TESTSUITE_AT = \ tests/libpspp/stringi-set.at \ tests/libpspp/tower.at \ tests/libpspp/u8-istream.at \ + tests/libpspp/zip.at \ tests/math/moments.at \ tests/math/randist.at \ tests/output/ascii.at \ diff --git a/tests/libpspp/zip-test.c b/tests/libpspp/zip-test.c new file mode 100644 index 0000000000..7a2fcb10c4 --- /dev/null +++ b/tests/libpspp/zip-test.c @@ -0,0 +1,95 @@ +/* PSPP - a program for statistical analysis. + Copyright (C) 2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + + +/* A simple program to zip or unzip a file */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include "libpspp/assertion.h" +#include +#include +#include +#include + +#include "xalloc.h" + +/* Exit with a failure code. + (Place a breakpoint on this function while debugging.) */ +static void +check_die (void) +{ + exit (EXIT_FAILURE); +} + +int +main (int argc, char **argv) +{ + if ( argc < 4) + { + fprintf (stderr, "Usage zip-test: {r|w} archive file0 file1 ... filen\n"); + check_die (); + } + + if ( 0 == strcmp ("w", argv[1])) + { + int i; + struct zip_writer *zw = zip_writer_create (argv[2]); + for (i = 3; i < argc; ++i) + { + FILE *fp = fopen (argv[i], "r"); + if (!fp ) check_die (); + zip_writer_add (zw, fp, argv[i]); + } + zip_writer_close (zw); + } + else if ( 0 == strcmp ("r", argv[1])) + { + const int BUFSIZE=256; + char buf[BUFSIZE]; + int i; + struct string str; + struct zip_reader *zr = zip_reader_create (argv[2], &str); + for (i = 3; i < argc; ++i) + { + int x = 0; + struct zip_member *zm = zip_member_open (zr, argv[i]); + FILE *fp = fopen (argv[i], "w"); + + while ((x = zip_member_read (zm, buf, BUFSIZE)) > 0) + { + fwrite (buf, x, 1, fp); + } + fclose (fp); + if ( x < 0) + { + fprintf (stderr, "Unzip failed: %s", ds_cstr (&str)); + check_die (); + } + + zip_member_unref (zm); + } + zip_reader_destroy (zr); + } + else + exit (1); + + return 0; +} diff --git a/tests/libpspp/zip.at b/tests/libpspp/zip.at new file mode 100644 index 0000000000..a725049e52 --- /dev/null +++ b/tests/libpspp/zip.at @@ -0,0 +1,43 @@ +AT_BANNER([zip]) + +AT_SETUP([Basic zip - unzip test]) +AT_KEYWORDS([compression]) + +AT_CHECK([dnl +here=`pwd` +dir1=$here/original +dir2=$here/recovered + +mkdir -p $dir1 + +# Generate files of differing sizes with random data in them +names="" +s=1; +while test $s -le 8192 ; do + name=`mktemp -p $dir1`; + dd if=/dev/urandom of=$name count=1 bs=$s 2> /dev/null + s=$(($s * 2)); + bn=`basename $name`; + names="$names $bn"; +done + +(cd $dir1 && $abs_top_builddir/tests/libpspp/zip-test w foo.zip $names) + + +mkdir -p $dir2 +cp $dir1/foo.zip $dir2 +cd $dir2 +$abs_top_builddir/tests/libpspp/zip-test r foo.zip $names + +# Compare the files to their originals +for f in $names; do + diff $dir1/$f $dir2/$f; + if test $? -ne 0 ; then exit 1; fi; +done + +exit 0 +]) + + +AT_CLEANUP +