Logo Search packages:      
Sourcecode: sash version File versions  Download package

cmd_gzip.c

/*
 * Copyright (c) 2002 by David I. Bell
 * Permission is granted to use, distribute, or modify this source,
 * provided that this copyright notice remains intact.
 *
 * The "gzip" and "gunzip" built-in commands.
 * These commands are optionally built into sash.
 * This uses the zlib library by Jean-loup Gailly to compress and
 * uncompress the files.
 */

#if   HAVE_GZIP


#include <sys/types.h>
#include <sys/stat.h>
#include <zlib.h>

#include "sash.h"


#define     GZ_EXT            ".gz"
#define     TGZ_EXT           ".tgz"
#define     Z_EXT       ".Z"
#define     TAR_EXT           ".tar"
#define     NO_EXT            ""


/*
 * Tables of conversions to make to file extensions.
 */
typedef     struct
{
      const char *      input;
      const char *      output;
} CONVERT;


static const CONVERT    gzipConvertTable[] =
{
      {TAR_EXT,   TGZ_EXT},
      {NO_EXT,    GZ_EXT}
};


static const CONVERT    gunzipConvertTable[] =
{
      {TGZ_EXT,   TAR_EXT},
      {GZ_EXT,    NO_EXT},
      {Z_EXT,           NO_EXT},
      {NO_EXT,    NO_EXT}
};


/*
 * Local routines to compress and uncompress files.
 */
static BOOL gzip(const char * inputFile, const char * outputFile);
static BOOL gunzip(const char * inputFile, const char * outputFile);

static const char * convertName
      (const CONVERT * table, const char * inFile);


void
do_gzip(int argc, const char ** argv)
{
      const char *      outPath;
      const char *      inFile;
      const char *      outFile;
      int         i;

      argc--;
      argv++;

      /*
       * Look for the -o option if it is present.
       * If present, it must be at the end of the command.
       * Remember the output path and remove it if found.
       */
      outPath = NULL;

      if ((argc >= 2) && (strcmp(argv[argc - 2], "-o") == 0) &&
            (argv[argc - 1][0] != '-'))
      {
            argc -= 2;
            outPath = argv[argc + 1];
      }

      /*
       * Now make sure that there are no more options.
       */
      for (i = 0; i < argc; i++)
      {
            if (argv[i][0] == '-')
            {
                  if (strcmp(argv[i], "-o") == 0)
                        fprintf(stderr, "Illegal use of -o\n");
                  else
                        fprintf(stderr, "Illegal option\n");

                  return;
            }
      }
      
      /*
       * If there is no output path specified, then compress each of
       * the input files in place using their full paths.  The input
       * file names are then deleted.
       */
      if (outPath == NULL)
      {
            while (!intFlag && (argc-- > 0))
            {
                  inFile = *argv++;

                  outFile = convertName(gzipConvertTable, inFile);

                  /*
                   * Try to compress the file.
                   */
                  if (!gzip(inFile, outFile))
                        continue;

                  /*
                   * This was successful.
                   * Try to delete the original file now.
                   */
                  if (unlink(inFile) < 0)
                  {
                        fprintf(stderr, "%s: %s\n", inFile,
                              "Compressed ok but unlink failed");
                  }
            }

            return;
      }

      /*
       * There is an output path specified.
       * If it is not a directory, then either compress the single
       * specified input file to the exactly specified output path,
       * or else complain.
       */
      if (!isDirectory(outPath))
      {
            if (argc == 1)
                  (void) gzip(*argv, outPath);
            else
                  fprintf(stderr, "Exactly one input file is required\n");

            return;
      }

      /*
       * There was an output directory specified.
       * Compress each of the input files into the specified
       * output directory, converting their extensions if possible.
       */
      while (!intFlag && (argc-- > 0))
      {
            inFile = *argv++;

            /*
             * Strip the path off of the input file name to make
             * the beginnings of the output file name.
             */
            outFile = strrchr(inFile, '/');

            if (outFile)
                  outFile++;
            else
                  outFile = inFile;

            /*
             * Convert the extension of the output file name if possible.
             * If we can't, then that is ok.
             */
            outFile = convertName(gzipConvertTable, outFile);

            /*
             * Now build the output path name by prefixing it with
             * the output directory.
             */
            outFile = buildName(outPath, outFile);

            /*
             * Compress the input file without deleting the input file.
             */
            (void) gzip(inFile, outFile);
      }
}


