summaryrefslogtreecommitdiffstats
path: root/src/package.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/package.c')
-rw-r--r--src/package.c310
1 files changed, 310 insertions, 0 deletions
diff --git a/src/package.c b/src/package.c
new file mode 100644
index 0000000..168a6d8
--- /dev/null
+++ b/src/package.c
@@ -0,0 +1,310 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <unistd.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "package.h"
+
+struct package_item;
+
+struct package_item {
+ char *name;
+ char *path;
+ struct package_item *next;
+};
+
+struct package {
+ char *base;
+ int count;
+ struct package_item *items;
+ struct package_item *last;
+};
+
+static int copy(FILE *f, FILE *t, uint32_t len) {
+ static char buf[2048*4];
+
+ while (len > 0) {
+ uint32_t n;
+
+ if (feof(f))
+ return -1;
+
+ n = len > sizeof(buf) ? sizeof(buf) : len;
+
+ if (fread(buf, n, 1, f) != 1)
+ return -1;
+
+ if (fwrite(buf, n, 1, t) != 1)
+ return -1;
+
+ len -= n;
+ }
+
+ return 0;
+}
+
+static char *tmp(char *fn, int l) {
+ char *t;
+
+ if (!(t = getenv("TMPDIR")))
+ if (!(t = getenv("TEMP")))
+ if (!(t = getenv("TMP")))
+ t = "/tmp";
+
+ snprintf(fn, l, "%s/pkgXXXXXX", t);
+ return fn;
+}
+
+static struct package_item *item_new(const char *name, const char *path) {
+ struct package_item *i = NULL;
+
+ if (!(i = malloc(sizeof(struct package_item))))
+ return NULL;
+
+ memset(i, 0, sizeof(struct package_item));
+
+ if (!(i->name = strdup(name)))
+ goto fail;
+
+ if (!(i->path = strdup(path)))
+ goto fail;
+
+ return i;
+
+fail:
+ if(i) {
+ if (i->path)
+ free(i->path);
+ if (i->name)
+ free(i->name);
+ free(i);
+ }
+
+ return NULL;
+}
+
+struct package* package_open(const char *fn) {
+ struct package *p;
+ FILE *f = NULL;
+ char path[PATH_MAX];
+
+ if (!(p = malloc(sizeof(struct package)))) {
+ fprintf(stderr, "Failed to allocate package structure.\n");
+ return NULL;
+ }
+
+ memset(p, 0, sizeof(struct package));
+
+ tmp(path, sizeof(path));
+ if (!mkdtemp(path)) {
+ fprintf(stderr, "Failed to create temporary directory.\n");
+ goto finish;
+ }
+
+ if (!(p->base = strdup(path))) {
+ fprintf(stderr, "Failed to allocate memory.\n");
+ goto finish;
+ }
+
+ if (fn) {
+ if (!(f = fopen(fn, "r"))) {
+ fprintf(stderr, "Failed to open <%s>: %s\n", fn, strerror(errno));
+ goto finish;
+ }
+
+ for (;;) {
+ uint32_t len;
+ char name[PACKAGE_ITEM_NAME_LEN+1];
+ FILE *d = NULL;
+ struct package_item *pi;
+
+ if (!fread(name, sizeof(name)-1, 1, f))
+ break;
+
+ name[sizeof(name)-1] = 0;
+
+ if (fread(&len, sizeof(len), 1, f) != 1) {
+ fprintf(stderr, "Short read while reading length: %s\n", strerror(errno));
+ goto finish;
+ }
+
+ snprintf(path, sizeof(path), "%s/%i", p->base, p->count++);
+ if (!(d = fopen(path, "w+"))) {
+ fprintf(stderr, "Failed to open target file: %s\n", strerror(errno));
+ goto finish;
+ }
+
+ if (copy(f, d, len) < 0) {
+ fprintf(stderr, "Copy failed: %s\n", strerror(errno));
+ fclose(d);
+ unlink(path);
+ goto finish;
+ }
+
+ fclose(d);
+ if (!(pi = item_new(name, path))) {
+ unlink(path);
+ fprintf(stderr, "Failed to allocate memory.\n");
+ goto finish;
+ }
+
+ assert(!!p->last == !!p->items);
+
+ if (p->last) {
+ p->last->next = pi;
+ p->last = pi;
+ } else
+ p->items = p->last = pi;
+ }
+
+ fclose(f);
+ }
+
+ return p;
+
+finish:
+ if (f)
+ fclose(f);
+
+ if (p)
+ package_remove(p);
+
+ return NULL;
+}
+
+int package_save(struct package *p, const char *fn) {
+ FILE *f, *s = NULL;
+ int r = 0;
+ struct package_item *i;
+
+ assert(p && fn);
+
+ if (!(f = fopen(fn, "w+")))
+ return -1;
+
+ for (i = p->items; i; i = i->next) {
+ char name[PACKAGE_ITEM_NAME_LEN+1];
+ uint32_t len;
+ struct stat st;
+
+ if (!i->name || !i->path)
+ continue;
+
+ if (!(s = fopen(i->path, "r")))
+ continue;
+
+ //fprintf(stderr, "Saving %s (%s)...\n", i->name, i->path);
+
+ memset(name, 0, sizeof(name));
+ strncpy(name, i->name, sizeof(name)-1);
+
+ if (fwrite(name, sizeof(name)-1, 1, f) != 1) {
+ r = 1;
+ break;
+ }
+
+ if (fstat(fileno(s), &st) < 0) {
+ fprintf(stderr, "Could not get file size: %s\n", strerror(errno));
+ r = 1;
+ break;
+ }
+
+ len = st.st_size;
+
+ if (fwrite(&len, sizeof(len), 1, f) != 1) {
+ r = 1;
+ break;
+ }
+
+ if (copy(s, f, len) < 0) {
+ fprintf(stderr, "Could not copy file to package: %s\n", strerror(errno));
+ r = 1;
+ break;
+ }
+
+ fclose(s);
+ s = NULL;
+ }
+
+ if (s)
+ fclose(s);
+
+ fclose(f);
+
+ if (r != 0)
+ unlink(fn);
+
+ return r;
+}
+
+const char *package_get_item(struct package* p, const char *name) {
+ struct package_item *i;
+ char path[PATH_MAX];
+ assert(p && name);
+
+ for (i = p->items; i; i = i->next) {
+ if (strncmp(name, i->name, PACKAGE_ITEM_NAME_LEN) == 0)
+ return i->path;
+ }
+
+ snprintf(path, sizeof(path), "%s/%i", p->base, p->count++);
+
+ if (!(i = item_new(name, path))) {
+ unlink(path);
+ return NULL;
+ }
+
+ assert(!!p->last == !!p->items);
+
+ if (p->last) {
+ p->last->next = i;
+ p->last = i;
+ } else
+ p->items = p->last = i;
+
+ return i->path;
+}
+
+void package_remove(struct package *p) {
+ struct package_item *i, *n;
+ assert(p);
+
+ for (i = p->items; i; i = n) {
+ n = i->next;
+
+ if (i->path)
+ if (unlink(i->path))
+ if (errno != ENOENT)
+ fprintf(stderr, "Failed to remove <%s>: %s\n", i->path, strerror(errno));
+
+ free(i->path);
+ free(i->name);
+ free(i);
+ }
+
+ if (p->base) {
+ if (rmdir(p->base))
+ fprintf(stderr, "Failed to remove <%s>: %s\n", p->base, strerror(errno));
+
+ free(p->base);
+ }
+ free(p);
+}
+
+
+int package_foreach(struct package *p, int (*cb) (struct package *p, char *name, char *path, void *u), void *u) {
+ struct package_item *i;
+ assert(p);
+
+ for (i = p->items; i; i = i->next)
+ if (cb(p, i->name, i->path, u) < 0)
+ return -1;
+
+ return 0;
+}