> Index: gtk/gtk_theme.c
>   
this file refers to the largest of the branches, customization/theming;
currently a theme is a set of button images; though adaptations are
possible; themes may be downloaded, a content-type served as
x/application-netsurf-theme, containerized as described in
utils/container.c, is automatically installed as a theme [as long as the
images are properly named]; a directory full of images, added to the
themes folder, may be added as a theme from the gtk preferences window
too; theme_init loads a saved selected theme from its folder, implements
it into every gtk window; theme_add adds the theme name to the list of
selectable themes; theme_verify maintains the integrity of the list;
theme_implement changes the images visible in the gtk window; theme_load
loads a set of images into an array for adding to buttons; to save
trouble involving gtk object counts [particularly deletion of a button
during customization removes the image reference] a fresh array is
loaded from the pixbuf cache every time a set of buttons is destined for
theme_implement; theme_prepare caches pixbufs from the image files to
avoid multiple filesystem calls; theme_image_default returns default
images when no theme is selected / when a theme has no image for a
button that has its default image set; the theme_install_* functions are
gtk-specific functions to respond when the appropriate content-type is
being fetched;
> ===================================================================
> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
> +++ gtk/gtk_theme.c   2009-07-10 12:49:36.000000000 +0100
> @@ -0,0 +1,591 @@
> +/*
> + * Copyright 2009 Mark Benjamin <[email protected]>
> + *
> + * 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/>.
> + */
> +
> +#include <gtk/gtk.h>
> +#include <stdio.h>
> +#include <sys/stat.h>
> +#include "content/content.h"
> +#include "content/content_type.h"
> +#include "gtk/gtk_gui.h"
> +#include "gtk/gtk_scaffolding.h"
> +#include "gtk/gtk_menu.h"
> +#include "gtk/gtk_theme.h"
> +#include "gtk/gtk_window.h"
> +#include "gtk/options.h"
> +#include "gtk/dialogs/gtk_options.h"
> +#include "utils/container.h"
> +#include "utils/log.h"
> +#include "utils/messages.h"
> +#include "utils/utils.h"
> +
> +struct nsgtk_theme_cache {
> +     GdkPixbuf       *image[PLACEHOLDER_BUTTON];
> +     GdkPixbuf       *searchimage[3]; /* back, forward, close */
> +     /* apng throbber image */
> +};
> +
> +char *current_theme_name = NULL;
> +static struct nsgtk_theme_cache *theme_cache_menu = NULL;
> +static struct nsgtk_theme_cache *theme_cache_toolbar = NULL;
> +
> +static struct nsgtk_theme *nsgtk_theme_default(GtkIconSize s);
> +static GtkImage *nsgtk_theme_image_default(int i, GtkIconSize s);
> +static bool nsgtk_theme_verify(const char *themename);
> +
> +#ifdef WITH_THEME_INSTALL
> +static struct content *theme_install_content = NULL;
> +
> +static void theme_install_callback(content_msg msg, struct content *c,
> +             intptr_t p1, intptr_t p2, union content_msg_data data);
> +static bool theme_install_read(char *data, unsigned long len);
> +#endif
> +
> +void nsgtk_theme_init()
> +{
> +     if (option_current_theme == 0)
> +             return;
> +     char *themefile = g_strconcat(res_dir_location, "themelist", NULL);
> +     nsgtk_scaffolding *list = scaf_list;
> +     nsgtk_theme_verify(NULL);
> +     FILE *fp = fopen(themefile, "r");
> +     char buf[50];
> +     int row_count = 0;
> +     if (fp == NULL)
> +             return;
> +     while (fgets(buf, sizeof(buf), fp)) {
> +             if (buf[0] == '\0')
> +                     continue;
> +
> +             if (row_count++ == option_current_theme) {
> +                     if (current_theme_name != NULL)
> +                             free(current_theme_name);
> +                     buf[strlen(buf) - 1] = '\0';
> +                     current_theme_name = strdup(buf);
> +                     break;
> +             }
> +     }
> +     fclose(fp);
> +     
> +     while (list) {
> +             nsgtk_theme_implement(list);
> +             list = nsgtk_scaffolding_iterate(list);
> +     }
> +}
> +
> +void nsgtk_theme_add(const char *themename)
> +{
> +     GtkWidget *notification, *label;
> +     char *labelcontent, *themefile = g_strconcat(res_dir_location,
> +                     "themelist", NULL);
> +     /* conduct verification here; no adding duplicates to list */
> +     if (nsgtk_theme_verify(themename) == false) {
> +             warn_user(messages_get("gtkThemeDup"), 0);
> +             g_free(themefile);
> +             return;
> +     }
> +     FILE *fp = fopen(themefile, "a");
> +     if (fp == NULL) {
> +             warn_user(messages_get("gtkFileError"), themefile);
> +             g_free(themefile);
> +             return;
> +     }
> +     fprintf(fp, "%s\n", themename);
> +     fclose(fp);
> +     g_free(themefile);
> +     
> +     /* notification that theme was added successfully */
> +     notification = gtk_dialog_new_with_buttons(messages_get("gtkThemeAdd"),
> +                     NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_OK,
> +                     GTK_RESPONSE_NONE, NULL);
> +     labelcontent = g_strconcat("\t\t\t", messages_get("gtkThemeAdd"),
> +                      "\t\t\t", NULL);
> +     label = gtk_label_new(labelcontent);
> +     g_signal_connect_swapped(notification, "response",
> +                     G_CALLBACK(gtk_widget_destroy), notification);
> +     gtk_container_add(GTK_CONTAINER(GTK_DIALOG(notification)->vbox), label);
> +     gtk_widget_show_all(notification);
> +     g_free(labelcontent);
> +     
> +     /* update combo */
> +     if (wndPreferences == NULL)
> +             return;
> +     nsgtk_options_combo_theme_add(themename);
> +
> +}
> +
> +bool nsgtk_theme_verify(const char *themename)
> +{    
> +     long filelength;
> +     FILE *fp;
> +     size_t val;
> +     char buf[50];
> +     char *themefile = g_strconcat(res_dir_location, "themelist", NULL);
> +     if (themename == NULL) {
> +             char *filecontent, *testfile;
> +             struct stat sta;
> +             fp = fopen(themefile, "r+");
> +             if (fp == NULL) {
> +                     warn_user(messages_get("gtkFileError"), themefile);
> +                     g_free(themefile);
> +                     return true;
> +             }
> +             fseek(fp, 0L, SEEK_END);
> +             filelength = ftell(fp);
> +             filecontent = malloc(filelength + 2);
> +             strcpy(filecontent, "gtk default theme\n");
> +             if (filecontent == NULL) {
> +                     warn_user(messages_get("NoMemory"), 0);
> +                     g_free(themefile);
> +                     return true;
> +             }
> +             fseek(fp, 0L, SEEK_SET);
> +             while (fgets(buf, sizeof(buf), fp)) {
> +             /* iterate list */
> +                     buf[strlen(buf) - 1] = '\0';
> +                     testfile = g_strconcat(res_dir_location, "themes/", 
> +                                     buf, NULL);
> +                     /* check every directory */
> +                     if (access(testfile, R_OK) == 0) {
> +                             stat(testfile, &sta);
> +                             if (S_ISDIR(sta.st_mode)) {
> +                                     free(testfile);
> +                                     buf[strlen(buf)] = '\n';
> +                                     strcat(filecontent, buf);
> +                             }
> +                     }
> +             }
> +             fclose(fp);
> +             fp = fopen(themefile, "w");
> +             if (fp == NULL) {
> +                     warn_user(messages_get("gtkFileError"), themefile);
> +                     free(filecontent);
> +                     g_free(themefile);
> +                     return true;
> +             }
> +             val = fwrite(filecontent, strlen(filecontent), 1, fp);
> +             if (val == 0)
> +                     LOG(("empty write themelist"));
> +             fclose(fp);
> +             free(filecontent);
> +             g_free(themefile);
> +             return true;
> +     } else {
> +             fp = fopen(themefile, "r");
> +             if (fp == NULL) {
> +                     warn_user(messages_get("gtkFileError"), themefile);
> +                     g_free(themefile);
> +                     return false;
> +             }
> +             while (fgets(buf, sizeof(buf), fp)) {
> +                     buf[strlen(buf) - 1] = '\0';
> +                     if (strcmp(buf, themename) == 0) {
> +                             g_free(themefile);
> +                             return false;
> +                     }
> +             }
> +             fclose(fp);
> +             g_free(themefile);
> +             return true;
> +     }
> +     
> +}
> +
> +void nsgtk_theme_implement(struct gtk_scaffolding *g)
> +{
> +     struct nsgtk_theme *theme[4];
> +     int i;
> +     for (i = 0; i < 3; i++)
> +             theme[i] = nsgtk_theme_load(GTK_ICON_SIZE_MENU);
> +     theme[3] = nsgtk_theme_load(GTK_ICON_SIZE_LARGE_TOOLBAR);
> +     for (i = BACK_BUTTON; i < PLACEHOLDER_BUTTON; i++) {
> +             if ((i == URL_BAR_ITEM) || (i == THROBBER_ITEM) || 
> +                             (i == WEBSEARCH_ITEM))
> +                     continue;
> +             if (nsgtk_scaffolding_button(g, i)->main != NULL) {
> +                     gtk_image_menu_item_set_image(nsgtk_scaffolding_button(
> +                                     g, i)->main,
> +                                     GTK_WIDGET(theme[0]->image[i]));
> +                     gtk_widget_show_all(GTK_WIDGET(
> +                                     nsgtk_scaffolding_button(g, i)->main));
> +             }
> +             if (nsgtk_scaffolding_button(g, i)->rclick != NULL) {
> +                     gtk_image_menu_item_set_image(nsgtk_scaffolding_button(
> +                                     g, i)->rclick,
> +                                     GTK_WIDGET(theme[1]->image[i]));
> +                     gtk_widget_show_all(GTK_WIDGET(
> +                                     nsgtk_scaffolding_button(
> +                                     g, i)->rclick));
> +             }
> +             if (nsgtk_scaffolding_button(g, i)->popup != NULL) {
> +                     gtk_image_menu_item_set_image(nsgtk_scaffolding_button(
> +                                     g, i)->popup,
> +                                     GTK_WIDGET(theme[2]->image[i]));
> +                     gtk_widget_show_all(GTK_WIDGET(
> +                                     nsgtk_scaffolding_button(g, i)->
> +                                     popup));
> +             }
> +             if ((nsgtk_scaffolding_button(g, i)->location != -1) && 
> +                             (nsgtk_scaffolding_button(g, i)->button
> +                                             != NULL)) {
> +                     gtk_tool_button_set_icon_widget(
> +                                     GTK_TOOL_BUTTON(
> +                                     nsgtk_scaffolding_button(
> +                                     g, i)->button),
> +                                     GTK_WIDGET(theme[3]->image[i]));
> +                     gtk_widget_show_all(GTK_WIDGET(
> +                                     nsgtk_scaffolding_button(g, i)->
> +                                     button));
> +             }
> +     }
> +     
> +     /* set search bar images */
> +     gtk_tool_button_set_icon_widget(
> +                     nsgtk_scaffolding_search(g)->buttons[0],
> +                     GTK_WIDGET(theme[0]->searchimage[0]));
> +     gtk_widget_show_all(GTK_WIDGET(
> +                     nsgtk_scaffolding_search(g)->buttons[0]));
> +     gtk_tool_button_set_icon_widget(
> +                     nsgtk_scaffolding_search(g)->buttons[1],
> +                     GTK_WIDGET(theme[0]->searchimage[1]));
> +     gtk_widget_show_all(GTK_WIDGET(
> +                     nsgtk_scaffolding_search(g)->buttons[1]));
> +     gtk_tool_button_set_icon_widget(
> +                     nsgtk_scaffolding_search(g)->buttons[2],
> +                     GTK_WIDGET(theme[0]->searchimage[2]));
> +     gtk_widget_show_all(GTK_WIDGET(
> +                     nsgtk_scaffolding_search(g)->buttons[2]));
> +     
> +     for (i = 0; i < 4; i++)
> +             free(theme[i]);
> +}
> +
> +struct nsgtk_theme *nsgtk_theme_load(GtkIconSize s)
> +{
> +     if (current_theme_name == NULL)
> +             return nsgtk_theme_default(s);
> +     struct nsgtk_theme *theme = malloc(sizeof(struct nsgtk_theme));
> +     if ((theme_cache_menu == NULL) || (theme_cache_toolbar == NULL))
> +             nsgtk_theme_prepare();
> +     /* load theme from cache */
> +     struct nsgtk_theme_cache *cachetheme = (s == GTK_ICON_SIZE_MENU) ?
> +                     theme_cache_menu : theme_cache_toolbar;
> +#define SET_BUTTON_IMAGE(q)\
> +     if (cachetheme->image[q##_BUTTON] != NULL)\
> +             theme->image[q##_BUTTON] = GTK_IMAGE(gtk_image_new_from_pixbuf(\
> +                             cachetheme->image[q##_BUTTON]));\
> +     else\
> +             theme->image[q##_BUTTON] = nsgtk_theme_image_default(\
> +                             q##_BUTTON, s);
> +     SET_BUTTON_IMAGE(BACK)
> +     SET_BUTTON_IMAGE(HISTORY)
> +     SET_BUTTON_IMAGE(FORWARD)
> +     SET_BUTTON_IMAGE(STOP)
> +     SET_BUTTON_IMAGE(RELOAD)
> +     SET_BUTTON_IMAGE(HOME)
> +     SET_BUTTON_IMAGE(NEWWINDOW)
> +     SET_BUTTON_IMAGE(NEWTAB)
> +     SET_BUTTON_IMAGE(OPENFILE)
> +     SET_BUTTON_IMAGE(CLOSETAB)
> +     SET_BUTTON_IMAGE(CLOSEWINDOW)
> +     SET_BUTTON_IMAGE(SAVEPAGE)
> +     SET_BUTTON_IMAGE(PRINTPREVIEW)
> +     SET_BUTTON_IMAGE(PRINT)
> +     SET_BUTTON_IMAGE(QUIT)
> +     SET_BUTTON_IMAGE(CUT)
> +     SET_BUTTON_IMAGE(COPY)
> +     SET_BUTTON_IMAGE(PASTE)
> +     SET_BUTTON_IMAGE(DELETE)
> +     SET_BUTTON_IMAGE(SELECTALL)
> +     SET_BUTTON_IMAGE(PREFERENCES)
> +     SET_BUTTON_IMAGE(ZOOMPLUS)
> +     SET_BUTTON_IMAGE(ZOOMMINUS)
> +     SET_BUTTON_IMAGE(ZOOMNORMAL)
> +     SET_BUTTON_IMAGE(FULLSCREEN)
> +     SET_BUTTON_IMAGE(VIEWSOURCE)
> +     SET_BUTTON_IMAGE(CONTENTS)
> +     SET_BUTTON_IMAGE(ABOUT)
> +     SET_BUTTON_IMAGE(PDF)
> +     SET_BUTTON_IMAGE(PLAINTEXT)
> +     SET_BUTTON_IMAGE(DRAWFILE)
> +     SET_BUTTON_IMAGE(POSTSCRIPT)
> +     SET_BUTTON_IMAGE(FIND)
> +     SET_BUTTON_IMAGE(DOWNLOADS)
> +     SET_BUTTON_IMAGE(SAVEWINDOWSIZE)
> +     SET_BUTTON_IMAGE(TOGGLEDEBUGGING)
> +     SET_BUTTON_IMAGE(SAVEBOXTREE)
> +     SET_BUTTON_IMAGE(SAVEDOMTREE)
> +     SET_BUTTON_IMAGE(LOCALHISTORY)
> +     SET_BUTTON_IMAGE(GLOBALHISTORY)
> +     SET_BUTTON_IMAGE(ADDBOOKMARKS)
> +     SET_BUTTON_IMAGE(SHOWBOOKMARKS)
> +     SET_BUTTON_IMAGE(OPENLOCATION)
> +     SET_BUTTON_IMAGE(NEXTTAB)
> +     SET_BUTTON_IMAGE(PREVTAB)
> +     SET_BUTTON_IMAGE(GUIDE)
> +     SET_BUTTON_IMAGE(INFO)
> +#undef SET_BUTTON_IMAGE      
> +#define SET_BUTTON_IMAGE(p, q)\
> +     if (cachetheme->searchimage[p] != NULL)\
> +             theme->searchimage[p] = GTK_IMAGE(gtk_image_new_from_pixbuf(\
> +                             cachetheme->searchimage[p]));\
> +     else if (cachetheme->image[q##_BUTTON] != NULL)\
> +             theme->searchimage[p] = GTK_IMAGE(gtk_image_new_from_pixbuf(\
> +                             cachetheme->image[q##_BUTTON]));\
> +     else\
> +             theme->searchimage[p] = nsgtk_theme_image_default(\
> +                             PLACEHOLDER_BUTTON + p, s);
> +     SET_BUTTON_IMAGE(0, BACK)
> +     SET_BUTTON_IMAGE(1, FORWARD)
> +     SET_BUTTON_IMAGE(2, CLOSEWINDOW)
> +#undef SET_BUTTON_IMAGE
> +     return theme;
> +}
> +
> +void nsgtk_theme_prepare(void)
> +{
> +     if (current_theme_name == NULL)
> +             return;
> +     if (theme_cache_menu == NULL)
> +             theme_cache_menu = malloc(sizeof(struct nsgtk_theme_cache));
> +     if (theme_cache_toolbar == NULL)
> +             theme_cache_toolbar = malloc(sizeof(struct nsgtk_theme_cache));
> +     char *path = g_strconcat(res_dir_location, "themes/",
> +                     current_theme_name, "/", NULL);
> +     char *filename;
> +#define CACHE_IMAGE(p, q)\
> +     filename = g_strconcat(path, #q, ".png", NULL);\
> +     theme_cache_toolbar->image[p##_BUTTON] =\
> +                     gdk_pixbuf_new_from_file_at_size(filename, 24, 24,\
> +                     NULL);\
> +     theme_cache_menu->image[p##_BUTTON] =\
> +                     gdk_pixbuf_new_from_file_at_size(filename, 16, 16,\
> +                     NULL);\
> +     g_free(filename)
> +     CACHE_IMAGE(BACK, back);
> +     CACHE_IMAGE(HISTORY, history);
> +     CACHE_IMAGE(FORWARD, forward);
> +     CACHE_IMAGE(STOP, stop);
> +     CACHE_IMAGE(RELOAD, reload);
> +     CACHE_IMAGE(HOME, home);
> +     CACHE_IMAGE(NEWWINDOW, newwindow);
> +     CACHE_IMAGE(NEWTAB, newtab);
> +     CACHE_IMAGE(OPENFILE, openfile);
> +     CACHE_IMAGE(CLOSETAB, closetab);
> +     CACHE_IMAGE(CLOSEWINDOW, closewindow);
> +     CACHE_IMAGE(SAVEPAGE, savepage);
> +     CACHE_IMAGE(PRINTPREVIEW, printpreview);
> +     CACHE_IMAGE(PRINT, print);
> +     CACHE_IMAGE(QUIT, quit);
> +     CACHE_IMAGE(CUT, cut);
> +     CACHE_IMAGE(COPY, copy);
> +     CACHE_IMAGE(PASTE, paste);
> +     CACHE_IMAGE(DELETE, delete);
> +     CACHE_IMAGE(SELECTALL, selectall);
> +     CACHE_IMAGE(PREFERENCES, preferences);
> +     CACHE_IMAGE(ZOOMPLUS, zoomplus);
> +     CACHE_IMAGE(ZOOMMINUS, zoomminus);
> +     CACHE_IMAGE(ZOOMNORMAL, zoomnormal);
> +     CACHE_IMAGE(FULLSCREEN, fullscreen);
> +     CACHE_IMAGE(VIEWSOURCE, viewsource);
> +     CACHE_IMAGE(CONTENTS, helpcontents);
> +     CACHE_IMAGE(ABOUT, helpabout);
> +     CACHE_IMAGE(PDF, pdf);
> +     CACHE_IMAGE(PLAINTEXT, plaintext);
> +     CACHE_IMAGE(DRAWFILE, drawfile);
> +     CACHE_IMAGE(POSTSCRIPT, postscript);
> +     CACHE_IMAGE(FIND, find);
> +     CACHE_IMAGE(DOWNLOADS, downloads);
> +     CACHE_IMAGE(SAVEWINDOWSIZE, savewindowsize);
> +     CACHE_IMAGE(TOGGLEDEBUGGING, toggledebugging);
> +     CACHE_IMAGE(SAVEBOXTREE, boxtree);
> +     CACHE_IMAGE(SAVEDOMTREE, domtree);
> +     CACHE_IMAGE(LOCALHISTORY, localhistory);
> +     CACHE_IMAGE(GLOBALHISTORY, globalhistory);
> +     CACHE_IMAGE(ADDBOOKMARKS, addbookmarks);
> +     CACHE_IMAGE(SHOWBOOKMARKS, showbookmarks);
> +     CACHE_IMAGE(OPENLOCATION, openlocation);
> +     CACHE_IMAGE(NEXTTAB, nexttab);
> +     CACHE_IMAGE(PREVTAB, prevtab);
> +     CACHE_IMAGE(GUIDE, helpguide);
> +     CACHE_IMAGE(INFO, helpinfo);
> +#undef CACHE_IMAGE
> +#define CACHE_IMAGE(p, q)\
> +     filename = g_strconcat(path, #q, ".png", NULL);\
> +     theme_cache_toolbar->searchimage[p] =\
> +                     gdk_pixbuf_new_from_file_at_size(filename, 24, 24,\
> +                     NULL);\
> +     theme_cache_menu->searchimage[p] =\
> +                     gdk_pixbuf_new_from_file_at_size(filename, 16, 16,\
> +                     NULL);\
> +     g_free(filename)
> +     CACHE_IMAGE(0, searchback);
> +     CACHE_IMAGE(1, searchforward);
> +     CACHE_IMAGE(2, searchclose);
> +#undef CACHE_IMAGE
> +     g_free(path);
> +}
> +
> +GtkImage *nsgtk_theme_image_default(int i, GtkIconSize s)
> +{
> +     char *imagefile;
> +     GtkImage *image;
> +     switch(i) {
> +#define BUTTON_IMAGE(p, q)\
> +     case p##_BUTTON:\
> +             return GTK_IMAGE(gtk_image_new_from_stock(#q, s))
> +     BUTTON_IMAGE(BACK, gtk-go-back);
> +     case HISTORY_BUTTON:
> +             imagefile = g_strconcat(res_dir_location, 
> +                             "arrow_down_8x32.png", NULL);
> +             image = GTK_IMAGE(gtk_image_new_from_file(imagefile));
> +             g_free(imagefile);
> +             return image;
> +     BUTTON_IMAGE(FORWARD, gtk-go-forward);
> +     BUTTON_IMAGE(STOP, gtk-stop);
> +     BUTTON_IMAGE(RELOAD, gtk-refresh);
> +     BUTTON_IMAGE(HOME, gtk-home);
> +     BUTTON_IMAGE(NEWWINDOW, gtk-new);
> +     BUTTON_IMAGE(NEWTAB, gtk-new);
> +     BUTTON_IMAGE(OPENFILE, gtk-open);
> +     BUTTON_IMAGE(CLOSETAB, gtk-close);
> +     BUTTON_IMAGE(CLOSEWINDOW, gtk-close);
> +     BUTTON_IMAGE(SAVEPAGE, gtk-save-as);
> +     BUTTON_IMAGE(PRINTPREVIEW, gtk-print-preview);
> +     BUTTON_IMAGE(PRINT, gtk-print);
> +     BUTTON_IMAGE(QUIT, gtk-quit);
> +     BUTTON_IMAGE(CUT, gtk-cut);
> +     BUTTON_IMAGE(COPY, gtk-copy);
> +     BUTTON_IMAGE(PASTE, gtk-paste);
> +     BUTTON_IMAGE(DELETE, gtk-delete);
> +     BUTTON_IMAGE(SELECTALL, gtk-select-all);
> +     BUTTON_IMAGE(FIND, gtk-find);
> +     BUTTON_IMAGE(PREFERENCES, gtk-preferences);
> +     BUTTON_IMAGE(ZOOMPLUS, gtk-zoom-in);
> +     BUTTON_IMAGE(ZOOMMINUS, gtk-zoom-out);
> +     BUTTON_IMAGE(ZOOMNORMAL, gtk-zoom-100);
> +     BUTTON_IMAGE(FULLSCREEN, gtk-fullscreen);
> +     BUTTON_IMAGE(VIEWSOURCE, gtk-index);
> +     BUTTON_IMAGE(CONTENTS, gtk-help);
> +     BUTTON_IMAGE(ABOUT, gtk-about);
> +#undef BUTTON_IMAGE
> +     case (PLACEHOLDER_BUTTON):
> +             return GTK_IMAGE(gtk_image_new_from_stock("gtk-go-back", s));
> +     case (PLACEHOLDER_BUTTON + 1):
> +             return GTK_IMAGE(gtk_image_new_from_stock("gtk-go-forward", 
> +                             s));
> +     case (PLACEHOLDER_BUTTON + 2):
> +             return GTK_IMAGE(gtk_image_new_from_stock("gtk-close", s));
> +     default:
> +             imagefile = g_strconcat(res_dir_location, "themes/Alpha.png", 
> +                             NULL);
> +             image = GTK_IMAGE(gtk_image_new_from_file(imagefile));
> +             g_free(imagefile);
> +             return image;
> +     }       
> +}
> +
> +
> +#ifdef WITH_THEME_INSTALL
> +/**
> + * Handle CONTENT_THEME
> + */
> +void theme_install_start(struct content *c)
> +{
> +     assert(c);
> +     assert(c->type == CONTENT_THEME);
> +
> +     /* stop theme sitting in memory cache */
> +     c->fresh = false;
> +     if (!content_add_user(c, theme_install_callback, 0, 0)) {
> +             warn_user("NoMemory", 0);
> +             return;
> +     }
> +}
> +
> +
> +/**
> + * Callback for fetchcache() for theme install fetches.
> + */
> +void theme_install_callback(content_msg msg, struct content *c,
> +             intptr_t p1, intptr_t p2, union content_msg_data data)
> +{
> +     switch (msg) {
> +     case CONTENT_MSG_READY:
> +             break;
> +
> +     case CONTENT_MSG_DONE:
> +             theme_install_content = c;
> +             if (!theme_install_read(c->source_data, c->source_size))
> +                     warn_user("ThemeInvalid", 0);
> +             break;
> +
> +     case CONTENT_MSG_ERROR:
> +             warn_user(data.error, 0);
> +             break;
> +
> +     case CONTENT_MSG_STATUS:
> +             break;
> +
> +     case CONTENT_MSG_LOADING:
> +     case CONTENT_MSG_REFORMAT:
> +     case CONTENT_MSG_REDRAW:
> +     case CONTENT_MSG_NEWPTR:
> +     case CONTENT_MSG_LAUNCH:
> +     case CONTENT_MSG_AUTH:
> +     default:
> +             assert(0);
> +             break;
> +     }
> +}
> +
> +/**
> + * handler saves theme data as a local theme
> + */
> +bool theme_install_read(char *data, unsigned long len)
> +{
> +     char *filename;
> +     int handle = g_file_open_tmp("nsgtkthemeXXXXXX", &filename, NULL);
> +     ssize_t written = write(handle, data, len);
> +     close(handle);
> +     if ((unsigned)written != len)
> +             return false;
> +     
> +     /* get name of theme; set as dirname */
> +     char *dirname = g_strconcat(res_dir_location, "themes/", NULL);
> +     if (dirname == NULL)
> +             return false;
> +
> +     /* save individual files in theme */
> +     filename = container_extract_theme(filename, dirname);
> +     g_free(dirname);
> +     if (filename == NULL)
> +             return false;
> +     nsgtk_theme_add(filename);
> +             
> +     return true;
> +}
> +#endif
> +
> +struct nsgtk_theme *nsgtk_theme_default(GtkIconSize s)
> +{
> +     struct nsgtk_theme *theme = malloc(sizeof(struct nsgtk_theme));
> +     for (int i = BACK_BUTTON; i <= PLACEHOLDER_BUTTON + 2; i++)
> +             theme->image[i] = nsgtk_theme_image_default(i, s);
> +     return theme;
> +}
> +


> Index: gtk/gtk_theme.h
> ===================================================================
> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
> +++ gtk/gtk_theme.h   2009-07-10 12:49:37.000000000 +0100
> @@ -0,0 +1,39 @@
> +/*
> + * Copyright 2009 Mark Benjamin <[email protected]>
> + *
> + * 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/>.
> + */
> +
> +#ifndef _NETSURF_GTK_THEME_H_
> +#define _NETSURF_GTK_THEME_H_
> +
> +#include <gtk/gtk.h>
> +#include "gtk/gtk_scaffolding.h"
> +
> +extern char *current_theme_name;
> +
> +struct nsgtk_theme {
> +     GtkImage        *image[PLACEHOLDER_BUTTON];
> +     GtkImage        *searchimage[3]; /* back, forward, close */
> +     /* apng throbber element */
> +};
> +
> +struct nsgtk_theme *nsgtk_theme_load(GtkIconSize s);
> +void nsgtk_theme_add(const char *themename);
> +void nsgtk_theme_init(void);
> +void nsgtk_theme_prepare(void);
> +void nsgtk_theme_implement(struct gtk_scaffolding *g);
> +
> +#endif

> Index: gtk/res/themes
>   
the new folder includes one sample theme I called 'gtk+' that is the
most up-to-date gtk images retrieved from the website
> ===================================================================
> Index: gtk/res/themes/gtk+
> ===================================================================
> Index: gtk/res/themes/gtk+/print.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/print.png differ
> Index: gtk/res/themes/gtk+/closetab.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/closetab.png differ
> Index: gtk/res/themes/gtk+/zoomnormal.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/zoomnormal.png differ
> Index: gtk/res/themes/gtk+/closewindow.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/closewindow.png differ
> Index: gtk/res/themes/gtk+/printpreview.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/printpreview.png differ
> Index: gtk/res/themes/gtk+/zoomminus.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/zoomminus.png differ
> Index: gtk/res/themes/gtk+/back.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/back.png differ
> Index: gtk/res/themes/gtk+/preferences.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/preferences.png differ
> Index: gtk/res/themes/gtk+/history.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/history.png differ
> Index: gtk/res/themes/gtk+/openfile.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/openfile.png differ
> Index: gtk/res/themes/gtk+/delete.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/delete.png differ
> Index: gtk/res/themes/gtk+/fullscreen.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/fullscreen.png differ
> Index: gtk/res/themes/gtk+/forward.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/forward.png differ
> Index: gtk/res/themes/gtk+/reload.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/reload.png differ
> Index: gtk/res/themes/gtk+/helpcontents.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/helpcontents.png differ
> Index: gtk/res/themes/gtk+/info
>   
this file, as well as the usual blurb, contains instructions for theming
> ===================================================================
> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
> +++ gtk/res/themes/gtk+/info  2009-07-10 12:49:47.000000000 +0100
> @@ -0,0 +1,81 @@
> +This file is part of NetSurf, http://www.netsurf-browser.org/
> +
> +The images in this theme folder 'gtk+' are from the gtk stock image set
> +http://library.gnome.org/devel/gtk/unstable/gtk-Stock-Items.html
> +
> +the image history.png is [for what it's worth!] Copyright 2009 Mark Benjamin
> +<[email protected]>
> +
> +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/>.
> +
> +*** Instructions for theming ***
> +
> +to create a theme, make a folder, whose name is the name of the theme;
> +put in the folder, a set of png images for the toolbuttons;
> +the names of the images should be a subset of the list
> +
> +     back.png,
> +     history.png,
> +     forward.png,
> +     stop.png,
> +     reload.png,
> +     home.png,
> +     newwindow.png,
> +     newtab.png,
> +     openfile.png,
> +     closetab.png,
> +     closewindow.png,
> +     savepage.png,
> +     pdf.png,
> +     plaintext.png,
> +     drawfile.png,
> +     postscript.png,
> +     printpreview.png,
> +     print.png,
> +     quit.png,
> +     cut.png,
> +     copy.png,
> +     paste.png,
> +     delete.png,
> +     selectall.png,
> +     find.png,
> +     preferences.png,
> +     zoomplus.png,
> +     zoomminus.png,
> +     zoomnormal.png,
> +     fullscreen.png,
> +     viewsource.png,
> +     downloads.png,
> +     savewindowsize.png,
> +     toggledebugging.png,
> +     saveboxtree.png,
> +     savedomtree.png,
> +     localhistory.png,
> +     globalhistory.png,
> +     addbookmarks.png,
> +     showbookmarks.png,
> +     openlocation.png,
> +     nexttab.png,
> +     prevtab.png,
> +     contents.png,
> +     guide.png,
> +     info.png,
> +     about.png,
> +     searchback.png,
> +     searchforward.png,
> +     searchclose.png
> +     
> +for local theming, the folder may be placed directly [as a subfolder] into 
> the netsurf/gtk/res/themes folder; then 'add theme' from the 
> preferences->advanced tab;
> +
> +for downloadable themes, compile netsurf/utils/container.c according to the 
> instructions in the header of that file; make a netsurf container of the 
> folder, serve it as content-type "application/x-netsurf-theme"; browse to it 
> in NetSurf, then NetSurf should automatically install it
> +
> Index: gtk/res/themes/gtk+/selectall.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/selectall.png differ
> Index: gtk/res/themes/gtk+/copy.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/copy.png differ
> Index: gtk/res/themes/gtk+/paste.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/paste.png differ
> Index: gtk/res/themes/gtk+/newtab.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/newtab.png differ
> Index: gtk/res/themes/gtk+/newwindow.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/newwindow.png differ
> Index: gtk/res/themes/gtk+/quit.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/quit.png differ
> Index: gtk/res/themes/gtk+/helpabout.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/helpabout.png differ
> Index: gtk/res/themes/gtk+/stop.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/stop.png differ
> Index: gtk/res/themes/gtk+/home.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/home.png differ
> Index: gtk/res/themes/gtk+/zoomplus.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/zoomplus.png differ
> Index: gtk/res/themes/gtk+/cut.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/cut.png differ
> Index: gtk/res/themes/gtk+/savepage.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/savepage.png differ
> Index: gtk/res/themes/gtk+/viewsource.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/gtk+/viewsource.png differ
> Index: gtk/res/themes/Alpha.png
> ===================================================================
> Binary files /dev/null and gtk/res/themes/Alpha.png differ
> Index: gtk/res/default.ico
> ===================================================================
> Binary files /dev/null and gtk/res/default.ico differ
> Index: gtk/res/themelist
> ===================================================================
> --- /dev/null 2009-04-16 19:17:07.000000000 +0100
> +++ gtk/res/themelist 2009-07-10 12:49:47.000000000 +0100
> @@ -0,0 +1,2 @@
> +gtk default theme
> +gtk+

> Index: Docs/Doxyfile
> ===================================================================
> --- Docs/Doxyfile     (revision 8438)
> +++ Docs/Doxyfile     (working copy)
> @@ -896,6 +896,8 @@
>  
>  PREDEFINED             = riscos CSS_INTERNALS WITH_ARTWORKS WITH_BMP 
> WITH_DRAW WITH_DRAW_EXPORT WITH_GIF WITH_JPEG WITH_MMAP WITH_MNG 
> WITH_NSSPRITE WITH_NS_SVG WITH_PLUGIN WITH_RSVG WITH_SAVE_COMPLETE 
> WITH_SPRITE WITH_THEME_INSTALL WITH_PDF_EXPORT
>  
> +PREDEFINED           = gtk WITH_THEME_INSTALL
> +
>  # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
>  # this tag can be used to specify a list of macro names that should be 
> expanded.
>  # The macro definition that is found in the sources will be used.

> Index: utils/config.h
> ===================================================================
> --- utils/config.h    (revision 8438)
> +++ utils/config.h    (working copy)
> @@ -55,6 +55,11 @@
>      #define WITH_MMAP
>  #endif
>  
> +#if defined(gtk)
> +     #define WITH_THEME_INSTALL
> +#endif
> +
> +
>  /* Configuration sanity checks: */
>  #if defined(WITH_NS_SVG) && defined(WITH_RSVG)
>      #error Cannot build WITH_NS_SVG and WITH_RSVG both enabled
> Index: utils/container.c
>   
debugging of container code; added detail to description of how to put a
theme into a container; added 'gui-style' function to unwrap container
> ===================================================================
> --- utils/container.c (revision 8438)
> +++ utils/container.c (working copy)
> @@ -19,26 +19,33 @@
>  /* 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
> + * gcc -I../ -DNSTHEME -o themetool container.c
>   *
> + * 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/container.h"
> -#include "utils/config.h"
> +#include "utils/log.h"
> +#include "utils/utils.h"
>  #ifdef WITH_MMAP
>  #include <sys/mman.h>
>  #endif
>  
>  struct container_dirent {
> -     unsigned char   filename[16];
> +     unsigned char   filename[64];
>       u_int32_t       startoffset;
>       u_int32_t       len;
>       u_int32_t       flags1;
> @@ -85,7 +92,7 @@
>                               sizeof(struct container_dirent));
>  
>       strncpy((char *)ctx->directory[ctx->entries - 1].filename,
> -                             (char *)entryname, 16);
> +                             (char *)entryname, 64);
>       ctx->directory[ctx->entries - 1].startoffset = offset;
>       ctx->directory[ctx->entries - 1].len = length;
>       ctx->directory[ctx->entries - 1].flags1 = 0;
> @@ -94,13 +101,14 @@
>  
>  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;
> +     return NULL;
>       }
>  
>       /* we don't actually load any of the data (including directory)
> @@ -109,16 +117,26 @@
>        */
>       ctx->processed = false;
>  
> -     fread(&ctx->header.magic, 4, 1, ctx->fh);
> +     val = fread(&ctx->header.magic, 4, 1, ctx->fh);
> +     if (val == 0)
> +             LOG(("empty read magic"));
>       ctx->header.magic = ntohl(ctx->header.magic);
>  
> -     fread(&ctx->header.parser, 4, 1, ctx->fh);
> +     val = fread(&ctx->header.parser, 4, 1, ctx->fh);
> +     if (val == 0)
> +             LOG(("empty read parser"));     
>       ctx->header.parser = ntohl(ctx->header.parser);
>  
> -     fread(ctx->header.name, 32, 1, ctx->fh);
> -     fread(ctx->header.author, 64, 1, ctx->fh);
> +     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"));
>  
> -     fread(&ctx->header.diroffset, 4, 1, ctx->fh);
> +     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) {
> @@ -132,7 +150,8 @@
>  
>  static void container_process(struct container_ctx *ctx)
>  {
> -     unsigned char filename[16];
> +     size_t val;
> +     unsigned char filename[64];
>       u_int32_t start, len, flags1, flags2;
>  
>  #ifdef WITH_MMAP
> @@ -141,14 +160,19 @@
>  #else
>       ctx->data = malloc(ctx->header.diroffset);
>       fseek(ctx->fh, 0, SEEK_SET);
> -     fread(ctx->data, ctx->header.diroffset, 1, ctx->fh);
> +     val = fread(ctx->data, ctx->header.diroffset, 1, ctx->fh);
> +     if (val == 0)
> +             LOG(("empty read diroffset"));
>  #endif
>       fseek(ctx->fh, ctx->header.diroffset, SEEK_SET);
>       /* now work through the directory structure taking it apart into
>        * our structure */
> -#define BEREAD(x) do { fread(&(x), 4, 1, ctx->fh);(x) = ntohl((x)); } while 
> (0)
> +#define BEREAD(x) do { val = fread(&(x), 4, 1, ctx->fh); if (val == 0)\
> +             LOG(("empty read"));(x) = ntohl((x)); } while (0)
>       do {
> -             fread(filename, 16, 1, ctx->fh);
> +             val = fread(filename, 64, 1, ctx->fh);
> +             if (val == 0)
> +                     LOG(("empty read filename"));
>               BEREAD(start);
>               BEREAD(len);
>               BEREAD(flags1);
> @@ -164,7 +188,7 @@
>                                       struct container_ctx *ctx,
>                                       const unsigned char *entryname)
>  {
> -     int i;
> +     unsigned int i;
>  
>       for (i = 1; i <= ctx->entries; i++) {
>               struct container_dirent *e = ctx->directory + i - 1;
> @@ -227,12 +251,16 @@
>  
>  static void container_write_dir(struct container_ctx *ctx)
>  {
> -     int i;
> +     size_t val;
> +     unsigned int i;
>       u_int32_t tmp;
> -#define BEWRITE(x) do {tmp = htonl((x)); fwrite(&tmp, 4, 1, ctx->fh);} 
> while(0)
> +#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;
> -             fwrite(e->filename, 16, 1, ctx->fh);
> +             val = fwrite(e->filename, 64, 1, ctx->fh);
> +             if (val == 0)
> +                     LOG(("empty write filename"));
>               BEWRITE(e->startoffset);
>               BEWRITE(e->len);
>               BEWRITE(e->flags1);
> @@ -241,13 +269,16 @@
>  #undef BEWRITE
>       /* empty entry signifies end of directory */
>       tmp = 0;
> -     fwrite(&tmp, 4, 8, ctx->fh);
> +     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");
> @@ -264,10 +295,18 @@
>       strncpy((char *)ctx->header.name, (char *)name, 32);
>       strncpy((char *)ctx->header.author, (char *)author, 64);
>  
> -     fwrite("NSTM", 4, 1, ctx->fh);
> -     fwrite(&ctx->header.parser, 4, 1, ctx->fh);
> -     fwrite(ctx->header.name, 32, 1, ctx->fh);
> -     fwrite(ctx->header.author, 64, 1, ctx->fh);
> +     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;
>  
> @@ -284,14 +323,17 @@
>                                       const unsigned char *data,
>                                       const u_int32_t datalen)
>  {
> +     size_t val;
>       container_add_to_dir(ctx, entryname, ftell(ctx->fh), datalen);
> -     fwrite(data, datalen, 1, ctx->fh);
> +     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;
> +             size_t flen, nflen, val;
>  
>               /* discover where the directory's going to go. */
>               flen = container_filelen(ctx->fh);
> @@ -300,7 +342,9 @@
>               /* write this location to the header */
>               fseek(ctx->fh, 104, SEEK_SET);
>               nflen = htonl(flen);
> -             fwrite(&nflen, 4, 1, ctx->fh);
> +             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 */
>               fseek(ctx->fh, flen, SEEK_SET);
> @@ -318,6 +362,72 @@
>       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
> + */
> +
> +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 = (char *)container_get_name(cctx);
> +     LOG(("theme name: %s", themename));
> +     LOG(("theme author: %s", container_get_author(cctx)));
> +     
> +     dirname = malloc(strlen(dirbasename) + strlen(themename) + 2);
> +     strcpy(dirname, dirbasename);
> +     strcat(dirname, themename);
> +     if (stat(dirname, &statbuf) != -1) {
> +             warn_user("DirectoryError", dirname);
> +             container_close(cctx);
> +             free(dirname);
> +             return NULL;
> +     }
> +     mkdir(dirname, 00777);
> +
> +     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[])
>  {
> @@ -355,7 +465,6 @@
>  #include <getopt.h>
>  #include <dirent.h>
>  #include <errno.h>
> -#include <sys/stat.h>
>  #include <unistd.h>
>  
>  static bool verbose = false;
> @@ -403,7 +512,8 @@
>               printf("theme author: %s\n", container_get_author(cctx));
>       }
>  
> -     while ( (e = container_iterate(cctx, &state)) ) {
> +     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);
> @@ -450,9 +560,9 @@
>                       /* not the metadirs, so we want to process this. */
>                       if (verbose == true)
>                               printf("adding %s\n", e->d_name);
> -                     if (strlen(e->d_name) > 15) {
> +                     if (strlen(e->d_name) > 63) {
>                               fprintf(stderr,
> -                     "warning: name truncated to 15 characters.\n");
> +                     "warning: name truncated to length 63.\n");
>                       }
>  
>                       snprintf(path, PATH_MAX, "%s/%s", dirname, e->d_name);
> Index: utils/container.h
> ===================================================================
> --- utils/container.h (revision 8438)
> +++ utils/container.h (working copy)
> @@ -47,4 +47,7 @@
>  /* 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__ */

-- 
Mark

http://www.halloit.com

Key ID 046B65CF


Reply via email to