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 \
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
--- /dev/null
+/* 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 <http://www.gnu.org/licenses/>. */
+
+
+#include <config.h>
+
+#include "inflate.h"
+
+#if HAVE_ZLIB_H
+
+#include <xalloc.h>
+#include <zlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#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
--- /dev/null
+/* 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 <http://www.gnu.org/licenses/>. */
+
+#ifndef INFLATE_H
+#define INFLATE_H 1
+
+#include <stddef.h>
+#include <stdbool.h>
+
+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
--- /dev/null
+/* 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 <http://www.gnu.org/licenses/>. */
+
+#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
+
--- /dev/null
+/* 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 <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <errno.h>
+#include <xalloc.h>
+#include <libpspp/assertion.h>
+
+#include <crc.h>
+
+#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);
+ }
+}
+
+
+\f
+
+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;
+}
+
--- /dev/null
+/* 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 <http://www.gnu.org/licenses/>. */
+
+
+#ifndef ZIP_READER_H
+#define ZIP_READER_H 1
+
+#include <inttypes.h>
+
+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
#include <config.h>
#include "libpspp/zip-writer.h"
+#include "libpspp/zip-private.h"
#include <errno.h>
#include <stdlib.h>
/* 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 */
}
/* Data descriptor. */
- put_u32 (zw, 0x08074b50);
+ put_u32 (zw, MAGIC_DDHD);
put_u32 (zw, crc);
put_u32 (zw, size);
put_u32 (zw, size);
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 */
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 */
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)
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 = \
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 \
--- /dev/null
+/* 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 <http://www.gnu.org/licenses/>. */
+
+
+/* A simple program to zip or unzip a file */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include "libpspp/assertion.h"
+#include <libpspp/compiler.h>
+#include <libpspp/zip-writer.h>
+#include <libpspp/zip-reader.h>
+#include <libpspp/str.h>
+
+#include "xalloc.h"
+\f
+/* 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;
+}
--- /dev/null
+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
+