Created
October 23, 2013 14:00
-
-
Save rjw57/7119323 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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