]> git.wh0rd.org Git - elf2flt.git/commitdiff
This patch allows elf2flt/flthdr's compression options to work in a wider
authorDavid McCullough <davidm@snapgear.com>
Wed, 27 Feb 2008 11:41:32 +0000 (11:41 +0000)
committerDavid McCullough <davidm@snapgear.com>
Wed, 27 Feb 2008 11:41:32 +0000 (11:41 +0000)
variety of environments (e.g. under MinGW/Win32), by linking with zlib
rather than by using external gzip/gunzip executables. The cp binary
isn't used any more either, and libiberty's make_temp_file() is used
instead of mkstemp() as a more portable way of creating a temporary file.

Also the compression logic is made somewhat clearer, IMO.

Signed-off-by: Julian Brown <julian@codesourcery.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Makefile.in
compress.c [new file with mode: 0644]
compress.h [new file with mode: 0644]
configure.in
elf2flt.c
flthdr.c

index 2d225d47ccedcb0892538eabd483851d1fef12cb..f041890ee94d79b2f761f175fa7f8078d104e170 100644 (file)
@@ -11,7 +11,7 @@ CC = @CC@
 CPU = @target_cpu@
 TARGET = @target_alias@
 CFLAGS = @CFLAGS@
-INCLUDES = @bfd_include_dir@ @binutils_include_dir@
+INCLUDES = @bfd_include_dir@ @binutils_include_dir@ @zlib_include_dir@
 CPPFLAGS = @CPPFLAGS@
 LDFLAGS = @LDFLAGS@
 LIBS = @LIBS@
@@ -50,11 +50,14 @@ PROGS = $(PROG_ELF2FLT) $(PROG_FLTHDR)
 
 all: $(PROGS)
 
-$(PROG_ELF2FLT): elf2flt.c stubs.c Makefile
-       $(CC) $(CCFLAGS) $(LDFLAGS) -o $@ $(srcdir)/elf2flt.c $(srcdir)/stubs.c $(LIBS)
+$(PROG_ELF2FLT): $(srcdir)/elf2flt.c compress.o $(srcdir)/stubs.c
+       $(CC) $(CCFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)
 
-$(PROG_FLTHDR): flthdr.c Makefile
-       $(CC) $(CCFLAGS) $(LDFLAGS) -o $@ $(srcdir)/flthdr.c $(LIBS)
+$(PROG_FLTHDR): $(srcdir)/flthdr.c compress.o
+       $(CC) $(CCFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)
+
+Makefile: $(srcdir)/Makefile.in
+       ./config.status $@
 
 clean:
        -rm -f $(PROGS) *.$(OBJEXT)
