summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorportix <portix@gmx.net>2013-05-16 12:34:44 +0200
committerportix <portix@gmx.net>2013-05-16 12:34:44 +0200
commit1af3ac44e45988340c7393589733cc4b1dbdc59b (patch)
treeb3ed23544e4e75967d69bf43eb299fad1cb3e419
parent71de3175729dfd1a486983b33b599397842ba909 (diff)
downloaddwb-1af3ac44e45988340c7393589733cc4b1dbdc59b.zip
Implementing exar_extract, use unsigned char for file data
-rw-r--r--tools/exar/exar.c264
-rw-r--r--tools/exar/exar.h30
-rw-r--r--tools/exar/main.c43
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);