From 65bdef55ab12bf07aa52fb9c184c073cbf562e3b Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Tue, 19 Jan 2016 15:10:29 +0000 Subject: move the container code to gtk directory as its only ever used there. --- gtk/Makefile.target | 5 +- gtk/scaffolding.c | 2 +- gtk/theme.c | 2 +- gtk/theme.h | 3 - gtk/theme_container.c | 765 ++++++++++++++++++++++++++++++++++++++++++++++++++ gtk/theme_container.h | 53 ++++ 6 files changed, 822 insertions(+), 8 deletions(-) create mode 100644 gtk/theme_container.c create mode 100644 gtk/theme_container.h (limited to 'gtk') 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 -#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 + * + * 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 . + */ + +/* 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 +#include +#include +#include +#include +#include +#include + +#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 +#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 +#include +#include +#include + +static bool verbose = false; + +static void show_usage(const char *argv0) +{ + fprintf(stderr, "%s [options] \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 + * + * 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 . + */ + +/** \file + * Container format handling for themes etc. */ + +#ifndef __CONTAINER_H__ +#define __CONTAINER_H__ + +#include + +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__ */ -- cgit v1.2.3