diff --git a/compress.c b/compress.c
new file mode 100644 (file)
index 0000000..69abc6a
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Helper functions to handle compression via zlib
+ *
+ * Copyright (C) 2007-2008 Julian Brown
+ * Copyright (C) 2008 Mike Frysinger
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <zlib.h>
+#include "compress.h"
+
+/* Open an (uncompressed) file as a stream.  Return 0 on success, 1 on
+   error.
+   NOTE: The MODE argument must remain valid for the lifetime of the stream,
+   because it is referred to by reopen_stream_compressed() if it is called.
+   String constants work fine.  */
+
+int
+fopen_stream_u(stream *fp, const char *path, const char *mode)
+{
+       fp->u.filep = fopen(path, mode);
+       fp->type = (fp->u.filep) ? UNCOMPRESSED : INVALID;
+       fp->mode = mode;
+       return (fp->u.filep) ? 0 : 1;
+}
+
+/* Read from stream.  Return number of elements read.  */
+
+size_t
+fread_stream(void *ptr, size_t size, size_t nmemb, stream *str)
+{
+       size_t read;
+
+       switch (str->type) {
+               case UNCOMPRESSED:
+               read = fread(ptr, size, nmemb, str->u.filep);
+               break;
+
+               case COMPRESSED:
+               read = gzread(str->u.gzfilep, ptr, size * nmemb) / size;
+               break;
+
+               default:
+               abort();
+       }
+
+       return read;
+}
+
+/* Write to stream.  Return number of elements written.  */
+
+size_t
+fwrite_stream(const void *ptr, size_t size, size_t nmemb, stream *str)
+{
+       size_t written;
+
+       switch (str->type) {
+               case UNCOMPRESSED:
+               written = fwrite(ptr, size, nmemb, str->u.filep);
+               break;
+
+               case COMPRESSED:
+               written = gzwrite(str->u.gzfilep, ptr, size * nmemb) / size;
+               break;
+
+               default:
+               abort();
+       }
+
+       return written;
+}
+
+/* Close stream.  */
+
+int
+fclose_stream(stream *str)
+{
+       switch (str->type) {
+               case UNCOMPRESSED:
+               return fclose(str->u.filep);
+
+               case COMPRESSED:
+               return gzclose(str->u.gzfilep);
+
+               default:
+               abort();
+       }
+
+       return 0;
+}
+
+int
+ferror_stream(stream *str)
+{
+       switch (str->type) {
+               case UNCOMPRESSED:
+               return ferror(str->u.filep);
+
+               case COMPRESSED:
+               {
+                       const char *err;
+                       int errno;
+
+                       err = gzerror(str->u.gzfilep, &errno);
+                       if (errno == Z_OK || errno == Z_STREAM_END)
+                               return 0;
+                       else if (errno == Z_ERRNO)
+                               return 1;
+                       else {
+                               fprintf(stderr, "%s\n", err);
+                               return 1;
+                       }
+               }
+               break;
+
+               default:
+               abort();
+       }
+
+       return 0;
+}
+
+/* Reopen a stream at the current file position.  */
+
+void
+reopen_stream_compressed(stream *str)
+{
+       int fd;
+       long offset, roffset;
+
+       /* Already a compressed stream, return immediately  */
+       if (str->type == COMPRESSED)
+               return;
+
+       if (str->type == INVALID)
+               abort();
+
+       fd = fileno(str->u.filep);
+       /* Get current (buffered) file position.  */
+       offset = ftell(str->u.filep);
+
+       /* Make sure there's nothing left in buffers.  */
+       fflush(str->u.filep);
+
+       /* Reposition underlying FD.  (Might be unnecessary?)  */
+       roffset = lseek(fd, offset, SEEK_SET);
+
+       assert(roffset == offset);
+
+       /* Reopen as compressed stream.  */
+       str->u.gzfilep = gzdopen(fd, str->mode);
+       gzsetparams(str->u.gzfilep, 9, Z_DEFAULT_STRATEGY);
+       str->type = COMPRESSED;
+}
+
+void
+transfer(stream *ifp, stream *ofp, int count)
+{
+       char cmd[1024];
+       int n, num;
+
+       while (count == -1 || count > 0) {
+               if (count == -1 || count > sizeof(cmd))
+                       num = sizeof(cmd);
+               else
+                       num = count;
+               n = fread_stream(cmd, 1, num, ifp);
+               if (n == 0)
+                       break;
+               if (fwrite_stream(cmd, n, 1, ofp) != 1) {
+                       fprintf(stderr, "Write failed :-(\n");
+                       exit(1);
+               }
+               if (count != -1)
+                       count -= n;
+       }
+       if (count > 0) {
+               fprintf(stderr, "Failed to transfer %d bytes\n", count);
+               exit(1);
+       }
+}
diff --git a/compress.h b/compress.h
new file mode 100644 (file)
index 0000000..d27f708
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Helper functions to handle compression via zlib
+ *
+ * Copyright (C) 2007-2008 Julian Brown
+ * Copyright (C) 2008 Mike Frysinger
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __ELF2FLT_COMPRESS_H__
+#define __ELF2FLT_COMPRESS_H__
+
+#include <zlib.h>
+
+typedef enum
+{
+  INVALID,
+  UNCOMPRESSED,
+  COMPRESSED
+} stream_type;
+
+/* Tagged union holding either a regular FILE* handle or a zlib gzFile
+   handle.  */
+typedef struct
+{
+  stream_type type;
+  const char *mode;
+  union
+    {
+      FILE *filep;
+      gzFile gzfilep;
+    } u;
+} stream;
+
+int fopen_stream_u(stream *fp, const char *path, const char *mode);
+size_t fread_stream(void *ptr, size_t size, size_t nmemb, stream *str);
+size_t fwrite_stream(const void *ptr, size_t size, size_t nmemb, stream *str);
+int fclose_stream(stream *str);
+int ferror_stream(stream *str);
+void reopen_stream_compressed(stream *str);
+void transfer(stream *ifp, stream *ofp, int count);
+
+#endif
index d43b50053691990e65b603afa4e450f874f9b446..d6e9d44fed9f95de7a4cead2a08428136a495b4a 100644 (file)
@@ -1,6 +1,12 @@
 dnl Process this file with autoconf to produce a configure script.
 AC_INIT(elf2flt.c)
 
