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 ++++ utils/container.c | 763 ------------------------------------------------- utils/container.h | 53 ---- 8 files changed, 822 insertions(+), 824 deletions(-) create mode 100644 gtk/theme_container.c create mode 100644 gtk/theme_container.h delete mode 100644 utils/container.c delete mode 100644 utils/container.h 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__ */ diff --git a/utils/container.c b/utils/container.c deleted file mode 100644 index 68ea32211..000000000 --- a/utils/container.c +++ /dev/null @@ -1,763 +0,0 @@ -/* - * 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/container.h" -#include "utils/log.h" -#include "utils/messages.h" -#include "utils/utils.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/utils/container.h b/utils/container.h deleted file mode 100644 index de880ecd5..000000000 --- a/utils/container.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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