X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flibpspp%2Fzip-writer.c;h=69244cc84e4b9b9aca8d4cb907196ad131e004b0;hb=bc5c6c1953ada1737620e27e6a968392a38d8c8f;hp=e93ec887e8be952e08a3db28cd79c4b139ec8249;hpb=551202adbc334f9c33f8290a4e407b422651bbd5;p=pspp diff --git a/src/libpspp/zip-writer.c b/src/libpspp/zip-writer.c index e93ec887e8..69244cc84e 100644 --- a/src/libpspp/zip-writer.c +++ b/src/libpspp/zip-writer.c @@ -1,5 +1,5 @@ /* PSPP - a program for statistical analysis. - Copyright (C) 2010 Free Software Foundation, Inc. + Copyright (C) 2010, 2012, 2014 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 @@ -19,17 +19,20 @@ #include "libpspp/zip-writer.h" #include "libpspp/zip-private.h" +#include +#include #include #include #include - -#include "libpspp/integer-format.h" +#include #include "gl/crc.h" -#include "gl/error.h" #include "gl/fwriteerror.h" #include "gl/xalloc.h" +#include "libpspp/message.h" +#include "libpspp/temp-file.h" + #include "gettext.h" #define _(msgid) gettext (msgid) @@ -37,9 +40,18 @@ struct zip_writer { char *file_name; /* File name, for use in error mesages. */ FILE *file; /* Output stream. */ + uint32_t offset; /* Offset in output stream. */ uint16_t date, time; /* Date and time in MS-DOS format. */ + bool ok; + + /* Member being added to the file. */ + char *m_name; + uint32_t m_start; + uint32_t m_size; + uint32_t m_crc; + /* Members already added to the file, so that we can summarize them to the central directory at the end of the ZIP file. */ struct zip_member *members; @@ -58,21 +70,24 @@ static void put_bytes (struct zip_writer *zw, const void *p, size_t n) { fwrite (p, 1, n, zw->file); + zw->offset += n; } static void put_u16 (struct zip_writer *zw, uint16_t x) { - if (INTEGER_NATIVE != INTEGER_LSB_FIRST) - integer_convert (INTEGER_NATIVE, &x, INTEGER_MSB_FIRST, &x, sizeof x); +#ifdef WORDS_BIGENDIAN + x = bswap_16 (x); +#endif put_bytes (zw, &x, sizeof x); } static void put_u32 (struct zip_writer *zw, uint32_t x) { - if (INTEGER_NATIVE != INTEGER_LSB_FIRST) - integer_convert (INTEGER_NATIVE, &x, INTEGER_MSB_FIRST, &x, sizeof x); +#ifdef WORDS_BIGENDIAN + x = bswap_32 (x); +#endif put_bytes (zw, &x, sizeof x); } @@ -81,84 +96,163 @@ put_u32 (struct zip_writer *zw, uint32_t x) struct zip_writer * zip_writer_create (const char *file_name) { - struct zip_writer *zw; - struct tm *tm; - time_t now; FILE *file; - - file = fopen (file_name, "wb"); - if (file == NULL) + if (strcmp (file_name, "-")) { - error (0, errno, _("%s: error opening output file"), file_name); - return NULL; + file = fopen (file_name, "wb"); + if (file == NULL) + { + msg_error (errno, _("%s: error opening output file"), file_name); + return NULL; + } } + else + { + if (isatty (STDOUT_FILENO)) + { + msg (ME, _("%s: not writing ZIP file to terminal"), file_name); + return NULL; + } - zw = xmalloc (sizeof *zw); - zw->file_name = xstrdup (file_name); - zw->file = file; - - now = time (NULL); - tm = localtime (&now); - zw->date = tm->tm_mday + ((tm->tm_mon + 1) << 5) + ((tm->tm_year - 80) << 9); - zw->time = tm->tm_sec / 2 + (tm->tm_min << 5) + (tm->tm_hour << 11); + file = stdout; + } - zw->members = NULL; - zw->n_members = 0; - zw->allocated_members = 0; + time_t now = time (NULL); + struct tm *tm = localtime (&now); + struct zip_writer *zw = xmalloc (sizeof *zw); + *zw = (struct zip_writer) { + .file_name = xstrdup (file_name), + .file = file, + .ok = true, + .date = tm->tm_mday + ((tm->tm_mon + 1) << 5) + ((tm->tm_year - 80) << 9), + .time = tm->tm_sec / 2 + (tm->tm_min << 5) + (tm->tm_hour << 11), + }; return zw; } -/* Adds the contents of FILE, with name MEMBER_NAME, to ZW. */ -void -zip_writer_add (struct zip_writer *zw, FILE *file, const char *member_name) +static void +put_local_header (struct zip_writer *zw, const char *member_name, uint32_t crc, + uint32_t size, int flag) { - struct zip_member *member; - uint32_t offset, size; - size_t bytes_read; - uint32_t crc; - char buf[4096]; - - /* Local file header. */ - offset = ftello (zw->file); 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, flag); /* general purpose bit flag */ put_u16 (zw, 0); /* compression method */ put_u16 (zw, zw->time); /* last mod file time */ put_u16 (zw, zw->date); /* last mod file date */ - put_u32 (zw, 0); /* crc-32 */ - put_u32 (zw, 0); /* compressed size */ - put_u32 (zw, 0); /* uncompressed size */ + put_u32 (zw, crc); /* crc-32 */ + put_u32 (zw, size); /* compressed size */ + put_u32 (zw, size); /* uncompressed size */ put_u16 (zw, strlen (member_name)); /* file name length */ put_u16 (zw, 0); /* extra field length */ put_bytes (zw, member_name, strlen (member_name)); +} - /* File data. */ - size = crc = 0; - fseeko (file, 0, SEEK_SET); - while ((bytes_read = fread (buf, 1, sizeof buf, file)) > 0) +/* Start adding a new member, named MEMBER_NAME, to ZW. Add content to the + member by calling zip_writer_add_write() possibly multiple times, then + finish it off with zip_writer_add_finish(). + + Only one member may be open at a time. */ +void +zip_writer_add_start (struct zip_writer *zw, const char *member_name) +{ + assert (!zw->m_name); + zw->m_name = xstrdup (member_name); + zw->m_start = zw->offset; + zw->m_size = 0; + zw->m_crc = 0; + put_local_header (zw, member_name, 0, 0, 1 << 3); +} + +/* Adds the N bytes in BUF to the currently open member in ZW. */ +void +zip_writer_add_write (struct zip_writer *zw, const void *buf, size_t n) +{ + assert (zw->m_name); + put_bytes (zw, buf, n); + zw->m_size += n; + zw->m_crc = crc32_update (zw->m_crc, buf, n); +} + +/* Finishes writing the currently open member in ZW. */ +void +zip_writer_add_finish (struct zip_writer *zw) +{ + assert (zw->m_name); + + /* Try to seek back to the local file header. If successful, overwrite it + with the correct file size and CRC. Otherwise, write data descriptor. */ + if (fseeko (zw->file, zw->m_start, SEEK_SET) == 0) { - put_bytes (zw, buf, bytes_read); - size += bytes_read; - crc = crc32_update (crc, buf, bytes_read); + uint32_t save_offset = zw->offset; + put_local_header (zw, zw->m_name, zw->m_crc, zw->m_size, 0); + if (fseeko (zw->file, zw->m_size, SEEK_CUR) + && zw->ok) + { + msg_error (errno, _("%s: error seeking in output file"), zw->file_name); + zw->ok = false; + } + zw->offset = save_offset; + } + else + { + put_u32 (zw, MAGIC_DDHD); + put_u32 (zw, zw->m_crc); + put_u32 (zw, zw->m_size); + put_u32 (zw, zw->m_size); } - - /* Data descriptor. */ - put_u32 (zw, MAGIC_DDHD); - put_u32 (zw, crc); - put_u32 (zw, size); - put_u32 (zw, size); /* Add to set of members. */ if (zw->n_members >= zw->allocated_members) zw->members = x2nrealloc (zw->members, &zw->allocated_members, sizeof *zw->members); - member = &zw->members[zw->n_members++]; - member->offset = offset; - member->size = size; - member->crc = crc; - member->name = xstrdup (member_name); + struct zip_member *member = &zw->members[zw->n_members++]; + member->offset = zw->m_start; + member->size = zw->m_size; + member->crc = zw->m_crc; + member->name = zw->m_name; + + zw->m_name = NULL; + zw->m_start = zw->m_size = zw->m_crc = 0; +} + +/* Adds the contents of FILE, with name MEMBER_NAME, to ZW. */ +void +zip_writer_add (struct zip_writer *zw, FILE *file, const char *member_name) +{ + zip_writer_add_start (zw, member_name); + + fseeko (file, 0, SEEK_SET); + for (;;) + { + char buf[4096]; + size_t n = fread (buf, 1, sizeof buf, file); + if (!n) + break; + zip_writer_add_write (zw, buf, n); + } + zip_writer_add_finish (zw); +} + +/* Adds a member named MEMBER_NAME whose contents is the null-terminated string + CONTENT. */ +void +zip_writer_add_string (struct zip_writer *zw, const char *member_name, + const char *content) +{ + zip_writer_add_memory (zw, member_name, content, strlen (content)); +} + +/* Adds a member named MEMBER_NAME whose contents is the SIZE bytes of + CONTENT. */ +void +zip_writer_add_memory (struct zip_writer *zw, const char *member_name, + const void *content, size_t size) +{ + zip_writer_add_start (zw, member_name); + zip_writer_add_write (zw, content, size); + zip_writer_add_finish (zw); } /* Finalizes the contents of ZW and closes it. Returns true if successful, @@ -174,7 +268,9 @@ zip_writer_close (struct zip_writer *zw) if (zw == NULL) return true; - dir_start = ftello (zw->file); + assert (!zw->m_name); + + dir_start = zw->offset; for (i = 0; i < zw->n_members; i++) { struct zip_member *m = &zw->members[i]; @@ -201,7 +297,7 @@ zip_writer_close (struct zip_writer *zw) free (m->name); } free (zw->members); - dir_end = ftello (zw->file); + dir_end = zw->offset; /* End of central directory record. */ put_u32 (zw, MAGIC_EOCD); /* end of central dir signature */ @@ -218,11 +314,10 @@ zip_writer_close (struct zip_writer *zw) the starting disk number */ put_u16 (zw, 0); /* .ZIP file comment length */ - if (!fwriteerror (zw->file)) - ok = true; - else + ok = zw->ok; + if (ok && zw->file != stdout && fwriteerror (zw->file)) { - error (0, errno, _("%s: write failed"), zw->file_name); + msg_error (errno, _("%s: write failed"), zw->file_name); ok = false; }