X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flibpspp%2Fzip-writer.c;h=fd36fc523a029b61e0dee5122ef330bbb8c4a4ed;hb=d1d0556e538bf04cb0b991b7acb205c144a2f826;hp=534e7ebab5734f1a4b572bb7c1abcc24de3af29a;hpb=9530bce28d817a0d106d34144289ba2b12d04c19;p=pspp diff --git a/src/libpspp/zip-writer.c b/src/libpspp/zip-writer.c index 534e7ebab5..fd36fc523a 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 @@ -17,18 +17,19 @@ #include #include "libpspp/zip-writer.h" +#include "libpspp/zip-private.h" +#include #include #include #include -#include "libpspp/integer-format.h" - #include "gl/crc.h" -#include "gl/error.h" #include "gl/fwriteerror.h" #include "gl/xalloc.h" +#include "libpspp/message.h" + #include "gettext.h" #define _(msgid) gettext (msgid) @@ -39,6 +40,8 @@ struct zip_writer uint16_t date, time; /* Date and time in MS-DOS format. */ + bool ok; + /* 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; @@ -62,16 +65,18 @@ put_bytes (struct zip_writer *zw, const void *p, size_t 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); } @@ -88,7 +93,7 @@ zip_writer_create (const char *file_name) file = fopen (file_name, "wb"); if (file == NULL) { - error (0, errno, _("%s: error opening output file"), file_name); + msg_error (errno, _("%s: error opening output file"), file_name); return NULL; } @@ -96,6 +101,8 @@ zip_writer_create (const char *file_name) zw->file_name = xstrdup (file_name); zw->file = file; + zw->ok = true; + now = time (NULL); tm = localtime (&now); zw->date = tm->tm_mday + ((tm->tm_mon + 1) << 5) + ((tm->tm_year - 80) << 9); @@ -108,6 +115,24 @@ zip_writer_create (const char *file_name) return zw; } +static void +put_local_header (struct zip_writer *zw, const char *member_name, uint32_t crc, + uint32_t size, int flag) +{ + put_u32 (zw, MAGIC_LHDR); /* local file header signature */ + put_u16 (zw, 10); /* version needed to extract */ + 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, 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)); +} + /* 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) @@ -119,23 +144,12 @@ zip_writer_add (struct zip_writer *zw, FILE *file, const char *member_name) char buf[4096]; /* Local file header. */ - offset = ftell (zw->file); - put_u32 (zw, 0x04034b50); /* 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 */ - 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_u16 (zw, strlen (member_name)); /* file name length */ - put_u16 (zw, 0); /* extra field length */ - put_bytes (zw, member_name, strlen (member_name)); + offset = ftello (zw->file); + put_local_header (zw, member_name, 0, 0, 1 << 3); /* File data. */ size = crc = 0; - fseek (file, 0, SEEK_SET); + fseeko (file, 0, SEEK_SET); while ((bytes_read = fread (buf, 1, sizeof buf, file)) > 0) { put_bytes (zw, buf, bytes_read); @@ -143,11 +157,25 @@ zip_writer_add (struct zip_writer *zw, FILE *file, const char *member_name) crc = crc32_update (crc, buf, bytes_read); } - /* Data descriptor. */ - put_u32 (zw, 0x08074b50); - put_u32 (zw, crc); - put_u32 (zw, size); - put_u32 (zw, size); + /* 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, offset, SEEK_SET) == 0) + { + put_local_header (zw, member_name, crc, size, 0); + if (fseeko (zw->file, size, SEEK_CUR) + && zw->ok) + { + msg_error (errno, _("%s: error seeking in output file"), zw->file_name); + zw->ok = false; + } + } + else + { + 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) @@ -173,13 +201,13 @@ zip_writer_close (struct zip_writer *zw) if (zw == NULL) return true; - dir_start = ftell (zw->file); + dir_start = ftello (zw->file); for (i = 0; i < zw->n_members; i++) { 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 */ @@ -200,10 +228,10 @@ zip_writer_close (struct zip_writer *zw) free (m->name); } free (zw->members); - dir_end = ftell (zw->file); + 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 */ @@ -217,11 +245,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 && fwriteerror (zw->file)) { - error (0, errno, _("%s: write failed"), zw->file_name); + msg_error (errno, _("%s: write failed"), zw->file_name); ok = false; }