+AC_ARG_WITH(zlib-prefix,
+       [ --with-zlib-prefix=<dir>  path to installed zlib ],
+       [ ac_zlib_prefix=$withval ],
+       [ ac_zlib_prefix=NONE ]
+)
+
 AC_ARG_WITH(libbfd,
        [ --with-libbfd=<file>  path to libbfd.a library to use ],
        [ ac_libbfd=$withval ],
@@ -63,6 +69,11 @@ if test "$ac_libbfd" = "NONE"; then
 else
   LIBS="$ac_libbfd $LIBS"
 fi
+if test "$ac_zlib_prefix" = "NONE"; then
+  AC_CHECK_LIB(z, deflate)
+else
+  LIBS="-L$ac_zlib_prefix/lib -lz $LIBS"
+fi
 
 bfd_include_dir=
 if test "$ac_bfd_include_dir" != "NONE"; then
@@ -74,6 +85,11 @@ if test "$ac_binutils_include_dir" != "NONE"; then
   binutils_include_dir="-I$ac_binutils_include_dir"
 fi
 
+zlib_include_dir=
+if test "$ac_zlib_prefix" != "NONE"; then
+  zlib_include_dir="-I$ac_zlib_prefix/include"
+fi
+
 binutils_ldscript_dir=
 if test "$ac_binutils_ldscript_dir" = "NONE"; then
   ac_binutils_ldscript_dir="\${TOOLDIR}/../${target_alias}/lib"
@@ -126,6 +142,7 @@ AC_SUBST(target_os)
 AC_SUBST(target_vendor)
 AC_SUBST(bfd_include_dir)
 AC_SUBST(binutils_include_dir)
+AC_SUBST(zlib_include_dir)
 AC_SUBST(binutils_ldscript_dir)
 AC_SUBST(got_check)
 AC_SUBST(emit_relocs)
index 552710c360a2960adc69d8c7bb4416a072752611..546305f6b95835a5eade341aca1883800c9ed5d6 100644 (file)
--- a/elf2flt.c
+++ b/elf2flt.c
@@ -74,6 +74,7 @@
 
 /* from uClinux-x.x.x/include/linux */
 #include "flat.h"     /* Binary flat header description                      */
+#include "compress.h"
 
 #ifdef TARGET_e1
 #include <e1.h>
@@ -143,7 +144,7 @@ int verbose = 0;      /* extra output when running */
 int pic_with_got = 0; /* do elf/got processing with PIC code */
 int load_to_ram = 0;  /* instruct loader to allocate everything into RAM */
 int ktrace = 0;       /* instruct loader output kernel trace on load */
-int compress = 0;     /* 1 = compress everything, 2 = compress data only */
+int docompress = 0;   /* 1 = compress everything, 2 = compress data only */
 int use_resolved = 0; /* If true, get the value of symbol references from */
                      /* the program contents, not from the relocation table. */
                      /* In this case, the input ELF file must be already */
@@ -1768,7 +1769,7 @@ static void usage(void)
 
 
 /* Write NUM zeroes to STREAM.  */
-static void write_zeroes (unsigned long num, FILE *stream)
+static void write_zeroes (unsigned long num, stream *stream)
 {
   char zeroes[1024];
   if (num > 0) {
@@ -1776,11 +1777,11 @@ static void write_zeroes (unsigned long num, FILE *stream)
        work for stdio output files.  */
     memset(zeroes, 0x00, 1024);
     while (num > sizeof(zeroes)) {
-      fwrite(zeroes, sizeof(zeroes), 1, stream);
+      fwrite_stream(zeroes, sizeof(zeroes), 1, stream);
       num -= sizeof(zeroes);
     }
     if (num > 0)
-      fwrite(zeroes, num, 1, stream);
+      fwrite_stream(zeroes, num, 1, stream);
   }
 }
 
@@ -1795,8 +1796,7 @@ int main(int argc, char *argv[])
   int opt;
   int i;
   int stack;
-  char  cmd[1024];
-  FILE *gf = NULL;
+  stream gf;
 
   asymbol **symbol_table;
   long number_of_symbols;
@@ -1818,8 +1818,6 @@ int main(int argc, char *argv[])
   
   struct flat_hdr hdr;
 
-  int gf_is_pipe = 0;
-
   program = argv[0];
   progname = argv[0];
   xmalloc_set_program_name(program);
@@ -1853,10 +1851,10 @@ int main(int argc, char *argv[])
       ktrace++;
       break;
     case 'z':
-      compress = 1;
+      docompress = 1;
       break;
     case 'd':
-      compress = 2;
+      docompress = 2;
       break;
     case 'p':
       pfile = optarg;
@@ -2065,7 +2063,7 @@ int main(int argc, char *argv[])
          | (load_to_ram ? FLAT_FLAG_RAM : 0)
          | (ktrace ? FLAT_FLAG_KTRACE : 0)
          | (pic_with_got ? FLAT_FLAG_GOTPIC : 0)
-         | (compress ? (compress == 2 ? FLAT_FLAG_GZDATA : FLAT_FLAG_GZIP) : 0)
+         | (docompress ? (docompress == 2 ? FLAT_FLAG_GZDATA : FLAT_FLAG_GZIP) : 0)
          );
   hdr.build_date = htonl((unsigned long)time(NULL));
   memset(hdr.filler, 0x00, sizeof(hdr.filler));