void
do_gunzip(int argc, const char ** argv)
{
      const char *      outPath;
      const char *      inFile;
      const char *      outFile;
      int         i;

      argc--;
      argv++;

      /*
       * Look for the -o option if it is present.
       * If present, it must be at the end of the command.
       * Remember the output path and remove it if found.
       */
      outPath = NULL;

      if ((argc >= 2) && (strcmp(argv[argc - 2], "-o") == 0) &&
            (argv[argc - 1][0] != '-'))
      {
            argc -= 2;
            outPath = argv[argc + 1];
      }

      /*
       * Now make sure that there are no more options.
       */
      for (i = 0; i < argc; i++)
      {
            if (argv[i][0] == '-')
            {
                  if (strcmp(argv[i], "-o") == 0)
                        fprintf(stderr, "Illegal use of -o\n");
                  else
                        fprintf(stderr, "Illegal option\n");

                  return;
            }
      }
      
      /*
       * If there is no output path specified, then uncompress each of
       * the input files in place using their full paths.  They must
       * have one of the proper compression extensions which is converted.
       * The input file names are then deleted.
       */
      if (outPath == NULL)
      {
            while (!intFlag && (argc-- > 0))
            {
                  inFile = *argv++;

                  outFile = convertName(gunzipConvertTable, inFile);

                  if (inFile == outFile)
                  {
                        fprintf(stderr, "%s: %s\n", inFile,
                              "missing compression extension");

                        continue;
                  }

                  /*
                   * Try to uncompress the file.
                   */
                  if (!gunzip(inFile, outFile))
                        continue;

                  /*
                   * This was successful.
                   * Try to delete the original file now.
                   */
                  if (unlink(inFile) < 0)
                  {
                        fprintf(stderr, "%s: %s\n", inFile,
                              "Uncompressed ok but unlink failed");
                  }
            }

            return;
      }

      /*
       * There is an output path specified.
       * If the output path is a device file then uncompress each of
       * the input files to the device file.
       */
      if (isDevice(outPath))
      {
            while (!intFlag && (argc-- > 0))
                  (void) gunzip(*argv++, outPath);

            return;
      }

      /*
       * If the output path is not a directory then either uncompress the
       * single specified input file to the exactly specified output path,
       * or else complain.
       */
      if (!isDirectory(outPath))
      {
            if (argc == 1)
                  (void) gunzip(*argv, outPath);
            else
                  fprintf(stderr, "Exactly one input file is required\n");

            return;
      }

      /*
       * There was an output directory specified.
       * Uncompress each of the input files into the specified
       * output directory, converting their extensions if possible.
       */
      while (!intFlag && (argc-- > 0))
      {
            inFile = *argv++;

            /*
             * Strip the path off of the input file name to make
             * the beginnings of the output file name.
             */
            outFile = strrchr(inFile, '/');

            if (outFile)
                  outFile++;
            else
                  outFile = inFile;

            /*
             * Convert the extension of the output file name if possible.
             * If we can't, then that is ok.
             */
            outFile = convertName(gunzipConvertTable, outFile);

            /*
             * Now build the output path name by prefixing it with
             * the output directory.
             */
            outFile = buildName(outPath, outFile);

            /*
             * Uncompress the input file without deleting the input file.
             */
            (void) gunzip(inFile, outFile);
      }
}


/*
 * Compress the specified input file to produce the output file.
 * Returns TRUE if successful.
 */
static BOOL
gzip(const char * inputFileName, const char * outputFileName)
{
      gzFile            outGZ;
      int         inFD;
      int         len;
      int         err;
      struct      stat  statBuf1;
      struct      stat  statBuf2;
      char        buf[BUF_SIZE];

      outGZ = NULL;
      inFD = -1;

      /*
       * See if the output file is the same as the input file.
       * If so, complain about it.
       */
      if (stat(inputFileName, &statBuf1) < 0)
      {
            perror(inputFileName);

            return FALSE;
      }

      if (stat(outputFileName, &statBuf2) < 0)
      {
            statBuf2.st_ino = -1;
            statBuf2.st_dev = -1;
      }

      if ((statBuf1.st_dev == statBuf2.st_dev) &&
            (statBuf1.st_ino == statBuf2.st_ino))
      {
            fprintf(stderr,
                  "Cannot compress file \"%s\" on top of itself\n",
                  inputFileName);

            return FALSE;
      }

      /*
       * Open the input file.
       */
      inFD = open(inputFileName, O_RDONLY);

      if (inFD < 0)
      {
            perror(inputFileName);

            goto failed;
      }

      /*
       * Ask the zlib library to open the output file.
       */
      outGZ = gzopen(outputFileName, "wb9");

      if (outGZ == NULL)
      {
            fprintf(stderr, "%s: gzopen failed\n", outputFileName);

            goto failed;
      }

      /*
       * Read the uncompressed data from the input file and write
       * the compressed data to the output file.
       */
      while ((len = read(inFD, buf, sizeof(buf))) > 0)
      {
            if (gzwrite(outGZ, buf, len) != len)
            {
                  fprintf(stderr, "%s: %s\n", inputFileName,
                        gzerror(outGZ, &err));

                  goto failed;
            }

            if (intFlag)
                  goto failed;
      }

      if (len < 0)
      {
            perror(inputFileName);

            goto failed;
      }

      /*
       * All done, close the files.
       */
      if (close(inFD))
      {
            perror(inputFileName);

            goto failed;
      }

      inFD = -1;

      if (gzclose(outGZ) != Z_OK)
      {
            fprintf(stderr, "%s: gzclose failed\n", outputFileName);

            goto failed;
      }

      outGZ = NULL;

      /*
       * Success.
       */
      return TRUE;


/*
 * Here on an error, to clean up.
 */
failed:
      if (inFD >= 0)
            (void) close(inFD);

      if (outGZ != NULL)
            (void) gzclose(outGZ);

      return FALSE;
}


