zoe : The Zip-on-Executable Library
Have you ever needed to package a set of files with a binary executable, but
found yourself struggling to keep track of multiple files and directories?
Or perhaps you’ve wanted to create self-extracting archives that can be run
without needing an external program? If so, then zoe
may be just what you
need.
Intro
zoe
is a simple C library that allows you to access a zip file that
has been appended to an executable. This means that your binary can include
all the files it needs to function, without needing to rely on external files
or archives. And because the zip file is appended to the end of the
executable, it’s all contained within a single file. Underneath zoe
is the
wonderful miniz library should you
need to perform more technical zip
file work.
For my first use case, I created zoe
to help me package sound performance
and audio samples with my rototem
sound tool. Using zoe
, rototem
can now
find and read the files contained within the appended zip file, without
needing to reference any external file. This made it much easier to distribute
the rototem
, as I only needed to send a single file.
But the usefulness of zoe
doesn’t stop there. Because the zip file is attached
to the executable, you can use command-line utilities like the zip
tool to
add, remove, or update the files within the archive. This makes it easy to keep
your files up-to-date, and allows users of the binary to do so themselves.
Library overview
Here’s a run-down of the zoe
library’s functions:
char *zoe_self(char *exename);
zoe_t *zoe_open(char *zipname);
void zoe_close(zoe_t *zoe);
int zoe_files(zoe_t *zoe);
char *zoe_name_at(zoe_t *zoe, int i);
int zoe_find(zoe_t *zoe, char *name);
zoe_file_t *zoe_fopen_at(zoe_t *zoe, int i);
void zoe_fclose(zoe_file_t *zfile);
#define ZOE_FILE(z) (z->mem)
zoe_self
resolves a full pathname to the exename provided. This enableszoe
to open the attached zip file regardless of where it’s located and without troubles due to symbolic filesystem links.zoe_open
finds the appended zip file and returns azoe_t
structure, which is used for further operations.zoe_close
frees any allocations created byzoe_open
.zoe_files
returns the number of files in the appended zip file.zoe_name_at
returns the filename using an index number (0 to the number returned fromzoe_files()-1
).zoe_find
returns the index of a file with the given name, or-1
if not found.zoe_fopen_at
opens a file in the appended zip using an index number and returns azoe_file_t
structure that contains aFILE
pointer that can be used with the C buffered I/O library, along with the providedZOE_FILE
macro.zoe_fclose
closes the file opened byzoe_fopen_at
.
Usage
Using zoe
is straightforward. Simply call zoe_open
with a full path to
your executable), and zoe
will validate and open the appended zip file.
You can then use the other functions to retrieve information about the
files in the archive, or to open and read the files themselves.
Clone zoe
and build it like this
git clone https://github.com/octetta/zoe.git
pushd zoe; make; popd
Create example.c
for learning like
// example.c
#include <stdio.h>
#include "zoe/zoe.h"
int main(int argc, char *argv[]) {
zoe_t *z = zoe_open(zoe_self(argv[0]));
int n = zoe_files(z);
printf("%d files in %s\n", n, argv[0]);
for (int i=0; i<n; i++)
printf("#%d -> %s\n", i, zoe_name_at(z, i));
zoe_close(z);
return 0;
}
Build and run the example without any attached zip file like
$ gcc example.c zoe/libzoe.a -o example
$ ./example
0 files in ./example
Add a starting .zip
file like
$ echo "this is the first archive file" > first
$ zip first.zip first
adding: first (deflated 6%)
$ cat example first.zip > example.zip
$ chmod +x example.zip
$ ./example.zip
1 files in ./example.zip
#0 -> first (size:31)
Add new file to the example.zip
binary like
$ zip -A example.zip example.c
Zip entry offsets appear off by 113576 bytes - correcting...
adding: example.c (deflated 40%)
$ ./example.zip
2 files in ./example.zip
#0 -> first (size:31)
#1 -> example.c (size:358)
Update an existing file in example.zip
like
$ echo "updating the first archive file" > first
$ zip -A example.zip first
Zip entry offsets do not need adjusting
updating: first (deflated 3%)
$ ./example.zip
2 files in ./example.zip
#0 -> first (size:32)
#1 -> example.c (size:358)
Remove a file from example.zip
like
$ zip -A -d example.zip example.c
Zip entry offsets do not need adjusting
deleting: example.c
$ ./example.zip
1 files in ./example.zip
#0 -> first (size:32)
Where to find out more about zoe
zoe
is on my GitHub page at https://github.com/octetta/zoe.git .
If you want to discuss zoe
, DISCORD INFO HERE.
Wrapping up
Overall, I’m happy with the results of my work on zoe
. It’s a
lightweight and versatile library that has already made my life much easier.
If you’re looking for a way to package files with your executable I highly
recommend giving zoe
a try.