# HG changeset patch # User Tomasz Mon # Date 1211120868 -7200 # Node ID c0b08527b121833ad3c8dba68fb4e8f76de0d6ef # Parent d0daee216c8d09383081f9c0559af03847990936 allow loading compressed skins diff -r d0daee216c8d -r c0b08527b121 src/skins/Makefile --- a/src/skins/Makefile Sun May 18 14:20:51 2008 +0200 +++ b/src/skins/Makefile Sun May 18 16:27:48 2008 +0200 @@ -5,7 +5,8 @@ pixbuf_effects.c \ ui_skin.c \ ui_skinned_window.c \ - ui_dock.c + ui_dock.c \ + util.c include ../../buildsys.mk include ../../extra.mk diff -r d0daee216c8d -r c0b08527b121 src/skins/plugin.c --- a/src/skins/plugin.c Sun May 18 14:20:51 2008 +0200 +++ b/src/skins/plugin.c Sun May 18 16:27:48 2008 +0200 @@ -57,7 +57,7 @@ gtk_window_set_resizable(GTK_WINDOW(mainwin), FALSE); - init_skins("/usr/local/share/audacious/Skins/Default"); + init_skins(config->skin); width = aud_active_skin->properties.mainwin_width; height = aud_active_skin->properties.mainwin_height; diff -r d0daee216c8d -r c0b08527b121 src/skins/skins_cfg.c --- a/src/skins/skins_cfg.c Sun May 18 14:20:51 2008 +0200 +++ b/src/skins/skins_cfg.c Sun May 18 16:27:48 2008 +0200 @@ -27,12 +27,14 @@ skins_cfg_t * skins_cfg_new(void) { skins_cfg_t *cfg = g_malloc0(sizeof(skins_cfg_t)); cfg->set = FALSE; + cfg->skin = NULL; return cfg; } void skins_cfg_delete(skins_cfg_t * cfg) { if (cfg != NULL) { + if (cfg->skin) g_free(cfg->skin); g_free(cfg); } } @@ -48,6 +50,9 @@ cfg->where = FALSE / TRUE; */ + if (!aud_cfg_db_get_string(cfgfile, "skins", "skin", &(cfg->skin))) + cfg->skin = g_strdup("/usr/local/share/audacious/Skins/Default"); + aud_cfg_db_close( cfgfile ); cfg->set = TRUE; @@ -67,6 +72,7 @@ aud_cfg_db_set_string(cfgfile, "skins", "field_name", cfg->where); aud_cfg_db_set_bool(cfgfile, "skins", "field_name", cfg->where); */ + aud_cfg_db_set_string(cfgfile, "skins", "skin", cfg->skin); aud_cfg_db_close(cfgfile); diff -r d0daee216c8d -r c0b08527b121 src/skins/skins_cfg.h --- a/src/skins/skins_cfg.h Sun May 18 14:20:51 2008 +0200 +++ b/src/skins/skins_cfg.h Sun May 18 16:27:48 2008 +0200 @@ -26,6 +26,7 @@ typedef struct { gboolean set; + gchar *skin; } skins_cfg_t; diff -r d0daee216c8d -r c0b08527b121 src/skins/ui_skin.c --- a/src/skins/ui_skin.c Sun May 18 14:20:51 2008 +0200 +++ b/src/skins/ui_skin.c Sun May 18 16:27:48 2008 +0200 @@ -39,6 +39,7 @@ #include #include "ui_skin.h" +#include "util.h" #if 0 #include "ui_equalizer.h" #include "ui_playlist.h" @@ -80,102 +81,6 @@ gboolean found; } FindFileContext; -typedef gboolean(*DirForeachFunc) (const gchar * path, - const gchar * basename, - gpointer user_data); - -gboolean -dir_foreach(const gchar * path, DirForeachFunc function, - gpointer user_data, GError ** error) -{ - GError *error_out = NULL; - GDir *dir; - const gchar *entry; - gchar *entry_fullpath; - - if (!(dir = g_dir_open(path, 0, &error_out))) { - g_propagate_error(error, error_out); - return FALSE; - } - - while ((entry = g_dir_read_name(dir))) { - entry_fullpath = g_build_filename(path, entry, NULL); - - if ((*function) (entry_fullpath, entry, user_data)) { - g_free(entry_fullpath); - break; - } - - g_free(entry_fullpath); - } - - g_dir_close(dir); - - return TRUE; -} - -static gboolean -find_file_func(const gchar * path, const gchar * basename, gpointer data) -{ - FindFileContext *context = data; - - if (strlen(path) > FILENAME_MAX) { - AUDDBG("Ignoring path: name too long (%s)\n", path); - return TRUE; - } - - if (aud_vfs_file_test(path, G_FILE_TEST_IS_REGULAR)) { - if (!strcasecmp(basename, context->to_match)) { - context->match = g_strdup(path); - context->found = TRUE; - return TRUE; - } - } - else if (aud_vfs_file_test(path, G_FILE_TEST_IS_DIR)) { - dir_foreach(path, find_file_func, context, NULL); - if (context->found) - return TRUE; - } - - return FALSE; -} - -gchar * -find_file_recursively(const gchar * path, const gchar * filename) -{ - FindFileContext context; - gchar *out = NULL; - - context.to_match = filename; - context.match = NULL; - context.found = FALSE; - - dir_foreach(path, find_file_func, &context, NULL); - - if (context.match) - { - out = g_filename_to_uri(context.match, NULL, NULL); - g_free(context.match); - } - - return out; -} - -gchar * -find_path_recursively(const gchar * path, const gchar * filename) -{ - FindFileContext context; - - context.to_match = filename; - context.match = NULL; - context.found = FALSE; - - dir_foreach(path, find_file_func, &context, NULL); - - return context.match; -} - - typedef struct _SkinPixmapIdMapping SkinPixmapIdMapping; typedef struct _SkinMaskInfo SkinMaskInfo; @@ -1535,10 +1440,10 @@ if (filename) g_free(filename); -#if 0 + skin_mask_create(skin, path, SKIN_MASK_MAIN, mainwin->window); skin_mask_create(skin, path, SKIN_MASK_MAIN_SHADE, mainwin->window); - +#if 0 skin_mask_create(skin, path, SKIN_MASK_EQ, equalizerwin->window); skin_mask_create(skin, path, SKIN_MASK_EQ_SHADE, equalizerwin->window); @@ -1609,7 +1514,7 @@ AUDDBG("skin %s already loaded\n", path); return FALSE; } -#if 0 + if (file_is_archive(path)) { AUDDBG("Attempt to load archive\n"); if (!(skin_path = archive_decompress(path))) { @@ -1620,10 +1525,7 @@ } else { skin_path = g_strdup(path); } -#else -skin_path = g_strdup(path); -#endif -#if 0 + // Check if skin path has all necessary files. if (!aud_cfg->allow_broken_skins && !skin_check_pixmaps(skin, skin_path)) { if(archive) del_directory(skin_path); @@ -1631,7 +1533,7 @@ AUDDBG("Skin path (%s) doesn't have all wanted pixmaps\n", skin_path); return FALSE; } -#endif + // skin_free() frees skin->path and variable path can actually be skin->path // and we want to get the path before possibly freeing it. newpath = g_strdup(path); @@ -1646,9 +1548,7 @@ skin_parse_hints(skin, skin_path); if (!skin_load_pixmaps(skin, skin_path)) { -#if 0 if(archive) del_directory(skin_path); -#endif g_free(skin_path); AUDDBG("Skin loading failed\n"); return FALSE; @@ -1672,12 +1572,12 @@ g_free(gtkrcpath); } #endif -#if 0 + if(archive) del_directory(skin_path); -#endif g_free(skin_path); + + gtk_widget_shape_combine_mask(mainwin, skin_get_mask(aud_active_skin, SKIN_MASK_MAIN + aud_cfg->player_shaded), 0, 0); #if 0 - gtk_widget_shape_combine_mask(mainwin, skin_get_mask(aud_active_skin, SKIN_MASK_MAIN + aud_cfg->player_shaded), 0, 0); gtk_widget_shape_combine_mask(equalizerwin, skin_get_mask(aud_active_skin, SKIN_MASK_EQ + aud_cfg->equalizer_shaded), 0, 0); #endif return TRUE; diff -r d0daee216c8d -r c0b08527b121 src/skins/util.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/skins/util.c Sun May 18 16:27:48 2008 +0200 @@ -0,0 +1,1134 @@ +/* Audacious - Cross-platform multimedia player + * Copyright (C) 2005-2008 Audacious development team + * + * Based on BMP: + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program 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; under version 3 of the License. + * + * This program 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 . + * + * The Audacious team does not consider modular code linking to + * Audacious or using our public API to be a derived work. + */ + +/*#define AUD_DEBUG*/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "util.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "platform/smartinclude.h" +#include + +#ifdef HAVE_FTS_H +# include +# include +# include +#endif + +#include +#include + +#ifdef USE_CHARDET +# include "../libguess/libguess.h" +# ifdef HAVE_UDET +# include +# endif +#endif + +#include "plugin.h" + +/* + * find in directory or subdirectories. return + * pointer to complete filename which has to be freed by calling + * "g_free()" after use. Returns NULL if file could not be found. + */ + +/* somebody tell me how to make use of those funcs from string.h that are in core... */ +gboolean +str_has_suffix_nocase(const gchar * str, const gchar * suffix) +{ + return (strcasecmp(str + strlen(str) - strlen(suffix), suffix) == 0); +} + +static gchar * +str_replace_char(gchar * str, gchar old, gchar new) +{ + gchar *match; + + g_return_val_if_fail(str != NULL, NULL); + + match = str; + while ((match = strchr(match, old))) + *match = new; + + return str; +} + +static gchar * +str_replace_drive_letter(gchar * str) +{ + gchar *match, *match_end; + + g_return_val_if_fail(str != NULL, NULL); + + while ((match = strstr(str, ":\\"))) { + match--; + match_end = match + 3; + *match++ = '/'; + while (*match_end) + *match++ = *match_end++; + *match = 0; /* the end of line */ + } + + return str; +} + +gchar * +convert_dos_path(gchar * path) +{ + g_return_val_if_fail(path != NULL, NULL); + + /* replace drive letter with '/' */ + str_replace_drive_letter(path); + + /* replace '\' with '/' */ + str_replace_char(path, '\\', '/'); + + return path; +} + +gchar * +escape_shell_chars(const gchar * string) +{ + const gchar *special = "$`\"\\"; /* Characters to escape */ + const gchar *in = string; + gchar *out, *escaped; + gint num = 0; + + while (*in != '\0') + if (strchr(special, *in++)) + num++; + + escaped = g_malloc(strlen(string) + num + 1); + + in = string; + out = escaped; + + while (*in != '\0') { + if (strchr(special, *in)) + *out++ = '\\'; + *out++ = *in++; + } + *out = '\0'; + + return escaped; +} + + +typedef struct { + const gchar *to_match; + gchar *match; + gboolean found; +} FindFileContext; + +static const struct { + AFormat afmt; + SAD_sample_format sadfmt; +} format_table[] = { + {FMT_U8, SAD_SAMPLE_U8}, + {FMT_S8, SAD_SAMPLE_S8}, + + {FMT_S16_LE, SAD_SAMPLE_S16_LE}, + {FMT_S16_BE, SAD_SAMPLE_S16_BE}, + {FMT_S16_NE, SAD_SAMPLE_S16}, + + {FMT_U16_LE, SAD_SAMPLE_U16_LE}, + {FMT_U16_BE, SAD_SAMPLE_U16_BE}, + {FMT_U16_NE, SAD_SAMPLE_U16}, + + {FMT_S24_LE, SAD_SAMPLE_S24_LE}, + {FMT_S24_BE, SAD_SAMPLE_S24_BE}, + {FMT_S24_NE, SAD_SAMPLE_S24}, + + {FMT_U24_LE, SAD_SAMPLE_U24_LE}, + {FMT_U24_BE, SAD_SAMPLE_U24_BE}, + {FMT_U24_NE, SAD_SAMPLE_U24}, + + {FMT_S32_LE, SAD_SAMPLE_S32_LE}, + {FMT_S32_BE, SAD_SAMPLE_S32_BE}, + {FMT_S32_NE, SAD_SAMPLE_S32}, + + {FMT_U32_LE, SAD_SAMPLE_U32_LE}, + {FMT_U32_BE, SAD_SAMPLE_U32_BE}, + {FMT_U32_NE, SAD_SAMPLE_U32}, + + {FMT_FLOAT, SAD_SAMPLE_FLOAT}, + {FMT_FIXED32, SAD_SAMPLE_FIXED32}, +}; + +SAD_sample_format +sadfmt_from_afmt(AFormat fmt) +{ + int i; + for (i = 0; i < sizeof(format_table) / sizeof(format_table[0]); i++) { + if (format_table[i].afmt == fmt) return format_table[i].sadfmt; + } + + return -1; +} + + +static gboolean +find_file_func(const gchar * path, const gchar * basename, gpointer data) +{ + FindFileContext *context = data; + + if (strlen(path) > FILENAME_MAX) { + AUDDBG("Ignoring path: name too long (%s)\n", path); + return TRUE; + } + + if (aud_vfs_file_test(path, G_FILE_TEST_IS_REGULAR)) { + if (!strcasecmp(basename, context->to_match)) { + context->match = g_strdup(path); + context->found = TRUE; + return TRUE; + } + } + else if (aud_vfs_file_test(path, G_FILE_TEST_IS_DIR)) { + dir_foreach(path, find_file_func, context, NULL); + if (context->found) + return TRUE; + } + + return FALSE; +} + +gchar * +find_file_recursively(const gchar * path, const gchar * filename) +{ + FindFileContext context; + gchar *out = NULL; + + context.to_match = filename; + context.match = NULL; + context.found = FALSE; + + dir_foreach(path, find_file_func, &context, NULL); + + if (context.match) + { + out = g_filename_to_uri(context.match, NULL, NULL); + g_free(context.match); + } + + return out; +} + +gchar * +find_path_recursively(const gchar * path, const gchar * filename) +{ + FindFileContext context; + + context.to_match = filename; + context.match = NULL; + context.found = FALSE; + + dir_foreach(path, find_file_func, &context, NULL); + + return context.match; +} + + +typedef enum { + ARCHIVE_UNKNOWN = 0, + ARCHIVE_DIR, + ARCHIVE_TAR, + ARCHIVE_TGZ, + ARCHIVE_ZIP, + ARCHIVE_TBZ2 +} ArchiveType; + +typedef gchar *(*ArchiveExtractFunc) (const gchar *, const gchar *); + +typedef struct { + ArchiveType type; + const gchar *ext; +} ArchiveExtensionType; + +static ArchiveExtensionType archive_extensions[] = { + {ARCHIVE_TAR, ".tar"}, + {ARCHIVE_ZIP, ".wsz"}, + {ARCHIVE_ZIP, ".zip"}, + {ARCHIVE_TGZ, ".tar.gz"}, + {ARCHIVE_TGZ, ".tgz"}, + {ARCHIVE_TBZ2, ".tar.bz2"}, + {ARCHIVE_TBZ2, ".bz2"}, + {ARCHIVE_UNKNOWN, NULL} +}; + +static gchar *archive_extract_tar(const gchar * archive, const gchar * dest); +static gchar *archive_extract_zip(const gchar * archive, const gchar * dest); +static gchar *archive_extract_tgz(const gchar * archive, const gchar * dest); +static gchar *archive_extract_tbz2(const gchar * archive, const gchar * dest); + +static ArchiveExtractFunc archive_extract_funcs[] = { + NULL, + NULL, + archive_extract_tar, + archive_extract_tgz, + archive_extract_zip, + archive_extract_tbz2 +}; + + +/* FIXME: these functions can be generalised into a function using a + * command lookup table */ + +static const gchar * +get_tar_command(void) +{ + static const gchar *command = NULL; + + if (!command) { + if (!(command = getenv("TARCMD"))) + command = "tar"; + } + + return command; +} + +static const gchar * +get_unzip_command(void) +{ + static const gchar *command = NULL; + + if (!command) { + if (!(command = getenv("UNZIPCMD"))) + command = "unzip"; + } + + return command; +} + + +static gchar * +archive_extract_tar(const gchar * archive, const gchar * dest) +{ + return g_strdup_printf("%s >/dev/null xf \"%s\" -C %s", + get_tar_command(), archive, dest); +} + +static gchar * +archive_extract_zip(const gchar * archive, const gchar * dest) +{ + return g_strdup_printf("%s >/dev/null -o -j \"%s\" -d %s", + get_unzip_command(), archive, dest); +} + +static gchar * +archive_extract_tgz(const gchar * archive, const gchar * dest) +{ + return g_strdup_printf("%s >/dev/null xzf \"%s\" -C %s", + get_tar_command(), archive, dest); +} + +static gchar * +archive_extract_tbz2(const gchar * archive, const gchar * dest) +{ + return g_strdup_printf("bzip2 -dc \"%s\" | %s >/dev/null xf - -C %s", + archive, get_tar_command(), dest); +} + + +ArchiveType +archive_get_type(const gchar * filename) +{ + gint i = 0; + + if (g_file_test(filename, G_FILE_TEST_IS_DIR)) + return ARCHIVE_DIR; + + while (archive_extensions[i].ext) { + if (g_str_has_suffix(filename, archive_extensions[i].ext)) { + return archive_extensions[i].type; + } + i++; + } + + return ARCHIVE_UNKNOWN; +} + +gboolean +file_is_archive(const gchar * filename) +{ + return (archive_get_type(filename) > ARCHIVE_DIR); +} + +gchar * +archive_basename(const gchar * str) +{ + gint i = 0; + + while (archive_extensions[i].ext) { + if (str_has_suffix_nocase(str, archive_extensions[i].ext)) { + const gchar *end = g_strrstr(str, archive_extensions[i].ext); + if (end) { + return g_strndup(str, end - str); + } + break; + } + i++; + } + + return NULL; +} + +/* + decompress_archive + + Decompresses the archive "filename" to a temporary directory, + returns the path to the temp dir, or NULL if failed, + watch out tho, doesn't actually check if the system command succeeds :-| +*/ + +gchar * +archive_decompress(const gchar * filename) +{ + gchar *tmpdir, *cmd, *escaped_filename; + ArchiveType type; +#ifndef HAVE_MKDTEMP + mode_t mode755 = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; +#endif + + if ((type = archive_get_type(filename)) <= ARCHIVE_DIR) + return NULL; + +#ifdef HAVE_MKDTEMP + tmpdir = g_build_filename(g_get_tmp_dir(), "audacious.XXXXXXXX", NULL); + if (!mkdtemp(tmpdir)) { + g_free(tmpdir); + AUDDBG("Unable to load skin: Failed to create temporary " + "directory: %s\n", g_strerror(errno)); + return NULL; + } +#else + tmpdir = g_strdup_printf("%s/audacious.%ld", g_get_tmp_dir(), (long) rand()); + make_directory(tmpdir, mode755); +#endif + + escaped_filename = escape_shell_chars(filename); + cmd = archive_extract_funcs[type] (escaped_filename, tmpdir); + g_free(escaped_filename); + + if (!cmd) { + AUDDBG("extraction function is NULL!\n"); + g_free(tmpdir); + return NULL; + } + + AUDDBG("Attempt to execute \"%s\"\n", cmd); + + if(system(cmd) != 0) + { + AUDDBG("could not execute cmd %s\n",cmd); + g_free(cmd); + return NULL; + } + g_free(cmd); + + return tmpdir; +} + + +#ifdef HAVE_FTS_H + +void +del_directory(const gchar * dirname) +{ + gchar *const argv[2] = { (gchar *) dirname, NULL }; + FTS *fts; + FTSENT *p; + + fts = fts_open(argv, FTS_PHYSICAL, (gint(*)())NULL); + while ((p = fts_read(fts))) { + switch (p->fts_info) { + case FTS_D: + break; + case FTS_DNR: + case FTS_ERR: + break; + case FTS_DP: + rmdir(p->fts_accpath); + break; + default: + unlink(p->fts_accpath); + break; + } + } + fts_close(fts); +} + +#else /* !HAVE_FTS */ + +gboolean +del_directory_func(const gchar * path, const gchar * basename, + gpointer params) +{ + if (!strcmp(basename, ".") || !strcmp(path, "..")) + return FALSE; + + if (g_file_test(path, G_FILE_TEST_IS_DIR)) { + dir_foreach(path, del_directory_func, NULL, NULL); + rmdir(path); + return FALSE; + } + + unlink(path); + + return FALSE; +} + +void +del_directory(const gchar * path) +{ + dir_foreach(path, del_directory_func, NULL, NULL); + rmdir(path); +} + +#endif /* ifdef HAVE_FTS */ + +static void +strip_string(GString *string) +{ + while (string->len > 0 && string->str[0] == ' ') + g_string_erase(string, 0, 1); + + while (string->len > 0 && string->str[string->len - 1] == ' ') + g_string_erase(string, string->len - 1, 1); +} + +static void +strip_lower_string(GString *string) +{ + gchar *lower; + strip_string(string); + + lower = g_ascii_strdown(string->str, -1); + g_free(string->str); + string->str = lower; +} + +static void +close_ini_file_free_value(gpointer value) +{ + g_free((gchar*)value); +} + +static void +close_ini_file_free_section(gpointer section) +{ + g_hash_table_destroy((GHashTable*)section); +} + +INIFile * +open_ini_file(const gchar *filename) +{ + GHashTable *ini_file = NULL; + GHashTable *section = NULL; + GString *section_name, *key_name, *value; + gpointer section_hash, key_hash; + gchar *buffer = NULL; + gsize off = 0; + gsize filesize = 0; + + unsigned char x[] = { 0xff, 0xfe, 0x00 }; + + g_return_val_if_fail(filename, NULL); + aud_vfs_file_get_contents(filename, &buffer, &filesize); + if (buffer == NULL) + return NULL; + + /* + * Convert UTF-16 into something useful. Original implementation + * by incomp@#audacious. Cleanups \nenolod + * FIXME: can't we use a GLib function for that? -- 01mf02 + */ + if (filesize > 2 && !memcmp(&buffer[0],&x,2)) + { + gchar *outbuf = g_malloc (filesize); /* it's safe to waste memory. */ + guint counter; + + for (counter = 2; counter < filesize; counter += 2) + { + if (!memcmp(&buffer[counter+1], &x[2], 1)) { + outbuf[(counter-2)/2] = buffer[counter]; + } else { + g_free(buffer); + g_free(outbuf); + return NULL; + } + } + + outbuf[(counter-2)/2] = '\0'; + + if ((filesize - 2) / 2 == (counter - 2) / 2) + { + g_free(buffer); + buffer = outbuf; + } + else + { + g_free(buffer); + g_free(outbuf); + return NULL; /* XXX wrong encoding */ + } + } + + section_name = g_string_new(""); + key_name = g_string_new(NULL); + value = g_string_new(NULL); + + ini_file = g_hash_table_new_full(NULL, NULL, NULL, + close_ini_file_free_section); + section = g_hash_table_new_full(NULL, NULL, NULL, + close_ini_file_free_value); + /* make a nameless section which should store all entries that are not + * embedded in a section */ + section_hash = GINT_TO_POINTER(g_string_hash(section_name)); + g_hash_table_insert(ini_file, section_hash, section); + + while (off < filesize) + { + /* ignore the following characters */ + if (buffer[off] == '\r' || buffer[off] == '\n' || + buffer[off] == ' ' || buffer[off] == '\t') + { + if (buffer[off] == '\n') + { + g_string_free(key_name, TRUE); + g_string_free(value, TRUE); + key_name = g_string_new(NULL); + value = g_string_new(NULL); + } + + off++; + continue; + } + + /* if we encounter a possible section statement */ + if (buffer[off] == '[') + { + g_string_free(section_name, TRUE); + section_name = g_string_new(NULL); + off++; + + if (off >= filesize) + goto return_sequence; + + while (buffer[off] != ']') + { + /* if the section statement has not been closed before a + * linebreak */ + if (buffer[off] == '\n') + break; + + g_string_append_c(section_name, buffer[off]); + off++; + if (off >= filesize) + goto return_sequence; + } + if (buffer[off] == '\n') + continue; + if (buffer[off] == ']') + { + off++; + if (off >= filesize) + goto return_sequence; + + strip_lower_string(section_name); + section_hash = GINT_TO_POINTER(g_string_hash(section_name)); + + /* if this section already exists, we don't make a new one, + * but reuse the old one */ + if (g_hash_table_lookup(ini_file, section_hash) != NULL) + section = g_hash_table_lookup(ini_file, section_hash); + else + { + section = g_hash_table_new_full(NULL, NULL, NULL, + close_ini_file_free_value); + g_hash_table_insert(ini_file, section_hash, section); + } + + continue; + } + } + + if (buffer[off] == '=') + { + off++; + if (off >= filesize) + goto return_sequence; + + while (buffer[off] != '\n' && buffer[off] != '\r') + { + g_string_append_c(value, buffer[off]); + off++; + if (off >= filesize) + break; + } + + strip_lower_string(key_name); + key_hash = GINT_TO_POINTER(g_string_hash(key_name)); + strip_string(value); + + if (key_name->len > 0 && value->len > 0) + g_hash_table_insert(section, key_hash, g_strdup(value->str)); + } + else + { + g_string_append_c(key_name, buffer[off]); + off++; + if (off >= filesize) + goto return_sequence; + } + } + +return_sequence: + g_string_free(section_name, TRUE); + g_string_free(key_name, TRUE); + g_string_free(value, TRUE); + g_free(buffer); + return ini_file; +} + +/** + * Frees the memory allocated for inifile. + */ +void +close_ini_file(INIFile *inifile) +{ + g_return_if_fail(inifile); + g_hash_table_destroy(inifile); +} + +/** + * Returns a string that corresponds to correct section and key in inifile. + * + * Returns NULL if value was not found in inifile. Otherwise returns a copy + * of string pointed by "section" and "key". Returned string should be freed + * after use. + */ +gchar * +read_ini_string(INIFile *inifile, const gchar *section, const gchar *key) +{ + GString *section_string; + GString *key_string; + gchar *value = NULL; + gpointer section_hash, key_hash; + GHashTable *section_table; + + g_return_val_if_fail(inifile, NULL); + + section_string = g_string_new(section); + key_string = g_string_new(key); + value = NULL; + + strip_lower_string(section_string); + strip_lower_string(key_string); + section_hash = GINT_TO_POINTER(g_string_hash(section_string)); + key_hash = GINT_TO_POINTER(g_string_hash(key_string)); + section_table = g_hash_table_lookup(inifile, section_hash); + + if (section_table) { + value = g_strdup(g_hash_table_lookup(section_table, + GINT_TO_POINTER(key_hash))); + } + + g_string_free(section_string, TRUE); + g_string_free(key_string, TRUE); + + g_return_val_if_fail(value, NULL); + return value; +} + +GArray * +read_ini_array(INIFile *inifile, const gchar *section, const gchar *key) +{ + gchar *temp; + GArray *a; + + g_return_val_if_fail((temp = read_ini_string(inifile, section, key)), NULL); + + a = string_to_garray(temp); + g_free(temp); + return a; +} + +GArray * +string_to_garray(const gchar * str) +{ + GArray *array; + gint temp; + const gchar *ptr = str; + gchar *endptr; + + array = g_array_new(FALSE, TRUE, sizeof(gint)); + for (;;) { + temp = strtol(ptr, &endptr, 10); + if (ptr == endptr) + break; + g_array_append_val(array, temp); + ptr = endptr; + while (!isdigit((int) *ptr) && (*ptr) != '\0') + ptr++; + if (*ptr == '\0') + break; + } + return (array); +} + +void +glist_movedown(GList * list) +{ + gpointer temp; + + if (g_list_next(list)) { + temp = list->data; + list->data = list->next->data; + list->next->data = temp; + } +} + +void +glist_moveup(GList * list) +{ + gpointer temp; + + if (g_list_previous(list)) { + temp = list->data; + list->data = list->prev->data; + list->prev->data = temp; + } +} + +GdkFont * +util_font_load(const gchar * name) +{ + GdkFont *font; + PangoFontDescription *desc; + + desc = pango_font_description_from_string(name); + font = gdk_font_from_description(desc); + + return font; +} +#if 0 +/* text_get_extents() taken from The GIMP (C) Spencer Kimball, Peter + * Mattis et al */ +gboolean +text_get_extents(const gchar * fontname, + const gchar * text, + gint * width, gint * height, gint * ascent, gint * descent) +{ + PangoFontDescription *font_desc; + PangoLayout *layout; + PangoRectangle rect; + + g_return_val_if_fail(fontname != NULL, FALSE); + g_return_val_if_fail(text != NULL, FALSE); + + /* FIXME: resolution */ + layout = gtk_widget_create_pango_layout(GTK_WIDGET(mainwin), text); + + font_desc = pango_font_description_from_string(fontname); + pango_layout_set_font_description(layout, font_desc); + pango_font_description_free(font_desc); + pango_layout_get_pixel_extents(layout, NULL, &rect); + + if (width) + *width = rect.width; + if (height) + *height = rect.height; + + if (ascent || descent) { + PangoLayoutIter *iter; + PangoLayoutLine *line; + + iter = pango_layout_get_iter(layout); + line = pango_layout_iter_get_line(iter); + pango_layout_iter_free(iter); + + pango_layout_line_get_pixel_extents(line, NULL, &rect); + + if (ascent) + *ascent = PANGO_ASCENT(rect); + if (descent) + *descent = -PANGO_DESCENT(rect); + } + + g_object_unref(layout); + + return TRUE; +} +#endif +/* counts number of digits in a gint */ +guint +gint_count_digits(gint n) +{ + guint count = 0; + + n = ABS(n); + do { + count++; + n /= 10; + } while (n > 0); + + return count; +} + +gboolean +dir_foreach(const gchar * path, DirForeachFunc function, + gpointer user_data, GError ** error) +{ + GError *error_out = NULL; + GDir *dir; + const gchar *entry; + gchar *entry_fullpath; + + if (!(dir = g_dir_open(path, 0, &error_out))) { + g_propagate_error(error, error_out); + return FALSE; + } + + while ((entry = g_dir_read_name(dir))) { + entry_fullpath = g_build_filename(path, entry, NULL); + + if ((*function) (entry_fullpath, entry, user_data)) { + g_free(entry_fullpath); + break; + } + + g_free(entry_fullpath); + } + + g_dir_close(dir); + + return TRUE; +} + +GtkWidget * +make_filebrowser(const gchar *title, gboolean save) +{ + GtkWidget *dialog; + GtkWidget *button; + + g_return_val_if_fail(title != NULL, NULL); + + dialog = gtk_file_chooser_dialog_new(title, GTK_WINDOW(mainwin), + save ? + GTK_FILE_CHOOSER_ACTION_SAVE : + GTK_FILE_CHOOSER_ACTION_OPEN, + NULL, NULL); + + button = gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT); + + gtk_button_set_use_stock(GTK_BUTTON(button), TRUE); + GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT); + + button = gtk_dialog_add_button(GTK_DIALOG(dialog), save ? + GTK_STOCK_SAVE : GTK_STOCK_OPEN, + GTK_RESPONSE_ACCEPT); + + gtk_button_set_use_stock(GTK_BUTTON(button), TRUE); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); + gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); /* centering */ + + return dialog; +} + +/** + * util_info_dialog: + * @title: The title of the message to show. + * @text: The text of the message to show. + * @button_text: The text of the button which will close the messagebox. + * @modal: Whether or not the messagebox should be modal. + * @button_action: Code to execute on when the messagebox is closed, or %NULL. + * @action_data: Optional opaque data to pass to @button_action. + * + * Displays a message box. + * + * Return value: A GTK widget handle for the message box. + **/ +GtkWidget * +util_info_dialog(const gchar * title, const gchar * text, + const gchar * button_text, gboolean modal, + GCallback button_action, gpointer action_data) +{ + GtkWidget *dialog; + GtkWidget *dialog_vbox, *dialog_hbox, *dialog_bbox; + GtkWidget *dialog_bbox_b1; + GtkWidget *dialog_textlabel; + GtkWidget *dialog_icon; + + dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_type_hint( GTK_WINDOW(dialog) , GDK_WINDOW_TYPE_HINT_DIALOG ); + gtk_window_set_modal( GTK_WINDOW(dialog) , modal ); + gtk_window_set_title( GTK_WINDOW(dialog) , title ); + gtk_container_set_border_width( GTK_CONTAINER(dialog) , 10 ); + + dialog_vbox = gtk_vbox_new( FALSE , 0 ); + dialog_hbox = gtk_hbox_new( FALSE , 0 ); + + /* icon */ + dialog_icon = gtk_image_new_from_stock( GTK_STOCK_DIALOG_INFO , GTK_ICON_SIZE_DIALOG ); + gtk_box_pack_start( GTK_BOX(dialog_hbox) , dialog_icon , FALSE , FALSE , 2 ); + + /* label */ + dialog_textlabel = gtk_label_new( text ); + /* gtk_label_set_selectable( GTK_LABEL(dialog_textlabel) , TRUE ); */ + gtk_box_pack_start( GTK_BOX(dialog_hbox) , dialog_textlabel , TRUE , TRUE , 2 ); + + gtk_box_pack_start( GTK_BOX(dialog_vbox) , dialog_hbox , FALSE , FALSE , 2 ); + gtk_box_pack_start( GTK_BOX(dialog_vbox) , gtk_hseparator_new() , FALSE , FALSE , 4 ); + + dialog_bbox = gtk_hbutton_box_new(); + gtk_button_box_set_layout( GTK_BUTTON_BOX(dialog_bbox) , GTK_BUTTONBOX_END ); + dialog_bbox_b1 = gtk_button_new_with_label( button_text ); + g_signal_connect_swapped( G_OBJECT(dialog_bbox_b1) , "clicked" , + G_CALLBACK(gtk_widget_destroy) , dialog ); + if ( button_action ) + g_signal_connect( G_OBJECT(dialog_bbox_b1) , "clicked" , + button_action , action_data ); + + gtk_container_add( GTK_CONTAINER(dialog_bbox) , dialog_bbox_b1 ); + gtk_box_pack_start( GTK_BOX(dialog_vbox) , dialog_bbox , FALSE , FALSE , 0 ); + + gtk_container_add( GTK_CONTAINER(dialog) , dialog_vbox ); + + GTK_WIDGET_SET_FLAGS( dialog_bbox_b1 , GTK_CAN_DEFAULT); + gtk_widget_grab_default( dialog_bbox_b1 ); + + gtk_widget_show_all(dialog); + + return dialog; +} + + +/** + * util_get_localdir: + * + * Returns a string with the full path of Audacious local datadir (where config files are placed). + * It's useful in order to put in the right place custom config files for audacious plugins. + * + * Return value: a string with full path of Audacious local datadir (should be freed after use) + **/ +gchar* +util_get_localdir(void) +{ + gchar *datadir; + gchar *tmp; + + if ( (tmp = getenv("XDG_CONFIG_HOME")) == NULL ) + datadir = g_build_filename( g_get_home_dir() , ".config" , "audacious" , NULL ); + else + datadir = g_build_filename( tmp , "audacious" , NULL ); + + return datadir; +} + + +gchar * +construct_uri(gchar *string, const gchar *playlist_name) // uri, path and anything else +{ + gchar *filename = g_strdup(string); + gchar *tmp, *path; + gchar *uri = NULL; + + /* try to translate dos path */ + convert_dos_path(filename); /* in place replacement */ + + // make full path uri here + // case 1: filename is raw full path or uri + if (filename[0] == '/' || strstr(filename, "://")) { + uri = g_filename_to_uri(filename, NULL, NULL); + if(!uri) { + uri = g_strdup(filename); + } + g_free(filename); + } + // case 2: filename is not raw full path nor uri, playlist path is full path + // make full path by replacing last part of playlist path with filename. (using g_build_filename) + else if (playlist_name[0] == '/' || strstr(playlist_name, "://")) { + path = g_filename_from_uri(playlist_name, NULL, NULL); + if (!path) { + path = g_strdup(playlist_name); + } + tmp = strrchr(path, '/'); *tmp = '\0'; + tmp = g_build_filename(path, filename, NULL); + g_free(path); g_free(filename); + uri = g_filename_to_uri(tmp, NULL, NULL); + g_free(tmp); + } + // case 3: filename is not raw full path nor uri, playlist path is not full path + // just abort. + else { + g_free(filename); + return NULL; + } + + AUDDBG("uri=%s\n", uri); + return uri; +} + +/* + * minimize number of realloc's: + * - set N to nearest power of 2 not less then N + * - double it + * + * -- asphyx + * + * XXX: what's so smart about this?? seems wasteful and silly. --nenolod + */ +gpointer +smart_realloc(gpointer ptr, gsize *size) +{ + *size = (size_t)pow(2, ceil(log(*size) / log(2)) + 1); + if (ptr != NULL) free(ptr); + ptr = malloc(*size); + return ptr; +} + +void +make_directory(const gchar * path, mode_t mode) +{ + if (g_mkdir_with_parents(path, mode) == 0) + return; + + g_printerr(_("Could not create directory (%s): %s\n"), path, + g_strerror(errno)); +} diff -r d0daee216c8d -r c0b08527b121 src/skins/util.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/skins/util.h Sun May 18 16:27:48 2008 +0200 @@ -0,0 +1,103 @@ +/* Audacious - Cross-platform multimedia player + * Copyright (C) 2005-2008 Audacious development team + * + * Based on BMP: + * Copyright (C) 2003-2004 BMP development team + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team + * + * This program 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; under version 3 of the License. + * + * This program 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 . + * + * The Audacious team does not consider modular code linking to + * Audacious or using our public API to be a derived work. + */ + +#ifndef UTIL_H +#define UTIL_H + +#ifdef _AUDACIOUS_CORE +# ifdef HAVE_CONFIG_H +# include "config.h" +# endif +#endif + +#include +#include + +G_BEGIN_DECLS + +#include "audacious/plugin.h" +#include "libSAD/libSAD.h" + +#define SWAP(a, b) { a^=b; b^=a; a^=b; } + +typedef gboolean(*DirForeachFunc) (const gchar * path, + const gchar * basename, + gpointer user_data); + + +gchar *find_file_recursively(const gchar * dirname, const gchar * file); +gchar *find_path_recursively(const gchar * dirname, const gchar * file); +void del_directory(const gchar * dirname); +gboolean dir_foreach(const gchar * path, DirForeachFunc function, + gpointer user_data, GError ** error); + + +INIFile *open_ini_file(const gchar *filename); +void close_ini_file(INIFile *key_file); +gchar *read_ini_string(INIFile *key_file, const gchar *section, + const gchar *key); +GArray *read_ini_array(INIFile *key_file, const gchar *section, + const gchar *key); + +GArray *string_to_garray(const gchar * str); + +void glist_movedown(GList * list); +void glist_moveup(GList * list); + +GdkFont *util_font_load(const gchar * name); +void util_set_cursor(GtkWidget * window); +gboolean text_get_extents(const gchar * fontname, const gchar * text, + gint * width, gint * height, gint * ascent, + gint * descent); + +gboolean file_is_archive(const gchar * filename); +gchar *archive_decompress(const gchar * path); +gchar *archive_basename(const gchar * path); + +guint gint_count_digits(gint n); + + +GtkWidget *make_filebrowser(const gchar *title, gboolean save); + +GtkWidget *util_info_dialog(const gchar * title, const gchar * text, + const gchar * button_text, gboolean modal, GCallback button_action, + gpointer action_data); + +GdkPixbuf *audacious_create_colorized_pixbuf(GdkPixbuf *src, gint red, gint green, gint blue); + +gchar *util_get_localdir(void); + +gchar *construct_uri(gchar *string, const gchar *playlist_name); + +SAD_sample_format sadfmt_from_afmt(AFormat fmt); + +/* minimizes number of realloc's */ +gpointer smart_realloc(gpointer ptr, gsize *size); + +void make_directory(const gchar * path, mode_t mode); + +G_END_DECLS + +#endif