/*
 * Uncompress the input file to produce the output file.
 * Returns TRUE if successful.
 */
static BOOL
gunzip(const char * inputFileName, const char * outputFileName)
{
      gzFile            inGZ;
      int         outFD;
      int         len;
      int         err;
      struct      stat  statBuf1;
      struct      stat  statBuf2;
      char        buf[BUF_SIZE];

      inGZ = NULL;
      outFD = -1;

      /*
       * See if the output file is the same as the input file.
       * If so, complain about it.
       */
      if (stat(inputFileName, &statBuf1) < 0)
      {
            perror(inputFileName);

            return FALSE;
      }

      if (stat(outputFileName, &statBuf2) < 0)
      {
            statBuf2.st_ino = -1;
            statBuf2.st_dev = -1;
      }

      if ((statBuf1.st_dev == statBuf2.st_dev) &&
            (statBuf1.st_ino == statBuf2.st_ino))
      {
            fprintf(stderr,
                  "Cannot uncompress file \"%s\" on top of itself\n",
                  inputFileName);

            return FALSE;
      }

      /*
       * Ask the zlib library to open the input file.
       */
      inGZ = gzopen(inputFileName, "rb");

      if (inGZ == NULL)
      {
            fprintf(stderr, "%s: gzopen failed\n", inputFileName);

            return FALSE;
      }

      /*
       * Create the output file.
       */
      if (isDevice(outputFileName))
            outFD = open(outputFileName, O_WRONLY);
      else
            outFD = open(outputFileName, O_WRONLY|O_CREAT|O_TRUNC, 0666);

      if (outFD < 0)
      {
            perror(outputFileName);

            goto failed;
      }

      /*
       * Read the compressed data from the input file and write
       * the uncompressed data extracted from it to the output file.
       */
      while ((len = gzread(inGZ, buf, sizeof(buf))) > 0)
      {
            if (fullWrite(outFD, buf, len) < 0)
            {
                  perror(outputFileName);

                  goto failed;
            }

            if (intFlag)
                  goto failed;
      }

      if (len < 0)
      {
            fprintf(stderr, "%s: %s\n", inputFileName,
                  gzerror(inGZ, &err));

            goto failed;
      }

      /*
       * All done, close the files.
       */
      if (close(outFD))
      {
            perror(outputFileName);

            goto failed;
      }

      outFD = -1;

      if (gzclose(inGZ) != Z_OK)
      {
            fprintf(stderr, "%s: gzclose failed\n", inputFileName);

            goto failed;
      }

      inGZ = NULL;

      /*
       * Success.
       */
      return TRUE;


/*
 * Here on an error, to clean up.
 */
failed:
      if (outFD >= 0)
            (void) close(outFD);

      if (inGZ != NULL)
            (void) gzclose(inGZ);

      return FALSE;
}


/*
 * Convert an input file name to an output file name according to
 * the specified convertion table.  The returned name points into a
 * static buffer which is overwritten on each call.  The table must
 * end in an entry which always specifies a successful conversion.
 * If no conversion is done the original input file name pointer is
 * returned.
 */
const char *
convertName(const CONVERT * table, const char * inputFile)
{
      int         inputLength;
      int         testLength;
      static char buf[PATH_LEN];

      inputLength = strlen(inputFile);

      for (;;)
      {
            testLength = strlen(table->input);

            if ((inputLength >= testLength) &&
                  (memcmp(table->input,
                        inputFile + inputLength - testLength,
                        testLength) == 0))
            {
                  break;
            }

            table++;
      }

      /*
       * If no conversion was done, return the original pointer.
       */
      if ((testLength == 0) && (table->output[0] == '\0'))
            return inputFile;

      /*
       * Build the new name and return it.
       */
      memcpy(buf, inputFile, inputLength - testLength);

      memcpy(buf + inputLength - testLength, table->output,
            strlen(table->output) + 1);

      return buf;
}


#endif

/* END CODE */

Generated by  Doxygen 1.6.0   Back to index