summaryrefslogtreecommitdiff
path: root/gtk
diff options
context:
space:
mode:
Diffstat (limited to 'gtk')
-rw-r--r--gtk/Makefile.target5
-rw-r--r--gtk/scaffolding.c2
-rw-r--r--gtk/theme.c2
-rw-r--r--gtk/theme.h3
-rw-r--r--gtk/theme_container.c765
-rw-r--r--gtk/theme_container.h53
6 files changed, 822 insertions, 8 deletions
diff --git a/gtk/Makefile.target b/gtk/Makefile.target
index 5efe65fd8..50d2a77fd 100644
--- a/gtk/Makefile.target
+++ b/gtk/Makefile.target
@@ -179,12 +179,11 @@ endif
S_GTK := font_pango.c bitmap.c gui.c schedule.c plotters.c \
treeview.c scaffolding.c gdk.c completion.c login.c throbber.c \
selection.c history.c window.c fetch.c download.c menu.c \
- print.c search.c tabs.c theme.c toolbar.c gettext.c \
+ print.c search.c tabs.c theme.c theme_container.c toolbar.c gettext.c \
compat.c cookies.c hotlist.c viewdata.c viewsource.c \
preferences.c about.c ssl_cert.c resources.c
-S_GTK := $(addprefix gtk/,$(S_GTK)) $(addprefix utils/,container.c)
-# code in utils/container.ch is non-universal it seems
+S_GTK := $(addprefix gtk/,$(S_GTK))
# This is the final source build list
# Note this is deliberately *not* expanded here as common and image
diff --git a/gtk/scaffolding.c b/gtk/scaffolding.c
index 19d5b2e15..a9a3d0589 100644
--- a/gtk/scaffolding.c
+++ b/gtk/scaffolding.c
@@ -62,12 +62,12 @@
#include "gtk/plotters.h"
#include "gtk/print.h"
#include "gtk/search.h"
-#include "gtk/theme.h"
#include "gtk/throbber.h"
#include "gtk/toolbar.h"
#include "gtk/window.h"
#include "gtk/gdk.h"
#include "gtk/scaffolding.h"
+#include "gtk/theme.h"
#include "gtk/tabs.h"
#include "gtk/schedule.h"
#include "gtk/viewdata.h"
diff --git a/gtk/theme.c b/gtk/theme.c
index eb43fc588..9baab13ed 100644
--- a/gtk/theme.c
+++ b/gtk/theme.c
@@ -24,7 +24,6 @@
#include "utils/config.h"
#include "utils/nsoption.h"
-#include "utils/container.h"
#include "utils/log.h"
#include "utils/messages.h"
#include "utils/utils.h"
@@ -39,6 +38,7 @@
#include "gtk/scaffolding.h"
#include "gtk/menu.h"
#include "gtk/theme.h"
+#include "gtk/theme_container.h"
#include "gtk/window.h"
#include "gtk/preferences.h"
diff --git a/gtk/theme.h b/gtk/theme.h
index b3059ee93..32a7ffcdb 100644
--- a/gtk/theme.h
+++ b/gtk/theme.h
@@ -19,9 +19,6 @@
#ifndef _NETSURF_GTK_THEME_H_
#define _NETSURF_GTK_THEME_H_
-#include <gtk/gtk.h>
-#include "gtk/scaffolding.h"
-
typedef enum search_buttons {
SEARCH_BACK_BUTTON = 0,
SEARCH_FORWARD_BUTTON,
diff --git a/gtk/theme_container.c b/gtk/theme_container.c
new file mode 100644
index 000000000..3a86ea9d4
--- /dev/null
+++ b/gtk/theme_container.c
@@ -0,0 +1,765 @@
+/*
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* To build a stand-alone command-line utility to create and dismantal
+ * these theme files, build this thusly:
+ *
+ * gcc -I../ -DNSTHEME -o themetool container.c
+ *
+ * [needs a c99 compiler]
+ *
+ * then for instance to create a theme file called mythemefilename
+ * ./themetool --verbose --create -n"My theme name" mythemefilename\
+ * --author "Myname" /path/to/directory/containing/theme/files/
+ */
+
+/** \file
+ * Container format handling for themes etc. */
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+
+#include "utils/config.h"
+#include "utils/log.h"
+#include "utils/messages.h"
+#include "utils/utils.h"
+
+#include "gtk/theme_container.h"
+
+#ifdef WITH_MMAP
+#include <sys/mman.h>
+#endif
+
+#ifdef NSTHEME
+bool verbose_log = true;
+#endif
+
+struct container_dirent {
+ unsigned char filename[64];
+ u_int32_t startoffset;
+ u_int32_t len;
+ u_int32_t flags1;
+ u_int32_t flags2;
+};
+
+struct container_header {
+ u_int32_t magic; /* 0x4d54534e little endian */
+ u_int32_t parser;
+ unsigned char name[32];
+ unsigned char author[64];
+ u_int32_t diroffset;
+};
+
+struct container_ctx {
+ FILE *fh;
+ bool creating;
+ bool processed;
+ struct container_header header;
+ unsigned int entries;
+ unsigned char *data;
+ struct container_dirent *directory;
+};
+
+inline static size_t container_filelen(FILE *fd)
+{
+ long o;
+ long a;
+
+ o = ftell(fd);
+ if (o == -1) {
+ LOG("Could not get current stream position");
+ return 0;
+ }
+
+ if (fseek(fd, 0, SEEK_END) != 0) {
+ LOG("Could not get seek to end of file");
+ return 0;
+ }
+ a = ftell(fd);
+
+ if (fseek(fd, o, SEEK_SET) != 0) {
+ LOG("Could not reset seek position in file");
+ return 0;
+ }
+ if (a == -1) {
+ LOG("could not ascertain size of file in theme container; omitting");
+ return 0;
+ }
+ if (((unsigned long) a) > SIZE_MAX) {
+ LOG("overlarge file in theme container; possible truncation");
+ return SIZE_MAX;
+ }
+ return (size_t) a;
+}
+
+static void container_add_to_dir(struct container_ctx *ctx,
+ const unsigned char *entryname,
+ const u_int32_t offset,
+ const u_int32_t length)
+{
+ struct container_dirent *temp;
+ temp = realloc(ctx->directory, ctx->entries *
+ sizeof(struct container_dirent));
+ if (temp == NULL) {
+ printf("error adding entry for %s to theme container\n", entryname);
+ return;
+ }
+ ctx->entries += 1;
+ ctx->directory = temp;
+
+ snprintf((char*)ctx->directory[ctx->entries - 1].filename,
+ sizeof(ctx->directory[ctx->entries - 1].filename),
+ "%s", (char *)entryname);
+
+ ctx->directory[ctx->entries - 1].startoffset = offset;
+ ctx->directory[ctx->entries - 1].len = length;
+ ctx->directory[ctx->entries - 1].flags1 = 0;
+ ctx->directory[ctx->entries - 1].flags2 = 0;
+}
+
+struct container_ctx *container_open(const char *filename)
+{
+ size_t val;
+ struct container_ctx *ctx = calloc(sizeof(struct container_ctx), 1);
+
+ ctx->fh = fopen(filename, "rb");
+
+ if (ctx->fh == NULL) {
+ free(ctx);
+ return NULL;
+ }
+
+ /* we don't actually load any of the data (including directory)
+ * until we need to, such that _get_name and _get_author are as quick
+ * as possible. When we have, this gets set to true.
+ */
+ ctx->processed = false;
+
+ val = fread(&ctx->header.magic, 4, 1, ctx->fh);
+ if (val == 0)
+ LOG("empty read magic");
+ ctx->header.magic = ntohl(ctx->header.magic);
+
+ val = fread(&ctx->header.parser, 4, 1, ctx->fh);
+ if (val == 0)
+ LOG("empty read parser");
+ ctx->header.parser = ntohl(ctx->header.parser);
+
+ val = fread(ctx->header.name, 32, 1, ctx->fh);
+ if (val == 0)
+ LOG("empty read name");
+ val = fread(ctx->header.author, 64, 1, ctx->fh);
+ if (val == 0)
+ LOG("empty read author");
+
+ val = fread(&ctx->header.diroffset, 4, 1, ctx->fh);
+ if (val == 0)
+ LOG("empty read diroffset");
+ ctx->header.diroffset = ntohl(ctx->header.diroffset);
+
+ if (ctx->header.magic != 0x4e53544d || ctx->header.parser != 3) {
+ fclose(ctx->fh);
+ free(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+static void container_process(struct container_ctx *ctx)
+{
+ size_t val;
+ unsigned char filename[64];
+ u_int32_t start, len, flags1, flags2;
+
+#ifdef WITH_MMAP
+ ctx->data = mmap(NULL, ctx->header.diroffset, PROT_READ, MAP_PRIVATE,
+ fileno(ctx->fh), 0);
+#else
+ ctx->data = malloc(ctx->header.diroffset);
+ if (fseek(ctx->fh, 0, SEEK_SET) != 0) {
+ return;
+ }
+ val = fread(ctx->data, ctx->header.diroffset, 1, ctx->fh);
+ if (val == 0)
+ LOG("empty read diroffset");
+#endif
+ if (fseek(ctx->fh, ctx->header.diroffset, SEEK_SET) != 0) {
+ return;
+ }
+ /* now work through the directory structure taking it apart into
+ * our structure */
+#define BEREAD(x) do { val = fread(&(x), 4, 1, ctx->fh); if (val == 0)\
+ LOG("empty read");(x) = ntohl((x)); } while (0)
+ do {
+ val = fread(filename, 64, 1, ctx->fh);
+ if (val == 0)
+ LOG("empty read filename");
+ BEREAD(start);
+ BEREAD(len);
+ BEREAD(flags1);
+ BEREAD(flags2);
+ if (filename[0] != '\0')
+ container_add_to_dir(ctx, filename, start, len);
+ } while (filename[0] != '\0');
+#undef BEREAD
+ ctx->processed = true;
+}
+
+static const struct container_dirent *container_lookup(
+ struct container_ctx *ctx,
+ const unsigned char *entryname)
+{
+ unsigned int i;
+
+ for (i = 1; i <= ctx->entries; i++) {
+ struct container_dirent *e = ctx->directory + i - 1;
+ if (strcmp((char *)e->filename, (char *)entryname) == 0)
+ return e;
+ }
+
+ return NULL;
+}
+
+const unsigned char *container_get(struct container_ctx *ctx,
+ const unsigned char *entryname,
+ u_int32_t *size)
+{
+ const struct container_dirent *e;
+
+ if (ctx->processed == false)
+ container_process(ctx);
+
+ e = container_lookup(ctx, entryname);
+
+ if (e == NULL)
+ return NULL;
+
+ *size = e->len;
+
+ return &ctx->data[e->startoffset];
+}
+
+const unsigned char *container_iterate(struct container_ctx *ctx, int *state)
+{
+ struct container_dirent *e;
+ unsigned char *r;
+
+ if (ctx->processed == false)
+ container_process(ctx);
+
+ e = ctx->directory + *state;
+
+ r = e->filename;
+
+ if (r == NULL || r[0] == '\0')
+ r = NULL;
+
+ *state += 1;
+
+ return r;
+}
+
+const unsigned char *container_get_name(struct container_ctx *ctx)
+{
+ return ctx->header.name;
+}
+
+const unsigned char *container_get_author(struct container_ctx *ctx)
+{
+ return ctx->header.author;
+}
+
+
+static void container_write_dir(struct container_ctx *ctx)
+{
+ size_t val;
+ unsigned int i;
+ u_int32_t tmp;
+#define BEWRITE(x) do {tmp = htonl((x)); val = fwrite(&tmp, 4, 1, ctx->fh);\
+ if (val == 0) LOG("empty write"); } while(0)
+ for (i = 1; i <= ctx->entries; i++) {
+ struct container_dirent *e = ctx->directory + i - 1;
+ val = fwrite(e->filename, 64, 1, ctx->fh);
+ if (val == 0)
+ LOG("empty write filename");
+ BEWRITE(e->startoffset);
+ BEWRITE(e->len);
+ BEWRITE(e->flags1);
+ BEWRITE(e->flags2);
+ }
+#undef BEWRITE
+ /* empty entry signifies end of directory */
+ tmp = 0;
+ val = fwrite(&tmp, 4, 8, ctx->fh);
+ if (val == 0)
+ LOG("empty write end");
+}
+
+struct container_ctx *container_create(const char *filename,
+ const unsigned char *name,
+ const unsigned char *author)
+{
+ size_t val;
+ struct container_ctx *ctx = calloc(sizeof(struct container_ctx), 1);
+
+ ctx->fh = fopen(filename, "wb");
+
+ if (ctx->fh == NULL) {
+ free(ctx);
+ return NULL;
+ }
+
+ ctx->creating = true;
+ ctx->entries = 0;
+ ctx->directory = NULL;
+ ctx->header.parser = htonl(3);
+
+ snprintf((char *)ctx->header.name,
+ sizeof(ctx->header.name),
+ "%s", (char *)name);
+
+ snprintf((char *)ctx->header.author,
+ sizeof(ctx->header.author),
+ "%s", (char *)author);
+
+ val = fwrite("NSTM", 4, 1, ctx->fh);
+ if (val == 0)
+ LOG("empty write NSTM");
+ val = fwrite(&ctx->header.parser, 4, 1, ctx->fh);
+ if (val == 0)
+ LOG("empty write parser");
+ val = fwrite(ctx->header.name, 32, 1, ctx->fh);
+ if (val == 0)
+ LOG("empty write name");
+ val = fwrite(ctx->header.author, 64, 1, ctx->fh);
+ if (val == 0)
+ LOG("empty write author");
+
+ ctx->header.diroffset = 108;
+
+ /* skip over the directory offset for now, and fill it in later.
+ * we don't know where it'll be yet!
+ */
+
+ if (fseek(ctx->fh, 108, SEEK_SET) == -1) {
+ LOG("directory offset seek failed");
+ free(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+void container_add(struct container_ctx *ctx, const unsigned char *entryname,
+ const unsigned char *data,
+ const u_int32_t datalen)
+{
+ size_t val;
+ container_add_to_dir(ctx, entryname, ftell(ctx->fh), datalen);
+ val = fwrite(data, datalen, 1, ctx->fh);
+ if (val == 0)
+ LOG("empty write add file");
+}
+
+void container_close(struct container_ctx *ctx)
+{
+ if (ctx->creating == true) {
+ size_t flen, nflen, val;
+
+ /* discover where the directory's going to go. */
+ flen = container_filelen(ctx->fh);
+ flen = (flen + 3) & (~3); /* round up to nearest 4 bytes */
+
+ /* write this location to the header */
+ if (fseek(ctx->fh, 104, SEEK_SET) == 0) {
+ nflen = htonl(flen);
+ val = fwrite(&nflen, 4, 1, ctx->fh);
+ if (val == 0)
+ LOG("empty write directory location");
+
+ /* seek to where the directory will be, and write it */
+ if (fseek(ctx->fh, flen, SEEK_SET) == 0) {
+ container_write_dir(ctx);
+ }
+ }
+
+ } else if (ctx->processed) {
+#ifdef WITH_MMAP
+ munmap(ctx->data, ctx->header.diroffset);
+#else
+ free(ctx->data);
+#endif
+ }
+
+ fclose(ctx->fh);
+ free(ctx);
+}
+
+#ifdef WITH_THEME_INSTALL
+
+/**
+ * install theme from container
+ * \param themefile a file containing the containerized theme
+ * \param dirbasename a directory basename including trailing path sep; the
+ * full path of the theme is then a subdirectory of that
+ * caller owns reference to returned string, NULL for error
+ */
+
+char *container_extract_theme(const char *themefile, const char *dirbasename)
+{
+ struct stat statbuf;
+ struct container_ctx *cctx;
+ FILE *fh;
+ size_t val;
+ const unsigned char *e, *d;
+ char *themename, *dirname;
+ char path[PATH_MAX];
+ int state = 0;
+ unsigned int i;
+ u_int32_t flen;
+
+ cctx = container_open(themefile);
+ if (cctx == NULL) {
+ warn_user("FileOpenError", themefile);
+ return NULL;
+ }
+ themename = strdup((const char *)container_get_name(cctx));
+ if (themename == NULL) {
+ warn_user("NoMemory", 0);
+ container_close(cctx);
+ return NULL;
+ }
+ LOG("theme name: %s", themename);
+ LOG("theme author: %s", container_get_author(cctx));
+
+ dirname = malloc(strlen(dirbasename) + strlen(themename) + 2);
+ if (dirname == NULL) {
+ warn_user(messages_get("NoMemory"), 0);
+ free(themename);
+ container_close(cctx);
+ return NULL;
+ }
+ strcpy(dirname, dirbasename);
+ strcat(dirname, themename);
+ if (stat(dirname, &statbuf) != -1) {
+ /* directory exists */
+ warn_user("DirectoryError", dirname);
+ container_close(cctx);
+ free(dirname);
+ free(themename);
+ return NULL;
+ }
+ if (mkdir(dirname, S_IRWXU) != 0) {
+ warn_user("DirectoryError", dirname);
+ container_close(cctx);
+ free(dirname);
+ free(themename);
+ return NULL;
+ }
+
+ for (e = container_iterate(cctx, &state), i = 0; i < cctx->entries;
+ e = container_iterate(cctx, &state), i++) {
+ LOG("extracting %s", e);
+ snprintf(path, PATH_MAX, "%s/%s", dirname, e);
+ fh = fopen(path, "wb");
+ if (fh == NULL) {
+ warn_user("FileOpenError", (char *)e);
+ } else {
+ d = container_get(cctx, e, &flen);
+ val = fwrite(d, flen, 1, fh);
+ if (val == 0)
+ LOG("empty write");
+ fclose(fh);
+ }
+ }
+ LOG("theme container unpacked");
+ container_close(cctx);
+ free(dirname);
+ return themename;
+
+}
+
+#endif
+
+#ifdef TEST_RIG
+int main(int argc, char *argv[])
+{
+ struct container_ctx *ctx = container_create("test.theme", "Test theme",
+ "Rob Kendrick");
+ u_int32_t size;
+ int state = 0;
+ char *n;
+
+ container_add(ctx, "CHEESE", "This is a test of some cheese.", sizeof("This is a test of some cheese."));
+ container_add(ctx, "FOO", "This is a test of some cheese.", sizeof("This is a test of some cheese."));
+
+ container_close(ctx);
+
+ ctx = container_open("test.theme");
+
+ printf("Theme name: %s\n", container_get_name(ctx));
+ printf("Theme author: %s\n", container_get_author(ctx));
+
+ printf("Test string: %s\n", container_get(ctx, "CHEESE", &size));
+ printf("Length of text: %d\n", size);
+
+ while ( (n = container_iterate(ctx, &state)) ) {
+ printf("%s\n", n);
+ }
+
+ container_close(ctx);
+
+ exit(0);
+}
+#endif
+
+#ifdef NSTHEME
+ /* code to implement a simple container creator/extractor */
+#include <getopt.h>
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+
+static bool verbose = false;
+
+static void show_usage(const char *argv0)
+{
+ fprintf(stderr, "%s [options] <theme file> <directory>\n", argv0);
+ fprintf(stderr, " --help This text\n");
+ fprintf(stderr, " --create Create theme file from directory\n");
+ fprintf(stderr, " --extract Extract theme file into directory\n");
+ fprintf(stderr, " --name x Set theme's name when creating\n");
+ fprintf(stderr, " --author x Set theme's author when creating\n");
+ fprintf(stderr, " --verbose Print progress information\n");
+ fprintf(stderr, "\nOne and only one of --create or --extract must be specified.\n");
+}
+
+static void extract_theme(const char *themefile, const char *dirname)
+{
+ struct stat statbuf;
+ struct container_ctx *cctx;
+ FILE *fh;
+ const unsigned char *e, *d;
+ char path[PATH_MAX];
+ int i, state = 0;
+ u_int32_t flen;
+
+
+ if (stat(dirname, &statbuf) != -1) {
+ fprintf(stderr, "error: directory '%s' already exists.\n",
+ dirname);
+ exit(1);
+ }
+
+ mkdir(dirname, S_IRWXU);
+
+ cctx = container_open(themefile);
+ if (cctx == NULL) {
+ fprintf(stderr, "error: unable to open theme file '%s'\n",
+ themefile);
+ exit(1);
+ }
+
+ if (verbose == true) {
+ printf("theme name: %s\n", container_get_name(cctx));
+ printf("theme author: %s\n", container_get_author(cctx));
+ }
+
+ for (e = container_iterate(cctx, &state), i = 0; i < cctx->entries;
+ e = container_iterate(cctx, &state), i++) {
+ if (verbose == true)
+ printf("extracting %s\n", e);
+ snprintf(path, PATH_MAX, "%s/%s", dirname, e);
+ fh = fopen(path, "wb");
+ if (fh == NULL) {
+ perror("warning: unable to open file for output");
+ } else {
+ d = container_get(cctx, e, &flen);
+ fwrite(d, flen, 1, fh);
+ fclose(fh);
+ }
+ }
+
+ container_close(cctx);
+
+}
+
+static void create_theme(const char *themefile, const char *dirname,
+ const unsigned char *name,
+ const unsigned char *author)
+{
+ DIR *dir = opendir(dirname);
+ FILE *fh;
+ struct dirent *e;
+ struct stat statbuf;
+ struct container_ctx *cctx;
+ unsigned char *data;
+ char path[PATH_MAX];
+ size_t flen;
+ int t;
+
+ if (dir == NULL) {
+ perror("error: unable to open directory");
+ exit(1);
+ }
+
+ cctx = container_create(themefile, name, author);
+
+ errno = 0; /* to distinguish between end of dir and err */
+
+ while ((e = readdir(dir)) != NULL) {
+ if (strcmp(e->d_name, ".") != 0 &&
+ strcmp(e->d_name, "..") != 0) {
+ /* not the metadirs, so we want to process this. */
+ if (verbose == true)
+ printf("adding %s\n", e->d_name);
+ if (strlen(e->d_name) > 63) {
+ fprintf(stderr,
+ "warning: name truncated to length 63.\n");
+ }
+
+ snprintf(path, PATH_MAX, "%s/%s", dirname, e->d_name);
+
+ stat(path, &statbuf);
+ if (S_ISDIR(statbuf.st_mode)) {
+ fprintf(stderr,
+ "warning: skipping directory '%s'\n",
+ e->d_name);
+ continue;
+ }
+
+ fh = fopen(path, "rb");
+ if (fh == NULL) {
+ fprintf(stderr,
+ "warning: unable to open, skipping.");
+ } else {
+ flen = statbuf.st_size;
+ data = malloc(flen);
+ t = fread(data, flen, 1, fh);
+ fclose(fh);
+ container_add(cctx, (unsigned char *)e->d_name,
+ data, flen);
+ free(data);
+ }
+ }
+ errno = 0;
+ }
+
+ if (errno != 0) {
+ perror("error: couldn't enumerate directory");
+ closedir(dir);
+ container_close(cctx);
+ exit(1);
+ }
+
+ closedir(dir);
+ container_close(cctx);
+}
+
+int main(int argc, char *argv[])
+{
+ static struct option l_opts[] = {
+ { "help", 0, 0, 'h' },
+ { "create", 0, 0, 'c' },
+ { "extract", 0, 0, 'x' },
+ { "name", 1, 0, 'n' },
+ { "author", 1, 0, 'a' },
+ { "verbose", 0, 0, 'v' },
+
+ { NULL, 0, 0, 0 }
+ };
+
+ static char *s_opts = "hcxn:a:v";
+ int optch, optidx;
+ bool creating = false, extracting = false;
+ unsigned char name[32] = { '\0' }, author[64] = { '\0' };
+ char *themefile, *dirname;
+
+ while ((optch = getopt_long(argc, argv, s_opts, l_opts, &optidx)) != -1)
+ switch (optch) {
+ case 'h':
+ show_usage(argv[0]);
+ exit(0);
+ break;
+ case 'c':
+ creating = true;
+ break;
+ case 'x':
+ extracting = true;
+ break;
+ case 'n':
+ strncpy((char *)name, optarg, 31);
+ if (strlen(optarg) > 32)
+ fprintf(stderr, "warning: theme name truncated to 32 characters.\n");
+ break;
+ case 'a':
+ strncpy((char *)author, optarg, 63);
+ if (strlen(optarg) > 64)
+ fprintf(stderr, "warning: theme author truncated to 64 characters.\n");
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ default:
+ show_usage(argv[0]);
+ exit(1);
+ break;
+ }
+
+ if (creating == extracting) {
+ show_usage(argv[0]);
+ exit(1);
+ }
+
+ if ((argc - optind) < 2) {
+ show_usage(argv[0]);
+ exit(1);
+ }
+
+ if (creating == true &&
+ (strlen((char *)name) == 0 || strlen((char *)author) == 0)) {
+ fprintf(stderr, "No theme name and/or author specified.\n");
+ show_usage(argv[0]);
+ exit(1);
+ }
+
+ themefile = strdup(argv[optind]);
+ dirname = strdup(argv[optind + 1]);
+
+ if (verbose == true)
+ printf("%s '%s' %s directory '%s'\n",
+ creating ? "creating" : "extracting", themefile,
+ creating ? "from" : "to", dirname);
+
+ if (creating) {
+ if (verbose == true)
+ printf("name = %s, author = %s\n", name, author);
+ create_theme(themefile, dirname, name, author);
+ } else {
+ extract_theme(themefile, dirname);
+ }
+
+ return 0;
+}
+#endif
diff --git a/gtk/theme_container.h b/gtk/theme_container.h
new file mode 100644
index 000000000..de880ecd5
--- /dev/null
+++ b/gtk/theme_container.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2006 Rob Kendrick <rjek@rjek.com>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/** \file
+ * Container format handling for themes etc. */
+
+#ifndef __CONTAINER_H__
+#define __CONTAINER_H__
+
+#include <sys/types.h>
+
+struct container_ctx;
+
+/* reading interface */
+struct container_ctx *container_open(const char *filename);
+const unsigned char *container_get(struct container_ctx *ctx,
+ const unsigned char *entryname,
+ u_int32_t *size);
+const unsigned char *container_get_name(struct container_ctx *ctx);
+const unsigned char *container_get_author(struct container_ctx *ctx);
+const unsigned char *container_iterate(struct container_ctx *ctx,
+ int *state);
+
+/* creating interface */
+struct container_ctx *container_create(const char *filename,
+ const unsigned char *name,
+ const unsigned char *author);
+void container_add(struct container_ctx *ctx, const unsigned char *entryname,
+ const unsigned char *data,
+ const u_int32_t datalen);
+
+/* common interface */
+void container_close(struct container_ctx *ctx);
+
+#ifdef WITH_THEME_INSTALL
+char *container_extract_theme(const char *themefile, const char *dirbasename);
+#endif
+#endif /* __CONTAINER_H__ */