Skip to content

Instantly share code, notes, and snippets.

@rjw57
Created October 23, 2013 14:00
Show Gist options
  • Save rjw57/7119323 to your computer and use it in GitHub Desktop.
Save rjw57/7119323 to your computer and use it in GitHub Desktop.
/* extractall.c - an example of extracting all files from a zip file in C. */
/* This file should be considered an example, and *not* an exemplar, on how to
* use libzip to extract an archive. */
/* Note: this file goto which is famously considered harmful. We use it only to
* provide poor-man's state cleanup on error and, in this example, it is safe.
* There are other ways to do this which you should consider in your
* application. We use it here because our program is entirely contained within
* one function and the amount of state we're manipulating is small. YMMV. */
/* Compile with something like:
*
* gcc -Wall -std=c99 -o extractall extractall.c -lzip
*/
/* standard library headers */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* for POSIX mkdir, open, etc */
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
/* zip file handling */
#include <zip.h>
/* constants */
#define ERR_SIZE 256
#define COPY_BUF_SIZE 2048
int main(int argc, char** argv) {
/* our program's state */
struct zip* p_zip = NULL; /* zip archive state */
zip_int64_t n_entries; /* number of entries in archive */
struct zip_file* p_file = NULL; /* state for a file within zip archive */
int file_fd = -1, bytes_read; /* state used when extracting file */
char copy_buf[COPY_BUF_SIZE]; /* temporary buffer for file contents */
/* options */
const char* password;
/* libzip error handling and reporting (actually only used for zip_open in
* this case) */
int error;
char errstr[ERR_SIZE];
/* handle command-line arguments */
if(argc < 2) {
fprintf(stderr, "syntax: %s <zipfile> [password]\n", argv[0]);
goto bail;
}
/* extract password from command-line options if specified */
password = (argc >= 3) ? argv[2] : NULL;
/* open the archive */
p_zip = zip_open(argv[1], 0, &error);
if(p_zip == NULL) {
zip_error_to_str(errstr, ERR_SIZE, error, errno);
fprintf(stderr, "%s: %s\n", argv[1], errstr);
goto bail;
}
/* set (or, perhaps, un-set) the default password for files */
if(zip_set_default_password(p_zip, password)) {
fprintf(stderr, "error setting default password: %s\n", zip_strerror(p_zip));
}
/* for each entry... */
n_entries = zip_get_num_entries(p_zip, 0);
for(zip_int64_t entry_idx=0; entry_idx < n_entries; entry_idx++) {
/* FIXME: we ignore mtime for this example. A stricter implementation may
* not do so. */
struct zip_stat file_stat;
/* get file information */
if(zip_stat_index(p_zip, entry_idx, 0, &file_stat)) {
fprintf(stderr, "error stat-ing file at index %i: %s\n",
(int)(entry_idx), zip_strerror(p_zip));
goto bail;
}
/* check which fields are valid */
if(!(file_stat.valid & ZIP_STAT_NAME)) {
fprintf(stderr, "warning: skipping entry at index %i with invalid name.\n",
(int)entry_idx);
continue;
}
/* show the user what we're doing */
printf("extracting: %s\n", file_stat.name);
/* is this a directory? */
if((file_stat.name[0] != '\0') && (file_stat.name[strlen(file_stat.name)-1] == '/')) {
/* yes, create it noting that it isn't an error if the directory already exists */
if(mkdir(file_stat.name, 0777) && (errno != EEXIST)) {
perror("error creating directory");
goto bail;
}
/* loop to the next file */
continue;
}
/* the file is not a directory if we get here */
/* try to open the file in the filesystem for writing */
if((file_fd = open(file_stat.name, O_CREAT | O_TRUNC | O_WRONLY, 0666)) == -1) {
perror("cannot open file for writing");
goto bail;
}
/* open the file in the archive */
if((p_file = zip_fopen_index(p_zip, entry_idx, 0)) == NULL) {
fprintf(stderr, "error extracting file: %s\n", zip_strerror(p_zip));
goto bail;
}
/* extract file */
do {
/* read some bytes */
if((bytes_read = zip_fread(p_file, copy_buf, COPY_BUF_SIZE)) == -1) {
fprintf(stderr, "error extracting file: %s\n", zip_strerror(p_zip));
goto bail;
}
/* if some bytes were read... */
if(bytes_read > 0) {
write(file_fd, copy_buf, bytes_read);
}
/* loop until we read no more bytes */
} while(bytes_read > 0);
/* close file in archive */
zip_fclose(p_file);
p_file = NULL;
/* close file in filesystem */
close(file_fd);
file_fd = -1;
}
/* close the archive */
if(zip_close(p_zip)) {
fprintf(stderr, "error closing archive: %s\n", zip_strerror(p_zip));
}
/* return back to OS */
return EXIT_SUCCESS;
bail:
/* jumped to if something goes wrong */
/* close any output file */
if(file_fd != -1) {
close(file_fd);
file_fd = -1;
}
/* close any archive file */
if(p_file) {
zip_fclose(p_file);
p_file = NULL;
}
/* close the zip file if it was opened */
if(p_zip)
zip_close(p_zip);
return EXIT_FAILURE;
}
/* vim:sw=2:sts=2:et
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment