Blob Blame History Raw
/* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */

/* libmwaw: tools
* Version: MPL 2.0 / LGPLv2+
*
* The contents of this file are subject to the Mozilla Public License Version
* 2.0 (the "License"); you may not use this file except in compliance with
* the License or as specified alternatively below. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* Major Contributor(s):
* Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
*
*
* All Rights Reserved.
*
* For minor contributions see the git repository.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
* in which case the provisions of the LGPLv2+ are applicable
* instead of those above.
*/
#ifdef _WIN32
#  define MWAWZIP_CDECL __cdecl
#else
#  define MWAWZIP_CDECL
#endif

#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <fstream>
#include <iostream>
#include <string>
#include <vector>

#include "input.h"
#include "xattr.h"
#include "zip.h"
#include "zip_internal.h"

#ifndef VERSION
#define VERSION "UNKNOWN VERSION"
#endif

static void usage(const char *cmdname)
{
  std::cerr << "Usage: " << cmdname << " [-h][-x][-D] FILENAME ZIPFILE\n";
  std::cerr << "\n";
  std::cerr << "try to zip the content of FILENAME in ZIPFILE.\n";
  std::cerr << "\n";
  std::cerr << "Options:\n";
  std::cerr << "\t -h: print this help,\n";
  std::cerr << "\t -x: do not zip a BinHex, an OLE2 or a Zip file,\n";
  std::cerr << "\t -v: output mwawZip version\n";
  std::cerr << "\t -D: only zip the file containing a resource fork or finder information.\n";
}

static int printVersion()
{
  std::cerr << "mwawZip " << VERSION << "\n";
  return 0;
}

int MWAWZIP_CDECL main(int argc, char **argv)
{
  bool checkZip=false;
  bool verbose=false;
  bool doNotCompressSimpleFile=false;
  int ch;
  while ((ch = getopt(argc, argv, "hvxD")) != -1) {
    switch (ch) {
    case 'v':
      printVersion();
      return 0;
    case 'x':
      checkZip=true;
      break;
    case 'D':
      doNotCompressSimpleFile=true;
      break;
    default:
      verbose=true;
      break;
    }
  }
  if (argc != 2+optind || verbose) {
    usage(argv[0]);
    return 1;
  }

  // check if it is a regular file
  struct stat status;
  if (stat(argv[optind], &status) || !S_ISREG(status.st_mode)) {
    std::cerr << argv[0] << ": the file " << argv[optind] << " is a not a regular file\n";
    return 1;
  }
  if (checkZip) {
    // we need to open the file to look for the file signature
    try {
      std::ifstream file(argv[optind], std::ios::binary);
      if (file.bad()) {
        std::cerr << argv[0] << ": the file " << argv[optind] << " seems bad\n";
        return 1;
      }
      file.seekg(0, std::ios::beg);
      char buff[4] = {'\0', '\0','\0','\0'};
      file.read(buff,4);
      // look for a zip file signature
      if (buff[0]=='P' && buff[1]=='K') {
        if (((buff[2]==char(3)||buff[2]==char(5)||buff[2]==char(7)) && buff[3]==buff[2]+char(1)) ||
            (buff[2]=='L'&&buff[3]=='I') || (buff[2]=='S'&&buff[3]=='p'))
          return 2;
      }
      // look for an ole file signature
      else if (buff[0]==char(0xd0) && buff[1]==char(0xcf) && buff[2]==char(0x11) && buff[3]==char(0xe0)) {
        file.read(buff,4);
        if (buff[0]==char(0xa1)&&buff[1]==char(0xb1) && buff[2]==char(0x1a) && buff[3]==char(0xe1))
          return 2;
      }
      // look for a binhex file signature
      else if (buff[0]=='('&&buff[1]=='T'&&buff[2]=='h'&&buff[3]=='i') {
        file.read(buff,4);
        if (buff[0]=='s'&&buff[1]==' '&&buff[2]=='f'&&buff[3]=='i')
          return 2;
      }
    }
    catch (...) {
    }
  }
  std::string resultFile(argv[optind+1]);
  // check if the file exists
  if (stat(resultFile.c_str(), &status)==0) {
    std::cerr  << argv[0] << ": the file " << resultFile << " already exists\n";
    return 1;
  }

  std::string originalFile(argv[optind]);
#ifdef WIN32
  for (auto &c : originalFile) {
    if (c=='\\') c='/';
  }
#endif
  /** find folder and base file name*/
  size_t sPos=originalFile.rfind('/');
  std::string folder(""), file("");
  if (sPos==std::string::npos)
    file = originalFile;
  else {
    folder=originalFile.substr(0,sPos+1);
    file=originalFile.substr(sPos+1);
  }

  try {
    //! the main data fork
    std::shared_ptr<libmwaw_zip::FileStream> fStream(new libmwaw_zip::FileStream(originalFile.c_str()));
    if (!fStream->ok()) {
      fprintf(stderr, "Failed to create stream for %s\n", originalFile.c_str());
      return 1;
    }
    //! the attributes
    libmwaw_zip::XAttr xattr(originalFile.c_str());
    // check first the classic attributes (which are no longer reconstructed)
    auto auxiStream = xattr.getClassicStream();
    if (!auxiStream)
      auxiStream = xattr.getStream();
    if (!auxiStream) {
      // look for a resource file
      std::string name=folder+"._"+file;
      if (stat(name.c_str(), &status) || !S_ISREG(status.st_mode)) {
        name=folder+"__MACOSX/._"+file;
        if (stat(name.c_str(), &status) || !S_ISREG(status.st_mode))
          name = "";
      }
      if (name.length()) {
        std::shared_ptr<libmwaw_zip::FileStream> fAuxiStream(new libmwaw_zip::FileStream(name.c_str()));
        if (fAuxiStream->ok())
          auxiStream = fAuxiStream;
      }
    }
    if (!auxiStream && doNotCompressSimpleFile)
      return 2;
    libmwaw_zip::Zip zip;
    if (!zip.open(argv[optind+1]))
      return 1;
    zip.add(fStream, file.c_str());
    if (auxiStream) {
      std::string name="._"+file;
      zip.add(auxiStream, name.c_str());
    }
    zip.close();
  }
  catch (...) {
    std::cerr << argv[0] << ": error when zipping file " << argv[optind] << "\n";
    return -1;
  }

  return 0;
}

// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: