mirror of
https://github.com/michaelrsweet/mxml.git
synced 2024-11-24 11:25:30 +00:00
Switch to using hand-rolled ZIP container "library" for EPUB files.
This commit is contained in:
parent
7f1beff016
commit
7c2551e996
24
Makefile.in
24
Makefile.in
@ -82,7 +82,7 @@ DOCFILES = doc/mxml.html doc/mxmldoc.xsd README.md COPYING CHANGES.md
|
||||
PUBLIBOBJS = mxml-attr.o mxml-entity.o mxml-file.o mxml-get.o \
|
||||
mxml-index.o mxml-node.o mxml-search.o mxml-set.o
|
||||
LIBOBJS = $(PUBLIBOBJS) mxml-private.o mxml-string.o
|
||||
OBJS = mxmldoc.o testmxml.o $(LIBOBJS)
|
||||
OBJS = mxmldoc.o testmxml.o zipc.o $(LIBOBJS)
|
||||
ALLTARGETS = $(LIBMXML) mxmldoc testmxml mxml.xml
|
||||
CROSSTARGETS = $(LIBMXML) mxmldoc
|
||||
TARGETS = $(@TARGETS@)
|
||||
@ -293,15 +293,16 @@ libmxml.1.dylib: $(LIBOBJS)
|
||||
# mxmldoc
|
||||
#
|
||||
|
||||
mxmldoc: $(LIBMXML) mxmldoc.o
|
||||
mxmldoc: $(LIBMXML) mxmldoc.o zipc.o
|
||||
echo Linking $@...
|
||||
$(CC) -L. $(LDFLAGS) -o $@ mxmldoc.o -lmxml $(LIBS)
|
||||
$(CC) -L. $(LDFLAGS) -o $@ mxmldoc.o zipc.o -lmxml $(LIBS)
|
||||
|
||||
mxmldoc-static: libmxml.a mxmldoc.o
|
||||
echo Linking $@...
|
||||
$(CC) $(LDFLAGS) -o $@ mxmldoc.o libmxml.a $(LIBS)
|
||||
$(CC) $(LDFLAGS) -o $@ mxmldoc.o zipc.o libmxml.a $(LIBS)
|
||||
|
||||
mxmldoc.o: mxml.h
|
||||
mxmldoc.o: mxml.h zipc.h
|
||||
zipc.o: zipc.h
|
||||
|
||||
|
||||
#
|
||||
@ -347,16 +348,23 @@ testmxml.o: mxml.h
|
||||
mxml.xml: mxmldoc-static mxml.h $(PUBLIBOBJS:.o=.c)
|
||||
echo Generating API documentation...
|
||||
$(RM) mxml.xml
|
||||
./mxmldoc-static --header doc/reference.heading mxml.xml mxml.h $(PUBLIBOBJS:.o=.c) >doc/reference.html
|
||||
./mxmldoc-static --header doc/reference.heading \
|
||||
--docversion @VERSION@ --author "Michael R Sweet" \
|
||||
--copyright "Copyright 2003-2017, All Rights Reserved." \
|
||||
mxml.xml mxml.h $(PUBLIBOBJS:.o=.c) >doc/reference.html
|
||||
./mxmldoc-static --man mxml --title "Mini-XML API" \
|
||||
--intro doc/intro.man --footer doc/footer.man \
|
||||
--author "Michael R Sweet" \
|
||||
--copyright "Copyright 2003-2017, All Rights Reserved." \
|
||||
mxml.xml >doc/mxml.man
|
||||
if test "x`uname`" = xDarwin; then \
|
||||
./mxmldoc-static --docset org.minixml.docset \
|
||||
--docversion @VERSION@ --feedname minixml.org \
|
||||
--feedurl http://www.minixml.org/org.minixml.atom \
|
||||
--header doc/docset.header --intro doc/docset.intro \
|
||||
--css doc/docset.css --title "Mini-XML API Reference" \
|
||||
--css doc/docset.css --author "Michael R Sweet" \
|
||||
--copyright "Copyright 2003-2017, All Rights Reserved." \
|
||||
--title "Mini-XML API Reference" \
|
||||
mxml.xml || exit 1; \
|
||||
$(RM) org.minixml.atom; \
|
||||
xcrun docsetutil package --output org.minixml.xar \
|
||||
@ -374,6 +382,8 @@ mxml.epub: mxml.xml
|
||||
echo Generating EPUB API documentation...
|
||||
./mxmldoc-static --header doc/docset.header \
|
||||
--intro doc/docset.intro --css doc/docset.css \
|
||||
--docversion @VERSION@ --author "Michael R Sweet" \
|
||||
--copyright "Copyright 2003-2017, All Rights Reserved." \
|
||||
--title "Mini-XML API Reference" --epub mxml.epub mxml.xml
|
||||
|
||||
|
||||
|
133
configure
vendored
133
configure
vendored
@ -3963,7 +3963,75 @@ if test $ac_cv_c_long_long = yes; then
|
||||
|
||||
fi
|
||||
|
||||
ac_ext=c
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gzgets" >&5
|
||||
$as_echo_n "checking for library containing gzgets... " >&6; }
|
||||
if ${ac_cv_search_gzgets+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
ac_func_search_save_LIBS=$LIBS
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
/* Override any GCC internal prototype to avoid an error.
|
||||
Use char because int might match the return type of a GCC
|
||||
builtin and then its argument prototype would still apply. */
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
char gzgets ();
|
||||
int
|
||||
main ()
|
||||
{
|
||||
return gzgets ();
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
for ac_lib in '' z; do
|
||||
if test -z "$ac_lib"; then
|
||||
ac_res="none required"
|
||||
else
|
||||
ac_res=-l$ac_lib
|
||||
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
|
||||
fi
|
||||
if ac_fn_c_try_link "$LINENO"; then :
|
||||
ac_cv_search_gzgets=$ac_res
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext \
|
||||
conftest$ac_exeext
|
||||
if ${ac_cv_search_gzgets+:} false; then :
|
||||
break
|
||||
fi
|
||||
done
|
||||
if ${ac_cv_search_gzgets+:} false; then :
|
||||
|
||||
else
|
||||
ac_cv_search_gzgets=no
|
||||
fi
|
||||
rm conftest.$ac_ext
|
||||
LIBS=$ac_func_search_save_LIBS
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gzgets" >&5
|
||||
$as_echo "$ac_cv_search_gzgets" >&6; }
|
||||
ac_res=$ac_cv_search_gzgets
|
||||
if test "$ac_res" != no; then :
|
||||
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
|
||||
LIBS="-lz $LIBS"
|
||||
fi
|
||||
|
||||
|
||||
# Check whether --enable-threads was given.
|
||||
if test "${enable_threads+set}" = set; then :
|
||||
enableval=$enable_threads;
|
||||
fi
|
||||
|
||||
|
||||
have_pthread=no
|
||||
PTHREAD_FLAGS=""
|
||||
PTHREAD_LIBS=""
|
||||
|
||||
if test "x$enable_threads" != xno; then
|
||||
ac_ext=c
|
||||
ac_cpp='$CPP $CPPFLAGS'
|
||||
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
|
||||
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
|
||||
@ -4360,68 +4428,7 @@ fi
|
||||
done
|
||||
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for archive_write_new in -larchive" >&5
|
||||
$as_echo_n "checking for archive_write_new in -larchive... " >&6; }
|
||||
if ${ac_cv_lib_archive_archive_write_new+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
ac_check_lib_save_LIBS=$LIBS
|
||||
LIBS="-larchive $LIBS"
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
/* Override any GCC internal prototype to avoid an error.
|
||||
Use char because int might match the return type of a GCC
|
||||
builtin and then its argument prototype would still apply. */
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
char archive_write_new ();
|
||||
int
|
||||
main ()
|
||||
{
|
||||
return archive_write_new ();
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_c_try_link "$LINENO"; then :
|
||||
ac_cv_lib_archive_archive_write_new=yes
|
||||
else
|
||||
ac_cv_lib_archive_archive_write_new=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext \
|
||||
conftest$ac_exeext conftest.$ac_ext
|
||||
LIBS=$ac_check_lib_save_LIBS
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_archive_archive_write_new" >&5
|
||||
$as_echo "$ac_cv_lib_archive_archive_write_new" >&6; }
|
||||
if test "x$ac_cv_lib_archive_archive_write_new" = xyes; then :
|
||||
|
||||
LIBS="-larchive $LIBS"
|
||||
ac_fn_c_check_header_mongrel "$LINENO" "archive.h" "ac_cv_header_archive_h" "$ac_includes_default"
|
||||
if test "x$ac_cv_header_archive_h" = xyes; then :
|
||||
$as_echo "#define HAVE_ARCHIVE_H 1" >>confdefs.h
|
||||
|
||||
fi
|
||||
|
||||
|
||||
|
||||
fi
|
||||
|
||||
|
||||
# Check whether --enable-threads was given.
|
||||
if test "${enable_threads+set}" = set; then :
|
||||
enableval=$enable_threads;
|
||||
fi
|
||||
|
||||
|
||||
have_pthread=no
|
||||
PTHREAD_FLAGS=""
|
||||
PTHREAD_LIBS=""
|
||||
|
||||
if test "x$enable_threads" != xno; then
|
||||
ac_fn_c_check_header_mongrel "$LINENO" "pthread.h" "ac_cv_header_pthread_h" "$ac_includes_default"
|
||||
ac_fn_c_check_header_mongrel "$LINENO" "pthread.h" "ac_cv_header_pthread_h" "$ac_includes_default"
|
||||
if test "x$ac_cv_header_pthread_h" = xyes; then :
|
||||
$as_echo "#define HAVE_PTHREAD_H 1" >>confdefs.h
|
||||
|
||||
|
@ -116,11 +116,8 @@ if test $ac_cv_c_long_long = yes; then
|
||||
AC_DEFINE(HAVE_LONG_LONG)
|
||||
fi
|
||||
|
||||
dnl EPUB support (via libarchive support for writing ZIP files...)
|
||||
AC_CHECK_LIB(archive,archive_write_new,[
|
||||
LIBS="-larchive $LIBS"
|
||||
AC_CHECK_HEADER(archive.h, AC_DEFINE(HAVE_ARCHIVE_H))
|
||||
])
|
||||
dnl EPUB support (via libz and zipc)
|
||||
AC_SEARCH_LIBS(gzgets,z,[LIBS="-lz $LIBS"])
|
||||
|
||||
dnl Threading support
|
||||
AC_ARG_ENABLE(threads, [ --enable-threads enable multi-threading support])
|
||||
|
@ -1,4 +1,4 @@
|
||||
.TH mxml 3 "Mini-XML API" "04/01/17" "Mini-XML API"
|
||||
.TH mxml 3 "Mini-XML API" "04/04/17" "Mini-XML API"
|
||||
.SH NAME
|
||||
mxml \- Mini-XML API
|
||||
.SH INCLUDE FILE
|
||||
|
109
mxmldoc.c
109
mxmldoc.c
@ -20,6 +20,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "mxml.h"
|
||||
#include "zipc.h"
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
#ifndef WIN32
|
||||
@ -32,14 +33,6 @@
|
||||
extern char **environ;
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
#ifdef HAVE_ARCHIVE_H
|
||||
# include <archive.h>
|
||||
# include <archive_entry.h>
|
||||
#elif defined(__APPLE__) /* Use archive 2.8.3 headers for macOS */
|
||||
# include "xcode/archive.h"
|
||||
# include "xcode/archive_entry.h"
|
||||
#endif /* HAVE_ARCHIVE_H */
|
||||
|
||||
|
||||
/*
|
||||
* This program scans source and header files and produces public API
|
||||
@ -608,12 +601,7 @@ main(int argc, /* I - Number of command-line args */
|
||||
* Write EPUB (XHTML) documentation...
|
||||
*/
|
||||
|
||||
#if defined(HAVE_ARCHIVE_H) || defined(__APPLE__)
|
||||
write_epub(section, title ? title : "Documentation", author ? author : "Unknown", copyright ? copyright : "Unknown", docversion ? docversion : "0.0", footerfile, headerfile, introfile, cssfile, epubfile, mxmldoc);
|
||||
#else
|
||||
fputs("mxmldoc: EPUB support not compiled in.\n", stderr);
|
||||
return (1);
|
||||
#endif /* HAVE_ARCHIVE_H || __APPLE__ */
|
||||
break;
|
||||
|
||||
case OUTPUT_HTML :
|
||||
@ -1089,6 +1077,9 @@ epub_ws_cb(mxml_node_t *node, /* I - Element node */
|
||||
switch (where)
|
||||
{
|
||||
case MXML_WS_BEFORE_CLOSE :
|
||||
if (node->child && node->child->type != MXML_ELEMENT)
|
||||
return (NULL);
|
||||
|
||||
for (depth = -4; node; node = node->parent, depth += 2);
|
||||
if (depth > 40)
|
||||
return (spaces);
|
||||
@ -1111,6 +1102,9 @@ epub_ws_cb(mxml_node_t *node, /* I - Element node */
|
||||
|
||||
default :
|
||||
case MXML_WS_AFTER_OPEN :
|
||||
if (node->child && node->child->type != MXML_ELEMENT)
|
||||
return (NULL);
|
||||
|
||||
return ("\n");
|
||||
}
|
||||
}
|
||||
@ -3396,7 +3390,6 @@ write_element(FILE *out, /* I - Output file */
|
||||
}
|
||||
|
||||
|
||||
#if defined(HAVE_ARCHIVE_H) || defined(__APPLE__)
|
||||
/*
|
||||
* 'write_epub()' - Write documentation as an EPUB file.
|
||||
*/
|
||||
@ -3425,11 +3418,10 @@ write_epub(const char *section, /* I - Section */
|
||||
*defval; /* Default value */
|
||||
char epubbase[256], /* Base name of EPUB file (identifier) */
|
||||
*epubptr; /* Pointer into base name */
|
||||
struct archive *epub; /* EPUB archive */
|
||||
struct archive_entry *entry; /* EPUB entry */
|
||||
zipc_t *epub; /* EPUB ZIP container */
|
||||
zipc_file_t *epubf; /* File in EPUB ZIP container */
|
||||
char xhtmlfile[1024], /* XHTML output filename */
|
||||
*xhtmlptr; /* Pointer into output filename */
|
||||
struct stat xhtmlinfo; /* XHTML output file information */
|
||||
mxml_node_t *content_opf, /* content.opf file */
|
||||
*package, /* package node */
|
||||
*metadata, /* metadata node */
|
||||
@ -3451,7 +3443,6 @@ write_epub(const char *section, /* I - Section */
|
||||
*navLabel; /* navLabel node */
|
||||
char *toc_ncx_string; /* toc.ncx file as a string */
|
||||
char toc_xhtmlfile[1024]; /* XHTML file for table-of-contents */
|
||||
struct stat toc_xhtmlinfo; /* XHTML file info */
|
||||
int toc_level; /* Current table-of-contents level */
|
||||
static const char *mimetype = /* mimetype file as a string */
|
||||
"application/epub+zip";
|
||||
@ -3956,110 +3947,52 @@ write_epub(const char *section, /* I - Section */
|
||||
* Make the EPUB archive...
|
||||
*/
|
||||
|
||||
epub = archive_write_new();
|
||||
|
||||
archive_write_set_format_zip(epub);
|
||||
archive_write_set_options(epub, "!experimental,!zip64");
|
||||
archive_write_open_filename(epub, epubfile);
|
||||
epub = zipcOpen(epubfile, "w");
|
||||
|
||||
/* mimetype file */
|
||||
entry = archive_entry_new();
|
||||
archive_entry_set_pathname(entry, "mimetype");
|
||||
archive_entry_set_size(entry, strlen(mimetype));
|
||||
archive_entry_set_filetype(entry, AE_IFREG);
|
||||
archive_write_header(epub, entry);
|
||||
archive_write_data(epub, mimetype, strlen(mimetype));
|
||||
archive_entry_free(entry);
|
||||
|
||||
/* META-INF directory */
|
||||
entry = archive_entry_new();
|
||||
archive_entry_set_pathname(entry, "META-INF");
|
||||
archive_entry_set_size(entry, 0);
|
||||
archive_entry_set_filetype(entry, AE_IFDIR);
|
||||
archive_write_header(epub, entry);
|
||||
archive_entry_free(entry);
|
||||
zipcCreateFileWithString(epub, "mimetype", mimetype);
|
||||
|
||||
/* META-INF/container.xml file */
|
||||
entry = archive_entry_new();
|
||||
archive_entry_set_pathname(entry, "META-INF/container.xml");
|
||||
archive_entry_set_size(entry, strlen(container_xml));
|
||||
archive_entry_set_filetype(entry, AE_IFREG);
|
||||
archive_write_header(epub, entry);
|
||||
archive_write_data(epub, container_xml, strlen(container_xml));
|
||||
archive_entry_free(entry);
|
||||
|
||||
/* OEBPS directory */
|
||||
entry = archive_entry_new();
|
||||
archive_entry_set_pathname(entry, "OEBPS");
|
||||
archive_entry_set_size(entry, 0);
|
||||
archive_entry_set_filetype(entry, AE_IFDIR);
|
||||
archive_write_header(epub, entry);
|
||||
archive_entry_free(entry);
|
||||
zipcCreateFileWithString(epub, "META-INF/container.xml", container_xml);
|
||||
|
||||
/* OEBPS/body.xhtml file */
|
||||
stat(xhtmlfile, &xhtmlinfo);
|
||||
|
||||
entry = archive_entry_new();
|
||||
archive_entry_set_pathname(entry, "OEBPS/body.xhtml");
|
||||
archive_entry_set_size(entry, xhtmlinfo.st_size);
|
||||
archive_entry_set_filetype(entry, AE_IFREG);
|
||||
archive_write_header(epub, entry);
|
||||
epubf = zipcCreateFile(epub, "OEBPS/body.xhtml", 1);
|
||||
if ((fp = fopen(xhtmlfile, "r")) != NULL)
|
||||
{
|
||||
char buffer[65536]; /* Copy buffer */
|
||||
int length; /* Number of bytes */
|
||||
|
||||
while ((length = (int)fread(buffer, 1, sizeof(buffer), fp)) > 0)
|
||||
archive_write_data(epub, buffer, length);
|
||||
zipcFileWrite(epubf, buffer, length);
|
||||
|
||||
fclose(fp);
|
||||
zipcFileFinish(epubf);
|
||||
}
|
||||
unlink(xhtmlfile);
|
||||
archive_entry_free(entry);
|
||||
|
||||
/* OEBPS/content.opf file */
|
||||
entry = archive_entry_new();
|
||||
archive_entry_set_pathname(entry, "OEBPS/content.opf");
|
||||
archive_entry_set_size(entry, strlen(content_opf_string));
|
||||
archive_entry_set_filetype(entry, AE_IFREG);
|
||||
archive_write_header(epub, entry);
|
||||
archive_write_data(epub, content_opf_string, strlen(content_opf_string));
|
||||
archive_entry_free(entry);
|
||||
zipcCreateFileWithString(epub, "OEBPS/content.opf", content_opf_string);
|
||||
|
||||
/* OEBPS/toc.ncx file */
|
||||
entry = archive_entry_new();
|
||||
archive_entry_set_pathname(entry, "OEBPS/toc.ncx");
|
||||
archive_entry_set_size(entry, strlen(toc_ncx_string));
|
||||
archive_entry_set_filetype(entry, AE_IFREG);
|
||||
archive_write_header(epub, entry);
|
||||
archive_write_data(epub, toc_ncx_string, strlen(toc_ncx_string));
|
||||
archive_entry_free(entry);
|
||||
zipcCreateFileWithString(epub, "OEBPS/toc.ncx", toc_ncx_string);
|
||||
|
||||
/* OEBPS/toc.xhtml file */
|
||||
stat(toc_xhtmlfile, &toc_xhtmlinfo);
|
||||
|
||||
entry = archive_entry_new();
|
||||
archive_entry_set_pathname(entry, "OEBPS/toc.xhtml");
|
||||
archive_entry_set_size(entry, toc_xhtmlinfo.st_size);
|
||||
archive_entry_set_filetype(entry, AE_IFREG);
|
||||
archive_write_header(epub, entry);
|
||||
epubf = zipcCreateFile(epub, "OEBPS/toc.xhtml", 1);
|
||||
if ((fp = fopen(toc_xhtmlfile, "r")) != NULL)
|
||||
{
|
||||
char buffer[65536]; /* Copy buffer */
|
||||
int length; /* Number of bytes */
|
||||
|
||||
while ((length = (int)fread(buffer, 1, sizeof(buffer), fp)) > 0)
|
||||
archive_write_data(epub, buffer, length);
|
||||
zipcFileWrite(epubf, buffer, length);
|
||||
|
||||
fclose(fp);
|
||||
zipcFileFinish(epubf);
|
||||
}
|
||||
unlink(toc_xhtmlfile);
|
||||
archive_entry_free(entry);
|
||||
|
||||
archive_write_close(epub);
|
||||
archive_write_finish(epub);
|
||||
zipcClose(epub);
|
||||
}
|
||||
#endif /* HAVE_ARCHIVE_H || __APPLE__ */
|
||||
|
||||
|
||||
/*
|
||||
|
@ -40,7 +40,8 @@
|
||||
272C00431E8C6B34007EBCAC /* mxmldoc.c in Sources */ = {isa = PBXBuildFile; fileRef = 272C00331E8C6ADE007EBCAC /* mxmldoc.c */; };
|
||||
272C00501E8C6B89007EBCAC /* libmxml.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 272C00051E8C6664007EBCAC /* libmxml.a */; };
|
||||
272C00511E8C6B8E007EBCAC /* libmxml.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 272C00051E8C6664007EBCAC /* libmxml.a */; };
|
||||
272C00561E8EF972007EBCAC /* libarchive.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 272C00551E8EF972007EBCAC /* libarchive.tbd */; };
|
||||
272C00591E943266007EBCAC /* zipc.c in Sources */ = {isa = PBXBuildFile; fileRef = 272C00571E943266007EBCAC /* zipc.c */; };
|
||||
272C005B1E943423007EBCAC /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 272C005A1E943423007EBCAC /* libz.tbd */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@ -122,6 +123,9 @@
|
||||
272C00391E8C6AEB007EBCAC /* testmxml */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = testmxml; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
272C00401E8C6B1B007EBCAC /* testmxml.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = testmxml.c; path = ../testmxml.c; sourceTree = "<group>"; };
|
||||
272C00551E8EF972007EBCAC /* libarchive.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libarchive.tbd; path = usr/lib/libarchive.tbd; sourceTree = SDKROOT; };
|
||||
272C00571E943266007EBCAC /* zipc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = zipc.c; path = ../zipc.c; sourceTree = "<group>"; };
|
||||
272C00581E943266007EBCAC /* zipc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = zipc.h; path = ../zipc.h; sourceTree = "<group>"; };
|
||||
272C005A1E943423007EBCAC /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@ -136,7 +140,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
272C00561E8EF972007EBCAC /* libarchive.tbd in Frameworks */,
|
||||
272C005B1E943423007EBCAC /* libz.tbd in Frameworks */,
|
||||
272C00511E8C6B8E007EBCAC /* libmxml.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -185,8 +189,10 @@
|
||||
272C00321E8C6ABB007EBCAC /* tools */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
272C00401E8C6B1B007EBCAC /* testmxml.c */,
|
||||
272C00331E8C6ADE007EBCAC /* mxmldoc.c */,
|
||||
272C00401E8C6B1B007EBCAC /* testmxml.c */,
|
||||
272C00571E943266007EBCAC /* zipc.c */,
|
||||
272C00581E943266007EBCAC /* zipc.h */,
|
||||
);
|
||||
name = tools;
|
||||
sourceTree = "<group>";
|
||||
@ -194,6 +200,7 @@
|
||||
272C00541E8EF971007EBCAC /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
272C005A1E943423007EBCAC /* libz.tbd */,
|
||||
272C00551E8EF972007EBCAC /* libarchive.tbd */,
|
||||
);
|
||||
name = Frameworks;
|
||||
@ -352,6 +359,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
272C00431E8C6B34007EBCAC /* mxmldoc.c in Sources */,
|
||||
272C00591E943266007EBCAC /* zipc.c in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
809
zipc.c
Normal file
809
zipc.c
Normal file
@ -0,0 +1,809 @@
|
||||
/*
|
||||
* Implementation of ZIP container mini-library.
|
||||
*
|
||||
* https://github.com/michaelrsweet/zipc
|
||||
*
|
||||
* Copyright 2017 by Michael R Sweet.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Include necessary headers...
|
||||
*/
|
||||
|
||||
#include "zipc.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <zlib.h>
|
||||
|
||||
|
||||
/*
|
||||
* Local constants...
|
||||
*/
|
||||
|
||||
#define ZIPC_LOCAL_HEADER 0x04034b50 /* Start of a local file header */
|
||||
#define ZIPC_DIR_HEADER 0x02014b50 /* File header in central directory */
|
||||
#define ZIPC_DIGITAL_SIG 0x05054b50 /* Digital signature at end */
|
||||
#define ZIPC_END_RECORD 0x06054b50 /* End of central directory record */
|
||||
|
||||
#define ZIPC_MADE_BY 0x0314 /* Version made by UNIX using zip 2.0 */
|
||||
#define ZIPC_VERSION 0x0014 /* Version needed: 2.0 */
|
||||
|
||||
#define ZIPC_FLAG_NONE 0 /* No flags */
|
||||
|
||||
#define ZIPC_FLAG_CMASK 0x0006 /* Compression fields */
|
||||
#define ZIPC_FLAG_CNORMAL 0x0000 /* Normal compression */
|
||||
#define ZIPC_FLAG_CMAX 0x0002 /* Maximum compression */
|
||||
#define ZIPC_FLAG_CFAST 0x0004 /* Fast compression */
|
||||
#define ZIPC_FLAG_CSUPER 0x0006 /* Super fast compression */
|
||||
|
||||
#define ZIPC_FLAG_DATA 0x0008 /* Length and CRC-32 fields follow data */
|
||||
|
||||
#define ZIPC_COMP_STORE 0 /* No compression */
|
||||
#define ZIPC_COMP_DEFLATE 8 /* Deflate compression */
|
||||
|
||||
|
||||
/*
|
||||
* Local types...
|
||||
*/
|
||||
|
||||
struct _zipc_s
|
||||
{
|
||||
FILE *fp; /* ZIP file */
|
||||
const char *error; /* Last error message */
|
||||
int alloc_files, /* Allocated file entries in ZIP */
|
||||
num_files; /* Number of file entries in ZIP */
|
||||
zipc_file_t *files; /* File entries in ZIP */
|
||||
unsigned int modtime; /* MS-DOS modification date/time */
|
||||
z_stream stream; /* Deflate stream for current file */
|
||||
char buffer[16384]; /* Deflate output buffer */
|
||||
};
|
||||
|
||||
struct _zipc_file_s
|
||||
{
|
||||
zipc_t *zc; /* ZIP container */
|
||||
char filename[256]; /* File/directory name */
|
||||
unsigned short flags; /* General purpose bit flags */
|
||||
unsigned short method; /* Compression method */
|
||||
unsigned int crc32; /* 32-bit CRC */
|
||||
size_t compressed_size; /* Size of file (compressed) */
|
||||
size_t uncompressed_size; /* Size of file (uncompressed) */
|
||||
size_t offset; /* Offset of this entry in file */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Local functions...
|
||||
*/
|
||||
|
||||
static zipc_file_t *zipc_add_file(zipc_t *zc, const char *filename, int compression);
|
||||
static int zipc_write(zipc_t *zc, const void *buffer, size_t bytes);
|
||||
static int zipc_write_dir_header(zipc_t *zc, zipc_file_t *zf);
|
||||
static int zipc_write_local_header(zipc_t *zc, zipc_file_t *zf);
|
||||
static int zipc_write_local_trailer(zipc_t *zc, zipc_file_t *zf);
|
||||
static int zipc_write_u16(zipc_t *zc, unsigned value);
|
||||
static int zipc_write_u32(zipc_t *zc, unsigned value);
|
||||
|
||||
|
||||
/*
|
||||
* 'zipcClose()' - Close a ZIP container, writing out the central directory.
|
||||
*/
|
||||
|
||||
int /* O - 0 on success, -1 on error */
|
||||
zipcClose(zipc_t *zc) /* I - ZIP container */
|
||||
{
|
||||
size_t i; /* Looping var */
|
||||
zipc_file_t *zf; /* Current file */
|
||||
long start, end; /* Central directory offsets */
|
||||
int status = 0; /* Return status */
|
||||
|
||||
|
||||
/*
|
||||
* First write the central directory headers...
|
||||
*/
|
||||
|
||||
start = ftell(zc->fp);
|
||||
|
||||
for (i = zc->num_files, zf = zc->files; i > 0; i --, zf ++)
|
||||
status |= zipc_write_dir_header(zc, zf);
|
||||
|
||||
end = ftell(zc->fp);
|
||||
|
||||
/*
|
||||
* Then write the end of central directory block...
|
||||
*/
|
||||
|
||||
status |= zipc_write_u32(zc, ZIPC_END_RECORD);
|
||||
status |= zipc_write_u16(zc, 0); /* Disk number */
|
||||
status |= zipc_write_u16(zc, 0); /* Disk number containing directory */
|
||||
status |= zipc_write_u16(zc, zc->num_files);
|
||||
status |= zipc_write_u16(zc, zc->num_files);
|
||||
status |= zipc_write_u32(zc, end - start);
|
||||
status |= zipc_write_u32(zc, (unsigned)start);
|
||||
status |= zipc_write_u16(zc, 0); /* file comment length */
|
||||
|
||||
if (fclose(zc->fp))
|
||||
status = -1;
|
||||
|
||||
if (zc->alloc_files)
|
||||
free(zc->files);
|
||||
|
||||
free(zc);
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 'zipcCreateFile()' - Create a ZIP container file.
|
||||
*
|
||||
* The "filename" value is the path within the ZIP container. Directories are
|
||||
* separated by the forward slash ("/").
|
||||
*
|
||||
* The "compressed" value determines whether the file is compressed within the
|
||||
* container.
|
||||
*/
|
||||
|
||||
zipc_file_t * /* I - ZIP container file */
|
||||
zipcCreateFile(
|
||||
zipc_t *zc, /* I - ZIP container */
|
||||
const char *filename, /* I - Filename in container */
|
||||
int compressed) /* I - 0 for uncompressed, 1 for compressed */
|
||||
{
|
||||
/*
|
||||
* Add the file and write the header...
|
||||
*/
|
||||
|
||||
zipc_file_t *zf = zipc_add_file(zc, filename, compressed);
|
||||
/* ZIP container file */
|
||||
|
||||
|
||||
if (zipc_write_local_header(zc, zf))
|
||||
return (NULL);
|
||||
else
|
||||
return (zf);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 'zipcCreateFileWithString()' - Add a file whose contents are a string.
|
||||
*
|
||||
* This function should be used for short ZIP container files like mimetype
|
||||
* or container descriptions.
|
||||
*
|
||||
* Note: Files added this way are not compressed.
|
||||
*/
|
||||
|
||||
int /* O - 0 on success, -1 on failure */
|
||||
zipcCreateFileWithString(
|
||||
zipc_t *zc, /* I - ZIP container */
|
||||
const char *filename, /* I - Filename in container */
|
||||
const char *contents) /* I - Contents of file */
|
||||
{
|
||||
zipc_file_t *zf = zipc_add_file(zc, filename, 0);
|
||||
/* ZIP container file */
|
||||
size_t len = strlen(contents); /* Length of contents */
|
||||
int status = 0; /* Return status */
|
||||
|
||||
|
||||
if (zf)
|
||||
{
|
||||
zf->uncompressed_size = len;
|
||||
zf->compressed_size = len;
|
||||
zf->crc32 = crc32(zf->crc32, (const Bytef *)contents, len);
|
||||
|
||||
status |= zipc_write_local_header(zc, zf);
|
||||
status |= zipc_write(zc, contents, len);
|
||||
}
|
||||
else
|
||||
status = -1;
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 'zipcError()' - Return a message describing the last detected error.
|
||||
*/
|
||||
|
||||
const char * /* O - Error string or NULL */
|
||||
zipcError(zipc_t *zc) /* I - ZIP container */
|
||||
{
|
||||
return (zc ? zc->error : NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 'zipcFileFinish()' - Finish writing to a file in a ZIP container.
|
||||
*/
|
||||
|
||||
int /* O - 0 on success, -1 on error */
|
||||
zipcFileFinish(zipc_file_t *zf) /* I - ZIP container file */
|
||||
{
|
||||
int status = 0; /* Return status */
|
||||
zipc_t *zc = zf->zc; /* ZIP container */
|
||||
|
||||
|
||||
if (zf->method != ZIPC_COMP_STORE)
|
||||
{
|
||||
int zstatus; /* Deflate status */
|
||||
|
||||
while ((zstatus = deflate(&zc->stream, Z_FINISH)) != Z_STREAM_END)
|
||||
{
|
||||
if (zstatus < Z_OK && zstatus != Z_BUF_ERROR)
|
||||
{
|
||||
zc->error = "Deflate failed.";
|
||||
status = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
status |= zipc_write(zf->zc, zc->buffer, (char *)zc->stream.next_out - zc->buffer);
|
||||
zf->compressed_size += (char *)zc->stream.next_out - zc->buffer;
|
||||
|
||||
zc->stream.next_out = (Bytef *)zc->buffer;
|
||||
zc->stream.avail_out = sizeof(zc->buffer);
|
||||
}
|
||||
|
||||
if ((char *)zc->stream.next_out > zc->buffer)
|
||||
{
|
||||
status |= zipc_write(zf->zc, zc->buffer, (char *)zc->stream.next_out - zc->buffer);
|
||||
zf->compressed_size += (char *)zc->stream.next_out - zc->buffer;
|
||||
}
|
||||
|
||||
deflateEnd(&zc->stream);
|
||||
}
|
||||
|
||||
status |= zipc_write_local_trailer(zc, zf);
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 'zipcFilePrintf()' - Write a formatted string to a file.
|
||||
*
|
||||
* The "zf" value is the one returned by the @link zipc_start_file@ function
|
||||
* used to create the ZIP container file.
|
||||
*
|
||||
* The "format" value is a standard printf format string and is followed by
|
||||
* any arguments needed by the string.
|
||||
*/
|
||||
|
||||
int /* O - 0 on success, -1 on error */
|
||||
zipcFilePrintf(zipc_file_t *zf, /* I - ZIP container file */
|
||||
const char *format, /* I - Printf-style format string */
|
||||
...) /* I - Additional arguments as needed */
|
||||
{
|
||||
char buffer[8192]; /* Format buffer */
|
||||
va_list ap; /* Pointer to additional arguments */
|
||||
|
||||
|
||||
va_start(ap, format);
|
||||
if (vsnprintf(buffer, sizeof(buffer), format, ap) < 0)
|
||||
{
|
||||
zf->zc->error = strerror(errno);
|
||||
va_end(ap);
|
||||
return (-1);
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
return (zipcFileWrite(zf, buffer, strlen(buffer)));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 'zipcFilePuts()' - Write a string to a file.
|
||||
*
|
||||
* The "zf" value is the one returned by the @link zipc_start_file@ function
|
||||
* used to create the ZIP container file.
|
||||
*
|
||||
* The "s" value is literal string that is written to the file. No newline is
|
||||
* added.
|
||||
*/
|
||||
|
||||
int /* O - 0 on success, -1 on error */
|
||||
zipcFilePuts(zipc_file_t *zf, /* I - ZIP container file */
|
||||
const char *s) /* I - String to write */
|
||||
{
|
||||
return (zipcFileWrite(zf, s, strlen(s)));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 'zipcFileWrite()' - Write data to a ZIP container file.
|
||||
*
|
||||
* The "zf" value is the one returned by the @link zipc_file_start@ function
|
||||
* used to create the ZIP container file.
|
||||
*
|
||||
* The "data" value points to the bytes to be written.
|
||||
*
|
||||
* The "bytes" value specifies the number of bytes to write.
|
||||
*/
|
||||
|
||||
int /* O - 0 on success, -1 on error */
|
||||
zipcFileWrite(zipc_file_t *zf, /* I - ZIP container file */
|
||||
const void *data, /* I - Data to write */
|
||||
size_t bytes) /* I - Number of bytes to write */
|
||||
{
|
||||
int status = 0; /* Return status */
|
||||
zipc_t *zc = zf->zc; /* ZIP container */
|
||||
|
||||
|
||||
zf->uncompressed_size += bytes;
|
||||
zf->crc32 = crc32(zf->crc32, (const Bytef *)data, bytes);
|
||||
|
||||
if (zf->method == ZIPC_COMP_STORE)
|
||||
{
|
||||
/*
|
||||
* Store the contents as literal data...
|
||||
*/
|
||||
|
||||
status = zipc_write(zc, data, bytes);
|
||||
zf->compressed_size += bytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Deflate (compress) the contents...
|
||||
*/
|
||||
|
||||
int zstatus; /* Deflate status */
|
||||
|
||||
zc->stream.next_in = (Bytef *)data;
|
||||
zc->stream.avail_in = (unsigned)bytes;
|
||||
|
||||
while (zc->stream.avail_in > 0)
|
||||
{
|
||||
if (zc->stream.avail_out < (int)(sizeof(zc->buffer) / 8))
|
||||
{
|
||||
status |= zipc_write(zf->zc, zc->buffer, (char *)zc->stream.next_out - zc->buffer);
|
||||
zf->compressed_size += (char *)zc->stream.next_out - zc->buffer;
|
||||
|
||||
zc->stream.next_out = (Bytef *)zc->buffer;
|
||||
zc->stream.avail_out = sizeof(zc->buffer);
|
||||
}
|
||||
|
||||
zstatus = deflate(&zc->stream, Z_NO_FLUSH);
|
||||
|
||||
if (zstatus < Z_OK && zstatus != Z_BUF_ERROR)
|
||||
{
|
||||
zc->error = "Deflate failed.";
|
||||
status = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 'zipcFileXMLPrintf()' - Write a formatted XML string to a file.
|
||||
*
|
||||
* The "zf" value is the one returned by the @link zipc_start_file@ function
|
||||
* used to create the ZIP container file.
|
||||
*
|
||||
* The "format" value is a printf-style format string supporting "%d", "%s",
|
||||
* and "%%" and is followed by any arguments needed by the string. Strings
|
||||
* ("%s") are escaped as needed.
|
||||
*/
|
||||
|
||||
int /* O - 0 on success, -1 on error */
|
||||
zipcFileXMLPrintf(
|
||||
zipc_file_t *zf, /* I - ZIP container file */
|
||||
const char *format, /* I - Printf-style format string */
|
||||
...) /* I - Additional arguments as needed */
|
||||
{
|
||||
int status = 0; /* Return status */
|
||||
va_list ap; /* Pointer to additional arguments */
|
||||
char buffer[65536], /* Buffer */
|
||||
*bufend = buffer + sizeof(buffer) - 6,
|
||||
/* End of buffer less """ */
|
||||
*bufptr = buffer; /* Pointer into buffer */
|
||||
const char *s; /* String pointer */
|
||||
int d; /* Number */
|
||||
|
||||
|
||||
va_start(ap, format);
|
||||
|
||||
while (*format && bufptr < bufend)
|
||||
{
|
||||
if (*format == '%')
|
||||
{
|
||||
format ++;
|
||||
|
||||
switch (*format)
|
||||
{
|
||||
case '%' : /* Substitute a single % */
|
||||
format ++;
|
||||
|
||||
*bufptr++ = '%';
|
||||
break;
|
||||
|
||||
case 'd' : /* Substitute a single integer */
|
||||
format ++;
|
||||
|
||||
d = va_arg(ap, int);
|
||||
snprintf(bufptr, bufend - bufptr, "%d", d);
|
||||
bufptr += strlen(bufptr);
|
||||
break;
|
||||
|
||||
case 's' : /* Substitute a single string */
|
||||
format ++;
|
||||
|
||||
s = va_arg(ap, const char *);
|
||||
while (*s && bufptr < bufend)
|
||||
{
|
||||
switch (*s)
|
||||
{
|
||||
case '&' : /* & */
|
||||
*bufptr++ = '&';
|
||||
*bufptr++ = 'a';
|
||||
*bufptr++ = 'm';
|
||||
*bufptr++ = 'p';
|
||||
*bufptr++ = ';';
|
||||
break;
|
||||
case '<' : /* < */
|
||||
*bufptr++ = '&';
|
||||
*bufptr++ = 'l';
|
||||
*bufptr++ = 't';
|
||||
*bufptr++ = ';';
|
||||
break;
|
||||
case '>' : /* > */
|
||||
*bufptr++ = '&';
|
||||
*bufptr++ = 'g';
|
||||
*bufptr++ = 't';
|
||||
*bufptr++ = ';';
|
||||
break;
|
||||
case '\"' : /* " */
|
||||
*bufptr++ = '&';
|
||||
*bufptr++ = 'q';
|
||||
*bufptr++ = 'u';
|
||||
*bufptr++ = 'o';
|
||||
*bufptr++ = 't';
|
||||
*bufptr++ = ';';
|
||||
break;
|
||||
default :
|
||||
*bufptr++ = *s;
|
||||
break;
|
||||
}
|
||||
|
||||
s ++;
|
||||
}
|
||||
|
||||
if (*s)
|
||||
{
|
||||
format += strlen(format);
|
||||
status = -1;
|
||||
zf->zc->error = "Not enough memory to hold XML string.";
|
||||
}
|
||||
break;
|
||||
|
||||
default : /* Something else we don't support... */
|
||||
format += strlen(format);
|
||||
status = -1;
|
||||
zf->zc->error = "Unsupported format character - only %%, %d, and %s are supported.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
*bufptr++ = *format++;
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
|
||||
if (*format)
|
||||
{
|
||||
status = -1;
|
||||
zf->zc->error = "Not enough memory to hold XML string.";
|
||||
}
|
||||
|
||||
if (bufptr > buffer)
|
||||
status |= zipcFileWrite(zf, buffer, bufptr - buffer);
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 'zipcOpen()' - Open a ZIP container.
|
||||
*
|
||||
* Currently the only supported "mode" value is "w" (write).
|
||||
*/
|
||||
|
||||
zipc_t * /* O - ZIP container */
|
||||
zipcOpen(const char *filename, /* I - Filename of container */
|
||||
const char *mode) /* I - Open mode ("w") */
|
||||
{
|
||||
zipc_t *zc; /* ZIP container */
|
||||
|
||||
|
||||
/*
|
||||
* Only support write mode for now...
|
||||
*/
|
||||
|
||||
if (strcmp(mode, "w"))
|
||||
{
|
||||
errno = EINVAL;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate memory...
|
||||
*/
|
||||
|
||||
if ((zc = calloc(1, sizeof(zipc_t))) != NULL)
|
||||
{
|
||||
time_t curtime; /* Current timestamp */
|
||||
struct tm *curdate; /* Current date/time */
|
||||
|
||||
/*
|
||||
* Open the container file...
|
||||
*/
|
||||
|
||||
if ((zc->fp = fopen(filename, "wb")) == NULL)
|
||||
{
|
||||
free(zc);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the current date/time and convert it to the packed MS-DOS format:
|
||||
*
|
||||
* Bits Description
|
||||
* ------ -----------
|
||||
* 0-4 Second
|
||||
* 5-10 Minute
|
||||
* 11-15 Hour
|
||||
* 16-20 Day (1-31)
|
||||
* 21-24 Month (1-12)
|
||||
* 25-31 Years since 1980
|
||||
*/
|
||||
|
||||
curtime = time(NULL);
|
||||
curdate = gmtime(&curtime);
|
||||
|
||||
zc->modtime = curdate->tm_sec |
|
||||
(curdate->tm_min << 5) |
|
||||
(curdate->tm_hour << 11) |
|
||||
(curdate->tm_mday << 16) |
|
||||
((curdate->tm_mon + 1) << 21) |
|
||||
((curdate->tm_year - 80) << 25);
|
||||
}
|
||||
|
||||
return (zc);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 'zipc_add_file()' - Add a file to the ZIP container.
|
||||
*/
|
||||
|
||||
static zipc_file_t * /* O - New file */
|
||||
zipc_add_file(zipc_t *zc, /* I - ZIP container */
|
||||
const char *filename, /* I - Name of file in container */
|
||||
int compression) /* I - 0 = uncompressed, 1 = compressed */
|
||||
{
|
||||
zipc_file_t *temp; /* File(s) */
|
||||
|
||||
|
||||
if (zc->num_files >= zc->alloc_files)
|
||||
{
|
||||
/*
|
||||
* Allocate more files...
|
||||
*/
|
||||
|
||||
zc->alloc_files += 10;
|
||||
|
||||
if (!zc->files)
|
||||
temp = malloc(zc->alloc_files * sizeof(zipc_file_t));
|
||||
else
|
||||
temp = realloc(zc->files, zc->alloc_files * sizeof(zipc_file_t));
|
||||
|
||||
if (!temp)
|
||||
{
|
||||
zc->error = strerror(errno);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
zc->files = temp;
|
||||
}
|
||||
|
||||
temp = zc->files + zc->num_files;
|
||||
zc->num_files ++;
|
||||
|
||||
memset(temp, 0, sizeof(zipc_file_t));
|
||||
|
||||
strncpy(temp->filename, filename, sizeof(temp->filename) - 1);
|
||||
|
||||
temp->zc = zc;
|
||||
temp->crc32 = crc32(0, NULL, 0);
|
||||
temp->offset = ftell(zc->fp);
|
||||
|
||||
if (compression)
|
||||
{
|
||||
temp->flags = ZIPC_FLAG_CMAX;
|
||||
temp->method = ZIPC_COMP_DEFLATE;
|
||||
|
||||
zc->stream.zalloc = (alloc_func)0;
|
||||
zc->stream.zfree = (free_func)0;
|
||||
zc->stream.opaque = (voidpf)0;
|
||||
|
||||
deflateInit2(&zc->stream, Z_BEST_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
|
||||
|
||||
zc->stream.next_out = (Bytef *)zc->buffer;
|
||||
zc->stream.avail_out = sizeof(zc->buffer);
|
||||
}
|
||||
|
||||
return (temp);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 'zipc_write()' - Write bytes to a ZIP container.
|
||||
*/
|
||||
|
||||
static int /* I - 0 on success, -1 on error */
|
||||
zipc_write(zipc_t *zc, /* I - ZIP container */
|
||||
const void *buffer, /* I - Buffer to write */
|
||||
size_t bytes) /* I - Number of bytes */
|
||||
{
|
||||
if (fwrite(buffer, bytes, 1, zc->fp))
|
||||
return (0);
|
||||
|
||||
zc->error = strerror(ferror(zc->fp));
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 'zipc_write_dir_header()' - Write a central directory file header.
|
||||
*/
|
||||
|
||||
static int /* I - 0 on success, -1 on error */
|
||||
zipc_write_dir_header(
|
||||
zipc_t *zc, /* I - ZIP container */
|
||||
zipc_file_t *zf) /* I - ZIP container file */
|
||||
{
|
||||
int status = 0; /* Return status */
|
||||
size_t filenamelen = strlen(zf->filename);
|
||||
/* Length of filename */
|
||||
|
||||
status |= zipc_write_u32(zc, ZIPC_DIR_HEADER);
|
||||
status |= zipc_write_u16(zc, ZIPC_MADE_BY);
|
||||
status |= zipc_write_u16(zc, ZIPC_VERSION);
|
||||
status |= zipc_write_u16(zc, zf->flags);
|
||||
status |= zipc_write_u16(zc, zf->method);
|
||||
status |= zipc_write_u32(zc, zc->modtime);
|
||||
status |= zipc_write_u32(zc, zf->crc32);
|
||||
status |= zipc_write_u32(zc, zf->compressed_size);
|
||||
status |= zipc_write_u32(zc, zf->uncompressed_size);
|
||||
status |= zipc_write_u16(zc, filenamelen);
|
||||
status |= zipc_write_u16(zc, 0); /* extra field length */
|
||||
status |= zipc_write_u16(zc, 0); /* comment length */
|
||||
status |= zipc_write_u16(zc, 0); /* disk number start */
|
||||
status |= zipc_write_u16(zc, 0); /* internal file attributes */
|
||||
status |= zipc_write_u32(zc, 0); /* external file attributes */
|
||||
status |= zipc_write_u32(zc, zf->offset);
|
||||
status |= zipc_write(zc, zf->filename, filenamelen);
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 'zipc_write_local_header()' - Write a local file header.
|
||||
*/
|
||||
|
||||
static int /* I - 0 on success, -1 on error */
|
||||
zipc_write_local_header(
|
||||
zipc_t *zc, /* I - ZIP container */
|
||||
zipc_file_t *zf) /* I - ZIP container file */
|
||||
{
|
||||
int status = 0; /* Return status */
|
||||
unsigned flags = zf->flags; /* General purpose flags */
|
||||
size_t filenamelen = strlen(zf->filename);
|
||||
/* Length of filename */
|
||||
|
||||
if (zf->uncompressed_size == 0)
|
||||
flags |= ZIPC_FLAG_DATA;
|
||||
|
||||
status |= zipc_write_u32(zc, ZIPC_LOCAL_HEADER);
|
||||
status |= zipc_write_u16(zc, ZIPC_VERSION);
|
||||
status |= zipc_write_u16(zc, flags);
|
||||
status |= zipc_write_u16(zc, zf->method);
|
||||
status |= zipc_write_u32(zc, zc->modtime);
|
||||
status |= zipc_write_u32(zc, zf->uncompressed_size == 0 ? 0 : zf->crc32);
|
||||
status |= zipc_write_u32(zc, zf->compressed_size);
|
||||
status |= zipc_write_u32(zc, zf->uncompressed_size);
|
||||
status |= zipc_write_u16(zc, filenamelen);
|
||||
status |= zipc_write_u16(zc, 0); /* extra field length */
|
||||
status |= zipc_write(zc, zf->filename, filenamelen);
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 'zipc_write_local_trailer()' - Write a local file trailer.
|
||||
*/
|
||||
|
||||
static int /* I - 0 on success, -1 on error */
|
||||
zipc_write_local_trailer(
|
||||
zipc_t *zc, /* I - ZIP container */
|
||||
zipc_file_t *zf) /* I - ZIP container file */
|
||||
{
|
||||
int status = 0; /* Return status */
|
||||
|
||||
|
||||
status |= zipc_write_u32(zc, zf->crc32);
|
||||
status |= zipc_write_u32(zc, zf->compressed_size);
|
||||
status |= zipc_write_u32(zc, zf->uncompressed_size);
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 'zipc_write_u16()' - Write a 16-bit unsigned integer.
|
||||
*/
|
||||
|
||||
static int /* I - 0 on success, -1 on error */
|
||||
zipc_write_u16(zipc_t *zc, /* I - ZIP container */
|
||||
unsigned value) /* I - Value to write */
|
||||
{
|
||||
unsigned char buffer[2]; /* Buffer */
|
||||
|
||||
|
||||
buffer[0] = (unsigned char)value;
|
||||
buffer[1] = (unsigned char)(value >> 8);
|
||||
|
||||
return (zipc_write(zc, buffer, sizeof(buffer)));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 'zipc_write_u32()' - Write a 32-bit unsigned integer.
|
||||
*/
|
||||
|
||||
static int /* I - 0 on success, -1 on error */
|
||||
zipc_write_u32(zipc_t *zc, /* I - ZIP container */
|
||||
unsigned value) /* I - Value to write */
|
||||
{
|
||||
unsigned char buffer[4]; /* Buffer */
|
||||
|
||||
|
||||
buffer[0] = (unsigned char)value;
|
||||
buffer[1] = (unsigned char)(value >> 8);
|
||||
buffer[2] = (unsigned char)(value >> 16);
|
||||
buffer[3] = (unsigned char)(value >> 24);
|
||||
|
||||
return (zipc_write(zc, buffer, sizeof(buffer)));
|
||||
}
|
71
zipc.h
Normal file
71
zipc.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Header file for ZIP container mini-library.
|
||||
*
|
||||
* https://github.com/michaelrsweet/zipc
|
||||
*
|
||||
* Copyright 2017 by Michael R Sweet.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef ZIPC_H
|
||||
# define ZIPC_H
|
||||
|
||||
/*
|
||||
* Include necessary headers...
|
||||
*/
|
||||
|
||||
# include <sys/types.h>
|
||||
|
||||
|
||||
/*
|
||||
* Types...
|
||||
*/
|
||||
|
||||
typedef struct _zipc_s zipc_t; /* ZIP container */
|
||||
typedef struct _zipc_file_s zipc_file_t;/* File/directory in ZIP container */
|
||||
|
||||
|
||||
/*
|
||||
* Functions...
|
||||
*/
|
||||
|
||||
extern int zipcClose(zipc_t *zc);
|
||||
extern zipc_file_t *zipcCreateFile(zipc_t *zc, const char *filename, int compressed);
|
||||
extern int zipcCreateFileWithString(zipc_t *zc, const char *filename, const char *contents);
|
||||
extern const char *zipcError(zipc_t *zc);
|
||||
extern int zipcFileFinish(zipc_file_t *zf);
|
||||
extern int zipcFilePrintf(zipc_file_t *zf, const char *format, ...)
|
||||
# ifdef __GNUC__
|
||||
__attribute__ ((__format__ (__printf__, 2, 3)))
|
||||
# endif /* __GNUC__ */
|
||||
;
|
||||
extern int zipcFilePuts(zipc_file_t *zf, const char *s);
|
||||
extern int zipcFileWrite(zipc_file_t *zf, const void *data, size_t bytes);
|
||||
extern int zipcFileXMLPrintf(zipc_file_t *zf, const char *format, ...)
|
||||
# ifdef __GNUC__
|
||||
__attribute__ ((__format__ (__printf__, 2, 3)))
|
||||
# endif /* __GNUC__ */
|
||||
;
|
||||
extern zipc_t *zipcOpen(const char *filename, const char *mode);
|
||||
|
||||
#endif /* !ZIPC_H */
|
Loading…
Reference in New Issue
Block a user