From: Ben Pfaff Date: Sun, 25 Oct 2020 05:29:08 +0000 (-0700) Subject: zip-writer: Fix writing a zip file to a pipe. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5692254f966863caef595ec6270f966c063b1b08;p=pspp zip-writer: Fix writing a zip file to a pipe. The code here had comment indicating that it could write a zip file to a pipe, but it didn't actually work. This fixes it and adds a test. --- diff --git a/src/libpspp/zip-writer.c b/src/libpspp/zip-writer.c index 0959551abe..b8240efe6b 100644 --- a/src/libpspp/zip-writer.c +++ b/src/libpspp/zip-writer.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "gl/crc.h" #include "gl/fwriteerror.h" @@ -38,6 +39,7 @@ 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. */ @@ -61,6 +63,7 @@ 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 @@ -91,16 +94,30 @@ zip_writer_create (const char *file_name) time_t now; FILE *file; - file = fopen (file_name, "wb"); - if (file == NULL) + if (strcmp (file_name, "-")) { - msg_error (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; + } + + file = stdout; } zw = xmalloc (sizeof *zw); zw->file_name = xstrdup (file_name); zw->file = file; + zw->offset = 0; zw->ok = true; @@ -139,13 +156,13 @@ void zip_writer_add (struct zip_writer *zw, FILE *file, const char *member_name) { struct zip_member *member; - uint32_t offset, size; + uint32_t size; size_t bytes_read; uint32_t crc; char buf[4096]; /* Local file header. */ - offset = ftello (zw->file); + uint32_t offset = zw->offset; put_local_header (zw, member_name, 0, 0, 1 << 3); /* File data. */ @@ -162,6 +179,7 @@ zip_writer_add (struct zip_writer *zw, FILE *file, const char *member_name) with the correct file size and CRC. Otherwise, write data descriptor. */ if (fseeko (zw->file, offset, SEEK_SET) == 0) { + uint32_t save_offset = zw->offset; put_local_header (zw, member_name, crc, size, 0); if (fseeko (zw->file, size, SEEK_CUR) && zw->ok) @@ -169,6 +187,7 @@ zip_writer_add (struct zip_writer *zw, FILE *file, const char *member_name) msg_error (errno, _("%s: error seeking in output file"), zw->file_name); zw->ok = false; } + zw->offset = save_offset; } else { @@ -229,7 +248,7 @@ zip_writer_close (struct zip_writer *zw) if (zw == NULL) return true; - dir_start = ftello (zw->file); + dir_start = zw->offset; for (i = 0; i < zw->n_members; i++) { struct zip_member *m = &zw->members[i]; @@ -256,7 +275,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 */ @@ -274,7 +293,7 @@ zip_writer_close (struct zip_writer *zw) put_u16 (zw, 0); /* .ZIP file comment length */ ok = zw->ok; - if (ok && fwriteerror (zw->file)) + if (ok && zw->file != stdout && fwriteerror (zw->file)) { msg_error (errno, _("%s: write failed"), zw->file_name); ok = false; diff --git a/tests/libpspp/zip.at b/tests/libpspp/zip.at index 9598717f82..77ca00e1c6 100644 --- a/tests/libpspp/zip.at +++ b/tests/libpspp/zip.at @@ -16,10 +16,8 @@ dnl along with this program. If not, see . dnl AT_BANNER([zip]) -AT_SETUP([Basic zip - unzip test]) +AT_SETUP([basic zip - unzip test]) AT_KEYWORDS([compression]) - -AT_CHECK([dnl here=`pwd` dir1=$here/original dir2=$here/recovered @@ -37,23 +35,68 @@ while test $s -le 8192 ; do names="$names $bn"; done -(cd $dir1 && zip-test w foo.zip $names) +AT_CHECK([cd $dir1 && zip-test w foo.zip $names]) +# If zipinfo is installed, make sure it can read the zipfile. +if (zipinfo -v) >/dev/null 2>&1; then + AT_CHECK([zipinfo $dir1/foo.zip], [0], [ignore]) +fi mkdir -p $dir2 cp $dir1/foo.zip $dir2 cd $dir2 -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 +AT_CHECK([zip-test r foo.zip $names]) -exit 0 +AT_CHECK([ + # 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 ]) +AT_CLEANUP -AT_CLEANUP +AT_SETUP([zip to pipe]) +AT_KEYWORDS([compression]) +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=$dir1/$s + dd if=/dev/urandom of=$name count=1 bs=$s 2> /dev/null + s=$(($s * 2)); + bn=`basename $name`; + names="$names $bn"; +done + +# The pipe through "cat" below is essential because it makes the +# output file un-seekable. +AT_CHECK([cd $dir1 && zip-test w - $names | cat > foo.zip]) + +# If zipinfo is installed, make sure it can read the zipfile. +if (zipinfo -v) >/dev/null 2>&1; then + AT_CHECK([zipinfo $dir1/foo.zip], [0], [ignore]) +fi +mkdir -p $dir2 +cp $dir1/foo.zip $dir2 +cd $dir2 + +AT_CHECK([zip-test r foo.zip $names]) + +AT_CHECK([ + # 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 +]) +AT_CLEANUP