@@ -2094,54 +2092,32 @@ int main(int argc, char *argv[])
   write(fd, &hdr, sizeof(hdr));
   close(fd);
 
-  /*
-   * get the compression command ready
-   */
-  sprintf(cmd, "gzip -f -9 >> %s", ofile);
-
-#define        START_COMPRESSOR do { \
-               if (gf) \
-                       if (gf_is_pipe) \
-                               pclose(gf); \
-                       else \
-                               fclose(gf); \
-               if (!(gf = popen(cmd, "w" BINARY_FILE_OPTS))) { \
-                       fprintf(stderr, "Can't run cmd %s\n", cmd); \
-                       exit(4); \
-               } \
-               gf_is_pipe = 1; \
-       } while (0)
-
-  gf = fopen(ofile, "ab");     /* Add 'b' to support non-posix (ie windows) */
-  if (!gf) {
-       fprintf(stderr, "Can't open file %s for writing\n", ofile); \
-       exit(4);
+  if (fopen_stream_u(&gf, ofile, "a" BINARY_FILE_OPTS)) {
+    fprintf(stderr, "Can't open file %s for writing\n", ofile);
+    exit(4);
   }
 
-  if (compress == 1)
-       START_COMPRESSOR;
+  if (docompress == 1)
+    reopen_stream_compressed(&gf);
 
   /* Fill in any hole at the beginning of the text segment.  */
   if (verbose)
-         printf("ZERO before text len=0x%x\n", text_offs);
-  write_zeroes(text_offs, gf);
+    printf("ZERO before text len=0x%x\n", text_offs);
+  write_zeroes(text_offs, &gf);
 
   /* Write the text segment.  */
-  fwrite(text, text_len, 1, gf);
+  fwrite_stream(text, text_len, 1, &gf);
 
-  if (compress == 2)
-       START_COMPRESSOR;
+  if (docompress == 2)
+    reopen_stream_compressed(&gf);
 
   /* Write the data segment.  */
-  fwrite(data, data_len, 1, gf);
+  fwrite_stream(data, data_len, 1, &gf);
 
   if (reloc)
-    fwrite(reloc, reloc_len * 4, 1, gf);
+    fwrite_stream(reloc, reloc_len * 4, 1, &gf);
 
-  if(gf_is_pipe)
-    pclose(gf);
-  else
-  fclose(gf);
+  fclose_stream(&gf);
 
   exit(0);
 }
