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_selfresolves a full pathname to the exename provided. This enableszoeto open the attached zip file regardless of where it’s located and without troubles due to symbolic filesystem links.zoe_openfinds the appended zip file and returns azoe_tstructure, which is used for further operations.zoe_closefrees any allocations created byzoe_open.zoe_filesreturns the number of files in the appended zip file.zoe_name_atreturns the filename using an index number (0 to the number returned fromzoe_files()-1).zoe_findreturns the index of a file with the given name, or-1if not found.zoe_fopen_atopens a file in the appended zip using an index number and returns azoe_file_tstructure that contains aFILEpointer that can be used with the C buffered I/O library, along with the providedZOE_FILEmacro.zoe_fclosecloses 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.