}
}
+ virtual const timeval& getModificationTime() const
+ {
+ return m_reader->getModTime();
+ }
+
private:
ReaderPtr m_reader;
};
using namespace zipper;
+const timeval zipper::s_now = {0,0};
+
+
class FileReader::FileReaderImpl
{
public:
errMsg;
throw IOException(message.str());
}
- initSize();
+ initStats();
}
FileReaderImpl(const std::string& filename, int fd, bool closeFd) :
m_fd(fd),
m_closeOnExit(closeFd)
{
- initSize();
+ initStats();
}
~FileReaderImpl()
}
const std::string& getSourceName() const { return m_filename; }
+ const timeval& getModTime() const { return m_modTime; }
zsize_t getSize() const { return m_size; }
}
private:
- void initSize()
+ void initStats()
{
// If we fail here, we need to essentially run the dtor manually.
- // initSize is called from the constructors, and so the dtor will
+ // initStats is called from the constructors, and so the dtor will
// NOT run if an exception is thrown.
struct stat buf;
else
{
m_size = buf.st_size;
+ m_modTime.tv_sec = buf.st_mtime;
+ m_modTime.tv_usec = 0;
}
}
int m_fd;
bool m_closeOnExit;
zsize_t m_size;
+ timeval m_modTime;
};
FileReader::FileReader(const std::string& filename) :
return m_impl->getSourceName();
}
+const timeval&
+FileReader::getModTime() const
+{
+ return m_impl->getModTime();
+}
+
zsize_t
FileReader::getSize() const
{
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
+#include <utime.h>
using namespace zipper;
class FileWriter::FileWriterImpl
{
public:
- FileWriterImpl(const std::string& filename, mode_t createPermissions) :
+ FileWriterImpl(
+ const std::string& filename,
+ mode_t createPermissions,
+ const timeval& modTime
+ ) :
m_filename(filename),
+ m_modTime(modTime),
m_fd(-1),
- m_closeOnExit(true)
+ m_closeOnExit(true),
+ m_setModTimeOnExit(true)
{
m_fd =
::open(
FileWriterImpl(const std::string& filename, int fd, bool closeFd) :
m_filename(filename),
m_fd(fd),
- m_closeOnExit(closeFd)
+ m_closeOnExit(closeFd),
+ m_setModTimeOnExit(false)
{
}
~FileWriterImpl()
{
close();
+
+ if (m_setModTimeOnExit)
+ {
+ struct timeval times[2];
+ if (s_now.tv_sec == m_modTime.tv_sec)
+ {
+ gettimeofday(×[0], NULL);
+ times[1] = times[0];
+ }
+ else
+ {
+ times[0] = m_modTime;
+ times[1] = m_modTime;
+ }
+ utimes(m_filename.c_str(), times);
+ }
}
virtual void writeData(
}
std::string m_filename;
+ timeval m_modTime;
int m_fd;
bool m_closeOnExit;
+ bool m_setModTimeOnExit;
};
-FileWriter::FileWriter(const std::string& filename, mode_t createPermissions) :
- m_impl(new FileWriterImpl(filename, createPermissions))
+FileWriter::FileWriter(
+ const std::string& filename,
+ mode_t createPermissions,
+ const timeval& modTime) :
+ m_impl(new FileWriterImpl(filename, createPermissions, modTime))
{
}
-2011-05-22 Version 1.0.1
+2011-05-27 Version 1.0.1
- Removed private classes from doxygen output
- - Removed private symbols from shared library
+ - Added the zipper utility executable
+ - Added timestamp support
2011-05-21 Version 1.0.0
- Initial release
libzipper is not a general-purpose archive management library, as it
does not provide access to the filesystem attributes of each file.
-(ie. libzipper does not support the concepts of file owner, group,
-permissions, or timestamps.
+(ie. libzipper does not support the concepts of file owner, group or
+permissions.
Missing Features
- zip64 support (for >4Gb zip files)
* Fixed packaging to meet debian policy (renamed package, fixed shlibs file
and symlinks
+ * Created the libzipper-tools and libzipper-docs binary packages.
- -- Michael McMaster <michael@codesrc.com> Sat, 22 May 2011 21:48:11 +1000
+ -- Michael McMaster <michael@codesrc.com> Fri, 27 May 2011 19:41:00 +1000
libzipper1 (1.0.0-1) unstable; urgency=low
Source: libzipper1
Priority: optional
Maintainer: Michael McMaster <michael@codesrc.com>
-Build-Depends: debhelper (>= 7.0.50~), autotools-dev, cdbs, zlib1g-dev, doxygen, graphviz
+Build-Depends: debhelper (>= 7.0.50~), autotools-dev, cdbs, pkg-config, zlib1g-dev, doxygen, texlive-font-utils, graphviz
Standards-Version: 3.9.2
Section: libs
Homepage: http://www.codesrc.com/src/libzipper
Section: libdevel
Architecture: any
Depends: libzipper1 (= ${binary:Version}), ${misc:Depends}
+Recommends: pkg-config
Suggests: libzipper-doc
Homepage: http://www.codesrc.com/src/libzipper
Description: simple interface for reading and writing compressed files
Package: libzipper-doc
Section: doc
Architecture: any
-Depends: libjs-jquery, ${misc:Depends}
+Depends: ${misc:Depends}
Homepage: http://www.codesrc.com/src/libzipper
Description: simple interface for reading and writing compressed files
libzipper offers a flexible C++ interface for reading and writing compressed
include /usr/share/cdbs/1/rules/debhelper.mk
include /usr/share/cdbs/1/class/autotools.mk
-# Make use of the libjs-jquery package
-# Required by lintian rule: embedded-javascript-library
+# Doxygen may copy an old version of jquery.js into the HTML documentation
+# tree, even though it is not used. This causes the
+# "embedded-javascript-library" lintian error.
+# Delete the file to avoid creating a dependency on the libjs-jquery package in
+# order to resolve the lintian error.
+# This is fixed in doxygen 1.7.4-2
install/libzipper-doc::
- rm $(DEB_DESTDIR)/usr/share/doc/libzipper/html/jquery.js
- ln -s ../../../javascript/jquery/jquery.js $(DEB_DESTDIR)/usr/share/doc/libzipper/html/jquery.js
+ if [ -f $(DEB_DESTDIR)/usr/share/doc/libzipper/html/jquery.js ]; then \
+ if grep jquery.js $(DEB_DESTDIR)/usr/share/doc/libzipper/html/*.html; then \
+ echo "ERROR: doxygen is making use of jquery.js in the HTML "; \
+ echo "documentation. Please add a dependency on libjs-jquery."; \
+ else \
+ rm $(DEB_DESTDIR)/usr/share/doc/libzipper/html/jquery.js; \
+ fi \
+ fi
FileEntry(
const ReaderPtr& reader,
zsize_t dataOffset,
- const std::string& filename
+ const std::string& filename,
+ time_t modTime
) :
m_reader(reader),
m_dataOffset(dataOffset),
m_fileName(filename)
{
+ m_modTime.tv_sec = modTime;
+ m_modTime.tv_usec = 0;
}
virtual bool isDecompressSupported() const
virtual zsize_t getCompressedSize() const { return -1; }
virtual zsize_t getUncompressedSize() const { return -1; }
+ virtual const timeval& getModificationTime() const { return m_modTime; }
virtual void decompress(Writer& writer)
{
ReaderPtr m_reader;
zsize_t m_dataOffset;
std::string m_fileName;
+ timeval m_modTime;
};
}
bool fcomment = (header[3] & 0x10) != 0;
bool fhcrc = (header[3] & 2) != 0;
+ time_t modTime = read32_le(&header[4]);
+
size_t offset(10);
if (fextra)
std::vector<CompressedFilePtr> result;
result.push_back(
- CompressedFilePtr(new FileEntry(reader, offset, embeddedName)));
+ CompressedFilePtr(
+ new FileEntry(reader, offset, embeddedName, modTime)));
return result;
}
{
uint8_t buffer[ChunkSize];
memcpy(buffer, Header, sizeof(Header));
+
+ write32_le(reader.getModTime().tv_sec, &buffer[4]); // modtime
+
zsize_t pos(sizeof(Header));
zsize_t filenameSize(filename.size());
#include <cassert>
#include <iostream>
+#include <time.h>
#include <string.h>
using namespace zipper;
namespace
{
+ time_t convertDosDateTime(uint16_t date, uint16_t time)
+ {
+ struct tm parts;
+ memset(&parts, 0, sizeof(parts));
+ parts.tm_sec = time & 0x1F;
+ parts.tm_min = (time & 0x7E0) >> 5;
+ parts.tm_hour = (time >> 11);
+ parts.tm_mday = date & 0x1F;
+ parts.tm_mon = ((date & 0x1E0) >> 5) - 1;
+ parts.tm_year = (date >> 9) + 80;
+ return mktime(&parts);
+ }
+
+ void convertDosDateTime(time_t in, uint16_t& date, uint16_t& time)
+ {
+ struct tm buf;
+ struct tm* parts(localtime_r(&in, &buf));
+
+ time =
+ parts->tm_sec +
+ (parts->tm_min << 5) +
+ (parts->tm_hour << 11);
+
+ date =
+ parts->tm_mday +
+ ((parts->tm_mon + 1) << 5) +
+ ((parts->tm_year - 80) << 9);
+ }
+
class FileEntry : public CompressedFile
{
public:
uint32_t crc,
zsize_t compressedSize,
zsize_t uncompressedSize,
+ time_t modTime,
zsize_t localHeaderOffset,
std::string fileName
) :
m_localHeaderOffset(localHeaderOffset),
m_fileName(fileName)
{
+ m_modTime.tv_sec = modTime;
+ m_modTime.tv_usec = 0;
}
virtual bool isDecompressSupported() const
return m_uncompressedSize;
}
+ virtual const timeval& getModificationTime() const { return m_modTime; }
+
virtual void decompress(Writer& writer)
{
enum
uint32_t m_crc;
zsize_t m_compressedSize;
zsize_t m_uncompressedSize;
+ timeval m_modTime;
zsize_t m_localHeaderOffset;
std::string m_fileName;
};
uint16_t versionNeeded(read16_le(buffer, pos + 6));
uint16_t gpFlag(read16_le(buffer, pos + 8));
uint16_t compressionMethod(read16_le(buffer, pos + 10));
+ uint16_t modTime(read16_le(buffer, pos + 12));
+ uint16_t modDate(read16_le(buffer, pos + 14));
uint32_t crc(read32_le(buffer, pos + 16));
uint32_t compressedSize(read32_le(buffer, pos + 20));
uint32_t uncompressedSize(read32_le(buffer, pos + 24));
crc,
compressedSize,
uncompressedSize,
+ convertDosDateTime(modDate, modTime),
localHeaderOffset,
fileName
)
{
ChunkSize = 64*1024,
WindowBits = 15,
- CRC32Pos = 14
+ TimePos = 10
};
static uint8_t Header[] =
outRecord.crc32);
// Go back and complete the header.
- uint8_t trailer[12];
- write32_le(outRecord.crc32, &trailer[0]);
- write32_le(outRecord.compressedSize, &trailer[4]);
- write32_le(outRecord.uncompressedSize, &trailer[8]);
+ convertDosDateTime(
+ reader.getModTime().tv_sec, outRecord.dosDate, outRecord.dosTime);
+ uint8_t trailer[16];
+ write16_le(outRecord.dosTime, &trailer[0]);
+ write16_le(outRecord.dosDate, &trailer[2]);
+ write32_le(outRecord.crc32, &trailer[4]);
+ write32_le(outRecord.compressedSize, &trailer[8]);
+ write32_le(outRecord.uncompressedSize, &trailer[12]);
writer->writeData(
- outRecord.localHeaderOffset + CRC32Pos, sizeof(trailer), &trailer[0]);
+ outRecord.localHeaderOffset + TimePos, sizeof(trailer), &trailer[0]);
}
void
20, 0x00, // Version (2.0)
20, 0x00, // Version Needed to extract (2.0)
0,0, // gp flag.
- 8,0, // deflate method
- 0,0, // file time
- 0,0 // file date
+ 8,0 // deflate method
};
zsize_t outPos(writer->getSize());
memcpy(buffer, FileHeader, sizeof(FileHeader));
zsize_t pos(sizeof(FileHeader));
+ write16_le(records[i].dosTime, &buffer[pos]);
+ pos += 2;
+ write16_le(records[i].dosDate, &buffer[pos]);
+ pos += 2;
+
write32_le(records[i].crc32, &buffer[pos]);
pos += 4;
uint32_t crc32;
zsize_t compressedSize;
zsize_t uncompressedSize;
+ uint16_t dosDate;
+ uint16_t dosTime;
std::string filename;
};
"under certain conditions.\n\n" <<
"Usage: \n" <<
- argv0 << " {zip|gzip} archive [file...]\n" <<
+ argv0 << " {zip|gzip} archive file [files...]\n" <<
argv0 << " {unzip|gunzip} archive" << std::endl;
}
builtPath << '/';
}
- FileWriter writer(entries[f]->getPath(), 0660);
+ FileWriter writer(
+ entries[f]->getPath(), 0660, entries[f]->getModificationTime());
entries[f]->decompress(writer);
}
}
#include <cstdint>
#include <sys/stat.h> // For mode_t
+#include <sys/time.h> // For timeval
/**
\mainpage libzipper C++ (de)compression library
libzipper is not a general-purpose archive management library, as it
does not provide access to the filesystem attributes of each file.
-(ie. libzipper does not support the concepts of file owner, group,
-permissions, or timestamps.
+(ie. libzipper does not support the concepts of file owner, group or
+permissions.
\section formats Supported Formats
<ul>
return Name;
}
+ virtual const timeval& getModTime() const
+ {
+ return zipper::s_now;
+ }
+
virtual zsize_t getSize() const { return m_data.size(); }
virtual void readData(zsize_t offset, zsize_t bytes, uint8_t* dest) const
uint32_t capabilities;
};
+ /// \brief When passed as a method parameter, it requests that the
+ /// current time be used instead.
+ extern const timeval s_now;
+
/// \brief Returns the capability details of the given format.
const Container& getContainer(ContainerFormat format);
/// the input filename.
virtual const std::string& getSourceName() const = 0;
+ /// Return the last-modified timestamp of the data.
+ /// If the special s_now value is returned, the current time should be
+ /// used instead.
+ virtual const timeval& getModTime() const = 0;
+
/// Returns the number of bytes available via readData()
///
/// \invariant getSize() is stable throughout the lifetime
virtual void readData(
zsize_t offset, zsize_t bytes, uint8_t* dest
) const = 0;
+
};
/// \brief FileReader is a file-based implementation of the Reader
/// Inherited from Reader
virtual const std::string& getSourceName() const;
+ /// Inherited from Reader
+ virtual const timeval& getModTime() const;
+
/// Inherited from Reader
virtual zsize_t getSize() const;
///
/// \param createPermissions The permissions set on the file if it is to
/// be created.
- FileWriter(const std::string& filename, mode_t createPermissions);
+ ///
+ /// \param modTime Set a specific modification time on the created file.
+ /// If the special s_now value is provided, the current time will be
+ /// used.
+ ///
+ FileWriter(
+ const std::string& filename,
+ mode_t createPermissions = 0664,
+ const timeval& modTime = s_now);
/// Write data to the supplied file.
///
/// getUncompressedSize() will return -1 of the FileSize capability
/// bit of the container is false.
virtual zsize_t getUncompressedSize() const = 0;
+
+ /// Return the modification time of the original file
+ virtual const timeval& getModificationTime() const = 0;
};
/// \typedef CompressedFilePtr
/// A shared pointer to a CompressedFile