index b1277cac576af57c7885d0f228d3b247cc254f9c..f3c54abb2ac432af85b88c594482fdc46f789eb0 100644 (file)
--- a/flthdr.c
+++ b/flthdr.c
@@ -15,6 +15,7 @@
 #include <time.h>
 #include <stdlib.h>   /* exit() */
 #include <string.h>   /* strcat(), strcpy() */
+#include <assert.h>
 
 /* macros for conversion between host and (internet) network byte order */
 #ifndef WIN32
@@ -25,6 +26,9 @@
 #define        BINARY_FILE_OPTS "b"
 #endif
 
+#include "compress.h"
+#include <libiberty.h>
+
 /* from uClinux-x.x.x/include/linux */
 #include "flat.h"     /* Binary flat header description                      */
 
 
 char *program_name;
 
-static char cmd[1024];
-static int print = 0, compress = 0, ramload = 0, stacksize = 0, ktrace = 0;
-
-/****************************************************************************/
+static int print = 0, docompress = 0, ramload = 0, stacksize = 0, ktrace = 0;
 
-void
-transferr(FILE *ifp, FILE *ofp, int count)
-{
-       int n, num;
-
-       while (count == -1 || count > 0) {
-               if (count == -1 || count > sizeof(cmd))
-                       num = sizeof(cmd);
-               else
-                       num = count;
-               n = fread(cmd, 1, num, ifp);
-               if (n == 0)
-                       break;
-               if (fwrite(cmd, n, 1, ofp) != 1) {
-                       fprintf(stderr, "Write failed :-(\n");
-                       exit(1);
-               }
-               if (count != -1)
-                       count -= n;
-       }
-       if (count > 0) {
-               fprintf(stderr, "Failed to transferr %d bytes\n", count);
-               exit(1);
-       }
-}
-       
 /****************************************************************************/
 
 void
 process_file(char *ifile, char *ofile)
 {
        int old_flags, old_stack, new_flags, new_stack;
-       FILE *ifp, *ofp;
-       int ofp_is_pipe = 0;
+       stream ifp, ofp;
        struct flat_hdr old_hdr, new_hdr;
-       char tfile[256];
-       char tfile2[256];
+       char *tfile, tmpbuf[256];
+       int input_error, output_error;
 
-       *tfile = *tfile2 = '\0';
+       *tmpbuf = '\0';
 
-       if ((ifp = fopen(ifile, "r" BINARY_FILE_OPTS)) == NULL) {
+       if (fopen_stream_u(&ifp, ifile, "r" BINARY_FILE_OPTS)) {
                fprintf(stderr, "Cannot open %s\n", ifile);
                return;
        }
 
-       if (fread(&old_hdr, sizeof(old_hdr), 1, ifp) != 1) {
+       if (fread_stream(&old_hdr, sizeof(old_hdr), 1, &ifp) != 1) {
                fprintf(stderr, "Cannot read header of %s\n", ifile);
                return;
        }
@@ -103,13 +77,13 @@ process_file(char *ifile, char *ofile)
        new_stack = old_stack = ntohl(old_hdr.stack_size);
        new_hdr = old_hdr;
 
-       if (compress == 1) {
+       if (docompress == 1) {
                new_flags |= FLAT_FLAG_GZIP;
                new_flags &= ~FLAT_FLAG_GZDATA;
-       } else if (compress == 2) {
+       } else if (docompress == 2) {
                new_flags |= FLAT_FLAG_GZDATA;
                new_flags &= ~FLAT_FLAG_GZIP;
-       } else if (compress < 0)
+       } else if (docompress < 0)
                new_flags &= ~(FLAT_FLAG_GZIP|FLAT_FLAG_GZDATA);
        
        if (ramload > 0)
@@ -163,13 +137,13 @@ process_file(char *ifile, char *ofile)
                        printf("-----------------------------------------------------------\n");
                        first = 0;
                }
-               *tfile = '\0';
-               strcat(tfile, (old_flags & FLAT_FLAG_KTRACE) ? "k" : "");
-               strcat(tfile, (old_flags & FLAT_FLAG_RAM) ? "r" : "");
-               strcat(tfile, (old_flags & FLAT_FLAG_GOTPIC) ? "p" : "");
-               strcat(tfile, (old_flags & FLAT_FLAG_GZIP) ? "z" :
+               *tmpbuf = '\0';
+               strcat(tmpbuf, (old_flags & FLAT_FLAG_KTRACE) ? "k" : "");
+               strcat(tmpbuf, (old_flags & FLAT_FLAG_RAM) ? "r" : "");
+               strcat(tmpbuf, (old_flags & FLAT_FLAG_GOTPIC) ? "p" : "");
+               strcat(tmpbuf, (old_flags & FLAT_FLAG_GZIP) ? "z" :
                                        ((old_flags & FLAT_FLAG_GZDATA) ? "d" : ""));
-               printf("-%-3.3s ", tfile);
+               printf("-%-3.3s ", tmpbuf);
                printf("%3d ", ntohl(old_hdr.rev));
                printf("%6d ", text=ntohl(old_hdr.data_start)-sizeof(struct flat_hdr));
                printf("%6d ", data=ntohl(old_hdr.data_end)-ntohl(old_hdr.data_start));
@@ -205,106 +179,74 @@ process_file(char *ifile, char *ofile)
        new_hdr.flags = htonl(new_flags);
        new_hdr.stack_size = htonl(new_stack);
 
-       strcpy(tfile, "/tmp/flatXXXXXX");
-       mkstemp(tfile);
-       if ((ofp = fopen(tfile, "w" BINARY_FILE_OPTS)) == NULL) {
+       tfile = make_temp_file("flthdr");
+
+       if (fopen_stream_u(&ofp, tfile, "w" BINARY_FILE_OPTS)) {
                fprintf(stderr, "Failed to open %s for writing\n", tfile);
                unlink(tfile);
-               unlink(tfile2);
                exit(1);
        }
 
-       if (fwrite(&new_hdr, sizeof(new_hdr), 1, ofp) != 1) {
+       /* Copy header (always uncompressed).  */
+       if (fwrite_stream(&new_hdr, sizeof(new_hdr), 1, &ofp) != 1) {
                fprintf(stderr, "Failed to write to  %s\n", tfile);
                unlink(tfile);
-               unlink(tfile2);
                exit(1);
        }
 
-       /*
-        * get ourselves a fully uncompressed copy of the text/data/relocs
-        * so that we can manipulate it more easily
-        */
-       if (old_flags & (FLAT_FLAG_GZIP|FLAT_FLAG_GZDATA)) {
-               FILE *tfp;
-
-               strcpy(tfile2, "/tmp/flat2XXXXXX");
-               mkstemp(tfile2);
-               
-               if (old_flags & FLAT_FLAG_GZDATA) {
-                       tfp = fopen(tfile2, "w" BINARY_FILE_OPTS);
-                       if (!tfp) {
-                               fprintf(stderr, "Failed to open %s for writing\n", tfile2);
-                               exit(1);
-                       }
-                       transferr(ifp, tfp, ntohl(old_hdr.data_start) -
-                                       sizeof(struct flat_hdr));
-                       fclose(tfp);
-               }
-
-               sprintf(cmd, "gunzip >> %s", tfile2);
-               tfp = popen(cmd, "w" BINARY_FILE_OPTS);
-               if(!tfp) {
-                       perror("popen");
-                       exit(1);
-               }
-               transferr(ifp, tfp, -1);
-               pclose(tfp);
-
-               fclose(ifp);
-               ifp = fopen(tfile2, "r" BINARY_FILE_OPTS);
-               if (!ifp) {
-                       fprintf(stderr, "Failed to open %s for reading\n", tfile2);
-                       unlink(tfile);
-                       unlink(tfile2);
-                       exit(1);
-               }
-       }
+       /* Whole input file (including text) is compressed: start decompressing
+          now.  */
+       if (old_flags & FLAT_FLAG_GZIP)
+               reopen_stream_compressed(&ifp);
 
+       /* Likewise, output file is compressed. Start compressing now.  */
        if (new_flags & FLAT_FLAG_GZIP) {
                printf("zflat %s --> %s\n", ifile, ofile);
-               fclose(ofp);
-               sprintf(cmd, "gzip -9 -f >> %s", tfile);
-               ofp = popen(cmd, "w" BINARY_FILE_OPTS);
-               ofp_is_pipe = 1;
-       } else if (new_flags & FLAT_FLAG_GZDATA) {
-               printf("zflat-data %s --> %s\n", ifile, ofile);
-               transferr(ifp, ofp, ntohl(new_hdr.data_start) -
-                               sizeof(struct flat_hdr));
-               fclose(ofp);
-               sprintf(cmd, "gzip -9 -f >> %s", tfile);
-               ofp = popen(cmd, "w" BINARY_FILE_OPTS);
-               ofp_is_pipe = 1;
+               reopen_stream_compressed(&ofp);
        }
 
-       if (!ofp) { /* can only happen if using gzip/gunzip */
-               fprintf(stderr, "Can't run cmd %s\n", cmd);
-               unlink(tfile);
-               unlink(tfile2);
-               exit(1);
+       transfer(&ifp, &ofp,
+                 ntohl(old_hdr.data_start) - sizeof(struct flat_hdr));
+
+       /* Only data and relocs were compressed in input.  Start decompressing
+          from here.  */
+       if (old_flags & FLAT_FLAG_GZDATA)
+               reopen_stream_compressed(&ifp);
+
+       /* Only data/relocs to be compressed in output.  Start compressing
+          from here.  */
+       if (new_flags & FLAT_FLAG_GZDATA) {
+               printf("zflat-data %s --> %s\n", ifile, ofile);
+               reopen_stream_compressed(&ofp);
        }
 
-       transferr(ifp, ofp, -1);
-       
-       if (ferror(ifp) || ferror(ofp)) {
+       transfer(&ifp, &ofp, -1);
+
+       input_error = ferror_stream(&ifp);
+       output_error = ferror_stream(&ofp);
+
+       if (input_error || output_error) {
                fprintf(stderr, "Error on file pointer%s%s\n",
-                               ferror(ifp) ? " input" : "", ferror(ofp) ? " output" : "");
+                               input_error ? " input" : "",
+                               output_error ? " output" : "");
                unlink(tfile);
-               unlink(tfile2);
                exit(1);
        }
 
-       fclose(ifp);
-       if (ofp_is_pipe)
-               pclose(ofp);
-       else
-               fclose(ofp);
+       fclose_stream(&ifp);
+       fclose_stream(&ofp);
+
+       /* Copy temporary file to output location.  */
+       fopen_stream_u(&ifp, tfile, "r" BINARY_FILE_OPTS);
+       fopen_stream_u(&ofp, ofile, "w" BINARY_FILE_OPTS);
+
+       transfer(&ifp, &ofp, -1);
+
+       fclose_stream(&ifp);
+       fclose_stream(&ofp);
 
-       /* cheat a little here to preserve file permissions */
-       sprintf(cmd, "cp %s %s", tfile, ofile);
-       system(cmd);
        unlink(tfile);
-       unlink(tfile2);
+       free(tfile);
 }
 
 /****************************************************************************/
@@ -343,9 +285,9 @@ main(int argc, char *argv[])
        while ((c = getopt(argc, argv, "pdzZrRkKs:o:")) != EOF) {
                switch (c) {
                case 'p': print = 1;                break;
-               case 'z': compress = 1;             break;
-               case 'd': compress = 2;             break;
-               case 'Z': compress = -1;            break;
+               case 'z': docompress = 1;           break;
+               case 'd': docompress = 2;           break;
+               case 'Z': docompress = -1;          break;
                case 'r': ramload = 1;              break;
                case 'R': ramload = -1;             break;
                case 'k': ktrace = 1;               break;
@@ -366,8 +308,8 @@ main(int argc, char *argv[])
 
        if (ofile && argc - optind > 1)
                usage("-o can only be used with a single file");
-       
-       if (!print && !compress && !ramload && !stacksize) /* no args == print */
+
+       if (!print && !docompress && !ramload && !stacksize) /* no args == print */
                print = argc - optind; /* greater than 1 is short format */
        
        for (c = optind; c < argc; c++) {