diff options
author | portix <portix@gmx.net> | 2013-05-16 12:34:44 +0200 |
---|---|---|
committer | portix <portix@gmx.net> | 2013-05-16 12:34:44 +0200 |
commit | 1af3ac44e45988340c7393589733cc4b1dbdc59b (patch) | |
tree | b3ed23544e4e75967d69bf43eb299fad1cb3e419 | |
parent | 71de3175729dfd1a486983b33b599397842ba909 (diff) | |
download | dwb-1af3ac44e45988340c7393589733cc4b1dbdc59b.zip |
Implementing exar_extract, use unsigned char for file data
-rw-r--r-- | tools/exar/exar.c | 264 | ||||
-rw-r--r-- | tools/exar/exar.h | 30 | ||||
-rw-r--r-- | tools/exar/main.c | 43 |
3 files changed, 223 insertions, 114 deletions
diff --git a/tools/exar/exar.c b/tools/exar/exar.c index 1ad73477..fe989741 100644 --- a/tools/exar/exar.c +++ b/tools/exar/exar.c @@ -23,6 +23,7 @@ #include <stdarg.h> #include <unistd.h> #include <ftw.h> +#include <assert.h> #include "exar.h" #define VERSION_BASE "exar-" @@ -42,21 +43,121 @@ #define DIR_FLAG (100) #define FILE_FLAG (102) -#define LOG(level, ...) do { if (s_verbose & EXAR_VERBOSE_L##level) \ +#define LOG(level, ...) do { if (s_verbose & EXAR_VERBOSE_L##level) { \ fprintf(stderr, "exar-log%d: ", level); \ - fprintf(stderr, __VA_ARGS__); } while(0) + fprintf(stderr, __VA_ARGS__); } } while(0) static size_t s_offset; static FILE *s_out; static unsigned char s_verbose = 0; +static void * +xmalloc(size_t size) +{ + void *ret = malloc(size); + if (ret == NULL) + { + fprintf(stderr, "Cannot malloc %lu bytes\n", size); + exit(EXIT_FAILURE); + } + return ret; +} + +static size_t +get_offset(char *buffer, size_t n, const char *path, int *end) +{ + const char *tmp = path, *slash; + size_t len = strlen(path); + size_t offset = 0; + int i=0; + + // strip trailing '/' + while (tmp[len-1] == '/') + len--; + strncpy(buffer, path, len); + + // get base name offset + slash = strrchr(buffer, '/'); + if (slash != NULL) + offset = slash - buffer + 1; + for (tmp = path + s_offset; *tmp && *tmp != '/'; i++, tmp++) + buffer[i] = *tmp; + if (end != NULL) + *end = i; + return offset; +} + +static int +check_version(FILE *f, int verbose) +{ + unsigned char version[SZ_VERSION] = {0}, orig_version[SZ_VERSION] = {0}; + LOG(2, "Reading version header\n"); + if (fread(version, 1, sizeof(version), f) != sizeof(version)) + { + if (verbose) + fprintf(stderr, "Not an exar file?\n"); + return -1; + } + memcpy(orig_version, VERSION, sizeof(orig_version)); + LOG(2, "Checking filetype\n"); + if (strncmp((char*)version, VERSION_BASE, 5)) + { + if (verbose) + fprintf(stderr, "Not an exar file?\n"); + return -1; + } + + LOG(1, "Found version %s\n", version); + if (memcmp(version, orig_version, SZ_VERSION)) + { + if (verbose) + fprintf(stderr, "Incompatible version number\n"); + return -1; + } + return 0; +} +static char +get_file_header(FILE *f, char *name, char *flag, size_t *size) +{ + char *endptr; + char fsize[SZ_SIZE]; + size_t fs; + *size = 0; + if (fread(name, 1, SZ_NAME, f) != SZ_NAME) + return -1; + if (fread(flag, 1, SZ_DFLAG, f) != SZ_DFLAG) + return -1; + if (fread(fsize, 1, SZ_SIZE, f) != SZ_SIZE) + return -1; + name[SZ_NAME-1] = 0; + fsize[SZ_SIZE-1] = 0; + if (*flag != DIR_FLAG && *flag != FILE_FLAG) + { + LOG(1, "No file flag found for %s\n", name); + fprintf(stderr, "The archive seems to be corrupted%s", "\n"); + return -1; + } + if (*flag == FILE_FLAG) + { + fs = strtoul(fsize, &endptr, 8); + if (*endptr) + { + LOG(1, "Cannot determine file size for %s\n", name); + fprintf(stderr, "The archive seems to be corrupted%s", "\n"); + return -1; + } + *size = fs; + } + return 0; +} + static int pack(const char *fpath, const struct stat *st, int tf) { (void)tf; char buffer[HDR_END] = {0}; - char rbuf[32]; + unsigned char rbuf[32]; size_t r; FILE *f = NULL; const char *stripped = &fpath[s_offset]; @@ -74,7 +175,7 @@ pack(const char *fpath, const struct stat *st, int tf) f = fopen(fpath, "r"); if (f == NULL) { - perror("fopen"); + perror(fpath); return 0; } } @@ -100,34 +201,11 @@ pack(const char *fpath, const struct stat *st, int tf) return 0; } -size_t -get_offset(char *buffer, size_t n, const char *path, int *end) -{ - const char *tmp = path, *slash; - size_t len = strlen(path); - size_t offset = 0; - int i=0; - - // strip trailing '/' - while (tmp[len-1] == '/') - len--; - strncpy(buffer, path, len); - - // get base name offset - slash = strrchr(buffer, '/'); - if (slash != NULL) - offset = slash - buffer + 1; - for (tmp = path + s_offset; *tmp && *tmp != '/'; i++, tmp++) - buffer[i] = *tmp; - if (end != NULL) - *end = i; - return offset; -} - - int exar_pack(const char *path) { + assert(path != NULL); + int ret; unsigned char version[SZ_VERSION] = {0}; char buffer[512]; @@ -140,7 +218,7 @@ exar_pack(const char *path) LOG(3, "Opening %s for writing\n", buffer); if ((s_out = fopen(buffer, "w")) == NULL) { - perror("fopen"); + perror(buffer); return -1; } @@ -155,48 +233,22 @@ exar_pack(const char *path) fclose(s_out); return ret; } -static int -check_version(FILE *f, int verbose) -{ - unsigned char version[SZ_VERSION] = {0}, orig_version[SZ_VERSION] = {0}; - LOG(2, "Reading version header\n"); - if (fread(version, 1, sizeof(version), f) != sizeof(version)) - { - if (verbose) - fprintf(stderr, "Not an exar file?\n"); - return -1; - } - memcpy(orig_version, VERSION, sizeof(orig_version)); - LOG(2, "Checking filetype\n"); - if (strncmp((char*)version, VERSION_BASE, 5)) - { - if (verbose) - fprintf(stderr, "Not an exar file?\n"); - return -1; - } - LOG(1, "Found version %s\n", version); - if (memcmp(version, orig_version, SZ_VERSION)) - { - if (verbose) - fprintf(stderr, "Incompatible version number\n"); - return -1; - } - return 0; -} int exar_unpack(const char *path, const char *dest) { + assert(path != NULL); + int ret = -1; - char name[SZ_NAME], size[SZ_SIZE], flag, rbuf; + char name[SZ_NAME], flag; + unsigned char rbuf; size_t fs; FILE *of, *f = NULL; - char *endptr; LOG(3, "Opening %s for reading\n", path); if ((f = fopen(path, "r")) == NULL) { - fprintf(stderr, "Cannot open %s\n", path); + perror(path); return -1; } if (check_version(f, 1) != 0) @@ -214,20 +266,8 @@ exar_unpack(const char *path, const char *dest) while (1) { - if (fread(name, 1, SZ_NAME, f) != SZ_NAME) - break; - if (fread(&flag, 1, SZ_DFLAG, f) != SZ_DFLAG) - break; - if (fread(size, 1, SZ_SIZE, f) != SZ_SIZE) + if (get_file_header(f, name, &flag, &fs) != 0) break; - name[SZ_NAME-1] = 0; - size[SZ_SIZE-1] = 0; - if (flag != DIR_FLAG && flag != FILE_FLAG) - { - LOG(1, "No file flag found for %s\n", name); - fprintf(stderr, "The archive seems to be corrupted%s", "\n"); - goto error_out; - } if (flag == DIR_FLAG) { LOG(1, "Creating directory %s\n", name); @@ -237,19 +277,11 @@ exar_unpack(const char *path, const char *dest) { LOG(1, "Unpacking %s\n", name); - fs = strtoul(size, &endptr, 8); - if (*endptr) - { - LOG(1, "Cannot determine file size for %s\n", name); - fprintf(stderr, "The archive seems to be corrupted%s", "\n"); - goto error_out; - } - LOG(3, "Opening %s for writing\n", name); of = fopen(name, "w"); if (of == NULL) { - perror("fopen"); + perror(name); goto error_out; } @@ -275,22 +307,24 @@ error_out: int exar_cat(const char *file1, const char *file2) { + assert(file1 != NULL && file2 != NULL); + int ret = -1; size_t r; FILE *f1 = NULL, *f2 = NULL; - char buffer[64]; + unsigned char buffer[64]; char offset_buffer[512]; LOG(3, "Opening file %s for writing\n", file1); if ((f1 = fopen(file1, "a")) == NULL) { - perror("fopen"); + perror(file1); goto error_out; } LOG(3, "Opening file %s for reading\n", file2); if ((f2 = fopen(file2, "r")) == NULL) { - perror("fopen"); + perror(file2); goto error_out; } if (check_version(f2, 0) == 0) @@ -322,6 +356,64 @@ error_out: } return ret; } +unsigned char * +exar_extract(const char *archive, const char *file, size_t *s) +{ + assert(archive != NULL && file != NULL); + + char name[SZ_NAME] = {0}, flag = 0; + size_t fs = 0; + FILE *f = NULL; + unsigned char *ret = NULL; + *s = 0; + + LOG(3, "Opening file %s for reading\n", archive); + if ((f = fopen(archive, "r")) == NULL) + { + perror(archive); + return NULL; + } + if (check_version(f, 1) != 0) + goto finish; + while (get_file_header(f, name, &flag, &fs) == 0) + { + if (flag == FILE_FLAG) + { + if (strcmp(file, name) == 0) + { + ret = xmalloc(fs); + LOG(3, "Reading %s\n", name); + if (fread(ret, 1, fs, f) != fs) + { + fprintf(stderr, "Failed to read %s\n", name); + *s = -1; + ret = NULL; + } + else + *s = fs; + goto finish; + } + else + { + LOG(3, "Skipping %s\n", name); + fseek(f, fs, SEEK_CUR); + } + } + else if (flag == DIR_FLAG && strcmp(file, name) == 0) + { + fprintf(stderr, "%s is a directory, only regular files can be extracted\n", file); + goto finish; + } + } + fprintf(stderr, "File %s was not found in %s\n", file, archive); +finish: + if (f != NULL) + { + LOG(3, "Closing %s\n", archive); + fclose(f); + } + return ret; +} void exar_verbose(unsigned char v) { diff --git a/tools/exar/exar.h b/tools/exar/exar.h index d6e34426..5369a7c9 100644 --- a/tools/exar/exar.h +++ b/tools/exar/exar.h @@ -45,7 +45,8 @@ enum { #define EXAR_VERBOSE_MASK (0x7) /* - * Set verbosity flags, exar will be most verbose if all flags are set + * Set verbosity flags, exar will be most verbose if all flags are set, log + * messages are printed to stderr. * @v_flags */ void @@ -61,6 +62,16 @@ exar_verbose(unsigned char v_flags); int exar_pack(const char *path); +/* + * Unpacks a file + * @path: Path to the extension archive + * @dest: Destination directory or NULL for current directory + * + * @returns 0 on success and -1 on error + * */ +int +exar_unpack(const char *path, const char *dest); + /* * Concatenates two archives or an archive and a file or directory * @file1: The archive to append @@ -71,14 +82,15 @@ exar_pack(const char *path); int exar_cat(const char *file1, const char *file2); -/* - * Unpacks a file - * @path: Path to the extension archive - * @dest: Destination directory or NULL for current directory +/* + * Extracts a file from an extension archive + * @archive The archive + * @file The path of the file in the archive + * @size Return location for the size, if an error occurs size will be set to -1 * - * @returns 0 on success and -1 on error + * @returns A newly allocated char buffer with the file content or NULL if an error + * occured or the file was not found int the archive * */ -int -exar_unpack(const char *path, const char *dest); - +unsigned char * +exar_extract(const char *archive, const char *file, size_t *size); #endif diff --git a/tools/exar/main.c b/tools/exar/main.c index 69069f0f..b6320e1d 100644 --- a/tools/exar/main.c +++ b/tools/exar/main.c @@ -25,6 +25,7 @@ enum { FLAG_P = 1<<3, FLAG_U = 1<<4, FLAG_C = 1<<5, + FLAG_E = 1<<6, }; #ifndef MIN #define MIN(X, Y) ((X) > (Y) ? (Y) : (X)) @@ -32,6 +33,9 @@ enum { #ifndef MAX #define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) #endif + +#define OP_FLAG(flag) ((flag) & ((FLAG_P|FLAG_U|FLAG_C|FLAG_E)^(flag))) +#define CHECK_FLAG(x, flag) (((x) & (flag)) && !((x) & ( (FLAG_P|FLAG_U|FLAG_C|FLAG_E)^(flag) ) )) void help(int ret) { @@ -41,6 +45,9 @@ help(int ret) " h : Print this help and exit.\n" " c[v] archive file : Concatenates a file, directory or archive to \n" " an existing archive.\n" + " e[v] archive file : Extracts an file from an archive and writes the content\n" + " to stdout, the archive is not modified, the file path \n" + " is the relative file path of the file in the archive.\n" " p[v] path : Pack file or directory 'path'.\n" " u[v] file [dir] : Pack 'file' to directory 'dir' or to \n" " current directory.\n" @@ -78,6 +85,9 @@ main (int argc, char **argv) case 'c' : flag |= FLAG_C; break; + case 'e' : + flag |= FLAG_E; + break; case 'v' : flag |= MAX(FLAG_V, MIN(EXAR_VERBOSE_MASK, ((flag & EXAR_VERBOSE_MASK) << 1))); break; @@ -91,26 +101,21 @@ main (int argc, char **argv) if (flag & EXAR_VERBOSE_MASK) exar_verbose(flag); - if (flag & FLAG_U) - { - if (flag & (FLAG_P | FLAG_C)) - help(EXIT_FAILURE); - else - exar_unpack(argv[2], argv[3]); - } - else if (flag & FLAG_P) + if (CHECK_FLAG(flag, FLAG_U)) + exar_unpack(argv[2], argv[3]); + else if (CHECK_FLAG(flag, FLAG_P)) + exar_pack(argv[2]); + else if (CHECK_FLAG(flag, FLAG_C) && argc > 3) + exar_cat(argv[2], argv[3]); + else if (CHECK_FLAG(flag, FLAG_E) && argc > 3) { - if (flag & (FLAG_U | FLAG_C)) - help(EXIT_FAILURE); - else - exar_pack(argv[2]); - } - else if (flag & FLAG_C) - { - if (flag & (FLAG_U | FLAG_P) || argc < 4) - help(EXIT_FAILURE); - else - exar_cat(argv[2], argv[3]); + size_t s; + unsigned char *content = exar_extract(argv[2], argv[3], &s); + if (content != NULL) + { + fwrite(content, 1, s, stdout); + free(content); + } } else help(EXIT_FAILURE); |